Skip to main content

Telemetry

Explanation

Telemetry and distributed tracing is a important part of complete application monitoring.

Tramvai provides a deep integration with OpenTelemetry Node.js SDK, with custom automatic instrumentation for internal Node.js modules and core Tramvai mechanisms.

Usage

Installation

You need to install @tramvai/module-opentelemetry:

npm i --save @tramvai/module-module-opentelemetry

Then connect to the project:

import { createApp } from '@tramvai/core';
import { OpenTelemetryModule } from '@tramvai/module-opentelemetry';

createApp({
name: 'tincoin',
modules: [ OpenTelemetryModule ],
});

This will enable OpenTelemetry with automatic instrumentation, and provide Tracer into the application.

Create spans

Simplest way to wrap operation in span is to use Tracer.trace method, e.g.:

import { OPENTELEMETRY_TRACER_TOKEN } from '@tramvai/module-opentelemetry';

const provider = {
provide: commandLineListTokens.resolvePageDeps,
useFactory: ({ tracer, apiService }) => {
return async function getSmth() {
tracer?.trace('get-smth', async (span) => {
// set attribute to the span
span.setAttribute('key', 'value');

// span will be ended automatically after `apiService.get` method resolves or rejects
return apiService.get('/smth');
});
}
},
deps: {
// tracer exists only server-side
tracer: optional(OPENTELEMETRY_TRACER_TOKEN),
apiService: API_SERVICE_TOKEN,
},
};

For more flexibility methods Tracer.startActiveSpan and Tracer.startSpan is available.

Tracer.trace and other span creation methods calls can be nested, with automatic context propagation.

Export traces

For export traces to OpenTelemetry collector, you need to provide custom span processor and exporter with OPENTELEMETRY_PROVIDER_SPAN_PROCESSOR_TOKEN token.

If you have a gRPC collector (e.g. Jaeger), library @opentelemetry/exporter-trace-otlp-grpc can be used:

import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node';
import { OPENTELEMETRY_PROVIDER_SPAN_PROCESSOR_TOKEN } from '@tramvai/module-opentelemetry';

const provider = {
provide: OPENTELEMETRY_PROVIDER_SPAN_PROCESSOR_TOKEN,
useFactory: ({ envManager }) => {
const url = envManager.get('OTEL_GRPC_COLLECTOR_ENDPOINT');
const exporter = new OTLPTraceExporter({ url });

return new BatchSpanProcessor(exporter);
},
deps: {
envManager: ENV_MANAGER_TOKEN,
},
};

Resource attributes

If you need to extend resource attributes, use OPENTELEMETRY_PROVIDER_RESOURCE_ATTRIBUTES_TOKEN token. Also, @opentelemetry/semantic-conventions library contains some of attribute names.

import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
import { OPENTELEMETRY_PROVIDER_RESOURCE_ATTRIBUTES_TOKEN } from '@tramvai/module-opentelemetry';

const provider = {
provide: OPENTELEMETRY_PROVIDER_RESOURCE_ATTRIBUTES_TOKEN,
useFactory: ({ appInfo }) => {
return {
[ATTR_SERVICE_NAME]: appInfo.appName,
}
},
deps: {
appInfo: APP_INFO_TOKEN,
},
};

Active span

If you need to set attributes to the active span, use Tracer.getActiveSpan method:

// tracer from OPENTELEMETRY_TRACER_TOKEN
function doSmt({ tracer }) {
const span = tracer.getActiveSpan();

// span can be absent, for example in `init` or `listen` command line stages
span?.setAttribute('key', 'value');
}

Automatic instrumentation

OpenTelemetry instrumentation libraries is not supported, because of it limitations:

  • instrumentations are registered before the module to instrument is require'ed
  • modules are not included in a bundle

Instead, module provides custom instrumentation for all significant cases:

Server module

All incoming requests are automatically wrapped in root span, where current request and response parameters will be set (path, method, status code, error).

Naming and attributes follow semantic conventions.

Context propagation

OpenTelemetryModule provides context propagation for incoming and outgoing requests.

Logs correlation

OpenTelemetryModule inject context for logs correlation.

All application logs will be extended with current span and trace ids in spanId and traceId properties.

Debug and testing

By default, in development mode ConsoleSpanExporter is used, which prints all spans to the console.

For testing purposes, you can use InMemorySpanExporter.