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
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
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/
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
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
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
// 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
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
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:
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_ENDPOINTOTEL_EXPORTER_OTLP_TRACES_ENDPOINTOTEL_EXPORTER_OTLP_METRICS_ENDPOINTOTEL_EXPORTER_OTLP_LOGS_ENDPOINT
Endpoint (OTLP/gRPC):
- Default:
http://localhost:4317[1] - Env vars:
OTEL_EXPORTER_OTLP_ENDPOINTOTEL_EXPORTER_OTLP_TRACES_ENDPOINTOTEL_EXPORTER_OTLP_METRICS_ENDPOINTOTEL_EXPORTER_OTLP_LOGS_ENDPOINT
Insecure:
- Default:
false - Env vars:
OTEL_EXPORTER_OTLP_INSECUREOTEL_EXPORTER_OTLP_TRACES_INSECUREOTEL_EXPORTER_OTLP_METRICS_INSECUREOTEL_EXPORTER_OTLP_LOGS_INSECURE
Timeout: Maximum time the OTLP exporter will wait for each batch export.
- Default: 10s
- Env vars:
OTEL_EXPORTER_OTLP_TIMEOUTOTEL_EXPORTER_OTLP_TRACES_TIMEOUTOTEL_EXPORTER_OTLP_METRICS_TIMEOUTOTEL_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_PROTOCOLOTEL_EXPORTER_OTLP_TRACES_PROTOCOLOTEL_EXPORTER_OTLP_METRICS_PROTOCOLOTEL_EXPORTER_OTLP_LOGS_PROTOCOL
