Language

API


C++ does not support the OpenTelemetry API. Select Datadog from the API dropdown to see C++ custom instrumentation documentation.

Rust does not support the Datadog API. Select OpenTelemetry from the API dropdown to see Rust custom instrumentation documentation.

Elixir does not support the Datadog API. Select OpenTelemetry from the API dropdown to see Elixir custom instrumentation documentation.

Datadog does not provide an Elixir tracing library. To send traces to Datadog, use the OpenTelemetry SDK for Elixir.

Overview

There are a few reasons to manually instrument your applications with the OpenTelemetry API:

  • You are not using Datadog supported library instrumentation.
  • You want to extend the Datadog SDK's functionality.
  • You need finer control over instrumenting your applications.

The Datadog SDK provides several techniques to help you achieve these goals. The following sections demonstrate how to use the OpenTelemetry API for custom instrumentation to use with Datadog.

Setup

OpenTelemetry is supported in Java after version 1.24.0.

To configure OpenTelemetry to use the Datadog trace provider:

  1. If you have not yet read the instructions for auto-instrumentation and setup, start with the Java Setup Instructions.

  2. Make sure you only depend on the OpenTelemetry API (and not the OpenTelemetry SDK).

  3. Set the dd.trace.otel.enabled system property or the DD_TRACE_OTEL_ENABLED environment variable to true.

Adding span tags

Add custom span tags

Add custom tags to your spans corresponding to any dynamic value within your application code such as customer.id.

import io.opentelemetry.api.trace.Span;

public void doSomething() {
  Span span = Span.current();
  span.setAttribute("user-name", "Some User");
}

Adding tags globally to all spans

The dd.tags property allows you to set tags across all generated spans for an application. This is useful for grouping stats for your applications, data centers, or any other tags you would like to see in Datadog.

java -javaagent:<DD-JAVA-AGENT-PATH>.jar \
    -Ddd.tags=datacenter:njc,<TAG_KEY>:<TAG_VALUE> \
    -jar <YOUR_APPLICATION_PATH>.jar

Setting errors on span

To set an error on a span, use the setStatus method:

import static io.opentelemetry.api.trace.StatusCode.ERROR;
import io.opentelemetry.api.trace.Span;

public void doSomething() {
  Span span = Span.current();
  span.setStatus(ERROR, "Some error details...");
}

Setting tags and errors on a root span from a child span

When you want to set tags or errors on the root span from within a child span, you can use the OpenTelemetry Context API:

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.context.Scope;

public class Example {

  private final static ContextKey<Span> CONTEXT_KEY =
    ContextKey.named("opentelemetry-traces-local-root-span");

  public void begin() {
    Tracer tracer = GlobalOpenTelemetry.getTracer("my-scope", "0.1.0");
    Span parentSpan = tracer.spanBuilder("begin").startSpan();
    try (Scope scope = parentSpan.makeCurrent()) {
      createChildSpan();
    } finally {
      parentSpan.end();
    }
  }

  private void createChildSpan() {
    Tracer tracer = GlobalOpenTelemetry.getTracer("my-scope", "0.1.0");
    Span childSpan = tracer.spanBuilder("child-span").startSpan();
    try {
      Span rootSpan = Context.current().get(CONTEXT_KEY);
        if (null != rootSpan) {
          rootSpan.setAttribute("my-attribute", "my-attribute-value");
          rootSpan.setStatus(StatusCode.ERROR, "Some error details...");
        }
    } finally {
      childSpan.end();
    }
  }

}

Adding spans

If you aren't using a supported framework instrumentation, or you would like additional depth in your application's traces, you may want to add custom instrumentation to your code for complete flame graphs or to measure execution times for pieces of code.

If modifying application code is not possible, use the environment variable dd.trace.methods to detail these methods.

If you have existing @Trace or similar annotations, or prefer to use annotations to complete any incomplete traces within Datadog, use Trace Annotations.

Trace annotations

Add @WithSpan to methods to have them be traced when running OpenTelemetry and the dd-java-agent.jar. If the Agent is not attached, this annotation has no effect on your application.

OpenTelemetry's @WithSpan annotation is provided by the opentelemetry-instrumentation-annotations dependency.

import io.opentelemetry.instrumentation.annotations.WithSpan;

public class SessionManager {

  @WithSpan
  public static void saveSession() {
    // your method implementation here
  }
}

Manually creating a new span

To manually create new spans within the current trace context:

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;

public class Example {

  public void doSomething() {
    Tracer tracer = GlobalOpenTelemetry.getTracer("my-scope", "0.1.0");
    Span span = tracer.spanBuilder("my-resource").startSpan();
    try (Scope scope = span.makeCurrent()) {
      // do some work
    } catch (Throwable t) {
      span.recordException(t);
      throw t;
    } finally {
      span.end();
    }
  }
}

Adding span events

Adding span events requires SDK version 1.40.0 or higher.

You can add span events using the addEvent API. This method requires a name parameter and optionally accepts attributes and timestamp parameters. The method creates a new span event with the specified properties and associates it with the corresponding span.

  • Name [required]: A string representing the event's name.
  • Attributes [optional]: Key-value pairs where the key is a non-empty string and the value is a primitive type or a homogeneous array of primitive values.
  • Timestamp [optional]: A UNIX timestamp representing the event's occurrence time. Expects an Instant object.
Attributes eventAttributes = Attributes.builder()
    .put(AttributeKey.longKey("int_val"), 1L)
    .put(AttributeKey.stringKey("string_val"), "two")
    .put(AttributeKey.longArrayKey("int_array"), Arrays.asList(3L, 4L))
    .put(AttributeKey.stringArrayKey("string_array"), Arrays.asList("5", "6"))
    .put(AttributeKey.booleanArrayKey("bool_array"), Arrays.asList(true, false))
    .build();

span.addEvent("Event With No Attributes");
span.addEvent("Event With Some Attributes", eventAttributes);

Read the OpenTelemetry specification for adding events for more information.

Recording exceptions

To record exceptions, use the recordException API:

span.recordException(new Exception("Error Message"));
span.recordException(new Exception("Error Message"),
    Attributes.builder().put(AttributeKey.stringKey("status"), "failed").build());

Read the OpenTelemetry specification for recording exceptions for more information.

Trace client and Agent configuration

Both the tracing client and Datadog Agent offer additional configuration options for context propagation. You can also exclude specific resources from sending traces to Datadog if you don't want those traces to be included in calculated metrics, such as traces related to health checks.

Propagating context with headers extraction and injection

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Resource filtering

Traces can be excluded based on their resource name, to remove synthetic traffic such as health checks from reporting traces to Datadog. This and other security and fine-tuning configurations can be found on the Security page or in Ignoring Unwanted Resources.

Setup

To configure OpenTelemetry to use the Datadog trace provider:

  1. If you have not yet read the instructions for auto-instrumentation and setup, start with the Python Setup Instructions.

  2. Set DD_TRACE_OTEL_ENABLED environment variable to true.

Creating custom spans

To create custom spans within an existing trace context:

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

def do_work():
    with tracer.start_as_current_span("operation_name") as span:
        # Perform the work that you want to track with the span
        print("Doing work...")
        # When the 'with' block ends, the span is automatically closed

Accessing active spans

To access the currently active span, use the get_current_span() function:

from opentelemetry import trace

current_span = trace.get_current_span()
# enrich 'current_span' with information

Adding span tags

Add attributes to a span to provide additional context or metadata:

from opentelemetry import trace

current_span = trace.get_current_span()

current_span.set_attribute("attribute_key1", 1)

Adding span events

Adding span events requires SDK version 2.9.0 or higher.

You can add span events using the add_event API. This method requires a name parameter and optionally accepts attributes and timestamp parameters.

span.add_event("Event With No Attributes")
span.add_event("Event With Some Attributes", {"int_val": 1, "string_val": "two", "int_array": [3, 4], "string_array": ["5", "6"], "bool_array": [True, False]})

Read the OpenTelemetry specification for adding events for more information.

Recording exceptions

To record exceptions, use the record_exception API:

span.record_exception(Exception("Error Message"))
span.record_exception(Exception("Error Message"), {"status": "failed"})

Read the OpenTelemetry specification for recording exceptions for more information.

Setup

To instrument your application, initialize the Datadog tracer (dd-trace) and explicitly register its TracerProvider with the OpenTelemetry API. This ensures all OpenTelemetry calls are routed through Datadog.

  1. Add the dependencies:

    npm install dd-trace @opentelemetry/api
    
  2. Initialize and register the tracer in your application's entry file (for example, index.js), before any other imports:

Complete example

// 1. Import the dd-trace library (do not initialize it yet)
const ddtrace = require('dd-trace');

// 2. Initialize the Datadog tracer. This must be the first operation.
const tracer = ddtrace.init({
  // service: 'my-nodejs-app'
  // ... other Datadog configurations
});

// 3. Create and register Datadog's TracerProvider.
const provider = new tracer.TracerProvider();
provider.register(); // This wires the @opentelemetry/api to Datadog

// 4. Import and use the OpenTelemetry API
const otel = require('@opentelemetry/api');
const otelTracer = otel.trace.getTracer(
  'my-custom-instrumentation' // A name for your specific instrumentation
);

// You can now use 'otelTracer' to create spans throughout your application.

Datadog combines these OpenTelemetry spans with other Datadog APM spans into a single trace of your application. It also supports integration instrumentation and OpenTelemetry automatic instrumentation.

Adding span tags

Add custom attributes to your spans to provide additional context:

function processData(i, param1, param2) {
  return otelTracer.startActiveSpan(`processData:${i}`, (span) => {
    const result = someOperation(param1, param2);

    // Add an attribute to the span
    span.setAttribute('app.processedData', result.toString());
    span.end();
    return result;
    });
}

Creating spans

To create a new span and properly close it, use the startActiveSpan method:

function performTask(iterations, param1, param2) {
  // Create a span. A span must be closed.
  return otelTracer.startActiveSpan('performTask', (span) => {
    const results = [];
    for (let i = 0; i < iterations; i++) {
      results.push(processData(i, param1, param2));
    }
    // Be sure to end the span!
    span.end();
    return results;
  });
}

Adding span events

Adding span events requires SDK version 5.17.0/4.41.0 or higher.

You can add span events using the addEvent API:

span.addEvent('Event With No Attributes')
span.addEvent('Event With Some Attributes', {"int_val": 1, "string_val": "two", "int_array": [3, 4], "string_array": ["5", "6"], "bool_array": [true, false]})

Read the OpenTelemetry specification for adding events for more information.

Recording exceptions

To record exceptions, use the recordException API:

span.recordException(new TestError())

Read the OpenTelemetry specification for recording exceptions for more information.

Filtering requests

In some cases, you may want to exclude certain requests from being instrumented, such as health checks or synthetic traffic. You can use the blocklist or allowlist option on the http plugin to ignore these requests.

