# Custom Instrumentation | Sentry for Zap

To capture transactions and spans customized to your organization's needs, you must first [set up tracing.](https://docs.sentry.io/platforms/go/guides/zap/tracing.md)

To instrument certain regions of your code, you can create transactions to capture them.

The following example creates a transaction based on an incoming request:

```go
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());
})
```

## [Add More Spans to the Transaction](https://docs.sentry.io/platforms/go/guides/zap/tracing/instrumentation/custom-instrumentation.md#add-more-spans-to-the-transaction)

The next example contains the implementation of the hypothetical `doWork` function called from the code snippet in the previous section. Our SDK can determine if there is currently an open transaction in the current context and add all newly created spans as child operations to that transaction. Keep in mind that each individual span also needs to be manually finished. Otherwise, spans will not show up on the transaction.

```go
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()

	// Set the OP based on values from https://develop.sentry.dev/sdk/performance/span-operations/
	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.

## [Retrieve a Transaction](https://docs.sentry.io/platforms/go/guides/zap/tracing/instrumentation/custom-instrumentation.md#retrieve-a-transaction)

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.

```go
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    transaction := sentry.TransactionFromContext(ctx)
    cookie, _ := r.Cookie("secret")
    transaction.SetTag("secret-cookie", cookie.Value)
    // ...
})
```

## [Adding Span & Transaction Data Attributes](https://docs.sentry.io/platforms/go/guides/zap/tracing/instrumentation/custom-instrumentation.md#adding-span--transaction-data-attributes)

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

```go
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  ctx := r.Context()

  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),
  }

  // Create a transaction and assign data attributes
  transaction := sentry.StartTransaction(ctx,
    fmt.Sprintf("%s %s", r.Method, r.URL.Path),
    options...,
  )
  transaction.SetData("dataAttr1", 42)
  transaction.SetData("dataAttr2", true)
  // omitted code ...
  transaction.Finish()

  // ... or 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:

```go
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  ctx := r.Context()
  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:

```go
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`:

```go
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
  },
})
```

## Pages in this section

- [Instrument HTTP Requests](https://docs.sentry.io/platforms/go/guides/zap/tracing/instrumentation/custom-instrumentation/requests-module.md)
- [Instrument Queues](https://docs.sentry.io/platforms/go/guides/zap/tracing/instrumentation/custom-instrumentation/queues-module.md)
