File size: 1,480 Bytes
6380833
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package server

import (
	"fmt"
	"log/slog"
	"net/http"
	"time"

	"github.com/go-chi/chi/v5/middleware"
	semconv "go.opentelemetry.io/otel/semconv/v1.27.0"
)

var (
	_ middleware.LogFormatter = (*RequestLogger)(nil)
	_ middleware.LogEntry     = (*RequestLoggerEntry)(nil)
)

func NewRequestLoggerMiddleware(handler slog.Handler) func(next http.Handler) http.Handler {
	return middleware.RequestLogger(&RequestLogger{Logger: handler})
}

type RequestLogger struct {
	Logger slog.Handler
}

func (l *RequestLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
	return &RequestLoggerEntry{logger: slog.New(l.Logger), request: r}
}

type RequestLoggerEntry struct {
	// logger is the underlying logger.
	logger *slog.Logger

	// request is the original request, stored for later use when writing the log entry.
	request *http.Request
}

func (e *RequestLoggerEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) {
	e.logger.LogAttrs(e.request.Context(), slog.LevelDebug, "request complete",
		slog.Int(string(semconv.HTTPResponseStatusCodeKey), status),
		slog.Int(string(semconv.HTTPResponseSizeKey), bytes),
		slog.Float64("http.response.duration_ms", float64(elapsed.Nanoseconds())/1000000.0),
	)
}

func (e *RequestLoggerEntry) Panic(v interface{}, stack []byte) {
	e.logger.LogAttrs(e.request.Context(), slog.LevelError, "request panicked",
		slog.String("stack", string(stack)),
		slog.String("panic", fmt.Sprintf("%+v", v)),
	)
}