From c4c7d6412ed1d53db7c3113e29e760373599a16e Mon Sep 17 00:00:00 2001 From: Laurent Demailly Date: Wed, 3 Apr 2024 11:59:11 -0700 Subject: [PATCH] Handle errors printing like slog json marshaller (#60) * Handle errors printing like slog json marshaller: use .Error() unless explicit marshaller is found - see comments * remove the previous special {} handling and adapt the test accordingly * make linter happy --- logger.go | 21 ++++++++++++++++----- logger_test.go | 6 ++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/logger.go b/logger.go index 1489f60..2fcb8f5 100644 --- a/logger.go +++ b/logger.go @@ -637,11 +637,8 @@ func toJSON(v any) string { return strconv.Quote(fmt.Sprintf("ERR marshaling %v: %v", v, err)) } str := string(bytes) - // This is kinda hacky way to handle both structured and custom serialization errors, and - // struct with no public fields for which we need to call Error() to get a useful string. - if e, isError := v.(error); isError && str == "{}" { - return fmt.Sprintf("%q", e.Error()) - } + // We now handle errors before calling toJSON: if there is a marshaller we use it + // otherwise we use the string from .Error() return str } @@ -653,6 +650,20 @@ func (v ValueType[T]) String() string { return fmt.Sprint(s) case string: return fmt.Sprintf("%q", s) + case error: + // Sadly structured errors like nettwork error don't have the reason in + // the exposed struct/JSON - ie on gets + // {"Op":"read","Net":"tcp","Source":{"IP":"127.0.0.1","Port":60067,"Zone":""}, + // "Addr":{"IP":"127.0.0.1","Port":3000,"Zone":""},"Err":{}} + // instead of + // read tcp 127.0.0.1:60067->127.0.0.1:3000: i/o timeout + // Noticed in https://github.com/fortio/fortio/issues/913 + _, hasMarshaller := s.(json.Marshaler) + if hasMarshaller { + return toJSON(v.Val) + } else { + return fmt.Sprintf("%q", s.Error()) + } /* It's all handled by json fallback now even though slightly more expensive at runtime, it's a lot simpler */ default: return toJSON(v.Val) // was fmt.Sprintf("%q", fmt.Sprint(v.Val)) diff --git a/logger_test.go b/logger_test.go index d212960..d7c175e 100644 --- a/logger_test.go +++ b/logger_test.go @@ -757,10 +757,16 @@ type customError struct { Code int } +type customErrorAlias customError + func (e customError) Error() string { return fmt.Sprintf("custom error %s (code %d)", e.Msg, e.Code) } +func (e customError) MarshalJSON() ([]byte, error) { + return json.Marshal(customErrorAlias(e)) +} + func TestSerializationOfError(t *testing.T) { var err error kv := Any("err", err)