We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Timestamps

This might seem obvious (and most default loggers do this), but always include timestamps in your logs.

Even if your logs are complete jank, timestamps at least let us do brute-force investigation. Take a look:

2023/10/01 12:34:57 INFO: User "alice" logged in
2023/10/01 12:34:57 INFO: Opening profile configuration for user "alice"
2023/10/01 12:34:57 ERROR: File not found

Each log entry alone isn't very useful, but the timestamps allow us to deduce that they're probably related, and that the "File not found" error likely relates to opening Alice's profile configuration file.

One exception is in automated tests. You may want to remove or overwrite timestamps for deterministic output.

Assignment

Write a test for requestLogger that verifies timestamped output.

  1. func Test_requestLogger(t *testing.T) {
    	logBuffer := &bytes.Buffer{}
    
    	logger := slog.New(slog.NewTextHandler(logBuffer, &slog.HandlerOptions{
    		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
    			if a.Key == slog.TimeKey {
    				return slog.Time(slog.TimeKey, time.Date(2023, 10, 1, 12, 34, 57, 0, time.UTC))
    			}
    			return a
    		},
    	}))
    
    	requestLoggerMiddleware := requestLogger(logger)
    	dummyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    	loggedHandler := requestLoggerMiddleware(dummyHandler)
    
    	req := httptest.NewRequest("GET", "http://lin.ko/api/stats", nil)
    	rr := httptest.NewRecorder()
    	loggedHandler.ServeHTTP(rr, req)
    
    	const expectedLogString = `time=2023-10-01T12:34:57.000Z level=INFO msg="Served request" method=GET path=/api/stats client_ip=192.0.2.1:1234` + "\n"
    	const expectedStatusCode = http.StatusOK
    
    	// replace the .Skip() call with two checks to verify the log string and status code here
    	// If either doesn't match, use t.Errorf to report the failure with a helpful message.
    	t.Skip()
    }
    

    Notice that we're using the httptest package to create a dummy HTTP request and response recorder. This is a cool way to "end-to-end" test an individual HTTP handler.

    • Compare logBuffer.String() to the expected log string.
    • Compare rr.Code to the expected status code.

Run and submit the CLI tests from the root of the Linko repo.