package wrapper import ( "context" "database/sql" "time" "github.com/unistack-org/micro/v3/logger" "github.com/unistack-org/micro/v3/meter" "github.com/unistack-org/micro/v3/tracer" ) var ( DefaultStatsInterval = 5 * time.Second // default metric prefix DefaultMetricPrefix = "micro_sql_" // default label prefix DefaultLabelPrefix = "micro_" ) var ( DatabaseHostLabel = "database_host" DatabaseNameLabel = "database_name" ServiceNameLabel = "service_name" ServiceVersionLabel = "service_version" ServiceIDLabel = "service_id" MaxOpenConnectionsLabel = "max_open_connections" OpenConnectionsLabel = "open_connections" InuseConnectionsLabel = "inuse_connections" IdleConnectionsLabel = "idle_connections" WaitConnectionsLabel = "wait_connections" BlockedSecondsLabel = "blocked_seconds" MaxIdleClosedLabel = "max_idle_closed" MaxLifetimeClosedLabel = "max_lifetime_closed" //srequest_total // counter //slatency_microseconds // summary //srequest_duration_seconds // histogramm ) type Options struct { Logger logger.Logger Meter meter.Meter Tracer tracer.Tracer DatabaseHost string DatabaseName string ServiceName string ServiceVersion string ServiceID string } type Option func(*Options) func DatabaseHost(host string) Option { return func(opts *Options) { opts.DatabaseHost = host } } func DatabaseName(name string) Option { return func(opts *Options) { opts.DatabaseName = name } } func ServiceName(name string) Option { return func(opts *Options) { opts.ServiceName = name } } func ServiceVersion(version string) Option { return func(opts *Options) { opts.ServiceVersion = version } } func ServiceID(id string) Option { return func(opts *Options) { opts.ServiceID = id } } func Meter(m meter.Meter) Option { return func(opts *Options) { opts.Meter = m } } func Logger(l logger.Logger) Option { return func(opts *Options) { opts.Logger = l } } func Tracer(t tracer.Tracer) Option { return func(opts *Options) { opts.Tracer = t } } type queryKey struct{} func QueryName(ctx context.Context, name string) context.Context { if ctx == nil { ctx = context.Background() } return context.WithValue(ctx, queryKey{}, name) } func getName(ctx context.Context) string { name := "Unknown" val, ok := ctx.Value(queryKey{}).(string) if ok && len(val) > 0 { name = val } return name } type Wrapper struct { db *sql.DB opts Options } func (w *Wrapper) collect() { labels := []string{ DatabaseHostLabel, w.opts.DatabaseHost, DatabaseNameLabel, w.opts.DatabaseName, ServiceNameLabel, w.opts.ServiceName, ServiceVersionLabel, w.opts.ServiceVersion, ServiceIDLabel, w.opts.ServiceID, } ticker := time.NewTicker(DefaultStatsInterval) defer ticker.Stop() for { select { case <-ticker.C: if w.db == nil { continue } stats := w.db.Stats() w.opts.Meter.FloatCounter(MaxOpenConnectionsLabel, meter.Labels(labels...)).Set(float64(stats.MaxOpenConnections)) w.opts.Meter.FloatCounter(OpenConnectionsLabel, meter.Labels(labels...)).Set(float64(stats.OpenConnections)) w.opts.Meter.FloatCounter(InuseConnectionsLabel, meter.Labels(labels...)).Set(float64(stats.InUse)) w.opts.Meter.FloatCounter(IdleConnectionsLabel, meter.Labels(labels...)).Set(float64(stats.Idle)) w.opts.Meter.FloatCounter(WaitConnectionsLabel, meter.Labels(labels...)).Set(float64(stats.WaitCount)) w.opts.Meter.FloatCounter(BlockedSecondsLabel, meter.Labels(labels...)).Set(stats.WaitDuration.Seconds()) w.opts.Meter.FloatCounter(MaxIdleClosedLabel, meter.Labels(labels...)).Set(float64(stats.MaxIdleClosed)) w.opts.Meter.FloatCounter(MaxLifetimeClosedLabel, meter.Labels(labels...)).Set(float64(stats.MaxLifetimeClosed)) } } }