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