@@ -44,6 +44,8 @@ import (
4444 "github.com/google/uuid"
4545 "golang.org/x/net/proxy"
4646 "google.golang.org/api/option"
47+
48+ tel "cloud.google.com/go/cloudsqlconn/internal/tel"
4749 sqladmin "google.golang.org/api/sqladmin/v1beta4"
4850)
4951
7173 //go:embed version.txt
7274 versionString string
7375 userAgent = "cloud-sql-go-connector/" + strings .TrimSpace (versionString )
76+ // dialerID is a unique ID for the dialer process.
77+ dialerID = uuid .New ().String ()
7478)
7579
7680// keyGenerator encapsulates the details of RSA key generation to provide lazy
@@ -174,7 +178,9 @@ type Dialer struct {
174178
175179 // dialerID uniquely identifies a Dialer. Used for monitoring purposes,
176180 // *only* when a client has configured OpenCensus exporters.
177- dialerID string
181+ dialerID string
182+ metricsMu sync.Mutex
183+ metricRecorders map [instance.ConnName ]tel.MetricRecorder
178184
179185 // dialFunc is the function used to connect to the address on the named
180186 // network. By default, it is golang.org/x/net/proxy#Dial.
@@ -190,6 +196,18 @@ type Dialer struct {
190196 // metadataExchangeDisabled true when the dialer should never
191197 // send MDX mdx requests.
192198 metadataExchangeDisabled bool
199+
200+ // applicationName is the name of the application using the dialer.
201+ applicationName string
202+
203+ // disableBuiltInMetrics turns the internal metric export into a no-op.
204+ disableBuiltInMetrics bool
205+
206+ // clientOpts are options for all Google Cloud API clients.
207+ clientOpts []option.ClientOption
208+
209+ // userAgent is the combined user agent string.
210+ userAgent string
193211}
194212
195213var (
@@ -208,11 +226,12 @@ func (nullLogger) Debugf(_ context.Context, _ string, _ ...interface{}) {}
208226// RSA keypair is generated will be faster.
209227func NewDialer (ctx context.Context , opts ... Option ) (* Dialer , error ) {
210228 cfg := & dialerConfig {
211- refreshTimeout : cloudsql .RefreshTimeout ,
212- dialFunc : proxy .Dial ,
213- logger : nullLogger {},
214- useragents : []string {userAgent },
215- failoverPeriod : cloudsql .FailoverPeriod ,
229+ refreshTimeout : cloudsql .RefreshTimeout ,
230+ dialFunc : proxy .Dial ,
231+ logger : nullLogger {},
232+ useragents : []string {userAgent },
233+ failoverPeriod : cloudsql .FailoverPeriod ,
234+ applicationName : "unknown" ,
216235 }
217236 for _ , opt := range opts {
218237 opt (cfg )
@@ -318,17 +337,49 @@ func NewDialer(ctx context.Context, opts ...Option) (*Dialer, error) {
318337 sqladmin : client ,
319338 logger : cfg .logger ,
320339 defaultDialConfig : dc ,
321- dialerID : uuid . New (). String () ,
340+ dialerID : dialerID ,
322341 iamTokenProvider : cfg .iamLoginTokenProvider ,
342+ metricRecorders : map [instance.ConnName ]tel.MetricRecorder {},
323343 dialFunc : cfg .dialFunc ,
324344 resolver : r ,
325345 failoverPeriod : cfg .failoverPeriod ,
326346 metadataExchangeDisabled : cfg .metadataExchangeDisabled ,
347+ userAgent : strings .Join (cfg .useragents , " " ),
348+ applicationName : cfg .applicationName ,
327349 }
328350
351+ // print dialer id to terminal for debugging purposes
352+ fmt .Println ("Cloud SQL Go Connector Dialer ID:" , d .dialerID )
353+
329354 return d , nil
330355}
331356
357+ // metricRecorder does a lazy initialization of the metric exporter.
358+ func (d * Dialer ) metricRecorder (ctx context.Context , inst instance.ConnName ) tel.MetricRecorder {
359+ d .metricsMu .Lock ()
360+ defer d .metricsMu .Unlock ()
361+ if mr , ok := d .metricRecorders [inst ]; ok {
362+ return mr
363+ }
364+ cfg := tel.Config {
365+ Enabled : ! d .disableBuiltInMetrics ,
366+ Version : versionString ,
367+ ResourceContainer : inst .Project (),
368+ ResourceID : inst .Name (),
369+ ClientUID : d .dialerID ,
370+ ApplicationName : d .applicationName ,
371+ Region : inst .Region (),
372+ ClientRegion : "Client-Region-Testing" , // TODO: detect client region
373+ ComputePlatform : "Compute-Platform-Testing" , // TODO: detect compute platform
374+ ConnectorType : tel .ConnectorTypeValue (d .userAgent ),
375+ ConnectorVersion : versionString ,
376+ DatabaseEngineType : "DB-Engine-Type-Testing" , // TODO: detect database engine type
377+ }
378+ mr := tel .NewMetricRecorder (ctx , d .logger , cfg , d .clientOpts ... )
379+ d .metricRecorders [inst ] = mr
380+ return mr
381+ }
382+
332383// Dial returns a net.Conn connected to the specified Cloud SQL instance. The
333384// icn argument may be the instance's connection name in the format
334385// "project-name:region:instance-name" or a DNS name that resolves to an
@@ -339,8 +390,29 @@ func (d *Dialer) Dial(ctx context.Context, icn string, opts ...DialOption) (conn
339390 return nil , ErrDialerClosed
340391 default :
341392 }
393+ cfg := d .defaultDialConfig
394+ for _ , opt := range opts {
395+ opt (& cfg )
396+ }
397+
398+ // Resolve the instance connection name to a ConnName struct.
399+ // Note: icn may be a domain name that resolves to an instance connection name.
400+ cn , err := d .resolver .Resolve (ctx , icn )
401+ if err != nil {
402+ return nil , err
403+ }
404+ mr := d .metricRecorder (ctx , cn )
405+
342406 startTime := time .Now ()
343407 var endDial trace.EndSpanFunc
408+ attrs := tel.Attributes {
409+ IAMAuthN : cfg .useIAMAuthN ,
410+ RefreshType : tel .RefreshAheadType ,
411+ IPType : cfg .ipType ,
412+ }
413+ if d .lazyRefresh {
414+ attrs .RefreshType = tel .RefreshLazyType
415+ }
344416 ctx , endDial = trace .StartSpan (ctx , "cloud.google.com/go/cloudsqlconn.Dial" ,
345417 trace .AddInstanceName (icn ),
346418 trace .AddDialerID (d .dialerID ),
@@ -349,10 +421,6 @@ func (d *Dialer) Dial(ctx context.Context, icn string, opts ...DialOption) (conn
349421 trace .RecordDialError (context .Background (), icn , d .dialerID , err )
350422 endDial (err )
351423 }()
352- cn , err := d .resolver .Resolve (ctx , icn )
353- if err != nil {
354- return nil , err
355- }
356424
357425 // Log if resolver changed the instance name input string.
358426 if cn .DomainName () != "" {
@@ -363,14 +431,10 @@ func (d *Dialer) Dial(ctx context.Context, icn string, opts ...DialOption) (conn
363431 d .logger .Debugf (ctx , "resolved instance connection string %s to %s" , icn , cn .String ())
364432 }
365433
366- cfg := d .defaultDialConfig
367- for _ , opt := range opts {
368- opt (& cfg )
369- }
370-
371434 var endInfo trace.EndSpanFunc
372435 ctx , endInfo = trace .StartSpan (ctx , "cloud.google.com/go/cloudsqlconn/internal.InstanceInfo" )
373- c , err := d .connectionInfoCache (ctx , cn , & cfg .useIAMAuthN )
436+ c , cacheHit , err := d .connectionInfoCache (ctx , cn , & cfg .useIAMAuthN )
437+ attrs .CacheHit = cacheHit
374438 if err != nil {
375439 endInfo (err )
376440 return nil , err
@@ -453,10 +517,16 @@ func (d *Dialer) Dial(ctx context.Context, icn string, opts ...DialOption) (conn
453517 n := c .openConnsCount .Add (1 )
454518 trace .RecordOpenConnections (ctx , int64 (n ), d .dialerID , cn .String ())
455519 trace .RecordDialLatency (ctx , icn , d .dialerID , latency )
520+ mr .RecordOpenConnection (ctx , attrs )
521+ mr .RecordConnectLatencies (ctx , attrs , latency )
456522
457523 closeFunc := func () {
458524 n := c .openConnsCount .Add (^ uint64 (0 )) // c.openConnsCount = c.openConnsCount - 1
459525 trace .RecordOpenConnections (context .Background (), int64 (n ), d .dialerID , cn .String ())
526+ mr .RecordClosedConnection (context .Background (), attrs )
527+ mr .RecordClosedConnectionCount (context .Background (), attrs )
528+ // lot the message to terminal for debugging purposes
529+ fmt .Println ("Cloud SQL Go Connector Dialer ID:" , d .dialerID , "closed connection to instance:" , cn .String ())
460530 }
461531 errFunc := func (err error ) {
462532 // io.EOF occurs when the server closes the connection. This is safe to
@@ -553,7 +623,7 @@ func (d *Dialer) EngineVersion(ctx context.Context, icn string) (string, error)
553623 if err != nil {
554624 return "" , err
555625 }
556- c , err := d .connectionInfoCache (ctx , cn , & d .defaultDialConfig .useIAMAuthN )
626+ c , _ , err := d .connectionInfoCache (ctx , cn , & d .defaultDialConfig .useIAMAuthN )
557627 if err != nil {
558628 return "" , err
559629 }
@@ -577,7 +647,7 @@ func (d *Dialer) Warmup(ctx context.Context, icn string, opts ...DialOption) err
577647 for _ , opt := range opts {
578648 opt (& cfg )
579649 }
580- c , err := d .connectionInfoCache (ctx , cn , & cfg .useIAMAuthN )
650+ c , _ , err := d .connectionInfoCache (ctx , cn , & cfg .useIAMAuthN )
581651 if err != nil {
582652 return err
583653 }
@@ -724,7 +794,7 @@ func createKey(cn instance.ConnName) cacheKey {
724794// modify the existing one, or leave it unchanged as needed.
725795func (d * Dialer ) connectionInfoCache (
726796 ctx context.Context , cn instance.ConnName , useIAMAuthN * bool ,
727- ) (* monitoredCache , error ) {
797+ ) (* monitoredCache , bool , error ) {
728798 k := createKey (cn )
729799
730800 d .lock .RLock ()
@@ -733,7 +803,7 @@ func (d *Dialer) connectionInfoCache(
733803
734804 if ok && ! c .isClosed () {
735805 c .UpdateRefresh (useIAMAuthN )
736- return c , nil
806+ return c , ok , nil
737807 }
738808
739809 d .lock .Lock ()
@@ -745,7 +815,7 @@ func (d *Dialer) connectionInfoCache(
745815 // c exists and is not closed
746816 if ok && ! c .isClosed () {
747817 c .UpdateRefresh (useIAMAuthN )
748- return c , nil
818+ return c , ok , nil
749819 }
750820
751821 // Create a new instance of monitoredCache
@@ -756,7 +826,7 @@ func (d *Dialer) connectionInfoCache(
756826 d .logger .Debugf (ctx , "[%v] Connection info added to cache" , cn .String ())
757827 rsaKey , err := d .keyGenerator .rsaKey ()
758828 if err != nil {
759- return nil , err
829+ return nil , ok , err
760830 }
761831 var cache connectionInfoCache
762832 if d .lazyRefresh {
@@ -779,7 +849,7 @@ func (d *Dialer) connectionInfoCache(
779849 c = newMonitoredCache (cache , cn , d .failoverPeriod , d .resolver , d .logger )
780850 d .cache [k ] = c
781851
782- return c , nil
852+ return c , ok , nil
783853}
784854
785855// newMDXRequest builds a metadata exchange request based on the connection
0 commit comments