Skip to content

Commit 375cca3

Browse files
authored
feat: add context debug logger (#573)
Callers may now provide a context to correlate logs with trace IDs in their implementation of the interface. This is a port of GoogleCloudPlatform/cloud-sql-go-connector#797
1 parent 49da68e commit 375cca3

File tree

8 files changed

+71
-25
lines changed

8 files changed

+71
-25
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ func main() {
312312

313313
The Go Connector supports optional debug logging to help diagnose problems with
314314
the background certificate refresh. To enable it, provide a logger that
315-
implements the `debug.Logger` interface when initializing the Dialer.
315+
implements the `debug.ContextLogger` interface when initializing the Dialer.
316316

317317
For example:
318318

@@ -326,7 +326,7 @@ import (
326326

327327
type myLogger struct{}
328328

329-
func (l *myLogger) Debugf(format string, args ...interface{}) {
329+
func (l *myLogger) Debugf(ctx context.Context, format string, args ...interface{}) {
330330
// Log as you like here
331331
}
332332

@@ -335,7 +335,7 @@ func connect() {
335335

336336
d, err := NewDialer(
337337
context.Background(),
338-
alloydbconn.WithDebugLogger(l),
338+
alloydbconn.WithContextDebugLogger(l),
339339
)
340340
// use dialer as usual...
341341
}

debug/debug.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,17 @@
1414

1515
package debug
1616

17+
import "context"
18+
1719
// Logger is the interface used for debug logging. By default, it is unused.
1820
type Logger interface {
1921
// Debugf is for reporting information about internal operations.
2022
Debugf(format string, args ...interface{})
2123
}
24+
25+
// ContextLogger is the interface used for debug logging. By default, it is
26+
// unused.
27+
type ContextLogger interface {
28+
// Debugf is for reporting information about internal operations.
29+
Debugf(ctx context.Context, format string, args ...interface{})
30+
}

dialer.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ type Dialer struct {
111111
staticConnInfo io.Reader
112112

113113
client *alloydbadmin.AlloyDBAdminClient
114-
logger debug.Logger
114+
logger debug.ContextLogger
115115

116116
// defaultDialCfg holds the constructor level DialOptions, so that it can
117117
// be copied and mutated by the Dial function.
@@ -134,7 +134,7 @@ type Dialer struct {
134134

135135
type nullLogger struct{}
136136

137-
func (nullLogger) Debugf(string, ...interface{}) {}
137+
func (nullLogger) Debugf(context.Context, string, ...interface{}) {}
138138

139139
// NewDialer creates a new Dialer.
140140
//
@@ -242,14 +242,14 @@ func (d *Dialer) Dial(ctx context.Context, instance string, opts ...DialOption)
242242

243243
var endInfo trace.EndSpanFunc
244244
ctx, endInfo = trace.StartSpan(ctx, "cloud.google.com/go/alloydbconn/internal.InstanceInfo")
245-
cache, err := d.connectionInfoCache(inst)
245+
cache, err := d.connectionInfoCache(ctx, inst)
246246
if err != nil {
247247
endInfo(err)
248248
return nil, err
249249
}
250250
ci, err := cache.ConnectionInfo(ctx)
251251
if err != nil {
252-
d.removeCached(inst, cache, err)
252+
d.removeCached(ctx, inst, cache, err)
253253
endInfo(err)
254254
return nil, err
255255
}
@@ -260,19 +260,19 @@ func (d *Dialer) Dial(ctx context.Context, instance string, opts ...DialOption)
260260
// The TLS handshake will not fail on an expired client certificate. It's
261261
// not until the first read where the client cert error will be surfaced.
262262
// So check that the certificate is valid before proceeding.
263-
if invalidClientCert(inst, d.logger, ci.Expiration) {
264-
d.logger.Debugf("[%v] Refreshing certificate now", inst.String())
263+
if invalidClientCert(ctx, inst, d.logger, ci.Expiration) {
264+
d.logger.Debugf(ctx, "[%v] Refreshing certificate now", inst.String())
265265
cache.ForceRefresh()
266266
// Block on refreshed connection info
267267
ci, err = cache.ConnectionInfo(ctx)
268268
if err != nil {
269-
d.removeCached(inst, cache, err)
269+
d.removeCached(ctx, inst, cache, err)
270270
return nil, err
271271
}
272272
}
273273
addr, ok := ci.IPAddrs[cfg.ipType]
274274
if !ok {
275-
d.removeCached(inst, cache, err)
275+
d.removeCached(ctx, inst, cache, err)
276276
err := errtype.NewConfigError(
277277
fmt.Sprintf("instance does not have IP of type %q", cfg.ipType),
278278
inst.String(),
@@ -288,10 +288,10 @@ func (d *Dialer) Dial(ctx context.Context, instance string, opts ...DialOption)
288288
if cfg.dialFunc != nil {
289289
f = cfg.dialFunc
290290
}
291-
d.logger.Debugf("[%v] Dialing %v", inst.String(), hostPort)
291+
d.logger.Debugf(ctx, "[%v] Dialing %v", inst.String(), hostPort)
292292
conn, err = f(ctx, "tcp", hostPort)
293293
if err != nil {
294-
d.logger.Debugf("[%v] Dialing %v failed: %v", inst.String(), hostPort, err)
294+
d.logger.Debugf(ctx, "[%v] Dialing %v failed: %v", inst.String(), hostPort, err)
295295
// refresh the instance info in case it caused the connection failure
296296
cache.ForceRefresh()
297297
return nil, errtype.NewDialError("failed to dial", inst.String(), err)
@@ -328,7 +328,7 @@ func (d *Dialer) Dial(ctx context.Context, instance string, opts ...DialOption)
328328
}
329329
tlsConn := tls.Client(conn, c)
330330
if err := tlsConn.HandshakeContext(ctx); err != nil {
331-
d.logger.Debugf("[%v] TLS handshake failed: %v", inst.String(), err)
331+
d.logger.Debugf(ctx, "[%v] TLS handshake failed: %v", inst.String(), err)
332332
// refresh the instance info in case it caused the handshake failure
333333
cache.ForceRefresh()
334334
_ = tlsConn.Close() // best effort close attempt
@@ -359,9 +359,11 @@ func (d *Dialer) Dial(ctx context.Context, instance string, opts ...DialOption)
359359
// removeCached stops all background refreshes and deletes the connection
360360
// info cache from the map of caches.
361361
func (d *Dialer) removeCached(
362+
ctx context.Context,
362363
i alloydb.InstanceURI, c connectionInfoCache, err error,
363364
) {
364365
d.logger.Debugf(
366+
ctx,
365367
"[%v] Removing connection info from cache: %v",
366368
i.String(),
367369
err,
@@ -373,18 +375,20 @@ func (d *Dialer) removeCached(
373375
}
374376

375377
func invalidClientCert(
376-
inst alloydb.InstanceURI, l debug.Logger, expiration time.Time,
378+
ctx context.Context,
379+
inst alloydb.InstanceURI, l debug.ContextLogger, expiration time.Time,
377380
) bool {
378381
now := time.Now().UTC()
379382
notAfter := expiration.UTC()
380383
invalid := now.After(notAfter)
381384
l.Debugf(
385+
ctx,
382386
"[%v] Now = %v, Current cert expiration = %v",
383387
inst.String(),
384388
now.Format(time.RFC3339),
385389
notAfter.Format(time.RFC3339),
386390
)
387-
l.Debugf("[%v] Cert is valid = %v", inst.String(), !invalid)
391+
l.Debugf(ctx, "[%v] Cert is valid = %v", inst.String(), !invalid)
388392
return invalid
389393
}
390394

@@ -550,7 +554,7 @@ func (d *Dialer) Close() error {
550554
}
551555

552556
func (d *Dialer) connectionInfoCache(
553-
uri alloydb.InstanceURI,
557+
ctx context.Context, uri alloydb.InstanceURI,
554558
) (monitoredCache, error) {
555559
d.lock.RLock()
556560
c, ok := d.cache[uri]
@@ -562,6 +566,7 @@ func (d *Dialer) connectionInfoCache(
562566
c, ok = d.cache[uri]
563567
if !ok {
564568
d.logger.Debugf(
569+
ctx,
565570
"[%v] Connection info added to cache",
566571
uri.String(),
567572
)

internal/alloydb/instance.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func (r *refreshOperation) isValid() bool {
137137
// expires (every ~56 minutes).
138138
type RefreshAheadCache struct {
139139
instanceURI InstanceURI
140-
logger debug.Logger
140+
logger debug.ContextLogger
141141
key *rsa.PrivateKey
142142
// refreshTimeout sets the maximum duration a refresh cycle can run
143143
// for.
@@ -165,7 +165,7 @@ type RefreshAheadCache struct {
165165
// caches connection info.
166166
func NewRefreshAheadCache(
167167
instance InstanceURI,
168-
l debug.Logger,
168+
l debug.ContextLogger,
169169
client *alloydbadmin.AlloyDBAdminClient,
170170
key *rsa.PrivateKey,
171171
refreshTimeout time.Duration,
@@ -265,6 +265,7 @@ func (i *RefreshAheadCache) scheduleRefresh(d time.Duration) *refreshOperation {
265265
// instance has been closed, don't schedule anything
266266
if err := i.ctx.Err(); err != nil {
267267
i.logger.Debugf(
268+
context.Background(),
268269
"[%v] Instance is closed, stopping refresh operations",
269270
i.instanceURI.String(),
270271
)
@@ -273,6 +274,7 @@ func (i *RefreshAheadCache) scheduleRefresh(d time.Duration) *refreshOperation {
273274
return
274275
}
275276
i.logger.Debugf(
277+
context.Background(),
276278
"[%v] Connection info refresh operation started",
277279
i.instanceURI.String(),
278280
)
@@ -288,17 +290,20 @@ func (i *RefreshAheadCache) scheduleRefresh(d time.Duration) *refreshOperation {
288290
nil,
289291
)
290292
i.logger.Debugf(
293+
ctx,
291294
"[%v] Connection info refresh operation failed, err = %v",
292295
i.instanceURI.String(),
293296
r.err,
294297
)
295298
} else {
296299
r.result, r.err = i.r.performRefresh(i.ctx, i.instanceURI, i.key)
297300
i.logger.Debugf(
301+
ctx,
298302
"[%v] Connection info refresh operation complete",
299303
i.instanceURI.String(),
300304
)
301305
i.logger.Debugf(
306+
ctx,
302307
"[%v] Current certificate expiration = %v",
303308
i.instanceURI.String(),
304309
r.result.Expiration.UTC().Format(time.RFC3339),
@@ -315,6 +320,7 @@ func (i *RefreshAheadCache) scheduleRefresh(d time.Duration) *refreshOperation {
315320
// if failed, scheduled the next refresh immediately
316321
if r.err != nil {
317322
i.logger.Debugf(
323+
ctx,
318324
"[%v] Connection info refresh operation scheduled immediately",
319325
i.instanceURI.String(),
320326
)
@@ -335,6 +341,7 @@ func (i *RefreshAheadCache) scheduleRefresh(d time.Duration) *refreshOperation {
335341
i.cur = r
336342
t := refreshDuration(time.Now(), i.cur.result.Expiration)
337343
i.logger.Debugf(
344+
ctx,
338345
"[%v] Connection info refresh operation scheduled at %v (now + %v)",
339346
i.instanceURI.String(),
340347
time.Now().Add(t).UTC().Format(time.RFC3339),

internal/alloydb/instance_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131

3232
type nullLogger struct{}
3333

34-
func (nullLogger) Debugf(string, ...interface{}) {}
34+
func (nullLogger) Debugf(context.Context, string, ...interface{}) {}
3535

3636
// genRSAKey generates an RSA key used for test.
3737
func genRSAKey() *rsa.PrivateKey {

internal/alloydb/lazy.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
// a caller requests connection info and the current certificate is expired.
2929
type LazyRefreshCache struct {
3030
uri InstanceURI
31-
logger debug.Logger
31+
logger debug.ContextLogger
3232
key *rsa.PrivateKey
3333
r refresher
3434
mu sync.Mutex
@@ -39,7 +39,7 @@ type LazyRefreshCache struct {
3939
// NewLazyRefreshCache initializes a new LazyRefreshCache.
4040
func NewLazyRefreshCache(
4141
uri InstanceURI,
42-
l debug.Logger,
42+
l debug.ContextLogger,
4343
client *alloydbadmin.AlloyDBAdminClient,
4444
key *rsa.PrivateKey,
4545
_ time.Duration,
@@ -72,30 +72,35 @@ func (c *LazyRefreshCache) ConnectionInfo(
7272
exp := c.cached.Expiration.UTC().Add(-refreshBuffer)
7373
if !c.needsRefresh && now.Before(exp) {
7474
c.logger.Debugf(
75+
ctx,
7576
"[%v] Connection info is still valid, using cached info",
7677
c.uri.String(),
7778
)
7879
return c.cached, nil
7980
}
8081

8182
c.logger.Debugf(
83+
ctx,
8284
"[%v] Connection info refresh operation started",
8385
c.uri.String(),
8486
)
8587
ci, err := c.r.performRefresh(ctx, c.uri, c.key)
8688
if err != nil {
8789
c.logger.Debugf(
90+
ctx,
8891
"[%v] Connection info refresh operation failed, err = %v",
8992
c.uri.String(),
9093
err,
9194
)
9295
return ConnectionInfo{}, err
9396
}
9497
c.logger.Debugf(
98+
ctx,
9599
"[%v] Connection info refresh operation complete",
96100
c.uri.String(),
97101
)
98102
c.logger.Debugf(
103+
ctx,
99104
"[%v] Current certificate expiration = %v",
100105
c.uri.String(),
101106
ci.Expiration.UTC().Format(time.RFC3339),

internal/alloydb/static.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,15 @@ func (s *staticData) UnmarshalJSON(data []byte) error {
7878

7979
// StaticConnectionInfoCache provides connection info that is never refreshed.
8080
type StaticConnectionInfoCache struct {
81-
logger debug.Logger
81+
logger debug.ContextLogger
8282
info ConnectionInfo
8383
}
8484

8585
// NewStaticConnectionInfoCache creates a connection info cache that will
8686
// always return the predefined connection info within the provided io.Reader
8787
func NewStaticConnectionInfoCache(
8888
inst InstanceURI,
89-
l debug.Logger,
89+
l debug.ContextLogger,
9090
r io.Reader,
9191
) (*StaticConnectionInfoCache, error) {
9292
data, err := io.ReadAll(r)

options.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type dialerConfig struct {
4646
tokenSource oauth2.TokenSource
4747
userAgents []string
4848
useIAMAuthN bool
49-
logger debug.Logger
49+
logger debug.ContextLogger
5050
lazyRefresh bool
5151

5252
staticConnInfo io.Reader
@@ -170,9 +170,29 @@ func WithIAMAuthN() Option {
170170
}
171171
}
172172

173+
type debugLoggerWithoutContext struct {
174+
logger debug.Logger
175+
}
176+
177+
// Debugf implements debug.ContextLogger.
178+
func (d *debugLoggerWithoutContext) Debugf(_ context.Context, format string, args ...interface{}) {
179+
d.logger.Debugf(format, args...)
180+
}
181+
182+
var _ debug.ContextLogger = new(debugLoggerWithoutContext)
183+
173184
// WithDebugLogger configures a debug logger for reporting on internal
174185
// operations. By default the debug logger is disabled.
186+
// Prefer WithContextLogger.
175187
func WithDebugLogger(l debug.Logger) Option {
188+
return func(d *dialerConfig) {
189+
d.logger = &debugLoggerWithoutContext{l}
190+
}
191+
}
192+
193+
// WithContextLogger configures a debug lgoger for reporting on internal
194+
// operations. By default the debug logger is disabled.
195+
func WithContextLogger(l debug.ContextLogger) Option {
176196
return func(d *dialerConfig) {
177197
d.logger = l
178198
}

0 commit comments

Comments
 (0)