// at the top of the entry point right after tracer.init()
tracer.use('http', {
  blocklist: ['/health', '/ping']
})

You can also split the configuration between client and server if needed:

tracer.use('http', {
  server: {
    blocklist: ['/ping']
  }
})

Additionally, you can exclude traces based on their resource name to prevent the Agent from sending them to Datadog. For more information on security and fine-tuning Agent configurations, read the Security or Ignoring Unwanted Resources documentation.

Imports

Import the following packages to setup the Datadog trace provider:

import (
	"context"
	"log"
	"os"

   "github.com/DataDog/dd-trace-go/v2/ddtrace/ext"
   "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry"
   "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
)

Setup

To configure OpenTelemetry to use the Datadog trace provider:

  1. Add your desired manual OpenTelemetry instrumentation to your Go code following the OpenTelemetry Go Manual Instrumentation documentation. Important! Where those instructions indicate that your code should call the OpenTelemetry SDK, call the Datadog tracing library instead.

  2. Install the OpenTelemetry package:

    go get go.opentelemetry.io/otel
    
  3. Install the Datadog OpenTelemetry wrapper package:

    go get github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry
    
  4. Import packages:

    import (
       "go.opentelemetry.io/otel"
       ddotel "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry"
    )
    
  5. Create a TracerProvider and defer the Shutdown method:

    provider := ddotel.NewTracerProvider()
    defer provider.Shutdown()
    
  6. Set the global TracerProvider:

    otel.SetTracerProvider(provider)
    
  7. Run your application.

Adding span tags

Add custom tags to your spans to attach additional metadata and context:

// Start a span.
ctx, span := t.Start(ctx, "read.file")
// Set an attribute, or a tag in Datadog terminology, on a span.
span.SetAttributes(attribute.String(ext.ResourceName, "test.json"))

Adding tags globally to all spans

Add tags to all spans by configuring the tracer with the WithGlobalTag option:

provider := ddotel.NewTracerProvider(
	ddtracer.WithGlobalTag("datacenter", "us-1"),
	ddtracer.WithGlobalTag("env", "dev"),
)
defer provider.Shutdown()

otel.SetTracerProvider(provider)
t := otel.Tracer("")

Setting errors on a span

To set an error on a span:

// Start a span.
ctx, span := t.Start(context.Background(), "spanName")

// Set an error on a span with 'span.SetAttributes'.
span.SetAttributes(attribute.String(ext.ErrorMsg, "errorMsg"))

// Alternatively, set an error via end span options.
EndOptions(span, tracer.WithError(errors.New("myErr")))
span.End()

Adding spans

Unlike other Datadog tracing libraries, when tracing Go applications, Datadog recommends that you explicitly manage and pass the Go context of your spans.

ctx, span := t.Start(
	ddotel.ContextWithStartOptions(context.Background(), ddtracer.Measured()), "span_name")

span.End()

Adding span events

Adding span events requires SDK version 1.67.0 or higher.

You can add span events using the AddEvent API:

ctx, span := tracer.StartSpan(context.Background(), "span_name")
span.AddEvent("Event With No Attributes")
span.AddEvent("Event With Some Attributes", oteltrace.WithAttributes(attribute.Int("int_val", 1), attribute.String("string_val", "two")))
span.Finish()

Read the OpenTelemetry specification for adding events for more information.

Trace client and Agent configuration

Propagating context with headers extraction and injection

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Resource filtering

Traces can be excluded based on their resource name, to remove synthetic traffic such as health checks from reporting traces to Datadog. This and other security and fine-tuning configurations can be found on the Security page.

Requirements and limitations

  • Datadog Ruby tracing library dd-trace-rb version 1.9.0 or greater.
  • Gem version support 1.1.0 or greater.

The following OpenTelemetry features implemented in the Datadog library as noted:

FeatureSupport notes
OpenTelemetry Context propagationDatadog and W3C Trace Context header formats are enabled by default.
Span processorsUnsupported
Span ExportersUnsupported
OpenTelemetry.loggerOpenTelemetry.logger is set to the same object as Datadog.logger. Configure through custom logging.
Trace/span ID generatorsID generation is performed by the tracing library, with support for 128-bit trace IDs.

Configuring OpenTelemetry to use the Datadog tracing library

  1. Add your desired manual OpenTelemetry instrumentation to your Ruby code following the OpenTelemetry Ruby Manual Instrumentation documentation. Important! Where those instructions indicate that your code should call the OpenTelemetry SDK, call the Datadog tracing library instead.

  2. Add the datadog gem to your Gemfile:

    source 'https://rubygems.org'
    gem 'datadog' # For dd-trace-rb v1.x, use the `ddtrace` gem.
    
  3. Install the gem by running bundle install.

  4. Add the following lines to your OpenTelemetry configuration file:

    require 'opentelemetry/sdk'
    require 'datadog/opentelemetry'
    
  5. Add a configuration block to your application:

    Datadog.configure do |c|
      ...
    end
    

Datadog combines these OpenTelemetry spans with other Datadog APM spans into a single trace of your application. It supports integration instrumentation and OpenTelemetry Automatic instrumentation also.

Adding span events

Adding span events requires SDK version 2.3.0 or higher.

You can add span events using the add_event API:

span.add_event('Event With No Attributes')
span.add_event(
  'Event With All Attributes',
  attributes: { 'int_val' => 1, 'string_val' => 'two', 'int_array' => [3, 4], 'string_array' => ['5', '6'], 'bool_array' => [false, true]}
)

Read the OpenTelemetry specification for adding events for more information.

Recording exceptions

To record exceptions, use the record_exception API:

span.record_exception(
  StandardError.new('Error Message')
)
span.record_exception(
  StandardError.new('Error Message'),
  attributes: { 'status' => 'failed' }
)

Read the OpenTelemetry specification for recording exceptions for more information.

Setup

To configure OpenTelemetry to use the Datadog trace provider:

  1. Add your desired manual OpenTelemetry instrumentation to your .NET code following the OpenTelemetry .NET Manual Instrumentation documentation. Note: Where those instructions indicate that your code should call the OpenTelemetry SDK, call the Datadog tracing library instead.

  2. Install the Datadog .NET tracing library and enable the tracer for your .NET Framework service or your .NET Core (and .NET 5+) service. You can optionally do this with Single Step APM Instrumentation.

  3. Set DD_TRACE_OTEL_ENABLED environment variable to true.

  4. Run your application.

Datadog combines these OpenTelemetry spans with other Datadog APM spans into a single trace of your application. It also supports OpenTelemetry instrumentation libraries.

Creating custom spans

To manually create spans that start a new, independent trace:

using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

// Start a new span
using (Activity? activity = Telemetry.ActivitySource.StartActivity("<RESOURCE NAME>"))
{
  activity?.SetTag("operation.name", "custom-operation");
  // Do something
}

Creating spans

To create custom spans within an existing trace context:

using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

using (Activity? parentScope = Telemetry.ActivitySource.StartActivity("<RESOURCE NAME>"))
{
   parentScope?.SetTag("operation.name", "manual.sortorders");
   using (Activity? childScope = Telemetry.ActivitySource.StartActivity("<RESOURCE NAME>"))
   {
       childScope?.SetTag("operation.name", "manual.sortorders.child");
       SortOrders();
   }
}

Adding span tags

Add custom tags to your spans to provide additional context:

using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

public class ShoppingCartController : Controller
{
    [HttpGet]
    public IActionResult Index(int customerId)
    {
      Activity? activity = Telemetry.ActivitySource.StartActivity("<RESOURCE NAME>")

      // Add a tag to the span for use in the Datadog web UI
      activity?.SetTag("customer.id", customerId.ToString());

      var cart = _shoppingCartRepository.Get(customerId);
      return View(cart);
    }
}

Setting errors on spans

Set error information on a span when an error occurs during its execution:

try
{
    // do work that can throw an exception
}
catch(Exception e)
{
    activity?.SetTag("error", 1);
    activity?.SetTag("error.message", exception.Message);
    activity?.SetTag("error.stack", exception.ToString());
    activity?.SetTag("error.type", exception.GetType().ToString());
}

Adding span events

Adding span events requires SDK version 2.53.0 or higher.

You can add span events using the AddEvent API:

var eventTags = new ActivityTagsCollection
{
    { "int_val", 1 },
    { "string_val", "two" },
    { "int_array", new int[] { 3, 4 } },
    { "string_array", new string[] { "5", "6" } },
    { "bool_array", new bool[] { true, false } }
};

activity.AddEvent(new ActivityEvent("Event With No Attributes"));
activity.AddEvent(new ActivityEvent("Event With Some Attributes", DateTimeOffset.Now, eventTags));

Read the OpenTelemetry specification for adding events for more information.

Propagating context with headers extraction and injection

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Setup

To configure OpenTelemetry to use the Datadog trace provider:

  1. Install OpenTelemetry API packages:

    composer require open-telemetry/sdk
    
  2. Add your desired manual OpenTelemetry instrumentation to your PHP code following the OpenTelemetry PHP Manual Instrumentation documentation.

  3. Install the Datadog PHP tracing library.

  4. Set DD_TRACE_OTEL_ENABLED to true.

Datadog combines these OpenTelemetry spans with other Datadog APM spans into a single trace of your application.

Adding span tags

You can add attributes at the exact moment as you are starting the span:

$span = $tracer->spanBuilder('mySpan')
    ->setAttribute('key', 'value')
    ->startSpan();

Or while the span is active:

$activeSpan = OpenTelemetry\API\Trace\Span::getCurrent();
$activeSpan->setAttribute('key', 'value');

Setting errors on a span

Exception information is captured and attached to a span if one is active when the exception is raised:

// Create a span
$span = $tracer->spanBuilder('mySpan')->startSpan();

throw new \Exception('Oops!');

// 'mySpan' will be flagged as erroneous and have
// the stack trace and exception message attached as tags

Flagging a trace as erroneous can also be done manually:

use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\Context\Context;

try {
    throw new \Exception('Oops!');
} catch (\Exception $e) {
    $rootSpan = Span::fromContext(Context::getRoot());
    $rootSpan->recordException($e);
}

Adding spans

To add a span:

// Get a tracer or use an existing one
$tracerProvider = \OpenTelemetry\API\Globals::tracerProvider();
$tracer = $tracerProvider->getTracer('datadog')

// Create a span
$span = $tracer->spanBuilder('mySpan')->startSpan();

// ... do stuff

// Close the span
$span->end();

Adding span events

Adding span events requires SDK version 1.3.0 or higher.

You can add span events using the addEvent API:

$span->addEvent("Event With No Attributes");
$span->addEvent(
    "Event With Some Attributes",
    [
        'int_val' => 1,
        'string_val' => "two",
        'int_array' => [3, 4],
        'string_array' => ["5", "6"],
        'bool_array' => [true, false]
    ]
);

