Skip to content

Commit 3a3f597

Browse files
committed
fix: Add interceptor to log GRPC calls
Signed-off-by: Marcelo E. Magallon <marcelo.magallon@grafana.com>
1 parent 739f6e3 commit 3a3f597

File tree

4 files changed

+65
-9
lines changed

4 files changed

+65
-9
lines changed

cmd/synthetic-monitoring-agent/grpc.go

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,84 @@ package main
33
import (
44
"context"
55
"crypto/tls"
6+
"strconv"
67
"strings"
8+
"time"
79

810
"github.com/grafana/synthetic-monitoring-agent/pkg/pb/synthetic_monitoring"
911
grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
12+
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
13+
"github.com/rs/zerolog"
1014
"google.golang.org/grpc"
1115
"google.golang.org/grpc/credentials"
1216
"google.golang.org/grpc/credentials/insecure"
1317
"google.golang.org/grpc/keepalive"
1418
)
1519

16-
func dialAPIServer(addr string, allowInsecure bool, apiToken string, grpcClientMetrics *grpcprom.ClientMetrics) (*grpc.ClientConn, error) {
20+
// interceptorLogger adapts zerolog.Logger to the grpc-middleware logging interface.
21+
// This allows structured logging of all gRPC client calls with proper log levels.
22+
func interceptorLogger(l zerolog.Logger) logging.Logger {
23+
return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) {
24+
l := l.With().Fields(fields).Logger()
25+
26+
switch lvl {
27+
case logging.LevelDebug:
28+
l.Debug().Msg(msg)
29+
case logging.LevelInfo:
30+
l.Info().Msg(msg)
31+
case logging.LevelWarn:
32+
l.Warn().Msg(msg)
33+
case logging.LevelError:
34+
l.Error().Msg(msg)
35+
default:
36+
l.Info().Msg(msg)
37+
}
38+
})
39+
}
40+
41+
func dialAPIServer(addr string, allowInsecure bool, apiToken string, logger zerolog.Logger, grpcClientMetrics *grpcprom.ClientMetrics) (*grpc.ClientConn, error) {
1742
apiCreds := creds{
1843
Token: apiToken,
1944
AllowInsecure: allowInsecure,
2045
}
2146

47+
// Configure logging options for gRPC interceptor
48+
logOpts := []logging.Option{
49+
// Don't include payload to avoid logging sensitive data by default.
50+
logging.WithLogOnEvents(
51+
logging.StartCall,
52+
logging.FinishCall,
53+
),
54+
55+
logging.WithFieldsFromContext(func(ctx context.Context) logging.Fields {
56+
return nil
57+
}),
58+
59+
logging.WithDurationField(func(duration time.Duration) logging.Fields {
60+
const (
61+
precision = 6
62+
bits = 64
63+
)
64+
65+
return logging.Fields{
66+
"grpc.duration",
67+
strconv.FormatFloat(duration.Seconds(), 'g', precision, bits),
68+
}
69+
}),
70+
}
71+
2272
opts := []grpc.DialOption{
2373
grpc.WithPerRPCCredentials(apiCreds),
24-
// Enable Prometheus metrics collection for all gRPC calls.
25-
// This provides observability into RPC success rates, latencies, and error codes.
26-
grpc.WithUnaryInterceptor(grpcClientMetrics.UnaryClientInterceptor()),
27-
grpc.WithStreamInterceptor(grpcClientMetrics.StreamClientInterceptor()),
74+
// Enable structured logging for all gRPC calls.
75+
// Logs start and finish of calls with method, duration, and status code.
76+
grpc.WithChainUnaryInterceptor(
77+
logging.UnaryClientInterceptor(interceptorLogger(logger), logOpts...),
78+
grpcClientMetrics.UnaryClientInterceptor(),
79+
),
80+
grpc.WithChainStreamInterceptor(
81+
logging.StreamClientInterceptor(interceptorLogger(logger), logOpts...),
82+
grpcClientMetrics.StreamClientInterceptor(),
83+
),
2884
// Keep-alive is necessary to detect network failures in absence of writes from the client.
2985
// Without it, the agent would hang if the server disappears while waiting for a response.
3086
// See https://github.com/grpc/grpc/blob/master/doc/keepalive.md

cmd/synthetic-monitoring-agent/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ func run(args []string, stdout io.Writer) error {
317317
config.GrpcApiServerAddr,
318318
config.GrpcInsecure,
319319
string(config.ApiToken),
320+
zl.With().Str("subsystem", "grpc").Logger(),
320321
grpcClientMetrics,
321322
)
322323
if err != nil {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ require (
3838
github.com/grafana/gsm-api-go-client v0.2.1
3939
github.com/grafana/loki/pkg/push v0.0.0-20250903135404-0b2d0b070e96
4040
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
41+
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3
4142
github.com/jpillora/backoff v1.0.0
4243
github.com/maypok86/otter/v2 v2.2.1
4344
github.com/mccutchen/go-httpbin/v2 v2.19.0
@@ -65,7 +66,6 @@ require (
6566
github.com/golang/protobuf v1.5.4 // indirect
6667
github.com/google/cel-go v0.25.0 // indirect
6768
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
68-
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
6969
github.com/kylelemons/godebug v1.1.0 // indirect
7070
github.com/mattn/go-colorable v0.1.14 // indirect
7171
github.com/mattn/go-isatty v0.0.20 // indirect

go.sum

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
44
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
55
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
66
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
7-
cloud.google.com/go/compute v1.23.4 h1:EBT9Nw4q3zyE7G45Wvv3MzolIrCJEuHys5muLY0wvAw=
87
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
98
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
109
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
@@ -121,8 +120,8 @@ github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrR
121120
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
122121
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
123122
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
124-
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
125-
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
123+
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
124+
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
126125
github.com/hokaccha/go-prettyjson v0.0.0-20180920040306-f579f869bbfe/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
127126
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
128127
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=

0 commit comments

Comments
 (0)