- 필수 기능
- 시작하기
- Glossary
- 표준 속성
- Guides
- Agent
- 통합
- 개방형텔레메트리
- 개발자
- Administrator's Guide
- API
- Datadog Mobile App
- CoScreen
- Cloudcraft
- 앱 내
- 서비스 관리
- 인프라스트럭처
- 애플리케이션 성능
- APM
- Continuous Profiler
- 스팬 시각화
- 데이터 스트림 모니터링
- 데이터 작업 모니터링
- 디지털 경험
- 소프트웨어 제공
- 보안
- AI Observability
- 로그 관리
- 관리
This tutorial walks you through the steps for enabling tracing on a sample Go application installed on a host. In this scenario, you install a Datadog Agent on the same host as the application.
For other scenarios, including applications in containers or on cloud infrastructure, Agent in a container, and applications written in different languages, see the other Enabling Tracing tutorials.
See Tracing Go Applications for general comprehensive tracing setup documentation for Go.
sudo
. The host has the following requirements:If you haven’t installed a Datadog Agent on your machine, go to Integrations > Agent and select your operating system. For example, on most Linux platforms, you can install the Agent by running the following script, replacing <YOUR_API_KEY>
with your Datadog API key:
DD_AGENT_MAJOR_VERSION=7 DD_API_KEY=<YOUR_API_KEY> DD_SITE="datadoghq.com" bash -c "$(curl -L https://install.datadoghq.com/scripts/install_script.sh)"
To send data to a Datadog site other than datadoghq.com
, replace the DD_SITE
environment variable with your Datadog site.
Verify that the Agent is running and sending data to Datadog by going to Events > Explorer, optionally filtering by the Datadog
Source facet, and looking for an event that confirms the Agent installation on the host:
Next, install a sample application to trace. The code sample for this tutorial can be found at github.com/DataDog/apm-tutorial-golang.git. Clone the git repository by running:
git clone https://github.com/DataDog/apm-tutorial-golang.git
Build the sample application using the following command. The command might take a while the first time you run it:
make runNotes
The sample notes
application is a basic REST API that stores data in an in-memory database. Use curl
to send a few API requests:
curl localhost:8080/notes
[]
because there is nothing in the database yetcurl -X POST 'localhost:8080/notes?desc=hello'
hello
and an ID value of 1
. Returns {"id":1,"description":"hello"}
.curl localhost:8080/notes/1
id
value of 1
: {"id":1,"description":"hello"}
curl -X POST 'localhost:8080/notes?desc=otherNote'
otherNote
and an ID value of 2
. Returns {"id":2,"description":"otherNote"}
curl localhost:8080/notes
[{"id":1,"description":"hello"},{"id";2,"description":"otherNote"}]
Run more API calls to see the application in action. When you’re done, run the following command to exit the application:
make exitNotes
Next, install the Go tracer. From your apm-tutorial-golang
directory, run:
go get gopkg.in/DataDog/dd-trace-go.v1/ddtrace
Now that the tracing library has been added to go.mod
, enable tracing support.
Uncomment the following imports in apm-tutorial-golang/cmd/notes/main.go
:
cmd/notes/main.go
sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql"
chitrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi"
httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"fmt"
Change the import:
_ "github.com/mattn/go-sqlite3"
to:
"github.com/mattn/go-sqlite3"
In the main()
function, uncomment the following lines:
cmd/notes/main.go
tracer.Start()
defer tracer.Stop()
cmd/notes/main.go
client = httptrace.WrapClient(client, httptrace.RTWithResourceNamer(func(req *http.Request) string {
return fmt.Sprintf("%s %s", req.Method, req.URL.Path)
}))
cmd/notes/main.go
r.Use(chitrace.Middleware(chitrace.WithServiceName("notes")))
In setupDB()
, uncomment the following lines:
cmd/notes/main.go
sqltrace.Register("sqlite3", &sqlite3.SQLiteDriver{}, sqltrace.WithServiceName("db"))
db, err := sqltrace.Open("sqlite3", "file::memory:?cache=shared")
Comment out the following line:
cmd/notes/main.go
db, err := sql.Open("sqlite3", "file::memory:?cache=shared")
Once you’ve made these changes, run:
go mod tidy
To start generating and collecting traces, launch the application again with make runNotes
.
Use curl
to again send requests to the application:
curl localhost:8080/notes
[]
curl -X POST 'localhost:8080/notes?desc=hello'
{"id":1,"description":"hello"}
curl localhost:8080/notes/1
{"id":1,"description":"hello"}
curl localhost:8080/notes
[{"id":1,"description":"hello"}]
Wait a few moments, and take a look at your Datadog UI. Navigate to APM > Traces. The Traces list shows something like this:
There are entries for the database (db
) and the notes
app. The traces list shows all the spans, when they started, what resource was tracked with the span, and how long it took.
If you don’t see traces, clear any filter in the Traces Search field (sometimes it filters on an environment variable such as ENV
that you aren’t using).
On the Traces page, click on a POST /notes
trace, and you’ll see a flame graph that shows how long each span took and what other spans occurred before a span completed. The bar at the top of the graph is the span you selected on the previous screen (in this case, the initial entry point into the notes application).
The width of a bar indicates how long it took to complete. A bar at a lower depth represents a span that completes during the lifetime of a bar at a higher depth.
The flame graph for a POST
trace looks something like this:
A GET /notes
trace looks something like this:
You can configure the tracing library to add tags to the telemetry it sends to Datadog. Tags help group, filter, and display data meaningfully in dashboards and graphs. To add tags, specify environment variables when running the application. The project Makefile
includes the environment variables DD_ENV
, DD_SERVICE
, and DD_VERSION
, which are set to enable Unified Service Tagging:
Makefile
run: build
DD_TRACE_SAMPLE_RATE=1 DD_SERVICE=notes DD_ENV=dev DD_VERSION=0.0.1 ./cmd/notes/notes &
Makefile
also sets the DD_TRACE_SAMPLE_RATE
environment variable to 1
, which represents a 100% sample rate. A 100% sample rate ensures that all requests to the notes service are sent to the Datadog backend for analysis and display for the purposes of this tutorial. In an actual production or high-volume environment, you wouldn't specify this high of a rate. Setting a high sample rate with this variable in the application overrides the Agent configuration and results in a very large volume of data being sent to Datadog. For most use cases, allow the Agent to automatically determine the sampling rate.For more information on available configuration options, see Configuring the Go Tracing Library.
Datadog has several fully supported libraries for Go that allow for automatic tracing when implemented in the code. In the cmd/notes/main.go
file, you can see the go-chi
, sql
, and http
libraries being aliased to the corresponding Datadog libraries: chitrace
, sqltrace
, and httptrace
respectively:
main.go
import (
...
sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql"
chitrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi"
httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http"
...
)
In cmd/notes/main.go
, the Datadog libraries are initialized with the WithServiceName
option. For example, the chitrace
library is initialized as follows:
main.go
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(chitrace.Middleware(chitrace.WithServiceName("notes")))
r.Mount("/", nr.Register())
Using chitrace.WithServiceName("notes")
ensures that all elements traced by the library fall under the service name notes
.
The main.go
file contains more implementation examples for each of these libraries. For an extensive list of libraries, see Go Compatibility Requirements.
In cases where code doesn’t fall under a supported library, you can create spans manually.
Remove the comments around the makeSpanMiddleware
function in notes/notesController.go
. It generates middleware that wraps a request in a span with the supplied name. To use this function, comment out the following lines:
notes/notesController.go
r.Get("/notes", nr.GetAllNotes) // GET /notes
r.Post("/notes", nr.CreateNote) // POST /notes
r.Get("/notes/{noteID}", nr.GetNoteByID) // GET /notes/123
r.Put("/notes/{noteID}", nr.UpdateNoteByID) // PUT /notes/123
r.Delete("/notes/{noteID}", nr.DeleteNoteByID) // DELETE /notes/123
Remove the comments around the following lines:
notes/notesController.go
r.Get("/notes", makeSpanMiddleware("GetAllNotes", nr.GetAllNotes)) // GET /notes
r.Post("/notes", makeSpanMiddleware("CreateNote", nr.CreateNote)) // POST /notes
r.Get("/notes/{noteID}", makeSpanMiddleware("GetNote", nr.GetNoteByID)) // GET /notes/123
r.Put("/notes/{noteID}", makeSpanMiddleware("UpdateNote", nr.UpdateNoteByID)) // PUT /notes/123
r.Delete("/notes/{noteID}", makeSpanMiddleware("DeleteNote", nr.DeleteNoteByID)) // DELETE /notes/123
Also remove the comment around the following import:
notes/notesController.go
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
There are several examples of custom tracing in the sample application. Here are a couple more examples. Remove the comments to enable these spans:
The doLongRunningProcess
function creates child spans from a parent context:
notes/notesHelper.go
func doLongRunningProcess(ctx context.Context) {
childSpan, ctx := tracer.StartSpanFromContext(ctx, "traceMethod1")
childSpan.SetTag(ext.ResourceName, "NotesHelper.doLongRunningProcess")
defer childSpan.Finish()
time.Sleep(300 * time.Millisecond)
log.Println("Hello from the long running process in Notes")
privateMethod1(ctx)
}
The privateMethod1
function demonstrates creating a completely separate service from a context:
notes/notesHelper.go
func privateMethod1(ctx context.Context) {
childSpan, _ := tracer.StartSpanFromContext(ctx, "manualSpan1",
tracer.SpanType("web"),
tracer.ServiceName("noteshelper"),
)
childSpan.SetTag(ext.ResourceName, "privateMethod1")
defer childSpan.Finish()
time.Sleep(30 * time.Millisecond)
log.Println("Hello from the custom privateMethod1 in Notes")
}
Uncomment the following imports:
notes/notesHelper.go
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
Launch the application with make runNotes
and try the curl
commands again to observe the custom spans and traces you’ve just configured:
curl localhost:8080/notes
[]
curl -X POST 'localhost:8080/notes?desc=hello'
{"id":1,"description":"hello"}
curl localhost:8080/notes/1
{"id":1,"description":"hello"}
curl localhost:8080/notes
[{"id":1,"description":"hello"}]
For more information on custom tracing, see Go Custom Instrumentation.
Tracing a single application is a great start, but the real value in tracing is seeing how requests flow through your services. This is called distributed tracing.
The sample project includes a second application called calendar
that returns a random date whenever it is invoked. The POST
endpoint in the notes application has a second query parameter named add_date
. When it is set to y
, the notes application calls the calendar application to get a date to add to the note.
To enable tracing in the calendar application, uncomment the following lines in cmd/calendar/main.go
:
cmd/calendar/main.go
chitrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/go-chi/chi"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
cmd/calendar/main.go
tracer.Start()
defer tracer.Stop()
cmd/calendar/main.go
r.Use(chitrace.Middleware(chitrace.WithServiceName("calendar")))
If the notes application is still running, use make exitNotes
to stop it.
Run make run
to start the sample application.
Send a POST request with the add_date
parameter:
curl -X POST 'localhost:8080/notes?desc=hello_again&add_date=y'
In the Trace Explorer, click this latest notes
trace to see a distributed trace between the two services:
This flame graph combines interactions from multiple applications:
chi
router through the supported go-chi
library.createNote
function that was manually traced by the makeSpanMiddleware
function. The function created a span from the context of the HTTP request.http
library and the client initialized in the main.go
file. This GET request is sent to the calendar application. The calendar application spans appear in blue because they are separate service.go-chi
router handles the GET request and the GetDate
function is manually traced with its own span under the GET request.db
call is its own service from the supported sql
library. It appears at the same level as the GET /Calendar
request because they are both called by the parent span CreateNote
.If you’re not receiving traces as expected, set up debug mode for the Go tracer. Read Enable debug mode to find out more.
추가 유용한 문서, 링크 및 기사: