@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
JavaScript
"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