Skip to content

Commit e73b510

Browse files
authored
Update metrics docs for JS (#3853)
1 parent 40745c9 commit e73b510

File tree

2 files changed

+166
-58
lines changed

2 files changed

+166
-58
lines changed

content/en/docs/concepts/signals/metrics.md

+4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ The instrument kind is one of the following:
7373
A histogram is a good choice if you are interested in value statistics. For
7474
example: How many requests take fewer than 1s?
7575

76+
For more on synchronous and asynchronous instruments, and which kind is best
77+
suited for your use case, see
78+
[Supplementary Guidelines](/docs/specs/otel/metrics/supplementary-guidelines/).
79+
7680
## Aggregation
7781

7882
In addition to the metric instruments, the concept of **aggregations** is an

content/en/docs/languages/js/instrumentation.md

+162-58
Original file line numberDiff line numberDiff line change
@@ -1192,30 +1192,10 @@ Node.js or Web SDKs.
11921192

11931193
## Metrics
11941194

1195-
To start producing [metrics](/docs/concepts/signals/metrics), you'll need to
1196-
have an initialized `MeterProvider` that lets you create a `Meter`. `Meter`s let
1197-
you create `Instrument`s that you can use to create different kinds of metrics.
1198-
OpenTelemetry JavaScript currently supports the following `Instrument`s:
1199-
1200-
- Counter, a synchronous instrument that supports non-negative increments
1201-
- Asynchronous Counter, an asynchronous instrument which supports non-negative
1202-
increments
1203-
- Histogram, a synchronous instrument that supports arbitrary values that are
1204-
statistically meaningful, such as histograms, summaries, or percentile
1205-
- Asynchronous Gauge, an asynchronous instrument that supports non-additive
1206-
values, such as room temperature
1207-
- UpDownCounter, a synchronous instrument that supports increments and
1208-
decrements, such as the number of active requests
1209-
- Asynchronous UpDownCounter, an asynchronous instrument that supports
1210-
increments and decrements
1211-
1212-
For more on synchronous and asynchronous instruments, and which kind is best
1213-
suited for your use case, see
1214-
[Supplementary Guidelines](/docs/specs/otel/metrics/supplementary-guidelines/).
1215-
1216-
If a `MeterProvider` is not created either by an instrumentation library or
1217-
manually, the OpenTelemetry Metrics API will use a no-op implementation and fail
1218-
to generate data.
1195+
[Metrics](/docs/concepts/signals/metrics) combine individual measurements into
1196+
aggregates, and produce data which is constant as a function of system load.
1197+
Aggregates lack details required to diagnose low level issues, but complement
1198+
spans by helping to identify trends and providing application runtime telemetry.
12191199

12201200
### Initialize Metrics
12211201

@@ -1275,9 +1255,8 @@ const resource = Resource.default().merge(
12751255

12761256
const metricReader = new PeriodicExportingMetricReader({
12771257
exporter: new ConsoleMetricExporter(),
1278-
1279-
// Default is 60000ms (60 seconds). Set to 3 seconds for demonstrative purposes only.
1280-
exportIntervalMillis: 3000,
1258+
// Default is 60000ms (60 seconds). Set to 10 seconds for demonstrative purposes only.
1259+
exportIntervalMillis: 10000,
12811260
});
12821261

12831262
const myServiceMeterProvider = new MeterProvider({
@@ -1314,8 +1293,8 @@ const resource = Resource.default().merge(
13141293
const metricReader = new PeriodicExportingMetricReader({
13151294
exporter: new ConsoleMetricExporter(),
13161295

1317-
// Default is 60000ms (60 seconds). Set to 3 seconds for demonstrative purposes only.
1318-
exportIntervalMillis: 3000,
1296+
// Default is 60000ms (60 seconds). Set to 10 seconds for demonstrative purposes only.
1297+
exportIntervalMillis: 10000,
13191298
});
13201299

13211300
const myServiceMeterProvider = new MeterProvider({
@@ -1358,7 +1337,10 @@ call `getMeter` to acquire a meter. For example:
13581337
```ts
13591338
import opentelemetry from '@opentelemetry/api';
13601339

1361-
const myMeter = opentelemetry.metrics.getMeter('my-service-meter');
1340+
const myMeter = opentelemetry.metrics.getMeter(
1341+
'instrumentation-scope-name',
1342+
'instrumentation-scope-version',
1343+
);
13621344

13631345
// You can now use a 'meter' to create instruments!
13641346
```
@@ -1368,59 +1350,181 @@ const myMeter = opentelemetry.metrics.getMeter('my-service-meter');
13681350
```js
13691351
const opentelemetry = require('@opentelemetry/api');
13701352

1371-
const myMeter = opentelemetry.metrics.getMeter('my-service-meter');
1353+
const myMeter = opentelemetry.metrics.getMeter(
1354+
'instrumentation-scope-name',
1355+
'instrumentation-scope-version',
1356+
);
13721357

13731358
// You can now use a 'meter' to create instruments!
13741359
```
13751360

13761361
{{% /tab %}} {{< /tabpane >}}
13771362

1363+
The values of `instrumentation-scope-name` and `instrumentation-scope-version`
1364+
should uniquely identify the
1365+
[Instrumentation Scope](/docs/concepts/instrumentation-scope/), such as the
1366+
package, module or class name. While the name is required, the version is still
1367+
recommended despite being optional.
1368+
13781369
It’s generally recommended to call `getMeter` in your app when you need it
13791370
rather than exporting the meter instance to the rest of your app. This helps
13801371
avoid trickier application load issues when other required dependencies are
13811372
involved.
13821373

1383-
### Synchronous and asynchronous instruments
1374+
In the case of the [example app](#example-app), there are two places where a
1375+
tracer may be acquired with an appropriate Instrumentation Scope:
13841376

1385-
OpenTelemetry instruments are either synchronous or asynchronous (observable).
1377+
First, in the _application file_ `app.ts` (or `app.js`):
13861378

1387-
Synchronous instruments take a measurement when they are called. The measurement
1388-
is done as another call during program execution, just like any other function
1389-
call. Periodically, the aggregation of these measurements is exported by a
1390-
configured exporter. Because measurements are decoupled from exporting values,
1391-
an export cycle may contain zero or multiple aggregated measurements.
1379+
{{< tabpane text=true >}} {{% tab TypeScript %}}
13921380

1393-
Asynchronous instruments, on the other hand, provide a measurement at the
1394-
request of the SDK. When the SDK exports, a callback that was provided to the
1395-
instrument on creation is invoked. This callback provides the SDK with a
1396-
measurement that is immediately exported. All measurements on asynchronous
1397-
instruments are performed once per export cycle.
1381+
```ts
1382+
/*app.ts*/
1383+
import { metrics, trace } from '@opentelemetry/api';
1384+
import express, { Express } from 'express';
1385+
import { rollTheDice } from './dice';
13981386

1399-
Asynchronous instruments are useful in several circumstances, such as:
1387+
const tracer = trace.getTracer('dice-server', '0.1.0');
1388+
const meter = metrics.getMeter('dice-server', '0.1.0');
14001389

1401-
- When updating a counter is not computationally cheap, and you don't want the
1402-
current executing thread to wait for the measurement
1403-
- Observations need to happen at frequencies unrelated to program execution
1404-
(i.e., they cannot be accurately measured when tied to a request lifecycle)
1405-
- There is no known timestamp for a measurement value
1390+
const PORT: number = parseInt(process.env.PORT || '8080');
1391+
const app: Express = express();
14061392

1407-
In cases like these, it's often better to observe a cumulative value directly,
1408-
rather than aggregate a series of deltas in post-processing (the synchronous
1409-
example). Take note of the use of `observe` rather than `add` in the appropriate
1410-
code examples below.
1393+
app.get('/rolldice', (req, res) => {
1394+
const rolls = req.query.rolls ? parseInt(req.query.rolls.toString()) : NaN;
1395+
if (isNaN(rolls)) {
1396+
res
1397+
.status(400)
1398+
.send("Request parameter 'rolls' is missing or not a number.");
1399+
return;
1400+
}
1401+
res.send(JSON.stringify(rollTheDice(rolls, 1, 6)));
1402+
});
14111403

1412-
### Using Counters
1404+
app.listen(PORT, () => {
1405+
console.log(`Listening for requests on http://localhost:${PORT}`);
1406+
});
1407+
```
14131408

1414-
Counters can be used to measure a non-negative, increasing value.
1409+
{{% /tab %}} {{% tab JavaScript %}}
14151410

14161411
```js
1417-
const counter = myMeter.createCounter('events.counter');
1412+
/*app.js*/
1413+
const { trace, metrics } = require('@opentelemetry/api');
1414+
const express = require('express');
1415+
const { rollTheDice } = require('./dice.js');
14181416

1419-
//...
1417+
const tracer = trace.getTracer('dice-server', '0.1.0');
1418+
const meter = metrics.getMeter('dice-server', '0.1.0');
14201419

1421-
counter.add(1);
1420+
const PORT = parseInt(process.env.PORT || '8080');
1421+
const app = express();
1422+
1423+
app.get('/rolldice', (req, res) => {
1424+
const rolls = req.query.rolls ? parseInt(req.query.rolls.toString()) : NaN;
1425+
if (isNaN(rolls)) {
1426+
res
1427+
.status(400)
1428+
.send("Request parameter 'rolls' is missing or not a number.");
1429+
return;
1430+
}
1431+
res.send(JSON.stringify(rollTheDice(rolls, 1, 6)));
1432+
});
1433+
1434+
app.listen(PORT, () => {
1435+
console.log(`Listening for requests on http://localhost:${PORT}`);
1436+
});
1437+
```
1438+
1439+
{{% /tab %}} {{< /tabpane >}}
1440+
1441+
And second, in the _library file_ `dice.ts` (or `dice.js`):
1442+
1443+
{{< tabpane text=true >}} {{% tab TypeScript %}}
1444+
1445+
```ts
1446+
/*dice.ts*/
1447+
import { trace, metrics } from '@opentelemetry/api';
1448+
1449+
const tracer = trace.getTracer('dice-lib');
1450+
const meter = metrics.getMeter('dice-lib');
1451+
1452+
function rollOnce(min: number, max: number) {
1453+
return Math.floor(Math.random() * (max - min) + min);
1454+
}
1455+
1456+
export function rollTheDice(rolls: number, min: number, max: number) {
1457+
const result: number[] = [];
1458+
for (let i = 0; i < rolls; i++) {
1459+
result.push(rollOnce(min, max));
1460+
}
1461+
return result;
1462+
}
14221463
```
14231464

1465+
{{% /tab %}} {{% tab JavaScript %}}
1466+
1467+
```js
1468+
/*dice.js*/
1469+
const { trace, metrics } = require('@opentelemetry/api');
1470+
1471+
const tracer = trace.getTracer('dice-lib');
1472+
const meter = metrics.getMeter('dice-lib');
1473+
1474+
function rollOnce(min, max) {
1475+
return Math.floor(Math.random() * (max - min) + min);
1476+
}
1477+
1478+
function rollTheDice(rolls, min, max) {
1479+
const result = [];
1480+
for (let i = 0; i < rolls; i++) {
1481+
result.push(rollOnce(min, max));
1482+
}
1483+
return result;
1484+
}
1485+
1486+
module.exports = { rollTheDice };
1487+
```
1488+
1489+
{{% /tab %}} {{< /tabpane >}}
1490+
1491+
Now that you have [meters](/docs/concepts/signals/metrics/#meter) initialized.
1492+
you can create
1493+
[metric instruments](/docs/concepts/signals/metrics/#metric-instruments).
1494+
1495+
### Create counters
1496+
1497+
Counters can be used to measure a non-negative, increasing value.
1498+
1499+
In the case of our [example app](#example-app) we can use this to count how
1500+
often the dice has been rolled:
1501+
1502+
{{< tabpane text=true >}} {{% tab TypeScript %}}
1503+
1504+
```ts
1505+
/*dice.ts*/
1506+
const counter = meter.createCounter('dice-lib.rolls.counter');
1507+
1508+
function rollOnce(min: number, max: number) {
1509+
counter.add(1);
1510+
return Math.floor(Math.random() * (max - min) + min);
1511+
}
1512+
```
1513+
1514+
{{% /tab %}} {{% tab JavaScript %}}
1515+
1516+
```js
1517+
/*dice.js*/
1518+
const counter = meter.createCounter('dice-lib.rolls.counter');
1519+
1520+
function rollOnce(min, max) {
1521+
counter.add(1);
1522+
return Math.floor(Math.random() * (max - min) + min);
1523+
}
1524+
```
1525+
1526+
{{% /tab %}} {{< /tabpane >}}
1527+
14241528
### Using UpDown Counters
14251529

14261530
UpDown counters can increment and decrement, allowing you to observe a

0 commit comments

Comments
 (0)