Read the OpenTelemetry specification for adding events for more information.

Recording exceptions

To record exceptions, use the recordException API:

$span->recordException(new \Exception("Error Message"));
$span->recordException(new \Exception("Error Message"), [ "status" => "failed" ]);

Read the OpenTelemetry specification for recording exceptions for more information.

Accessing active spans

To access the currently active span:

$span = OpenTelemetry\API\Trace\Span::getCurrent();

The Datadog Rust SDK is in Preview.

Datadog provides support for custom instrumentation in Rust applications through the datadog-opentelemetry crate. This library is built on the OpenTelemetry (OTel) API and SDK, providing a tracer that includes Datadog-specific features and an exporter.

Because this library is built on OpenTelemetry, you use the standard OpenTelemetry API to create traces and spans.

Setup

To configure your Rust application to send OpenTelemetry traces to Datadog:

1. Add dependencies

Add datadog-opentelemetry and the core opentelemetry crate to your Cargo.toml:

cargo add datadog-opentelemetry opentelemetry

2. Initialize the Tracer

In your application's main function, initialize the Datadog tracer provider:

You must shut down the provider before your application exits to ensure all pending traces are flushed.

use datadog_opentelemetry;
use opentelemetry::{global, trace::Tracer};
use std::time::Duration;

fn main() {
    // This picks up env var configuration (like DD_SERVICE)
    // and initializes the global tracer provider
    let tracer_provider = datadog_opentelemetry::tracing()
        .init();

    // --- Your application code starts here ---
    let tracer = global::tracer("my-component");

    tracer.in_span("my-operation", |_cx| {
        // ... do work ...
    });

    println!("Doing work...");

    // --- Your application code ends here ---

    // Shut down the tracer provider to flush remaining spans
    tracer_provider.shutdown_with_timeout(Duration::from_secs(5)).expect("tracer shutdown error");
}

3. Ensure Agent is running

The Datadog exporter sends traces to the Datadog Agent, which must be running and accessible.

Configuration

The Datadog Rust SDK is configured using environment variables. For a complete list of options, see the Configuration documentation.

Examples

Get a Tracer

Get an instance of a Tracer from the global provider:

use opentelemetry::global;

let tracer = global::tracer("my-component");

Create a span

Use tracer.in_span to create a new span. The span is automatically ended when the closure finishes:

use opentelemetry::{global, trace::Tracer};

fn do_work() {
    let tracer = global::tracer("my-component");

    tracer.in_span("operation_name", |_cx| {
        // The span is active within this closure
        println!("Doing work...");
    });
}

Create a child span

To create a child span, nest in_span calls:

use opentelemetry::{global, trace::Tracer};

fn parent_operation() {
    let tracer = global::tracer("my-component");

    tracer.in_span("parent_operation", |_cx| {
        tracer.in_span("child_operation", |_cx| {
            // This span is automatically parented to "parent_operation"
            println!("Doing child work...");
        });
        println!("Doing parent work...");
    });
}

Add span tags

Add attributes to a span using the set_attribute method:

use opentelemetry::trace::{Tracer, TraceContextExt};
use opentelemetry::KeyValue;

fn add_tags_to_span() {
    let tracer = opentelemetry::global::tracer("my-component");

    tracer.in_span("operation.with.tags", |cx| {
        let span = cx.span();

        span.set_attribute(KeyValue::new("customer.id", "12345"));
        span.set_attribute(KeyValue::new("http.method", "GET"));
    });
}

Add span events

Add time-stamped log messages to a span using the add_event method:

use opentelemetry::trace::{Tracer, TraceContextExt};
use opentelemetry::KeyValue;

fn add_events_to_span() {
    let tracer = opentelemetry::global::tracer("my-component");

    tracer.in_span("operation.with.events", |cx| {
        let span = cx.span();

        span.add_event("Data received", vec![]);
        span.add_event(
            "Processing data",
            vec![
                KeyValue::new("data.size_bytes", 1024),
                KeyValue::new("data.format", "json"),
            ],
        );
    });
}

Context propagation

Because Rust does not have automatic instrumentation, you must manually propagate the trace context when making or receiving remote calls to connect traces across services.

For more information, see Trace Context Propagation.

Overview

Use the Datadog API to programmatically create, modify, or delete traces to send to Datadog. This is useful for tracing in-house code not captured by automatic instrumentation, removing unwanted spans from traces, and providing deeper visibility and context into spans, including adding span tags.

The Datadog Java tracer interoperates with the opentracing-api library for custom instrumentation. If you would prefer to use the OpenTelemetry API for your custom instrumentation, see Java Custom Instrumentation using OpenTelemetry instead.

Prerequisites

  • If you have not read the setup instructions for automatic instrumentation, start with the Java Setup Instructions.
  • To compile the examples on this page, add the opentracing-api dependency to your project.

Adding tags

Add custom span tags to your spans to customize your observability within Datadog. The span tags are applied to your incoming traces, allowing you to correlate observed behavior with code-level information such as merchant tier, checkout amount, or user ID.

Add custom span tags

Add custom tags to your spans corresponding to any dynamic value within your application code such as customer.id.

import org.apache.cxf.transport.servlet.AbstractHTTPServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;

@WebServlet
class ShoppingCartServlet extends AbstractHttpServlet {
    @Override
    void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // Get the active span
        final Span span = GlobalTracer.get().activeSpan();
        if (span != null) {
          // customer_id -> 254889
          // customer_tier -> platinum
          // cart_value -> 867
          span.setTag("customer.id", customer_id);
          span.setTag("customer.tier", customer_tier);
          span.setTag("cart.value", cart_value);
        }
        // [...]
    }
}

Adding tags globally to all spans

The dd.tags property allows setting tags across all generated spans for an application. This can be useful for grouping stats for your applications, datacenters, or any other tags you would like to see within the Datadog UI.

java -javaagent:<DD-JAVA-AGENT-PATH>.jar \
     -Ddd.tags=datacenter:njc,<TAG_KEY>:<TAG_VALUE> \
     -jar <YOUR_APPLICATION_PATH>.jar

Set errors on a span

To customize an error associated with one of your spans, set the error tag on the span and use Span.log() to set an "error event". The error event is a Map<String,Object> containing a Fields.ERROR_OBJECT->Throwable entry, a Fields.MESSAGE->String, or both.

import io.opentracing.Span;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import io.opentracing.log.Fields;
...
    // Get active span if not available in current method
    final Span span = GlobalTracer.get().activeSpan();
    if (span != null) {
      span.setTag(Tags.ERROR, true);
      span.log(Collections.singletonMap(Fields.ERROR_OBJECT, ex));
    }

Note: Span.log() is a generic OpenTracing mechanism for associating events to the current timestamp. The Java Tracer only supports logging error events. Alternatively, you can set error tags directly on the span without log():

import io.opentracing.Span;
import io.opentracing.tag.Tags;
import io.opentracing.util.GlobalTracer;
import datadog.trace.api.DDTags;
import java.io.PrintWriter;
import java.io.StringWriter;

...
    final Span span = GlobalTracer.get().activeSpan();
    if (span != null) {
      span.setTag(Tags.ERROR, true);
      span.setTag(DDTags.ERROR_MSG, ex.getMessage());
      span.setTag(DDTags.ERROR_TYPE, ex.getClass().getName());

      final StringWriter errorString = new StringWriter();
      ex.printStackTrace(new PrintWriter(errorString));
      span.setTag(DDTags.ERROR_STACK, errorString.toString());
    }

Note: You can add any relevant error metadata listed in the trace view docs. If the current span isn't the root span, mark it as an error by using the dd-trace-api library to grab the root span with MutableSpan, then use setError(true). See the setting tags & errors on a root span section for more details.

Set tags and errors on a root span from a child span

When an event or condition happens downstream, you may want that behavior or value reflected as a tag on the top level or root span. This can be useful to count an error or for measuring performance, or setting a dynamic tag for observability.

import java.util.Collections;
import io.opentracing.Span;
import io.opentracing.Scope;
import datadog.trace.api.interceptor.MutableSpan;
import io.opentracing.log.Fields;
import io.opentracing.util.GlobalTracer;
import io.opentracing.util.Tracer;

Tracer tracer = GlobalTracer.get();
final Span span = tracer.buildSpan("<OPERATION_NAME>").start();
// Note: The scope in the try with resource block below
// will be automatically closed at the end of the code block.
// If you do not use a try with resource statement, you need
// to call scope.close().
try (final Scope scope = tracer.activateSpan(span)) {
    // exception thrown here
} catch (final Exception e) {
    // Set error tag on span as normal
    span.log(Collections.singletonMap(Fields.ERROR_OBJECT, e));

    // Set error on root span
    if (span instanceof MutableSpan) {
        MutableSpan localRootSpan = ((MutableSpan) span).getLocalRootSpan();
        localRootSpan.setError(true);
        localRootSpan.setTag("some.other.tag", "value");
    }
} finally {
    // Close span in a finally block
    span.finish();
}

If you are not manually creating a span, you can still access the root span through the GlobalTracer:

import io.opentracing.Span;
import io.opentracing.util.GlobalTracer;
import datadog.trace.api.interceptor.MutableSpan;

...

final Span span = GlobalTracer.get().activeSpan();
if (span != null && (span instanceof MutableSpan)) {
    MutableSpan localRootSpan = ((MutableSpan) span).getLocalRootSpan();
    // do stuff with root span
}

Note: Although MutableSpan and Span share many similar methods, they are distinct types. MutableSpan is Datadog specific and not part of the OpenTracing API.

Adding spans

If you aren't using a supported framework instrumentation, or you would like additional depth in your application's traces, you may want to add custom instrumentation to your code for complete flame graphs or to measure execution times for pieces of code.

If modifying application code is not possible, use the environment variable dd.trace.methods to detail these methods.

If you have existing @Trace or similar annotations, or prefer to use annotations to complete any incomplete traces within Datadog, use Trace Annotations.

Datadog trace methods

Using the dd.trace.methods system property, you can get visibility into unsupported frameworks without changing application code.

java -javaagent:/path/to/dd-java-agent.jar -Ddd.env=prod -Ddd.service.name=db-app -Ddd.trace.methods=store.db.SessionManager[saveSession] -jar path/to/application.jar

To trace several functions within the same class, use the following syntax:

java -javaagent:/path/to/dd-java-agent.jar -Ddd.env=prod -Ddd.service.name=db-app -Ddd.trace.methods=store.db.SessionManager[saveSession,loadSession] -jar path/to/application.jar

The only difference between this approach and using @Trace annotations is the customization options for the operation and resource names. With DD Trace Methods, operationName is trace.annotation and resourceName is SessionManager.saveSession.

Trace annotations

Add @Trace to methods to have them be traced when running with dd-java-agent.jar. If the Agent is not attached, this annotation has no effect on your application.

