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.

195 lines 8.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NoCodeInstrumentation = void 0; /* * 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 path = require("path"); const fs = require("fs"); const instrumentation_1 = require("@opentelemetry/instrumentation"); const version_1 = require("../../../version"); const api_1 = require("@opentelemetry/api"); class NoCodeInstrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super('splunk-opentelemetry-instrumentation-nocode', version_1.VERSION, config); } init() { var _a; const configPath = process.env.NOCODE_CONFIG_PATH ? path.normalize(process.env.NOCODE_CONFIG_PATH) : path.resolve(process.cwd(), 'nocode.config.json'); this._diag.debug(`Loading NoCodeInstrumentation config from ${configPath}`); try { if (!fs.existsSync(configPath)) { this._diag.debug(`NoCode config file not found at ${configPath}, skipping instrumentation`); return []; } const fileContent = fs.readFileSync(configPath, 'utf-8'); const definitions = JSON.parse(fileContent); if (Array.isArray(definitions)) { this._config.definitions = definitions; // Normalize all paths for (const def of definitions) { if (def.absolutePath) { if (!path.isAbsolute(def.absolutePath)) { def.absolutePath = path.resolve(process.cwd(), def.absolutePath); } def.absolutePath = path.normalize(def.absolutePath); } (_a = def.files) === null || _a === void 0 ? void 0 : _a.forEach((file) => { file.name = path.normalize(file.name); }); } } else { this._diag.warn(`Config at ${configPath} is not a valid array`); } } catch (err) { this._diag.error(`Failed to load NoCodeInstrumentation config from ${configPath}`, err); } return this.parseConfig(this._config); } _wrappedFunction(def) { return (original) => { const instrumentation = this; return function wrapped(...args) { function getAttribute(attrDefinition, args) { const index = attrDefinition.attrIndex; if (!Array.isArray(args) || index >= args.length) return {}; let current = args[index]; // attrPath not defined == primitive value if (!attrDefinition.attrPath) { return { [attrDefinition.key]: current }; } for (const key of attrDefinition.attrPath.split('.')) { if (current !== null && typeof current === 'object' && key in current) { current = current[key]; } else { return {}; } } return { [attrDefinition.key]: current }; } const spanName = def.spanName || `${def.methodName}`; const attrDefinitions = def.attributes || []; const spanAttributes = []; for (const attrDefinition of attrDefinitions) { const attr = getAttribute(attrDefinition, args); if (Object.keys(attr).length > 0) { spanAttributes.push(attr); instrumentation._diag.info(`Extracted attributes for ${attrDefinition.key}: ${JSON.stringify(attr)}`); } else { instrumentation._diag.info(`No attributes extracted for ${attrDefinition.key}`); } } const span = instrumentation.tracer.startSpan(spanName, { attributes: Object.assign({}, ...spanAttributes), }); const activeContext = api_1.trace.setSpan(api_1.context.active(), span); return api_1.context.with(activeContext, () => { let result; try { result = original.apply(this, args); } catch (error) { if (error instanceof Error) { span.recordException(error); span.setStatus({ code: 2, message: error.message }); } span.end(); throw error; } if (result instanceof Promise) { return result .then((res) => { span.end(); return res; }) .catch((err) => { if (err instanceof Error) { span.recordException(err); span.setStatus({ code: 2, message: err.message }); } span.end(); throw err; }); } else { span.end(); return result; } }); }; }; } parseConfig(config) { const definitions = config.definitions || []; const moduleDefs = []; for (const def of definitions) { const fileDefs = (def.files || []).map((f) => new instrumentation_1.InstrumentationNodeModuleFile(f.name, ['*'], this.generatePatchFunction(f), this.generateUnpatchFunction(f))); if (def.moduleName) { moduleDefs.push(new instrumentation_1.InstrumentationNodeModuleDefinition(def.moduleName, def.supportedVersions || ['*'], this.generatePatchFunction(def.mainModuleMethods || []), this.generateUnpatchFunction(def.mainModuleMethods || []), fileDefs)); } else if (def.absolutePath) { moduleDefs.push(new instrumentation_1.InstrumentationNodeModuleDefinition(def.absolutePath, ['*'], // for absolute paths otel does no version checking undefined, undefined, fileDefs)); } } return moduleDefs; } generatePatchFunction(targets // eslint-disable-next-line @typescript-eslint/no-explicit-any ) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (moduleExports) => { const targetArray = Array.isArray(targets) ? targets : [targets]; for (const target of targetArray) { const methodName = target.methodName; if (!moduleExports || typeof moduleExports[methodName] !== 'function') { this._diag.warn(`Method ${methodName} not found on module`); continue; } this._wrap(moduleExports, methodName, this._wrappedFunction(target)); } return moduleExports; }; } generateUnpatchFunction(targets // eslint-disable-next-line @typescript-eslint/no-explicit-any ) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (moduleExports) => { const targetArray = Array.isArray(targets) ? targets : [targets]; for (const target of targetArray) { const methodName = target.methodName; if (!moduleExports || typeof moduleExports[methodName] !== 'function') { this._diag.debug(`Method ${methodName} not found on module`); continue; } if ((0, instrumentation_1.isWrapped)(moduleExports[methodName])) { this._unwrap(moduleExports, methodName); } } }; } } exports.NoCodeInstrumentation = NoCodeInstrumentation; //# sourceMappingURL=nocode.js.map