Using open telemetry in Golang

In such a way that traces and metrics are emitted to the console.

You can use in other framework too look like gin

Connect open telemetry into gin

Open telemetry examples

https://github.com/open-telemetry/opentelemetry-go/tree/main/example

Get dependency of open telemetry in Golang

bash
go get "go.opentelemetry.io/otel" \  "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" \
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace" \  "go.opentelemetry.io/otel/propagation" \
"go.opentelemetry.io/otel/sdk/metric" \  "go.opentelemetry.io/otel/sdk/resource" \
"go.opentelemetry.io/otel/sdk/trace" \  "go.opentelemetry.io/otel/semconv/v1.21.0" \
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"

make otel.go file

go
package main
import (
"context"
"errors"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.21.0")//
setupOTelSDK bootstraps the OpenTelemetry pipeline.// If it does not
return an error, make sure to call shutdown for proper cleanup.func setupOTelSDK(ctx
context.Context, serviceName, serviceVersion string) (shutdown func(context.Context) error, err
error) {
    var shutdownFuncs []func(context.Context) error // shutdown calls cleanup
    functions registered via shutdownFuncs.// The errors from the calls are joined.// Each
    registered cleanup will be invoked once.shutdown =
    func(ctx context.Context) error {
        var err error for _, fn := range shutdownFuncs {
            err = errors.Join(err, fn(ctx))
        }
        shutdownFuncs = nil return err
    }
    // handleErr calls shutdown for cleanup and makes sure that all errors are returned.handleErr :=
    func(inErr error) {
        err = errors.Join(inErr, shutdown(ctx))
    }
    // Set up resource.res, err := newResource(serviceName, serviceVersion) if err != nil {
        handleErr(err) return
    }
    // Set up propagator.prop := newPropagator() otel.SetTextMapPropagator(prop) // Set up trace
    provider.tracerProvider, err := newTraceProvider(res) if err != nil {
        handleErr(err) return
    }
    shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown)
    otel.SetTracerProvider(tracerProvider) // Set up meter provider.meterProvider, err :=
    newMeterProvider(res) if err != nil {
        handleErr(err) return
    }
    shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown)
    otel.SetMeterProvider(meterProvider) return
}
func newResource(serviceName, serviceVersion string) (*resource.Resource, error) {
    return resource.Merge(resource.Default(), resource.NewWithAttributes(semconv.SchemaURL,
    semconv.ServiceName(serviceName), semconv.ServiceVersion(serviceVersion),))
}
func newPropagator() propagation.TextMapPropagator {
    return propagation.NewCompositeTextMapPropagator(propagation.TraceContext{
    }, propagation.Baggage{
    },)
}
func newTraceProvider(res *resource.Resource) (*trace.TracerProvider, error) {
    traceExporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) if err != nil {
        return nil, err
    }
    traceProvider := trace.NewTracerProvider(trace.WithBatcher(traceExporter, // Default is 5s.Set
    to 1s for demonstrative purposes.trace.WithBatchTimeout(time.Second)), trace.WithResource(res),)
    return traceProvider, nil
}
func newMeterProvider(res *resource.Resource) (*metric.MeterProvider, error) {
    metricExporter, err := stdoutmetric.New() if err != nil {
        return nil, err
    }
    meterProvider := metric.NewMeterProvider(metric.WithResource(res),
    metric.WithReader(metric.NewPeriodicReader(metricExporter, // Default is 1m.Set to 3s for
    demonstrative purposes.metric.WithInterval(3*time.Second))),)
    return meterProvider, nil
}

add Gin in open telemetry

https://signoz.io/blog/opentelemetry-gin/

https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/github.com/gin-gonic/gin/otelgin/example/server.go

go
func InitTracer(serviceName string) (*sdktrace.TracerProvider, error) {
    secureOption := otlptracegrpc.WithInsecure() exporter, err :=
    otlptrace.New(context.Background(), otlptracegrpc.NewClient(secureOption,
    otlptracegrpc.WithEndpoint(config.OtelEndpoint()),),) if err != nil {
        log.Fatal(err)
    }
    resources, err := resource.New(context.Background(),
    resource.WithAttributes(attribute.String("service.name", ServiceName()),),) if err != nil {
        log.Fatal("Could not set resources: ", err)
    }
    tp :=
    sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.TraceIDRatioBased(config.OtelSampleRate())), //sdktrace.AlwaysSample() sdktrace.WithBatcher(
    exporter), sdktrace.WithResource(resources),) otel.SetTracerProvider(tp) // Set up meter
    provider.metricExporter, err := stdoutmetric.New() if err != nil {
        return nil, err
    }
    meterProvider :=
    metric.NewMeterProvider(metric.WithReader(metric.NewPeriodicReader(metricExporter, // Default is
    1m.Set to 3s for demonstrative purposes.metric.WithInterval(3*time.Second))),
    metric.WithResource(resources),) otel.SetMeterProvider(meterProvider) //propagator
    otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{
    }, propagation.Baggage{
    })) TracerMain = tp.Tracer(serviceName) return tp, nil
}

