@@ -20,7 +20,13 @@ import (
2020 "golang.org/x/exp/slices"
2121)
2222
23- const timeRE = `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}(Z|[+-]\d{2}:\d{2})`
23+ // textTimeRE is a regexp to match log timestamps for Text handler.
24+ // This is RFC3339Nano with the fixed 3 digit sub-second precision.
25+ const textTimeRE = `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}(Z|[+-]\d{2}:\d{2})`
26+
27+ // jsonTimeRE is a regexp to match log timestamps for Text handler.
28+ // This is RFC3339Nano with an arbitrary sub-second precision.
29+ const jsonTimeRE = `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})`
2430
2531func TestLogTextHandler (t * testing.T ) {
2632 var buf bytes.Buffer
@@ -30,7 +36,7 @@ func TestLogTextHandler(t *testing.T) {
3036 check := func (want string ) {
3137 t .Helper ()
3238 if want != "" {
33- want = "time=" + timeRE + " " + want
39+ want = "time=" + textTimeRE + " " + want
3440 }
3541 checkLogOutput (t , buf .String (), want )
3642 buf .Reset ()
@@ -112,7 +118,7 @@ func TestConnections(t *testing.T) {
112118 // log.Logger's output goes through the handler.
113119 SetDefault (New (NewTextHandler (& slogbuf , & HandlerOptions {AddSource : true })))
114120 log .Print ("msg2" )
115- checkLogOutput (t , slogbuf .String (), "time=" + timeRE + ` level=INFO source=.*logger_test.go:\d{3} msg=msg2` )
121+ checkLogOutput (t , slogbuf .String (), "time=" + textTimeRE + ` level=INFO source=.*logger_test.go:\d{3} msg=msg2` )
116122
117123 // The default log.Logger always outputs at Info level.
118124 slogbuf .Reset ()
@@ -371,7 +377,7 @@ func TestNewLogLogger(t *testing.T) {
371377 h := NewTextHandler (& buf , nil )
372378 ll := NewLogLogger (h , LevelWarn )
373379 ll .Print ("hello" )
374- checkLogOutput (t , buf .String (), "time=" + timeRE + ` level=WARN msg=hello` )
380+ checkLogOutput (t , buf .String (), "time=" + textTimeRE + ` level=WARN msg=hello` )
375381}
376382
377383func checkLogOutput (t * testing.T , got , wantRegexp string ) {
@@ -507,3 +513,64 @@ func callerPC(depth int) uintptr {
507513 runtime .Callers (depth , pcs [:])
508514 return pcs [0 ]
509515}
516+
517+ // panicTextAndJsonMarshaler is a type that panics in MarshalText and MarshalJSON.
518+ type panicTextAndJsonMarshaler struct {
519+ msg any
520+ }
521+
522+ func (p panicTextAndJsonMarshaler ) MarshalText () ([]byte , error ) {
523+ panic (p .msg )
524+ }
525+
526+ func (p panicTextAndJsonMarshaler ) MarshalJSON () ([]byte , error ) {
527+ panic (p .msg )
528+ }
529+
530+ func TestPanics (t * testing.T ) {
531+ // Revert any changes to the default logger. This is important because other
532+ // tests might change the default logger using SetDefault. Also ensure we
533+ // restore the default logger at the end of the test.
534+ currentLogger := Default ()
535+ currentLogWriter := log .Writer ()
536+ currentLogFlags := log .Flags ()
537+ t .Cleanup (func () {
538+ SetDefault (currentLogger )
539+ log .SetOutput (currentLogWriter )
540+ log .SetFlags (currentLogFlags )
541+ })
542+
543+ var logBuf bytes.Buffer
544+ log .SetOutput (& logBuf )
545+ log .SetFlags (log .Lshortfile &^ log .LstdFlags )
546+
547+ SetDefault (New (newDefaultHandler (log .Output )))
548+ for _ , pt := range []struct {
549+ in any
550+ out string
551+ }{
552+ {(* panicTextAndJsonMarshaler )(nil ), `logger_test.go:\d+: INFO msg p=<nil>` },
553+ {panicTextAndJsonMarshaler {io .ErrUnexpectedEOF }, `logger_test.go:\d+: INFO msg p="!PANIC: unexpected EOF"` },
554+ {panicTextAndJsonMarshaler {"panicking" }, `logger_test.go:\d+: INFO msg p="!PANIC: panicking"` },
555+ {panicTextAndJsonMarshaler {42 }, `logger_test.go:\d+: INFO msg p="!PANIC: 42"` },
556+ } {
557+ Info ("msg" , "p" , pt .in )
558+ checkLogOutput (t , logBuf .String (), pt .out )
559+ logBuf .Reset ()
560+ }
561+
562+ SetDefault (New (NewJSONHandler (& logBuf , nil )))
563+ for _ , pt := range []struct {
564+ in any
565+ out string
566+ }{
567+ {(* panicTextAndJsonMarshaler )(nil ), `{"time":"` + jsonTimeRE + `","level":"INFO","msg":"msg","p":null}` },
568+ {panicTextAndJsonMarshaler {io .ErrUnexpectedEOF }, `{"time":"` + jsonTimeRE + `","level":"INFO","msg":"msg","p":"!PANIC: unexpected EOF"}` },
569+ {panicTextAndJsonMarshaler {"panicking" }, `{"time":"` + jsonTimeRE + `","level":"INFO","msg":"msg","p":"!PANIC: panicking"}` },
570+ {panicTextAndJsonMarshaler {42 }, `{"time":"` + jsonTimeRE + `","level":"INFO","msg":"msg","p":"!PANIC: 42"}` },
571+ } {
572+ Info ("msg" , "p" , pt .in )
573+ checkLogOutput (t , logBuf .String (), pt .out )
574+ logBuf .Reset ()
575+ }
576+ }
0 commit comments