From 3e1edf07d946d7a556ceb45cf20e378bb536bcde Mon Sep 17 00:00:00 2001 From: Keepers Date: Thu, 7 Nov 2024 16:51:09 -0700 Subject: [PATCH 1/2] fix up otel init Otel initialization is still somewhat hacky, but we're moving towards a cleaner implementation This change introduces the metrics (aka, meter) producer, and moves all producers (trace, log, metrics) into their own constructor funcs. Next PR will use this provider to implement a metrics api. --- clog/builder.go | 6 +- clog/logger.go | 17 +-- clues_test.go | 25 +++-- go.mod | 6 +- go.sum | 8 ++ internal/node/otel.go | 252 +++++++++++++++++++++++++++++++++--------- 6 files changed, 241 insertions(+), 73 deletions(-) diff --git a/clog/builder.go b/clog/builder.go index f5139d4..c78b3e7 100644 --- a/clog/builder.go +++ b/clog/builder.go @@ -126,8 +126,10 @@ func (b builder) log(l logLevel, msg string) { // add otel logging if provided otelLogger := b.otel - if otelLogger == nil { - otelLogger = cluesNode.OTELLogger() + if otelLogger == nil && + cluesNode.OTEL != nil && + cluesNode.OTEL.Logger != nil { + otelLogger = cluesNode.OTEL.Logger } if otelLogger != nil { diff --git a/clog/logger.go b/clog/logger.go index da7f66d..3a59325 100644 --- a/clog/logger.go +++ b/clog/logger.go @@ -7,7 +7,7 @@ import ( "sync" "time" - otellog "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -23,7 +23,7 @@ var ( ) type clogger struct { - otel otellog.Logger + otel log.Logger zsl *zap.SugaredLogger set Settings } @@ -146,12 +146,15 @@ func singleton(ctx context.Context, set Settings) *clogger { zsl := genLogger(set) - otelLogger := clues.In(ctx).OTELLogger() - cloggerton = &clogger{ - otel: otelLogger, - zsl: zsl, - set: set, + zsl: zsl, + set: set, + } + + node := clues.In(ctx) + + if node.OTEL != nil && node.OTEL.Logger != nil { + cloggerton.otel = node.OTEL.Logger } return cloggerton diff --git a/clues_test.go b/clues_test.go index 4234bda..c5ee58c 100644 --- a/clues_test.go +++ b/clues_test.go @@ -8,6 +8,8 @@ import ( "regexp" "testing" + "github.com/stretchr/testify/require" + "github.com/alcionai/clues" "github.com/alcionai/clues/cecrets" "github.com/alcionai/clues/internal/tester" @@ -104,25 +106,30 @@ func TestAddSpan(t *testing.T) { } for _, test := range table { for _, init := range []bool{true, false} { - t.Run(test.name, func(t *testing.T) { + tname := fmt.Sprintf("%s-%v", test.name, init) + + t.Run(tname, func(t *testing.T) { ctx := context.Background() if init { ocfg := clues.OTELConfig{GRPCEndpoint: "localhost:4317"} ictx, err := clues.InitializeOTEL(ctx, test.name, ocfg) + require.NoError(t, err, "initializing otel") if err != nil { - t.Error("initializing clues", err) return } - defer func() { - err := clues.Close(ictx) - if err != nil { - t.Error("closing clues:", err) - return - } - }() + // FIXME: this is causing failures at the moment which are non-trivial to + // hack around. Will need to return to it for more complete otel/grpc testing. + // suggestion: https://github.com/pellared/opentelemetry-go-contrib/blob/8f8e9b60693177b91af45d0495289fc52aa5c50e/instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_test.go#L88 + // defer func() { + // err := clues.Close(ictx) + // require.NoError(t, err, "closing clues") + // if err != nil { + // return + // } + // }() ctx = ictx } diff --git a/go.mod b/go.mod index 1f45e2e..5b7c6e3 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,14 @@ require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/otel v1.31.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.7.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 go.opentelemetry.io/otel/log v0.7.0 + go.opentelemetry.io/otel/metric v1.31.0 go.opentelemetry.io/otel/sdk v1.31.0 + go.opentelemetry.io/otel/sdk/log v0.7.0 + go.opentelemetry.io/otel/sdk/metric v1.31.0 go.opentelemetry.io/otel/trace v1.31.0 go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f @@ -24,7 +29,6 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect - go.opentelemetry.io/otel/metric v1.31.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/net v0.30.0 // indirect diff --git a/go.sum b/go.sum index 76d2394..8a2318b 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.7.0 h1:iNba3cIZTDPB2+IAbVY/3TUN+pCCLrNYo2GaGtsKBak= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.7.0/go.mod h1:l5BDPiZ9FbeejzWTAX6BowMzQOM/GeaUQ6lr3sOcSkc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU= @@ -37,6 +41,10 @@ go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozR go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/log v0.7.0 h1:dXkeI2S0MLc5g0/AwxTZv6EUEjctiH8aG14Am56NTmQ= +go.opentelemetry.io/otel/sdk/log v0.7.0/go.mod h1:oIRXpW+WD6M8BuGj5rtS0aRu/86cbDV/dAfNaZBIjYM= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= diff --git a/internal/node/otel.go b/internal/node/otel.go index 66a2215..f75fd44 100644 --- a/internal/node/otel.go +++ b/internal/node/otel.go @@ -4,14 +4,21 @@ import ( "context" "fmt" "net/http" + "time" "github.com/alcionai/clues/internal/stringify" + "github.com/pkg/errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" - otellog "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/global" + "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" + sdkLog "go.opentelemetry.io/otel/sdk/log" + sdkMetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" sdkTrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.26.0" @@ -25,11 +32,17 @@ import ( // ------------------------------------------------------------ type OTELClient struct { - grpcConn *grpc.ClientConn - traceProvider *sdkTrace.TracerProvider - tracer trace.Tracer - logger otellog.Logger - serviceName string + serviceName string + grpcConn *grpc.ClientConn + + LoggerProvider *sdkLog.LoggerProvider + Logger log.Logger + + MeterProvider *sdkMetric.MeterProvider + Meter metric.Meter + + TracerProvider *sdkTrace.TracerProvider + Tracer trace.Tracer } func (cli *OTELClient) Close(ctx context.Context) error { @@ -37,13 +50,37 @@ func (cli *OTELClient) Close(ctx context.Context) error { return nil } - if cli.traceProvider != nil { - err := cli.traceProvider.ForceFlush(ctx) + if cli.MeterProvider != nil { + err := cli.MeterProvider.ForceFlush(ctx) + if err != nil { + fmt.Println("forcing meter provider flush:", err) + } + + err = cli.MeterProvider.Shutdown(ctx) + if err != nil { + return fmt.Errorf("shutting down otel meterprovider: %w", err) + } + } + + if cli.LoggerProvider != nil { + err := cli.LoggerProvider.ForceFlush(ctx) if err != nil { fmt.Println("forcing trace provider flush:", err) } - err = cli.traceProvider.Shutdown(ctx) + cli.LoggerProvider.Shutdown(ctx) + if err != nil { + return fmt.Errorf("shutting down otel logger provider: %w", err) + } + } + + if cli.TracerProvider != nil { + err := cli.TracerProvider.ForceFlush(ctx) + if err != nil { + fmt.Println("forcing trace provider flush:", err) + } + + err = cli.TracerProvider.Shutdown(ctx) if err != nil { return fmt.Errorf("shutting down otel trace provider: %w", err) } @@ -85,35 +122,106 @@ func NewOTELClient( config OTELConfig, ) (*OTELClient, error) { // -- Resource - srvResource, err := resource.New(ctx, resource.WithAttributes( + server, err := resource.New(ctx, resource.WithAttributes( semconv.ServiceNameKey.String(serviceName))) if err != nil { - return nil, fmt.Errorf("creating otel resource: %w", err) + return nil, errors.Wrap(err, "creating otel server resource") } - // -- Exporter + // -- Client - conn, err := grpc.NewClient( - config.GRPCEndpoint, - // Note the use of insecure transport here. TLS is recommended in production. - grpc.WithTransportCredentials(insecure.NewCredentials())) + client := OTELClient{} + + // just a qol wrapper for shutting down on errors in this constructor. + closeClient := func() { + err := client.Close(ctx) + if err != nil { + fmt.Println("err closing client: %w", err) + } + } + + // -- grpc client + + // Note the use of insecure transport here. TLS is recommended in production. + creds := grpc.WithTransportCredentials(insecure.NewCredentials()) + + client.grpcConn, err = grpc.NewClient(config.GRPCEndpoint, creds) if err != nil { return nil, fmt.Errorf("creating new grpc connection: %w", err) } - exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn)) + // -- Tracing + + client.TracerProvider, err = newTracerProvider(ctx, client.grpcConn, server) if err != nil { - return nil, fmt.Errorf("creating a trace exporter: %w", err) + closeClient() + return nil, errors.Wrap(err, "generating a tracerProvider") } - // -- TracerProvider + // set propagation to include traceContext and baggage (the default is no-op). + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{})) + otel.SetTracerProvider(client.TracerProvider) + client.Tracer = client.TracerProvider.Tracer(serviceName + "/tracer") + + // -- Logging + + // generate a logger provider + // LoggerProvider := global.GetLoggerProvider() + client.LoggerProvider, err = newLoggerProvider(ctx, client.grpcConn, server) + if err != nil { + closeClient() + return nil, errors.Wrap(err, "generating a tracerProvider") + } + + global.SetLoggerProvider(client.LoggerProvider) + client.Logger = client.LoggerProvider.Logger(serviceName) + + // -- Metrics + + client.MeterProvider, err = newMeterProvider(ctx, client.grpcConn, server) + if err != nil { + closeClient() + return nil, errors.Wrap(err, "generating a tracerProvider") + } + + otel.SetMeterProvider(client.MeterProvider) + client.Meter = client.MeterProvider.Meter(serviceName) + + // Shutdown will flush any remaining spans and shut down the exporter. + return &client, nil +} + +// newTracerProvider constructs a new tracer that manages batch exports +// of tracing values. +func newTracerProvider( + ctx context.Context, + conn *grpc.ClientConn, + server *resource.Resource, +) (*sdkTrace.TracerProvider, error) { + if ctx != nil { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, 5*time.Second) + defer cancel() + } + + exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn)) + if err != nil { + return nil, errors.Wrap(err, "constructing a tracer exporter") + } // Register the trace exporter with a TracerProvider, using a batch // span processor to aggregate spans before export. batchSpanProcessor := sdkTrace.NewBatchSpanProcessor(exporter) tracerProvider := sdkTrace.NewTracerProvider( - sdkTrace.WithResource(srvResource), + sdkTrace.WithResource(server), + // FIXME: need to investigate other options... + // * case handling for parent-not-sampled + // * blocking on full queue + // * max queue size + // FIXME: need to refine trace sampling. sdkTrace.WithSampler(sdkTrace.AlwaysSample()), sdkTrace.WithSpanProcessor(batchSpanProcessor), sdkTrace.WithRawSpanLimits(sdkTrace.SpanLimits{ @@ -125,32 +233,78 @@ func NewOTELClient( LinkCountLimit: -1, })) - // set global propagator to traceContext (the default is no-op). - otel.SetTextMapPropagator(propagation.TraceContext{}) - otel.SetTracerProvider(tracerProvider) + return tracerProvider, nil +} - // -- Logger +// newMeterProvider constructs a new meter that manages batch exports +// of metrics. +func newMeterProvider( + ctx context.Context, + conn *grpc.ClientConn, + server *resource.Resource, +) (*sdkMetric.MeterProvider, error) { + if ctx != nil { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, 5*time.Second) + defer cancel() + } - // generate a logger provider - logProvider := global.GetLoggerProvider() + exporter, err := otlpmetricgrpc.New( + ctx, + otlpmetricgrpc.WithGRPCConn(conn), + otlpmetricgrpc.WithCompressor("gzip")) + if err != nil { + return nil, errors.Wrap(err, "constructing a meter exporter") + } - // -- Client + periodicReader := sdkMetric.NewPeriodicReader( + exporter, + sdkMetric.WithInterval(1*time.Minute)) + + meterProvider := sdkMetric.NewMeterProvider( + sdkMetric.WithResource(server), + // FIXME: need to investigate other options... + // * view + // * interval + // * aggregation + // * temporality + sdkMetric.WithReader(periodicReader)) + + return meterProvider, nil +} - client := OTELClient{ - grpcConn: conn, - traceProvider: tracerProvider, - tracer: tracerProvider.Tracer(serviceName + "/tracer"), - logger: logProvider.Logger(serviceName), +// newLoggerProvider constructs a new logger that manages batch exports +// of logs. +func newLoggerProvider( + ctx context.Context, + conn *grpc.ClientConn, + server *resource.Resource, +) (*sdkLog.LoggerProvider, error) { + if ctx != nil { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, 5*time.Second) + defer cancel() } - // Shutdown will flush any remaining spans and shut down the exporter. - return &client, nil + exporter, err := otlploggrpc.New(ctx, otlploggrpc.WithGRPCConn(conn)) + if err != nil { + return nil, errors.Wrap(err, "constructing a logger exporter") + } + + loggerProvider := sdkLog.NewLoggerProvider( + sdkLog.WithResource(server), + // FIXME: need to investigate other options... + // * interval + // * buffer size + // * count limit + // * value length limit + sdkLog.WithProcessor(sdkLog.NewBatchProcessor(exporter))) + + return loggerProvider, nil } // ------------------------------------------------------------ // annotations. basically otel's version of With() -// Not currently used; we're just mashing everything in as a -// string right now, same as clues does. // ------------------------------------------------------------ type Annotation struct { @@ -171,23 +325,23 @@ func (a Annotation) IsAttribute() bool { return a.kind == "attribute" } -func (a Annotation) KV() otellog.KeyValue { +func (a Annotation) KV() log.KeyValue { if a.kind != "attribute" { - return otellog.KeyValue{} + return log.KeyValue{} } // FIXME: needs extensive type support switch a.v.(type) { case int: - return otellog.Int(a.k, a.v.(int)) + return log.Int(a.k, a.v.(int)) case int64: - return otellog.Int64(a.k, a.v.(int64)) + return log.Int64(a.k, a.v.(int64)) case string: - return otellog.String(a.k, a.v.(string)) + return log.String(a.k, a.v.(string)) case bool: - return otellog.Bool(a.k, a.v.(bool)) + return log.Bool(a.k, a.v.(bool)) default: // everything else gets stringified - return otellog.String(a.k, stringify.Marshal(a.v, false)) + return log.String(a.k, stringify.Marshal(a.v, false)) } } @@ -273,7 +427,7 @@ func (dn *Node) AddSpan( return ctx, dn } - ctx, span := dn.OTEL.tracer.Start(ctx, name) + ctx, span := dn.OTEL.Tracer.Start(ctx, name) spawn := dn.SpawnDescendant() spawn.Span = span @@ -310,13 +464,3 @@ func (dn *Node) AddSpanAttributes( dn.Span.SetAttributes(attribute.String(k, stringify.Marshal(v, false))) } } - -// OTELLogger gets the otel logger instance from the otel client. -// Returns nil if otel wasn't initialized. -func (dn *Node) OTELLogger() otellog.Logger { - if dn == nil || dn.OTEL == nil { - return nil - } - - return dn.OTEL.logger -} From 7fa716668bb12af49122066d5824b99ce793e716 Mon Sep 17 00:00:00 2001 From: keepers Date: Tue, 3 Dec 2024 14:17:57 -0700 Subject: [PATCH 2/2] fix up nil context return, error messages --- internal/node/node_test.go | 30 +++++++++++++++++------------- internal/node/otel.go | 36 +++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/internal/node/node_test.go b/internal/node/node_test.go index ccfe5f6..d9298b2 100644 --- a/internal/node/node_test.go +++ b/internal/node/node_test.go @@ -14,31 +14,35 @@ import ( func TestNode_Init(t *testing.T) { table := []struct { - name string - node *Node - ctx context.Context + name string + node *Node + ctx context.Context + wantErr require.ErrorAssertionFunc }{ { - name: "nil ctx", - node: &Node{}, - ctx: nil, + name: "nil ctx", + node: &Node{}, + ctx: nil, + wantErr: require.Error, }, { - name: "nil node", - node: nil, - ctx: context.Background(), + name: "nil node", + node: nil, + ctx: context.Background(), + wantErr: require.NoError, }, { - name: "context.Context", - node: &Node{}, - ctx: context.Background(), + name: "context.Context", + node: &Node{}, + ctx: context.Background(), + wantErr: require.NoError, }, } for _, test := range table { t.Run(test.name, func(t *testing.T) { err := test.node.InitOTEL(test.ctx, test.name, OTELConfig{}) - require.NoError(t, err) + test.wantErr(t, err) }) } } diff --git a/internal/node/otel.go b/internal/node/otel.go index f75fd44..efa3822 100644 --- a/internal/node/otel.go +++ b/internal/node/otel.go @@ -155,7 +155,7 @@ func NewOTELClient( client.TracerProvider, err = newTracerProvider(ctx, client.grpcConn, server) if err != nil { closeClient() - return nil, errors.Wrap(err, "generating a tracerProvider") + return nil, errors.Wrap(err, "generating a tracer provider") } // set propagation to include traceContext and baggage (the default is no-op). @@ -172,7 +172,7 @@ func NewOTELClient( client.LoggerProvider, err = newLoggerProvider(ctx, client.grpcConn, server) if err != nil { closeClient() - return nil, errors.Wrap(err, "generating a tracerProvider") + return nil, errors.Wrap(err, "generating a logger provider") } global.SetLoggerProvider(client.LoggerProvider) @@ -183,7 +183,7 @@ func NewOTELClient( client.MeterProvider, err = newMeterProvider(ctx, client.grpcConn, server) if err != nil { closeClient() - return nil, errors.Wrap(err, "generating a tracerProvider") + return nil, errors.Wrap(err, "generating a meter provider") } otel.SetMeterProvider(client.MeterProvider) @@ -200,12 +200,14 @@ func newTracerProvider( conn *grpc.ClientConn, server *resource.Resource, ) (*sdkTrace.TracerProvider, error) { - if ctx != nil { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, 5*time.Second) - defer cancel() + if ctx == nil { + return nil, errors.New("nil ctx") } + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, 5*time.Second) + defer cancel() + exporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn)) if err != nil { return nil, errors.Wrap(err, "constructing a tracer exporter") @@ -243,12 +245,14 @@ func newMeterProvider( conn *grpc.ClientConn, server *resource.Resource, ) (*sdkMetric.MeterProvider, error) { - if ctx != nil { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, 5*time.Second) - defer cancel() + if ctx == nil { + return nil, errors.New("nil ctx") } + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, 5*time.Second) + defer cancel() + exporter, err := otlpmetricgrpc.New( ctx, otlpmetricgrpc.WithGRPCConn(conn), @@ -280,12 +284,14 @@ func newLoggerProvider( conn *grpc.ClientConn, server *resource.Resource, ) (*sdkLog.LoggerProvider, error) { - if ctx != nil { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, 5*time.Second) - defer cancel() + if ctx == nil { + return nil, errors.New("nil ctx") } + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, 5*time.Second) + defer cancel() + exporter, err := otlploggrpc.New(ctx, otlploggrpc.WithGRPCConn(conn)) if err != nil { return nil, errors.Wrap(err, "constructing a logger exporter")