Datadog's Trace annotation is provided by the dd-trace-api dependency.

The available arguments for the @Trace annotation are:

  • operationName: Set the operation name for the trace (default: The method's name).
  • resourceName: Set the resource name for the trace (default: The same value as operationName).
  • noParent: Set to true to always start a new trace at that method. Supported from v1.22.0+ of dd-trace-java (default: false).
import datadog.trace.api.Trace;

public class SessionManager {

    @Trace(operationName = "database.persist", resourceName = "SessionManager.saveSession")
    public static void saveSession() {
        // your method implementation here
    }
}

Note: Through the dd.trace.annotations system property, other tracing method annotations can be recognized by Datadog as @Trace. You can find a list in TraceAnnotationsInstrumentation.java if you have previously decorated your code.

Manually creating a new span

In addition to automatic instrumentation, the @Trace annotation, and dd.trace.methods configurations , you can customize your observability by programmatically creating spans around any block of code. Spans created in this manner integrate with other tracing mechanisms automatically. In other words, if a trace has already started, the manual span will have its caller as its parent span. Similarly, any traced methods called from the wrapped block of code will have the manual span as its parent.

import datadog.trace.api.DDTags;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;

class SomeClass {
    void someMethod() {
        Tracer tracer = GlobalTracer.get();

        // Service and resource name tags are required.
        // You can set them when creating the span:
        Span span = tracer.buildSpan("<OPERATION_NAME>")
            .withTag(DDTags.SERVICE_NAME, "<SERVICE_NAME>")
            .withTag(DDTags.RESOURCE_NAME, "<RESOURCE_NAME>")
            .start();
        // Note: The scope in the try with resource block below
        // will be automatically closed at the end of the code block.
        // If you do not use a try with resource statement, you need
        // to call scope.close().
        try (Scope scope = tracer.activateSpan(span)) {
            // Alternatively, set tags after creation
            span.setTag("my.tag", "value");

            // The code you're tracing

        } catch (Exception e) {
            // Set error on span
        } finally {
            // Close span in a finally block
            span.finish();
        }
    }
}

Extending tracers

The tracing libraries are designed to be extensible. Customers may consider writing a custom post-processor called a TraceInterceptor to intercept Spans then adjust or discard them accordingly (for example, based on regular expressions). The following example implements two interceptors to achieve complex post-processing logic.

import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import datadog.trace.api.interceptor.TraceInterceptor;
import datadog.trace.api.interceptor.MutableSpan;

class FilteringInterceptor implements TraceInterceptor {
    @Override
    public Collection<? extends MutableSpan> onTraceComplete(
            Collection<? extends MutableSpan> trace) {

        List<MutableSpan> filteredTrace = new ArrayList<>();
        for (final MutableSpan span : trace) {
          String orderId = (String) span.getTags().get("order.id");

          // Drop spans when the order id starts with "TEST-"
          if (orderId == null || !orderId.startsWith("TEST-")) {
            filteredTrace.add(span);
          }
        }

        return filteredTrace;
    }

    @Override
    public int priority() {
        // some high unique number so this interceptor is last
        return 100;
    }
}

class PricingInterceptor implements TraceInterceptor {
    @Override
    public Collection<? extends MutableSpan> onTraceComplete(
            Collection<? extends MutableSpan> trace) {

        for (final MutableSpan span : trace) {
          Map<String, Object> tags = span.getTags();
          Double originalPrice = (Double) tags.get("order.price");
          Double discount = (Double) tags.get("order.discount");

          // Set a tag from a calculation from other tags
          if (originalPrice != null && discount != null) {
            span.setTag("order.value", originalPrice - discount);
          }
        }

        return trace;
    }

    @Override
    public int priority() {
        return 20; // some unique number
    }
}

Near the start of your application, register the interceptors with the following:

datadog.trace.api.GlobalTracer.get().addTraceInterceptor(new FilteringInterceptor());
datadog.trace.api.GlobalTracer.get().addTraceInterceptor(new PricingInterceptor());

Trace client and Agent configuration

There are additional configurations possible for both the tracing client and Datadog Agent for context propagation, as well as to exclude specific Resources from sending traces to Datadog in the event these traces are not wanted to count in metrics calculated, such as Health Checks.

Propagating context with headers extraction and injection

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Resource filtering

Traces can be excluded based on their resource name, to remove synthetic traffic such as health checks from reporting traces to Datadog. This and other security and fine-tuning configurations can be found on the Security page or in Ignoring Unwanted Resources.

If you have not read the setup instructions for automatic instrumentation, start with the Python Setup Instructions.

If you aren't using supported library instrumentation (see library compatibility), you may want to manually instrument your code.

You may also want to extend the functionality of the ddtrace library or gain finer control over instrumenting your application. Several techniques are provided by the library to accomplish this.

Creating spans

The ddtrace library creates spans automatically with ddtrace-run for many libraries and frameworks. However, you may want to gain visibility into your own code and this is achieved by using spans.

Within your web request (for example, make_sandwich_request), you may perform several operations, like get_ingredients() and assemble_sandwich(), which are useful to measure.

def make_sandwich_request(request):
    ingredients = get_ingredients()
    sandwich = assemble_sandwich(ingredients)

Using decorators

ddtrace provides a decorator tracer.wrap() that can be used to decorate the functions of interest. This is useful if you would like to trace the function regardless of where it is being called from.

from ddtrace import tracer

@tracer.wrap(service="my-sandwich-making-svc", resource="resource_name")
def get_ingredients():
    # go to the pantry
    # go to the fridge
    # maybe go to the store
    return

# You can provide more information to customize the span
@tracer.wrap("assemble_sandwich", service="my-sandwich-making-svc", resource="resource_name")
def assemble_sandwich(ingredients):
    return

To learn more, read API details for the decorator for ddtrace.Tracer.wrap().

Using context managers

To trace an arbitrary block of code, use the ddtrace.Span context manager as below, or view the advanced usage documentation.

from ddtrace import tracer

def make_sandwich_request(request):
    # Capture both operations in a span
    with tracer.trace("sandwich.make"):
        ingredients = get_ingredients()
        sandwich = assemble_sandwich(ingredients)

def make_sandwich_request(request):
    # Capture both operations in a span
    with tracer.trace("sandwich.create", resource="resource_name") as outer_span:

        with tracer.trace("get_ingredients", resource="resource_name") as span:
            ingredients = get_ingredients()

        with tracer.trace("assemble_sandwich", resource="resource_name") as span:
            sandwich = assemble_sandwich(ingredients)

To learn more, read the full API details for ddtrace.Tracer().

Manual span creation

If the decorator and context manager methods are still not enough to satisfy your tracing needs, a manual API is provided which allows you to start and finish spans however you may require:

def make_sandwich_request(request):
    span = tracer.trace("sandwich.create", resource="resource_name")
    ingredients = get_ingredients()
    sandwich = assemble_sandwich(ingredients)
    span.finish()  # remember to finish the span

For more API details of the decorator, read the ddtrace.Tracer.trace documentation or the ddtrace.Span.finish documentation.

Accessing active spans

The built-in instrumentation and your own custom instrumentation create spans around meaningful operations. You can access the active span in order to include meaningful data.

from ddtrace import tracer

def make_sandwich_request(request):
    # Capture both operations in a span
    with tracer.trace("sandwich.make") as my_span:
        ingredients = get_ingredients()
        sandwich = assemble_sandwich(ingredients)

Current span

def get_ingredients():
    # Get the active span
    span = tracer.current_span()
    # this span is my_span from make_sandwich_request above

Root span

def assemble_sandwich(ingredients):
    with tracer.trace("another.operation") as another_span:
        # Get the active root span
        span = tracer.current_root_span()
        # this span is my_span from make_sandwich_request above

Adding tags

Adding tags locally

Tags can be added to a span using the set_tag method on a span:

from ddtrace import tracer

def make_sandwich_request(request):
    with tracer.trace("sandwich.make") as span:
        ingredients = get_ingredients()
        span.set_tag("num_ingredients", len(ingredients))

Adding tags globally

Tags can be globally set on the tracer. These tags are be applied to every span that is created.

from ddtrace import tracer
from myapp import __version__

# This will be applied to every span
tracer.set_tags({"version": __version__, "<TAG_KEY_2>": "<TAG_VALUE_2>"})

Setting errors on a span

Exception information is captured and attached to a span if there is one active when the exception is raised.

from ddtrace import tracer

with tracer.trace("throws.an.error") as span:
    raise Exception("Oops!")

# `span` will be flagged as erroneous and have
# the stack trace and exception message attached as tags

Flagging a span as erroneous can also be done manually:

from ddtrace import tracer

span = tracer.trace("operation")
span.error = 1
span.finish()

In the event you want to flag the local root span with the error raised:

import os
from ddtrace import tracer

try:
    raise TypeError
except TypeError as e:
    root_span = tracer.current_root_span()
    (exc_type, exc_val, exc_tb) = sys.exc_info()
    # this sets the error type, marks the span as an error, and adds the traceback
    root_span.set_exc_info(exc_type, exc_val, exc_tb)

Propagating context with headers extraction and injection

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Baggage

Manipulating Baggage on a span:

from ddtrace import tracer

# Start a new span and set baggage
with tracer.trace("example") as span:
    # set_baggage_item
    span.context.set_baggage_item("key1", "value1")
    span.context.set_baggage_item("key2", "value2")

    # get_all_baggage_items
    all_baggage = span.context.get_all_baggage_items()
    print(all_baggage) # {'key1': 'value1', 'key2': 'value2'}

    # remove_baggage_item
    span.context.remove_baggage_item("key1")
    print(span.context.get_all_baggage_items()) # {'key2': 'value2'}

    # get_baggage_item
    print(span.context.get_baggage_item("key1")) # None
    print(span.context.get_baggage_item("key2")) # value2

    # remove_all_baggage_items
    span.context.remove_all_baggage_items()
    print(span.context.get_all_baggage_items()) # {}

To see an example in action, see flask-baggage on trace-examples.

ddtrace-api

The ddtrace-api Python package is in Preview and may not include all the API calls you need. If you need more complete functionality, use the API as described in the previous sections.

The ddtrace-api package provides a stable public API for Datadog APM's custom Python instrumentation. This package implements only the API interface, not the underlying functionality that creates and sends spans to Datadog.

This separation between interface (ddtrace-api) and implementation (ddtrace) offers several benefits:

  • You can rely on an API that changes less frequently and more predictably for your custom instrumentation
  • If you only use automatic instrumentation, you can ignore API changes entirely
  • If you implement both single-step and custom instrumentation, you avoid depending on multiple copies of the ddtrace package

To use ddtrace-api:

  1. Install both the ddtrace and ddtrace-api libraries:

    pip install 'ddtrace>=3.1' ddtrace-api
    
  2. Instrument your Python application using ddtrace-run by prefixing your Python entry-point command:

    ddtrace-run python app.py
    
  3. After this is set up, you can write custom instrumentation exactly like the examples in the previous sections, but you import from ddtrace_api instead of ddtrace.

    For example:

    from ddtrace_api import tracer
    
    @tracer.wrap(service="my-sandwich-making-svc", resource="resource_name")
    def get_ingredients():
        # go to the pantry
        # go to the fridge
        # maybe go to the store
        return
    

See that package's API definition for the full list of supported API calls.

If you have not yet read the setup instructions for automatic instrumentation and setup, start with the Node.js Setup Instructions.

If you aren't using supported library instrumentation (see library compatibility), you may want to manually instrument your code.

You may also want to extend the functionality of the dd-trace library or gain finer control over instrumenting your application. Several techniques are provided by the library to accomplish this.

Adding tags

The built-in instrumentation and your own custom instrumentation create spans around meaningful operations.

Adding tags locally

You can access the active span in order to include meaningful data by adding tags.

const span = tracer.scope().active()

To learn more, read API details for Scope.

You can add tags to a span using the setTag or addTags method on a span. Supported value types are string, number, and object.

// add a foo:bar tag
span.setTag('foo', 'bar')

// add a user_id:5 tag
span.setTag('user_id', 5)

// add a obj.first:foo and obj.second:bar tags
span.setTag('obj', { first: 'foo', second: 'bar' })

// add a foo:bar and baz:qux tags
span.addTags({
  foo: 'bar',
  baz: 'qux'
})

Adding tags globally

You can add tags to every span by configuring them directly on the tracer, either with the comma-separated DD_TAGS environment variable or with the tags option on the tracer initialization:

// equivalent to DD_TAGS=foo:bar,baz:qux
tracer.init({
  tags: {
    foo: 'bar',
    baz: 'qux'
  }
})

// All spans will now have these tags

Adding tags through component hooks

Some Datadog integrations support span hooks that can be used to update the span right before it's finished. This is useful to modify or add tags to a span that is otherwise inaccessible from your code.

// at the top of the entry point right after tracer.init()
tracer.use('express', {
  // hook will be executed right before the request span is finished
  hooks: {
    request: (span, req, res) => {
      span.setTag('customer.id', req.query.customer_id)
    }
  }
})

To learn more, read API details for individual plugins.

Setting errors on a span

Errors can be added to a span with the special error tag that supports error objects. This splits the error into three tags: error.type, error.message, and error.stack.

try {
  getIngredients()
} catch (e) {
  span.setTag('error', e)
}

When using tracer.trace() or tracer.wrap() this is done automatically when an error is thrown.

Creating spans

The dd-trace library creates spans automatically with tracer.init() for many libraries and frameworks. However, you may want to gain visibility into your own code and this is achieved using spans.

Within your web request (for example, /make-sandwich), you may perform several operations, like getIngredients() and assembleSandwich(), which are useful to measure.

Synchronous code

Synchronous code can be traced with tracer.trace() which automatically finishes the span when its callback returns and captures any thrown error automatically.

app.get('/make-sandwich', (req, res) => {
  const sandwich = tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
    const ingredients = tracer.trace('get_ingredients', { resource: 'resource_name' }, () => {
      return getIngredients()
    })

    return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
      assembleSandwich(ingredients)
    })
  })

  res.end(sandwich)
})

