From abab07732bae8996c99b4023e2d556117c93e32d Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 20 Oct 2022 17:06:28 +0100 Subject: [PATCH 01/16] docs: add documentation about using Metrics API/SDK --- doc/metrics.md | 215 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 doc/metrics.md diff --git a/doc/metrics.md b/doc/metrics.md new file mode 100644 index 0000000000..4ca78c6ac6 --- /dev/null +++ b/doc/metrics.md @@ -0,0 +1,215 @@ +# Metrics + +This quick start is for end users of OpenTelemetry who wish to manually measure their applications. If you are a library author, please see the [Library Authors Guide](library-author.md). If you wish to automatically instrument your application, see the automatic instrumentation documentation for the SDK you wish to use. + +For a high-level overview of OpenTelemetry metrics in general and definitions of some common terms, you can refer to the [OpenTelemetry Specification Overview][spec-overview] + +_Metrics API Specification: _ + +_Metrics API Reference: _ + +- [Acquiring a Meter](#acquiring-a-meter) +- [Starting and Ending a Span](#starting-and-ending-a-span) +- [Describing a Span](#describing-a-span) + - [Span Relationships](#span-relationships) + - [Span Attributes](#span-attributes) + - [Span Kind](#span-kind) + - [Client](#client) + - [Server](#server) + - [Internal](#internal) + - [Producer](#producer) + - [Consumer](#consumer) + - [Semantic Conventions](#semantic-conventions) + +## Acquiring a Meter + +In OpenTelemetry, Metrics measurement operations are performed using methods on a _meter_. You can get a meter by calling [`getMeter`](https://open-telemetry.github.io/opentelemetry-js-api/classes/metricsapi.html#getmetrics) on the global meter provider. `getMeter` takes the name and version of the application or library acquiring the meter, and provides a meter which can be used to create instruments. + +```typescript +import { metrics } from '@opentelemetry/api-metrics'; + +const meter = metrics.getMeter("my-application", "0.1.0"); +``` + +## Create a metric instrument + +In OpenTelemetry, all _metrics_ are composed of [`Instruments`](https://open-telemetry.github.io/opentelemetry-js-api/interfaces/instrument.html). A instrument is responsible for reporting measurements, +there are four types of instruments that can be created: + + - Counter, a synchronous instrument which supports non-negative increments + - Asynchronous Counter, a asynchronous instrument which supports non-negative increments + - Histogram,a synchronous instrument which supports arbitrary values that are statistically meaningful, such as histograms, summaries or percentile + - Asynchronous Gauge, asynchronous instrument which supports non-additive values, such as room temperature + - UpDownCounter, a synchronous instrument which supports increments and decrements, such as number of active requests + - Asynchronous UpDownCounter, a asynchronous instrument which supports increments and decrements + +You can create a Counter instrument by calling [`Meter#createCounter`](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api_metrics.Meter.html#createCounter). The only required argument to `createCounter` is the _instrument name_, which should describe the item that is being measurement. + +```typescript +const counter = meter.createCounter("events.counter"); + +// increase the counter +counter.add(1); + +``` + +Most of the time, instruments will be used to measure operations in your application. The following example shows what it might look like to manually measure duration a function. + +```typescript +async function myTask() { + const histogram = meter.createHistogram("taks.duration"); + const startTime = new Date().getTime() + try { + // Wait for five seconds bore continueing code execution + await setTimeout(5_000) + } catch (err) { + } finally { + const endTime = new Date().getTime() + const executionTime = endTime - startTime + + // Record the duration of the task operation + histogram.record(executionTime) + } +} + +await myTask() +``` + +## Describing a instrument measurement + +Using attributes, kind, and the related [semantic conventions](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/metrics/semantic_conventions), we can more accurately describe the measurement in a way our metrics backend will more easily understand. The following example uses these mechanisms, which are described below. + +```typescript +import { NetTransportValues SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { trace, context, SpanKind, SpanStatusCode } from '@opentelemetry/api'; + +async function onGet(request, response) { + // HTTP semantic conventions determine the span name and attributes for this span + const span = tracer.startSpan(`GET /user/:id`, { + // attributes can be added when the span is started + attributes: { + // Attributes from the HTTP trace semantic conventions + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md + [SemanticAttributes.HTTP_METHOD]: "GET", + [SemanticAttributes.HTTP_FLAVOR]: "1.1", + [SemanticAttributes.HTTP_URL]: request.url, + [SemanticAttributes.NET_PEER_IP]: "192.0.2.5", + }, + // This span represents a remote incoming synchronous request + kind: SpanKind.SERVER + }); + + const userId = request.params.id; + + // Create a new context from the current context which has the span "active" + const ctx = trace.setSpan(context.active(), span); + + // Call getUser with the newly created context + // + // context.with calls a function with an associated "active" context. Within + // the function, calling context.active() returns the currently active context. + // If there is no active context, the ROOT_CONTEXT will be returned, which + // has no key-value pairs. + // + // context.with requires at least 2 arguments: a context and a function to be called. + // If a third argument is provided, it will be bound to `this` `this` inside the function. + // Any additional parameters are used as arguments when calling the function. + // + // Return value is the value returned from getUser + // | Context to be used as the "active" context + // | | Function to be called + // | | | Object assigned to this during function execution + // | | | | Passed as the first argument to getUser + // | | | | | + // V V V V V + const user = await context.with(ctx, getUser, undefined, userId); + + // Attributes may also be added after the span is started. + // http.status_code is required by the HTTP trace semantic conventions + span.setAttribute("http.status_code", 200); + + response.send(user.toJson()); + span.setStatus({ + code: SpanStatusCode.OK, + }); + span.end(); + + // Attributes MAY NOT be added after the span ends + span.setAttribute("my.attribute", false); // this does nothing +} + +async function getUser(userId) { + // when this span is created, it will automatically use the span from the context as its parent + const span = tracer.startSpan("SELECT ShopDb.Users", { + attributes: { + // Attributes from the database trace semantic conventions + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md + [SemanticAttributes.DB_SYSTEM]: "mysql", + [SemanticAttributes.DB_CONNECTION_STRING]: "Server=shopdb.example.com;Database=ShopDb;Uid=billing_user;TableCache=true;UseCompression=True;MinimumPoolSize=10;MaximumPoolSize=50;", + [SemanticAttributes.DB_USER]: "app_user", + [SemanticAttributes.NET_PEER_NAME]: "shopdb.example.com", + [SemanticAttributes.NET_PEER_IP]: "192.0.2.12", + [SemanticAttributes.NET_PEER_PORT]: 3306, + [SemanticAttributes.NET_TRANSPORT]: NetTransportValues.IP_TCP, + [SemanticAttributes.DB_NAME]: "ShopDb", + [SemanticAttributes.DB_STATEMENT]: `Select * from Users WHERE user_id = ${userId}`, + [SemanticAttributes.DB_OPERATION]: "SELECT", + [SemanticAttributes.DB_SQL_TABLE]: "Users", + }, + kind: SpanKind.CLIENT, + }); + const user = await db.select("Users", { id: userId }); + + span.setStatus({ + code: SpanStatusCode.OK, + }); + span.end(); + return user; +} + +server.on("GET", "/user/:id", onGet); +``` + +### Span Relationships + +One of the most important aspects of spans is their relationships to each other. For instance, if one span describes an incoming request which makes a database call, it is recommended to trace the database call as a separate span which is a child of the original request span. In order to do this, when we create a span we can tell OpenTelemetry which span to use as its parent using a mechanism called _Context_. + +Context is a very important part of the OpenTelemetry API which cannot be adequately explained in a single paragraph. To read more about context, see the [context documentation](context.md). + +### Span Attributes + +While name, start time, end time, and status are the minimum information required to trace an operation, most of the time they will not be enough information on their own to effectively observe an application. To solve this, OpenTelemetry uses _Span Attributes_. Span attributes are an object with string keys and string, number, or boolean values which describe the span. For example, we can use the span attributes to add route and http response code information to the example above. + +### Span Kind + +When a span is created, it is one of `Client`, `Server`, `Internal`, `Producer`, or `Consumer`. This span kind provides a hint to the tracing backend as to how the trace should be assembled. According to the OpenTelemetry specification, the parent of a server span is always a client span, and the child of a client span is always a server span. Similarly, the parent of a consumer span is always a producer and the child of a producer span is always a consumer. If not provided, the span kind is assumed to be internal. + +For more information regarding SpanKind, see . + +#### Client + +Client spans represent a synchronous outgoing remote call such as an outgoing HTTP request or database call. Note that in this context, "synchronous" does not refer to `async/await`, but to the fact that it is not queued for later processing. + +#### Server + +Server spans represent a synchronous incoming remote call such as an incoming HTTP request or remote procedure call. + +#### Internal + +Internal spans represent operations which do not cross a process boundary. Things like instrumenting a function call or an express middleware may use internal spans. + +#### Producer + +Producer spans represent the creation of a job which may be asynchronously processed later. It may be a remote job such as one inserted into a job queue or a local job handled by an event listener. + +#### Consumer + +Consumer spans represent the processing of a job created by a producer and may start long after the producer span has already ended. + +### Semantic Conventions + +One problem with span names and attributes is recognizing, categorizing, and analyzing them in your tracing backend. Between different applications, libraries, and tracing backends there might be different names and expected values for various attributes. For example, your application may use `http.status` to describe the HTTP status code, but a library you use may use `http.status_code`. In order to solve this problem, OpenTelemetry uses a library of semantic conventions which describe the name and attributes which should be used for specific types of spans. The use of semantic conventions is always recommended where applicable, but they are merely conventions. For example, you may find that some name other than the name suggested by the semantic conventions more accurately describes your span, you may decide not to include a span attribute which is suggested by semantic conventions for privacy reasons, or you may wish to add a custom attribute which isn't covered by semantic conventions. All of these cases are fine, but please keep in mind that if you stray from the semantic conventions, the categorization of spans in your tracing backend may be affected. + +_See the current trace semantic conventions in the OpenTelemetry Specification repository: _ + +[spec-overview]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md From a5806eed5ecd24d67dc1fc280af01b2479205de3 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Fri, 21 Oct 2022 10:04:06 +0100 Subject: [PATCH 02/16] docs: update the docs --- doc/metrics.md | 211 +++++++++++++++++++++---------------------------- 1 file changed, 91 insertions(+), 120 deletions(-) diff --git a/doc/metrics.md b/doc/metrics.md index 4ca78c6ac6..09226abbce 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -9,16 +9,9 @@ _Metrics API Specification: _ - [Acquiring a Meter](#acquiring-a-meter) -- [Starting and Ending a Span](#starting-and-ending-a-span) -- [Describing a Span](#describing-a-span) - - [Span Relationships](#span-relationships) - - [Span Attributes](#span-attributes) - - [Span Kind](#span-kind) - - [Client](#client) - - [Server](#server) - - [Internal](#internal) - - [Producer](#producer) - - [Consumer](#consumer) +- [Create a metric instrument](#create-a-metric-instrument) +- [Describing a instrument measurement](#describing-a-instrument-measurement) + - [Metric Attributes](#metric-attributes) - [Semantic Conventions](#semantic-conventions) ## Acquiring a Meter @@ -77,139 +70,117 @@ await myTask() ## Describing a instrument measurement -Using attributes, kind, and the related [semantic conventions](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/metrics/semantic_conventions), we can more accurately describe the measurement in a way our metrics backend will more easily understand. The following example uses these mechanisms, which are described below. +Using attributes, kind, and the related [semantic conventions](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/metrics/semantic_conventions), we can more accurately describe the measurement in a way our metrics backend will more easily understand. The following example uses these mechanisms, which are described below, for recording a measurement +of a HTTP request. -```typescript -import { NetTransportValues SemanticAttributes } from '@opentelemetry/semantic-conventions'; -import { trace, context, SpanKind, SpanStatusCode } from '@opentelemetry/api'; - -async function onGet(request, response) { - // HTTP semantic conventions determine the span name and attributes for this span - const span = tracer.startSpan(`GET /user/:id`, { - // attributes can be added when the span is started - attributes: { - // Attributes from the HTTP trace semantic conventions - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md - [SemanticAttributes.HTTP_METHOD]: "GET", - [SemanticAttributes.HTTP_FLAVOR]: "1.1", - [SemanticAttributes.HTTP_URL]: request.url, - [SemanticAttributes.NET_PEER_IP]: "192.0.2.5", - }, - // This span represents a remote incoming synchronous request - kind: SpanKind.SERVER - }); - - const userId = request.params.id; - - // Create a new context from the current context which has the span "active" - const ctx = trace.setSpan(context.active(), span); - - // Call getUser with the newly created context - // - // context.with calls a function with an associated "active" context. Within - // the function, calling context.active() returns the currently active context. - // If there is no active context, the ROOT_CONTEXT will be returned, which - // has no key-value pairs. - // - // context.with requires at least 2 arguments: a context and a function to be called. - // If a third argument is provided, it will be bound to `this` `this` inside the function. - // Any additional parameters are used as arguments when calling the function. - // - // Return value is the value returned from getUser - // | Context to be used as the "active" context - // | | Function to be called - // | | | Object assigned to this during function execution - // | | | | Passed as the first argument to getUser - // | | | | | - // V V V V V - const user = await context.with(ctx, getUser, undefined, userId); - - // Attributes may also be added after the span is started. - // http.status_code is required by the HTTP trace semantic conventions - span.setAttribute("http.status_code", 200); - - response.send(user.toJson()); - span.setStatus({ - code: SpanStatusCode.OK, - }); - span.end(); +Each metric instruments allows to associate a description, unit of measure, and the value type. +The description of a metric instrument can expose up in the metrics backend, the unit or value type +can be used to information about the record measurement itself. - // Attributes MAY NOT be added after the span ends - span.setAttribute("my.attribute", false); // this does nothing -} - -async function getUser(userId) { - // when this span is created, it will automatically use the span from the context as its parent - const span = tracer.startSpan("SELECT ShopDb.Users", { - attributes: { - // Attributes from the database trace semantic conventions - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md - [SemanticAttributes.DB_SYSTEM]: "mysql", - [SemanticAttributes.DB_CONNECTION_STRING]: "Server=shopdb.example.com;Database=ShopDb;Uid=billing_user;TableCache=true;UseCompression=True;MinimumPoolSize=10;MaximumPoolSize=50;", - [SemanticAttributes.DB_USER]: "app_user", - [SemanticAttributes.NET_PEER_NAME]: "shopdb.example.com", - [SemanticAttributes.NET_PEER_IP]: "192.0.2.12", - [SemanticAttributes.NET_PEER_PORT]: 3306, - [SemanticAttributes.NET_TRANSPORT]: NetTransportValues.IP_TCP, - [SemanticAttributes.DB_NAME]: "ShopDb", - [SemanticAttributes.DB_STATEMENT]: `Select * from Users WHERE user_id = ${userId}`, - [SemanticAttributes.DB_OPERATION]: "SELECT", - [SemanticAttributes.DB_SQL_TABLE]: "Users", - }, - kind: SpanKind.CLIENT, +```typescript +async function myTask() { + const httpServerDuration = meter.createHistogram("http.server.duration", { + description: 'A http server duration', + unit: 'milliseconds', + valueType: ValueType.INT }); - const user = await db.select("Users", { id: userId }); + const startTime = new Date().getTime() + try { + // Wait for five seconds bore continueing code execution + await setTimeout(5_000) + } catch (err) { + } finally { + const endTime = new Date().getTime() + const executionTime = endTime - startTime - span.setStatus({ - code: SpanStatusCode.OK, - }); - span.end(); - return user; + httpServerDuration.record(executionTime, { + [SemanticAttributes.HTTP_METHOD]: 'POST', + [SemanticAttributes.HTTP_STATUS_CODE]: '200', + [SemanticAttributes.HTTP_SCHEME]: 'https', + }) + } } -server.on("GET", "/user/:id", onGet); +await myTask() ``` -### Span Relationships - -One of the most important aspects of spans is their relationships to each other. For instance, if one span describes an incoming request which makes a database call, it is recommended to trace the database call as a separate span which is a child of the original request span. In order to do this, when we create a span we can tell OpenTelemetry which span to use as its parent using a mechanism called _Context_. - -Context is a very important part of the OpenTelemetry API which cannot be adequately explained in a single paragraph. To read more about context, see the [context documentation](context.md). +In the above example we are recording a measurement of roughly 5000ms and associates +three metric attributes with this measurement. Metrics backends can show these metric +attributes. In Prometheus the metric attributes would become labels and can be used +as part of queries, and allow search queries, such as what's the 90% percentile of +all successful POST requests. -### Span Attributes +### Metric Attributes -While name, start time, end time, and status are the minimum information required to trace an operation, most of the time they will not be enough information on their own to effectively observe an application. To solve this, OpenTelemetry uses _Span Attributes_. Span attributes are an object with string keys and string, number, or boolean values which describe the span. For example, we can use the span attributes to add route and http response code information to the example above. +While name and measurement are the minimum required to record a metric measurement, +most of the time they will not be enough information on their own to effectively observer +an application. To solve this, OpenTelemetry uses _Metric Attributes_. Metric attributes are object with +string keys and string values which add more context to the measurement. -### Span Kind +For example, when you are measuring the number of inflight requests, you might want to be able to count +the number of POST, or GET requests. You can add the a metric attribute for `http.method` to allow more +flexibility when leveraging your metric measurement like in Grafana dashboards. -When a span is created, it is one of `Client`, `Server`, `Internal`, `Producer`, or `Consumer`. This span kind provides a hint to the tracing backend as to how the trace should be assembled. According to the OpenTelemetry specification, the parent of a server span is always a client span, and the child of a client span is always a server span. Similarly, the parent of a consumer span is always a producer and the child of a producer span is always a consumer. If not provided, the span kind is assumed to be internal. - -For more information regarding SpanKind, see . +### Semantic Conventions -#### Client +One problem with metrics names and attributes is recognizing, categorizing, and analyzing them in your metrics backend. Between different applications, libraries, and metrics backends there might be different names and expected values for various attributes. For example, your application may use `http.status` to describe the HTTP status code, but a library you use may use `http.status_code`. In order to solve this problem, OpenTelemetry uses a library of semantic conventions which describe the name and attributes which should be used for specific types of metrics. -Client spans represent a synchronous outgoing remote call such as an outgoing HTTP request or database call. Note that in this context, "synchronous" does not refer to `async/await`, but to the fact that it is not queued for later processing. +The use of semantic conventions is always recommended where applicable, but they are merely conventions. For example, you may find that some name other than the name suggested by the semantic conventions more accurately describes your metric, you may decide not to include a metric attribute which is suggested by semantic conventions for privacy reasons, or you may wish to add a custom attribute which isn't covered by semantic conventions. All of these cases are fine, but please keep in mind that if you stray from the semantic conventions, the categorization of metrics in your metrics backend may be affected. -#### Server +_See the current metrics semantic conventions in the OpenTelemetry Specification repository: _ -Server spans represent a synchronous incoming remote call such as an incoming HTTP request or remote procedure call. +[spec-overview]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md -#### Internal +### Configuring metric views -Internal spans represent operations which do not cross a process boundary. Things like instrumenting a function call or an express middleware may use internal spans. +TODO -#### Producer +### Histogram instrument -Producer spans represent the creation of a job which may be asynchronously processed later. It may be a remote job such as one inserted into a job queue or a local job handled by an event listener. +The Histogram metric instruments requires you to define a collection of buckets were +each of the recording measurements fall in. The buckets can not be defined when the +histogram metric gets created but need to configured via the Views which were discussed +in the previous section. -#### Consumer +Below an example is given how you can define explicit buckets for a histogram. -Consumer spans represent the processing of a job created by a producer and may start long after the producer span has already ended. +```typescript +// Define view for the histogram metric +const histogramView = new View({ + aggregation: new ExplicitBucketHistogramAggregation([0, 1, 5, 10, 15, 20, 25, 30]), + instrumentName: 'http.server.duration', + instrumentType: InstrumentType.HISTOGRAM, +}); + +// Note, the instrumentName is the same as the name that has been passed for +// the Meter#createHistogram function + +// Create an instance of the metric provider +const meterProvider = new MeterProvider({ + views: [ + histogramView + ] +}); + +// Create histogram metric +const httpServerDuration = meter.createHistogram("http.server.duration", { + description: 'A http server duration', + unit: 'milliseconds', + valueType: ValueType.INT +}); + +// Record measurement for histogram +httpServerDuration.record(50, { + [SemanticAttributes.HTTP_METHOD]: 'POST', + [SemanticAttributes.HTTP_STATUS_CODE]: '200', + [SemanticAttributes.HTTP_SCHEME]: 'https', +}); +``` -### Semantic Conventions +## Exporting measurements to Prometheus -One problem with span names and attributes is recognizing, categorizing, and analyzing them in your tracing backend. Between different applications, libraries, and tracing backends there might be different names and expected values for various attributes. For example, your application may use `http.status` to describe the HTTP status code, but a library you use may use `http.status_code`. In order to solve this problem, OpenTelemetry uses a library of semantic conventions which describe the name and attributes which should be used for specific types of spans. The use of semantic conventions is always recommended where applicable, but they are merely conventions. For example, you may find that some name other than the name suggested by the semantic conventions more accurately describes your span, you may decide not to include a span attribute which is suggested by semantic conventions for privacy reasons, or you may wish to add a custom attribute which isn't covered by semantic conventions. All of these cases are fine, but please keep in mind that if you stray from the semantic conventions, the categorization of spans in your tracing backend may be affected. +TODO -_See the current trace semantic conventions in the OpenTelemetry Specification repository: _ +## Exporting measurements to Opentelemetry Protocol -[spec-overview]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md +TODO From 8c62a8fff61d21b876460d44efa6b62d193f2979 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 14:22:18 +0100 Subject: [PATCH 03/16] docs: add documentation about Metric Views and exporters --- doc/metrics.md | 176 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 168 insertions(+), 8 deletions(-) diff --git a/doc/metrics.md b/doc/metrics.md index 09226abbce..58e4d5d1ae 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -133,14 +133,48 @@ _See the current metrics semantic conventions in the OpenTelemetry Specification ### Configuring metric views -TODO +A [Metric View](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view) provides the ability to customise the metrics that are exposed by the +Metrics SDK. Metric Views allows you to do: + +- Customise which Metric Attributes are reported on metrics +- Customise which instruments get processed/ignored +- Change the aggregration of a metric +- Define explicit bucket sizes to Histogram instruments + +The Metric view requires the instrument selection query, and the configuration +for the resulting metric. The first step is select to the metrics to whom the View +is relevant, the second step is to configure the customisations for the the selected +metrics. + +A Metric View is a class that can be instantiated via: +````typescript +const view = new View({ + name: 'metric-view', // optionally, give the view a unique name + // select instruments with a specific name + instrumentName: 'http.server.duration', +}); +```` + +In the above example a View is created which select instruments with the name `http.server.duration` other options to select instruments are: + + - By Instrument Type: use `instrumentType` to select instruments of the given type + - By Meter: use `meterName` to select meters with the given name + - By Meter Version: use `meterVersion` to select meters with the given version + - By Meter Schema URL: use `meterSchemaUrl` to select meters with given schema url + +The `instrumentName` has support for wildcards, so you can select all instruments +using `*` or select all instruments starting with 'http.' by using `http.*`. + +*Note*: The Views can only be defined when the `MeterProvider` instance gets +instantiated. A proposal is submitted to ease the ability to define Metris Views: +https://github.com/open-telemetry/oteps/issues/209 -### Histogram instrument +### Configuring explicit bucket sizes for the Histogram instrument -The Histogram metric instruments requires you to define a collection of buckets were -each of the recording measurements fall in. The buckets can not be defined when the -histogram metric gets created but need to configured via the Views which were discussed -in the previous section. +The Histogram instruments has default set of bucket sizes defined which not might +not all suit your needs. The bucket sizes can be overriden by configuring a different +aggregration for the Histogram instrument. The `ExplicitBucketHistogramAggregation` +should be used to define the bucket sizes for the Histogram instrument. Below an example is given how you can define explicit buckets for a histogram. @@ -177,10 +211,136 @@ httpServerDuration.record(50, { }); ``` +### Dropping instrument from being exported + +In some circumstances you don't want specific metrics to be exported by Opentelemetry, +for example, you might be using custom instrumentation or third-party packages that +define their own metrics you are not interested in. + +In such cases you can define a customer view which drop the instruments you are +not interesting in, for example, you can drop instruments of a specific meter or +instruments with a specific name: + +The following view drops all instruments that are associated with a meter with +the name `pubsub`: + +```typescript +const dropView = new View({ + aggregation: new DropAggregation(), + meterName: 'pubsub', +}); +``` + +Alternatively, you can also drop instruments with a specific instrument name, +for example, all instruments of which the name starts with `http`: + +```typescript +const dropView = new View({ + aggregation: new DropAggregation(), + instrumentName: 'htpp*', +}); +``` + +### Customising the metric attributes of instrument + +If you want to limit the Metric Attributes that get exported in measurements of +an instrument, you can create a Metric View which defines which attributes should +be exported. Attributes that are missing in the list will not be exported. + +In the example below will drop all attributes except attribute `environment` for +all instruments. + +```typescript +new View({ + // only export the attribute 'environment' + attributeKeys: ['environment'], + // apply the view to all instruments + instrumentName: '*', +}) +``` + +### + +# Exporting measurements + +After you have instrumented your application with metrics, you also need to make +sure that the metrics get collected by your metrics backend. The most common formats +that are used are Prometheus and OLTP. + +The latter is the [Opentelemetry protocol format](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md) +which is supported by the Opentelemetry Collector. The former is based on the [OpenMetrics +format](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md) can be consumed by Prometheus and Thanos or other OpenMetrics compatible +backends. + +*Note*: Both Opentelemetry Javascript and Opentelemetry Collector support +exporters for different formats, such as [Cloud Monitoring](https://github.com/GoogleCloudPlatform/opentelemetry-operations-js/tree/master/packages/opentelemetry-cloud-monitoring-exporter). + ## Exporting measurements to Prometheus -TODO +If you want to export your metrics to Prometheus you need to initialise Opentelemetry +to use the Prometheus exporter `PrometheusExporter` which is included in the +`@opentelemetry/exporter-prometheus`-package. + +```typescript +const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); +const { MeterProvider } = require('@opentelemetry/sdk-metrics'); + +// Add your port and startServer to the Prometheus options +const options = { port: 9464, startServer: true }; +const exporter = new PrometheusExporter(options); + +// Creates MeterProvider and installs the exporter as a MetricReader +const meterProvider = new MeterProvider(); +meterProvider.addMetricReader(exporter); +const meter = meterProvider.getMeter('example-prometheus'); + +// Now, start recording data +const counter = meter.createCounter('metric_name', { + description: 'Example of a counter' +}); +counter.add(10, { pid: process.pid }); +``` + +In the above example the instantiated `PrometheusExporter` is configured to expose +a new http server on port 9464. You can now access the metrics at the endpoint +http://localhost:9464/metrics. This is the url that can be scraped by Prometheus so it can consumed the metrics collected by Opentelemetry in your application. + +More information about Prometheus and how to configure can be found at: +[https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config](Prometheus Scraping Config) + +For a fully functioning code example for using this exporter, please have a look +at: https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/examples/prometheus ## Exporting measurements to Opentelemetry Protocol -TODO +Opentelemetry Javascript comes with three different kind of exporters that export +the collected metrics in the Opentelemetry Protocol (OTLP). The three exports +different in the transport method to send the metrics to a backend that supports +the OTLP protocol, (a) [https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-metrics-otlp-http](over HTTP) (b) [https://www.npmjs.com/package/@opentelemetry/exporter-metrics-otlp-grpc](over GRPC) (c) [https://www.npmjs.com/package/@opentelemetry/exporter-metrics-otlp-proto](over Protofbuf). + +In the example below shows how you can configure Opentelemetry Javascript to use +the OTLP exporter over HTTP. + +```typescript +const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); +const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http'); +const collectorOptions = { + url: '', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics + concurrencyLimit: 1, // an optional limit on pending requests +}; +const exporter = new OTLPMetricExporter(collectorOptions); +const meterProvider = new MeterProvider({}); + +meterProvider.addMetricReader(new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, +})); + +// Now, start recording data +const meter = meterProvider.getMeter('example-meter'); +const counter = meter.createCounter('metric_name'); +counter.add(10, { 'key': 'value' }); +``` + +For a fully functioning code example for using this exporter, please have a look +at: https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/otlp-exporter-node \ No newline at end of file From b3f392cf4328178b5d74fef10495f8ed39c0be0b Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 14:26:04 +0100 Subject: [PATCH 04/16] docs: improve metrics docs --- doc/metrics.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/metrics.md b/doc/metrics.md index 58e4d5d1ae..53fbbd8b7f 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -13,7 +13,13 @@ _Metrics API Reference: Date: Mon, 24 Oct 2022 14:34:53 +0100 Subject: [PATCH 05/16] docs: attempt to fix linting issues --- doc/metrics.md | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/doc/metrics.md b/doc/metrics.md index 53fbbd8b7f..4edf31216b 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -20,6 +20,7 @@ _Metrics API Reference: ### Configuring explicit bucket sizes for the Histogram instrument The Histogram instruments has default set of bucket sizes defined which not might not all suit your needs. The bucket sizes can be overriden by configuring a different -aggregration for the Histogram instrument. The `ExplicitBucketHistogramAggregation` +aggregration for the Histogram instrument. The `ExplicitBucketHistogramAggregation` should be used to define the bucket sizes for the Histogram instrument. Below an example is given how you can define explicit buckets for a histogram. @@ -237,7 +239,7 @@ const dropView = new View({ }); ``` -Alternatively, you can also drop instruments with a specific instrument name, +Alternatively, you can also drop instruments with a specific instrument name, for example, all instruments of which the name starts with `http`: ```typescript @@ -254,7 +256,7 @@ an instrument, you can create a Metric View which defines which attributes shoul be exported. Attributes that are missing in the list will not be exported. In the example below will drop all attributes except attribute `environment` for -all instruments. +all instruments. ```typescript new View({ @@ -265,25 +267,25 @@ new View({ }) ``` -# Exporting measurements +## Exporting measurements After you have instrumented your application with metrics, you also need to make sure that the metrics get collected by your metrics backend. The most common formats -that are used are Prometheus and OLTP. +that are used are Prometheus and OLTP. The latter is the [Opentelemetry protocol format](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md) which is supported by the Opentelemetry Collector. The former is based on the [OpenMetrics format](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md) can be consumed by Prometheus and Thanos or other OpenMetrics compatible backends. -*Note*: Both Opentelemetry Javascript and Opentelemetry Collector support +_Note_: Both Opentelemetry Javascript and Opentelemetry Collector support exporters for different formats, such as [Cloud Monitoring](https://github.com/GoogleCloudPlatform/opentelemetry-operations-js/tree/master/packages/opentelemetry-cloud-monitoring-exporter). ## Exporting measurements to Prometheus If you want to export your metrics to Prometheus you need to initialise Opentelemetry -to use the Prometheus exporter `PrometheusExporter` which is included in the -`@opentelemetry/exporter-prometheus`-package. +to use the Prometheus exporter `PrometheusExporter` which is included in the +`@opentelemetry/exporter-prometheus`-package. ```typescript const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); @@ -307,13 +309,13 @@ counter.add(10, { pid: process.pid }); In the above example the instantiated `PrometheusExporter` is configured to expose a new http server on port 9464. You can now access the metrics at the endpoint -http://localhost:9464/metrics. This is the url that can be scraped by Prometheus so it can consumed the metrics collected by Opentelemetry in your application. +. This is the url that can be scraped by Prometheus so it can consumed the metrics collected by Opentelemetry in your application. More information about Prometheus and how to configure can be found at: [https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config](Prometheus Scraping Config) For a fully functioning code example for using this exporter, please have a look -at: https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/examples/prometheus +at: ## Exporting measurements to Opentelemetry Protocol @@ -323,7 +325,7 @@ different in the transport method to send the metrics to a backend that supports the OTLP protocol, (a) [https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-metrics-otlp-http](over HTTP) (b) [https://www.npmjs.com/package/@opentelemetry/exporter-metrics-otlp-grpc](over GRPC) (c) [https://www.npmjs.com/package/@opentelemetry/exporter-metrics-otlp-proto](over Protofbuf). In the example below shows how you can configure Opentelemetry Javascript to use -the OTLP exporter over HTTP. +the OTLP exporter over HTTP. ```typescript const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); @@ -347,4 +349,4 @@ counter.add(10, { 'key': 'value' }); ``` For a fully functioning code example for using this exporter, please have a look -at: https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/otlp-exporter-node \ No newline at end of file +at: From 38136db806de80a049aeaa378807078b190230a1 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 14:38:26 +0100 Subject: [PATCH 06/16] chore: add entry to CHANGELOG.md file --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f69e2ae6e4..4cd49b70d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## Unreleased +* doc: Added Metrics documentation [#1745](https://github.com/open-telemetry/opentelemetry-js/pull/1745) @weyert + ### :boom: Breaking Change ### :rocket: (Enhancement) From eba80463bfca7cd2a1d29e225ee28ad5ee88992f Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 14:39:01 +0100 Subject: [PATCH 07/16] chore: add entry to CHANGELOG.md file --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cd49b70d8..646692cb3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,6 @@ All notable changes to this project will be documented in this file. ## Unreleased -* doc: Added Metrics documentation [#1745](https://github.com/open-telemetry/opentelemetry-js/pull/1745) @weyert - ### :boom: Breaking Change ### :rocket: (Enhancement) @@ -21,6 +19,7 @@ All notable changes to this project will be documented in this file. [#3295](https://github.com/open-telemetry/opentelemetry-js/issues/3295) ### :books: (Refine Doc) +* doc: Added Metrics documentation [#1745](https://github.com/open-telemetry/opentelemetry-js/pull/1745) @weyert ### :house: (Internal) From 82fca502a47e3786843131c7350cd9e632c1cbde Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 14:39:13 +0100 Subject: [PATCH 08/16] chore: add entry to CHANGELOG.md file --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 646692cb3c..1b534941ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file. [#3295](https://github.com/open-telemetry/opentelemetry-js/issues/3295) ### :books: (Refine Doc) + * doc: Added Metrics documentation [#1745](https://github.com/open-telemetry/opentelemetry-js/pull/1745) @weyert ### :house: (Internal) From 745dcc47af21bde6589ae1803875f73fc9348ec9 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 16:23:36 +0100 Subject: [PATCH 09/16] doc: add getting started chapter --- doc/metrics.md | 206 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/doc/metrics.md b/doc/metrics.md index 4edf31216b..1ca600f0d9 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -8,6 +8,7 @@ _Metrics API Specification: _ +- [Getting Started](#getting-started) - [Acquiring a Meter](#acquiring-a-meter) - [Create a metric instrument](#create-a-metric-instrument) - [Describing a instrument measurement](#describing-a-instrument-measurement) @@ -21,6 +22,211 @@ _Metrics API Reference: { + // Resources have been detected and SDK is started + console.log(`SDK started`) + +// Start the http server + const fastify = require('fastify')({ + logger: true + }) + + fastify.get('/', function (request, reply) { + reply.send({ hello: 'world' }) + }) + + fastify.listen({ port: 3000 }, function (err, address) { + if (err) { + fastify.log.error(err) + process.exit(1) + } + + console.log(`Server is now listening on ${address}`) + }) +}); + +// You can also use the shutdown method to gracefully shut down the SDK before process shutdown +// or on some operating system signal. +const process = require("process"); +process.on("SIGTERM", () => { + sdk + .shutdown() + .then( + () => console.log("SDK shut down successfully"), + (err) => console.log("Error shutting down SDK", err) + ) + .finally(() => process.exit(0)); +}); +``` + +In the above example we are initialising the Node SDK to enable the Metrics SDK +and configure it to export the metrics in Prometheus format by registering the +`PrometheusExporter`. + +You can now run the instrument application and it will run the HTTP server on +port 3000 with command: + +```bash +node app.js +``` + +Now when accessing the HTTP server via http://localhost:3000 you will +see the following: + +``` +{"hello":"world"} +``` + +### Add manual instrumentation + +Automatic instrumentation is powerful but it doesn't capture what's going on in +your application. For that you'll need to write some manual instrumentation. Below +we will show you how you can count thu number of times a HTTP endpoint has been +accessed. + +#### Counting number of incoming http requests + +First, modify `app.js` to include code that initializes a meter and uses it to +create a counter instrument which counts the number of times the `/` http endpoint +has been requested. + +```javascript +const api = require('@opentelemetry/api-metrics') +const opentelemetry = require("@opentelemetry/sdk-node"); +const { PrometheusExporter } = require("@opentelemetry/exporter-prometheus"); +const { + getNodeAutoInstrumentations, +} = require("@opentelemetry/auto-instrumentations-node"); + +const prometheusExporter = new PrometheusExporter({ startServer: true }); + +const sdk = new opentelemetry.NodeSDK({ + // Optional - If omitted, the metrics SDK will not be initialized + metricReader: prometheusExporter, + // Optional - you can use the metapackage or load each instrumentation individually + instrumentations: [getNodeAutoInstrumentations()], + // See the Configuration section below for additional configuration options +}); + +// You can optionally detect resources asynchronously from the environment. +// Detected resources are merged with the resources provided in the SDK configuration. +sdk.start().then(() => { + // Resources have been detected and SDK is started + console.log(`SDK started`) + + // Create Meter with the name `http-server` + const appMeter = api.metrics.getMeter('http-server') + // Use the created Meter to create a counter instrument + const numberOfRequests = appMeter.createCounter('request-counter') + + // Start the http server + const fastify = require('fastify')({ + logger: true + }) + + fastify.get('/', function (request, reply) { + // Increase the counter by 1 each time the `/` endpoint is requested + numberOfRequests.add(1) + reply.send({ hello: 'world' }) + }) + + fastify.listen({ port: 3000 }, function (err, address) { + if (err) { + fastify.log.error(err) + process.exit(1) + } + + console.log(`Server is now listening on ${address}`) + }) +}); + +// You can also use the shutdown method to gracefully shut down the SDK before process shutdown +// or on some operating system signal. +const process = require("process"); +process.on("SIGTERM", () => { + sdk + .shutdown() + .then( + () => console.log("SDK shut down successfully"), + (err) => console.log("Error shutting down SDK", err) + ) + .finally(() => process.exit(0)); +}); +``` + +Now run the application again: + +```bash +node app.js +``` + +When you navigate to http://localhost:3000, the counter instrument will be increased +each time the page is accessed. If you want to see the exporter instruments, you +can access via the dedicates metrics endpoint for Prometheus by accessing: +http://localhost:9464/metrics the contents will look similar to: + +``` +# HELP request_counter_total description missing +# TYPE request_counter_total counter +request_counter_total 6 1666624810428 +``` + +In the above example output you can that one instrument is available with the +name `request_counter_total`: + +``` +request_counter_total 6 1666624810428 +``` + +The postfixe `_total` get automatically to the instrument name for each counter insturment +when the measurements are getting exported in the Prometheus format. In the above +example you see that we access our `/` endpoint six times. + ## Acquiring a Meter In OpenTelemetry, Metrics measurement operations are performed using methods on a _meter_. You can get a meter by calling [`getMeter`](https://open-telemetry.github.io/opentelemetry-js-api/classes/metricsapi.html#getmetrics) on the global meter provider. `getMeter` takes the name and version of the application or library acquiring the meter, and provides a meter which can be used to create instruments. From 9de6e34d4d24fed86aeda51b7441738166ac1345 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 16:25:04 +0100 Subject: [PATCH 10/16] doc: fix grammar --- doc/metrics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/metrics.md b/doc/metrics.md index 1ca600f0d9..14676aacf7 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -23,7 +23,7 @@ _Metrics API Reference: Date: Mon, 24 Oct 2022 16:37:55 +0100 Subject: [PATCH 11/16] doc: update metrics.md --- doc/metrics.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/metrics.md b/doc/metrics.md index 14676aacf7..826d8348f4 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -42,7 +42,8 @@ npm install fastify @opentelemetry/sdk-node @opentelemetry/exporter-prometheus @ The `@opentelemetry/sdk-node` and `@opentelemetry/auto-instrumentations-node` will install allt he necessary packages to start with Opentelemetry including instrumentation for a wide variety of popular -packages, such as `http`, `fetch` etc. +packages, such as `http`, `fetch` etc. The package `@opentelemetry/exporter-prometheus` is installed +to export our measured metrics in the Prometheus format as popular approach to expose metrics. ### Create the sample HTTP Server Create a file `app.js`: From d7a67fcf94e6ab235b4c84af7f7684447eeadea2 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 23:03:54 +0100 Subject: [PATCH 12/16] doc: Update doc/metrics.md Co-authored-by: Daniel Dyla --- doc/metrics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/metrics.md b/doc/metrics.md index 826d8348f4..6e11161f5f 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -23,7 +23,7 @@ _Metrics API Reference: Date: Mon, 24 Oct 2022 23:04:12 +0100 Subject: [PATCH 13/16] doc: Update doc/metrics.md Co-authored-by: Daniel Dyla --- doc/metrics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/metrics.md b/doc/metrics.md index 6e11161f5f..de6487b270 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -40,7 +40,7 @@ Now install Fastify and OpenTelemetry: npm install fastify @opentelemetry/sdk-node @opentelemetry/exporter-prometheus @opentelemetry/auto-instrumentations-node ``` -The `@opentelemetry/sdk-node` and `@opentelemetry/auto-instrumentations-node` will install allt he +The `@opentelemetry/sdk-node` and `@opentelemetry/auto-instrumentations-node` will install all the necessary packages to start with Opentelemetry including instrumentation for a wide variety of popular packages, such as `http`, `fetch` etc. The package `@opentelemetry/exporter-prometheus` is installed to export our measured metrics in the Prometheus format as popular approach to expose metrics. From b2961b1afff399f147e123022835f76416a959c2 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 23:04:25 +0100 Subject: [PATCH 14/16] doc: Update doc/metrics.md Co-authored-by: Daniel Dyla --- doc/metrics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/metrics.md b/doc/metrics.md index de6487b270..8aa0023eb1 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -43,7 +43,7 @@ npm install fastify @opentelemetry/sdk-node @opentelemetry/exporter-prometheus @ The `@opentelemetry/sdk-node` and `@opentelemetry/auto-instrumentations-node` will install all the necessary packages to start with Opentelemetry including instrumentation for a wide variety of popular packages, such as `http`, `fetch` etc. The package `@opentelemetry/exporter-prometheus` is installed -to export our measured metrics in the Prometheus format as popular approach to expose metrics. +to export our collected metrics to Prometheus. ### Create the sample HTTP Server Create a file `app.js`: From 914d8a464b8b46f1433e8a36f452ce6331931f79 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 24 Oct 2022 23:08:34 +0100 Subject: [PATCH 15/16] docs: update spelling of words to American English --- doc/metrics.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/doc/metrics.md b/doc/metrics.md index 8aa0023eb1..a5f0d598ce 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -17,13 +17,13 @@ _Metrics API Reference: { }); ``` -In the above example we are initialising the Node SDK to enable the Metrics SDK +In the above example we are initializing the Node SDK to enable the Metrics SDK and configure it to export the metrics in Prometheus format by registering the `PrometheusExporter`. @@ -127,12 +127,12 @@ see the following: Automatic instrumentation is powerful but it doesn't capture what's going on in your application. For that you'll need to write some manual instrumentation. Below -we will show you how you can count thu number of times a HTTP endpoint has been +we will show you how you can count the number of times a HTTP endpoint has been accessed. #### Counting number of incoming http requests -First, modify `app.js` to include code that initializes a meter and uses it to +First, modify `app.js` to include code that initializes a meter and uses it to create a counter instrument which counts the number of times the `/` http endpoint has been requested. @@ -208,7 +208,7 @@ node app.js When you navigate to http://localhost:3000, the counter instrument will be increased each time the page is accessed. If you want to see the exporter instruments, you -can access via the dedicates metrics endpoint for Prometheus by accessing: +can access via the dedicates metrics endpoint for Prometheus by accessing: http://localhost:9464/metrics the contents will look similar to: ``` @@ -224,7 +224,7 @@ name `request_counter_total`: request_counter_total 6 1666624810428 ``` -The postfixe `_total` get automatically to the instrument name for each counter insturment +The postfixed `_total` get automatically to the instrument name for each counter instrument when the measurements are getting exported in the Prometheus format. In the above example you see that we access our `/` endpoint six times. @@ -347,17 +347,17 @@ _See the current metrics semantic conventions in the OpenTelemetry Specification ## Configuring metric views -A [Metric View](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view) provides the ability to customise the metrics that are exposed by the +A [Metric View](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view) provides the ability to customize the metrics that are exposed by the Metrics SDK. Metric Views allows you to do: -- Customise which Metric Attributes are reported on metrics -- Customise which instruments get processed/ignored -- Change the aggregration of a metric +- Customize which Metric Attributes are reported on metrics +- Customize which instruments get processed/ignored +- Change the aggregation of a metric - Define explicit bucket sizes to Histogram instruments The Metric view requires the instrument selection query, and the configuration for the resulting metric. The first step is select to the metrics to whom the View -is relevant, the second step is to configure the customisations for the the selected +is relevant, the second step is to configure the customizations for the the selected metrics. A Metric View is a class that can be instantiated via: @@ -381,14 +381,14 @@ The `instrumentName` has support for wildcards, so you can select all instrument using `*` or select all instruments starting with 'http.' by using `http.*`. _Note_: The Views can only be defined when the `MeterProvider` instance gets -instantiated. A proposal is submitted to ease the ability to define Metris Views: +instantiated. A proposal is submitted to ease the ability to define Metrics Views: ### Configuring explicit bucket sizes for the Histogram instrument The Histogram instruments has default set of bucket sizes defined which not might -not all suit your needs. The bucket sizes can be overriden by configuring a different -aggregration for the Histogram instrument. The `ExplicitBucketHistogramAggregation` +not all suit your needs. The bucket sizes can be overridden by configuring a different +aggregation for the Histogram instrument. The `ExplicitBucketHistogramAggregation` should be used to define the bucket sizes for the Histogram instrument. Below an example is given how you can define explicit buckets for a histogram. @@ -456,7 +456,7 @@ const dropView = new View({ }); ``` -### Customising the metric attributes of instrument +### Customizing the metric attributes of instrument If you want to limit the Metric Attributes that get exported in measurements of an instrument, you can create a Metric View which defines which attributes should @@ -490,7 +490,7 @@ exporters for different formats, such as [Cloud Monitoring](https://github.com/G ## Exporting measurements to Prometheus -If you want to export your metrics to Prometheus you need to initialise Opentelemetry +If you want to export your metrics to Prometheus you need to initialize Opentelemetry to use the Prometheus exporter `PrometheusExporter` which is included in the `@opentelemetry/exporter-prometheus`-package. From 1f14a8c5e137b6e66aff4e9909c157a409d16f65 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Tue, 25 Oct 2022 11:01:09 +0100 Subject: [PATCH 16/16] doc: updated Metrics doc based on pichlermarc feedback --- doc/metrics.md | 67 ++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/doc/metrics.md b/doc/metrics.md index a5f0d598ce..b2479d6a0f 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -41,14 +41,14 @@ npm install fastify @opentelemetry/sdk-node @opentelemetry/exporter-prometheus @ ``` The `@opentelemetry/sdk-node` and `@opentelemetry/auto-instrumentations-node` will install all the -necessary packages to start with Opentelemetry including instrumentation for a wide variety of popular +necessary packages to start with OpenTelemetry including instrumentation for a wide variety of popular packages, such as `http`, `fetch` etc. The package `@opentelemetry/exporter-prometheus` is installed to export our collected metrics to Prometheus. ### Create the sample HTTP Server Create a file `app.js`: -```javascript +```javaScript const api = require('@opentelemetry/api-metrics') const opentelemetry = require("@opentelemetry/sdk-node"); const { PrometheusExporter } = require("@opentelemetry/exporter-prometheus"); @@ -136,7 +136,7 @@ First, modify `app.js` to include code that initializes a meter and uses it to create a counter instrument which counts the number of times the `/` http endpoint has been requested. -```javascript +```javaScript const api = require('@opentelemetry/api-metrics') const opentelemetry = require("@opentelemetry/sdk-node"); const { PrometheusExporter } = require("@opentelemetry/exporter-prometheus"); @@ -226,11 +226,11 @@ request_counter_total 6 1666624810428 The postfixed `_total` get automatically to the instrument name for each counter instrument when the measurements are getting exported in the Prometheus format. In the above -example you see that we access our `/` endpoint six times. +example you see that we accessed our `/` endpoint six times. ## Acquiring a Meter -In OpenTelemetry, Metrics measurement operations are performed using methods on a _meter_. You can get a meter by calling [`getMeter`](https://open-telemetry.github.io/opentelemetry-js-api/classes/metricsapi.html#getmetrics) on the global meter provider. `getMeter` takes the name and version of the application or library acquiring the meter, and provides a meter which can be used to create instruments. +In OpenTelemetry, Instruments that allow for measurement operations are acquired through a _meter_. You can get a meter by calling [`getMeter`](https://open-telemetry.github.io/opentelemetry-js-api/classes/metricsapi.html#getmetrics) on the global meter provider. `getMeter` takes the name and version of the application or library acquiring the meter, and provides a meter which can be used to create instruments. ```typescript import { metrics } from '@opentelemetry/api-metrics'; @@ -240,15 +240,15 @@ const meter = metrics.getMeter("my-application", "0.1.0"); ## Create a metric instrument -In OpenTelemetry, all _metrics_ are composed of [`Instruments`](https://open-telemetry.github.io/opentelemetry-js-api/interfaces/instrument.html). A instrument is responsible for reporting measurements, +In OpenTelemetry, all _metrics_ are composed of [`Instruments`](https://open-telemetry.github.io/opentelemetry-js-api/interfaces/instrument.html). An instrument is responsible for reporting measurements, there are four types of instruments that can be created: - Counter, a synchronous instrument which supports non-negative increments - Asynchronous Counter, a asynchronous instrument which supports non-negative increments -- Histogram,a synchronous instrument which supports arbitrary values that are statistically meaningful, such as histograms, summaries or percentile -- Asynchronous Gauge, asynchronous instrument which supports non-additive values, such as room temperature +- Histogram, a synchronous instrument which supports arbitrary values that are statistically meaningful, such as histograms, summaries or percentile +- Asynchronous Gauge, an asynchronous instrument which supports non-additive values, such as room temperature - UpDownCounter, a synchronous instrument which supports increments and decrements, such as number of active requests -- Asynchronous UpDownCounter, a asynchronous instrument which supports increments and decrements +- Asynchronous UpDownCounter, an asynchronous instrument which supports increments and decrements You can create a Counter instrument by calling [`Meter#createCounter`](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api_metrics.Meter.html#createCounter). The only required argument to `createCounter` is the _instrument name_, which should describe the item that is being measurement. @@ -260,14 +260,14 @@ counter.add(1); ``` -Most of the time, instruments will be used to measure operations in your application. The following example shows what it might look like to manually measure duration a function. +Most of the time, instruments will be used to measure operations in your application. The following example shows what it might look like to manually measure a function's duration. ```typescript async function myTask() { const histogram = meter.createHistogram("taks.duration"); const startTime = new Date().getTime() try { - // Wait for five seconds bore continueing code execution + // Wait for five seconds before continuing code execution await setTimeout(5_000) } catch (err) { } finally { @@ -300,7 +300,7 @@ async function myTask() { }); const startTime = new Date().getTime() try { - // Wait for five seconds bore continueing code execution + // Wait for five seconds before continuing code execution await setTimeout(5_000) } catch (err) { } finally { @@ -318,7 +318,7 @@ async function myTask() { await myTask() ``` -In the above example we are recording a measurement of roughly 5000ms and associates +In the above example we are recording a measurement of roughly 5000ms and associate three metric attributes with this measurement. Metrics backends can show these metric attributes. In Prometheus the metric attributes would become labels and can be used as part of queries, and allow search queries, such as what's the 90% percentile of @@ -327,7 +327,7 @@ all successful POST requests. ### Metric Attributes While name and measurement are the minimum required to record a metric measurement, -most of the time they will not be enough information on their own to effectively observer +most of the time they will not be enough information on their own to effectively observe an application. To solve this, OpenTelemetry uses _Metric Attributes_. Metric attributes are object with string keys and string values which add more context to the measurement. @@ -386,8 +386,8 @@ instantiated. A proposal is submitted to ease the ability to define Metrics View ### Configuring explicit bucket sizes for the Histogram instrument -The Histogram instruments has default set of bucket sizes defined which not might -not all suit your needs. The bucket sizes can be overridden by configuring a different +The Histogram instrument has a predefined set of bucket sizes defined which might not +suit all your needs. The bucket sizes can be overridden by configuring a different aggregation for the Histogram instrument. The `ExplicitBucketHistogramAggregation` should be used to define the bucket sizes for the Histogram instrument. @@ -428,16 +428,15 @@ httpServerDuration.record(50, { ### Dropping instrument from being exported -In some circumstances you don't want specific metrics to be exported by Opentelemetry, -for example, you might be using custom instrumentation or third-party packages that +In some circumstances you don't want specific metrics to be exported by OpenTelemetry, +for example, you might be using custom instrumentations or third-party packages that define their own metrics you are not interested in. -In such cases you can define a customer view which drop the instruments you are -not interesting in, for example, you can drop instruments of a specific meter or +In such cases you can define a view which drops the instruments you are +not interested in. For example, you can drop instruments of a specific meter or instruments with a specific name: -The following view drops all instruments that are associated with a meter with -the name `pubsub`: +The following view drops all instruments that are associated with a meter named `pubsub`: ```typescript const dropView = new View({ @@ -452,7 +451,7 @@ for example, all instruments of which the name starts with `http`: ```typescript const dropView = new View({ aggregation: new DropAggregation(), - instrumentName: 'htpp*', + instrumentName: 'http*', }); ``` @@ -480,17 +479,17 @@ After you have instrumented your application with metrics, you also need to make sure that the metrics get collected by your metrics backend. The most common formats that are used are Prometheus and OLTP. -The latter is the [Opentelemetry protocol format](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md) -which is supported by the Opentelemetry Collector. The former is based on the [OpenMetrics +The latter is the [OpenTelemetry protocol format](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md) +which is supported by the OpenTelemetry Collector. The former is based on the [OpenMetrics format](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md) can be consumed by Prometheus and Thanos or other OpenMetrics compatible backends. -_Note_: Both Opentelemetry Javascript and Opentelemetry Collector support +_Note_: Both OpenTelemetry JavaScript and OpenTelemetry Collector support exporters for different formats, such as [Cloud Monitoring](https://github.com/GoogleCloudPlatform/opentelemetry-operations-js/tree/master/packages/opentelemetry-cloud-monitoring-exporter). ## Exporting measurements to Prometheus -If you want to export your metrics to Prometheus you need to initialize Opentelemetry +If you want to export your metrics to Prometheus you need to initialize OpenTelemetry to use the Prometheus exporter `PrometheusExporter` which is included in the `@opentelemetry/exporter-prometheus`-package. @@ -516,7 +515,7 @@ counter.add(10, { pid: process.pid }); In the above example the instantiated `PrometheusExporter` is configured to expose a new http server on port 9464. You can now access the metrics at the endpoint -. This is the url that can be scraped by Prometheus so it can consumed the metrics collected by Opentelemetry in your application. +. This is the URL that can be scraped by Prometheus so it can consumed the metrics collected by OpenTelemetry in your application. More information about Prometheus and how to configure can be found at: [https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config](Prometheus Scraping Config) @@ -524,19 +523,17 @@ More information about Prometheus and how to configure can be found at: For a fully functioning code example for using this exporter, please have a look at: -## Exporting measurements to Opentelemetry Protocol +## Exporting measurements to OpenTelemetry Protocol -Opentelemetry Javascript comes with three different kind of exporters that export -the collected metrics in the Opentelemetry Protocol (OTLP). The three exports -different in the transport method to send the metrics to a backend that supports +OpenTelemetry JavaScript comes with three different kinds of exporters that export the OTLP protocol, (a) [https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-metrics-otlp-http](over HTTP) (b) [https://www.npmjs.com/package/@opentelemetry/exporter-metrics-otlp-grpc](over GRPC) (c) [https://www.npmjs.com/package/@opentelemetry/exporter-metrics-otlp-proto](over Protofbuf). -In the example below shows how you can configure Opentelemetry Javascript to use -the OTLP exporter over HTTP. +The example below shows how you can configure OpenTelemetry JavaScript to use +the OTLP exporter using http/protobuf. ```typescript const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); -const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http'); +const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-proto'); const collectorOptions = { url: '', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics concurrencyLimit: 1, // an optional limit on pending requests