Skip to content

Commit 8c53766

Browse files
authored
Simplified logging middleware; Fields are now "any" type; Moved logging providers to examples only. (#552)
Fixes #517 * Simpler interface, only one method. This allows adapter to be ultra simple, so moved them to examples only (!). * Fields are string, values are any. * "More" slog compatibility. I did not go for using slog natively as it's still experimental (see bigger discussion: https://gophers.slack.com/archives/C029RQSEE/p1679860885943619) Signed-off-by: bwplotka <bwplotka@gmail.com>
1 parent e41e6bd commit 8c53766

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1116
-4426
lines changed

Makefile

+6-5
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,18 @@ lint: ## Runs various static analysis tools against our code.
8585
lint: $(BUF) $(COPYRIGHT) fmt docs
8686
@echo ">> lint proto files"
8787
@$(BUF) lint
88-
88+
89+
@echo ">> ensuring copyright headers"
90+
@$(COPYRIGHT) $(shell go list -f "{{.Dir}}" ./... | xargs -i find "{}" -name "*.go")
91+
@$(call require_clean_work_tree,"set copyright headers")
92+
@echo ">> ensured all .go files have copyright headers"
93+
8994
@echo "Running lint for all modules: $(MODULES)"
9095
@$(call require_clean_work_tree,"before lint")
9196
for dir in $(MODULES) ; do \
9297
$(MAKE) lint_module DIR=$${dir} ; \
9398
done
9499
@$(call require_clean_work_tree,"lint and format files")
95-
@echo ">> ensuring copyright headers"
96-
@$(COPYRIGHT) $(shell go list -f "{{.Dir}}" ./... | xargs -i find "{}" -name "*.go")
97-
@$(call require_clean_work_tree,"set copyright headers")
98-
@echo ">> ensured all .go files have copyright headers"
99100

100101
.PHONY: lint_module
101102
# PROTIP:

README.md

+10-17
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,23 @@ This repository offers ready-to-use middlewares that implements gRPC interceptor
1616
1717
Additional great feature of interceptors is the fact we can chain those. For example below you can find example server side chain of interceptors with full observabiliy correlation, auth and panic recovery:
1818

19-
```go mdox-exec="sed -n '116,132p' examples/server/main.go"
19+
```go mdox-exec="sed -n '136,151p' examples/server/main.go"
2020
grpcSrv := grpc.NewServer(
2121
grpc.ChainUnaryInterceptor(
2222
// Order matters e.g. tracing interceptor have to create span first for the later exemplars to work.
2323
otelgrpc.UnaryServerInterceptor(),
2424
srvMetrics.UnaryServerInterceptor(grpcprom.WithExemplarFromContext(exemplarFromContext)),
25-
logging.UnaryServerInterceptor(kit.InterceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID)),
25+
logging.UnaryServerInterceptor(interceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID)),
2626
selector.UnaryServerInterceptor(auth.UnaryServerInterceptor(authFn), selector.MatchFunc(allButHealthZ)),
2727
recovery.UnaryServerInterceptor(recovery.WithRecoveryHandler(grpcPanicRecoveryHandler)),
2828
),
2929
grpc.ChainStreamInterceptor(
3030
otelgrpc.StreamServerInterceptor(),
3131
srvMetrics.StreamServerInterceptor(grpcprom.WithExemplarFromContext(exemplarFromContext)),
32-
logging.StreamServerInterceptor(kit.InterceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID)),
32+
logging.StreamServerInterceptor(interceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID)),
3333
selector.StreamServerInterceptor(auth.StreamServerInterceptor(authFn), selector.MatchFunc(allButHealthZ)),
3434
recovery.StreamServerInterceptor(recovery.WithRecoveryHandler(grpcPanicRecoveryHandler)),
3535
),
36-
)
3736
```
3837

3938
This pattern offers clean and explicit shared functionality for all your gRPC methods. Full, buildable examples can be found in [examples](examples) directory.
@@ -48,15 +47,8 @@ All paths should work with `go get <path>`.
4847
* [`github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth`](interceptors/auth) - a customizable (via `AuthFunc`) piece of auth middleware.
4948

5049
#### Observability
51-
* Metrics:
52-
* [`github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus`](providers/prometheus) - Prometheus client-side and server-side monitoring middleware. Supports exemplars. Moved from deprecated now [`go-grpc-prometheus`](https://github.com/grpc-ecosystem/go-grpc-prometheus).
53-
* [`github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging`](interceptors/logging) - a customizable logging middleware offering extended per request logging. It requires logging provider. Available ones:
54-
* [`github.com/grpc-ecosystem/go-grpc-middleware/providers/logr`](providers/logr) - adapter for [logr](https://github.com/go-logr/logr).
55-
* [`github.com/grpc-ecosystem/go-grpc-middleware/providers/logrus`](providers/logrus) - adapter for [logrus](https://github.com/sirupsen/logrus).
56-
* [`github.com/grpc-ecosystem/go-grpc-middleware/providers/kit`](providers/kit) - adapter for [go-kit/log](https://github.com/go-kit/log).
57-
* [`github.com/grpc-ecosystem/go-grpc-middleware/providers/phuslog`](providers/phuslog) - adapter for [phuslog](https://github.com/phuslu/log)
58-
* [`github.com/grpc-ecosystem/go-grpc-middleware/providers/zap`](providers/zap) - adapter for [zap](https://github.com/uber-go/zap).
59-
* [`github.com/grpc-ecosystem/go-grpc-middleware/providers/zerolog`](providers/zerolog) - adapter for [zerolog](https://github.com/rs/zerolog).
50+
* Metrics with [`github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus`](providers/prometheus) - Prometheus client-side and server-side monitoring middleware. Supports exemplars. Moved from deprecated now [`go-grpc-prometheus`](https://github.com/grpc-ecosystem/go-grpc-prometheus). It's a separate module, so core module has limited number of dependencies.
51+
* Logging with [`github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging`](interceptors/logging) - a customizable logging middleware offering extended per request logging. It requires logging adapter, see examples in [`interceptors/logging/examples`](interceptors/logging/examples) for `go-kit`, `log`, `logr`, `logrus`, `slog`, `zap` and `zerolog`.
6052
* Tracing:
6153
* (external) [`go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`](https://go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc) - official OpenTelemetry tracing interceptors as used in [example](examples).
6254
* (external) [`github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing`](https://pkg.go.dev/github.com/grpc-ecosystem/go-grpc-middleware@v1.4.0/tracing/opentracing) - deprecated [OpenTracing](http://opentracing.io/) client-side and server-side interceptors if you still need it!
@@ -79,9 +71,9 @@ All paths should work with `go get <path>`.
7971

8072
## Structure of this repository
8173

82-
The main interceptors are available in the subdirectories of the [`interceptors` directory](interceptors) e.g. [`interceptors/validator`](interceptors/validator). Some interceptors work as a standalone piece of middleware with extra parameters e.g. [`auth`](interceptors/auth). Some, like [`interceptors/logging`](interceptors/logging) benefit from adapters maintained as a *separate Go Modules* in [`providers`](providers) directory. For example, there is an adapter for `go-kit/log` logger in [`providers/kit`](providers/kit) directory that works with logging e.g. `logging.StreamServerInterceptor(kit.InterceptorLogger(rpcLogger))`. The separate module, might be a little bit of the initial pain to discover and version in your `go.mod`, but it allows core interceptors to be ultra slim in terms of dependencies.
74+
The main interceptors are available in the subdirectories of the [`interceptors` directory](interceptors) e.g. [`interceptors/validator`](interceptors/validator), [`interceptors/auth`](interceptors/auth) or [`interceptors/logging`](interceptors/logging).
8375

84-
Some providers are standalone interceptors too. For example [`providers/prometheus`](providers/prometheus) offer metrics middleware (there is no "interceptor/metrics" at the moment).
76+
Some interceptors or utilities of interceptors requires opinionated code that depends on larger amount of dependencies. Those are places in `providers` directory as separate Go module, with separate versioning. For example [`providers/prometheus`](providers/prometheus) offer metrics middleware (there is no "interceptor/metrics" at the moment). The separate module, might be a little bit harder to discover and version in your `go.mod`, but it allows core interceptors to be ultra slim in terms of dependencies.
8577

8678
The [`interceptors` directory](interceptors) also holds generic interceptors that accepts [`Reporter`](interceptors/reporter.go) interface which allows creating your own middlewares with ease.
8779

@@ -101,13 +93,14 @@ go get github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus
10193

10294
[go-grpc-middleware v1](https://pkg.go.dev/github.com/grpc-ecosystem/go-grpc-middleware) was created near 2015 and became a popular choice for gRPC users. However, many have changed since then. The main changes of v2 compared to v1:
10395

104-
* Multiple, separate Go modules in "providers" e.g. loggers, so interceptors can be extended without the dependency hell to the core framework (e.g. if use go-kit logger, do you really want to import logrus?). This allows greater extensibility.
96+
* Path for separate, multiple Go modules in "providers". This allows to add in future specific providers for certain middlewares if needed. This allows interceptors to be extended without the dependency hell to the core framework (e.g. if use some other metric provider, do you want to import prometheus?). This allows greater extensibility.
97+
* Loggers are removed. The [`interceptors/logging`](interceptors/logging) got simplified and writing adapter for each logger is straightforward. For convenience, we will maintain examples for popular providers in [`interceptors/logging/examples`](interceptors/logging/examples), but those are meant to be copied, not imported.
10598
* `grpc_opentracing` interceptor was removed. This is because tracing instrumentation evolved. OpenTracing is deprecated and OpenTelemetry has now a [superior tracing interceptor](https://go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc).
10699
* `grpc_ctxtags` interceptor was removed. Custom tags can be added to logging fields using `logging.InjectFields`. Proto option to add logging field was clunky in practice and we don't see any use of it nowadays, so it's removed.
107100
* One of the most powerful interceptor was imported from https://github.com/grpc-ecosystem/go-grpc-prometheus (repo is now deprecated). This consolidation allows easier maintenance, easier use and consistent API.
108101
* Chain interceptors was removed, because `grpc` implemented one.
109102
* Moved to the new proto API (google.golang.org/protobuf).
110-
* All "deciders", so functions that decide what to do based on gRPC service name and method (aka "fullMethodName") now use typed ["interceptors.CallMeta"](interceptors/callmeta.go) allowing better context and explicitness.
103+
* All "deciders", so functions that decide what to do based on gRPC service name and method (aka "fullMethodName") are removed (!). Use [`github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/selector`](interceptors/selector) interceptor to select what method, type or service should use what interceptor.
111104
* No more snake case package names. We have now single word meaningful package names. If you have collision in package names we recommend adding grpc prefix e.g. `grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"`.
112105
* All the options (if any) are in the form of `<package_name>.With<Option Name>`, with extensibility to add more of them.
113106
* `v2` is the main (default) development branch.