To learn more, read API details for tracer.trace().

Promises

Promises can be traced with tracer.trace() which automatically finishes the span when the returned promise resolves, and captures any rejection error automatically.

const getIngredients = () => {
    return new Promise((resolve, reject) => {
        resolve('Salami');
    });
};

app.get('/make-sandwich', (req, res) => {
  return tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
    return tracer.trace('get_ingredients', { resource: 'resource_name' }, () => getIngredients())
      .then((ingredients) => {
        return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
          return assembleSandwich(ingredients)
        })
      })
  }).then(sandwich => res.end(sandwich))
})

Async/await

Async/await can be traced with tracer.trace() which automatically finishes the span when the returned promise resolves, and captures any rejection error automatically.

app.get('/make-sandwich', async (req, res) => {
  const sandwich = await tracer.trace('sandwich.make', { resource: 'resource_name' }, async () => {
    const ingredients = await tracer.trace('get_ingredients', { resource: 'resource_name' }, () => {
      return getIngredients()
    })

    return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
      return assembleSandwich(ingredients)
    })
  })

  res.end(sandwich)
})

Wrapper

You can wrap an existing function without changing its code. This is useful to trace functions for which you don't control the code. This can be done with tracer.wrap() which takes the same arguments as tracer.trace() except its last argument which is the function to wrap instead of a callback.


// After the functions are defined
getIngredients = tracer.wrap('get_ingredients', { resource: 'resource_name' }, getIngredients)
assembleSandwich = tracer.wrap('assemble_sandwich', { resource: 'resource_name' }, assembleSandwich)

// Where routes are defined
app.get('/make-sandwich', (req, res) => {

  const sandwich = tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
    const ingredients = getIngredients()

    return assembleSandwich(ingredients)
  })

  res.end(sandwich)
})

To learn more, read API details for tracer.wrap().

Request filtering

You may not want some requests of an application to be instrumented. A common case would be health checks or other synthetic traffic. These can be ignored by using the blocklist or allowlist option on the http plugin.

// at the top of the entry point right after tracer.init()
tracer.use('http', {
  blocklist: ['/health', '/ping']
})

This configuration can be split between client and server if needed. For example,

tracer.use('http', {
  server: {
    blocklist: ['/ping']
  }
})

Additionally, traces can be excluded based on their resource name, so that the Agent doesn't send them to Datadog. This and other security and fine-tuning Agent configurations can be found on the Security page or in Ignoring Unwanted Resources.

dd-trace-api

The dd-trace-api packages is in Preview and may not include all the API calls you need. If you need more complete functionality, use the API as described in the previous sections.

The dd-trace-api package provides a stable public API for Datadog APM's custom Node.js instrumentation. This package implements only the API interface, not the underlying functionality that creates and sends spans to Datadog.

This separation between interface (dd-trace-api) and implementation (dd-trace) offers several benefits:

  • You can rely on an API that changes less frequently and more predictably for your custom instrumentation
  • If you only use automatic instrumentation, you can ignore API changes entirely
  • If you implement both single-step and custom instrumentation, you avoid depending on multiple copies of the dd-trace package

To use dd-trace-api:

  1. Install the dd-trace and dd-trace-api libraries in your app. Note: dd-trace is installed for you with single-step instrumentation, but you need to install dd-trace-api manually in your app.

    npm install dd-trace dd-trace-api
    
  2. Instrument your Node.js application using dd-trace. If you're using single-step instrumentation, you can skip this step.

     node --require dd-trace/init app.js
    
  3. After this is set up, you can write custom instrumentation exactly like the examples in the previous sections, but you require dd-trace-api instead of dd-trace.

    For example:

const tracer = require('dd-trace-api')
const express = require('express')
const app = express()

app.get('/make-sandwich', (req, res) => {
  const sandwich = tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
    const ingredients = tracer.trace('get_ingredients', { resource: 'resource_name' }, () => {
      return getIngredients()
    })

    return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
      assembleSandwich(ingredients)
    })
  })

  res.end(sandwich)
})

See that package's API definition for the full list of supported API calls.

If you have not yet read the instructions for auto-instrumentation and setup, start with the Go Setup Instructions.

This documentation uses v2 of the Go tracer, which Datadog recommends for all users. If you are using v1, see the migration guide to upgrade to v2.

This page details common use cases for adding and customizing observability with Datadog APM.

Adding tags

Add custom span tags to your spans to customize your observability within Datadog. The span tags are applied to your incoming traces, allowing you to correlate observed behavior with code-level information such as merchant tier, checkout amount, or user ID.

Add custom span tags

Add tags directly to a Span interface by calling SetTag:

package main

import (
    "log"
    "net/http"

    "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
)

func handler(w http.ResponseWriter, r *http.Request) {
    // Create a span for a web request at the /posts URL.
    span := tracer.StartSpan("web.request", tracer.ResourceName("/posts"))
    defer span.Finish()

    // Set tag
    span.SetTag("http.url", r.URL.Path)
    span.SetTag("<TAG_KEY>", "<TAG_VALUE>")
}

