mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Added OTEL DB stats metrics, renamed trace attributes to expected OTEL names (#42097)
1. Added DB metrics via otelsql.RegisterDBStatsMetrics() `db.sql.connection.open` `db.sql.connection.max_open` `db.sql.connection.wait` `db.sql.connection.wait_duration` `db.sql.connection.closed_max_idle` `db.sql.connection.closed_max_idle_time` `db.sql.latency.*` 2. renamed these metrics to signoz convention/expected names `db.sql.connection.open` -> `db.client.connection.usage` `db.sql.connection.max_open` -> `db.client.connection.max` `db.sql.connection.wait` -> `db.client.connection.wait_count` `db.sql.connection.wait_duration` -> `db.client.connection.wait_time` `db.sql.connection.closed_max_idle` -> `db.client.connection.idle.max` `db.sql.connection.closed_max_idle_time` -> `db.client.connection.idle.min` 3. created custom dashboard to display these metrics, (import via json) <img width="1580" height="906" alt="Screenshot 2026-03-19 at 2 44 43 PM" src="https://github.com/user-attachments/assets/f1b64ed6-e534-4490-8955-bc1205dd21d4" /> 4. Fixed metrics for service db dashboards Signoz expects `db.system` : Identifies the database type (e.g., postgresql, mysql, mongodb). `db.statement` : The actual query being executed (e.g., SELECT * FROM users). `db.operation` : The type of operation (e.g., SELECT, INSERT). `service.name` : The name of the service making the call. We needed to set the `db.system` attribute explicitly. `db.operation` is missing because otelsql doesn't capture this by default. Decided not to add this for now as the dashboards work without. Can be a future enhancement. <img width="1563" height="487" alt="Screenshot 2026-03-19 at 2 45 18 PM" src="https://github.com/user-attachments/assets/51028e16-ee2c-45a9-9025-26f17b0db67a" /> # Checklist for submitter ## Testing - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added a new observability dashboard for database and connection performance metrics, including RPS, latency, connection pool saturation, and queue statistics. * Enhanced database metrics collection with automatic registration of connection and query performance indicators. * Standardized OpenTelemetry metric naming to align with industry conventions for improved observability compatibility. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
40e91c0ece
commit
6ed3ba6801
4 changed files with 1800 additions and 1 deletions
|
|
@ -228,9 +228,40 @@ func runServeCmd(cmd *cobra.Command, configManager configpkg.Manager, debug, dev
|
|||
if err != nil {
|
||||
initFatal(err, "Failed to initialize OTEL metrics exporter")
|
||||
}
|
||||
|
||||
// Create views to rename otelsql metrics to match what OpenTelemetry Signoz expects
|
||||
// Reference: https://opentelemetry.io/docs/specs/semconv/db/database-metrics/
|
||||
dbMetricViews := []sdkmetric.View{
|
||||
sdkmetric.NewView(
|
||||
sdkmetric.Instrument{Name: "db.sql.connection.open"},
|
||||
sdkmetric.Stream{Name: "db.client.connection.count"},
|
||||
),
|
||||
sdkmetric.NewView(
|
||||
sdkmetric.Instrument{Name: "db.sql.connection.max_open"},
|
||||
sdkmetric.Stream{Name: "db.client.connection.max"},
|
||||
),
|
||||
sdkmetric.NewView(
|
||||
sdkmetric.Instrument{Name: "db.sql.connection.wait"},
|
||||
sdkmetric.Stream{Name: "db.client.connection.wait_count"},
|
||||
),
|
||||
sdkmetric.NewView(
|
||||
sdkmetric.Instrument{Name: "db.sql.connection.wait_duration"},
|
||||
sdkmetric.Stream{Name: "db.client.connection.wait_time"},
|
||||
),
|
||||
sdkmetric.NewView(
|
||||
sdkmetric.Instrument{Name: "db.sql.connection.closed_max_idle"},
|
||||
sdkmetric.Stream{Name: "db.client.connection.closed.max_idle"},
|
||||
),
|
||||
sdkmetric.NewView(
|
||||
sdkmetric.Instrument{Name: "db.sql.connection.closed_max_idle_time"},
|
||||
sdkmetric.Stream{Name: "db.client.connection.closed.max_idle_time"},
|
||||
),
|
||||
}
|
||||
|
||||
meterProvider = sdkmetric.NewMeterProvider(
|
||||
sdkmetric.WithResource(res),
|
||||
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExporter)),
|
||||
sdkmetric.WithView(dbMetricViews...),
|
||||
)
|
||||
otel.SetMeterProvider(meterProvider)
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
|
||||
)
|
||||
|
||||
|
|
@ -342,7 +343,10 @@ var otelTracedDriverName string
|
|||
func init() {
|
||||
var err error
|
||||
otelTracedDriverName, err = otelsql.Register("mysql",
|
||||
otelsql.WithAttributes(semconv.DBSystemNameMySQL),
|
||||
otelsql.WithAttributes(
|
||||
attribute.String("db.system", "mysql"),
|
||||
semconv.DBSystemNameMySQL,
|
||||
),
|
||||
otelsql.WithSpanOptions(otelsql.SpanOptions{
|
||||
// DisableErrSkip ignores driver.ErrSkip errors which are frequently returned by the MySQL driver
|
||||
// when certain optional methods or paths are not implemented/taken.
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/XSAM/otelsql"
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/ngrok/sqlmw"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
// ConnectorFactory creates a driver.Connector for custom database authentication.
|
||||
|
|
@ -114,6 +116,26 @@ func NewDB(conf *MysqlConfig, opts *DBOptions, otelDriverName string) (*sqlx.DB,
|
|||
if dbError != nil {
|
||||
return nil, dbError
|
||||
}
|
||||
|
||||
// Register database/sql.DBStats metrics when using OpenTelemetry tracing.
|
||||
if opts.TracingConfig != nil && opts.TracingConfig.TracingEnabled && opts.TracingConfig.TracingType != "elasticapm" {
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("db.system", "mysql"),
|
||||
}
|
||||
// Parse DSN to extract address and database name for metric differentiation
|
||||
if cfg, err := mysql.ParseDSN(dsn); err == nil {
|
||||
if cfg.Addr != "" {
|
||||
attrs = append(attrs, attribute.String("db.addr", cfg.Addr))
|
||||
}
|
||||
if cfg.DBName != "" {
|
||||
attrs = append(attrs, attribute.String("db.name", cfg.DBName))
|
||||
}
|
||||
}
|
||||
if err := otelsql.RegisterDBStatsMetrics(db.DB, otelsql.WithAttributes(attrs...)); err != nil {
|
||||
opts.Logger.WarnContext(context.Background(), "failed to register DB stats metrics", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
1742
tools/signoz/database_custom_dashboard.json
Normal file
1742
tools/signoz/database_custom_dashboard.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue