-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
611 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
This module contains helpers for the `log/slog` package. https://pkg.go.dev/log/slog | ||
|
||
Right now I am just copy-and-pasting helpers as I write/use them in my own | ||
projects. Please let me know if you are using this module yourself or if you | ||
have any suggestions for improvements. | ||
|
||
# Features per third party module/package | ||
|
||
## `context` (github.com/mbyio/sloghelpers/pkg/slogcontext) | ||
|
||
- a `slog.Handler` that wraps another `slog.Handler` and adds attributes from | ||
the context.Context to the log record. | ||
|
||
## `net/http` (github.com/mbyio/sloghelpers/pkg/net/sloghttp) | ||
|
||
- a `http.RoundTripper` that emits a log for each outbound HTTP request | ||
|
||
- a HTTP middleware that logs information about each inbound HTTP request | ||
|
||
## `github.com/jackc/pgx/v5` (github.com/mbyio/sloghelpers/pkg/github.com/jackc/pgx/v5/slogpgxv5) | ||
|
||
- a pgx query tracer that logs every SQL query, including the query text, duration, and error (if any) | ||
|
||
- query parameters are not logged for security reasons | ||
|
||
## `google.golang.org/grpc` (github.com/mbyio/sloghelpers/pkg/google.golang.org/sloggrpc | ||
|
||
- dial options to add logging to outbound GRPC requests | ||
|
||
- you can use this to add logging to GCP client libraries |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
module github.com/mbyio/sloghelpers | ||
|
||
go 1.21.4 | ||
|
||
require ( | ||
github.com/google/go-cmp v0.6.0 | ||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 | ||
github.com/jackc/pgx/v5 v5.5.0 | ||
go.uber.org/mock v0.3.0 | ||
google.golang.org/grpc v1.53.0 | ||
) | ||
|
||
require ( | ||
github.com/golang/protobuf v1.5.2 // indirect | ||
github.com/jackc/pgpassfile v1.0.0 // indirect | ||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect | ||
golang.org/x/crypto v0.12.0 // indirect | ||
golang.org/x/net v0.14.0 // indirect | ||
golang.org/x/sys v0.11.0 // indirect | ||
golang.org/x/text v0.12.0 // indirect | ||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect | ||
google.golang.org/protobuf v1.31.0 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= | ||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 h1:HcUWd006luQPljE73d5sk+/VgYPGUReEVz2y1/qylwY= | ||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4= | ||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= | ||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= | ||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= | ||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= | ||
github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw= | ||
github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= | ||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= | ||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= | ||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= | ||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= | ||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= | ||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= | ||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= | ||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= | ||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= | ||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= | ||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= | ||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= | ||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= | ||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= | ||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= | ||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package slogpgxv5querytracer | ||
|
||
import ( | ||
"context" | ||
"log/slog" | ||
"time" | ||
|
||
"github.com/jackc/pgx/v5" | ||
) | ||
|
||
type SlogPgxQueryTracer struct { | ||
logger *slog.Logger | ||
} | ||
|
||
func New(logger *slog.Logger) *SlogPgxQueryTracer { | ||
return &SlogPgxQueryTracer{ | ||
logger: logger, | ||
} | ||
} | ||
|
||
type startDataKeyType struct{} | ||
|
||
var startDataKey startDataKeyType | ||
|
||
type startData struct { | ||
startAt time.Time | ||
sql string | ||
} | ||
|
||
func (s *SlogPgxQueryTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context { | ||
// Record data in the context. We'll log it when the query ends. | ||
return context.WithValue(ctx, startDataKey, &startData{ | ||
startAt: time.Now(), | ||
sql: data.SQL, | ||
}) | ||
} | ||
|
||
func (s *SlogPgxQueryTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) { | ||
sd := ctx.Value(startDataKey).(*startData) | ||
endAt := time.Now() | ||
s.logger.DebugContext(ctx, "sql query", | ||
"query", sd.sql, | ||
"startAt", sd.startAt, | ||
"endAt", endAt, | ||
"duration", endAt.Sub(sd.startAt), | ||
"error", data.Err, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package sloggrpc | ||
|
||
import ( | ||
"context" | ||
"log/slog" | ||
|
||
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" | ||
"google.golang.org/grpc" | ||
) | ||
|
||
// interceptorLogger adapts slog logger to interceptor logger. | ||
// This code is simple enough to be copied and not imported. | ||
// | ||
// Copied from https://github.com/grpc-ecosystem/go-grpc-middleware/blob/main/interceptors/logging/examples/slog/example_test.go | ||
func interceptorLogger(l *slog.Logger) logging.Logger { | ||
return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) { | ||
l.Log(ctx, slog.Level(lvl), msg, fields...) | ||
}) | ||
} | ||
|
||
func UnaryDialOption(logger *slog.Logger) grpc.DialOption { | ||
opts := []logging.Option{ | ||
logging.WithLogOnEvents(logging.FinishCall), | ||
} | ||
return grpc.WithUnaryInterceptor(logging.UnaryClientInterceptor(interceptorLogger(logger), opts...)) | ||
} | ||
|
||
func StreamDialOption(logger *slog.Logger) grpc.DialOption { | ||
opts := []logging.Option{ | ||
logging.WithLogOnEvents(logging.FinishCall), | ||
} | ||
return grpc.WithStreamInterceptor(logging.StreamClientInterceptor(interceptorLogger(logger), opts...)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package sloghttp | ||
|
||
import ( | ||
"log/slog" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
type slogRoundTripper struct { | ||
logger *slog.Logger | ||
rt http.RoundTripper | ||
} | ||
|
||
func (srt *slogRoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) { | ||
startAt := time.Now() | ||
defer func() { | ||
endAt := time.Now() | ||
var statusCode int | ||
if resp != nil { | ||
statusCode = resp.StatusCode | ||
} | ||
// TODO record request and response body if available | ||
// TODO consider reporting at TRACE or DEBUG level | ||
srt.logger.DebugContext( | ||
req.Context(), | ||
"outbound HTTP request", | ||
"method", req.Method, | ||
"url", req.URL.String(), | ||
"status_code", statusCode, | ||
"duration", endAt.Sub(startAt), | ||
"error", err, | ||
) | ||
}() | ||
return srt.rt.RoundTrip(req) | ||
} | ||
|
||
// NewRoundTripper returns a new http.RoundTripper that wraps another RoundTripper and logs | ||
// requests and responses. | ||
func NewRoundTripper(logger *slog.Logger, rt http.RoundTripper) http.RoundTripper { | ||
return &slogRoundTripper{ | ||
logger: logger, | ||
rt: rt, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package slogcontexthandler | ||
|
||
//go:generate go run go.uber.org/mock/[email protected] -destination mock_handler_test.go -package slogcontexthandler log/slog Handler |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.