Add metric with open telemetry

https://github.com/open-telemetry/opentelemetry-go/blob/main/example/dice/rolldice.go

ADD event

javascript
var span trace.Span ctx, span = tracer.Start(ctx, "operation") defer span.End()
span.AddEvent("Nice operation!", trace.WithAttributes(attribute.Int("bogons", 100)))
span.SetAttributes(anotherKey.String("yes")) if err := foo.SubOperation(ctx);
err != nil {
  panic(err)
}

Connect to prometeus

Sampling

go
type key intconst sampleKey key = iotatype sampler struct {
    delegate trace.Sampler
}
func New(delegate trace.Sampler) trace.Sampler {
    return &sampler{
        delegate: delegate
    }
}
func (s *sampler) ShouldSample(p trace.SamplingParameters) trace.SamplingResult {
    if _, ok := p.ParentContext.Value(sampleKey).(struct{
    });
    ok {
        return trace.SamplingResult{
            Decision: trace.RecordAndSample, Tracestate:
            traceapi.SpanContextFromContext(p.ParentContext).TraceState(),
        }
    }
    return s.delegate.ShouldSample(p)
}
func (s *sampler) Description() string {
    return fmt.Sprintf("my-sampler{%s}", s.delegate.Description())
}
func main() {
    s := New(trace.ParentBased(trace.TraceIDRatioBased(0.5))) tracer :=
    trace.NewTracerProvider(trace.WithSampler(s)).Tracer("my-app") ctx :=
    context.WithValue(context.Background(), sampleKey, struct{
    }
    {
    }) _, span := tracer.Start(ctx, "span name") fmt.Println(span.SpanContext().IsSampled())
}

Add graceful shutdown

go
// setupOTelSDK bootstraps the OpenTelemetry pipeline.// If it does not
return an error, make sure to call shutdown for proper cleanup.func SetupOTelSDK(ctx
context.Context, serviceName, serviceVersion string) (shutdown func(context.Context) error, err
error) {
    var shutdownFuncs []func(context.Context) error // shutdown calls cleanup
    functions registered via shutdownFuncs.// The errors from the calls are joined.// Each
    registered cleanup will be invoked once.shutdown =
    func(ctx context.Context) error {
        var err error for _, fn := range shutdownFuncs {
            err = errors.Join(err, fn(ctx))
        }
        shutdownFuncs = nil return err
    }
    // handleErr calls shutdown for cleanup and makes sure that all errors are returned.handleErr :=
    func(inErr error) {
        err = errors.Join(inErr, shutdown(ctx))
    }
    // Set up resource.res, err := newResource(serviceName, serviceVersion) if err != nil {
        handleErr(err) return
    }
    // Set up propagator.prop := newPropagator() otel.SetTextMapPropagator(prop) // Set up trace
    provider.tracerProvider, err := newTraceProvider(res) if err != nil {
        handleErr(err) return
    }
    shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown)
    otel.SetTracerProvider(tracerProvider) // Set up meter provider.meterProvider, err :=
    newMeterProvider(res) if err != nil {
        handleErr(err) return
    }
    shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown)
    otel.SetMeterProvider(meterProvider) return
}
func newResource(serviceName, serviceVersion string) (*resource.Resource, error) {
    return resource.Merge(resource.Default(), resource.NewWithAttributes(semconv.SchemaURL,
    semconv.ServiceName(serviceName), semconv.ServiceVersion(serviceVersion),))
}
func newPropagator() propagation.TextMapPropagator {
    return propagation.NewCompositeTextMapPropagator(propagation.TraceContext{
    }, propagation.Baggage{
    },)
}
func newTraceProvider(res *resource.Resource) (*trace.TracerProvider, error) {
    traceExporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) if err != nil {
        return nil, err
    }
    traceProvider := trace.NewTracerProvider(trace.WithBatcher(traceExporter, // Default is 5s.Set
    to 1s for demonstrative purposes.trace.WithBatchTimeout(time.Second)), trace.WithResource(res),)
    return traceProvider, nil
}
func newMeterProvider(res *resource.Resource) (*metric.MeterProvider, error) {
    metricExporter, err := stdoutmetric.New() if err != nil {
        return nil, err
    }
    meterProvider := metric.NewMeterProvider(metric.WithResource(res),
    metric.WithReader(metric.NewPeriodicReader(metricExporter, // Default is 1m.Set to 3s for
    demonstrative purposes.metric.WithInterval(3*time.Second))),)
    return meterProvider, nil
}

Add traceClientHttp

