Skip to content

Commit f1eda2b

Browse files
Reformat OT instrumentation guide
1 parent cfe304f commit f1eda2b

1 file changed

Lines changed: 61 additions & 52 deletions

File tree

guides/OT_INSTRUMENTATION.md

Lines changed: 61 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
Instrumenting ONS services for Open Telemetry
2-
===============
1+
# Instrumenting ONS services for Open Telemetry
2+
3+
## Instrumenting Java services for OT
34

4-
# Instrumenting Java services for OT
55
These lines to be added in the Dockerfile(s), and to any run.sh scripts that are used locally:
66

77
`-javaagent:target/dependency/aws-opentelemetry-agent-1.31.0.jar \`
88

99
`-Dotel.propagators=tracecontext,baggage \`
1010

1111
For instance:
12-
```
12+
13+
```sh
1314
ENTRYPOINT java $JAVA_OPTS \
1415
-Drestolino.realm=$REALM \
1516
-Drestolino.files=$RESTOLINO_STATIC \
@@ -21,9 +22,9 @@ ENTRYPOINT java $JAVA_OPTS \
2122
com.github.davidcarboni.restolino.Main
2223
```
2324

24-
2525
The following entry needs to be added to the pom:
26-
```
26+
27+
```xml
2728
<!-- OpenTelemetry-->
2829
<dependency>
2930
<groupId>software.amazon.opentelemetry</groupId>
@@ -32,7 +33,6 @@ The following entry needs to be added to the pom:
3233
</dependency>
3334
```
3435

35-
3636
The following environment variables need to be set on the instance:
3737

3838
`OTEL_SERVICE_NAME=<service name>`
@@ -43,9 +43,9 @@ The URL specified has an identifier of http. In fact the protocol in use is GRPC
4343
on GRPC). However, the java URL parsing lib doesn't recognise 'grpc://' as a valid protocol, so the configuration
4444
requires it to be specified as above.
4545

46-
4746
Spans can be created around individual calls within the code as follows:
48-
```
47+
48+
```java
4949
import io.opentelemetry.api.GlobalOpenTelemetry;
5050
import io.opentelemetry.api.trace.Span;
5151
import io.opentelemetry.context.Scope;
@@ -67,41 +67,43 @@ try (Scope scope = span.makeCurrent()) {
6767
}
6868
```
6969

70-
7170
(NB - using the tracer pulled from the global scope this way works, but it's preferable to instantiate the tracer in the init code for the service then inject it into your method. See here for full guidance: [Open Telemetry Docs](https://opentelemetry.io/docs/instrumentation/java/manual/#:~:text=To%20create%20Spans%2C%20you%20only,set%20by%20the%20OpenTelemetry%20SDK.&text=It's%20required%20to%20call%20end,you%20want%20it%20to%20end)).
7271

73-
74-
## Logging Implementation
72+
### Logging Implementation
7573

7674
The existing logging library has been modified to extract the traceId from the traceparent header (if it exists) and add it to the TraceId section of the log message. This will enable log entries to be correlated with trace ids which will allow engineers to zero in on problems quickly and accurately.
7775

78-
7976
The original logging library was modified in order to manage the change centrally and avoid the need for code changes across multiple applications.
8077

78+
## Instrumenting Go services for OT
8179

82-
# Instrumenting Go services for OT
8380
The following environment variables need to be created:
84-
```
81+
82+
```go
8583
OTServiceName string `envconfig:"OTEL_SERVICE_NAME"`
8684
OTExporterOTLPEndpoint string `envconfig:"OTEL_EXPORTER_OTLP_ENDPOINT"`
8785
OTBatchTimeout time.Duration `envconfig:"OTEL_BATCH_TIMEOUT"`
8886
```
87+
8988
These can then be set in the config:
90-
```
89+
90+
```go
9191
cfg = &Config{
9292
OTExporterOTLPEndpoint: "localhost:4317",
9393
OTServiceName: "service-name",
9494
OTBatchTimeout: 5 * time.Second,
9595
}
9696
```
97-
Note that the exporter endpoint is `<hostname>:<port>`, unlike the java configuration there is no protocol identifier
97+
98+
Note that the exporter endpoint is `<hostname>:<port>`, unlike the Java configuration there is no protocol identifier
9899

99100
Import the shared init library for go dp-otel-go
100101

101102
`import "github.com/ONSdigital/dp-otel-go"`
102103

103104
From the init code of the library initialise the otel services:
104-
```
105+
106+
```go
105107
//Set up OpenTelemetry
106108
cfg, err := config.Get()
107109