func main() {
    tracer.Start(tracer.WithService("<SERVICE_NAME>"))
    defer tracer.Stop()
    http.HandleFunc("/posts", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Datadog's integrations make use of the Context type to propagate the current active span. If you want to add span tags attached to a Context, call the SpanFromContext function:

package main

import (
    "net/http"

    "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
)

func handler(w http.ResponseWriter, r *http.Request) {
    // Retrieve a span for a web request attached to a Go Context.
    if span, ok := tracer.SpanFromContext(r.Context()); ok {
        // Set tag
        span.SetTag("http.url", r.URL.Path)
    }
}

Adding tags globally to all spans

Add tags to all spans by configuring the tracer with the WithGlobalTag option:

package main

import (
    "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
)

func main() {
    tracer.Start(
        tracer.WithGlobalTag("datacenter", "us-1"),
        tracer.WithGlobalTag("env", "dev"),
    )
    defer tracer.Stop()
}

Set errors on a span

To set an error on one of your spans, use tracer.WithError as below:

err := someOperation()
span.Finish(tracer.WithError(err))

Adding spans

If you aren't using supported library instrumentation (see Library compatibility), you may want to manually instrument your code.

Unlike other Datadog tracing libraries, when tracing Go applications, it's recommended that you explicitly manage and pass the Go context of your spans. This approach helps ensure accurate span relationships and meaningful tracing. For more information, see the Go context library documentation or documentation for any third-party libraries integrated with your application.

Manually creating a span

To manually create spans, use the tracer package (see the v2 API on Datadog's godoc; for v1, see the v1 godoc).

You can create spans in two ways:

//v2: Create a span with a resource name, which is the child of parentSpan.
span := parentSpan.StartChild("mainOp", tracer.ResourceName("/user"))

//v1: Create a span with a resource name, which is the child of parentSpan.
span := tracer.StartSpan("mainOp", tracer.ResourceName("/user"), tracer.ChildOf(parentSpan))

// v1 and v2: Create a span which will be the child of the span in the Context ctx, if there is a span in the context.
// Returns the new span, and a new context containing the new span.
span, ctx := tracer.StartSpanFromContext(ctx, "mainOp", tracer.ResourceName("/user"))

Asynchronous traces

func main() {
	span, ctx := tracer.StartSpanFromContext(context.Background(), "mainOp")
	defer span.Finish()

	go func() {
		asyncSpan := tracer.StartSpanFromContext(ctx, "asyncOp")
		defer asyncSpan.Finish()
		performOp()
	}()
}

Distributed tracing

Create a distributed trace by manually propagating the tracing context:

package main

import (
    "net/http"

    "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
)

func handler(w http.ResponseWriter, r *http.Request) {
    span, ctx := tracer.StartSpanFromContext(r.Context(), "post.process")
    defer span.Finish()

    req, err := http.NewRequest("GET", "http://example.com", nil)
    req = req.WithContext(ctx)
    // Inject the span Context in the Request headers
    err = tracer.Inject(span.Context(), tracer.HTTPHeadersCarrier(req.Header))
    if err != nil {
        // Handle or log injection error
    }
    http.DefaultClient.Do(req)
}

Then, on the server side, to continue the trace, start a new Span from the extracted Context:

package main

import (
    "net/http"

    "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
)

func handler(w http.ResponseWriter, r *http.Request) {
    // Extract the span Context and continue the trace in this service
    sctx, err := tracer.Extract(tracer.HTTPHeadersCarrier(r.Header))
    if err != nil {
        // Handle or log extraction error
    }

    span := tracer.StartSpan("post.filter", tracer.ChildOf(sctx))
    defer span.Finish()
}

Trace client and Agent configuration

There are additional configurations possible for both the tracing client and Datadog Agent for context propagation with B3 Headers, as well as excluding specific resources from sending traces to Datadog in the event these traces are not wanted in metrics calculated, such as Health Checks.

Propagating context with headers extraction and injection

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Resource filtering

Traces can be excluded based on their resource name, to remove synthetic traffic such as health checks from reporting traces to Datadog. This and other security and fine-tuning configurations can be found on the Security page.

If you have not yet read the instructions for auto-instrumentation and setup, read the Ruby Setup Instructions.

This page details describes use cases for adding and customizing observability with Datadog APM.

Requirements

Make sure you require the appropriate gem for your Ruby tracer version:

  • For v2.x, require the datadog gem:

    require 'datadog'
    
  • For v1.x, require the ddtrace gem:

    require 'ddtrace'
    

Adding tags

Add custom span tags to your spans to customize your observability within Datadog. The span tags are applied to your incoming traces, allowing you to correlate observed behavior with code-level information such as merchant tier, checkout amount, or user ID.

Add custom span tags

Add custom tags to your spans corresponding to any dynamic value within your application code such as customer.id.

Active spans

Access the current active span from any method within your code.

Note: If the method is called and there is no active span, active_span is nil.

# get '/shopping_cart/:customer_id', to: 'shopping_cart#index'
class ShoppingCartController < ApplicationController
  # GET /shopping_cart
  def index
    # Get the active span and set customer_id -> 254889
    Datadog::Tracing.active_span&.set_tag('customer.id', params.permit([:customer_id]))

    # [...]
  end

  # POST /shopping_cart
  def create
    # [...]
  end
end

Manually instrumented spans

Add tags directly to Datadog::Span objects by calling #set_tag:

# An example of a Sinatra endpoint,
# with Datadog tracing around the request.
get '/posts' do
  Datadog::Tracing.trace('web.request') do |span|
    span.set_tag('http.url', request.path)
    span.set_tag('<TAG_KEY>', '<TAG_VALUE>')
  end
end

Adding tags globally to all spans

Add tags to all spans by configuring the tracer with the tags option:

Datadog.configure do |c|
  c.tags = { 'team' => 'qa' }
end

You can also use the DD_TAGS environment variable to set tags on all spans for an application. For more information on Ruby environment variables, read the setup documentation.

Setting errors on a span

There are two ways to set an error on a span:

  • Call span.set_error and pass in the Exception Object. This automatically extracts the error type, message, and backtrace.
require 'timeout'

def example_method
  span = Datadog::Tracing.trace('example.trace')
  puts 'some work'
  sleep(1)
  raise StandardError, "This is an exception"
rescue StandardError => error
  Datadog::Tracing.active_span&.set_error(error)
  raise
ensure
  span.finish
end

example_method()
  • Or, use tracer.trace which by default sets the error type, message, and backtrace. To configure this behavior you can use the on_error option, which is the Handler invoked when a block is provided to trace, and the block raises an error. The Proc is provided span and error as arguments. By default, on_error sets error on the span.

Default behavior for on_error:

require 'timeout'

def example_method
  puts 'some work'
  sleep(1)
  raise StandardError, "This is an exception"
end

Datadog::Tracing.trace('example.trace') do |span|
  example_method()
end

Custom behavior for on_error:

require 'timeout'

def example_method
  puts 'some work'
  sleep(1)
  raise StandardError.new "This is a special exception"
end

custom_error_handler = proc do |span, error|
  span.set_tag('custom_tag', 'custom_value')
  span.set_error(error) unless error.message.include?("a special exception")
end

Datadog::Tracing.trace('example.trace', on_error: custom_error_handler) do |span|
  example_method()
end

Adding spans

If you aren't using supported library instrumentation (see library compatibility), you can manually instrument your code. Add tracing to your code by using the Datadog::Tracing.trace method, which you can wrap around any Ruby code.

To trace any Ruby code, you can use the Datadog::Tracing.trace method:

Datadog::Tracing.trace(name, resource: resource, **options) do |span|
  # Wrap this block around the code you want to instrument
  # Additionally, you can modify the span here.
  # for example, change the resource name, or set tags
end

Where name is a String that describes the generic kind of operation being done (for example 'web.request', or 'request.parse').

resource is a String with the name of the action being operated on. Traces with the same resource value will be grouped together for the purpose of metrics. Resources are usually domain specific, such as a URL, query, request, etc. (e.g. 'Article#submit', http://example.com/articles/list.)

For all the available **options, see the reference guide.

Manually creating a new span

Programmatically create spans around any block of code. Spans created in this manner integrate with other tracing mechanisms automatically. In other words, if a trace has already started, the manual span will have its caller as its parent span. Similarly, any traced methods called from the wrapped block of code will have the manual span as its parent.

# An example of a Sinatra endpoint,
# with Datadog tracing around the request,
# database query, and rendering steps.
get '/posts' do
  Datadog::Tracing.trace('web.request', service: '<SERVICE_NAME>', resource: 'GET /posts') do |span|
    # Trace the activerecord call
    Datadog::Tracing.trace('posts.fetch') do
      @posts = Posts.order(created_at: :desc).limit(10)
    end

    # Add some APM tags
    span.set_tag('http.method', request.request_method)
    span.set_tag('posts.count', @posts.length)

    # Trace the template rendering
    Datadog::Tracing.trace('template.render') do
      erb :index
    end
  end
end

Post-processing traces

Some applications might require that traces be altered or filtered out before they are sent to Datadog. The processing pipeline allows you to create processors to define such behavior.

Filtering

You can use the Datadog::Tracing::Pipeline::SpanFilter processor to remove spans, when the block evaluates as truthy:

Datadog::Tracing.before_flush(
  # Remove spans that match a particular resource
  Datadog::Tracing::Pipeline::SpanFilter.new { |span| span.resource =~ /PingController/ },
  # Remove spans that are trafficked to localhost
  Datadog::Tracing::Pipeline::SpanFilter.new { |span| span.get_tag('host') == 'localhost' }
)

Processing

You can use the Datadog::Tracing::Pipeline::SpanProcessor processor to modify spans:

Datadog::Tracing.before_flush(
  # Strip matching text from the resource field
  Datadog::Tracing::Pipeline::SpanProcessor.new { |span| span.resource.gsub!(/password=.*/, '') }
)

Custom processor

Processors can be any object that responds to #call accepting trace as an argument (which is an Array of Datadog::Span.)

For example, using the short-hand block syntax:

Datadog::Tracing.before_flush do |trace|
   # Processing logic...
   trace
end

The following example implements a processor to achieve complex post-processing logic:

Datadog::Tracing.before_flush do |trace|
  trace.spans.each do |span|
    originalPrice = span.get_tag('order.price'))
    discount = span.get_tag('order.discount'))

    # Set a tag from a calculation from other tags
    if (originalPrice != nil && discount != nil)
      span.set_tag('order.value', originalPrice - discount)
    end
  end
  trace
end

For a custom processor class:

class MyCustomProcessor
  def call(trace)
    # Processing logic...
    trace
  end
end

Datadog::Tracing.before_flush(MyCustomProcessor.new)

In both cases, the processor method must return the trace object; this return value will be passed to the next processor in the pipeline.

Trace client and Agent configuration

There are additional configurations possible for both the tracing client and Datadog Agent for context propagation with B3 Headers, as well as to exclude specific Resources from sending traces to Datadog in the event these traces are not wanted to count in metrics calculated, such as Health Checks.

Propagating context with headers extraction and injection

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Baggage

Baggage is a hash that can be accessed through the API and is propagated by default. See the following example to manipulate Baggage:

# set_baggage_item
Datadog::Tracing.baggage['key1'] = 'value1'
Datadog::Tracing.baggage['key2'] = 'value2'

# get_all_baggage_items
all_baggage = Datadog::Tracing.baggage
puts(all_baggage) # {"key1"=>"value1", "key2"=>"value2"}

# remove_baggage_item
Datadog::Tracing.baggage.delete('key1')
puts(Datadog::Tracing.baggage) # {"key2"=>"value2"}

# get_baggage_item
puts(Datadog::Tracing.baggage['key1']) # nil
puts(Datadog::Tracing.baggage['key2']) # "value2"

# remove_all_baggage_items
Datadog::Tracing.baggage.clear
puts(Datadog::Tracing.baggage) # {}

Resource filtering

Traces can be excluded based on their resource name, to remove synthetic traffic such as health checks from reporting traces to Datadog. This and other security and fine-tuning configurations can be found on the Security page.

If you have not yet read the instructions for automatic instrumentation and setup, start with the .NET/.NET Core or .NET Framework Setup Instructions.

This page details common use cases for adding and customizing observability with Datadog APM. For a list of supported runtimes, see the .NET Framework Compatibility Requirements or the .NET Core Compatibility Requirements.

There are several ways to get more than the default automatic instrumentation:

You can combine these solutions with each other to achieve the instrumentation detail you want. However, automatic instrumentation must be setup first.

Instrument methods through configuration

Using the DD_TRACE_METHODS environment variable, you can get visibility into unsupported frameworks without changing application code. For full details on the input format for DD_TRACE_METHODS, see the .NET Framework configuration instructions or the .NET Core configuration instructions. For example, to instrument a method called SaveSession defined on the Store.Managers.SessionManager type, set:

DD_TRACE_METHODS=Store.Managers.SessionManager[SaveSession]

The resulting span has an operationName attribute with the value trace.annotation and a resourceName attribute with the value SaveSession.

If you want to customize the span's attributes and you have the ability to modify the source code, you can instrument methods through attributes instead.

Instrument methods through attributes

Add [Trace] to methods for Datadog to trace them when running with automatic instrumentation. If automatic instrumentation is not enabled, this attribute has no effect on your application.

[Trace] attributes have the default operation name trace.annotation and resource name of the traced method. You can set operation name and resource name as named arguments of the [Trace] attribute to better reflect what is being instrumented. Operation name and resource name are the only possible arguments that can be set for the [Trace] attribute. For example:

using Datadog.Trace.Annotations;

namespace Store.Managers
{
    public class SessionManager
    {
        [Trace(OperationName = "database.persist", ResourceName = "SessionManager.SaveSession")]
        public static void SaveSession()
        {
            // your method implementation here
        }
    }
}

Custom instrumentation with code

This feature requires adding the Datadog.Trace NuGet package to your application. It provides an API to directly access the Tracer and the active span.

Starting with v3.0.0, custom instrumentation requires you also use automatic instrumentation. You should aim to keep both automatic and custom instrumentation package versions (for example: MSI and NuGet) in sync, and ensure you don't mix major versions of packages.

Configuring Datadog in code

There are multiple ways to configure your application: using environment variables, a web.config file, or a datadog.json file, as described in our documentation. The Datadog.Trace NuGet package also allows you to configure settings in code.

To override configuration settings, create an instance of TracerSettings, and pass it to the static Tracer.Configure() method:

using Datadog.Trace;

// Create a settings object using the existing
// environment variables and config sources
var settings = TracerSettings.FromDefaultSources();

// Override a value
settings.GlobalTags.Add("SomeKey", "SomeValue");

// Replace the tracer configuration
Tracer.Configure(settings);

Calling Tracer.Configure() replaces the settings for all subsequent traces, both for custom instrumentation and for automatic instrumentation.

Replacing the configuration should be done once, as early as possible in your application.

Create custom traces/spans

In addition to automatic instrumentation, the [Trace] attribute, and DD_TRACE_METHODS configurations, you can customize your observability by programmatically creating spans around any block of code.

To create and activate a custom span, use Tracer.Instance.StartActive(). If a trace is already active (when created by automatic instrumentation, for example), the span is part of the current trace. If there is no current trace, a new one is started.

Ensure you dispose of the scope returned from StartActive. Disposing the scope closes the span, and ensures the trace is flushed to Datadog once all its spans are closed.

using Datadog.Trace;

// Start a new span
using (var scope = Tracer.Instance.StartActive("custom-operation"))
{
    // Do something
}

Add custom span tags to your spans to customize your observability within Datadog. The span tags are applied to your incoming traces, allowing you to correlate observed behavior with code-level information such as merchant tier, checkout amount, or user ID.

Manually creating a new span

Manually created spans automatically integrate with spans from other tracing mechanisms. In other words, if a trace has already started, the manual span has its caller as its parent span. Similarly, any traced methods called from the wrapped block of code have the manual span as its parent.

using (var parentScope =
       Tracer.Instance.StartActive("manual.sortorders"))
{
    parentScope.Span.ResourceName = "<RESOURCE NAME>";
    using (var childScope =
           Tracer.Instance.StartActive("manual.sortorders.child"))
    {
        // Nest using statements around the code to trace
        childScope.Span.ResourceName = "<RESOURCE NAME>";
        SortOrders();
    }
}

Add custom span tags

Add custom tags to your spans corresponding to any dynamic value within your application code such as customer.id.

using Datadog.Trace;

public class ShoppingCartController : Controller
{
    private IShoppingCartRepository _shoppingCartRepository;

    [HttpGet]
    public IActionResult Index(int customerId)
    {
        // Access the active scope through the global tracer
        // Note: This can return null if there is no active span
        var scope = Tracer.Instance.ActiveScope;

        if (scope != null)
        {
            // Add a tag to the span for use in the Datadog web UI
            scope.Span.SetTag("customer.id", customerId.ToString());
        }

        var cart = _shoppingCartRepository.Get(customerId);

        return View(cart);
    }
}

Usage with ASP.NET IHttpModule

To access the current request span from a custom ASP.NET IHttpModule, it is best to read Tracer.Instance.ActiveScope in the PreRequestHandlerExecute event (or AcquireRequestState if you require session state).

While Datadog creates the request span at the start of the ASP.NET pipeline, the execution order of IHttpModules is not guaranteed. If your module runs before Datadog's, ActiveScope may be null during early events like BeginRequest. The PreRequestHandlerExecute event occurs late enough in the lifecycle to help ensure the Datadog module has run and the span is available.

ActiveScope can still be null for other reasons (for example, if instrumentation is disabled), so you should always check for null.

using System;
using System.Web;
using Datadog.Trace;

public class MyCustomModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        // Prefer reading ActiveScope late in the pipeline
        context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;

        // If you need session state, you can also hook AcquireRequestState:
        // context.AcquireRequestState += OnPreRequestHandlerExecute;
    }

    private void OnPreRequestHandlerExecute(object sender, EventArgs e)
    {
        // Earlier events (e.g., BeginRequest) may run before the Datadog module,
        // so ActiveScope can be null there. Here it should be available.
        var scope = Tracer.Instance.ActiveScope;
        if (scope == null)
        {
            return; // there is no active scope, for example, if instrumentation is disabled
        }

        // Example: add a custom tag
        scope.Span.SetTag("my.custom.tag", "some_value");
    }

    public void Dispose()
    {
    }
}

