UNPKG

@bugsnag/core-performance

Version:
120 lines (117 loc) 6.44 kB
import { BatchProcessor } from './batch-processor.js'; import { validateConfig, schema } from './config.js'; import { TracePayloadEncoder } from './delivery.js'; import FixedProbabilityManager from './fixed-probability-manager.js'; import ProbabilityFetcher from './probability-fetcher.js'; import ProbabilityManager from './probability-manager.js'; import { BufferingProcessor } from './processor.js'; import Sampler from './sampler.js'; import { DefaultSpanContextStorage } from './span-context.js'; import { SpanFactory } from './span-factory.js'; import { timeToNumber } from './time.js'; function createClient(options) { const bufferingProcessor = new BufferingProcessor(); const spanContextStorage = options.spanContextStorage || new DefaultSpanContextStorage(options.backgroundingListener); let logger = options.schema.logger.defaultValue; let appState = 'starting'; const sampler = new Sampler(1.0); const SpanFactoryClass = options.spanFactory || SpanFactory; const spanFactory = new SpanFactoryClass(bufferingProcessor, sampler, options.idGenerator, options.spanAttributesSource, options.clock, options.backgroundingListener, logger, spanContextStorage); const setAppState = (state) => { appState = state; }; const plugins = options.plugins(spanFactory, spanContextStorage, setAppState, appState); return { start: (config) => { const configuration = validateConfig(config, options.schema); // if using the default endpoint add the API key as a subdomain // e.g. convert URL https://otlp.bugsnag.com/v1/traces to URL https://<project_api_key>.otlp.bugsnag.com/v1/traces if (configuration.endpoint === schema.endpoint.defaultValue) { configuration.endpoint = configuration.endpoint.replace('https://', `https://${configuration.apiKey}.`); } // Correlate errors with span by monkey patching _notify on the error client // and utilizing the setTraceCorrelation method on the event if (configuration.bugsnag && typeof configuration.bugsnag.Event.prototype.setTraceCorrelation === 'function' && configuration.bugsnag.Client) { const originalNotify = configuration.bugsnag.Client.prototype._notify; configuration.bugsnag.Client.prototype._notify = function (...args) { const currentSpanContext = spanContextStorage.current; if (currentSpanContext && typeof args[0].setTraceCorrelation === 'function') { args[0].setTraceCorrelation(currentSpanContext.traceId, currentSpanContext.id); } originalNotify.apply(this, args); }; } const delivery = options.deliveryFactory(configuration.endpoint); options.spanAttributesSource.configure(configuration); spanFactory.configure(configuration); const probabilityManagerPromise = configuration.samplingProbability === undefined ? ProbabilityManager.create(options.persistence, sampler, new ProbabilityFetcher(delivery, configuration.apiKey)) : FixedProbabilityManager.create(sampler, configuration.samplingProbability); probabilityManagerPromise.then((manager) => { const batchProcessor = new BatchProcessor(delivery, configuration, options.retryQueueFactory(delivery, configuration.retryQueueMaxSize), sampler, manager, new TracePayloadEncoder(options.clock, configuration, options.resourceAttributesSource)); spanFactory.reprocessEarlySpans(batchProcessor); // register with the backgrounding listener - we do this in 'start' as // there's nothing to do if we're backgrounded before start is called // e.g. we can't trigger delivery until we have the apiKey and endpoint // from configuration options.backgroundingListener.onStateChange(state => { batchProcessor.flush(); // ensure we have a fresh probability value when returning to the // foreground if (state === 'in-foreground') { manager.ensureFreshProbability(); } }); logger = configuration.logger; }); for (const plugin of configuration.plugins) { plugins.push(plugin); } for (const plugin of plugins) { plugin.configure(configuration, spanFactory, setAppState, appState); } }, startSpan: (name, spanOptions) => { const cleanOptions = spanFactory.validateSpanOptions(name, spanOptions); const span = spanFactory.startSpan(cleanOptions.name, cleanOptions.options); span.setAttribute('bugsnag.span.category', 'custom'); return spanFactory.toPublicApi(span); }, startNetworkSpan: (networkSpanOptions) => { const spanInternal = spanFactory.startNetworkSpan(networkSpanOptions); const span = spanFactory.toPublicApi(spanInternal); // Overwrite end method to set status code attribute // once we release the setAttribute API we can simply return the span const networkSpan = { ...span, end: (endOptions) => { spanFactory.endSpan(spanInternal, timeToNumber(options.clock, endOptions.endTime), { 'http.status_code': endOptions.status }); } }; return networkSpan; }, getPlugin: (Constructor) => { for (const plugin of plugins) { if (plugin instanceof Constructor) { return plugin; } } }, get currentSpanContext() { return spanContextStorage.current; }, get appState() { return appState; }, ...(options.platformExtensions && options.platformExtensions(spanFactory, spanContextStorage)) }; } function createNoopClient() { const noop = () => { }; return { start: noop, startSpan: () => ({ id: '', traceId: '', end: noop, isValid: () => false }), currentSpanContext: undefined }; } export { createClient, createNoopClient };