UNPKG

@jufab/opentelemetry-angular-interceptor

Version:

@jufab/opentelemetry-angular-interceptor is an Angular Library to deploy [OpenTelemetry](https://opentelemetry.io/) in your Angular application

494 lines (363 loc) 20.2 kB
# OpenTelemetry Angular Interceptor @jufab/opentelemetry-angular-interceptor is an Angular Library to deploy [OpenTelemetry](https://opentelemetry.io/) in your Angular application This library uses [opentelemetry-js package](https://github.com/open-telemetry/opentelemetry-js) **Use Angular >= 13.0.0** More info : https://jufab.github.io/opentelemetry-angular-interceptor/ [![npm version](https://badge.fury.io/js/%40jufab%2Fopentelemetry-angular-interceptor.svg)](https://badge.fury.io/js/%40jufab%2Fopentelemetry-angular-interceptor) [![codecov](https://codecov.io/gh/jufab/opentelemetry-angular-interceptor/branch/master/graph/badge.svg)](https://codecov.io/gh/jufab/opentelemetry-angular-interceptor) ## Table of contents - [OpenTelemetry Angular Interceptor](#opentelemetry-angular-interceptor) - [Table of contents](#table-of-contents) - [Getting started](#getting-started) - [Content](#content) - [Installation](#installation) - [Configuration](#configuration) - [Example global Configuration](#example-global-configuration) - [Common Configuration](#common-configuration) - [BatchSpanProcessor Configuration](#batchspanprocessor-configuration) - [OpenTelemetry-collector Configuration](#opentelemetry-collector-configuration) - [Jaeger Propagator Configuration](#jaeger-propagator-configuration) - [Zipkin Exporter Configuration](#zipkin-exporter-configuration) - [B3 Propagator Configuration](#b3-propagator-configuration) - [Ignore URL Configuration](#ignore-url-configuration) - [External Configuration](#external-configuration) - [Angular module](#angular-module) - [Commons Module](#commons-module) - [Exporter module](#exporter-module) - [Propagator module](#propagator-module) - [Interceptor Module](#interceptor-module) - [Instrumentation Module](#instrumentation-module) - [Interceptor Module And Instrumentation Module](#interceptor-module-and-instrumentation-module) - [Injection token](#injection-token) - [(Optional) Logging in OtelColExporterModule](#optional-logging-in-otelcolexportermodule) - [NGXLogger](#ngxlogger) - [(Optional) Add span attributes during interception](#optional-add-span-attributes-during-interception) - [How it works](#how-it-works) - [Example](#example) - [Run](#run) - [Interceptor](#interceptor) - [Instrumentation](#instrumentation) - [\[Optional\] Result in OpenTelemetry-collector](#optional-result-in-opentelemetry-collector) - [Troubleshoot](#troubleshoot) - [Angular 10 Warning](#angular-10-warning) - [Other](#other) ## Getting started ### Content This library offers two possibilities to use it in Angular App : - **Interceptor** : catch every external call with the HttpClient from angular - **Instrumentation** : use instrumentation from opentelemetry-js with web plugins _(You need to install and configure it)_ like : - [@opentelemetry/instrumentation-document-load](hhttps://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/web/opentelemetry-instrumentation-document-load) - [@opentelemetry/instrumentation-fetch](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-fetch) - [@opentelemetry/instrumentation-xml-http-request](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-xml-http-request) - ... ### Installation With npm : ``` npm i @jufab/opentelemetry-angular-interceptor ``` ### Configuration Use the "OpentelemetryConfig" interface to configure the Tracer ```typescript export interface OpenTelemetryConfig { commonConfig: CommonCollectorConfig; batchSpanProcessorConfig?: BatchSpanProcessorConfig; otelcolConfig?: OtelCollectorConfig; jaegerPropagatorConfig?: JaegerPropagatorConfig; zipkinConfig?: ZipkinCollectorConfig; b3PropagatorConfig?: B3PropagatorConfig; ignoreUrls?: IgnoreUrlsConfig; } ``` #### Example global Configuration _From the interceptor-example_ ```typescript opentelemetryConfig: { commonConfig: { console: true, //(boolean) Display trace on console production: false, //(boolean) Send trace with BatchSpanProcessor (true) or SimpleSpanProcessor (false) logBody: true, //(boolean) true add body in a log, nothing otherwise serviceName: 'interceptor-example', //Service name send in trace resourceAttributes: { // extra resource attributes like service.namespace [ATTR_SERVICE_VERSION]: 'version 1.0.0', // Service version }, probabilitySampler: '0.7', //Samples a configurable percentage of traces, string value between '0' to '1' logLevel:DiagLogLevel.ALL //(Enum) DiagLogLevel is an Enum from @opentelemetry/api }, batchSpanProcessorConfig: { //Only if production = true in commonConfig maxQueueSize: '2048', // The maximum queue size. After the size is reached spans are dropped. maxExportBatchSize: '512', // The maximum batch size of every export. It must be smaller or equal to maxQueueSize. scheduledDelayMillis: '5000', // The interval between two consecutive exports exportTimeoutMillis: '30000', // How long the export can run before it is cancelled }, otelcolConfig: { url: 'http://localhost:4318/v1/traces', //URL of opentelemetry collector }, jaegerPropagatorConfig: { customHeader: 'custom-header', } } ``` _From the instrumentation-example_ ```typescript backendApp.get('/api/config', (req,res) => { return res.status(200).send({ commonConfig: { console: true, // Display trace on console production: true, // Send Trace with BatchSpanProcessor (true) or SimpleSpanProcessor (false) serviceName: 'instrumentation-example', // Service name send in trace resourceAttributes: { // extra resource attributes like service.namespace 'service.namespace': 'namespace' }, probabilitySampler: '0.75', // 75% sampling logLevel: 99 //ALL Log, DiagLogLevel is an Enum from @opentelemetry/api }, otelcolConfig: { url: 'http://localhost:4318/v1/traces', // URL of opentelemetry collector } }); }) ``` #### Common Configuration * console: (boolean) Display trace on console if true * production: (boolean)Send trace via BatchSpanProcessor (Async) or SimpleSpanProcessor (Sync) : It's recommend to use BatchSpanProcessor on Production. * serviceName: (string) Service name in your trace * resourceAttributes: list of extra resource attributes * probabilitySampler: (string) Samples a configurable percentage of traces, value between 0 to 1 * logBody: (boolean) true add body in a log, nothing otherwise * logLevel: (DiagLogLevel) log level #### BatchSpanProcessor Configuration _This configuration applies if production is true in commonConfig._ * maxQueueSize: (string) The maximum queue size. After the size is reached spans are dropped. * maxExportBatchSize: (string) The maximum batch size of every export. It must be smaller or equal to maxQueueSize. * scheduledDelayMillis: (string) The interval between two consecutive exports * exportTimeoutMillis: (string) How long the export can run before it is cancelled #### OpenTelemetry-collector Configuration * url: (string) url of opentelemetry collector (default : http://localhost:4318/v1/traces) * headers: list of custom header (more info: https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-http) * concurrencyLimit (string) : An optional limit on pending requests (more info : https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-http) * timeoutMillis (string): Maximum time the OTLP exporter will wait for each batch export. The default value is 10000ms (more info : https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-http) #### Jaeger Propagator Configuration * customHeader: (string) custom header (more info : https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-propagator-jaeger) #### Zipkin Exporter Configuration * url: (string) url of zipkin collector (default : http://localhost:9411/api/v2/spans) * headers: list of custom header (more info : https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-exporter-zipkin) #### B3 Propagator Configuration * multiHeader : (string) Single or Multi Header for b3propagator (default: multi). Value : 'O' (single), '1' (multi) (more info: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-propagator-b3) #### Ignore URL Configuration * urls : (Array<string | RegExp>) URLs that partially match any regex in ignoreUrls will not be traced. In addition, URLs that are _exact matches_ of strings in ignoreUrls will also not be traced #### External Configuration Instrumentation example project have an external configuration to show how you can do it. ### Angular module You need 3 modules to add to your application. - [Exporter Module](#exporter-module) : to define type and export of traces. - [Propagator Module](#propagator-module) : to define propagation in your HTTP header. - Last Module, 2 choices : - [OpenTelemetryInterceptorModule](#interceptor-module) : to activate interceptor in all your http call. - [OtelWebTracerModule](#instrumentation-module) : to activate instrumentation (you need a component to activate it, see [Component otel-instrumentation](#component-otel-instrumentation)). #### Commons Module You add this modules in your application module (generally app.module.ts) ##### Exporter module There is 4 exporters: * NoopSpanExporterModule : This a fake exporter * OtelColExporterModule : OpenTelemetry exporter (more info : https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-trace-otlp-http) * ConsoleSpanExporterModule : Console Exporter * ZipkinExporterModule : Zipkin Exporter (more info : https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-exporter-zipkin) ##### Propagator module there is 6 propagators (more info about propagator: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-core) * NoopHttpTextPropagatorModule : This is a fake propagator * B3PropagatorModule : Use B3 propagator * W3CTraceContextPropagatorModule : Use W3CTraceContext propagator * JaegerHttpTracePropagatorModule : Use JaegerHttpPropagator (more info about this one: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-propagator-jaeger) * AwsXrayPropagatorModule : Use AWS X-Ray propagator * CompositePropagatorModule : use all of the propagator #### Interceptor Module Just add OpenTelemetryInterceptorModule to insert Interceptor ```typescript import { NgModule } from '@angular/core'; ... import { AppComponent } from './app.component'; import { HttpClientModule } from '@angular/common/http'; import { OpenTelemetryInterceptorModule, OtelColExporterModule, CompositePropagatorModule } from '@jufab/opentelemetry-angular-interceptor'; import { environment } from '../environments/environment'; ... @NgModule({ declarations: [AppComponent, ...], imports: [ ... HttpClientModule, //Insert module OpenTelemetryInterceptorModule with configuration, HttpClientModule is used for interceptor OpenTelemetryInterceptorModule.forRoot(environment.opentelemetryConfig), //Insert OtelCol exporter module OtelColExporterModule, //Insert propagator module CompositePropagatorModule, ... ], providers: [], bootstrap: [AppComponent], }) export class AppModule {} ``` #### Instrumentation Module Declare this OtelWebTracerModule to configure instrumentation. You need to provide Web instrumentation on the `OTEL_INSTRUMENTATION_PLUGINS` token in providers section of NgModule _Example in instrumentation-example project_ ```typescript ... import { OtelColExporterModule, CompositePropagatorModule, OtelWebTracerModule } from 'projects/opentelemetry-interceptor/src/public-api'; ... @NgModule({ declarations: [AppComponent, ...], imports: [ ... // OtelCol Exporter Module OtelColExporterModule, // Composite Propagator Module CompositePropagatorModule, // OtelWebTracerModule to configure instrumentation component. OtelWebTracerModule.forRoot(environment.openTelemetryConfig), ... ], providers: [ {provide: OTEL_INSTRUMENTATION_PLUGINS, useValue: [new XMLHttpRequestInstrumentation()]} ], bootstrap: [AppComponent], }) export class AppModule { } ``` *This module uses APP_INITIALIZER token to load instrumentation (multi:true). No component needs now* #### Interceptor Module And Instrumentation Module `Don't use them at the same time : you're going to have the same trace twice.` #### Injection token This library exposes injection token. You can use them to override or customize. * OTEL_EXPORTER : token to inject an implementation of `IExporter` * OTEL_PROPAGATOR : token to inject an implementation of `IPropagator` * OTEL_CONFIG : token to inject an `OpenTelemetryConfig` * OTEL_INSTRUMENTATION_PLUGINS : token to inject an `InstrumentationOption` array * OTEL_LOGGER : more info in [(Optional) Logging in OtelColExporterModule](#optional-logging-in-otelcolexportermodule) * OTEL_CUSTOM_SPAN : more infor in [(Optional) Add span attributes during interception](#optional-add-span-attributes-during-interception) ### (Optional) Logging in OtelColExporterModule You can add a logger to the [OtelColExporterModule](projects/opentelemetry-interceptor/src/lib/services/exporter/otelcol/otelcol-exporter.module.ts) with the [OTEL_LOGGER](projects/opentelemetry-interceptor/src/lib/configuration/opentelemetry-config.ts) token. You can use a custom logger which implements the [DiagLogger](https://open-telemetry.github.io/opentelemetry-js-api/enums/diagloglevel.html) in @opentelemetry/api. Or, you can use an existing logger which implements the same functions (error, warn, info, debug) like [ngx-logger](https://www.npmjs.com/package/ngx-logger). #### NGXLogger You can use [ngx-logger](https://www.npmjs.com/package/ngx-logger). In your [appModule](projects/example-app/src/app/app.module.ts), insert LoggerModule and configure it ```typescript @NgModule({ ... imports: [ LoggerModule.forRoot(environment.loggerConfig), ] ... ``` And use OTEL_LOGGER token to inject NGXLogger ```typescript @NgModule({ ... providers: [ ... { provide: OTEL_LOGGER, useExisting: NGXLogger } ... ] ``` Don't forget to set "logLevel" in [Common Configuration](#common-configuration) (Level must be the same between NGXLogger and common configuration) > You can see an example in the [interceptor-example](#example). ### (Optional) Add span attributes during interception _This option is only available for Interceptor Module_ Implement a [`CustomSpan`](projects/opentelemetry-interceptor/src/lib/interceptor/custom-span.interface.ts) and the method `add(span: Span, request: HttpRequest<unknown>, response: HttpResponse<unknown> | HttpErrorResponse): Span` - span : Current span, you can set or get attributes - request : Current request in interceptor - response : Current response in interceptor Implement CustomSpan class like : ```typescript class CustomSpanImpl implements CustomSpan { add(span: Span, request: HttpRequest<unknown>, response: HttpResponse<unknown> | HttpErrorResponse): Span { span.setAttribute('mycustom.key', request.params + ";" + response.status); return span; } } ``` Inject it in you App module with `OTEL_CUSTOM_SPAN` : ```typescript @NgModule({ ... providers: [ ... { provide: OTEL_CUSTOM_SPAN, useClass: CustomSpanImpl } ... ] ``` > You can see an example in the [interceptor-example](#example). ## How it works This library is based on [HttpClientModule](https://angular.io/api/common/http/HttpClientModule) and the [HTTP_INTERCEPTORS](https://angular.io/api/common/http/HTTP_INTERCEPTORS) OpenTelemetryInterceptor implement an [HttpInterceptor](https://angular.io/api/common/http/HttpInterceptor) and the intercept method. This implementation initialise a [WebTracerProvider](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-web/src/WebTracerProvider.ts), create a [Span](https://open-telemetry.github.io/opentelemetry-js/interfaces/span.html) and add [header propagation](https://open-telemetry.github.io/opentelemetry-js/interfaces/textmappropagator.html) in the current call. > The response body is adding by an event in span. ## Example This project has two example Angular Application: - [projects/interceptor-example](projects/interceptor-example) - [projects/instrumentation-example](projects/instrumentation-example) You can see how configure and insert all modules. You can althought test __opentelemetry-angular-interceptor__ with this two applications. ### Run #### Interceptor To start this Interceptor example application, run command : ``` npm run start:complete-interceptor-example ``` and open the application at http://localhost:4200 #### Instrumentation To start this Instrumentation example application, run command : ``` npm run start:complete-instrumentation-example ``` and open the application at http://localhost:4200 ### [Optional] Result in OpenTelemetry-collector If you want to see the result in a collector *, there's a docker-compose available in this project. You can start it with this command : ``` docker-compose -f collector/docker-compose.yaml up -d ``` Go to the jaeger application (http://localhost:16686) to see result. More info about the collector here : https://github.com/open-telemetry/opentelemetry-collector > _* without an Agent or a Collector you can see an error in your browser about sending a "trace"._ ## Troubleshoot ### Angular 10 Warning ```shell WARNING in xxx/fesm2015/jufab-opentelemetry-angular-interceptor.js depends on '@opentelemetry/web'. CommonJS or AMD dependencies can cause optimization bailouts. For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies WARNING in xxx/fesm2015/jufab-opentelemetry-angular-interceptor.js depends on '@opentelemetry/core'. CommonJS or AMD dependencies can cause optimization bailouts. For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies WARNING in xxx/fesm2015/jufab-opentelemetry-angular-interceptor.js depends on '@opentelemetry/tracing'. CommonJS or AMD dependencies can cause optimization bailouts. For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies WARNING in xxx/fesm2015/jufab-opentelemetry-angular-interceptor.js depends on '@opentelemetry/api'. CommonJS or AMD dependencies can cause optimization bailouts. For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies WARNING in xxx/fesm2015/jufab-opentelemetry-angular-interceptor.js depends on '@opentelemetry/exporter-collector/build/src/platform/browser'. CommonJS or AMD dependencies can cause optimization bailouts. For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies ``` Add to your angular.json ```json "options": { "allowedCommonJsDependencies": [ "@opentelemetry/api", "@opentelemetry/exporter-collector", "@opentelemetry/exporter-zipkin", "@opentelemetry/tracing", "@opentelemetry/web", "@opentelemetry/core", "@opentelemetry/propagator-jaeger", "@opentelemetry/propagator-b3", "@opentelemetry/instrumentation", "@opentelemetry/instrumentation-xml-http-request", "@opentelemetry/instrumentation-document-load", "@opentelemetry/instrumentation-fetch", "@opentelemetry/context-zone-peer-dep" ], ``` ### Other |Error|Fix| |-----|---| |error TS2694: Namespace 'NodeJS' has no exported member 'Timeout'.|Need dependence @type/node >= 12.0.2| |error TS1086: An accessor cannot be declared in an ambient context.|Need dependence typescript >= 3.6.0|