Set errors on a span

To mark errors that occur in your code, use the Span.SetException(Exception) method. The method marks the span as an error and adds related span metadata to provide insight into the exception.

try
{
    // do work that can throw an exception
}
catch(Exception e)
{
    span.SetException(e);
}

This sets the following tags on the span:

  • "error.message":exception.Message
  • "error.stack":exception.ToString()
  • "error.type":exception.GetType().ToString()

Propagating context with headers extraction and injection

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Adding tags globally to all spans

Use the DD_TAGS environment variable to set tags across all generated spans for an application. This can be useful for grouping stats for your applications, data centers, or regions within the Datadog UI. For example:

DD_TAGS=datacenter:njc,key2:value2

Resource filtering

You can exclude traces based on the resource name to remove Synthetics traffic such as health checks. For more information about security and additional configurations, see Configure the Datadog Agent or Tracer for Data Security.

If you have not yet read the instructions for auto-instrumentation and setup, start with the PHP Setup Instructions. Even if Datadog does not officially support your web framework, you may not need to perform any manual instrumentation. See automatic instrumentation for more details.

Annotations

If you are using PHP 8, as of v0.84 of the tracer, you can add attributes to your code to instrument it. It is a lighter alternative to custom instrumentation written in code. For example, add the #[DDTrace\Trace] attribute to methods for Datadog to trace them.

<?php
class Server {
    #[\DDTrace\Trace(name: "spanName", resource: "resourceName", type: "Custom", service: "myService", tags: ["aTag" => "aValue"])]
    static function process($arg) {}

    #[\DDTrace\Trace]
    function get() {
      Foo::simple(1);
    }
}

You can provide the following arguments:

  • $name: The operation name to be assigned to the span. Defaults to the function name.
  • $resource: The resource to be assigned to the span.
  • $type: The type to be assigned to the span.
  • $service: The service to be assigned to the span. Defaults to default or inherited service name.
  • $tags: The tags to be assigned to the span.
  • $recurse: Whether recursive calls shall be traced.
  • $run_if_limited: Whether the function shall be traced in limited mode. (For example, when span limit exceeded)

If a namespace is present, you must use the fully qualified name of the attribute #[\DDTrace\Trace]. Alternatively, you can import the namespace with use DDTrace\Trace; and use #[Trace].

Writing custom instrumentation

To write custom instrumentation, you do not need any additional composer package.

The Datadog APM PHP Api is fully documented in stubs. This allows you to have automated documentation in PHPStorm.

A sample application to be instrumented

Assume the following directory structure:

