Skip to content

Commit

Permalink
add addVarToSpan() (#41)
Browse files Browse the repository at this point in the history
* add and expose new function

* change node version in release.yml

* add customSpanProcessor

* update version

* rename function
  • Loading branch information
EddeCCC authored Jul 20, 2023
1 parent 5b97008 commit e093188
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 19 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ on:
jobs:
build-and-release:
runs-on: ubuntu-20.04
container:
image: node:16.17
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Build plugin
run: |
npm install --global yarn
yarn install
yarn build && yarn build-dev
- name: Prepare SBOM generation
Expand All @@ -29,6 +30,7 @@ jobs:
json: true
- name: Package SBOMs
run: |
apt-get update && apt-get install zip
zip -r dist/boomerang-opentelemetry-sboms.zip boomerang-opentelemetry-sboms
- name: Create Release
uses: softprops/action-gh-release@v1
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "boomerang-opentelemetry-plugin",
"version": "0.25.0-3",
"version": "0.25.0-7",
"description": "This is a Boomerang plugin for collecting spans using the OpenTelemetry framework and exporting them, e.g., to an OpenTelemetry collector.",
"repository": "https://github.com/NovatecConsulting/boomerang-opentelemetry-plugin",
"license": "Apache-2.0",
Expand Down
48 changes: 31 additions & 17 deletions src/impl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-docu
import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction';
import { PluginProperties, ContextFunction, PropagationHeader } from '../types';
import { patchExporter, patchExporterClass } from './patchCollectorPrototype';
import { MultiSpanProcessor, CustomSpanProcessor } from './spanProcessing';

/**
* TODOs:
Expand Down Expand Up @@ -97,6 +98,8 @@ export default class OpenTelemetryTracingImpl {

private traceProvider: WebTracerProvider;

private customSpanProcessor = new CustomSpanProcessor();

public register = () => {
// return if already initialized
if (this.initialized) {
Expand Down Expand Up @@ -146,11 +149,15 @@ export default class OpenTelemetryTracingImpl {
patchExporterClass();
}

const batchSpanProcessor = new BatchSpanProcessor(exporter, {
...this.defaultProperties.exporter,
...this.props.exporter,
});

const multiSpanProcessor = new MultiSpanProcessor([batchSpanProcessor, this.customSpanProcessor]);

providerWithZone.addSpanProcessor(
new BatchSpanProcessor(exporter, {
...this.defaultProperties.exporter,
...this.props.exporter,
})
multiSpanProcessor
);
} else {
// register console exporter for logging all recorded traces to the console
Expand All @@ -174,6 +181,14 @@ export default class OpenTelemetryTracingImpl {
return api;
};

public addVarToSpans = (key: string, value: string) => {
// Add Variable to active span
let activeSpan = api.trace.getSpan(api.context.active());
if(activeSpan != undefined) activeSpan.setAttribute(key, value);
// And to all following spans
this.customSpanProcessor.addCustomAttribute(key,value);
}

public setBeaconUrl = (url: string) => {
this.beaconUrl = url;
};
Expand Down Expand Up @@ -250,46 +265,45 @@ export default class OpenTelemetryTracingImpl {

private getInstrumentationPlugins = () => {
const { plugins, corsUrls, plugins_config } = this.props;
const insrumentations: any = [];
const instrumentations: any = [];

// XMLHttpRequest Instrumentation for web plugin
if (plugins_config?.instrument_xhr?.enabled !== false) {
insrumentations.push(new XMLHttpRequestInstrumentation(plugins_config.instrument_xhr));
}
else if (plugins?.instrument_xhr !== false) {
insrumentations.push(
instrumentations.push(new XMLHttpRequestInstrumentation(plugins_config.instrument_xhr));
} else if (plugins?.instrument_xhr !== false) {
instrumentations.push(
new XMLHttpRequestInstrumentation({
propagateTraceHeaderCorsUrls: corsUrls,
propagateTraceHeaderCorsUrls: corsUrls
})
);
}

// Instrumentation for the fetch API if available
const isFetchAPISupported = 'fetch' in window;
if (isFetchAPISupported && plugins_config?.instrument_fetch?.enabled !== false) {
insrumentations.push(new FetchInstrumentation(plugins_config.instrument_fetch));
instrumentations.push(new FetchInstrumentation(plugins_config.instrument_fetch));
}
else if (isFetchAPISupported && plugins?.instrument_fetch !== false) {
insrumentations.push(new FetchInstrumentation());
instrumentations.push(new FetchInstrumentation());
}

// Instrumentation for the document on load (initial request)
if (plugins_config?.instrument_document_load?.enabled !== false) {
insrumentations.push(new DocumentLoadInstrumentation(plugins_config.instrument_document_load));
instrumentations.push(new DocumentLoadInstrumentation(plugins_config.instrument_document_load));
}
else if (plugins?.instrument_document_load !== false) {
insrumentations.push(new DocumentLoadInstrumentation());
instrumentations.push(new DocumentLoadInstrumentation());
}

// Instrumentation for user interactions
if (plugins_config?.instrument_user_interaction?.enabled !== false) {
insrumentations.push(new UserInteractionInstrumentation(plugins_config.instrument_user_interaction));
instrumentations.push(new UserInteractionInstrumentation(plugins_config.instrument_user_interaction));
}
else if (plugins?.instrument_user_interaction !== false) {
insrumentations.push(new UserInteractionInstrumentation());
instrumentations.push(new UserInteractionInstrumentation());
}

return insrumentations;
return instrumentations;
};

/**
Expand Down
83 changes: 83 additions & 0 deletions src/impl/spanProcessing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Context } from '@opentelemetry/api';
import { Span, ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base';

/**
* SpanProcessor for special operations
*/
export class CustomSpanProcessor implements SpanProcessor {

/**
* Map of custom attributes that should be added to all spans
*/
private customAttributes:Map<string,string>;

constructor() {
this.customAttributes = new Map<string, string>();
}

forceFlush(): Promise<void> {
return Promise.resolve(undefined);
}

onEnd(span: ReadableSpan): void {
// No operation necessary
}

onStart(span: Span, parentContext: Context): void {
if(this.customAttributes.size > 0) {
this.customAttributes.forEach(
// For some reason forEach() twists key and value
(value:string, key:string) => span.setAttribute(key,value)
)
}
}

shutdown(): Promise<void> {
return Promise.resolve(undefined);
}

public addCustomAttribute(key:string, value:string) {
this.customAttributes.set(key, value);
}
}

/**
* Storage to allow the use of multiple SpanProcessors
*/
export class MultiSpanProcessor implements SpanProcessor {

private readonly spanProcessors: SpanProcessor[];
constructor(spanProcessors: SpanProcessor[]) {
this.spanProcessors = spanProcessors;
}

forceFlush(): Promise<void> {
return Promise.all(
this.spanProcessors.map((processor) => {
if (processor.forceFlush) {
return processor.forceFlush();
}
return Promise.resolve();
})
).then(() => {});
}

onEnd(span: ReadableSpan): void {
for (const processor of this.spanProcessors) {
processor.onEnd(span);
}
}

onStart(span: Span, parentContext: Context): void {
for (const processor of this.spanProcessors) {
processor.onStart(span, parentContext);
}
}

shutdown(): Promise<void> {
return Promise.all(
this.spanProcessors.map((processor) => processor.shutdown())
).then(() => {});
}
}

9 changes: 9 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ if (currentEntriesFn) {
// Returns the internally used OpenTelemetry API
getOpenTelemetryApi: impl.getOpenTelemetryApi,

// Adds a custom variable to the current span as well as all following spans
// that are created by opentelemetry.js
// Adds optionally a custom variable to the current beacon
addVarToSpans: (key: string, value: string, addToBeacon: boolean = false): void => {
impl.addVarToSpans(key, value);

if(addToBeacon) window.BOOMR.addVar(key, value);
},

is_complete: () => {
// This method should determine if the plugin has completed doing what it
// needs to do and return true if so or false otherwise
Expand Down

0 comments on commit e093188

Please sign in to comment.