Initialize OpenTelemetry SDK for Go. Replace Endpoint and URLPath with the endpoint obtained in the Prerequisites section.

You can submit data by using OpenTelemetry SDK for Go or forward data by using the open source OpenTelemetry Collector.

  • If you submit data by using OpenTelemetry SDK for Go, replace Endpoint and URLPath with the endpoint obtained in the Prerequisites section.
  • If you forward data by using the open source OpenTelemetry Collector, replace Endpoint with the IP address of your on-premises service and delete the headers
go
func initProvider(
)
func() {
    ctx := context.Background()
    traceClientHttp := otlptracehttp.NewClient(otlptracehttp.WithEndpoint("127.0.XX.XX:8080"),
    //Replace Endpoint with the endpoint obtained in the Prerequisites
    section.otlptracehttp.WithURLPath("/adapt_xxxxx/api/otlp/traces"), //Replace URLPath with the
    endpoint obtained in the Prerequisites section.otlptracehttp.WithInsecure())
    otlptracehttp.WithCompression(1)
    traceExp, err := otlptrace.New(ctx, traceClientHttp)
    handleErr(err, "Failed to create the collector trace
exporter")
    res, err := resource.New(ctx, resource.WithFromEnv(), resource.WithProcess(),
    resource.WithTelemetrySDK(), resource.WithHost(), resource.WithAttributes(// Specify the service
    name displayed on the backend of Tracing
    Analysis.semconv.ServiceNameKey.String(common.ClientServiceName),),
    )
    handleErr(err, "failed to create resource")
    bsp := sdktrace.NewBatchSpanProcessor(traceExp)
    tracerProvider := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()),
    sdktrace.WithResource(res), sdktrace.WithSpanProcessor(bsp),
    )
    // Set the global propagator to tracecontext.The global propagator is not specified by
    default.otel.SetTextMapPropagator(propagation.TraceContext{
    })
    otel.SetTracerProvider(tracerProvider)
    log.Println("OTEL init success")
    return func() {
        cxt, cancel := context.WithTimeout(ctx, time.Second) defer cancel() if err :=
        traceExp.Shutdown(cxt);
        err != nil {
            otel.Handle(err)
        }
    }
}

Add console tracer

For run locally

go
exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())  if err != nil {  log.Fatal(err)}

if you had a “ failed to build resolver: passthrough: received empty target in Build() ” error in tests, then put below code for your open telemetry in test cases:

go
tp = SDKTrace.NewTracerProvider(  SDKTrace.WithSpanProcessor(sentryOtel.NewSentrySpanProcessor()), )
otel.SetTracerProvider(tp) otel.SetTextMapPropagator(sentryOtel.NewSentryPropagator()) Tracer =
tp.Tracer(serviceName) return tp}

OpenTelemetry Protocol Exporter

Endpoint (OTLP/HTTP):

When using OTEL_EXPORTER_OTLP_ENDPOINT, exporters MUST construct per-signal URLs as described below.

  • Default: http://localhost:4318 [1]
  • Env vars: OTEL_EXPORTER_OTLP_ENDPOINT OTEL_EXPORTER_OTLP_TRACES_ENDPOINT OTEL_EXPORTER_OTLP_METRICS_ENDPOINT OTEL_EXPORTER_OTLP_LOGS_ENDPOINT

Endpoint (OTLP/gRPC):

  • Default: http://localhost:4317 [1]
  • Env vars: OTEL_EXPORTER_OTLP_ENDPOINT OTEL_EXPORTER_OTLP_TRACES_ENDPOINT OTEL_EXPORTER_OTLP_METRICS_ENDPOINT OTEL_EXPORTER_OTLP_LOGS_ENDPOINT

Insecure:

  • Default: false
  • Env vars: OTEL_EXPORTER_OTLP_INSECURE OTEL_EXPORTER_OTLP_TRACES_INSECURE OTEL_EXPORTER_OTLP_METRICS_INSECURE OTEL_EXPORTER_OTLP_LOGS_INSECURE

Timeout: Maximum time the OTLP exporter will wait for each batch export.

  • Default: 10s
  • Env vars: OTEL_EXPORTER_OTLP_TIMEOUT OTEL_EXPORTER_OTLP_TRACES_TIMEOUT OTEL_EXPORTER_OTLP_METRICS_TIMEOUT OTEL_EXPORTER_OTLP_LOGS_TIMEOUT

Protocol: The transport protocol. Options MUST be one of: grpc, http/protobuf, http/json. See Specify Protocol for more details.

  • Default: http/protobuf
  • Env vars: OTEL_EXPORTER_OTLP_PROTOCOL OTEL_EXPORTER_OTLP_TRACES_PROTOCOL OTEL_EXPORTER_OTLP_METRICS_PROTOCOL OTEL_EXPORTER_OTLP_LOGS_PROTOCOL