@@ -122,12 +124,14 @@ defer func() {
122124
err = errors.Join(err, otelShutdown(context.Background()))
123125
}()
124126
```
127+
125128
NB: if this isn't done any calls to the otel service will fail silently. If you find that traces are not coming through, ensure this code is getting called.
126129

130+
### Instrumenting http handlers
127131

128-
## Instrumenting http handlers
129132
There are a wide range of different facilities for instrumenting http calls. The simplest (taken here from dp-search-api) simply creates a new opentelemetry handler to pass to the server and attaches otelmux middlewarer to the router:
130-
```
133+
134+
```go
131135
import "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux"
132136
import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
133137
...
@@ -139,10 +143,9 @@ router.Use(otelmux.Middleware(cfg.OTServiceName))
139143
server := serviceList.GetHTTPServer(cfg.BindAddr, otelHandler)
140144
```
141145

142-
143146
Where gorillamux or Chi is not being used for the router, it may be necessary to instrument individual routes as follows:
144147

145-
```
148+
```go
146149
func routes(router *mux.Router, hc *healthcheck.HealthCheck) *RendererAPI {
147150
api := RendererAPI{router: router}
148151

@@ -160,28 +163,31 @@ func routes(router *mux.Router, hc *healthcheck.HealthCheck) *RendererAPI {
160163
```
161164

162165
Where the server is configured with an api field:
163-
```
166+
167+
```go
164168
return &Service{
165-
api: searchAPI,
169+
api: searchAPI,
166170
}
167171
```
168-
Handlers can be wrapped as below:
169-
```
172+
173+
Handlers can be wrapped as below:
174+
175+
```go
170176
func (a *SearchAPI) RegisterGetSearch(...) *SearchAPI {
171-
a.Router.Handle(
172-
"/search",
173-
otelhttp.NewHandler(
174-
SearchHandlerFunc(
177+
a.Router.Handle(
178+
"/search",
179+
otelhttp.NewHandler(
180+
SearchHandlerFunc(
175181
...
176-
), "/search"),
177-
).Methods(http.MethodGet)
178-
return a
182+
), "/search"),
183+
).Methods(http.MethodGet)
184+
return a
179185
}
180186
```
181187

182-
183188
The following shows an alternative way to instrument:
184-
```
189+
190+
```go
185191
func CreateRendererAPI(ctx context.Context, bindAddr string, allowedOrigins string, errorChan chan error, hc *healthcheck.HealthCheck) {
186192
router := mux.NewRouter()
187193
routes(router, hc)
@@ -201,35 +207,37 @@ func CreateRendererAPI(ctx context.Context, bindAddr string, allowedOrigins stri
201207
}
202208
```
203209

204-
205210
A purely middleware approach can also be taken where Alice is already in place chaining middleware. Both otelmux and otelhttp are used here to capture all requests with sufficient detail. Here you can see an example instrumentation:
206-
```
211+
212+
```go
207213
func New(cfg Config) http.Handler {
208214
router := mux.NewRouter()
209-
router.Use(otelmux.Middleware(cfg.OTServiceName))
210-
middleware := []alice.Constructor{
211-
otelhttp.NewMiddleware(cfg.OTServiceName),
215+
router.Use(otelmux.Middleware(cfg.OTServiceName))
216+
middleware := []alice.Constructor{
217+
otelhttp.NewMiddleware(cfg.OTServiceName),
212218
...
213-
}
214-
newAlice := alice.New(middleware...).Then(router)
219+
}
220+
newAlice := alice.New(middleware...).Then(router)
215221
}
216222
```
217223

218-
## Instrumenting http calls
224+
### Instrumenting http calls
225+
219226
Outgoing service calls need to be instrumented to include the traceparent header when the handler itself is not instrumented. This can be done as follows:
220-
```
227+
228+
```go
221229
import ("go.opentelemetry.io/otel"
222230
"go.opentelemetry.io/otel/propagation")
223231
...
224232

225233
otel.GetTextMapPropagator().Inject(req.Context(), propagation.HeaderCarrier(req.Header))
226234
```
227235

228-
229-
## Manually adding spans:
236+
### Manually adding spans
230237

231238
Similarly to the Java approach, you can create a span manually as follows:
232-
```
239+
240+
```go
233241
import ("go.opentelemetry.io/otel")
234242
...
235243
...
@@ -239,18 +247,19 @@ ctx, span := tracer.Start(r.Context(), "table render span")
239247
defer span.End()
240248
```
241249

242-
## Mongo Instrumentation:
250+
### Mongo Instrumentation
251+
243252
The `dp-mongodb` package has been instrumented centrally as of version 3.7.0. This means that there is no need for additional instrumentation in services that import this package version or above. Make sure this version or above is imported!
244253

254+
### Kafka Instrumentation
245255

246-
## Kafka Instrumentation:
247256
The `dp-kafka` package has also be instrumented centerally as of version 4, this requires the context to be passed in order to work:
248-
```
257+
258+
```go
249259
kafkaProducer.Channels().Output <- kafka.BytesMessage{Value: bytes, Context: ctx}
250260
```
251261

252-
253-
## Logging Implementation
262+
### Logging Implementation - Go
254263

255264
Go http request middleware was created to extract the traceId from the traceparent header and insert into the expected place in the request context (as controlled by the RequestIdKey in the github.com/ONSdigital/dp-net/v2/request package)
256265

0 commit comments

Comments
 (0)