diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..5050893 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1 @@ +FROM mperel/dev-container \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..5aba40e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,73 @@ +// For format details, see https://aka.ms/vscode-remote/devcontainer.json or the definition README at +// https://github.com/microsoft/vscode-dev-containers/tree/master/containers/docker-existing-dockerfile +{ + "name": "Dev Dockerfile", + "dockerFile": "Dockerfile", + // The optional 'runArgs' property can be used to specify additional runtime arguments. + "runArgs": [ + // Enable go debugger + "--cap-add=SYS_PTRACE", + "--security-opt", + "seccomp=unconfined", + // map host docker daemon into container for sibling containers + "-v", + "/var/run/docker.sock:/var/run/docker.sock", + "--network", + "host" + ], + "settings": { + // General settings + "files.eol": "\n", + "terminal.integrated.shell.linux": "/bin/bash", + // Go settings + "go.lintTool": "golangci-lint", + "go.lintFlags": [ + "--fast" + ], + "vim.insertModeKeyBindings": [ + { + "before": [ + "j", + "j" + ], + "after": [ + "" + ] + } + ], + // Go recommended settings with modules: https://github.com/golang/tools/blob/master/gopls/doc/vscode.md#vscode + "go.useLanguageServer": true, + "[go]": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true, + }, + // Optional: Disable snippets, as they conflict with completion ranking. + "editor.snippetSuggestions": "none", + }, + "[go.mod]": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true, + }, + }, + "gopls": { + // Add parameter placeholders when completing a function. + "usePlaceholders": true, + // Allow multiple modules + "experimentalWorkspaceModule": true, + }, + // Python settings + "python.pythonPath": "/usr/bin/python", + "python.formatting.blackPath": "/usr/local/bin/black", + "python.formatting.provider": "black", + "[python]": { + "editor.formatOnSave": true, + } + }, + "extensions": [ + "golang.go", + "ms-python.python", + "vscodevim.vim", + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4565c07 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# What is this? +This is a demo of Open Telemetry's distributed tracing capabilities. +In `docker-compose.yml` there are variety of services: +* `server` - a service that implements an http server +* `client` - a service that sends a few requests to the server +* `jaeger` - an open source telemetry backend +* `zipkin` - an open source telemetry backend +* `otel-agent` - a service that receives traces from `server` and `client` +* `otel-collector` - a service that receives traces forwarded from `otel-agent` + and exports them to `jaeger` and `zipkin` + +# Why is this interesting? +By using Open Telemetry with the agent and collector, backends are swappable +and all services handle tracing in the same way, regardless of language. + +Specifically, applications send traces to the agent, which forwards them to the +collector, and the collector defines backends via exporters in yaml. + +Here we use 2 exporters, `jaeger` and `zipkin`, but there are many possible +exporters including +[Azure Monitor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/azuremonitorexporter). + +# How to use? +`docker-compose up --build` brings up all services. +The `client` sends a few requests to `server`. The distributed traces appear in +`jaeger` and `zipkin`. + +`jaeger` can be accessed at `http://localhost:16686`. + +`zipkin` can be accessed at `http://localhost:9411`. + +`docker-compose down` cleans up all resources. + +If you would like to manually make requests to the server after the client ends, +navigate to `http://localhost:8080/hello`. + +After requests have been made, if you choose the `client` service in `jaeger`, +you should see something similar to: + +![Overview](./docs/jaeger.png) + +Note that you can see all traces that started from the client. If you click on +a trace, you can see the distributed spans that make up the trace: + +![Spans](./docs/jaeger-span.png) + +# How to navigate the code? +Start by reading the comments in `src/cmd/client/client.go`. +They describe how to create a trace that propagates to the server over +an http request. + +Next, read the comments in `src/cmd/server/server.go`. They describe +how the propagated trace is used in subsequent spans. + +Finally, read the comments in `src/pkg/tracer_provider/tracer_provider.go`. They +describe boilerplate code that sets up a tracer provider for each application. + +# Development +A dev container has been provided. To use: +* Ensure the `Remote - Containers` extension is installed in VSCode +* Open the project in the container +* Install the Go extension libraries with `Go: Install/Update tools` from + the command palette + +# Citations +The collector code is adapted from +[this official otel example](https://github.com/open-telemetry/opentelemetry-collector/tree/main/examples/demo). + +The client / server code is adapted from +[this official otel example](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation/net/http/otelhttp/example). diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..ecd7516 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,44 @@ +version: "3" +services: + jaeger: + image: jaegertracing/all-in-one@sha256:710ddea40c84ce01acdb9fdb4c0a76ac11990e425a666968a3ae360dabc4e15f + ports: + - "16686:16686" + zipkin: + image: openzipkin/zipkin@sha256:1ae0572be3d26fd9ab3fd2da5e8feaa0ca0078dbc31e2ddfb881b1a56bc332b1 + ports: + - "9411:9411" + otel-collector: + image: otel/opentelemetry-collector@sha256:6f1c53a611091402f0403b628554621591effc7cf88e94be1f9141be007d11c7 + command: ["--config=/etc/otel-collector-config.yaml"] + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + depends_on: + - jaeger + - zipkin + otel-agent: + image: otel/opentelemetry-collector@sha256:6f1c53a611091402f0403b628554621591effc7cf88e94be1f9141be007d11c7 + command: ["--config=/etc/otel-agent-config.yaml"] + volumes: + - ./otel-agent-config.yaml:/etc/otel-agent-config.yaml + depends_on: + - otel-collector + client: + command: ["/app/client"] + build: + context: ./src + args: + CMD: client + depends_on: + - otel-agent + - server + server: + command: ["/app/server"] + build: + context: ./src + args: + CMD: server + ports: + - "8080:8080" + depends_on: + - otel-agent diff --git a/docker-lock.json b/docker-lock.json new file mode 100644 index 0000000..42e6f84 --- /dev/null +++ b/docker-lock.json @@ -0,0 +1,58 @@ +{ + "composefiles": { + "docker-compose.yaml": [ + { + "name": "golang", + "tag": "latest", + "digest": "04b95d37ab61bd05b6f163383dbd54da4553be2b427b8980a72f778be4edec6b", + "dockerfile": "src/Dockerfile", + "service": "client" + }, + { + "name": "scratch", + "tag": "", + "digest": "", + "dockerfile": "src/Dockerfile", + "service": "client" + }, + { + "name": "jaegertracing/all-in-one", + "tag": "latest", + "digest": "710ddea40c84ce01acdb9fdb4c0a76ac11990e425a666968a3ae360dabc4e15f", + "service": "jaeger" + }, + { + "name": "otel/opentelemetry-collector", + "tag": "latest", + "digest": "6f1c53a611091402f0403b628554621591effc7cf88e94be1f9141be007d11c7", + "service": "otel-agent" + }, + { + "name": "otel/opentelemetry-collector", + "tag": "latest", + "digest": "6f1c53a611091402f0403b628554621591effc7cf88e94be1f9141be007d11c7", + "service": "otel-collector" + }, + { + "name": "golang", + "tag": "latest", + "digest": "04b95d37ab61bd05b6f163383dbd54da4553be2b427b8980a72f778be4edec6b", + "dockerfile": "src/Dockerfile", + "service": "server" + }, + { + "name": "scratch", + "tag": "", + "digest": "", + "dockerfile": "src/Dockerfile", + "service": "server" + }, + { + "name": "openzipkin/zipkin", + "tag": "latest", + "digest": "1ae0572be3d26fd9ab3fd2da5e8feaa0ca0078dbc31e2ddfb881b1a56bc332b1", + "service": "zipkin" + } + ] + } +} \ No newline at end of file diff --git a/docs/jaeger-span.png b/docs/jaeger-span.png new file mode 100644 index 0000000..e274186 Binary files /dev/null and b/docs/jaeger-span.png differ diff --git a/docs/jaeger.png b/docs/jaeger.png new file mode 100644 index 0000000..de93da3 Binary files /dev/null and b/docs/jaeger.png differ diff --git a/otel-agent-config.yaml b/otel-agent-config.yaml new file mode 100644 index 0000000..5376901 --- /dev/null +++ b/otel-agent-config.yaml @@ -0,0 +1,25 @@ +receivers: + otlp: + protocols: + grpc: + jaeger: + protocols: + grpc: + zipkin: + +exporters: + otlp: + endpoint: "otel-collector:4317" + insecure: true + logging: + loglevel: debug + +processors: + batch: + +service: + pipelines: + traces: + receivers: [otlp, jaeger, zipkin] + processors: [batch] + exporters: [otlp, logging] diff --git a/otel-collector-config.yaml b/otel-collector-config.yaml new file mode 100644 index 0000000..ae4d9a1 --- /dev/null +++ b/otel-collector-config.yaml @@ -0,0 +1,25 @@ +receivers: + otlp: + protocols: + grpc: + +exporters: + logging: + + zipkin: + endpoint: "http://zipkin:9411/api/v2/spans" + format: proto + + jaeger: + endpoint: "jaeger:14250" + insecure: true + +processors: + batch: + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [logging, jaeger, zipkin] diff --git a/src/Dockerfile b/src/Dockerfile new file mode 100644 index 0000000..e5f7837 --- /dev/null +++ b/src/Dockerfile @@ -0,0 +1,14 @@ +FROM golang@sha256:04b95d37ab61bd05b6f163383dbd54da4553be2b427b8980a72f778be4edec6b AS builder +WORKDIR /app +COPY go.mod . +COPY go.sum . +RUN go mod download +COPY . . + +FROM builder AS prodBuilder +ARG CMD +RUN CGO_ENABLED=0 go build -ldflags="-w -s" "./cmd/${CMD}" + +FROM scratch AS prod +ARG CMD +COPY --from=prodBuilder "/app/${CMD}" "/app/${CMD}" diff --git a/src/cmd/client/client.go b/src/cmd/client/client.go new file mode 100644 index 0000000..b47050b --- /dev/null +++ b/src/cmd/client/client.go @@ -0,0 +1,106 @@ +package main + +import ( + "context" + "errors" + "io" + "net/http" + "time" + + "github.com/michaelperel/otel-demo/pkg/tracer_provider" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +// Annotate the tracer with the library name. In Jaeger, this shows up as a tag. +var tr = otel.Tracer("cmd/client") + +func makeRequest() error { + ctx, span := tr.Start( + context.Background(), + "make request", + trace.WithSpanKind(trace.SpanKindClient), + ) + defer span.End() + + // Auto instrument the http client. + client := http.Client{ + Transport: otelhttp.NewTransport(http.DefaultTransport), + } + + // Optionally, add arbitrary values to the context as "baggage" + ctx = baggage.ContextWithValues(ctx, attribute.String("username", "donuts")) + + // Creating a request with the trace context will allow information about + // the trace to propagate to the server (implementation detail: + // via request headers). + req, err := http.NewRequestWithContext( + ctx, + "GET", + "http://server:8080/hello", + nil, + ) + if err != nil { + span.RecordError(err) + return err + } + + // Simulate work before the client is done, so you can view + // different time lengths in the span in telemetry backends. + time.Sleep(1 * time.Second) + + res, err := client.Do(req) + if err != nil { + span.RecordError(err) + return err + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + span.RecordError(err) + return err + } + + span.AddEvent(string(body)) + + return nil +} + +func main() { + addr := "otel-agent:4317" + + // Initializing the trace provider with the service name makes + // all traces easy to find by service. + shutdown := tracer_provider.Initialize(addr, "client") + defer shutdown() + + // Wait for the server to start (obviously, in a real project, use retry + // logic rather than sleeping) + _, span := tr.Start(context.Background(), "wait for server") + time.Sleep(10 * time.Second) + + // Adding an event is the same as logging a message in the span. + span.AddEvent("slept for 10 seconds, to wait for server to come up") + span.End() + + for i := 0; i < 5; i++ { + if err := makeRequest(); err != nil { + panic(err) + } + } + + // Record an error, just to see what it looks like in the backend + _, errSpan := tr.Start(context.Background(), "example error") + defer errSpan.End() + + errSpan.RecordError(errors.New("example error")) + + // Setting the status fails the entire span. In Jaeger, this causes the + // span to appear red. + errSpan.SetStatus(codes.Error, "fail entire span") +} diff --git a/src/cmd/server/server.go b/src/cmd/server/server.go new file mode 100644 index 0000000..ee6a926 --- /dev/null +++ b/src/cmd/server/server.go @@ -0,0 +1,58 @@ +package main + +import ( + "fmt" + "net/http" + "time" + + "github.com/michaelperel/otel-demo/pkg/tracer_provider" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/trace" +) + +var tr = otel.Tracer("cmd/server") + +func helloHandler(w http.ResponseWriter, r *http.Request) { + // Simulate work to make viewing the span easier in the telemetry backend. + time.Sleep(1 * time.Second) + + // Trace context propagated from the client request in r.Context(). + ctx := r.Context() + _, span := tr.Start( + ctx, + "handle request", + trace.WithSpanKind(trace.SpanKindClient), + ) + defer span.End() + + // Baggage from client propagates as well + uk := attribute.Key("username") + username := baggage.Value(ctx, uk) + span.AddEvent("reading username baggage...", trace.WithAttributes(uk.String(username.AsString()))) + + // Add the response as an attribute to the span. This can show up + // differently in different backends, but in Jaeger attributes show up + // as tags. + response := "hello, world" + span.SetAttributes(attribute.String("body", response)) + + fmt.Fprint(w, response) +} + +func main() { + addr := "otel-agent:4317" + + shutdown := tracer_provider.Initialize(addr, "server") + defer shutdown() + + otelHandler := otelhttp.NewHandler(http.HandlerFunc(helloHandler), "/hello") + http.Handle("/hello", otelHandler) + + err := http.ListenAndServe(":8080", nil) + if err != nil { + panic(err) + } +} diff --git a/src/go.mod b/src/go.mod new file mode 100644 index 0000000..11e189c --- /dev/null +++ b/src/go.mod @@ -0,0 +1,15 @@ +module github.com/michaelperel/otel-demo + +go 1.16 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 + go.opentelemetry.io/otel v0.20.0 + go.opentelemetry.io/otel/exporters/otlp v0.20.0 + go.opentelemetry.io/otel/sdk v0.20.0 + go.opentelemetry.io/otel/trace v0.20.0 + golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 // indirect + google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece // indirect + google.golang.org/grpc v1.37.0 +) diff --git a/src/go.sum b/src/go.sum new file mode 100644 index 0000000..669f92c --- /dev/null +++ b/src/go.sum @@ -0,0 +1,148 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= +go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 h1:Q3C9yzW6I9jqEc8sawxzxZmY48fs9u220KXq6d5s3XU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= +go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= +go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= +go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= +go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= +go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= +go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8= +go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= +go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8= +go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= +go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= +go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece h1:1YM0uhfumvoDu9sx8+RyWwTI63zoCQvI23IYFRlvte0= +google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/src/pkg/tracer_provider/tracer_provider.go b/src/pkg/tracer_provider/tracer_provider.go new file mode 100644 index 0000000..bfabc8d --- /dev/null +++ b/src/pkg/tracer_provider/tracer_provider.go @@ -0,0 +1,65 @@ +package tracer_provider + +import ( + "context" + "log" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp" + "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/semconv" + "google.golang.org/grpc" +) + +func Initialize(addr, serviceName string) func() { + ctx := context.Background() + + // Export all traces to otel agent + exp, err := otlp.NewExporter(ctx, otlpgrpc.NewDriver( + otlpgrpc.WithInsecure(), + otlpgrpc.WithEndpoint(addr), + otlpgrpc.WithDialOption(grpc.WithBlock()), + )) + handleErr(err, "failed to create exporter") + + // Every trace should be searchable by service name in backends. + res, err := resource.New(ctx, + resource.WithAttributes( + semconv.ServiceNameKey.String(serviceName), + ), + ) + handleErr(err, "failed to create resource") + + // Process spans in batches, for performance + bsp := sdktrace.NewBatchSpanProcessor(exp) + tracerProvider := sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithResource(res), + sdktrace.WithSpanProcessor(bsp), + ) + + // Set the global tracer provider + otel.SetTracerProvider(tracerProvider) + + // Ensure that the trace context and baggage propagate in requests + otel.SetTextMapPropagator( + propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ), + ) + + return func() { + handleErr(tracerProvider.Shutdown(ctx), "failed to shutdown provider") + handleErr(exp.Shutdown(ctx), "failed to stop exporter") + } +} + +func handleErr(err error, message string) { + if err != nil { + log.Fatalf("%s: %v", message, err) + } +}