refactor: combine tracing & metrics in observability package

This commit is contained in:
Dominik Süß 2025-02-05 17:11:28 +01:00
commit 826c36f162
No known key found for this signature in database
GPG key ID: 9B1DDB4B45B86806
10 changed files with 64 additions and 81 deletions

View file

@ -50,9 +50,9 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/media/ffmpeg" "github.com/superseriousbusiness/gotosocial/internal/media/ffmpeg"
"github.com/superseriousbusiness/gotosocial/internal/messages" "github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/metrics"
"github.com/superseriousbusiness/gotosocial/internal/middleware" "github.com/superseriousbusiness/gotosocial/internal/middleware"
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/observability"
"github.com/superseriousbusiness/gotosocial/internal/oidc" "github.com/superseriousbusiness/gotosocial/internal/oidc"
"github.com/superseriousbusiness/gotosocial/internal/processing" "github.com/superseriousbusiness/gotosocial/internal/processing"
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline" tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
@ -61,7 +61,6 @@ import (
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage" gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/internal/subscriptions" "github.com/superseriousbusiness/gotosocial/internal/subscriptions"
"github.com/superseriousbusiness/gotosocial/internal/timeline" "github.com/superseriousbusiness/gotosocial/internal/timeline"
"github.com/superseriousbusiness/gotosocial/internal/tracing"
"github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/transport"
"github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/web" "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). // 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) return fmt.Errorf("error initializing tracing: %w", err)
} }
@ -393,7 +392,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
} }
// Initialize metrics. // 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) 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. // Add tracing middleware if enabled.
if config.GetTracingEnabled() { if config.GetTracingEnabled() {
middlewares = append(middlewares, tracing.InstrumentGin()) middlewares = append(middlewares, observability.TracingMiddleware())
} }
// Add metrics middleware if enabled. // Add metrics middleware if enabled.
if config.GetMetricsEnabled() { if config.GetMetricsEnabled() {
middlewares = append(middlewares, metrics.InstrumentGin()) middlewares = append(middlewares, observability.MetricsMiddleware())
} }
middlewares = append(middlewares, []gin.HandlerFunc{ middlewares = append(middlewares, []gin.HandlerFunc{

View file

@ -38,8 +38,8 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/language" "github.com/superseriousbusiness/gotosocial/internal/language"
"github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/metrics"
"github.com/superseriousbusiness/gotosocial/internal/middleware" "github.com/superseriousbusiness/gotosocial/internal/middleware"
"github.com/superseriousbusiness/gotosocial/internal/observability"
"github.com/superseriousbusiness/gotosocial/internal/oidc" "github.com/superseriousbusiness/gotosocial/internal/oidc"
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline" tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
"github.com/superseriousbusiness/gotosocial/internal/router" "github.com/superseriousbusiness/gotosocial/internal/router"
@ -47,7 +47,6 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/storage" "github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/internal/subscriptions" "github.com/superseriousbusiness/gotosocial/internal/subscriptions"
"github.com/superseriousbusiness/gotosocial/internal/timeline" "github.com/superseriousbusiness/gotosocial/internal/timeline"
"github.com/superseriousbusiness/gotosocial/internal/tracing"
"github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/web" "github.com/superseriousbusiness/gotosocial/internal/web"
"github.com/superseriousbusiness/gotosocial/testrig" "github.com/superseriousbusiness/gotosocial/testrig"
@ -127,7 +126,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
} }
config.SetInstanceLanguages(parsedLangs) config.SetInstanceLanguages(parsedLangs)
if err := tracing.Initialize(); err != nil { if err := observability.InitializeTracing(); err != nil {
return fmt.Errorf("error initializing tracing: %w", err) 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) defer testrig.StopWorkers(state)
// Initialize metrics. // 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) 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 middleware.AddRequestID(config.GetRequestIDHeader()), // requestID middleware must run before tracing
} }
if config.GetTracingEnabled() { if config.GetTracingEnabled() {
middlewares = append(middlewares, tracing.InstrumentGin()) middlewares = append(middlewares, observability.TracingMiddleware())
} }
if config.GetMetricsEnabled() { if config.GetMetricsEnabled() {
middlewares = append(middlewares, metrics.InstrumentGin()) middlewares = append(middlewares, observability.MetricsMiddleware())
} }
middlewares = append(middlewares, []gin.HandlerFunc{ middlewares = append(middlewares, []gin.HandlerFunc{

View file

@ -15,7 +15,7 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
//go:build !nometrics //go:build !nootel
package metrics package metrics

View file

@ -15,7 +15,7 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
//go:build nometrics //go:build nootel
package metrics package metrics

View file

@ -41,9 +41,8 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations" "github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log" "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/state"
"github.com/superseriousbusiness/gotosocial/internal/tracing"
"github.com/uptrace/bun" "github.com/uptrace/bun"
"github.com/uptrace/bun/dialect" "github.com/uptrace/bun/dialect"
"github.com/uptrace/bun/dialect/pgdialect" "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. // Add our SQL connection hooks.
db.AddQueryHook(queryHook{}) db.AddQueryHook(queryHook{})
if config.GetTracingEnabled() { metricsEnabled := config.GetMetricsEnabled()
db.AddQueryHook(tracing.InstrumentBun()) tracingEnabled := config.GetTracingEnabled()
} if metricsEnabled || tracingEnabled {
if config.GetMetricsEnabled() { db.AddQueryHook(observability.InstrumentBun(tracingEnabled, metricsEnabled))
db.AddQueryHook(metrics.InstrumentBun())
} }
// table registration is needed for many-to-many, see: // table registration is needed for many-to-many, see:

View file

@ -15,29 +15,28 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
//go:build notracing //go:build !nootel
package tracing package observability
import ( import (
"errors"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/uptrace/bun" "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 { func InstrumentBun(traces bool, metrics bool) bun.QueryHook {
if config.GetTracingEnabled() { opts := []bunotel.Option{
return errors.New("tracing was disabled at build time") bunotel.WithFormattedQueries(true),
} }
return nil if !traces {
} opts = append(opts, bunotel.WithTracerProvider(tracenoop.NewTracerProvider()))
}
func InstrumentGin() gin.HandlerFunc { if !metrics {
return func(c *gin.Context) {} opts = append(opts, bunotel.WithMeterProvider(metricnoop.NewMeterProvider()))
} }
return bunotel.NewQueryHook(
func InstrumentBun() bun.QueryHook { opts...,
return nil )
} }

View file

@ -15,24 +15,24 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
//go:build !nometrics //go:build !nootel
package metrics package observability
import ( import (
"context" "context"
"errors" "errors"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/gin-gonic/gin"
"github.com/technologize/otel-go-contrib/otelginmetrics" "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"
"go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
sdk "go.opentelemetry.io/otel/sdk/metric" sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/exemplar"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0" semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
) )
@ -41,7 +41,7 @@ const (
serviceName = "GoToSocial" serviceName = "GoToSocial"
) )
func Initialize(db db.DB) error { func InitializeMetrics(db db.DB) error {
if !config.GetMetricsEnabled() { if !config.GetMetricsEnabled() {
return nil return nil
} }
@ -66,6 +66,7 @@ func Initialize(db db.DB) error {
} }
meterProvider := sdk.NewMeterProvider( meterProvider := sdk.NewMeterProvider(
sdk.WithExemplarFilter(exemplar.AlwaysOffFilter),
sdk.WithResource(r), sdk.WithResource(r),
sdk.WithReader(prometheusExporter), sdk.WithReader(prometheusExporter),
) )
@ -127,12 +128,6 @@ func Initialize(db db.DB) error {
return nil return nil
} }
func InstrumentGin() gin.HandlerFunc { func MetricsMiddleware() gin.HandlerFunc {
return otelginmetrics.Middleware(serviceName) return otelginmetrics.Middleware(serviceName)
} }
func InstrumentBun() bun.QueryHook {
return bunotel.NewQueryHook(
bunotel.WithMeterProvider(otel.GetMeterProvider()),
)
}

View file

@ -15,30 +15,32 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
//go:build nometrics //go:build nootel
package metrics package observability
import ( import (
"errors" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/uptrace/bun" "github.com/uptrace/bun"
) )
func Initialize(db db.DB) error { func InitializeMetrics(db db.DB) error {
if config.GetMetricsEnabled() { return nil
return errors.New("metrics was disabled at build time") }
} func InitializeTracing() error {
return nil return nil
} }
func InstrumentGin() gin.HandlerFunc { func MetricsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {} return nil
} }
func InstrumentBun() bun.QueryHook { func TracingMiddleware() gin.HandlerFunc {
return nil
}
func InstrumentBun(traces bool, metrics bool) bun.QueryHook {
return nil return nil
} }

View file

@ -15,9 +15,9 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
//go:build !notracing //go:build !nootel
package tracing package observability
import ( import (
"context" "context"
@ -25,8 +25,6 @@ import (
"codeberg.org/gruf/go-kv" "codeberg.org/gruf/go-kv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/uptrace/bun"
"github.com/uptrace/bun/extra/bunotel"
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
@ -45,10 +43,10 @@ import (
const ( const (
tracerKey = "gotosocial-server-tracer" 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() { if !config.GetTracingEnabled() {
return nil return nil
} }
@ -119,7 +117,7 @@ func Initialize() error {
// InstrumentGin is a middleware injecting tracing information based on the // InstrumentGin is a middleware injecting tracing information based on the
// otelgin implementation found at // otelgin implementation found at
// https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/github.com/gin-gonic/gin/otelgin/gintrace.go // 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() provider := otel.GetTracerProvider()
tracer := provider.Tracer( tracer := provider.Tracer(
tracerName, tracerName,
@ -182,9 +180,3 @@ func InjectRequestID() gin.HandlerFunc {
} }
} }
} }
func InstrumentBun() bun.QueryHook {
return bunotel.NewQueryHook(
bunotel.WithFormattedQueries(true),
)
}

View file

@ -17,8 +17,7 @@ GO_GCFLAGS=${GO_GCFLAGS-}
# Available Go build tags, with explanation, followed by benefits of enabling it: # Available Go build tags, with explanation, followed by benefits of enabling it:
# - kvformat: enables prettier output of log fields (slightly better performance) # - 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) # - 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) # - nootel: disables compiling-in otel support (reduced binary size)
# - nometrics: disables compiling-in otel metrics support (reduced binary size, better performance)
# - noerrcaller: disables caller function prefix in errors (slightly better performance, at cost of err readability) # - 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) # - 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) # - debugenv: enables /debug/pprof endpoint if DEBUG=1 env during runtime (adds debug, at performance cost)