Reputation: 46609
Let's say I have a func that takes in a slice of Person:
type Person struct {
ID uuid.UUID
FirstName string
LastName string
... plus 20 more fields
}
When logging I might want to log just the ID. Is there a simple way to do this without creating another type? I'm using Logrus. If I was in JS I'd just use the map function. Example logger line:
logger.Debugf("personProcessor starting for people: %v", persons)
But this will result in a lot of unnecessary output in my logs. The ID is enough to find the person we are processing.
Upvotes: 1
Views: 1093
Reputation:
Use the reflect package to write a "generic" function for extracting the fields:
func extractField(s interface{}, name string) []interface{} {
var result []interface{}
v := reflect.ValueOf(s)
for i := 0; i < v.Len(); i++ {
result = append(result, reflect.Indirect(v.Index(i)).FieldByName(name).Interface())
}
return result
}
Use it like this:
logger.Debugf("personProcessor starting for people: %v", extractField(persons, "ID"))
Run an example on the playground
The function can be extended to included formatting bells and whistles:
// sprintFields prints the field name in slice of struct s
// using the specified format.
func sprintFields(s interface{}, name string, sep string, format string) string {
var fields []string
v := reflect.ValueOf(s)
for i := 0; i < v.Len(); i++ {
fields = append(fields,
fmt.Sprintf(format,
reflect.Indirect(v.Index(i)).FieldByName(name).Interface()))
}
return strings.Join(fields, sep)
}
Upvotes: 2
Reputation: 8405
See this function that uses reflect and string building:
func printPersonField(persons []Person, field string) {
buf := bytes.NewBuffer(nil)
fmt.Fprintf(buf, "Persons (%s): [", field)
for i := range persons {
if i > 0 {
fmt.Fprint(buf, ", ")
}
fmt.Fprint(buf, reflect.ValueOf(persons[i]).FieldByName(field).Interface())
}
fmt.Fprint(buf, "]")
log.Println(buf.String())
}
Usage like so:
func main() {
persons := []Person{
{1, "first1", "last1"},
{2, "first2", "last2"},
{3, "first3", "last3"},
}
printPersonField(persons, "ID")
printPersonField(persons, "FirstName")
printPersonField(persons, "LastName")
}
Output:
2009/11/10 23:00:00 Persons (ID): [1, 2, 3]
2009/11/10 23:00:00 Persons (FirstName): [first1, first2, first3]
2009/11/10 23:00:00 Persons (LastName): [last1, last2, last3]
Note that this isn't "generic", as in it won't work for any type. It strictly takes a []Person
input. If you want to make it work for other types of slices of other structs, then see I Love Reflection's answer.
Upvotes: 0
Reputation: 1
I am not sure about Logrus, but this works with the standard library:
package main
import (
"fmt"
"strconv"
)
type Person struct {
ID int
FirstName, LastName string
}
func (p Person) String() string {
return strconv.Itoa(p.ID)
}
func main() {
p := Person{9, "First", "Last"}
fmt.Println(p) // 9
}
Upvotes: 0