examples/client/main.go

+22-3
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ package main
55

66
import (
77
"context"
8+
"fmt"
89
"net/http"
910
"os"
1011
"syscall"
1112
"time"
1213

1314
"github.com/go-kit/log"
1415
"github.com/go-kit/log/level"
15-
"github.com/grpc-ecosystem/go-grpc-middleware/providers/kit"
1616
grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
1717
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
1818
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/timeout"
@@ -38,6 +38,25 @@ const (
3838
targetGRPCAddr = "localhost:8080"
3939
)
4040

41+
// interceptorLogger adapts go-kit logger to interceptor logger.
42+
// This code is simple enough to be copied and not imported.
43+
func interceptorLogger(l log.Logger) logging.Logger {
44+
return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) {
45+
largs := append([]any{"msg", msg}, fields...)
46+
switch lvl {
47+
case logging.LevelDebug:
48+
_ = level.Debug(l).Log(largs...)
49+
case logging.LevelInfo:
50+
_ = level.Info(l).Log(largs...)
51+
case logging.LevelWarn:
52+
_ = level.Warn(l).Log(largs...)
53+
case logging.LevelError:
54+
_ = level.Error(l).Log(largs...)
55+
default:
56+
panic(fmt.Sprintf("unknown level %v", lvl))
57+
}
58+
})
59+
}
4160
func main() {
4261
// Setup logging.
4362
logger := log.NewLogfmtLogger(os.Stderr)
@@ -85,11 +104,11 @@ func main() {
85104
timeout.UnaryClientInterceptor(500*time.Millisecond),
86105
otelgrpc.UnaryClientInterceptor(),
87106
clMetrics.UnaryClientInterceptor(grpcprom.WithExemplarFromContext(exemplarFromContext)),
88-
logging.UnaryClientInterceptor(kit.InterceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID))),
107+
logging.UnaryClientInterceptor(interceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID))),
89108
grpc.WithChainStreamInterceptor(
90109
otelgrpc.StreamClientInterceptor(),
91110
clMetrics.StreamClientInterceptor(grpcprom.WithExemplarFromContext(exemplarFromContext)),
92-
logging.StreamClientInterceptor(kit.InterceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID))),
111+
logging.StreamClientInterceptor(interceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID))),
93112
)
94113
if err != nil {
95114
level.Error(logger).Log("err", err)

examples/go.mod

-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ go 1.19
44

55
require (
66
github.com/go-kit/log v0.2.1
7-
github.com/grpc-ecosystem/go-grpc-middleware/providers/kit v1.0.0
87
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0
98
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0
109
github.com/oklog/run v1.1.0
@@ -44,7 +43,6 @@ require (
4443
)
4544

4645
replace (
47-
github.com/grpc-ecosystem/go-grpc-middleware/providers/kit => ../providers/kit
4846
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus => ../providers/prometheus
4947
github.com/grpc-ecosystem/go-grpc-middleware/v2 => ../
5048
)

examples/server/main.go

+23-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package main
55

66
import (
77
"context"
8+
"fmt"
89
"net"
910
"net/http"
1011
"os"
@@ -13,7 +14,6 @@ import (
1314

1415
"github.com/go-kit/log"
1516
"github.com/go-kit/log/level"
16-
"github.com/grpc-ecosystem/go-grpc-middleware/providers/kit"
1717
grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
1818
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
1919
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
@@ -43,6 +43,26 @@ const (
4343
httpAddr = ":8081"
4444
)
4545

46+
// interceptorLogger adapts go-kit logger to interceptor logger.
47+
// This code is simple enough to be copied and not imported.
48+
func interceptorLogger(l log.Logger) logging.Logger {
49+
return logging.LoggerFunc(func(_ context.Context, lvl logging.Level, msg string, fields ...any) {
50+
largs := append([]any{"msg", msg}, fields...)
51+
switch lvl {
52+
case logging.LevelDebug:
53+
_ = level.Debug(l).Log(largs...)
54+
case logging.LevelInfo:
55+
_ = level.Info(l).Log(largs...)
56+
case logging.LevelWarn:
57+
_ = level.Warn(l).Log(largs...)
58+
case logging.LevelError:
59+
_ = level.Error(l).Log(largs...)
60+
default:
61+
panic(fmt.Sprintf("unknown level %v", lvl))
62+
}
63+
})
64+
}
65+
4666
func main() {
4767
// Setup logging.
4868
logger := log.NewLogfmtLogger(os.Stderr)
@@ -118,14 +138,14 @@ func main() {
118138
// Order matters e.g. tracing interceptor have to create span first for the later exemplars to work.
119139
otelgrpc.UnaryServerInterceptor(),
120140
srvMetrics.UnaryServerInterceptor(grpcprom.WithExemplarFromContext(exemplarFromContext)),
121-
logging.UnaryServerInterceptor(kit.InterceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID)),
141+
logging.UnaryServerInterceptor(interceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID)),
122142
selector.UnaryServerInterceptor(auth.UnaryServerInterceptor(authFn), selector.MatchFunc(allButHealthZ)),
123143
recovery.UnaryServerInterceptor(recovery.WithRecoveryHandler(grpcPanicRecoveryHandler)),
124144
),
125145
grpc.ChainStreamInterceptor(
126146
otelgrpc.StreamServerInterceptor(),
127147
srvMetrics.StreamServerInterceptor(grpcprom.WithExemplarFromContext(exemplarFromContext)),
128-
logging.StreamServerInterceptor(kit.InterceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID)),
148+
logging.StreamServerInterceptor(interceptorLogger(rpcLogger), logging.WithFieldsFromContext(logTraceID)),
129149
selector.StreamServerInterceptor(auth.StreamServerInterceptor(authFn), selector.MatchFunc(allButHealthZ)),
130150
recovery.StreamServerInterceptor(recovery.WithRecoveryHandler(grpcPanicRecoveryHandler)),
131151
),

interceptors/logging/doc.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ https://github.com/opentracing/specification/blob/master/semantic_conventions.md
2525
2626
Implementations:
2727
28+
* providers/kit
29+
* providers/logr
2830
* providers/logrus
31+
* providers/phuslog
32+
* providers/slog
2933
* providers/zap
30-
* providers/kit
3134
* providers/zerolog
32-
* providers/phuslog
33-
* providers/logr
3435
*/
3536
package logging

interceptors/logging/examples/go.mod

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module github.com/grpc-ecosystem/go-grpc-middleware/interceptors/logging/examples
2+
3+
go 1.19
4+
5+
require (
6+
github.com/go-kit/log v0.2.1
7+
github.com/go-logr/logr v1.2.4
8+
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0
9+
github.com/phuslu/log v1.0.83
10+
github.com/rs/zerolog v1.29.0
11+
github.com/sirupsen/logrus v1.9.0
12+
go.uber.org/zap v1.24.0
13+
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
14+
google.golang.org/grpc v1.53.0
15+
k8s.io/klog/v2 v2.90.1
16+
)
17+
18+
require (
19+
github.com/go-logfmt/logfmt v0.5.1 // indirect
20+
github.com/golang/protobuf v1.5.2 // indirect
21+
github.com/mattn/go-colorable v0.1.12 // indirect
22+
github.com/mattn/go-isatty v0.0.14 // indirect
23+
go.uber.org/atomic v1.7.0 // indirect
24+
go.uber.org/multierr v1.6.0 // indirect
25+
golang.org/x/net v0.8.0 // indirect
26+
golang.org/x/sys v0.6.0 // indirect
27+
golang.org/x/text v0.8.0 // indirect
28+
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
29+
google.golang.org/protobuf v1.30.0 // indirect
30+
)
31+
32+
replace github.com/grpc-ecosystem/go-grpc-middleware/v2 => ../../../

0 commit comments

Comments
 (0)