@jufab/opentelemetry-angular-interceptor

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

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
@jufab/opentelemetry-angular-interceptor
3551.8.016 days ago4 years agoMinified + gzip package size for @jufab/opentelemetry-angular-interceptor in KB

Readme

OpenTelemetry Angular Interceptor
@jufab/opentelemetry-angular-interceptor is an Angular Library to deploy OpenTelemetry in your Angular application
This library uses opentelemetry-js package
Use Angular >= 13.0.0
More info : https://jufab.github.io/opentelemetry-angular-interceptor/
npm version codecov

Table of contents

- Table of contents - 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 - Example
- [Run](#run)
  - [Interceptor](#interceptor)
  - [Instrumentation](#instrumentation)
- [\[Optional\] Result in OpenTelemetry-collector](#optional-result-in-opentelemetry-collector)
- 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 - @opentelemetry/instrumentation-fetch - @opentelemetry/instrumentation-xml-http-request - ...

Installation

With npm :
npm i @jufab/opentelemetry-angular-interceptor

Configuration

Use the "OpentelemetryConfig" interface to configure the Tracer
export interface OpenTelemetryConfig {
  commonConfig: CommonCollectorConfig;
  batchSpanProcessorConfig?: BatchSpanProcessorConfig;
  otelcolConfig?: OtelCollectorConfig;
  jaegerPropagatorConfig?: JaegerPropagatorConfig;
  zipkinConfig?: ZipkinCollectorConfig;
  b3PropagatorConfig?: B3PropagatorConfig;
  ignoreUrls?: IgnoreUrlsConfig;
}

Example global Configuration

From the interceptor-example
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
        [SemanticResourceAttributes.SERVICE_NAMESPACE]: 'namespace'
      },
      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
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) 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.
- OpenTelemetryInterceptorModule
: to activate interceptor in all your http call. - OtelWebTracerModule : to activate instrumentation (you need a component to activate it, see 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
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
...
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 APPINITIALIZER 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.
  • OTELEXPORTER : token to inject an implementation of IExporter
  • OTEL
PROPAGATOR : token to inject an implementation of IPropagator
  • OTELCONFIG : token to inject an OpenTelemetryConfig
  • OTEL
  • INSTRUMENTATIONPLUGINS : token to inject an InstrumentationOption array
  • OTEL
  • LOGGER : more info in (Optional) Logging in OtelColExporterModule
  • OTELCUSTOMSPAN : more infor in (Optional) Add span attributes during interception
  • (Optional) Logging in OtelColExporterModule

    You can add a logger to the OtelColExporterModule
    with the OTELLOGGER token.
    You can use a custom logger which implements the DiagLogger in @opentelemetry/api.
    Or, you can use an existing logger which implements the same functions (error, warn, info, debug) like ngx-logger.

    NGXLogger

    You can use ngx-logger.
    In your appModule, insert LoggerModule and configure it
    @NgModule({
      ...
      imports: [
        LoggerModule.forRoot(environment.loggerConfig),
      ]
      ...
    And use OTELLOGGER token to inject NGXLogger
    @NgModule({
      ...
      providers: [
        ...
        { provide: OTEL_LOGGER, useExisting: NGXLogger }
        ...
      ]

    Don't forget to set "logLevel" in Common Configuration
    (Level must be the same between NGXLogger and common configuration)
    You can see an example in the interceptor-example.

    (Optional) Add span attributes during interception

    This option is only available for Interceptor Module
    Implement a CustomSpan 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 :
    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 :
    @NgModule({
      ...
      providers: [
        ...
        { provide: OTEL_CUSTOM_SPAN, useClass: CustomSpanImpl }
        ...
      ]

    You can see an example in the interceptor-example.

    How it works

    This library is based on HttpClientModule and the HTTPINTERCEPTORS
    OpenTelemetryInterceptor implement an HttpInterceptor and the intercept method.
    This implementation initialise a WebTracerProvider, create a Span and add header propagation in the current call.
    The response body is adding by an event in span.

    Example

    This project has two example Angular Application:

    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

    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
    "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|