gocontext.go (113 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 apm // import "go.elastic.co/apm/v2"
import (
"context"
)
// ContextWithSpan returns a copy of parent in which the given span
// is stored, associated with the key ContextSpanKey.
func ContextWithSpan(parent context.Context, s *Span) context.Context {
return OverrideContextWithSpan(parent, s)
}
// ContextWithTransaction returns a copy of parent in which the given
// transaction is stored, associated with the key ContextTransactionKey.
func ContextWithTransaction(parent context.Context, t *Transaction) context.Context {
return OverrideContextWithTransaction(parent, t)
}
// ContextWithBodyCapturer returns a copy of parent in which the given
// body capturer is stored, associated with the key bodyCapturerKey.
func ContextWithBodyCapturer(parent context.Context, bc *BodyCapturer) context.Context {
return OverrideContextWithBodyCapturer(parent, bc)
}
// SpanFromContext returns the current Span in context, if any. The span must
// have been added to the context previously using ContextWithSpan, or the
// top-level StartSpan function.
func SpanFromContext(ctx context.Context) *Span {
return OverrideSpanFromContext(ctx)
}
// TransactionFromContext returns the current Transaction in context, if any.
// The transaction must have been added to the context previously using
// ContextWithTransaction.
func TransactionFromContext(ctx context.Context) *Transaction {
return OverrideTransactionFromContext(ctx)
}
// BodyCapturerFromContext returns the BodyCapturer in context, if any.
// The body capturer must have been added to the context previously using
// ContextWithBodyCapturer.
func BodyCapturerFromContext(ctx context.Context) *BodyCapturer {
return OverrideBodyCapturerFromContext(ctx)
}
// DetachedContext returns a new context detached from the lifetime
// of ctx, but which still returns the values of ctx.
//
// DetachedContext can be used to maintain the trace context required
// to correlate events, but where the operation is "fire-and-forget",
// and should not be affected by the deadline or cancellation of ctx.
func DetachedContext(ctx context.Context) context.Context {
return &detachedContext{Context: context.Background(), orig: ctx}
}
type detachedContext struct {
context.Context
orig context.Context
}
// Value returns c.orig.Value(key).
func (c *detachedContext) Value(key interface{}) interface{} {
return c.orig.Value(key)
}
// StartSpan is equivalent to calling StartSpanOptions with a zero SpanOptions struct.
func StartSpan(ctx context.Context, name, spanType string) (*Span, context.Context) {
return StartSpanOptions(ctx, name, spanType, SpanOptions{})
}
// StartSpanOptions starts and returns a new Span within the sampled transaction
// and parent span in the context, if any. If the span isn't dropped, it will be
// stored in the resulting context.
//
// If opts.Parent is non-zero, its value will be used in preference to any parent
// span in ctx.
//
// StartSpanOptions always returns a non-nil Span. Its End method must be called
// when the span completes.
func StartSpanOptions(ctx context.Context, name, spanType string, opts SpanOptions) (*Span, context.Context) {
var span *Span
if opts.parent = SpanFromContext(ctx); opts.parent != nil {
if opts.parent.tx == nil && opts.parent.tracer != nil {
span = opts.parent.tracer.StartSpan(name, spanType, opts.parent.transactionID, opts)
} else {
span = opts.parent.tx.StartSpanOptions(name, spanType, opts)
}
} else {
tx := TransactionFromContext(ctx)
span = tx.StartSpanOptions(name, spanType, opts)
}
if !span.Dropped() {
ctx = ContextWithSpan(ctx, span)
}
return span, ctx
}
// CaptureError returns a new Error related to the sampled transaction
// and span present in the context, if any, and sets its exception info
// from err. The Error.Handled field will be set to true, and a stacktrace
// set either from err, or from the caller.
//
// If the provided error is nil, then CaptureError will also return nil;
// otherwise a non-nil Error will always be returned. If there is no
// transaction or span in the context, then the returned Error's Send
// method will have no effect.
func CaptureError(ctx context.Context, err error) *Error {
if err == nil {
return nil
}
if span := SpanFromContext(ctx); span != nil {
if span.tracer == nil {
return &Error{cause: err, err: err.Error()}
}
e := span.tracer.NewError(err)
e.Handled = true
e.SetSpan(span)
return e
} else if tx := TransactionFromContext(ctx); tx != nil {
if tx.tracer == nil {
return &Error{cause: err, err: err.Error()}
}
e := tx.tracer.NewError(err)
e.Handled = true
bc := BodyCapturerFromContext(ctx)
if bc != nil {
e.Context.SetHTTPRequest(bc.request)
e.Context.SetHTTPRequestBody(bc)
}
e.SetTransaction(tx)
return e
} else {
return &Error{cause: err, err: err.Error()}
}
}
var (
// OverrideContextWithSpan returns a copy of parent in which the given
// span is stored, associated with the key ContextSpanKey.
//
// OverrideContextWithSpan is a variable to allow other packages, such
// as apmot, to replace it at package init time.
OverrideContextWithSpan = defaultContextWithSpan
// OverrideContextWithTransaction returns a copy of parent in which the
// given transaction is stored, associated with the key
// ContextTransactionKey.
//
// ContextWithTransaction is a variable to allow other packages, such as
// apmot, to replace it at package init time.
OverrideContextWithTransaction = defaultContextWithTransaction
// OverrideContextWithBodyCapturer returns a copy of parent in which the
// given body capturer is stored, associated with the key
// bodyCapturerKey.
//
// OverrideContextWithBodyCapturer is a variable to allow other packages,
// such as apmot, to replace it at package init time.
OverrideContextWithBodyCapturer = defaultContextWithBodyCapturer
// OverrideSpanFromContext returns the current Span in context, if any.
// The span must have been added to the context previously using
// ContextWithSpan, or the top-level StartSpan function.
//
// SpanFromContext is a variable to allow other packages, such as apmot,
// to replace it at package init time.
OverrideSpanFromContext = defaultSpanFromContext
// OverrideTransactionFromContext returns the current Transaction in
// context, if any. The transaction must have been added to the context
// previously using ContextWithTransaction.
//
// OverrideTransactionFromContext is a variable to allow other packages,
// such as apmot, to replace it at package init time.
OverrideTransactionFromContext = defaultTransactionFromContext
// OverrideBodyCapturerFromContext returns the BodyCapturer in context,
// if any. The body capturer must have been added to the context
// previously using ContextWithBodyCapturer.
//
// OverrideBodyCapturerFromContext is a variable to allow other
// packages, such as apmot, to replace it at package init time.
OverrideBodyCapturerFromContext = defaultBodyCapturerFromContext
)
type spanKey struct{}
type transactionKey struct{}
type bodyCapturerKey struct{}
// defaultContextWithSpan is the default value for ContextWithSpan.
func defaultContextWithSpan(ctx context.Context, span *Span) context.Context {
return context.WithValue(ctx, spanKey{}, span)
}
// defaultContextWithTransaction is the default value for ContextWithTransaction.
func defaultContextWithTransaction(ctx context.Context, tx *Transaction) context.Context {
return context.WithValue(ctx, transactionKey{}, tx)
}
// defaultContextWithBodyCapturer is the default value for ContextWithBodyCapturer.
func defaultContextWithBodyCapturer(ctx context.Context, bc *BodyCapturer) context.Context {
return context.WithValue(ctx, bodyCapturerKey{}, bc)
}
// defaultSpanFromContext is the default value for SpanFromContext.
func defaultSpanFromContext(ctx context.Context) *Span {
span, _ := ctx.Value(spanKey{}).(*Span)
return span
}
// defaultTransactionFromContext is the default value for TransactionFromContext.
func defaultTransactionFromContext(ctx context.Context) *Transaction {
tx, _ := ctx.Value(transactionKey{}).(*Transaction)
return tx
}
// defaultBodyCapturerFromContext is the default value for BodyCapturerFromContext.
func defaultBodyCapturerFromContext(ctx context.Context) *BodyCapturer {
bc, _ := ctx.Value(bodyCapturerKey{}).(*BodyCapturer)
return bc
}