.
|-- composer.json
|-- docker-compose.yml
|-- index.php
`-- src
    |-- Exceptions
    |   `-- NotFound.php
    |-- Services
    |   `-- SampleRegistry.php
    `-- utils
        `-- functions.php

Within this, two files contain the functions and methods that are interesting to instrument. The most relevant files are src/utils/functions.php:

namespace App;

function some_utility_function($someArg)
{
    return 'result';
}

And src/Services/SampleRegistry.php:

namespace App\Services;

use App\Exceptions\NotFound;
use Exception;

class SampleRegistry
{
    public function put($key, $value)
    {
        \App\some_utility_function('some argument');
        // Return the id of the item inserted
        return 456;
    }

    public function faultyMethod()
    {
        throw new Exception('Generated at runtime');
    }

    public function get($key)
    {
        // The service uses an exception to report a key not found.
        throw new NotFound('The key was not found');
    }

    public function compact()
    {
        // This function executes some operations on the registry and
        // returns nothing. In the middle of the function, we have an
        // interesting value that is not returned but can be related
        // to the slowness of the function

        $numberOfItemsProcessed = 123;

        // ...
    }
}

Steps to instrument the sample application

To avoid mixing application or service business logic with instrumentation code, write the required code in a separate file:

  1. Create a file datadog/instrumentation.php and add it to the composer autoloader:

    {
        ...
        "autoload": {
            ...
            "files": [
                ...
                "datadog/instrumentation.php"
            ]
        },
        ...
    }
    
  2. Dump the autoloader by running composer dump.

    The file that contains the custom instrumentation code and the actual classes that are instrumented are not required to be in the same codebase and package. By separating them, you can publish an open source composer package containing only your instrumentation code, which others might find useful.

  3. In the datadog/instrumentation.php file, check if the extension is loaded:

    if (!extension_loaded('ddtrace')) {
        return;
    }
    
  4. Instrument a function. For \App\some_utility_function, if you are not interested in any specific aspect of the function other than the execution time:

    \DDTrace\trace_function('App\some_utility_function', function (\DDTrace\SpanData $span, $args, $ret, $exception) {});
    
  5. For the SampleRegistry::put method, add tags with the returned item identifier and the key. Because put is a method, use \DDTrace\trace_method instead of \DDTrace\trace_function:

    \DDTrace\trace_method(
        'App\Services\SampleRegistry',
        'put',
        function (\DDTrace\SpanData $span, $args, $ret, $exception) {
            $span->meta['app.cache.key'] = $args[0]; // The first argument is the 'key'
            $span->meta['app.cache.item_id'] = $ret; // The returned value
        }
    );
    
  6. For SampleRegistry::faultyMethod, which generates an exception, there is nothing extra to do. If the method is instrumented, the default exception reporting mechanism attaches the exception message and stack trace.

  7. For SampleRegistry::get, which uses a NotFound exception as part of business logic, you can prevent marking the span as an error by unsetting the exception:

    \DDTrace\trace_method(
        'App\Services\SampleRegistry',
        'get',
        function (\DDTrace\SpanData $span, $args, $ret, $exception) {
            if ($exception instanceof \App\Exceptions\NotFound) {
                unset($span->exception);
                $span->resource = 'cache.get.not_found';
            }
        }
    );
    
  8. For SampleRegistry::compact, to add a tag with a value that is neither an argument nor the return value, you can access the active span directly from within the method:

    public function compact()
    {
        $numberOfItemsProcessed = 123;
    
        // Add instrumenting code within your business logic.
        if (\function_exists('\DDTrace\active_span') && $span = \DDTrace\active_span()) {
            $span->meta['registry.compact.items_processed'] = $numberOfItemsProcessed;
        }
    
        // ...
    }
    

Details about trace_function and trace_method

The DDTrace\trace_function and DDTrace\trace_method functions instrument (trace) specific function and method calls. These functions automatically handle the following tasks:

  • Open a span before the code executes.
  • Set any errors from the instrumented call on the span.
  • Close the span when the instrumented call is done.

Additional tags are set on the span from the closure (called a tracing closure).

For example, the following snippet traces the CustomDriver::doWork method and adds custom tags:

<?php
\DDTrace\trace_method(
    'CustomDriver',
    'doWork',
    function (\DDTrace\SpanData $span, array $args, $retval, $exception) {
        // This closure runs after the instrumented call
        $span->name = 'CustomDriver.doWork';
        $span->resource = 'CustomDriver.doWork';
        $span->service = 'php';

        // If an exception was thrown from the instrumented call, return value is null
        $span->meta['doWork.size'] = $exception ? 0 : count($retval),
        // Access object members via $this
        $span->meta['doWork.thing'] = $this->workToDo;
    }
);
?>

Accessing active spans

The built-in instrumentation and your own custom instrumentation creates spans around meaningful operations. You can access the active span in order to include meaningful data.

Current span

The following method returns a DDTrace\SpanData object. When tracing is disabled, null is returned.

<?php
$span = \DDTrace\active_span();
if ($span) {
    $span->meta['customer.id'] = get_customer_id();
}
?>

Root span

The following method returns a DDTrace\SpanData object. When tracing is disabled, null is returned. This is useful in contexts where the metadata to be added to the root span does not exist in early script execution.

<?php
$span = \DDTrace\root_span();
if ($span) {
    $span->meta['customer.id'] = get_customer_id();
}
?>

Adding tags

When you set tags, to avoid overwriting existing tags automatically added by the Datadog core instrumentation, do write $span->meta['mytag'] = 'value'. Do not write $span->meta = ['mytag' => 'value'].

Adding tags locally

Add tags to a span by using the DDTrace\SpanData::$meta array.

<?php

\DDTrace\trace_function(
    'myRandFunc',
    function(\DDTrace\SpanData $span, array $args, $retval) {
        // ...
        $span->meta['rand.range'] = $args[0] . ' - ' . $args[1];
        $span->meta['rand.value'] = $retval;
    }
);

Adding tags globally

Set the DD_TAGS environment variable (version 0.47.0+) to automatically apply tags to every span that is created.

DD_TAGS=key1:value1,<TAG_KEY>:<TAG_VALUE>

Setting errors on a span

Thrown exceptions are automatically attached to the active span, unless the exception is thrown at a deeper level in the call stack and it is caught before it reaches any function that is traced.

<?php

function doRiskyThing() {
    throw new Exception('Oops!');
}

\DDTrace\trace_function(
    'doRiskyThing',
    function() {
        // Span will be flagged as erroneous and have
        // the stack trace and exception message attached as tags
    }
);

Set the error.message tag to manually flag a span as erroneous.

<?php

function doRiskyThing() {
    return SOME_ERROR_CODE;
}

\DDTrace\trace_function(
    'doRiskyThing',
    function(\DDTrace\SpanData $span, $args, $retval) {
        if ($retval === SOME_ERROR_CODE) {
            $span->meta['error.message'] = 'Foo error';
            // Optional:
            $span->meta['error.type'] = 'CustomError';
            $span->meta['error.stack'] = (new \Exception)->getTraceAsString();
        }
    }
);

Span links associate one or more spans together that don't have a typical parent-child relationship. They may associate spans within the same trace or spans across different traces.

To add a span link from an existing span:

$spanA = \DDTrace\start_trace_span();
$spanA->name = 'spanA';
\DDTrace\close_span();

$spanB = \DDTrace\start_trace_span();
$spanB->name = 'spanB';
// Link spanB to spanA
$spanB->links[] = $spanA->getLink();
\DDTrace\close_span();

Context propagation for distributed traces

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Resource filtering

Traces can be excluded based on their resource name, to remove synthetic traffic such as health checks from reporting traces to Datadog. This and other security and fine-tuning configurations can be found on the Security page.

API reference

The Datadog APM PHP Api is fully documented in stubs. This allows you to have automated documentation in PHPStorm.

Parameters of the tracing closure

The tracing closure provided to DDTrace\trace_method() and DDTrace\trace_function() has four parameters:

function(
    DDTrace\SpanData $span,
    array $args,
    mixed $retval,
    Exception|null $exception
);
  1. $span: An instance of DDTrace\SpanData to write to the span properties
  2. $args: An array of arguments from the instrumented call
  3. $retval: The return value of the instrumented call
  4. $exception: An instance of the exception that was thrown in the instrumented call or null if no exception was thrown

Advanced configurations

Tracing internal functions and methods

As of version 0.76.0, all internal functions can unconditionally be traced.

On older versions, tracing internal functions and methods requires setting the DD_TRACE_TRACED_INTERNAL_FUNCTIONS environment variable, which takes a CSV of functions or methods that is to be instrumented. For example, DD_TRACE_TRACED_INTERNAL_FUNCTIONS=array_sum,mt_rand,DateTime::add. Once a function or method has been added to the list, it can be instrumented using DDTrace\trace_function() and DDTrace\trace_method() respectively. The DD_TRACE_TRACED_INTERNAL_FUNCTIONS environment variable is obsolete as of version 0.76.0.

Running the tracing closure before the instrumented call

By default, tracing closures are treated as posthook closures meaning they are executed after the instrumented call. Some cases require running the tracing closure before the instrumented call. In that case, tracing closures are marked as prehook using an associative configuration array.

\DDTrace\trace_function('foo', [
    'prehook' => function (\DDTrace\SpanData $span, array $args) {
        // This tracing closure will run before the instrumented call
    }
]);

Debugging sandboxed errors

Tracing closures are "sandboxed" in that exceptions thrown and errors raised inside of them do no impact the instrumented call.

<?php

function my_func() {
  echo 'Hello!' . PHP_EOL;
}

\DDTrace\trace_function(
  'my_func',
  function() {
    throw new \Exception('Oops!');
  }
);

my_func();
echo 'Done.' . PHP_EOL;

/*
Hello!
Done.
*/

To debug, set the environment variable DD_TRACE_DEBUG=1 to expose any exceptions or errors that may have occurred in a tracing closure.

/*
Hello!
Exception thrown in tracing closure for my_func: Oops!
Done.
*/

Zend framework 1 manual instrumentation

Zend framework 1 is automatically instrumented by default, so you are not required to modify your ZF1 project. However, if automatic instrumentation is disabled, enable the tracer manually.

First, download the latest source code from the releases page. Extract the zip file and copy the src/DDTrace folder to your application's /library folder. Then add the following to your application/configs/application.ini file:

autoloaderNamespaces[] = "DDTrace_"
pluginPaths.DDTrace = APPLICATION_PATH "/../library/DDTrace/Integrations/ZendFramework/V1"
resources.ddtrace = true

PHP code optimization

Prior to PHP 7, some frameworks provided ways to compile PHP classes (for example, through the Laravel's php artisan optimize command).

While this has been deprecated if you are using PHP 7.x, you still may use this caching mechanism in your app prior to version 7.x. In this case, Datadog suggests you use the OpenTracing API instead of adding datadog/dd-trace to your Composer file.

If you have not yet read the setup instructions, start with the C++ Setup Instructions.

Creating spans

To manually instrument a method:

{
  // Create a root span for the current request.
  auto root_span = tracer.create_span();
  root_span.set_name("get_ingredients");
  // Set a resource name for the root span.
  root_span.set_resource_name("bologna_sandwich");
  // Create a child span with the root span as its parent.
  auto child_span = root_span.create_child();
  child_span.set_name("cache_lookup");
  // Set a resource name for the child span.
  child_span.set_resource_name("ingredients.bologna_sandwich");
  // Spans can be finished at an explicit time ...
  child_span.set_end_time(std::chrono::steady_clock::now());
} // ... or implicitly when the destructor is invoked.
  // For example, root_span finishes here.

Adding tags

Add custom span tags to your spans to customize your observability within Datadog. Span tags are applied to your incoming traces, allowing you to correlate observed behavior with code-level information such as merchant tier, checkout amount, or user ID.

Note that some Datadog tags are necessary for unified service tagging.

Adding tags locally

Add tags directly to a span object by calling Span::set_tag. For example:

// Add tags directly to a span by calling `Span::set_tag`
auto span = tracer.create_span();
span.set_tag("key must be string", "value must also be a string");

// Or, add tags by setting a `SpanConfig`
datadog::tracing::SpanConfig opts;
opts.tags.emplace("team", "apm-proxy");
auto span2 = tracer.create_span(opts);

Adding tags globally

Environment variable

To set tags across all your spans, set the DD_TAGS environment variable as a list of key:value pairs separated by commas.

export DD_TAGS=team:apm-proxy,key:value

In code

datadog::tracing::TracerConfig tracer_config;
tracer_config.tags = {
  {"team", "apm-proxy"},
  {"apply", "on all spans"}
};

const auto validated_config = datadog::tracing::finalize_config(tracer_config);
auto tracer = datadog::tracing::Tracer(*validated_config);

// All new spans will have contains tags defined in `tracer_config.tags`
auto span = tracer.create_span();

Set errors on a span

To associate a span with an error, set one or more error-related tags on the span. For example:

span.set_error(true);

Add more specific information about the error by setting any combination of error.message, error.stack, or error.type by using respectively Span::set_error_message, Span::set_error_stack and Span::set_error_type. See Error Tracking for more information about error tags.

An example of adding a combination of error tags:

// Associate this span with the "bad file descriptor" error from the standard
// library.
span.set_error_message("error");
span.set_error_stack("[EBADF] invalid file");
span.set_error_type("errno");

Using any of the Span::set_error_* result in an underlying call to Span::set_error(true).

To unset an error on a span, set Span::set_error to false, which removes any combination of Span::set_error_stack, Span::set_error_type or Span::set_error_message.

// Clear any error information associated with this span.
span.set_error(false);

Propagating context with headers extraction and injection

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read Trace Context Propagation for information.

Resource filtering

Traces can be excluded based on their resource name, to remove synthetic traffic such as health checks from sending traces and influencing trace metrics. Find information about this and other security and fine-tuning configuration on the Security page.

Further reading