diff --git a/CHANGELOG.md b/CHANGELOG.md index 95f9ad0..c3d0bf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2.3.1 (2024-03-18) + +### Bug fixes + +* Handle empty pointers to complex structs in metadata.Add + [#221](https://github.com/bugsnag/bugsnag-go/pull/221) + ## 2.3.0 (2024-03-05) ### Bug fixes diff --git a/v2/bugsnag.go b/v2/bugsnag.go index b79f0d4..458f72a 100644 --- a/v2/bugsnag.go +++ b/v2/bugsnag.go @@ -21,7 +21,7 @@ import ( ) // Version defines the version of this Bugsnag notifier -const Version = "2.3.0" +const Version = "2.3.1" var panicHandlerOnce sync.Once var sessionTrackerOnce sync.Once diff --git a/v2/metadata.go b/v2/metadata.go index e5f6279..f812411 100644 --- a/v2/metadata.go +++ b/v2/metadata.go @@ -52,7 +52,7 @@ func (meta MetaData) AddStruct(tab string, obj interface{}) { meta[tab] = content } else { // Wasn't a struct - meta.Add("Extra data", tab, obj) + meta.Add("Extra data", tab, val) } } @@ -84,32 +84,38 @@ func (s sanitizer) Sanitize(data interface{}) interface{} { // Sanitizers are passed by value, so we can modify s and it only affects // s.Seen for nested calls. s.Seen = append(s.Seen, data) + t := reflect.TypeOf(data) + v := reflect.ValueOf(data) + + if t == nil { + return "" + } + + // Handle nil pointers and interfaces specifically + if t.Kind() == reflect.Interface || t.Kind() == reflect.Ptr { + if v.IsNil() { + return "" + } + } // Handle certain well known interfaces and types - switch data := data.(type) { + switch dataT := data.(type) { case error: - return data.Error() + return dataT.Error() case time.Time: - return data.Format(time.RFC3339Nano) + return dataT.Format(time.RFC3339Nano) case fmt.Stringer: // This also covers time.Duration - return data.String() + return dataT.String() case encoding.TextMarshaler: - if b, err := data.MarshalText(); err == nil { + if b, err := dataT.MarshalText(); err == nil { return string(b) } } - t := reflect.TypeOf(data) - v := reflect.ValueOf(data) - - if t == nil { - return "" - } - switch t.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, @@ -121,9 +127,6 @@ func (s sanitizer) Sanitize(data interface{}) interface{} { return data case reflect.Interface, reflect.Ptr: - if v.IsNil() { - return "" - } return s.Sanitize(v.Elem().Interface()) case reflect.Array, reflect.Slice: diff --git a/v2/metadata_test.go b/v2/metadata_test.go index 112ad6a..d67d56e 100644 --- a/v2/metadata_test.go +++ b/v2/metadata_test.go @@ -34,6 +34,22 @@ func (_textMarshaller) MarshalText() ([]byte, error) { return []byte("marshalled text"), nil } +type _testStringer struct{} + +func (s _testStringer) String() string { + return "something" +} + +type _testError struct{} + +func (s _testError) Error() string { + return "errorstr" +} + +type _testStruct struct { + Name *_testStringer +} + var account = _account{} var notifier = New(Configuration{}) @@ -77,6 +93,70 @@ func TestMetaDataAdd(t *testing.T) { } } +func TestMetadataAddPointer(t *testing.T) { + var pointer *_testStringer + md := MetaData{} + md.AddStruct("emptypointer", pointer) + fullPointer := &_testStringer{} + md.AddStruct("fullpointer", fullPointer) + + if !reflect.DeepEqual(md, MetaData{ + "Extra data": { + "emptypointer": "", + "fullpointer": "something", + }, + }) { + t.Errorf("metadata.AddStruct didn't work: %#v", md) + } +} + +func TestMetadataAddNil(t *testing.T) { + md := MetaData{} + md.AddStruct("map", map[string]interface{}{ + "data": _testStruct{Name: nil}, + }) + + var nilMap map[string]interface{} + md.AddStruct("nilmap", nilMap) + + var nilError _testError + md.AddStruct("error", nilError) + + var nilErrorPtr *_testError + md.AddStruct("errorNilPtr", nilErrorPtr) + + var timeVar time.Time + md.AddStruct("timeUnset", timeVar) + + var duration time.Duration + md.AddStruct("durationUnset", duration) + + var marshalNilPtr *_textMarshaller + md.AddStruct("marshalNilPtr", marshalNilPtr) + + var marshalFullPtr = &_textMarshaller{} + md.AddStruct("marshalFullPtr", marshalFullPtr) + + if !reflect.DeepEqual(md, MetaData{ + "map": { + "data": map[string]interface{}{ + "Name": "", + }, + }, + "nilmap": map[string]interface{}{}, + "Extra data": { + "error": "errorstr", + "errorNilPtr": "", + "timeUnset": "0001-01-01T00:00:00Z", + "durationUnset": "0s", + "marshalFullPtr": "marshalled text", + "marshalNilPtr": "", + }, + }) { + t.Errorf("metadata.AddStruct didn't work: %#v", md) + } +} + func TestMetaDataUpdate(t *testing.T) { m := MetaData{