module/apmotel/tracer.go (88 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package apmotel // import "go.elastic.co/apm/module/apmotel/v2" import ( "context" "time" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/embedded" "go.elastic.co/apm/v2" ) type tracer struct { provider *tracerProvider embedded.Tracer } func newTracer(p *tracerProvider) *tracer { return &tracer{p, nil} } // Start forwards the call to APM Agent func (t *tracer) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { config := trace.NewSpanStartConfig(opts...) startTime := config.Timestamp() if startTime.IsZero() { startTime = time.Now() } s := &span{ provider: t.provider, attributes: config.Attributes(), spanKind: config.SpanKind(), startTime: startTime, } var links []apm.SpanLink for _, l := range config.Links() { links = append(links, apm.SpanLink{ Trace: [16]byte(l.SpanContext.TraceID()), Span: [8]byte(l.SpanContext.SpanID()), }) } var psc trace.SpanContext if config.NewRoot() { ctx = trace.ContextWithSpanContext(ctx, psc) } else { // If not root, check if a *span is already present, if it's not, // attempt to obtain an APM transaction from the context. psc = trace.SpanContextFromContext(ctx) s.spanContext = psc var parent *span var ok bool if parent, ok = trace.SpanFromContext(ctx).(*span); !ok { // Try to find a obtain an APM transaction from the agent context. if tx := apm.TransactionFromContext(ctx); tx != nil { parent = &span{tx: tx} } } // Use the parent if it exists. Otherwise, we'll create a new // transaction using the trace context from `psc`. if parent != nil { // This is a child span. Create a span, not a transaction. // The parent may be a span or a transaction. var tc apm.TraceContext if parent.span != nil { tc = parent.span.TraceContext() } else { tc = parent.tx.TraceContext() } s.span = parent.tx.StartSpanOptions(spanName, "", apm.SpanOptions{ Parent: tc, Start: startTime, Links: links, }) ctx = apm.ContextWithSpan(ctx, s.span) s.tx = parent.tx s.spanContext = trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID(s.span.TraceContext().Trace), SpanID: trace.SpanID(s.span.TraceContext().Span), TraceFlags: trace.TraceFlags(0).WithSampled(s.tx.Sampled()), }) return trace.ContextWithSpan(ctx, s), s } } tranOpts := apm.TransactionOptions{ Links: links, } if psc.HasTraceID() && psc.HasSpanID() { tranOpts.TraceContext = apm.TraceContext{ Trace: [16]byte(psc.TraceID()), Span: [8]byte(psc.SpanID()), Options: apm.TraceOptions(0).WithRecorded(psc.IsSampled()), } } s.tx = t.provider.apmTracer.StartTransactionOptions(spanName, "", tranOpts) ctx = apm.ContextWithTransaction(ctx, s.tx) s.spanContext = trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID(s.tx.TraceContext().Trace), SpanID: trace.SpanID(s.tx.TraceContext().Span), TraceFlags: trace.TraceFlags(0).WithSampled(s.tx.Sampled()), }) return trace.ContextWithSpan(ctx, s), s }