Custom Instrumentation

Learn how to capture performance data on any action in your app.

If you're using one of the supported HTTP middleware packages, transactions are created for you. Within your handlers, add child spans to track specific operations:

Copied
func doWork(ctx context.Context) {
	// Set the OP based on values from https://develop.sentry.dev/sdk/performance/span-operations/
	span := sentry.StartSpan(ctx, "function")
	span.Description = "suboperation1"
	// omitted code ...
	span.Finish()

	span = sentry.StartSpan(ctx, "function")
	span.Description = "suboperation2"
	// omitted code ...
	span.Finish()
}

All finished spans are sent together as a transaction when the root span is finished. Make sure to call Finish() appropriately. Often times defer span.Finish() is handy.

In cases where you want to access the current transaction but don't have a direct reference to it, use sentry.TransactionFromContext. This function returns the root span of the current transaction, or nil if no transaction is started.

Copied
transaction := sentry.TransactionFromContext(ctx)
if transaction != nil {
	transaction.SetTag("key", "value")
}

If you're not using a supported framework middleware, you need to create transactions manually. The following example creates a transaction based on an incoming request:

Copied
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	hub := sentry.GetHubFromContext(ctx)
	if hub == nil {
		// Check the concurrency guide for more details: https://docs.sentry.io/platforms/go/concurrency/
		hub = sentry.CurrentHub().Clone()
		ctx = sentry.SetHubOnContext(ctx, hub)
	}

	options := []sentry.SpanOption{
		// Set the OP based on values from https://develop.sentry.dev/sdk/performance/span-operations/
		sentry.WithOpName("http.server"),
		sentry.ContinueFromRequest(r),
		sentry.WithTransactionSource(sentry.SourceURL),
	}

	transaction := sentry.StartTransaction(ctx,
		fmt.Sprintf("%s %s", r.Method, r.URL.Path),
		options...,
	)
	defer transaction.Finish()

	doWork(transaction.Context());
})

You can capture data attributes along with your spans and transactions. You can specify data attributes when starting a span or transaction:

Copied
// Create a span and assign data attributes
span := sentry.StartSpan(ctx, "span1")
span.SetData("dataAttr1", 42)
span.SetData("dataAttr2", true)
// omitted code ...
span.Finish()

Or you can add data attributes to an existing transaction or span:

Copied
transaction := sentry.TransactionFromContext(ctx)

if transaction != nil {
	transaction.SetData("dataAttr1", 42)
	transaction.SetData("dataAttr2", true)
}

span := sentry.SpanFromContext(ctx)
span.SetData("dataAttr1", 42)
span.SetData("dataAttr2", true)

Or you can update existing transaction and span data by:

Copied
if d, found := transaction.Data["dataAttr1"]; found {
  if dataAttr1, ok := d.(int); ok {
    transaction.SetData("dataAttr1", dataAttr1.(int)+42)
  }
}

if d, found := span.Data["dataAttr1"]; found {
  if dataAttr1, ok := d.(int); ok {
    span.SetData("dataAttr1", dataAttr1.(int)+42)
  }
}

To attach data attributes to the transaction and all its spans, you can use BeforeSendTransaction:

Copied
sentry.Init(sentry.ClientOptions{
  Dsn: "___PUBLIC_DSN___",
  BeforeSendTransaction: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
    for _, sp := range event.Spans {
      sp.SetData("dataAttr1", 42)
      sp.SetData("dataAttr2", true)
    }

    dataCtx, ok := event.Contexts["trace"]["data"].(map[string]any)
    if !ok {
      dataCtx = make(map[string]any)
      event.Contexts["trace"]["data"] = dataCtx
    }
    dataCtx["num"] = 42
    return event
  },
})
Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").