From de0e63848ea64679b8adc77f370851fc3a0a19e1 Mon Sep 17 00:00:00 2001 From: Jonathan Hurter Date: Mon, 30 Dec 2024 14:47:53 +0100 Subject: [PATCH] expermient(logs) Add support for the Loggable interface --- logger/loggable.go | 18 ++++++++++++++++-- logger/loggable_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/logger/loggable.go b/logger/loggable.go index 5fc3d26c..5022561c 100644 --- a/logger/loggable.go +++ b/logger/loggable.go @@ -8,10 +8,17 @@ import ( "github.com/sirupsen/logrus" ) +type Loggable interface { + ToLogrusFields() logrus.Fields +} + // FieldsFor extracts loggable fields from a struct based on the "log" tag. // It returns a logrus.Fields map where the keys are the tag values prefixed // with the provided prefix, and the values are the corresponding field values. // +// If the struct implements the Loggable interface. The `log` tags are ignored +// and the ToLogrusFields method is used to extract the fields. +// // If the struct has no fields with the "log" tag, it checks if the struct // implements the fmt.Stringer interface. If it does, it adds a single field // with the prefix as the key and the result of the String() method as the value. @@ -25,10 +32,17 @@ import ( // Returns: // - logrus.Fields: A map of loggable fields. func FieldsFor(value interface{}, prefix string) logrus.Fields { - val := reflect.ValueOf(value) - fields := logrus.Fields{} + if loggableValue, ok := value.(Loggable); ok { + for k, v := range loggableValue.ToLogrusFields() { + fields[fmt.Sprintf("%s_%s", prefix, k)] = v + } + return fields + } + + val := reflect.ValueOf(value) + for i := 0; i < val.NumField(); i++ { name, found := val.Type().Field(i).Tag.Lookup("log") if found { diff --git a/logger/loggable_test.go b/logger/loggable_test.go index 158f477a..f0d04416 100644 --- a/logger/loggable_test.go +++ b/logger/loggable_test.go @@ -13,6 +13,18 @@ type StructWithTags struct { Field3 string } +type StructWithTagsAndLoggable struct { + Field1 string `log:"field1"` + Field2 string `log:"field2"` + Field3 string +} + +func (s StructWithTagsAndLoggable) ToLogrusFields() logrus.Fields { + return logrus.Fields{ + "another": "test", + } +} + type StructWithoutTagsButWithStringer struct { Field1 string Field2 string @@ -46,6 +58,23 @@ func TestFieldsFor(t *testing.T) { }, fields) }) + t.Run("when the struct has some tags and implements Loggable", func(t *testing.T) { + // Given a struct with tags and that implements Loggable + s := StructWithTagsAndLoggable{ + Field1: "value1", + Field2: "value2", + Field3: "value3", + } + + // When we try to add it to a logger + fields := FieldsFor(s, "prefix") + + // Then it should be added as separate fields + assert.Equal(t, logrus.Fields{ + "prefix_another": "test", + }, fields) + }) + t.Run("when the struct has no tags but has a stringer", func(t *testing.T) { // Given a struct without tags but with a stringer s := StructWithoutTagsButWithStringer{