Skip to content

Commit

Permalink
App manual instrumentation
Browse files Browse the repository at this point in the history
Signed-off-by: Pavol Loffay <p.loffay@gmail.com>
  • Loading branch information
pavolloffay committed Oct 26, 2023
1 parent af98f18 commit 36b03b1
Show file tree
Hide file tree
Showing 8 changed files with 1,779 additions and 1,366 deletions.
78 changes: 72 additions & 6 deletions 03-app-instrumentation.md
@@ -1,7 +1,6 @@
# OpenTelemetry metrics instrumentation

This tutorial step focuses on instrumenting the services of the
[sample application](./app).
This tutorial step focuses on instrumenting the services of the [sample application](./app).

## Application Description

Expand Down Expand Up @@ -46,11 +45,11 @@ your application _manually_ or _automatically_:

- Manual instrumentation means that you modify your code yourself: you initialize and
configure the SDK, you load instrumentation libraries, you create your own spans,
metrics, etc.
metrics using the API.
Developers can use this approach to tune the observability of their application to
their needs.
- Automatic instrumentation means that you don't have to touch your code to get your
application emit code.
application emit telemetry data.
Automatic instrumentation is great to get you started with OpenTelemetry, and it is
also valuable for Application Operators, who have no access or insights about the
source code.
Expand All @@ -59,8 +58,75 @@ In the following we will introduce you to both approaches.

## Manual Instrumentation

* Use API to create custom metrics
* Configure exporter OTLP/Prometheus
As a developer you can add OpenTelemetry to your code by using the
language-specific APIs and SDKs.

In this tutorial we will only instrument the [frontend](./app/frontend) service manually, we will use
automatic instrumentation for the other services in the next step.

Before continuing make sure you can run OpenTelemetry collector locally:
```bash
docker run --rm -it -p 4317:4317 --name=otel-collector -v ./app:/tmp ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:0.88.0 --config /tmp/collector-docker.yaml
```

For development you can run the app locally by installing all dependencies
and running it with `nodemon` from the [./app/frontend](./app/frontend/) directory:

```bash
cd app/frontend
npm install
npx nodemon index.js
```

If you don't have `Node.JS` installed locally, you can use a container for development:

```bash
cd app/frontend
docker run -p 4000:4000 --link otel-collector --rm -it --workdir=/app -v ${PWD}:/app:z node:18-alpine /bin/sh
npm install
npx nodemon index.js
```

Open the [index.js](./app/frontend/index.js) file with your preferred editor.
Use the instructions provided by the
[official OpenTelemetry documentation](https://opentelemetry.io/docs/instrumentation/js/getting-started/nodejs/)
to add tracing & metrics. A few differences in your implementation:

- Instead of creating a dedicated `instrument.js` you can add the initialization of the SDK at the top of `index.js` directly.

Give it a try yourself, if you are unsure how to accomplish this, you can peek
into the [instrument.js](./app/frontend/instrument.js) file.

To see if spans are emitted to the collector, call the frontend service via your
browser or curl:

```bash
curl localhost:4000/
```

The **Internal Server Error** response is OK for now, because you don't have the backends
running.

If all works, the frontend application should emit metrics and print them to the standard output:
```bash
{
descriptor: {
name: 'request_total',
type: 'UP_DOWN_COUNTER',
description: 'A counter of request',
unit: '',
valueType: 0
},
dataPointType: 3,
dataPoints: [
{ attributes: {}, startTime: [Array], endTime: [Array], value: 1 }
]
}
```
Now replace the `ConsoleSpanExporter` with an `OTLPTraceExporter` as outlined in the [Exporters](https://opentelemetry.io/docs/instrumentation/js/exporters/) documentation (make use of `opentelemetry/exporter-metrics-otlp-grpc` & `opentelemetry/exporter-trace-otlp-grpc`)
Finally, look into the `index.js` file once again, there are a few additional `TODOs` for you!
## Auto-instrumentation
Expand Down
32 changes: 32 additions & 0 deletions app/collector-docker.yaml
@@ -0,0 +1,32 @@
# docker run --rm -it --name=otelcol -p 4317:4317 -v ./app:/tmp ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:0.88.0 --config /tmp/collector-docker.yaml
receivers:
otlp:
protocols:
grpc:
http:

processors:

exporters:
debug:
verbosity: detailed

extensions:
health_check:
pprof:
zpages:

service:
extensions: [zpages]
telemetry:
logs:
level: info
pipelines:
traces:
receivers: [otlp]
processors: []
exporters: [debug]
metrics:
receivers: [otlp]
processors: []
exporters: [debug]
1 change: 1 addition & 0 deletions app/frontend/.gitignore
@@ -0,0 +1 @@
node_modules/
7 changes: 7 additions & 0 deletions app/frontend/index.js
Expand Up @@ -6,6 +6,8 @@ const http = require("http");
const app = require("express")();
const pino = require('pino-http')()

var otelsdkinit = require('./instrument.js');

app.use(pino)

const port = process.env.FRONTEND_PORT || 4000;
Expand All @@ -19,12 +21,17 @@ const gameCounter = myMeter.createUpDownCounter('app_games_total', {
description: "A counter of how often the game has been played",
valueType: ValueType.INT
})
const requestCounter = myMeter.createUpDownCounter('request_total', {
description: "Counter of requests",
valueType: ValueType.INT
})
const winCounter = myMeter.createUpDownCounter('app_wins_total', {
description: "A counter per player who has won",
valueType: ValueType.INT
})

app.get("/", (req, res) => {
requestCounter.add(1);
const { player1, player2 } = Object.assign({player1: "Player 1", player2: "Player 2"}, req.query)
if(player1 == 'Player 1') {
req.log.info('Player 1 prefers to stay anonymous.')
Expand Down
6 changes: 4 additions & 2 deletions app/frontend/instrument.js
Expand Up @@ -11,14 +11,16 @@ const opentelemetry = require("@opentelemetry/sdk-node");
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node");
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-grpc");
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');

const { PeriodicExportingMetricReader, MeterProvider, ConsoleMetricExporter } = require('@opentelemetry/sdk-metrics')

const sdk = new opentelemetry.NodeSDK({
traceExporter: new OTLPTraceExporter(),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter(),
// exporter: new ConsoleMetricExporter()
}),
instrumentations: [getNodeAutoInstrumentations()]
});

sdk.start()
sdk.start()

0 comments on commit 36b03b1

Please sign in to comment.