From 7eb56efd4e0309ee4b122ededd0f88f88fa8fad1 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Mon, 18 Sep 2023 14:55:54 +0200 Subject: [PATCH 01/15] restructure files --- src/impl/index.ts | 6 +++--- .../{ => instrumentation}/documentLoadInstrumentation.ts | 5 ++--- src/impl/{ => transaction}/servertiming.ts | 0 src/impl/{ => transaction}/transactionIdGeneration.ts | 0 src/impl/{ => transaction}/transactionSpanManager.ts | 1 - 5 files changed, 5 insertions(+), 7 deletions(-) rename src/impl/{ => instrumentation}/documentLoadInstrumentation.ts (97%) rename src/impl/{ => transaction}/servertiming.ts (100%) rename src/impl/{ => transaction}/transactionIdGeneration.ts (100%) rename src/impl/{ => transaction}/transactionSpanManager.ts (97%) diff --git a/src/impl/index.ts b/src/impl/index.ts index 9ee4c74..b4e7f10 100644 --- a/src/impl/index.ts +++ b/src/impl/index.ts @@ -31,9 +31,9 @@ import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-u import { PluginProperties, ContextFunction, PropagationHeader } from '../types'; import { patchExporter, patchExporterClass } from './patchCollectorPrototype'; import { MultiSpanProcessor, CustomSpanProcessor } from './spanProcessing'; -import { DocumentLoadServerTimingInstrumentation, patchTracer } from './documentLoadInstrumentation'; -import { CustomIdGenerator } from './transactionIdGeneration'; -import { TransactionSpanManager } from './transactionSpanManager'; +import { DocumentLoadServerTimingInstrumentation, patchTracer } from './instrumentation/documentLoadInstrumentation'; +import { CustomIdGenerator } from './transaction/transactionIdGeneration'; +import { TransactionSpanManager } from './transaction/transactionSpanManager'; /** * TODOs: diff --git a/src/impl/documentLoadInstrumentation.ts b/src/impl/instrumentation/documentLoadInstrumentation.ts similarity index 97% rename from src/impl/documentLoadInstrumentation.ts rename to src/impl/instrumentation/documentLoadInstrumentation.ts index 8f0d701..0d04640 100644 --- a/src/impl/documentLoadInstrumentation.ts +++ b/src/impl/instrumentation/documentLoadInstrumentation.ts @@ -2,13 +2,12 @@ import { InstrumentationConfig } from '@opentelemetry/instrumentation'; import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load'; import * as api from '@opentelemetry/api'; -import { captureTraceParentFromPerformanceEntries } from './servertiming'; +import { captureTraceParentFromPerformanceEntries } from '../transaction/servertiming'; import { PerformanceEntries } from '@opentelemetry/sdk-trace-web'; import { Span, Tracer } from '@opentelemetry/sdk-trace-base'; - import { isTracingSuppressed } from '@opentelemetry/core/build/src/trace/suppress-tracing' import { sanitizeAttributes } from '@opentelemetry/core/build/src/common/attributes'; -import { TransactionSpanManager } from './transactionSpanManager'; +import { TransactionSpanManager } from '../transaction/transactionSpanManager'; export interface DocumentLoadServerTimingInstrumentationConfig extends InstrumentationConfig { recordTransaction: boolean; diff --git a/src/impl/servertiming.ts b/src/impl/transaction/servertiming.ts similarity index 100% rename from src/impl/servertiming.ts rename to src/impl/transaction/servertiming.ts diff --git a/src/impl/transactionIdGeneration.ts b/src/impl/transaction/transactionIdGeneration.ts similarity index 100% rename from src/impl/transactionIdGeneration.ts rename to src/impl/transaction/transactionIdGeneration.ts diff --git a/src/impl/transactionSpanManager.ts b/src/impl/transaction/transactionSpanManager.ts similarity index 97% rename from src/impl/transactionSpanManager.ts rename to src/impl/transaction/transactionSpanManager.ts index 684ade1..110a9ef 100644 --- a/src/impl/transactionSpanManager.ts +++ b/src/impl/transaction/transactionSpanManager.ts @@ -1,5 +1,4 @@ import api, { Span } from '@opentelemetry/api'; -import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; import { CustomIdGenerator } from './transactionIdGeneration'; /** From 29702ae28be2af7ddb1411ed812510b8c612a720 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Mon, 18 Sep 2023 15:20:08 +0200 Subject: [PATCH 02/15] add new XhrInstrumentation --- src/impl/index.ts | 5 +- .../xmlHttpRequestInstrumentation.ts | 46 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/impl/instrumentation/xmlHttpRequestInstrumentation.ts diff --git a/src/impl/index.ts b/src/impl/index.ts index b4e7f10..b9bf6bd 100644 --- a/src/impl/index.ts +++ b/src/impl/index.ts @@ -34,6 +34,7 @@ import { MultiSpanProcessor, CustomSpanProcessor } from './spanProcessing'; import { DocumentLoadServerTimingInstrumentation, patchTracer } from './instrumentation/documentLoadInstrumentation'; import { CustomIdGenerator } from './transaction/transactionIdGeneration'; import { TransactionSpanManager } from './transaction/transactionSpanManager'; +import { CustomXMLHttpRequestInstrumentation } from './instrumentation/xmlHttpRequestInstrumentation'; /** * TODOs: @@ -326,10 +327,10 @@ export default class OpenTelemetryTracingImpl { // XMLHttpRequest Instrumentation for web plugin if (plugins_config?.instrument_xhr?.enabled !== false) { - instrumentations.push(new XMLHttpRequestInstrumentation(plugins_config.instrument_xhr)); + instrumentations.push(new CustomXMLHttpRequestInstrumentation(plugins_config.instrument_xhr)); } else if (plugins?.instrument_xhr !== false) { instrumentations.push( - new XMLHttpRequestInstrumentation({ + new CustomXMLHttpRequestInstrumentation({ propagateTraceHeaderCorsUrls: corsUrls }) ); diff --git a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts new file mode 100644 index 0000000..40f33f6 --- /dev/null +++ b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts @@ -0,0 +1,46 @@ +import { InstrumentationConfig } from '@opentelemetry/instrumentation'; +import * as api from '@opentelemetry/api'; +import { + XMLHttpRequestInstrumentation, + XMLHttpRequestInstrumentationConfig +} from '@opentelemetry/instrumentation-xml-http-request'; +import { Span } from '@opentelemetry/api'; + + +type ExposedXHRSuper = { + _createSpan(xhr: XMLHttpRequest, url: string, method: string): api.Span | undefined; +} + +export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumentation { + + constructor(config: XMLHttpRequestInstrumentationConfig & InstrumentationConfig = {}) { + super(config); + + const exposedSuper = this as any as ExposedXHRSuper; + const _superStartSpan: ExposedXHRSuper['_createSpan'] = exposedSuper._createSpan.bind(this); + + exposedSuper._createSpan = (xhr, url, method) => { + const span = _superStartSpan(xhr, url, method); + + this.addUrlParamsToSpan(span, url); + + return span; + } + } + + private addUrlParamsToSpan(span: Span, url: string){ + const urlParams = url.split("?")[1]; + + if(urlParams) { + const keyValuePairs = urlParams.split("&"); + for(const keyValue of keyValuePairs) { + const [key, value] = keyValue.split("="); + span.setAttribute(key, value); + } + } + } +} + + + + From b0625bcfc4cf93ac7773e56bb8bad34d2945ff8f Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Wed, 20 Sep 2023 10:36:38 +0200 Subject: [PATCH 03/15] update DocumentLoadInstrumentation and FetchInstrumentation --- src/impl/index.ts | 9 +++-- .../documentLoadInstrumentation.ts | 17 +++++++++ .../instrumentation/fetchInstrumentation.ts | 36 +++++++++++++++++++ src/impl/instrumentation/urlParams.ts | 15 ++++++++ .../xmlHttpRequestInstrumentation.ts | 24 +++++-------- src/types.d.ts | 10 +++--- 6 files changed, 88 insertions(+), 23 deletions(-) create mode 100644 src/impl/instrumentation/fetchInstrumentation.ts create mode 100644 src/impl/instrumentation/urlParams.ts diff --git a/src/impl/index.ts b/src/impl/index.ts index b9bf6bd..1b94d99 100644 --- a/src/impl/index.ts +++ b/src/impl/index.ts @@ -35,6 +35,7 @@ import { DocumentLoadServerTimingInstrumentation, patchTracer } from './instrume import { CustomIdGenerator } from './transaction/transactionIdGeneration'; import { TransactionSpanManager } from './transaction/transactionSpanManager'; import { CustomXMLHttpRequestInstrumentation } from './instrumentation/xmlHttpRequestInstrumentation'; +import { CustomFetchInstrumentation } from './instrumentation/fetchInstrumentation'; /** * TODOs: @@ -60,7 +61,8 @@ export default class OpenTelemetryTracingImpl { path: "", applyCustomAttributesOnSpan: null, //(span: Span, request: Request) => { }, ignoreUrls: [], - propagateTraceHeaderCorsUrls: [] + propagateTraceHeaderCorsUrls: [], + excludeParameterKeys: [] }, instrument_xhr: { enabled: false, @@ -69,6 +71,7 @@ export default class OpenTelemetryTracingImpl { propagateTraceHeaderCorsUrls: [], ignoreUrls: [], clearTimingResources: false, + excludeParameterKeys: [] }, instrument_document_load: { enabled: false, @@ -339,10 +342,10 @@ export default class OpenTelemetryTracingImpl { // Instrumentation for the fetch API if available const isFetchAPISupported = 'fetch' in window; if (isFetchAPISupported && plugins_config?.instrument_fetch?.enabled !== false) { - instrumentations.push(new FetchInstrumentation(plugins_config.instrument_fetch)); + instrumentations.push(new CustomFetchInstrumentation(plugins_config.instrument_fetch)); } else if (isFetchAPISupported && plugins?.instrument_fetch !== false) { - instrumentations.push(new FetchInstrumentation()); + instrumentations.push(new CustomFetchInstrumentation()); } return instrumentations; diff --git a/src/impl/instrumentation/documentLoadInstrumentation.ts b/src/impl/instrumentation/documentLoadInstrumentation.ts index 0d04640..4bded57 100644 --- a/src/impl/instrumentation/documentLoadInstrumentation.ts +++ b/src/impl/instrumentation/documentLoadInstrumentation.ts @@ -8,6 +8,8 @@ import { Span, Tracer } from '@opentelemetry/sdk-trace-base'; import { isTracingSuppressed } from '@opentelemetry/core/build/src/trace/suppress-tracing' import { sanitizeAttributes } from '@opentelemetry/core/build/src/common/attributes'; import { TransactionSpanManager } from '../transaction/transactionSpanManager'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { addUrlParams } from './urlParams'; export interface DocumentLoadServerTimingInstrumentationConfig extends InstrumentationConfig { recordTransaction: boolean; @@ -31,6 +33,12 @@ export function patchTracer() { return api.trace.wrapSpanContext(api.INVALID_SPAN_CONTEXT); } + /* + ####################################### + OVERWRITTEN LOGIC START + ####################################### + */ + let parentContext; //getParent(options, context); if(options.root) parentContext = undefined; else parentContext = api.trace.getSpanContext(context); @@ -48,6 +56,12 @@ export function patchTracer() { if(transactionSpanId) spanId = transactionSpanId; } + /* + ####################################### + OVERWRITTEN LOGIC END + ####################################### + */ + let traceId; let traceState; let parentSpanId; @@ -133,6 +147,8 @@ export class DocumentLoadServerTimingInstrumentation extends DocumentLoadInstrum const exposedSpan = span as any as Span; if(exposedSpan.name == "documentLoad") TransactionSpanManager.setTransactionSpan(span); + addUrlParams(span, location.href, []); + return span; } @@ -146,4 +162,5 @@ export class DocumentLoadServerTimingInstrumentation extends DocumentLoadInstrum return _superEndSpan(span, performanceName, entries); }; } + } diff --git a/src/impl/instrumentation/fetchInstrumentation.ts b/src/impl/instrumentation/fetchInstrumentation.ts new file mode 100644 index 0000000..9db3596 --- /dev/null +++ b/src/impl/instrumentation/fetchInstrumentation.ts @@ -0,0 +1,36 @@ +import * as api from '@opentelemetry/api'; +import { addUrlParams } from './urlParams'; +import { FetchInstrumentation, FetchInstrumentationConfig } from '@opentelemetry/instrumentation-fetch'; + +export interface CustomFetchInstrumentationConfig extends FetchInstrumentationConfig { + excludeParameterKeys?: string[]; +} + +type ExposedFetchSuper = { + _createSpan(url: string, options: Partial): api.Span | undefined; +} + +export class CustomFetchInstrumentation extends FetchInstrumentation { + + private readonly excludeKeys: string[] = []; + + constructor(config: CustomFetchInstrumentationConfig = {}) { + super(config); + this.excludeKeys = config.excludeParameterKeys; + + const exposedSuper = this as any as ExposedFetchSuper; + const _superStartSpan: ExposedFetchSuper['_createSpan'] = exposedSuper._createSpan.bind(this); + + exposedSuper._createSpan = (url, options = {}) => { + const span = _superStartSpan(url, options); + + addUrlParams(span, url, this.excludeKeys); + + return span; + } + } +} + + + + diff --git a/src/impl/instrumentation/urlParams.ts b/src/impl/instrumentation/urlParams.ts new file mode 100644 index 0000000..044c32a --- /dev/null +++ b/src/impl/instrumentation/urlParams.ts @@ -0,0 +1,15 @@ +import { Span } from '@opentelemetry/api'; + +export function addUrlParams(span: Span, url: string, excludeKeys: string[]){ + const urlParams = url.split("?")[1]; + + if(urlParams) { + const keyValuePairs = urlParams.split("&"); + for(const keyValue of keyValuePairs) { + const [key, value] = keyValue.split("="); + span.setAttribute(key, value); + + if(!this.excludeKeys.includes(key)) window.BOOMR.addVar(key, value); + } + } +} diff --git a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts index 40f33f6..1e0934d 100644 --- a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts +++ b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts @@ -1,11 +1,14 @@ -import { InstrumentationConfig } from '@opentelemetry/instrumentation'; import * as api from '@opentelemetry/api'; import { XMLHttpRequestInstrumentation, XMLHttpRequestInstrumentationConfig } from '@opentelemetry/instrumentation-xml-http-request'; import { Span } from '@opentelemetry/api'; +import { addUrlParams } from './urlParams'; +export interface CustomXMLHttpRequestInstrumentationConfig extends XMLHttpRequestInstrumentationConfig { + excludeParameterKeys?: string[]; +} type ExposedXHRSuper = { _createSpan(xhr: XMLHttpRequest, url: string, method: string): api.Span | undefined; @@ -13,8 +16,11 @@ type ExposedXHRSuper = { export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumentation { - constructor(config: XMLHttpRequestInstrumentationConfig & InstrumentationConfig = {}) { + private readonly excludeKeys: string[] = []; + + constructor(config: CustomXMLHttpRequestInstrumentationConfig = {}) { super(config); + this.excludeKeys = config.excludeParameterKeys; const exposedSuper = this as any as ExposedXHRSuper; const _superStartSpan: ExposedXHRSuper['_createSpan'] = exposedSuper._createSpan.bind(this); @@ -22,23 +28,11 @@ export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumen exposedSuper._createSpan = (xhr, url, method) => { const span = _superStartSpan(xhr, url, method); - this.addUrlParamsToSpan(span, url); + addUrlParams(span, url, this.excludeKeys); return span; } } - - private addUrlParamsToSpan(span: Span, url: string){ - const urlParams = url.split("?")[1]; - - if(urlParams) { - const keyValuePairs = urlParams.split("&"); - for(const keyValue of keyValuePairs) { - const [key, value] = keyValue.split("="); - span.setAttribute(key, value); - } - } - } } diff --git a/src/types.d.ts b/src/types.d.ts index 1677b73..a834968 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,9 +1,9 @@ import { PropagateTraceHeaderCorsUrls } from '@opentelemetry/sdk-trace-web'; import { CollectorExporterNodeConfigBase } from '@opentelemetry/exporter-collector'; -import { FetchInstrumentationConfig } from "@opentelemetry/instrumentation-fetch"; -import { XMLHttpRequestInstrumentationConfig } from "@opentelemetry/instrumentation-xml-http-request"; -import { DocumentLoadServerTimingInstrumentationConfig } from './impl/documentLoadInstrumentation'; +import { DocumentLoadServerTimingInstrumentationConfig } from './impl/instrumentation/documentLoadInstrumentation'; import { InstrumentationConfig } from "@opentelemetry/instrumentation"; +import { CustomXMLHttpRequestInstrumentationConfig } from './impl/instrumentation/xmlHttpRequestInstrumentation'; +import { CustomFetchInstrumentationConfig } from './impl/instrumentation/fetchInstrumentation'; export interface PluginProperties { samplingRate: number; @@ -37,8 +37,8 @@ export interface OTPluginProperties { } export interface OTPluginConfig { - instrument_fetch: FetchInstrumentationConfig; - instrument_xhr: XMLHttpRequestInstrumentationConfig; + instrument_fetch: CustomFetchInstrumentationConfig; + instrument_xhr: CustomXMLHttpRequestInstrumentationConfig; instrument_document_load: DocumentLoadServerTimingInstrumentationConfig; instrument_user_interaction: InstrumentationConfig; } From 28b43e160ad774471758e129c0c0cf02ddf8e435 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Wed, 20 Sep 2023 11:01:42 +0200 Subject: [PATCH 04/15] add check for excludeParameterKeys --- .../instrumentation/documentLoadInstrumentation.ts | 14 ++++++++------ src/impl/instrumentation/fetchInstrumentation.ts | 7 +++++-- src/impl/instrumentation/urlParams.ts | 2 +- .../xmlHttpRequestInstrumentation.ts | 8 +++++--- src/types.d.ts | 4 ++-- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/impl/instrumentation/documentLoadInstrumentation.ts b/src/impl/instrumentation/documentLoadInstrumentation.ts index 4bded57..d156b59 100644 --- a/src/impl/instrumentation/documentLoadInstrumentation.ts +++ b/src/impl/instrumentation/documentLoadInstrumentation.ts @@ -8,10 +8,9 @@ import { Span, Tracer } from '@opentelemetry/sdk-trace-base'; import { isTracingSuppressed } from '@opentelemetry/core/build/src/trace/suppress-tracing' import { sanitizeAttributes } from '@opentelemetry/core/build/src/common/attributes'; import { TransactionSpanManager } from '../transaction/transactionSpanManager'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { addUrlParams } from './urlParams'; -export interface DocumentLoadServerTimingInstrumentationConfig extends InstrumentationConfig { +export interface CustomDocumentLoadInstrumentationConfig extends InstrumentationConfig { recordTransaction: boolean; exporterDelay: number; } @@ -20,7 +19,7 @@ export interface DocumentLoadServerTimingInstrumentationConfig extends Instrumen * Patch the Tracer class to use the transaction span as root span */ export function patchTracer() { - // Overwrite startSpan in Tracer class + // Overwrite startSpan() in Tracer class // Copy of the original startSpan()-function with additional logic inside the function to determine the parentContext Tracer.prototype.startSpan = function ( name: string, @@ -126,12 +125,15 @@ export class DocumentLoadServerTimingInstrumentation extends DocumentLoadInstrum readonly component: string = 'document-load-server-timing'; moduleName = this.component; - constructor(config: DocumentLoadServerTimingInstrumentationConfig) { + constructor(config: CustomDocumentLoadInstrumentationConfig) { super(config); + + //Store original functions in variables const exposedSuper = this as any as ExposedDocumentLoadSuper; const _superStartSpan: ExposedDocumentLoadSuper['_startSpan'] = exposedSuper._startSpan.bind(this); const _superEndSpan: ExposedDocumentLoadSuper['_endSpan'] = exposedSuper._endSpan.bind(this); + //Override function exposedSuper._startSpan = (spanName, performanceName, entries, parentSpan) => { if (!(entries as PerformanceEntriesWithServerTiming).serverTiming && performance.getEntriesByType) { const navEntries = performance.getEntriesByType('navigation'); @@ -141,8 +143,8 @@ export class DocumentLoadServerTimingInstrumentation extends DocumentLoadInstrum (entries as PerformanceEntriesWithServerTiming).serverTiming = navEntries[0].serverTiming; } } - captureTraceParentFromPerformanceEntries(entries); + const span = _superStartSpan(spanName, performanceName, entries, parentSpan); const exposedSpan = span as any as Span; if(exposedSpan.name == "documentLoad") TransactionSpanManager.setTransactionSpan(span); @@ -152,6 +154,7 @@ export class DocumentLoadServerTimingInstrumentation extends DocumentLoadInstrum return span; } + //Override function exposedSuper._endSpan = (span, performanceName, entries) => { const transactionSpan = TransactionSpanManager.getTransactionSpan(); @@ -162,5 +165,4 @@ export class DocumentLoadServerTimingInstrumentation extends DocumentLoadInstrum return _superEndSpan(span, performanceName, entries); }; } - } diff --git a/src/impl/instrumentation/fetchInstrumentation.ts b/src/impl/instrumentation/fetchInstrumentation.ts index 9db3596..5d3b8a4 100644 --- a/src/impl/instrumentation/fetchInstrumentation.ts +++ b/src/impl/instrumentation/fetchInstrumentation.ts @@ -16,14 +16,17 @@ export class CustomFetchInstrumentation extends FetchInstrumentation { constructor(config: CustomFetchInstrumentationConfig = {}) { super(config); - this.excludeKeys = config.excludeParameterKeys; + if(config.excludeParameterKeys) + this.excludeKeys = config.excludeParameterKeys; + + //Store original function in variable const exposedSuper = this as any as ExposedFetchSuper; const _superStartSpan: ExposedFetchSuper['_createSpan'] = exposedSuper._createSpan.bind(this); + //Override function exposedSuper._createSpan = (url, options = {}) => { const span = _superStartSpan(url, options); - addUrlParams(span, url, this.excludeKeys); return span; diff --git a/src/impl/instrumentation/urlParams.ts b/src/impl/instrumentation/urlParams.ts index 044c32a..8f0dea0 100644 --- a/src/impl/instrumentation/urlParams.ts +++ b/src/impl/instrumentation/urlParams.ts @@ -9,7 +9,7 @@ export function addUrlParams(span: Span, url: string, excludeKeys: string[]){ const [key, value] = keyValue.split("="); span.setAttribute(key, value); - if(!this.excludeKeys.includes(key)) window.BOOMR.addVar(key, value); + if(!excludeKeys.includes(key)) window.BOOMR.addVar(key, value); } } } diff --git a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts index 1e0934d..0652c67 100644 --- a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts +++ b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts @@ -3,7 +3,6 @@ import { XMLHttpRequestInstrumentation, XMLHttpRequestInstrumentationConfig } from '@opentelemetry/instrumentation-xml-http-request'; -import { Span } from '@opentelemetry/api'; import { addUrlParams } from './urlParams'; export interface CustomXMLHttpRequestInstrumentationConfig extends XMLHttpRequestInstrumentationConfig { @@ -20,14 +19,17 @@ export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumen constructor(config: CustomXMLHttpRequestInstrumentationConfig = {}) { super(config); - this.excludeKeys = config.excludeParameterKeys; + if(config.excludeParameterKeys) + this.excludeKeys = config.excludeParameterKeys; + + //Store original function in variable const exposedSuper = this as any as ExposedXHRSuper; const _superStartSpan: ExposedXHRSuper['_createSpan'] = exposedSuper._createSpan.bind(this); + //Override function exposedSuper._createSpan = (xhr, url, method) => { const span = _superStartSpan(xhr, url, method); - addUrlParams(span, url, this.excludeKeys); return span; diff --git a/src/types.d.ts b/src/types.d.ts index a834968..5da9e86 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,6 +1,6 @@ import { PropagateTraceHeaderCorsUrls } from '@opentelemetry/sdk-trace-web'; import { CollectorExporterNodeConfigBase } from '@opentelemetry/exporter-collector'; -import { DocumentLoadServerTimingInstrumentationConfig } from './impl/instrumentation/documentLoadInstrumentation'; +import { CustomDocumentLoadInstrumentationConfig } from './impl/instrumentation/documentLoadInstrumentation'; import { InstrumentationConfig } from "@opentelemetry/instrumentation"; import { CustomXMLHttpRequestInstrumentationConfig } from './impl/instrumentation/xmlHttpRequestInstrumentation'; import { CustomFetchInstrumentationConfig } from './impl/instrumentation/fetchInstrumentation'; @@ -39,7 +39,7 @@ export interface OTPluginProperties { export interface OTPluginConfig { instrument_fetch: CustomFetchInstrumentationConfig; instrument_xhr: CustomXMLHttpRequestInstrumentationConfig; - instrument_document_load: DocumentLoadServerTimingInstrumentationConfig; + instrument_document_load: CustomDocumentLoadInstrumentationConfig; instrument_user_interaction: InstrumentationConfig; } From 051742c28f79b35c8f196a995d8ab9b56c384cf1 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Wed, 20 Sep 2023 11:30:55 +0200 Subject: [PATCH 05/15] refactor documentLoadInstrumentation --- src/impl/index.ts | 12 +-- .../documentLoadInstrumentation.ts | 77 ++++++++++++------- .../instrumentation/fetchInstrumentation.ts | 6 +- .../xmlHttpRequestInstrumentation.ts | 6 +- 4 files changed, 61 insertions(+), 40 deletions(-) diff --git a/src/impl/index.ts b/src/impl/index.ts index 1b94d99..9bd9c7a 100644 --- a/src/impl/index.ts +++ b/src/impl/index.ts @@ -24,14 +24,12 @@ import { import { Resource } from '@opentelemetry/resources'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { B3InjectEncoding, B3Propagator } from '@opentelemetry/propagator-b3'; -import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request'; -import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch'; import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load'; import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction'; import { PluginProperties, ContextFunction, PropagationHeader } from '../types'; import { patchExporter, patchExporterClass } from './patchCollectorPrototype'; import { MultiSpanProcessor, CustomSpanProcessor } from './spanProcessing'; -import { DocumentLoadServerTimingInstrumentation, patchTracer } from './instrumentation/documentLoadInstrumentation'; +import { CustomDocumentLoadInstrumentation, patchTracer } from './instrumentation/documentLoadInstrumentation'; import { CustomIdGenerator } from './transaction/transactionIdGeneration'; import { TransactionSpanManager } from './transaction/transactionSpanManager'; import { CustomXMLHttpRequestInstrumentation } from './instrumentation/xmlHttpRequestInstrumentation'; @@ -77,7 +75,8 @@ export default class OpenTelemetryTracingImpl { enabled: false, path: "", recordTransaction: false, - exporterDelay: 20 + exporterDelay: 20, + excludeParameterKeys: [] }, instrument_user_interaction: { enabled: false, @@ -311,10 +310,7 @@ export default class OpenTelemetryTracingImpl { // Instrumentation for the document on load (initial request) if (plugins_config?.instrument_document_load?.enabled !== false) { - if(this.isTransactionRecordingEnabled()) - instrumentations.push(new DocumentLoadServerTimingInstrumentation(plugins_config.instrument_document_load)); - else - instrumentations.push(new DocumentLoadInstrumentation(plugins_config.instrument_document_load)); + instrumentations.push(new CustomDocumentLoadInstrumentation(plugins_config.instrument_document_load)); } else if (plugins?.instrument_document_load !== false) { instrumentations.push(new DocumentLoadInstrumentation()); diff --git a/src/impl/instrumentation/documentLoadInstrumentation.ts b/src/impl/instrumentation/documentLoadInstrumentation.ts index d156b59..43a3934 100644 --- a/src/impl/instrumentation/documentLoadInstrumentation.ts +++ b/src/impl/instrumentation/documentLoadInstrumentation.ts @@ -11,12 +11,14 @@ import { TransactionSpanManager } from '../transaction/transactionSpanManager'; import { addUrlParams } from './urlParams'; export interface CustomDocumentLoadInstrumentationConfig extends InstrumentationConfig { - recordTransaction: boolean; - exporterDelay: number; + recordTransaction?: boolean; + exporterDelay?: number; + excludeParameterKeys?: string[]; } /** * Patch the Tracer class to use the transaction span as root span + * OpenTelemetry version: 0.25.0 */ export function patchTracer() { // Overwrite startSpan() in Tracer class @@ -121,48 +123,71 @@ type ExposedDocumentLoadSuper = { _endSpan(span: api.Span | undefined, performanceName: string, entries: PerformanceEntries): void; } -export class DocumentLoadServerTimingInstrumentation extends DocumentLoadInstrumentation { +export class CustomDocumentLoadInstrumentation extends DocumentLoadInstrumentation { readonly component: string = 'document-load-server-timing'; moduleName = this.component; + // Per default transaction should not be recorded + private recordTransaction = false; + + private excludeUrlKeys: string[] = []; + constructor(config: CustomDocumentLoadInstrumentationConfig) { super(config); + if(config.excludeParameterKeys) + this.excludeUrlKeys = config.excludeParameterKeys; + + if(config.recordTransaction) + this.recordTransaction = config.recordTransaction; + //Store original functions in variables const exposedSuper = this as any as ExposedDocumentLoadSuper; const _superStartSpan: ExposedDocumentLoadSuper['_startSpan'] = exposedSuper._startSpan.bind(this); const _superEndSpan: ExposedDocumentLoadSuper['_endSpan'] = exposedSuper._endSpan.bind(this); - //Override function - exposedSuper._startSpan = (spanName, performanceName, entries, parentSpan) => { - if (!(entries as PerformanceEntriesWithServerTiming).serverTiming && performance.getEntriesByType) { - const navEntries = performance.getEntriesByType('navigation'); - // @ts-ignore - if (navEntries[0]?.serverTiming) { + if(this.recordTransaction) { + //Override function + exposedSuper._startSpan = (spanName, performanceName, entries, parentSpan) => { + if (!(entries as PerformanceEntriesWithServerTiming).serverTiming && performance.getEntriesByType) { + const navEntries = performance.getEntriesByType('navigation'); // @ts-ignore - (entries as PerformanceEntriesWithServerTiming).serverTiming = navEntries[0].serverTiming; + if (navEntries[0]?.serverTiming) { + // @ts-ignore + (entries as PerformanceEntriesWithServerTiming).serverTiming = navEntries[0].serverTiming; + } } - } - captureTraceParentFromPerformanceEntries(entries); + captureTraceParentFromPerformanceEntries(entries); - const span = _superStartSpan(spanName, performanceName, entries, parentSpan); - const exposedSpan = span as any as Span; - if(exposedSpan.name == "documentLoad") TransactionSpanManager.setTransactionSpan(span); + const span = _superStartSpan(spanName, performanceName, entries, parentSpan); + const exposedSpan = span as any as Span; + if(exposedSpan.name == "documentLoad") TransactionSpanManager.setTransactionSpan(span); - addUrlParams(span, location.href, []); + addUrlParams(span, location.href, this.excludeUrlKeys); - return span; - } + return span; + } - //Override function - exposedSuper._endSpan = (span, performanceName, entries) => { + //Override function + exposedSuper._endSpan = (span, performanceName, entries) => { - const transactionSpan = TransactionSpanManager.getTransactionSpan(); - // Don't close transactionSpan - // transactionSpan will be closed through "beforeunload"-event - if(transactionSpan && transactionSpan == span) return; + const transactionSpan = TransactionSpanManager.getTransactionSpan(); + // Don't close transactionSpan + // transactionSpan will be closed through "beforeunload"-event + if(transactionSpan && transactionSpan == span) return; - return _superEndSpan(span, performanceName, entries); - }; + return _superEndSpan(span, performanceName, entries); + }; + } + else { + //Override function + exposedSuper._startSpan = (spanName, performanceName, entries, parentSpan) => { + const span = _superStartSpan(spanName, performanceName, entries, parentSpan); + const exposedSpan = span as any as Span; + if(exposedSpan.name == "documentLoad") addUrlParams(span, location.href, this.excludeUrlKeys); + + return span; + } + } } } diff --git a/src/impl/instrumentation/fetchInstrumentation.ts b/src/impl/instrumentation/fetchInstrumentation.ts index 5d3b8a4..87660e1 100644 --- a/src/impl/instrumentation/fetchInstrumentation.ts +++ b/src/impl/instrumentation/fetchInstrumentation.ts @@ -12,13 +12,13 @@ type ExposedFetchSuper = { export class CustomFetchInstrumentation extends FetchInstrumentation { - private readonly excludeKeys: string[] = []; + private excludeUrlKeys: string[] = []; constructor(config: CustomFetchInstrumentationConfig = {}) { super(config); if(config.excludeParameterKeys) - this.excludeKeys = config.excludeParameterKeys; + this.excludeUrlKeys = config.excludeParameterKeys; //Store original function in variable const exposedSuper = this as any as ExposedFetchSuper; @@ -27,7 +27,7 @@ export class CustomFetchInstrumentation extends FetchInstrumentation { //Override function exposedSuper._createSpan = (url, options = {}) => { const span = _superStartSpan(url, options); - addUrlParams(span, url, this.excludeKeys); + addUrlParams(span, url, this.excludeUrlKeys); return span; } diff --git a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts index 0652c67..f60a427 100644 --- a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts +++ b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts @@ -15,13 +15,13 @@ type ExposedXHRSuper = { export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumentation { - private readonly excludeKeys: string[] = []; + private excludeUrlKeys: string[] = []; constructor(config: CustomXMLHttpRequestInstrumentationConfig = {}) { super(config); if(config.excludeParameterKeys) - this.excludeKeys = config.excludeParameterKeys; + this.excludeUrlKeys = config.excludeParameterKeys; //Store original function in variable const exposedSuper = this as any as ExposedXHRSuper; @@ -30,7 +30,7 @@ export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumen //Override function exposedSuper._createSpan = (xhr, url, method) => { const span = _superStartSpan(xhr, url, method); - addUrlParams(span, url, this.excludeKeys); + addUrlParams(span, url, this.excludeUrlKeys); return span; } From 191e9bc659d70efda5373525b9bfc4320494bd70 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Thu, 21 Sep 2023 11:44:53 +0200 Subject: [PATCH 06/15] add userInteractionInstrumentation --- src/impl/index.ts | 34 ++++++++--------- .../documentLoadInstrumentation.ts | 12 +++--- .../instrumentation/fetchInstrumentation.ts | 13 +++---- src/impl/instrumentation/urlParams.ts | 6 +++ .../userInteractionInstrumentation.ts | 38 +++++++++++++++++++ .../xmlHttpRequestInstrumentation.ts | 13 +++---- src/types.d.ts | 14 +++++-- 7 files changed, 87 insertions(+), 43 deletions(-) create mode 100644 src/impl/instrumentation/userInteractionInstrumentation.ts diff --git a/src/impl/index.ts b/src/impl/index.ts index 9bd9c7a..bb9690d 100644 --- a/src/impl/index.ts +++ b/src/impl/index.ts @@ -24,8 +24,6 @@ import { import { Resource } from '@opentelemetry/resources'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { B3InjectEncoding, B3Propagator } from '@opentelemetry/propagator-b3'; -import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load'; -import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction'; import { PluginProperties, ContextFunction, PropagationHeader } from '../types'; import { patchExporter, patchExporterClass } from './patchCollectorPrototype'; import { MultiSpanProcessor, CustomSpanProcessor } from './spanProcessing'; @@ -34,6 +32,7 @@ import { CustomIdGenerator } from './transaction/transactionIdGeneration'; import { TransactionSpanManager } from './transaction/transactionSpanManager'; import { CustomXMLHttpRequestInstrumentation } from './instrumentation/xmlHttpRequestInstrumentation'; import { CustomFetchInstrumentation } from './instrumentation/fetchInstrumentation'; +import { CustomUserInteractionInstrumentation } from './instrumentation/userInteractionInstrumentation'; /** * TODOs: @@ -59,8 +58,7 @@ export default class OpenTelemetryTracingImpl { path: "", applyCustomAttributesOnSpan: null, //(span: Span, request: Request) => { }, ignoreUrls: [], - propagateTraceHeaderCorsUrls: [], - excludeParameterKeys: [] + propagateTraceHeaderCorsUrls: [] }, instrument_xhr: { enabled: false, @@ -68,21 +66,23 @@ export default class OpenTelemetryTracingImpl { applyCustomAttributesOnSpan: null, // (span: Span, xhr: XMLHttpRequest) => { }, propagateTraceHeaderCorsUrls: [], ignoreUrls: [], - clearTimingResources: false, - excludeParameterKeys: [] + clearTimingResources: false }, instrument_document_load: { enabled: false, path: "", recordTransaction: false, - exporterDelay: 20, - excludeParameterKeys: [] + exporterDelay: 20 }, instrument_user_interaction: { enabled: false, path: "", }, }, + includeRequestParameter: { + enabled: false, + excludeKeys: [] + }, exporter: { maxQueueSize: 100, maxExportBatchSize: 10, @@ -305,43 +305,43 @@ export default class OpenTelemetryTracingImpl { }; private getInstrumentationPlugins = () => { - const { plugins, corsUrls, plugins_config } = this.props; + const { plugins, corsUrls, plugins_config, includeRequestParameter } = this.props; const instrumentations: any = []; // Instrumentation for the document on load (initial request) if (plugins_config?.instrument_document_load?.enabled !== false) { - instrumentations.push(new CustomDocumentLoadInstrumentation(plugins_config.instrument_document_load)); + instrumentations.push(new CustomDocumentLoadInstrumentation(plugins_config.instrument_document_load, includeRequestParameter)); } else if (plugins?.instrument_document_load !== false) { - instrumentations.push(new DocumentLoadInstrumentation()); + instrumentations.push(new CustomDocumentLoadInstrumentation({}, includeRequestParameter)); } // Instrumentation for user interactions if (plugins_config?.instrument_user_interaction?.enabled !== false) { - instrumentations.push(new UserInteractionInstrumentation(plugins_config.instrument_user_interaction)); + instrumentations.push(new CustomUserInteractionInstrumentation(plugins_config.instrument_user_interaction, includeRequestParameter)); } else if (plugins?.instrument_user_interaction !== false) { - instrumentations.push(new UserInteractionInstrumentation()); + instrumentations.push(new CustomUserInteractionInstrumentation({}, includeRequestParameter)); } // XMLHttpRequest Instrumentation for web plugin if (plugins_config?.instrument_xhr?.enabled !== false) { - instrumentations.push(new CustomXMLHttpRequestInstrumentation(plugins_config.instrument_xhr)); + instrumentations.push(new CustomXMLHttpRequestInstrumentation(plugins_config.instrument_xhr, includeRequestParameter)); } else if (plugins?.instrument_xhr !== false) { instrumentations.push( new CustomXMLHttpRequestInstrumentation({ propagateTraceHeaderCorsUrls: corsUrls - }) + }, includeRequestParameter) ); } // Instrumentation for the fetch API if available const isFetchAPISupported = 'fetch' in window; if (isFetchAPISupported && plugins_config?.instrument_fetch?.enabled !== false) { - instrumentations.push(new CustomFetchInstrumentation(plugins_config.instrument_fetch)); + instrumentations.push(new CustomFetchInstrumentation(plugins_config.instrument_fetch, includeRequestParameter)); } else if (isFetchAPISupported && plugins?.instrument_fetch !== false) { - instrumentations.push(new CustomFetchInstrumentation()); + instrumentations.push(new CustomFetchInstrumentation({}, includeRequestParameter)); } return instrumentations; diff --git a/src/impl/instrumentation/documentLoadInstrumentation.ts b/src/impl/instrumentation/documentLoadInstrumentation.ts index 43a3934..b54f87c 100644 --- a/src/impl/instrumentation/documentLoadInstrumentation.ts +++ b/src/impl/instrumentation/documentLoadInstrumentation.ts @@ -9,11 +9,11 @@ import { isTracingSuppressed } from '@opentelemetry/core/build/src/trace/suppres import { sanitizeAttributes } from '@opentelemetry/core/build/src/common/attributes'; import { TransactionSpanManager } from '../transaction/transactionSpanManager'; import { addUrlParams } from './urlParams'; +import { RequestParameterConfig } from '../../types'; export interface CustomDocumentLoadInstrumentationConfig extends InstrumentationConfig { recordTransaction?: boolean; exporterDelay?: number; - excludeParameterKeys?: string[]; } /** @@ -130,17 +130,17 @@ export class CustomDocumentLoadInstrumentation extends DocumentLoadInstrumentati // Per default transaction should not be recorded private recordTransaction = false; - private excludeUrlKeys: string[] = []; + private readonly excludeUrlKeys: string[] = []; - constructor(config: CustomDocumentLoadInstrumentationConfig) { + constructor(config: CustomDocumentLoadInstrumentationConfig = {}, requestParameterConfig: RequestParameterConfig) { super(config); - if(config.excludeParameterKeys) - this.excludeUrlKeys = config.excludeParameterKeys; - if(config.recordTransaction) this.recordTransaction = config.recordTransaction; + if(requestParameterConfig.enabled) + this.excludeUrlKeys = requestParameterConfig.excludeKeys; + //Store original functions in variables const exposedSuper = this as any as ExposedDocumentLoadSuper; const _superStartSpan: ExposedDocumentLoadSuper['_startSpan'] = exposedSuper._startSpan.bind(this); diff --git a/src/impl/instrumentation/fetchInstrumentation.ts b/src/impl/instrumentation/fetchInstrumentation.ts index 87660e1..0049adf 100644 --- a/src/impl/instrumentation/fetchInstrumentation.ts +++ b/src/impl/instrumentation/fetchInstrumentation.ts @@ -1,10 +1,7 @@ import * as api from '@opentelemetry/api'; import { addUrlParams } from './urlParams'; import { FetchInstrumentation, FetchInstrumentationConfig } from '@opentelemetry/instrumentation-fetch'; - -export interface CustomFetchInstrumentationConfig extends FetchInstrumentationConfig { - excludeParameterKeys?: string[]; -} +import { RequestParameterConfig } from '../../types'; type ExposedFetchSuper = { _createSpan(url: string, options: Partial): api.Span | undefined; @@ -12,13 +9,13 @@ type ExposedFetchSuper = { export class CustomFetchInstrumentation extends FetchInstrumentation { - private excludeUrlKeys: string[] = []; + private readonly excludeUrlKeys: string[] = []; - constructor(config: CustomFetchInstrumentationConfig = {}) { + constructor(config: FetchInstrumentationConfig = {}, requestParameterConfig: RequestParameterConfig) { super(config); - if(config.excludeParameterKeys) - this.excludeUrlKeys = config.excludeParameterKeys; + if(requestParameterConfig.enabled) + this.excludeUrlKeys = requestParameterConfig.excludeKeys; //Store original function in variable const exposedSuper = this as any as ExposedFetchSuper; diff --git a/src/impl/instrumentation/urlParams.ts b/src/impl/instrumentation/urlParams.ts index 8f0dea0..97687c5 100644 --- a/src/impl/instrumentation/urlParams.ts +++ b/src/impl/instrumentation/urlParams.ts @@ -1,5 +1,11 @@ import { Span } from '@opentelemetry/api'; +/** + * Add url parameters to spans and if not excluded also to the current beacon + * @param span current span + * @param url complete request url + * @param excludeKeys list of keys, which should not be written to beacons + */ export function addUrlParams(span: Span, url: string, excludeKeys: string[]){ const urlParams = url.split("?")[1]; diff --git a/src/impl/instrumentation/userInteractionInstrumentation.ts b/src/impl/instrumentation/userInteractionInstrumentation.ts new file mode 100644 index 0000000..be9eab6 --- /dev/null +++ b/src/impl/instrumentation/userInteractionInstrumentation.ts @@ -0,0 +1,38 @@ +import * as api from '@opentelemetry/api'; +import { addUrlParams } from './urlParams'; +import { FetchInstrumentation, FetchInstrumentationConfig } from '@opentelemetry/instrumentation-fetch'; +import { RequestParameterConfig } from '../../types'; +import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction'; +import { InstrumentationConfig } from '@opentelemetry/instrumentation'; + +type ExposedUserInteractionSuper = { + _createSpan(element: EventTarget | null | undefined, eventName: string, parentSpan?: api.Span | undefined): api.Span | undefined; +} + +export class CustomUserInteractionInstrumentation extends UserInteractionInstrumentation { + + private readonly excludeUrlKeys: string[] = []; + + constructor(config: InstrumentationConfig = {}, requestParameterConfig: RequestParameterConfig) { + super(config); + + if(requestParameterConfig.enabled) + this.excludeUrlKeys = requestParameterConfig.excludeKeys; + + //Store original function in variable + const exposedSuper = this as any as ExposedUserInteractionSuper; + const _superStartSpan: ExposedUserInteractionSuper['_createSpan'] = exposedSuper._createSpan.bind(this); + + //Override function + exposedSuper._createSpan = (element, eventName, parentSpan) => { + const span = _superStartSpan(element, eventName, parentSpan); + addUrlParams(span, location.href, this.excludeUrlKeys); + + return span; + } + } +} + + + + diff --git a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts index f60a427..b0dba84 100644 --- a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts +++ b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts @@ -4,10 +4,7 @@ import { XMLHttpRequestInstrumentationConfig } from '@opentelemetry/instrumentation-xml-http-request'; import { addUrlParams } from './urlParams'; - -export interface CustomXMLHttpRequestInstrumentationConfig extends XMLHttpRequestInstrumentationConfig { - excludeParameterKeys?: string[]; -} +import { RequestParameterConfig } from '../../types'; type ExposedXHRSuper = { _createSpan(xhr: XMLHttpRequest, url: string, method: string): api.Span | undefined; @@ -15,13 +12,13 @@ type ExposedXHRSuper = { export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumentation { - private excludeUrlKeys: string[] = []; + private readonly excludeUrlKeys: string[] = []; - constructor(config: CustomXMLHttpRequestInstrumentationConfig = {}) { + constructor(config: XMLHttpRequestInstrumentationConfig = {}, requestParameterConfig: RequestParameterConfig) { super(config); - if(config.excludeParameterKeys) - this.excludeUrlKeys = config.excludeParameterKeys; + if(requestParameterConfig.enabled) + this.excludeUrlKeys = requestParameterConfig.excludeKeys; //Store original function in variable const exposedSuper = this as any as ExposedXHRSuper; diff --git a/src/types.d.ts b/src/types.d.ts index 5da9e86..e70e7d6 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -2,8 +2,8 @@ import { PropagateTraceHeaderCorsUrls } from '@opentelemetry/sdk-trace-web'; import { CollectorExporterNodeConfigBase } from '@opentelemetry/exporter-collector'; import { CustomDocumentLoadInstrumentationConfig } from './impl/instrumentation/documentLoadInstrumentation'; import { InstrumentationConfig } from "@opentelemetry/instrumentation"; -import { CustomXMLHttpRequestInstrumentationConfig } from './impl/instrumentation/xmlHttpRequestInstrumentation'; -import { CustomFetchInstrumentationConfig } from './impl/instrumentation/fetchInstrumentation'; +import { FetchInstrumentationConfig } from '@opentelemetry/instrumentation-fetch'; +import { XMLHttpRequestInstrumentationConfig } from '@opentelemetry/instrumentation-xml-http-request'; export interface PluginProperties { samplingRate: number; @@ -12,6 +12,7 @@ export interface PluginProperties { consoleOnly: boolean; plugins: OTPluginProperties; plugins_config: OTPluginConfig; + includeRequestParameter: RequestParameterConfig; exporter: OTExportProperties; commonAttributes: StringMap; prototypeExporterPatch: boolean; @@ -37,12 +38,17 @@ export interface OTPluginProperties { } export interface OTPluginConfig { - instrument_fetch: CustomFetchInstrumentationConfig; - instrument_xhr: CustomXMLHttpRequestInstrumentationConfig; + instrument_fetch: FetchInstrumentationConfig; + instrument_xhr: XMLHttpRequestInstrumentationConfig; instrument_document_load: CustomDocumentLoadInstrumentationConfig; instrument_user_interaction: InstrumentationConfig; } +export interface RequestParameterConfig { + enabled?: boolean; + excludeKeys: string[]; +} + export interface OTExportProperties { // The maximum queue size. After the size is reached spans are dropped. maxQueueSize: number; From b45259f81170aa5553c37f4f6375506d9063b21e Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Thu, 21 Sep 2023 12:02:52 +0200 Subject: [PATCH 07/15] add check, if span is existing --- src/impl/instrumentation/documentLoadInstrumentation.ts | 4 ++-- src/impl/instrumentation/fetchInstrumentation.ts | 6 +++--- src/impl/instrumentation/userInteractionInstrumentation.ts | 7 ++++--- src/impl/instrumentation/xmlHttpRequestInstrumentation.ts | 6 +++--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/impl/instrumentation/documentLoadInstrumentation.ts b/src/impl/instrumentation/documentLoadInstrumentation.ts index b54f87c..f28159f 100644 --- a/src/impl/instrumentation/documentLoadInstrumentation.ts +++ b/src/impl/instrumentation/documentLoadInstrumentation.ts @@ -163,7 +163,7 @@ export class CustomDocumentLoadInstrumentation extends DocumentLoadInstrumentati const exposedSpan = span as any as Span; if(exposedSpan.name == "documentLoad") TransactionSpanManager.setTransactionSpan(span); - addUrlParams(span, location.href, this.excludeUrlKeys); + if(span) addUrlParams(span, location.href, this.excludeUrlKeys); return span; } @@ -184,7 +184,7 @@ export class CustomDocumentLoadInstrumentation extends DocumentLoadInstrumentati exposedSuper._startSpan = (spanName, performanceName, entries, parentSpan) => { const span = _superStartSpan(spanName, performanceName, entries, parentSpan); const exposedSpan = span as any as Span; - if(exposedSpan.name == "documentLoad") addUrlParams(span, location.href, this.excludeUrlKeys); + if(span && exposedSpan.name == "documentLoad") addUrlParams(span, location.href, this.excludeUrlKeys); return span; } diff --git a/src/impl/instrumentation/fetchInstrumentation.ts b/src/impl/instrumentation/fetchInstrumentation.ts index 0049adf..71f6875 100644 --- a/src/impl/instrumentation/fetchInstrumentation.ts +++ b/src/impl/instrumentation/fetchInstrumentation.ts @@ -19,12 +19,12 @@ export class CustomFetchInstrumentation extends FetchInstrumentation { //Store original function in variable const exposedSuper = this as any as ExposedFetchSuper; - const _superStartSpan: ExposedFetchSuper['_createSpan'] = exposedSuper._createSpan.bind(this); + const _superCreateSpan: ExposedFetchSuper['_createSpan'] = exposedSuper._createSpan.bind(this); //Override function exposedSuper._createSpan = (url, options = {}) => { - const span = _superStartSpan(url, options); - addUrlParams(span, url, this.excludeUrlKeys); + const span = _superCreateSpan(url, options); + if(span) addUrlParams(span, url, this.excludeUrlKeys); return span; } diff --git a/src/impl/instrumentation/userInteractionInstrumentation.ts b/src/impl/instrumentation/userInteractionInstrumentation.ts index be9eab6..dc24403 100644 --- a/src/impl/instrumentation/userInteractionInstrumentation.ts +++ b/src/impl/instrumentation/userInteractionInstrumentation.ts @@ -21,12 +21,13 @@ export class CustomUserInteractionInstrumentation extends UserInteractionInstrum //Store original function in variable const exposedSuper = this as any as ExposedUserInteractionSuper; - const _superStartSpan: ExposedUserInteractionSuper['_createSpan'] = exposedSuper._createSpan.bind(this); + const _superCreateSpan: ExposedUserInteractionSuper['_createSpan'] = exposedSuper._createSpan.bind(this); //Override function exposedSuper._createSpan = (element, eventName, parentSpan) => { - const span = _superStartSpan(element, eventName, parentSpan); - addUrlParams(span, location.href, this.excludeUrlKeys); + const span = _superCreateSpan(element, eventName, parentSpan); + + if(span) addUrlParams(span, location.href, this.excludeUrlKeys); return span; } diff --git a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts index b0dba84..17ef0e4 100644 --- a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts +++ b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts @@ -22,12 +22,12 @@ export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumen //Store original function in variable const exposedSuper = this as any as ExposedXHRSuper; - const _superStartSpan: ExposedXHRSuper['_createSpan'] = exposedSuper._createSpan.bind(this); + const _superCreateSpan: ExposedXHRSuper['_createSpan'] = exposedSuper._createSpan.bind(this); //Override function exposedSuper._createSpan = (xhr, url, method) => { - const span = _superStartSpan(xhr, url, method); - addUrlParams(span, url, this.excludeUrlKeys); + const span = _superCreateSpan(xhr, url, method); + if(span) addUrlParams(span, url, this.excludeUrlKeys); return span; } From 6a4da211476228f5d6bdb748c710ea907a17200a Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Tue, 26 Sep 2023 09:33:39 +0200 Subject: [PATCH 08/15] refactor instrumentations --- src/impl/index.ts | 33 +++++++++++-------- .../documentLoadInstrumentation.ts | 21 +++++++----- .../instrumentation/fetchInstrumentation.ts | 16 ++++----- src/impl/instrumentation/urlParams.ts | 2 +- .../userInteractionInstrumentation.ts | 15 ++++----- .../xmlHttpRequestInstrumentation.ts | 16 ++++----- src/types.d.ts | 8 +++-- 7 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/impl/index.ts b/src/impl/index.ts index bb9690d..2a50bdc 100644 --- a/src/impl/index.ts +++ b/src/impl/index.ts @@ -79,9 +79,11 @@ export default class OpenTelemetryTracingImpl { path: "", }, }, - includeRequestParameter: { - enabled: false, - excludeKeys: [] + global_instrumentation: { + requestParameter: { + enabled: false, + excludeKeysFromBeacons: [] + } }, exporter: { maxQueueSize: 100, @@ -305,43 +307,46 @@ export default class OpenTelemetryTracingImpl { }; private getInstrumentationPlugins = () => { - const { plugins, corsUrls, plugins_config, includeRequestParameter } = this.props; + const { + plugins, + corsUrls, + plugins_config, + global_instrumentation + } = this.props; const instrumentations: any = []; // Instrumentation for the document on load (initial request) if (plugins_config?.instrument_document_load?.enabled !== false) { - instrumentations.push(new CustomDocumentLoadInstrumentation(plugins_config.instrument_document_load, includeRequestParameter)); + instrumentations.push(new CustomDocumentLoadInstrumentation(plugins_config.instrument_document_load, global_instrumentation)); } else if (plugins?.instrument_document_load !== false) { - instrumentations.push(new CustomDocumentLoadInstrumentation({}, includeRequestParameter)); + instrumentations.push(new CustomDocumentLoadInstrumentation({}, global_instrumentation)); } // Instrumentation for user interactions if (plugins_config?.instrument_user_interaction?.enabled !== false) { - instrumentations.push(new CustomUserInteractionInstrumentation(plugins_config.instrument_user_interaction, includeRequestParameter)); + instrumentations.push(new CustomUserInteractionInstrumentation(plugins_config.instrument_user_interaction, global_instrumentation)); } else if (plugins?.instrument_user_interaction !== false) { - instrumentations.push(new CustomUserInteractionInstrumentation({}, includeRequestParameter)); + instrumentations.push(new CustomUserInteractionInstrumentation({}, global_instrumentation)); } // XMLHttpRequest Instrumentation for web plugin if (plugins_config?.instrument_xhr?.enabled !== false) { - instrumentations.push(new CustomXMLHttpRequestInstrumentation(plugins_config.instrument_xhr, includeRequestParameter)); + instrumentations.push(new CustomXMLHttpRequestInstrumentation(plugins_config.instrument_xhr, global_instrumentation)); } else if (plugins?.instrument_xhr !== false) { instrumentations.push( - new CustomXMLHttpRequestInstrumentation({ - propagateTraceHeaderCorsUrls: corsUrls - }, includeRequestParameter) + new CustomXMLHttpRequestInstrumentation({ propagateTraceHeaderCorsUrls: corsUrls }, global_instrumentation) ); } // Instrumentation for the fetch API if available const isFetchAPISupported = 'fetch' in window; if (isFetchAPISupported && plugins_config?.instrument_fetch?.enabled !== false) { - instrumentations.push(new CustomFetchInstrumentation(plugins_config.instrument_fetch, includeRequestParameter)); + instrumentations.push(new CustomFetchInstrumentation(plugins_config.instrument_fetch, global_instrumentation)); } else if (isFetchAPISupported && plugins?.instrument_fetch !== false) { - instrumentations.push(new CustomFetchInstrumentation({}, includeRequestParameter)); + instrumentations.push(new CustomFetchInstrumentation({}, global_instrumentation)); } return instrumentations; diff --git a/src/impl/instrumentation/documentLoadInstrumentation.ts b/src/impl/instrumentation/documentLoadInstrumentation.ts index f28159f..c22c883 100644 --- a/src/impl/instrumentation/documentLoadInstrumentation.ts +++ b/src/impl/instrumentation/documentLoadInstrumentation.ts @@ -9,7 +9,7 @@ import { isTracingSuppressed } from '@opentelemetry/core/build/src/trace/suppres import { sanitizeAttributes } from '@opentelemetry/core/build/src/common/attributes'; import { TransactionSpanManager } from '../transaction/transactionSpanManager'; import { addUrlParams } from './urlParams'; -import { RequestParameterConfig } from '../../types'; +import { GlobalInstrumentationConfig, RequestParameterConfig } from '../../types'; export interface CustomDocumentLoadInstrumentationConfig extends InstrumentationConfig { recordTransaction?: boolean; @@ -130,17 +130,13 @@ export class CustomDocumentLoadInstrumentation extends DocumentLoadInstrumentati // Per default transaction should not be recorded private recordTransaction = false; - private readonly excludeUrlKeys: string[] = []; - - constructor(config: CustomDocumentLoadInstrumentationConfig = {}, requestParameterConfig: RequestParameterConfig) { + constructor(config: CustomDocumentLoadInstrumentationConfig = {}, globalInstrumentationConfig: GlobalInstrumentationConfig) { super(config); + const { requestParameter} = globalInstrumentationConfig; if(config.recordTransaction) this.recordTransaction = config.recordTransaction; - if(requestParameterConfig.enabled) - this.excludeUrlKeys = requestParameterConfig.excludeKeys; - //Store original functions in variables const exposedSuper = this as any as ExposedDocumentLoadSuper; const _superStartSpan: ExposedDocumentLoadSuper['_startSpan'] = exposedSuper._startSpan.bind(this); @@ -163,7 +159,10 @@ export class CustomDocumentLoadInstrumentation extends DocumentLoadInstrumentati const exposedSpan = span as any as Span; if(exposedSpan.name == "documentLoad") TransactionSpanManager.setTransactionSpan(span); - if(span) addUrlParams(span, location.href, this.excludeUrlKeys); + if(span && exposedSpan.name == "documentLoad" && requestParameter?.enabled) { + if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, location.href, requestParameter.excludeKeysFromBeacons); + else addUrlParams(span, location.href); + } return span; } @@ -184,7 +183,11 @@ export class CustomDocumentLoadInstrumentation extends DocumentLoadInstrumentati exposedSuper._startSpan = (spanName, performanceName, entries, parentSpan) => { const span = _superStartSpan(spanName, performanceName, entries, parentSpan); const exposedSpan = span as any as Span; - if(span && exposedSpan.name == "documentLoad") addUrlParams(span, location.href, this.excludeUrlKeys); + + if(span && exposedSpan.name == "documentLoad" && requestParameter?.enabled) { + if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, location.href, requestParameter.excludeKeysFromBeacons); + else addUrlParams(span, location.href); + } return span; } diff --git a/src/impl/instrumentation/fetchInstrumentation.ts b/src/impl/instrumentation/fetchInstrumentation.ts index 71f6875..93c83a3 100644 --- a/src/impl/instrumentation/fetchInstrumentation.ts +++ b/src/impl/instrumentation/fetchInstrumentation.ts @@ -1,7 +1,7 @@ import * as api from '@opentelemetry/api'; import { addUrlParams } from './urlParams'; import { FetchInstrumentation, FetchInstrumentationConfig } from '@opentelemetry/instrumentation-fetch'; -import { RequestParameterConfig } from '../../types'; +import { GlobalInstrumentationConfig, RequestParameterConfig } from '../../types'; type ExposedFetchSuper = { _createSpan(url: string, options: Partial): api.Span | undefined; @@ -9,13 +9,9 @@ type ExposedFetchSuper = { export class CustomFetchInstrumentation extends FetchInstrumentation { - private readonly excludeUrlKeys: string[] = []; - - constructor(config: FetchInstrumentationConfig = {}, requestParameterConfig: RequestParameterConfig) { + constructor(config: FetchInstrumentationConfig = {}, globalInstrumentationConfig: GlobalInstrumentationConfig) { super(config); - - if(requestParameterConfig.enabled) - this.excludeUrlKeys = requestParameterConfig.excludeKeys; + const { requestParameter} = globalInstrumentationConfig; //Store original function in variable const exposedSuper = this as any as ExposedFetchSuper; @@ -24,7 +20,11 @@ export class CustomFetchInstrumentation extends FetchInstrumentation { //Override function exposedSuper._createSpan = (url, options = {}) => { const span = _superCreateSpan(url, options); - if(span) addUrlParams(span, url, this.excludeUrlKeys); + + if(span && requestParameter?.enabled) { + if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, url, requestParameter.excludeKeysFromBeacons); + else addUrlParams(span, url); + } return span; } diff --git a/src/impl/instrumentation/urlParams.ts b/src/impl/instrumentation/urlParams.ts index 97687c5..6287716 100644 --- a/src/impl/instrumentation/urlParams.ts +++ b/src/impl/instrumentation/urlParams.ts @@ -6,7 +6,7 @@ import { Span } from '@opentelemetry/api'; * @param url complete request url * @param excludeKeys list of keys, which should not be written to beacons */ -export function addUrlParams(span: Span, url: string, excludeKeys: string[]){ +export function addUrlParams(span: Span, url: string, excludeKeys: string[] = []){ const urlParams = url.split("?")[1]; if(urlParams) { diff --git a/src/impl/instrumentation/userInteractionInstrumentation.ts b/src/impl/instrumentation/userInteractionInstrumentation.ts index dc24403..39d5bc0 100644 --- a/src/impl/instrumentation/userInteractionInstrumentation.ts +++ b/src/impl/instrumentation/userInteractionInstrumentation.ts @@ -1,7 +1,7 @@ import * as api from '@opentelemetry/api'; import { addUrlParams } from './urlParams'; import { FetchInstrumentation, FetchInstrumentationConfig } from '@opentelemetry/instrumentation-fetch'; -import { RequestParameterConfig } from '../../types'; +import { GlobalInstrumentationConfig, RequestParameterConfig } from '../../types'; import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction'; import { InstrumentationConfig } from '@opentelemetry/instrumentation'; @@ -11,13 +11,9 @@ type ExposedUserInteractionSuper = { export class CustomUserInteractionInstrumentation extends UserInteractionInstrumentation { - private readonly excludeUrlKeys: string[] = []; - - constructor(config: InstrumentationConfig = {}, requestParameterConfig: RequestParameterConfig) { + constructor(config: InstrumentationConfig = {}, globalInstrumentationConfig: GlobalInstrumentationConfig) { super(config); - - if(requestParameterConfig.enabled) - this.excludeUrlKeys = requestParameterConfig.excludeKeys; + const { requestParameter} = globalInstrumentationConfig; //Store original function in variable const exposedSuper = this as any as ExposedUserInteractionSuper; @@ -27,7 +23,10 @@ export class CustomUserInteractionInstrumentation extends UserInteractionInstrum exposedSuper._createSpan = (element, eventName, parentSpan) => { const span = _superCreateSpan(element, eventName, parentSpan); - if(span) addUrlParams(span, location.href, this.excludeUrlKeys); + if(span && requestParameter?.enabled) { + if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, location.href, requestParameter.excludeKeysFromBeacons); + else addUrlParams(span, location.href); + } return span; } diff --git a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts index 17ef0e4..558951f 100644 --- a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts +++ b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts @@ -4,7 +4,7 @@ import { XMLHttpRequestInstrumentationConfig } from '@opentelemetry/instrumentation-xml-http-request'; import { addUrlParams } from './urlParams'; -import { RequestParameterConfig } from '../../types'; +import { GlobalInstrumentationConfig, RequestParameterConfig } from '../../types'; type ExposedXHRSuper = { _createSpan(xhr: XMLHttpRequest, url: string, method: string): api.Span | undefined; @@ -12,13 +12,9 @@ type ExposedXHRSuper = { export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumentation { - private readonly excludeUrlKeys: string[] = []; - - constructor(config: XMLHttpRequestInstrumentationConfig = {}, requestParameterConfig: RequestParameterConfig) { + constructor(config: XMLHttpRequestInstrumentationConfig = {}, globalInstrumentationConfig: GlobalInstrumentationConfig) { super(config); - - if(requestParameterConfig.enabled) - this.excludeUrlKeys = requestParameterConfig.excludeKeys; + const { requestParameter} = globalInstrumentationConfig; //Store original function in variable const exposedSuper = this as any as ExposedXHRSuper; @@ -27,7 +23,11 @@ export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumen //Override function exposedSuper._createSpan = (xhr, url, method) => { const span = _superCreateSpan(xhr, url, method); - if(span) addUrlParams(span, url, this.excludeUrlKeys); + + if(span && requestParameter?.enabled) { + if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, url, requestParameter.excludeKeysFromBeacons); + else addUrlParams(span, url); + } return span; } diff --git a/src/types.d.ts b/src/types.d.ts index e70e7d6..e1cb394 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -12,7 +12,7 @@ export interface PluginProperties { consoleOnly: boolean; plugins: OTPluginProperties; plugins_config: OTPluginConfig; - includeRequestParameter: RequestParameterConfig; + global_instrumentation: GlobalInstrumentationConfig; exporter: OTExportProperties; commonAttributes: StringMap; prototypeExporterPatch: boolean; @@ -44,9 +44,13 @@ export interface OTPluginConfig { instrument_user_interaction: InstrumentationConfig; } +export interface GlobalInstrumentationConfig { + requestParameter: RequestParameterConfig; +} + export interface RequestParameterConfig { enabled?: boolean; - excludeKeys: string[]; + excludeKeysFromBeacons: string[]; } export interface OTExportProperties { From 812f938e60483c0b55bd7b9049555922f828df24 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Tue, 26 Sep 2023 09:46:19 +0200 Subject: [PATCH 09/15] update README --- README.md | 70 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index c8b0b94..6bd9e86 100755 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ Currently implemented features: * Automatic local context propagation using _Zone Context Manager_. [More details ↗](https://www.npmjs.com/package/@opentelemetry/context-zone) * Exporting collected spans to an OpenTelemetry collector. * Providing access to the OpenTelemtry Tracing-API for manual instrumentation. +* Adding global variables to spans during runtime via _addVarToSpans()_ +* Tracing of the whole transaction with the document load span as root span ### OpenTelemetry Plugins @@ -78,6 +80,8 @@ BOOMR.init({ instrument_document_load: { enabled: false, path: "", + recordTransaction: false, //If true, the transaction will be traced with the document load span as root span + exporterDelay: 20 // Delay to allow the exporter to export the transaction span before page unload }, instrument_user_interaction: { enabled: false, @@ -91,6 +95,14 @@ BOOMR.init({ instrument_document_load: true, instrument_user_interaction: true }, + // Additional instrumentation config, which will be applied to all plugins + global_instrumentation_config: { + // Include request paramater to spans and the corresponding beacons + requestParameter: { + enabled: false, + excludeKeysFromBeacons: [] //Keys, which should not be included in beacons, for instance due to cardinality concerns + } + }, exporter: { maxQueueSize: 100, maxExportBatchSize: 10, @@ -109,26 +121,28 @@ BOOMR.init({ ``` Available options are: -| Option | Description | Default value | -|---|---|---| -| `samplingRate` | Sampling rate to use when collecting spans. Value must be between `0` and `1`. | `1` | -| `corsUrls` | Array of CORS URLs to take into consideration when propagating trace information. By default, CORS URLs are excluded from the propagation. | `[]` | -| `collectorConfiguration` | Object that defines the OpenTelemetry collector configuration, like the URL to send spans to. See [CollectorExporterNodeConfigBase](https://www.npmjs.com/package/@opentelemetry/exporter-collector) interface for all options. | `undefined` | -| `consoleOnly` | If `true` spans will be logged on the console and not sent to the collector endpoint. | `false` | -| `plugins` | Object for enabling and disabling OpenTelemetry plugins. | | -| `plugins.instrument_fetch` | Enabling the [OpenTelemetry plugin](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-fetch) for insturmentation of the fetch API. This will only be used in case the `fetch` API exists. | `true` | -| `plugins.instrument_xhr` | Enabling the [OpenTelemetry plugin](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-xml-http-request) for insturmentation of the XMLHttpRequest API. | `true` | -| `plugins.instrument_document_load` | Enabling the [OpenTelemetry plugin](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/web/opentelemetry-instrumentation-document-load) for insturmentation of the document load (initial request). | `true` | -| `plugins.instrument_user_interaction` | Enabling the [OpenTelemetry plugin](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/web/opentelemetry-instrumentation-user-interaction) for insturmentation of user interactions. | `true` | -| `exporter` | Object for configuring the span exporter. Only used if `consoleOnly` is not enabled. || -| `exporter.maxQueueSize` | The maximum queue size. After the size is reached spans are dropped. | `100` | -| `exporter.maxExportBatchSize` | The maximum batch size of every export. It must be smaller or equal to `maxQueueSize`. | `10` | -| `exporter.scheduledDelayMillis` | The interval between two consecutive exports. | `500` | -| `exporter.exportTimeoutMillis` | How long the export can run before it is cancelled. | `30000` | -| `commonAttributes` | An Object defining common span attributes which will be added to each recorded span. | `{}` | -| `serviceName` | A `string` or function which can be used to set the spans' service name. A function can be defined for dynamically providing the service name, e.g. based on Boomerang values. | `undefined` | -| `prototypeExporterPatch` | Patches the OpenTelemetry collector-span-exporter, so it is compatible with the Prototype framework. This is only necessary and should only be activated, when the Prototype framework is used. [For more information see the linked file](https://github.com/NovatecConsulting/boomerang-opentelemetry-plugin/blob/master/src/impl/patchCollectorPrototype.ts). | `false` | -| `propagationHeader` | Defines the format of the context propagation header. Available formats: `TRACE_CONTEXT`, `B3_SINGLE`, `B3_MULTI` | `TRACE_CONTEXT` | +| Option | Description | Default value | +|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---| +| `samplingRate` | Sampling rate to use when collecting spans. Value must be between `0` and `1`. | `1` | +| `corsUrls` | Array of CORS URLs to take into consideration when propagating trace information. By default, CORS URLs are excluded from the propagation. | `[]` | +| `collectorConfiguration` | Object that defines the OpenTelemetry collector configuration, like the URL to send spans to. See [CollectorExporterNodeConfigBase](https://www.npmjs.com/package/@opentelemetry/exporter-collector) interface for all options. | `undefined` | +| `consoleOnly` | If `true` spans will be logged on the console and not sent to the collector endpoint. | `false` | +| `plugins` | Object for enabling and disabling OpenTelemetry plugins. | | +| `plugins.instrument_fetch` | Enabling the [OpenTelemetry plugin](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-fetch) for insturmentation of the fetch API. This will only be used in case the `fetch` API exists. | `true` | +| `plugins.instrument_xhr` | Enabling the [OpenTelemetry plugin](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-xml-http-request) for insturmentation of the XMLHttpRequest API. | `true` | +| `plugins.instrument_document_load` | Enabling the [OpenTelemetry plugin](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/web/opentelemetry-instrumentation-document-load) for insturmentation of the document load (initial request). | `true` | +| `plugins.instrument_user_interaction` | Enabling the [OpenTelemetry plugin](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/web/opentelemetry-instrumentation-user-interaction) for insturmentation of user interactions. | `true` | +| `global_instrumentation` | Object for configuring additional instrumentations, which will be applied to every OpenTelemetry plugin. || +| `global_instrumentation.requestParameter` | If enabled, existing request parameters will be added as attributes to spans and, if not excluded, will be added to the corresponding beacon as well. || +| `exporter` | Object for configuring the span exporter. Only used if `consoleOnly` is not enabled. || +| `exporter.maxQueueSize` | The maximum queue size. After the size is reached spans are dropped. | `100` | +| `exporter.maxExportBatchSize` | The maximum batch size of every export. It must be smaller or equal to `maxQueueSize`. | `10` | +| `exporter.scheduledDelayMillis` | The interval between two consecutive exports. | `500` | +| `exporter.exportTimeoutMillis` | How long the export can run before it is cancelled. | `30000` | +| `commonAttributes` | An Object defining common span attributes which will be added to each recorded span. | `{}` | +| `serviceName` | A `string` or function which can be used to set the spans' service name. A function can be defined for dynamically providing the service name, e.g. based on Boomerang values. | `undefined` | +| `prototypeExporterPatch` | Patches the OpenTelemetry collector-span-exporter, so it is compatible with the Prototype framework. This is only necessary and should only be activated, when the Prototype framework is used. [For more information see the linked file](https://github.com/NovatecConsulting/boomerang-opentelemetry-plugin/blob/master/src/impl/patchCollectorPrototype.ts). | `false` | +| `propagationHeader` | Defines the format of the context propagation header. Available formats: `TRACE_CONTEXT`, `B3_SINGLE`, `B3_MULTI` | `TRACE_CONTEXT` | ## Manual Instrumentation @@ -157,7 +171,21 @@ span.end(); The plugin also provides direct access to the OpenTelemetry API via the following function: `getOpenTelemetryApi()`. This returns the OpenTelemetry API and can be used for more advanced data collection. -### Asynchronous inclusion of Boomerang +## Transaction Recording + +If `plugins.instrument_document_load.recordTransaction` is set `true`, the document load span will be kept open during the whole transaction +and will be used as root span. +This transaction span will stay open until page unload or until the function +`startNewTransaction(spanName: string)` was called. This function closes the current transaction span and opens a new one with +the provided span name. + +Additionally, during the page load the document load span will check the `Server-Timing`-response-header for an existing trace context. +If existing, this trace context will be used to create the document load span. +The trace context should be included in the `Server-Timing`-header like this: + +`traceparent; desc="00-f524a0cf2c5246077dd36b094d8e1132-b5fa4f189acedb66-01"` + +## Asynchronous inclusion of Boomerang Make sure to check that `window.BOOMR.plugins.OpenTelemetry` actually exists prior to using it in your code in case you load boomerang asynchronously. From 9a0d57fd178ac44b3ba1cf5664599d0490c0b483 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Tue, 10 Oct 2023 08:09:48 +0200 Subject: [PATCH 10/15] little refactor --- .../instrumentation/documentLoadInstrumentation.ts | 13 ++++--------- src/impl/instrumentation/fetchInstrumentation.ts | 6 ++---- src/impl/instrumentation/urlParams.ts | 2 +- .../userInteractionInstrumentation.ts | 6 ++---- .../xmlHttpRequestInstrumentation.ts | 6 ++---- 5 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/impl/instrumentation/documentLoadInstrumentation.ts b/src/impl/instrumentation/documentLoadInstrumentation.ts index c22c883..d3b08c0 100644 --- a/src/impl/instrumentation/documentLoadInstrumentation.ts +++ b/src/impl/instrumentation/documentLoadInstrumentation.ts @@ -159,17 +159,14 @@ export class CustomDocumentLoadInstrumentation extends DocumentLoadInstrumentati const exposedSpan = span as any as Span; if(exposedSpan.name == "documentLoad") TransactionSpanManager.setTransactionSpan(span); - if(span && exposedSpan.name == "documentLoad" && requestParameter?.enabled) { - if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, location.href, requestParameter.excludeKeysFromBeacons); - else addUrlParams(span, location.href); - } + if(span && exposedSpan.name == "documentLoad" && requestParameter?.enabled) + addUrlParams(span, location.href, requestParameter.excludeKeysFromBeacons); return span; } //Override function exposedSuper._endSpan = (span, performanceName, entries) => { - const transactionSpan = TransactionSpanManager.getTransactionSpan(); // Don't close transactionSpan // transactionSpan will be closed through "beforeunload"-event @@ -184,10 +181,8 @@ export class CustomDocumentLoadInstrumentation extends DocumentLoadInstrumentati const span = _superStartSpan(spanName, performanceName, entries, parentSpan); const exposedSpan = span as any as Span; - if(span && exposedSpan.name == "documentLoad" && requestParameter?.enabled) { - if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, location.href, requestParameter.excludeKeysFromBeacons); - else addUrlParams(span, location.href); - } + if(span && exposedSpan.name == "documentLoad" && requestParameter?.enabled) + addUrlParams(span, location.href, requestParameter.excludeKeysFromBeacons); return span; } diff --git a/src/impl/instrumentation/fetchInstrumentation.ts b/src/impl/instrumentation/fetchInstrumentation.ts index 93c83a3..395ff26 100644 --- a/src/impl/instrumentation/fetchInstrumentation.ts +++ b/src/impl/instrumentation/fetchInstrumentation.ts @@ -21,10 +21,8 @@ export class CustomFetchInstrumentation extends FetchInstrumentation { exposedSuper._createSpan = (url, options = {}) => { const span = _superCreateSpan(url, options); - if(span && requestParameter?.enabled) { - if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, url, requestParameter.excludeKeysFromBeacons); - else addUrlParams(span, url); - } + if(span && requestParameter?.enabled) + addUrlParams(span, url, requestParameter.excludeKeysFromBeacons); return span; } diff --git a/src/impl/instrumentation/urlParams.ts b/src/impl/instrumentation/urlParams.ts index 6287716..6e398ac 100644 --- a/src/impl/instrumentation/urlParams.ts +++ b/src/impl/instrumentation/urlParams.ts @@ -15,7 +15,7 @@ export function addUrlParams(span: Span, url: string, excludeKeys: string[] = [] const [key, value] = keyValue.split("="); span.setAttribute(key, value); - if(!excludeKeys.includes(key)) window.BOOMR.addVar(key, value); + if(excludeKeys && !excludeKeys.includes(key)) window.BOOMR.addVar(key, value); } } } diff --git a/src/impl/instrumentation/userInteractionInstrumentation.ts b/src/impl/instrumentation/userInteractionInstrumentation.ts index 39d5bc0..82c6b8a 100644 --- a/src/impl/instrumentation/userInteractionInstrumentation.ts +++ b/src/impl/instrumentation/userInteractionInstrumentation.ts @@ -23,10 +23,8 @@ export class CustomUserInteractionInstrumentation extends UserInteractionInstrum exposedSuper._createSpan = (element, eventName, parentSpan) => { const span = _superCreateSpan(element, eventName, parentSpan); - if(span && requestParameter?.enabled) { - if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, location.href, requestParameter.excludeKeysFromBeacons); - else addUrlParams(span, location.href); - } + if(span && requestParameter?.enabled) + addUrlParams(span, location.href, requestParameter.excludeKeysFromBeacons); return span; } diff --git a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts index 558951f..dc030b7 100644 --- a/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts +++ b/src/impl/instrumentation/xmlHttpRequestInstrumentation.ts @@ -24,10 +24,8 @@ export class CustomXMLHttpRequestInstrumentation extends XMLHttpRequestInstrumen exposedSuper._createSpan = (xhr, url, method) => { const span = _superCreateSpan(xhr, url, method); - if(span && requestParameter?.enabled) { - if(requestParameter.excludeKeysFromBeacons) addUrlParams(span, url, requestParameter.excludeKeysFromBeacons); - else addUrlParams(span, url); - } + if(span && requestParameter?.enabled) + addUrlParams(span, url, requestParameter.excludeKeysFromBeacons); return span; } From fbc1a069ff98b5a59b89017486a9289142a16b68 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Tue, 10 Oct 2023 17:09:29 +0200 Subject: [PATCH 11/15] fix README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6bd9e86..d05747f 100755 --- a/README.md +++ b/README.md @@ -96,8 +96,8 @@ BOOMR.init({ instrument_user_interaction: true }, // Additional instrumentation config, which will be applied to all plugins - global_instrumentation_config: { - // Include request paramater to spans and the corresponding beacons + global_instrumentation: { + // Include request parameter to spans and the corresponding beacons requestParameter: { enabled: false, excludeKeysFromBeacons: [] //Keys, which should not be included in beacons, for instance due to cardinality concerns From afff597ac35210db45c52f689cee16fb21c0b6f2 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Wed, 11 Oct 2023 18:41:17 +0200 Subject: [PATCH 12/15] refactor addUrlParams() --- src/impl/instrumentation/urlParams.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/impl/instrumentation/urlParams.ts b/src/impl/instrumentation/urlParams.ts index 6e398ac..5ceeb2f 100644 --- a/src/impl/instrumentation/urlParams.ts +++ b/src/impl/instrumentation/urlParams.ts @@ -15,7 +15,8 @@ export function addUrlParams(span: Span, url: string, excludeKeys: string[] = [] const [key, value] = keyValue.split("="); span.setAttribute(key, value); - if(excludeKeys && !excludeKeys.includes(key)) window.BOOMR.addVar(key, value); + // if excludeKey equals null OR key is not included, add var to beacon + if(!excludeKeys || !excludeKeys.includes(key)) window.BOOMR.addVar(key, value); } } } From ad43a51eee92652dd5f91612723a061e58ae5c64 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Wed, 11 Oct 2023 21:29:45 +0200 Subject: [PATCH 13/15] update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c923791..1426bbf 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "boomerang-opentelemetry-plugin", - "version": "0.25.0-7", + "version": "0.25.0-9", "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", From 9a51618fb5084b3e88dcbcb24b38c123ac22e434 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Wed, 11 Oct 2023 21:31:42 +0200 Subject: [PATCH 14/15] refactor Tracer instrumentation --- src/impl/index.ts | 28 ++++++++++++------- .../documentLoadInstrumentation.ts | 13 +++++++-- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/impl/index.ts b/src/impl/index.ts index 2a50bdc..e95cae2 100644 --- a/src/impl/index.ts +++ b/src/impl/index.ts @@ -1,4 +1,4 @@ -import api, { context, trace, Span } from '@opentelemetry/api'; +import api, { context, trace, Span, SpanOptions, Context } from '@opentelemetry/api'; import { AlwaysOnSampler, AlwaysOffSampler, @@ -27,7 +27,10 @@ import { B3InjectEncoding, B3Propagator } from '@opentelemetry/propagator-b3'; import { PluginProperties, ContextFunction, PropagationHeader } from '../types'; import { patchExporter, patchExporterClass } from './patchCollectorPrototype'; import { MultiSpanProcessor, CustomSpanProcessor } from './spanProcessing'; -import { CustomDocumentLoadInstrumentation, patchTracer } from './instrumentation/documentLoadInstrumentation'; +import { + CustomDocumentLoadInstrumentation, + patchTracerForTransactions +} from './instrumentation/documentLoadInstrumentation'; import { CustomIdGenerator } from './transaction/transactionIdGeneration'; import { TransactionSpanManager } from './transaction/transactionSpanManager'; import { CustomXMLHttpRequestInstrumentation } from './instrumentation/xmlHttpRequestInstrumentation'; @@ -181,10 +184,8 @@ export default class OpenTelemetryTracingImpl { // store the webtracer this.traceProvider = providerWithZone; - // If recordTransaction is enabled, patch the Tracer to always use the transaction span as root span - // and initialize the transaction data storage + // If recordTransaction is enabled, initialize the transaction manager if(this.isTransactionRecordingEnabled()) { - patchTracer(); const delay = this.props.plugins_config?.instrument_document_load?.exporterDelay; TransactionSpanManager.initialize(true, this.customIdGenerator); @@ -260,15 +261,22 @@ export default class OpenTelemetryTracingImpl { */ private instrumentTracerClass = () => { const { commonAttributes, serviceName } = this.props; - // don't patch the function if no attributes are defined - if (Object.keys(commonAttributes).length <= 0) { + + let startSpanFunction: (name: string, options?: SpanOptions, context?: Context) => (Span); + + // If recordTransaction is enabled, patch the Tracer to always use the transaction span as root span + if(this.isTransactionRecordingEnabled) + startSpanFunction = patchTracerForTransactions(); + else + startSpanFunction = Tracer.prototype.startSpan; + + // don't patch the function if no attributes are defined AND no serviceName is defined + if (!serviceName && Object.keys(commonAttributes).length <= 0) { return; } - const originalStartSpanFunction = Tracer.prototype.startSpan; - Tracer.prototype.startSpan = function () { - const span: Span = originalStartSpanFunction.apply(this, arguments); + const span: Span = startSpanFunction.apply(this, arguments); // add common attributes to each span if (commonAttributes) { diff --git a/src/impl/instrumentation/documentLoadInstrumentation.ts b/src/impl/instrumentation/documentLoadInstrumentation.ts index d3b08c0..eec8a95 100644 --- a/src/impl/instrumentation/documentLoadInstrumentation.ts +++ b/src/impl/instrumentation/documentLoadInstrumentation.ts @@ -10,6 +10,7 @@ import { sanitizeAttributes } from '@opentelemetry/core/build/src/common/attribu import { TransactionSpanManager } from '../transaction/transactionSpanManager'; import { addUrlParams } from './urlParams'; import { GlobalInstrumentationConfig, RequestParameterConfig } from '../../types'; +import { Context, SpanOptions } from '@opentelemetry/api'; export interface CustomDocumentLoadInstrumentationConfig extends InstrumentationConfig { recordTransaction?: boolean; @@ -18,12 +19,17 @@ export interface CustomDocumentLoadInstrumentationConfig extends Instrumentation /** * Patch the Tracer class to use the transaction span as root span + * For any additional instrumentation of the startSpan() function, you have to use the + * new returned function + * * OpenTelemetry version: 0.25.0 + * + * @return new startSpan() function */ -export function patchTracer() { +export function patchTracerForTransactions(): (name: string, options?: SpanOptions, context?: Context) => (api.Span) { // Overwrite startSpan() in Tracer class // Copy of the original startSpan()-function with additional logic inside the function to determine the parentContext - Tracer.prototype.startSpan = function ( + const overwrittenFunction = function ( name: string, options: api.SpanOptions = {}, context = api.context.active() @@ -115,6 +121,9 @@ export function patchTracer() { span.setAttributes(Object.assign(attributes, samplingResult.attributes)); return span; } + + Tracer.prototype.startSpan = overwrittenFunction; + return overwrittenFunction; } type PerformanceEntriesWithServerTiming = PerformanceEntries & {serverTiming?: ReadonlyArray<({name: string, duration: number, description: string})>} From 3af56406dd1f25e1fbb6fc08e949e30b366c7558 Mon Sep 17 00:00:00 2001 From: EddeCCC Date: Thu, 12 Oct 2023 09:32:40 +0200 Subject: [PATCH 15/15] little refactor --- README.md | 2 +- src/impl/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d05747f..d1fb9cf 100755 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ This transaction span will stay open until page unload or until the function the provided span name. Additionally, during the page load the document load span will check the `Server-Timing`-response-header for an existing trace context. -If existing, this trace context will be used to create the document load span. +If existing, this trace context will be used to create the document load span. The trace context has to be in the W3C-format. The trace context should be included in the `Server-Timing`-header like this: `traceparent; desc="00-f524a0cf2c5246077dd36b094d8e1132-b5fa4f189acedb66-01"` diff --git a/src/impl/index.ts b/src/impl/index.ts index e95cae2..3a4aff0 100644 --- a/src/impl/index.ts +++ b/src/impl/index.ts @@ -265,7 +265,7 @@ export default class OpenTelemetryTracingImpl { let startSpanFunction: (name: string, options?: SpanOptions, context?: Context) => (Span); // If recordTransaction is enabled, patch the Tracer to always use the transaction span as root span - if(this.isTransactionRecordingEnabled) + if(this.isTransactionRecordingEnabled()) startSpanFunction = patchTracerForTransactions(); else startSpanFunction = Tracer.prototype.startSpan;