@@ -3,28 +3,84 @@ package main
33import (
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
0 commit comments