@jufab/opentelemetry-angular-interceptor is an Angular Library to deploy [OpenTelemetry](https://opentelemetry.io/) in your Angular application
npm install @jufab/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/


- OpenTelemetry Angular Interceptor
- Table of contents
- Getting started
- Content
- Installation
- Configuration
- Example global Configuration
- Common Configuration
- BatchSpanProcessor Configuration
- OpenTelemetry-collector Configuration
- Jaeger Propagator Configuration
- Zipkin Exporter Configuration
- B3 Propagator Configuration
- Ignore URL Configuration
- External Configuration
- Angular module
- Commons Module
- Exporter module
- Propagator module
- Interceptor Module
- Instrumentation Module
- Interceptor Module And Instrumentation Module
- Injection token
- (Optional) Logging in OtelColExporterModule
- NGXLogger
- (Optional) Add span attributes during interception
- How it works
- Example
- Run
- Interceptor
- Instrumentation
- [\[Optional\] Result in OpenTelemetry-collector](#optional-result-in-opentelemetry-collector)
- Troubleshoot
- Angular 10 Warning
- Other
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
- ...
With npm :
```
npm i @jufab/opentelemetry-angular-interceptor
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
#### External Configuration
Instrumentation example project have an external configuration to show how you can do it.
You need 3 modules to add to your application.
- Exporter Module : to define type and export of traces.
- Propagator Module : to define propagation in your HTTP header.
- Last Module, 2 choices :
- 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
`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 IExporterIPropagator
* OTEL_PROPAGATOR : token to inject an implementation of OpenTelemetryConfig
* OTEL_CONFIG : token to inject an InstrumentationOption
* OTEL_INSTRUMENTATION_PLUGINS : token to inject an array
* OTEL_LOGGER : more info in (Optional) Logging in OtelColExporterModule
* OTEL_CUSTOM_SPAN : more infor in (Optional) Add span attributes during interception
You can add a logger to the OtelColExporterModule with the OTEL_LOGGER 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
`typescript`
@NgModule({
...
imports: [
LoggerModule.forRoot(environment.loggerConfig),
]
...`
And use OTEL_LOGGER token to inject NGXLoggertypescript`
@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.
_This option is only available for Interceptor Module_
Implement a CustomSpan and the method add(span: Span, request: HttpRequest
- 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
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.
This library is based on HttpClientModule and the HTTP_INTERCEPTORS
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.
This project has two example Angular Application:
- projects/interceptor-example
- projects/instrumentation-example
You can see how configure and insert all modules.
You can althought test __opentelemetry-angular-interceptor__ with this two applications.
#### 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
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"._
`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"
],
|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|