@@ -22,6 +22,7 @@ import (
2222 "os"
2323 "strings"
2424 "testing"
25+ "time"
2526
2627 "github.com/stretchr/testify/assert"
2728 "github.com/stretchr/testify/require"
@@ -44,7 +45,7 @@ func TestIsWriterTTY(t *testing.T) {
4445func TestConsoleLogOutput (t * testing.T ) {
4546 t .Parallel ()
4647
47- testConsolLogOutput (t , func (logger * slog.Logger ) {
48+ testConsoleLogOutput (t , func (logger * slog.Logger ) {
4849 logger .Info ("hello" , slog .String ("a" , "b" ))
4950 logger .Info ("hello world" )
5051 }, []map [string ]any {{
@@ -56,7 +57,7 @@ func TestConsoleLogOutput(t *testing.T) {
5657 slog .MessageKey : "hello world" ,
5758 }}, withConsoleColor (true ))
5859
59- testConsolLogOutput (t , func (logger * slog.Logger ) {
60+ testConsoleLogOutput (t , func (logger * slog.Logger ) {
6061 logger .Info ("info" , slog .String ("a" , "b" ))
6162 logger .Error ("error" )
6263 }, []map [string ]any {{
@@ -68,7 +69,7 @@ func TestConsoleLogOutput(t *testing.T) {
6869 slog .MessageKey : "error" ,
6970 }})
7071
71- testConsolLogOutput (t , func (logger * slog.Logger ) {
72+ testConsoleLogOutput (t , func (logger * slog.Logger ) {
7273 logger = logger .With (slog .String ("a" , "b" ))
7374 logger = logger .WithGroup ("g" )
7475 logger .Error ("error message" , slog .String ("c" , "d" ))
@@ -87,7 +88,7 @@ func TestConsoleLogOutput(t *testing.T) {
8788 "a" : "b" ,
8889 }}, withConsoleColor (true ))
8990
90- testConsolLogOutput (t , func (logger * slog.Logger ) {
91+ testConsoleLogOutput (t , func (logger * slog.Logger ) {
9192 logger .Info ("key spaces" , slog .String ("a key" , "with spaces" ))
9293 }, []map [string ]any {{
9394 slog .LevelKey : colorize ("INFO" , getColor (slog .LevelInfo )),
@@ -96,7 +97,7 @@ func TestConsoleLogOutput(t *testing.T) {
9697 }}, withConsoleColor (true ))
9798}
9899
99- func testConsolLogOutput (t * testing.T , run func (logger * slog.Logger ), expects []map [string ]any , options ... consoleHandlerOption ) {
100+ func testConsoleLogOutput (t * testing.T , run func (logger * slog.Logger ), expects []map [string ]any , options ... consoleHandlerOption ) {
100101 t .Helper ()
101102 var buf bytes.Buffer
102103 consoleHandler := newConsoleHandler (& buf , slog .LevelInfo , options ... )
@@ -118,16 +119,31 @@ func testConsolLogOutput(t *testing.T, run func(logger *slog.Logger), expects []
118119 require .Equal (t , len (expects ), len (outputs ))
119120 for i := range len (outputs ) {
120121 output , expect := outputs [i ], expects [i ]
122+ // Verify the timestamp is present and parseable, then remove it before
123+ // comparing the rest of the output (timestamps are dynamic).
124+ ts , ok := output [slog .TimeKey ]
125+ if assert .True (t , ok , "expected timestamp in log output" ) {
126+ timestamp , ok := ts .(string )
127+ assert .True (t , ok , "expected timestamp to be string type in log output" )
128+ _ , err := time .Parse (time .RFC3339 , timestamp )
129+ assert .NoError (t , err , "timestamp should be valid RFC3339" )
130+ }
131+ delete (output , slog .TimeKey )
121132 assert .Equal (t , expect , output )
122133 }
123134}
124135
125- // testParseLogLine passes the output of a single log line.
136+ // testParseLogLine parses the output of a single log line.
137+ // The format is: TIME\tLEVEL\tMESSAGE\t{...JSON attrs...}\n
126138func testParseLogLine (lineBytes []byte ) (map [string ]any , error ) {
127139 top := map [string ]any {}
128140 line := string (bytes .TrimSpace (lineBytes ))
129- index , line , _ := strings .Cut (line , consoleSeparator )
130- top [slog .LevelKey ] = index
141+ // First field is the timestamp.
142+ timestamp , line , _ := strings .Cut (line , consoleSeparator )
143+ top [slog .TimeKey ] = timestamp
144+ // Second field is the level.
145+ level , line , _ := strings .Cut (line , consoleSeparator )
146+ top [slog .LevelKey ] = level
131147 if len (line ) == 0 {
132148 return top , nil
133149 }
0 commit comments