UNPKG

@splunk/otel

Version:

The Splunk distribution of OpenTelemetry Node Instrumentation provides a Node agent that automatically instruments your Node application to capture and report distributed traces to Splunk APM.

345 lines 16.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports._setDefaultOptions = _setDefaultOptions; exports.defaultSpanExporterFactory = defaultSpanExporterFactory; exports.otlpSpanExporterFactory = otlpSpanExporterFactory; exports.consoleSpanExporterFactory = consoleSpanExporterFactory; exports.defaultSpanProcessorFactory = defaultSpanProcessorFactory; exports.defaultPropagatorFactory = defaultPropagatorFactory; /* * Copyright Splunk Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const util = require("util"); const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base"); const propagator_b3_1 = require("@opentelemetry/propagator-b3"); const propagator_aws_xray_1 = require("@opentelemetry/propagator-aws-xray"); const instrumentations_1 = require("../instrumentations"); const exporter_trace_otlp_proto_1 = require("@opentelemetry/exporter-trace-otlp-proto"); const resource_1 = require("../resource"); const utils_1 = require("../utils"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const api_1 = require("@opentelemetry/api"); const core_1 = require("@opentelemetry/core"); const SplunkBatchSpanProcessor_1 = require("./SplunkBatchSpanProcessor"); const resources_1 = require("@opentelemetry/resources"); const NextJsSpanProcessor_1 = require("./NextJsSpanProcessor"); const configuration_1 = require("../configuration"); const convert_1 = require("../configuration/convert"); const defaults_1 = require("../defaults"); function createSampler(userConfig) { if (userConfig.sampler !== undefined) { return userConfig.sampler; } const configSampler = (0, configuration_1.configGetSampler)(); if (configSampler === undefined) { if ((0, utils_1.getNonEmptyEnvVar)('OTEL_TRACES_SAMPLER') === undefined) { return new sdk_trace_base_1.AlwaysOnSampler(); } } return configSampler; } function _setDefaultOptions(options = {}) { const accessToken = options.accessToken || (0, configuration_1.getNonEmptyConfigVar)('SPLUNK_ACCESS_TOKEN') || ''; const realm = options.realm || (0, configuration_1.getNonEmptyConfigVar)('SPLUNK_REALM'); if (realm) { if (!accessToken) { throw new Error('Splunk realm is set, but access token is unset. To send traces to the Observability Cloud, both need to be set'); } } const envResource = (0, resource_1.getDetectedResource)(); const resourceFactory = options.resourceFactory || ((r) => r); let resource = resourceFactory((0, resources_1.resourceFromAttributes)(envResource.attributes || {}).merge((0, configuration_1.configGetResource)())); const serviceName = options.serviceName || (0, configuration_1.getNonEmptyConfigVar)('OTEL_SERVICE_NAME') || resource.attributes[semantic_conventions_1.ATTR_SERVICE_NAME]; resource = resource.merge((0, resources_1.resourceFromAttributes)({ [semantic_conventions_1.ATTR_SERVICE_NAME]: serviceName || (0, utils_1.defaultServiceName)(), })); const extraTracerConfig = options.tracerConfig || {}; const sampler = createSampler(extraTracerConfig); const tracerConfig = Object.assign({ resource, sampler, generalLimits: { attributeCountLimit: (0, configuration_1.getConfigNumber)('OTEL_ATTRIBUTE_COUNT_LIMIT', defaults_1.DEFAULT_COUNT_LIMIT), attributeValueLengthLimit: (0, configuration_1.getConfigNumber)('OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT', defaults_1.DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT), }, spanLimits: { attributeValueLengthLimit: (0, configuration_1.getConfigNumber)('OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT', defaults_1.DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT), attributeCountLimit: (0, configuration_1.getConfigNumber)('OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT', defaults_1.DEFAULT_COUNT_LIMIT), linkCountLimit: (0, configuration_1.getConfigNumber)('OTEL_SPAN_LINK_COUNT_LIMIT', defaults_1.DEFAULT_SPAN_LINK_COUNT_LIMIT), eventCountLimit: (0, configuration_1.getConfigNumber)('OTEL_SPAN_EVENT_COUNT_LIMIT', defaults_1.DEFAULT_COUNT_LIMIT), attributePerEventCountLimit: (0, configuration_1.getConfigNumber)('OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT', defaults_1.DEFAULT_COUNT_LIMIT), attributePerLinkCountLimit: (0, configuration_1.getConfigNumber)('OTEL_LINK_ATTRIBUTE_COUNT_LIMIT', defaults_1.DEFAULT_COUNT_LIMIT), } }, extraTracerConfig); const instrumentations = options.instrumentations || (0, instrumentations_1.getInstrumentations)(); if (instrumentations.length === 0) { api_1.diag.warn('No instrumentations set to be loaded. Install an instrumentation package to enable auto-instrumentation.'); } return { realm, endpoint: options.endpoint, serviceName: String(resource.attributes[semantic_conventions_1.ATTR_SERVICE_NAME]), accessToken, serverTimingEnabled: options.serverTimingEnabled || (0, configuration_1.getConfigBoolean)('SPLUNK_TRACE_RESPONSE_HEADER_ENABLED', true), captureHttpRequestUriParams: options.captureHttpRequestUriParams || (0, configuration_1.configGetUriParameterCapture)(), instrumentations, tracerConfig, spanExporterFactory: options.spanExporterFactory || defaultSpanExporterFactory(realm), spanProcessorFactory: options.spanProcessorFactory || defaultSpanProcessorFactory, propagatorFactory: options.propagatorFactory || defaultPropagatorFactory, }; } const SUPPORTED_EXPORTER_TYPES = ['console', 'otlp', 'none']; const SpanExporterMap = { console: consoleSpanExporterFactory, otlp: otlpSpanExporterFactory, none: () => [], }; function containsSupportedRealmExporter(exporterTypes) { return exporterTypes.includes('otlp'); } function areValidExporterTypes(types) { for (const t of types) { if (!SUPPORTED_EXPORTER_TYPES.includes(t)) { return false; } } return true; } function getExporterTypes(realm) { const traceExporters = (0, utils_1.getEnvArray)('OTEL_TRACES_EXPORTER') || [ 'otlp', ]; if (realm) { if (!containsSupportedRealmExporter(traceExporters)) { throw new Error('Setting the Splunk realm with an explicit OTEL_TRACES_EXPORTER requires OTEL_TRACES_EXPORTER to be either otlp or be left undefined'); } } if (!areValidExporterTypes(traceExporters)) { throw new Error(`Invalid value for OTEL_TRACES_EXPORTER env variable: ${util.inspect(process.env.OTEL_TRACES_EXPORTER)}. Choose from ${util.inspect(SUPPORTED_EXPORTER_TYPES, { compact: true, })} or leave undefined.`); } return traceExporters; } function defaultSpanExporterFactory(realm) { const configTracerProvider = (0, configuration_1.getConfigTracerProvider)(); if (configTracerProvider === undefined) { const exporterTypes = getExporterTypes(realm); return (options) => { const factories = exporterTypes.map((t) => SpanExporterMap[t]); return factories.flatMap((factory) => factory(options)); }; } return () => []; } function otlpSpanExporterFactory(options) { let protocol = (0, utils_1.getEnvValueByPrecedence)([ 'OTEL_EXPORTER_OTLP_TRACES_PROTOCOL', 'OTEL_EXPORTER_OTLP_PROTOCOL', ]); let endpoint = options.endpoint; const accessToken = options.accessToken; if (options.realm !== undefined) { if (protocol !== undefined && protocol !== 'http/protobuf') { api_1.diag.warn(`OTLP span exporter factory: defaulting protocol to 'http/protobuf' instead of ${protocol} due to realm being defined.`); } const envEndpoint = (0, utils_1.getEnvValueByPrecedence)([ 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', ]); if (endpoint === undefined && envEndpoint === undefined) { endpoint = `https://ingest.${options.realm}.signalfx.com/v2/trace/otlp`; protocol = 'http/protobuf'; } else { api_1.diag.warn('OTLP span exporter factory: Realm value ignored (full endpoint URL has been specified).'); } } protocol = protocol !== null && protocol !== void 0 ? protocol : 'http/protobuf'; switch (protocol) { case 'grpc': { const grpcModule = require('@grpc/grpc-js'); const otlpGrpc = require('@opentelemetry/exporter-trace-otlp-grpc'); const metadata = new grpcModule.Metadata(); if (accessToken) { // for forward compatibility, is not currently supported metadata.set('X-SF-TOKEN', accessToken); } return new otlpGrpc.OTLPTraceExporter({ url: endpoint, metadata, }); } case 'http/protobuf': { const headers = accessToken ? { 'X-SF-TOKEN': accessToken, } : undefined; const url = (0, utils_1.ensureResourcePath)(endpoint, '/v1/traces'); return new exporter_trace_otlp_proto_1.OTLPTraceExporter({ url, headers, }); } default: throw new Error(`Expected OTLP protocol to be either grpc or http/protobuf, got ${protocol}.`); } } function consoleSpanExporterFactory() { return new sdk_trace_base_1.ConsoleSpanExporter(); } function toSpanExporter(configExporter) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; if (configExporter.otlp_http !== undefined) { const otlpHttp = configExporter.otlp_http; const configHeaders = otlpHttp.headers || []; const headers = {}; for (const header of configHeaders) { if (header.value !== null) { headers[header.name] = header.value; } } const url = (0, utils_1.ensureResourcePath)(otlpHttp.endpoint, '/v1/traces'); return new exporter_trace_otlp_proto_1.OTLPTraceExporter({ url, headers, timeoutMillis: (_a = otlpHttp.timeout) !== null && _a !== void 0 ? _a : undefined, compression: (0, convert_1.toCompression)(otlpHttp.compression), httpAgentOptions: { ca: (0, utils_1.readFileContent)((_b = otlpHttp.tls) === null || _b === void 0 ? void 0 : _b.ca_file), cert: (0, utils_1.readFileContent)((_c = otlpHttp.tls) === null || _c === void 0 ? void 0 : _c.cert_file), key: (0, utils_1.readFileContent)((_d = otlpHttp.tls) === null || _d === void 0 ? void 0 : _d.key_file), }, }); } else if (configExporter.otlp_grpc !== undefined) { const cfgGrpc = configExporter.otlp_grpc; const GrpcModule = require('@grpc/grpc-js'); const otlpGrpc = require('@opentelemetry/exporter-trace-otlp-grpc'); const metadata = new GrpcModule.Metadata(); for (const header of cfgGrpc.headers || []) { if (header.value !== null) { metadata.set(header.name, header.value); } } const credentials = cfgGrpc.tls === undefined ? undefined : GrpcModule.credentials.createSsl((0, utils_1.readFileContent)((_e = cfgGrpc.tls) === null || _e === void 0 ? void 0 : _e.cert_file), (0, utils_1.readFileContent)((_f = cfgGrpc.tls) === null || _f === void 0 ? void 0 : _f.key_file), (0, utils_1.readFileContent)((_g = cfgGrpc.tls) === null || _g === void 0 ? void 0 : _g.ca_file)); return new otlpGrpc.OTLPTraceExporter({ url: (_h = cfgGrpc.endpoint) !== null && _h !== void 0 ? _h : undefined, metadata, timeoutMillis: (_j = cfgGrpc.timeout) !== null && _j !== void 0 ? _j : undefined, compression: (0, convert_1.toCompression)(cfgGrpc.compression), credentials, }); } else if (configExporter.console !== undefined) { return new sdk_trace_base_1.ConsoleSpanExporter(); } else if (configExporter['otlp_file/development'] !== undefined) { api_1.diag.warn('span exporter "otlp_file/development" is not supported'); } else if (configExporter.zipkin !== undefined) { api_1.diag.warn('span exporter "zipkin" is not supported'); } return undefined; } function defaultSpanProcessorFactory(options) { var _a, _b, _c, _d, _e; const configTracerProvider = (0, configuration_1.getConfigTracerProvider)(); const nextJsFixEnabled = (0, configuration_1.getConfigBoolean)('SPLUNK_NEXTJS_FIX_ENABLED', false); if (configTracerProvider === undefined) { let exporters = []; const spanExporters = options.spanExporterFactory(options); if (!Array.isArray(spanExporters)) { exporters = [spanExporters]; } else { exporters = spanExporters; } const processors = []; if (nextJsFixEnabled) { processors.push(new NextJsSpanProcessor_1.NextJsSpanProcessor()); } for (const exporter of exporters) { processors.push(new SplunkBatchSpanProcessor_1.SplunkBatchSpanProcessor(exporter)); } return processors; } const processors = []; for (const processor of configTracerProvider.processors) { if (processor.batch !== undefined) { const batch = processor.batch; const configExporter = batch.exporter; const exporter = toSpanExporter(configExporter); if (exporter !== undefined) { processors.push(new SplunkBatchSpanProcessor_1.SplunkBatchSpanProcessor(exporter, { maxExportBatchSize: (_a = batch.max_export_batch_size) !== null && _a !== void 0 ? _a : undefined, scheduledDelayMillis: (_b = batch.schedule_delay) !== null && _b !== void 0 ? _b : undefined, exportTimeoutMillis: (_c = batch.export_timeout) !== null && _c !== void 0 ? _c : undefined, maxQueueSize: (_d = batch.max_queue_size) !== null && _d !== void 0 ? _d : undefined, })); } } else if (processor.simple !== undefined) { const exporter = toSpanExporter((_e = processor.simple) === null || _e === void 0 ? void 0 : _e.exporter); if (exporter !== undefined) { processors.push(new sdk_trace_base_1.SimpleSpanProcessor(exporter)); } } } return processors; } // eslint-disable-next-line @typescript-eslint/no-unused-vars function defaultPropagatorFactory(_options) { let propagatorKeys = []; const configPropagators = (0, configuration_1.configGetPropagators)(); if (configPropagators === undefined) { propagatorKeys = (0, utils_1.getEnvArray)('OTEL_PROPAGATORS') || [ 'tracecontext', 'baggage', ]; } else { propagatorKeys = configPropagators; } const propagators = []; for (const propagator of propagatorKeys) { switch (propagator) { case 'baggage': propagators.push(new core_1.W3CBaggagePropagator()); break; case 'tracecontext': propagators.push(new core_1.W3CTraceContextPropagator()); break; case 'b3multi': propagators.push(new propagator_b3_1.B3Propagator({ injectEncoding: propagator_b3_1.B3InjectEncoding.MULTI_HEADER })); break; case 'b3': propagators.push(new propagator_b3_1.B3Propagator()); break; case 'xray': propagators.push(new propagator_aws_xray_1.AWSXRayPropagator()); break; default: break; } } return new core_1.CompositePropagator({ propagators, }); } //# sourceMappingURL=options.js.map