diff --git a/cmd/gotosocial/action/server/server.go b/cmd/gotosocial/action/server/server.go index 4060eeb7f..dcd30a9b5 100644 --- a/cmd/gotosocial/action/server/server.go +++ b/cmd/gotosocial/action/server/server.go @@ -50,9 +50,9 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/media/ffmpeg" "github.com/superseriousbusiness/gotosocial/internal/messages" - "github.com/superseriousbusiness/gotosocial/internal/metrics" "github.com/superseriousbusiness/gotosocial/internal/middleware" "github.com/superseriousbusiness/gotosocial/internal/oauth" + "github.com/superseriousbusiness/gotosocial/internal/observability" "github.com/superseriousbusiness/gotosocial/internal/oidc" "github.com/superseriousbusiness/gotosocial/internal/processing" tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline" @@ -61,7 +61,6 @@ import ( gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/subscriptions" "github.com/superseriousbusiness/gotosocial/internal/timeline" - "github.com/superseriousbusiness/gotosocial/internal/tracing" "github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/web" @@ -196,7 +195,7 @@ var Start action.GTSAction = func(ctx context.Context) error { } // Initialize tracing (noop if not enabled). - if err := tracing.Initialize(); err != nil { + if err := observability.InitializeTracing(); err != nil { return fmt.Errorf("error initializing tracing: %w", err) } @@ -393,7 +392,7 @@ var Start action.GTSAction = func(ctx context.Context) error { } // Initialize metrics. - if err := metrics.Initialize(state.DB); err != nil { + if err := observability.InitializeMetrics(state.DB); err != nil { return fmt.Errorf("error initializing metrics: %w", err) } @@ -426,12 +425,12 @@ var Start action.GTSAction = func(ctx context.Context) error { // Add tracing middleware if enabled. if config.GetTracingEnabled() { - middlewares = append(middlewares, tracing.InstrumentGin()) + middlewares = append(middlewares, observability.TracingMiddleware()) } // Add metrics middleware if enabled. if config.GetMetricsEnabled() { - middlewares = append(middlewares, metrics.InstrumentGin()) + middlewares = append(middlewares, observability.MetricsMiddleware()) } middlewares = append(middlewares, []gin.HandlerFunc{ diff --git a/cmd/gotosocial/action/testrig/testrig.go b/cmd/gotosocial/action/testrig/testrig.go index 7de3f78a1..b19325234 100644 --- a/cmd/gotosocial/action/testrig/testrig.go +++ b/cmd/gotosocial/action/testrig/testrig.go @@ -38,8 +38,8 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/language" "github.com/superseriousbusiness/gotosocial/internal/log" - "github.com/superseriousbusiness/gotosocial/internal/metrics" "github.com/superseriousbusiness/gotosocial/internal/middleware" + "github.com/superseriousbusiness/gotosocial/internal/observability" "github.com/superseriousbusiness/gotosocial/internal/oidc" tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline" "github.com/superseriousbusiness/gotosocial/internal/router" @@ -47,7 +47,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/subscriptions" "github.com/superseriousbusiness/gotosocial/internal/timeline" - "github.com/superseriousbusiness/gotosocial/internal/tracing" "github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/web" "github.com/superseriousbusiness/gotosocial/testrig" @@ -127,7 +126,7 @@ var Start action.GTSAction = func(ctx context.Context) error { } config.SetInstanceLanguages(parsedLangs) - if err := tracing.Initialize(); err != nil { + if err := observability.InitializeTracing(); err != nil { return fmt.Errorf("error initializing tracing: %w", err) } @@ -195,7 +194,7 @@ var Start action.GTSAction = func(ctx context.Context) error { defer testrig.StopWorkers(state) // Initialize metrics. - if err := metrics.Initialize(state.DB); err != nil { + if err := observability.InitializeMetrics(state.DB); err != nil { return fmt.Errorf("error initializing metrics: %w", err) } @@ -213,11 +212,11 @@ var Start action.GTSAction = func(ctx context.Context) error { middleware.AddRequestID(config.GetRequestIDHeader()), // requestID middleware must run before tracing } if config.GetTracingEnabled() { - middlewares = append(middlewares, tracing.InstrumentGin()) + middlewares = append(middlewares, observability.TracingMiddleware()) } if config.GetMetricsEnabled() { - middlewares = append(middlewares, metrics.InstrumentGin()) + middlewares = append(middlewares, observability.MetricsMiddleware()) } middlewares = append(middlewares, []gin.HandlerFunc{ diff --git a/internal/api/metrics/metrics.go b/internal/api/metrics/metrics.go index bbca861ef..5161a3b71 100644 --- a/internal/api/metrics/metrics.go +++ b/internal/api/metrics/metrics.go @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//go:build !nometrics +//go:build !nootel package metrics diff --git a/internal/api/metrics/no_metrics.go b/internal/api/metrics/no_metrics.go index ae7fc7ce7..5aa7b722d 100644 --- a/internal/api/metrics/no_metrics.go +++ b/internal/api/metrics/no_metrics.go @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//go:build nometrics +//go:build nootel package metrics diff --git a/internal/db/bundb/bundb.go b/internal/db/bundb/bundb.go index c9dd7866d..18fe5384c 100644 --- a/internal/db/bundb/bundb.go +++ b/internal/db/bundb/bundb.go @@ -41,9 +41,8 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" - "github.com/superseriousbusiness/gotosocial/internal/metrics" + "github.com/superseriousbusiness/gotosocial/internal/observability" "github.com/superseriousbusiness/gotosocial/internal/state" - "github.com/superseriousbusiness/gotosocial/internal/tracing" "github.com/uptrace/bun" "github.com/uptrace/bun/dialect" "github.com/uptrace/bun/dialect/pgdialect" @@ -324,11 +323,10 @@ func bunDB(sqldb *sql.DB, dialect func() schema.Dialect) *bun.DB { // Add our SQL connection hooks. db.AddQueryHook(queryHook{}) - if config.GetTracingEnabled() { - db.AddQueryHook(tracing.InstrumentBun()) - } - if config.GetMetricsEnabled() { - db.AddQueryHook(metrics.InstrumentBun()) + metricsEnabled := config.GetMetricsEnabled() + tracingEnabled := config.GetTracingEnabled() + if metricsEnabled || tracingEnabled { + db.AddQueryHook(observability.InstrumentBun(tracingEnabled, metricsEnabled)) } // table registration is needed for many-to-many, see: diff --git a/internal/tracing/no_trace.go b/internal/observability/bun.go similarity index 60% rename from internal/tracing/no_trace.go rename to internal/observability/bun.go index 12f46ce53..4baf786e4 100644 --- a/internal/tracing/no_trace.go +++ b/internal/observability/bun.go @@ -15,29 +15,28 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//go:build notracing +//go:build !nootel -package tracing +package observability import ( - "errors" - - "github.com/gin-gonic/gin" - "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/uptrace/bun" + "github.com/uptrace/bun/extra/bunotel" + metricnoop "go.opentelemetry.io/otel/metric/noop" + tracenoop "go.opentelemetry.io/otel/trace/noop" ) -func Initialize() error { - if config.GetTracingEnabled() { - return errors.New("tracing was disabled at build time") +func InstrumentBun(traces bool, metrics bool) bun.QueryHook { + opts := []bunotel.Option{ + bunotel.WithFormattedQueries(true), } - return nil -} - -func InstrumentGin() gin.HandlerFunc { - return func(c *gin.Context) {} -} - -func InstrumentBun() bun.QueryHook { - return nil + if !traces { + opts = append(opts, bunotel.WithTracerProvider(tracenoop.NewTracerProvider())) + } + if !metrics { + opts = append(opts, bunotel.WithMeterProvider(metricnoop.NewMeterProvider())) + } + return bunotel.NewQueryHook( + opts..., + ) } diff --git a/internal/metrics/metrics.go b/internal/observability/metrics.go similarity index 91% rename from internal/metrics/metrics.go rename to internal/observability/metrics.go index e22a67dad..f2ff2ac0d 100644 --- a/internal/metrics/metrics.go +++ b/internal/observability/metrics.go @@ -15,24 +15,24 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//go:build !nometrics +//go:build !nootel -package metrics +package observability import ( "context" "errors" - "github.com/gin-gonic/gin" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/db" + + "github.com/gin-gonic/gin" "github.com/technologize/otel-go-contrib/otelginmetrics" - "github.com/uptrace/bun" - "github.com/uptrace/bun/extra/bunotel" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/metric" sdk "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/exemplar" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.24.0" ) @@ -41,7 +41,7 @@ const ( serviceName = "GoToSocial" ) -func Initialize(db db.DB) error { +func InitializeMetrics(db db.DB) error { if !config.GetMetricsEnabled() { return nil } @@ -66,6 +66,7 @@ func Initialize(db db.DB) error { } meterProvider := sdk.NewMeterProvider( + sdk.WithExemplarFilter(exemplar.AlwaysOffFilter), sdk.WithResource(r), sdk.WithReader(prometheusExporter), ) @@ -127,12 +128,6 @@ func Initialize(db db.DB) error { return nil } -func InstrumentGin() gin.HandlerFunc { +func MetricsMiddleware() gin.HandlerFunc { return otelginmetrics.Middleware(serviceName) } - -func InstrumentBun() bun.QueryHook { - return bunotel.NewQueryHook( - bunotel.WithMeterProvider(otel.GetMeterProvider()), - ) -} diff --git a/internal/metrics/no_metrics.go b/internal/observability/no_otel.go similarity index 73% rename from internal/metrics/no_metrics.go rename to internal/observability/no_otel.go index 25e156307..122d7a897 100644 --- a/internal/metrics/no_metrics.go +++ b/internal/observability/no_otel.go @@ -15,30 +15,32 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//go:build nometrics +//go:build nootel -package metrics +package observability import ( - "errors" + "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/gin-gonic/gin" - "github.com/superseriousbusiness/gotosocial/internal/config" - "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/uptrace/bun" ) -func Initialize(db db.DB) error { - if config.GetMetricsEnabled() { - return errors.New("metrics was disabled at build time") - } +func InitializeMetrics(db db.DB) error { + return nil +} +func InitializeTracing() error { return nil } -func InstrumentGin() gin.HandlerFunc { - return func(c *gin.Context) {} -} - -func InstrumentBun() bun.QueryHook { +func MetricsMiddleware() gin.HandlerFunc { + return nil +} + +func TracingMiddleware() gin.HandlerFunc { + return nil +} + +func InstrumentBun(traces bool, metrics bool) bun.QueryHook { return nil } diff --git a/internal/tracing/tracing.go b/internal/observability/tracing.go similarity index 95% rename from internal/tracing/tracing.go rename to internal/observability/tracing.go index dd1e19be9..7827004a3 100644 --- a/internal/tracing/tracing.go +++ b/internal/observability/tracing.go @@ -15,9 +15,9 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//go:build !notracing +//go:build !nootel -package tracing +package observability import ( "context" @@ -25,8 +25,6 @@ import ( "codeberg.org/gruf/go-kv" "github.com/gin-gonic/gin" - "github.com/uptrace/bun" - "github.com/uptrace/bun/extra/bunotel" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" @@ -45,10 +43,10 @@ import ( const ( tracerKey = "gotosocial-server-tracer" - tracerName = "github.com/superseriousbusiness/gotosocial/internal/tracing" + tracerName = "github.com/superseriousbusiness/gotosocial/internal/observability" ) -func Initialize() error { +func InitializeTracing() error { if !config.GetTracingEnabled() { return nil } @@ -119,7 +117,7 @@ func Initialize() error { // InstrumentGin is a middleware injecting tracing information based on the // otelgin implementation found at // https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go -func InstrumentGin() gin.HandlerFunc { +func TracingMiddleware() gin.HandlerFunc { provider := otel.GetTracerProvider() tracer := provider.Tracer( tracerName, @@ -182,9 +180,3 @@ func InjectRequestID() gin.HandlerFunc { } } } - -func InstrumentBun() bun.QueryHook { - return bunotel.NewQueryHook( - bunotel.WithFormattedQueries(true), - ) -} diff --git a/scripts/build.sh b/scripts/build.sh index 3e60cd306..b22861240 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -17,8 +17,7 @@ GO_GCFLAGS=${GO_GCFLAGS-} # Available Go build tags, with explanation, followed by benefits of enabling it: # - kvformat: enables prettier output of log fields (slightly better performance) # - timetzdata: embed timezone database inside binary (allow setting local time inside Docker containers, at cost of 450KB) -# - notracing: disables compiling-in otel tracing support (reduced binary size, better performance) -# - nometrics: disables compiling-in otel metrics support (reduced binary size, better performance) +# - nootel: disables compiling-in otel support (reduced binary size) # - noerrcaller: disables caller function prefix in errors (slightly better performance, at cost of err readability) # - debug: enables /debug/pprof endpoint (adds debug, at performance cost) # - debugenv: enables /debug/pprof endpoint if DEBUG=1 env during runtime (adds debug, at performance cost)