OpenTelemetry module for Nest.
Setting up observability metrics with nestjs requires multiple libraries and patterns. OpenTelemetry has support for multiple exporters and types of metrics such as Prometheus Metrics.
$ npm i nestjs-otel --save
Some peers dependencies are required when some configurations are enabled.
@opentelemetry/exporter-prometheus
@opentelemetry/host-metrics
@opentelemetry/metrics
opentelemetry-node-metrics
nodeSDKConfiguration
property accepts OpenTelemetry NodeSDK configuration.
const OpenTelemetryModuleConfig = OpenTelemetryModule.forRoot({
metrics: {
hostMetrics: true, // Includes Host Metrics
defaultMetrics: true, // Includes Default Metrics
apiMetrics: {
enable: true, // Includes api metrics
timeBuckets: [],
},
},
nodeSDKConfiguration: {
metricExporter: new PrometheusExporter({
port: 8081,
}),
spanProcessor: new BatchSpanProcessor(new JaegerExporter()),
contextManager: new AsyncLocalStorageContextManager(),
textMapPropagator: new CompositePropagator({
propagators: [
new JaegerPropagator(),
new HttpTraceContextPropagator(),
new HttpBaggagePropagator(),
new B3Propagator(),
new B3Propagator({
injectEncoding: B3InjectEncoding.MULTI_HEADER,
}),
],
}),
instrumentations: [getNodeAutoInstrumentations()],
},
});
@Module({
imports: [
OpenTelemetryModuleConfig,
],
})
export class AppModule {}
If you need, you can define a custom Tracing Span for a method. It works async or sync. Span takes its name from the parameter; but by default, it is the same as the method's name
import { Span } from 'nestjs-otel';
@Span('CRITICAL_SECTION')
async getBooks() {
return [`Harry Potter and the Philosopher's Stone`];
}
In case you need to access native span methods for special logics in the method block:
import { TraceService } from 'nestjs-otel';
@Injectable()
export class BookService {
constructor(private readonly traceService: TraceService) {}
@Span()
async getBooks() {
const currentSpan = this.traceService.getSpan(); // --> retrives current span, comes from http or @Span
await this.doSomething();
currentSpan.addEvent('event 1');
currentSpan.end(); // current span end
const span = this.traceService.startSpan('sub_span'); // start new span
span.setAttributes({ userId: 1 });
await this.doSomethingElse();
span.end(); // new span ends
return [`Harry Potter and the Philosopher's Stone`];
}
}
OpenTelemetry Metrics allow a user to collect data and export it to metrics backend like Prometheus.
import { MetricService } from 'nestjs-otel';
import { Counter } from '@opentelemetry/api-metrics';
@Injectable()
export class BookService {
private customMetricCounter: Counter;
constructor(private readonly metricService: MetricService) {
this.customMetricCounter = this.metricService.getCounter('custom_counter', {
description: 'Description for counter',
});
}
async getBooks() {
this.customMetricCounter.add(1);
return [`Harry Potter and the Philosopher's Stone`];
}
}
Impl | Metric | Description | Labels | Metric Type |
---|---|---|---|---|
โ | http_request_total | Total number of HTTP requests. | method, path | Counter |
โ | http_response_total | Total number of HTTP responses. | method, status, path | Counter |
โ | http_response_success_total | Total number of all successful responses. | - | Counter |
โ | http_response_error_total | Total number of all response errors. | - | Counter |
โ | http_request_duration_seconds | HTTP latency value recorder in seconds. | method, status, path | ValueRecorder |
โ | http_client_error_total | Total number of client error requests. | - | Counter |
โ | http_server_error_total | Total number of server error requests. | - | Counter |
http_server_aborts_total | Total number of data transfers aborted by the client. | - | Counter | |
http_client_aborts_total | Total number of data transfers aborted by the server. | - | Counter | |
http_connection_error_total | Total of connection errors. | - | Counter | |
http_request_size_bytes | Current total of incoming bytes. | - | ValueRecorder | |
http_response_size_bytes | Current total of outgoing bytes. | - | ValueRecorder | |
http_slow_request_total | The server handled slow requests counter - t=%d . |
- | Counter |
When nodeSDKConfiguration.metricExporter
is defined with a PrometheusExporter
it will start a new process on port 8081
(default port) and metrics will be available at http://localhost:8081/metrics
.
import Pino, { Logger } from 'pino';
import { LoggerOptions } from 'pino';
import { trace, context } from '@opentelemetry/api';
export const loggerOptions: LoggerOptions = {
formatters: {
log(object) {
const span = trace.getSpan(context.active());
if (!span) return { ...object };
const { spanId, traceId } = trace.getSpan(context.active())?.spanContext();
return { ...object, spanId, traceId };
},
},
};
export const logger: Logger = Pino(loggerOptions);