diff --git a/example/example.go b/example/example.go index 1b9c52a..3c5f306 100644 --- a/example/example.go +++ b/example/example.go @@ -41,15 +41,15 @@ func run(ctx context.Context, opts ...service.Option) { done := make(chan struct{}) ready := make(chan struct{}) - otel.Configure( - // otel.WithDSN("http://127.0.0.1:4318"), + p := otel.Configure( + ctx, otel.WithServiceName(name), otel.WithServiceVersion(version), otel.WithDeploymentEnvironment("tests"), otel.WithTraceSampler(sdktrace.AlwaysSample()), otel.WithMetricPrometheusBridge(), ) - defer otel.Shutdown(context.WithoutCancel(ctx)) + defer p.Shutdown(ctx) address := "0.0.0.0:9991" diff --git a/otel/client.go b/otel/client.go index 504a91b..1d25698 100644 --- a/otel/client.go +++ b/otel/client.go @@ -15,6 +15,17 @@ import ( const dummySpanName = "__dummy__" +type Provider interface { + TraceURL(span trace.Span) string + ReportError(ctx context.Context, err error, opts ...trace.EventOption) + ReportPanic(ctx context.Context, val any) + Shutdown(ctx context.Context) error + ForceFlush(ctx context.Context) error + TracerProvider() *sdktrace.TracerProvider + MeterProvider() *sdkmetric.MeterProvider + LoggerProvider() *sdklog.LoggerProvider +} + type client struct { dsn *DSN tracer trace.Tracer @@ -32,6 +43,7 @@ func newClient(dsn *DSN) *client { } func (c *client) Shutdown(ctx context.Context) (lastErr error) { + ctx = context.WithoutCancel(ctx) if c.tp != nil { if err := c.tp.Shutdown(ctx); err != nil { lastErr = err @@ -99,6 +111,30 @@ func (c *client) ReportPanic(ctx context.Context, val any) { } } +func (c *client) TracerProvider() *sdktrace.TracerProvider { + if c.tp == nil { + return sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithIDGenerator(newIDGenerator()), + ) + } + return c.tp +} + +func (c *client) MeterProvider() *sdkmetric.MeterProvider { + if c.mp == nil { + return sdkmetric.NewMeterProvider() + } + return c.mp +} + +func (c *client) LoggerProvider() *sdklog.LoggerProvider { + if c.lp == nil { + return sdklog.NewLoggerProvider() + } + return c.lp +} + func (c *client) reportPanic(ctx context.Context, val interface{}) { span := trace.SpanFromContext(ctx) if !span.IsRecording() { diff --git a/otel/config.go b/otel/config.go index ee8fd2f..a00f812 100644 --- a/otel/config.go +++ b/otel/config.go @@ -12,6 +12,8 @@ import ( "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + + "go.linka.cloud/grpc-toolkit/logger" ) type config struct { @@ -62,7 +64,8 @@ func newConfig(opts []Option) *config { return conf } -func (conf *config) newResource() *resource.Resource { +func (conf *config) newResource(ctx context.Context) *resource.Resource { + log := logger.C(ctx) if conf.resource != nil { if len(conf.resourceAttributes) > 0 { log.Warnf("WithResource overrides WithResourceAttributes (discarding %v)", @@ -75,8 +78,6 @@ func (conf *config) newResource() *resource.Resource { return conf.resource } - ctx := context.TODO() - res, err := resource.New(ctx, resource.WithFromEnv(), resource.WithTelemetrySDK(), diff --git a/otel/logging.go b/otel/logging.go index 51a9db0..502e34c 100644 --- a/otel/logging.go +++ b/otel/logging.go @@ -7,11 +7,14 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp" "go.opentelemetry.io/otel/log/global" sdklog "go.opentelemetry.io/otel/sdk/log" + + "go.linka.cloud/grpc-toolkit/logger" ) func configureLogging(ctx context.Context, conf *config) *sdklog.LoggerProvider { + log := logger.C(ctx) var opts []sdklog.LoggerProviderOption - if res := conf.newResource(); res != nil { + if res := conf.newResource(ctx); res != nil { opts = append(opts, sdklog.WithResource(res)) } diff --git a/otel/metrics.go b/otel/metrics.go index 77d1f8e..06b72fc 100644 --- a/otel/metrics.go +++ b/otel/metrics.go @@ -10,11 +10,14 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/metricdata" + + "go.linka.cloud/grpc-toolkit/logger" ) func configureMetrics(ctx context.Context, conf *config) *sdkmetric.MeterProvider { + log := logger.C(ctx) opts := conf.metricOptions - if res := conf.newResource(); res != nil { + if res := conf.newResource(ctx); res != nil { opts = append(opts, sdkmetric.WithResource(res)) } diff --git a/otel/otel.go b/otel/otel.go index f7c643b..ea44615 100644 --- a/otel/otel.go +++ b/otel/otel.go @@ -4,17 +4,14 @@ import ( "context" "os" "strings" - "sync/atomic" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" - sdktrace "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/trace" "go.linka.cloud/grpc-toolkit/logger" ) -var log = logger.StandardLogger().WithField("name", "otel") +var dummy = newClient(&DSN{}) // Configure configures OpenTelemetry. // By default, it: @@ -23,27 +20,28 @@ var log = logger.StandardLogger().WithField("name", "otel") // - sets tracecontext + baggage composite context propagator. // // You can use OTEL_DISABLED env var to completely skip otel configuration. -func Configure(opts ...Option) { +func Configure(ctx context.Context, opts ...Option) Provider { if _, ok := os.LookupEnv("OTEL_DISABLED"); ok { - return + return dummy } - ctx := context.TODO() + log := logger.C(ctx) + conf := newConfig(opts) if !conf.tracingEnabled && !conf.metricsEnabled && !conf.loggingEnabled { - return + return dummy } if len(conf.dsn) == 0 { log.Warn("no DSN provided (otel-go is disabled)") - return + return dummy } dsn, err := ParseDSN(conf.dsn[0]) if err != nil { log.Warnf("invalid DSN: %s (otel is disabled)", err) - return + return dummy } if strings.HasSuffix(dsn.Host, ":4318") { @@ -63,7 +61,7 @@ func Configure(opts ...Option) { client.lp = configureLogging(ctx, conf) } - atomicClient.Store(client) + return client } func configurePropagator(conf *config) { @@ -76,47 +74,3 @@ func configurePropagator(conf *config) { } otel.SetTextMapPropagator(textMapPropagator) } - -// ------------------------------------------------------------------------------ - -var ( - fallbackClient = newClient(&DSN{}) - atomicClient atomic.Value -) - -func activeClient() *client { - v := atomicClient.Load() - if v == nil { - return fallbackClient - } - return v.(*client) -} - -func TraceURL(span trace.Span) string { - return activeClient().TraceURL(span) -} - -func ReportError(ctx context.Context, err error, opts ...trace.EventOption) { - activeClient().ReportError(ctx, err, opts...) -} - -func ReportPanic(ctx context.Context, val any) { - activeClient().ReportPanic(ctx, val) -} - -func Shutdown(ctx context.Context) error { - return activeClient().Shutdown(ctx) -} - -func ForceFlush(ctx context.Context) error { - return activeClient().ForceFlush(ctx) -} - -func TracerProvider() *sdktrace.TracerProvider { - return activeClient().tp -} - -// SetLogger sets the logger to the given one. -func SetLogger(logger logger.Logger) { - log = logger -} diff --git a/otel/tracing.go b/otel/tracing.go index 60c5aaa..e214f58 100644 --- a/otel/tracing.go +++ b/otel/tracing.go @@ -15,15 +15,18 @@ import ( "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" + + "go.linka.cloud/grpc-toolkit/logger" ) func configureTracing(ctx context.Context, conf *config) *sdktrace.TracerProvider { + log := logger.C(ctx) provider := conf.tracerProvider if provider == nil { var opts []sdktrace.TracerProviderOption opts = append(opts, sdktrace.WithIDGenerator(newIDGenerator())) - if res := conf.newResource(); res != nil { + if res := conf.newResource(ctx); res != nil { opts = append(opts, sdktrace.WithResource(res)) } if conf.traceSampler != nil {