DDTrace Golang
There are two ways to instrument a Go application:
1. Compile-time Instrumentation¶
- Does not require source-code changes and instruments supported libraries at build time.
- Works well in CI/CD when you want broad, centrally managed coverage.
2. Manual Instrumentation¶
Use dd-trace-go in code to create spans around selected operations. This option:
- Gives precise control over which parts of the application are traced.
- Requires source-code changes.
This guide uses dd-trace-go/v2. Upstream no longer maintains v1. A legacy application that cannot migrate must keep its matching v1 API; never mix v1 and v2 import paths.
Requirements¶
- Applications must use Go modules. Module vendoring is supported.
- Go must be 1.18+; for production, prefer a Go version still maintained upstream.
- Install DataKit and enable the DDTrace collector.
- Install Orchestrion and ensure
$(go env GOBIN)or$(go env GOPATH)/binis onPATH:
- Register Orchestrion in the project root:
This updates go.mod, go.sum, and creates orchestrion.tool.go. Commit those files with the application so local and CI builds use the same instrumentation dependency.
- Build, test, and run through Orchestrion:
More Documentation¶
Manual instrumentation¶
Install the v2 trace SDK:
If profiling is needed, install the profiler as well and enable the DataKit Profiling collector:
Other libraries related to components, as needed, for example:
go get github.com/DataDog/dd-trace-go/contrib/gorilla/mux/v2
go get github.com/DataDog/dd-trace-go/contrib/net/http/v2
go get github.com/DataDog/dd-trace-go/contrib/database/sql/v2
See the GitHub integration library or the Datadog support matrix for available integrations. Automatic HTTP integration does not cover every business operation, so add manual spans for critical business logic.
Code Examples¶
Simple HTTP Server¶
package main
import (
"log"
"net/http"
"time"
httptrace "github.com/DataDog/dd-trace-go/contrib/net/http/v2"
"github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
"github.com/DataDog/dd-trace-go/v2/profiler"
)
func main() {
tracer.Start(
tracer.WithService("test"),
tracer.WithEnv("test"),
)
defer tracer.Stop()
err := profiler.Start(
profiler.WithService("test"),
profiler.WithEnv("test"),
profiler.WithProfileTypes(
profiler.CPUProfile,
profiler.HeapProfile,
// The profiles below are disabled by
// default to keep overhead low, but
// can be enabled as needed.
// profiler.BlockProfile,
// profiler.MutexProfile,
// profiler.GoroutineProfile,
),
)
if err != nil {
log.Fatal(err)
}
defer profiler.Stop()
// Create a traced mux router
mux := httptrace.NewServeMux()
// Continue using the router as you normally would.
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(time.Second)
w.Write([]byte("Hello World!"))
})
if err := http.ListenAndServe(":18080", mux); err != nil {
log.Fatal(err)
}
}
Compile and run
profiler.Start() is optional. When enabled, profiling data is delivered to the DataKit Profiling receiver, not the trace receiver described on this page. Confirm that the profile collector is enabled.
Manual Tracing¶
The following code demonstrates trace data collection for a file opening operation.
Start the tracer at the application entry point and pass the parent span context to downstream operations. In v2, use StartChild or StartSpanFromContext to establish parent/child relationships instead of the v1 ChildOf pattern:
package main
import (
"os"
"time"
"github.com/DataDog/dd-trace-go/v2/ddtrace/ext"
"github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
)
func main() {
tracer.Start(
tracer.WithEnv("prod"),
tracer.WithService("test-file-read"),
tracer.WithServiceVersion("1.2.3"),
tracer.WithGlobalTag("project", "add-ddtrace-in-golang-project"),
)
// end of app exit, make sure tracer stopped
defer tracer.Stop()
tick := time.NewTicker(time.Second)
defer tick.Stop()
// your-app-main-entry...
for {
runApp()
runAppWithError()
select {
case <-tick.C:
}
}
}
func runApp() {
var err error
span := tracer.StartSpan("get.data")
defer func() { span.Finish(tracer.WithError(err)) }()
child := span.StartChild("read.file")
child.SetTag(ext.ResourceName, os.Args[0])
bts, err := os.ReadFile(os.Args[0])
span.SetTag("file_len", len(bts))
child.Finish(tracer.WithError(err))
}
func runAppWithError() {
var err error
span := tracer.StartSpan("get.data")
child := span.StartChild("read.file")
child.SetTag(ext.ResourceName, "somefile-not-found.go")
defer func() {
child.Finish(tracer.WithError(err))
span.Finish(tracer.WithError(err))
}()
_, err = os.ReadFile("somefile-not-found.go")
}
Compile and run
After running the program for a while, you can see trace data similar to the following in Guance:
Supported Environment Variables¶
The following environment variables configure DDTrace at process startup:
See DDTrace-Go documentation for the complete list, precedence, and version-specific behavior.
Attention
Avoid setting conflicting values for the same setting in code and environment variables. Precedence can differ by Go SDK version; when overriding a value, use the running SDK's documentation and startup logs as the source of truth.
-
DD_VERSIONSets the application version, such as
1.2.3,2022.02.13 -
DD_SERVICESets the application service name
-
DD_ENVSets the current environment of the application, such as
prod,pre-prod, etc. -
DD_AGENT_HOSTDefault:
localhostSets the DataKit host name or IP to which traces are sent.
DD_TRACE_AGENT_URL, when set, normally takes precedence. -
DD_TRACE_AGENT_PORTSets the trace receiver port. The common upstream default is
8126; explicitly specify the DataKit HTTP port (normally9529) for DataKit. -
DD_DOGSTATSD_HOST,DD_DOGSTATSD_PORTThe DogStatsD destination for runtime metrics. The normal default port is
8125. To receive DogStatsD data from the Go SDK, enable the DataKit StatsD collector; do not point it at the trace port9529. -
DD_TRACE_SAMPLING_RULESA JSON rule array evaluated in order.
sample_rateranges from[0.0, 1.0].Example 1: Set the global sampling rate to 20%:
DD_TRACE_SAMPLING_RULES='[{"sample_rate": 0.2}]' ./my-appExample 2: Sample traces at 10% when service matches
app1.*and the span name isabc, otherwise at 20%:DD_TRACE_SAMPLING_RULES='[{"service": "app1.*", "name": "abc", "sample_rate": 0.1}, {"sample_rate": 0.2}]' ./my-app -
DD_TRACE_SAMPLE_RATEv2 default:
1.0Sets the global SDK-side sample rate. It is a simpler, separate setting from
DD_TRACE_SAMPLING_RULES; use rules when sampling must vary by service or operation. -
DD_TRACE_RATE_LIMITSets the maximum number of sampled traces per second for each Go process. When sampling rules or a sample rate are set, the usual default is
100. -
DD_TAGSDefault:
[]Here you can inject a set of global tags, which will appear in each span and profile data. Multiple tags can be separated by spaces and commas, such as
layer:api,team:intake,layer:api team:intake -
DD_TRACE_STARTUP_LOGSDefault:
trueEnable DDTrace-related configuration and diagnostic logs
-
DD_TRACE_DEBUGDefault:
falseEnable DDTrace-related debug logs
-
DD_TRACE_ENABLEDDefault:
trueEnable trace switch. If this switch is manually turned off, no trace data will be generated
-
DD_SERVICE_MAPPINGDefault:
nullDynamically rename service names, service name mappings can be separated by spaces and commas, such asmysql:mysql-service-name,postgres:postgres-service-name,mysql:mysql-service-name postgres:postgres-service-name
