@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.
147 lines • 7.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ElasticsearchInstrumentation = 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 api_1 = require("@opentelemetry/api");
const core_1 = require("@opentelemetry/core");
const instrumentation_1 = require("@opentelemetry/instrumentation");
const version_1 = require("../../../version");
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
const utils_1 = require("./utils");
const helpers_1 = require("./helpers");
var AttributeNames;
(function (AttributeNames) {
AttributeNames["ELASTICSEARCH_INDICES"] = "elasticsearch.request.indices";
})(AttributeNames || (AttributeNames = {}));
class ElasticsearchInstrumentation extends instrumentation_1.InstrumentationBase {
constructor(config = {}) {
super('splunk-opentelemetry-instrumentation-elasticsearch', version_1.VERSION, config);
this._isEnabled = false;
}
init() {
const apiModuleFiles = helpers_1.ELASTICSEARCH_API_FILES.map(({ path, operationClassName }) => new instrumentation_1.InstrumentationNodeModuleFile(`@elastic/elasticsearch/api/${path}`, ['>=5 <8'], (moduleExports, moduleVersion) => {
api_1.diag.debug(`elasticsearch instrumentation: patch elasticsearch ${operationClassName}.`);
this.moduleVersion = moduleVersion;
this._isEnabled = true;
const modulePrototypeKeys = Object.keys(moduleExports.prototype);
if (modulePrototypeKeys.length > 0) {
modulePrototypeKeys.forEach((functionName) => {
this._wrap(moduleExports.prototype, functionName, this.wrappedApiRequest(operationClassName, functionName));
});
return moduleExports;
}
// For versions <= 7.9.0
const instrumentation = this;
return function (opts) {
const module = moduleExports(opts);
instrumentation.patchObject(operationClassName, module);
return module;
};
}, (moduleExports) => {
api_1.diag.debug(`elasticsearch instrumentation: unpatch elasticsearch.`);
this._isEnabled = false;
const modulePrototypeKeys = Object.keys(moduleExports.prototype);
if (modulePrototypeKeys.length > 0) {
modulePrototypeKeys.forEach((functionName) => {
this._unwrap(moduleExports.prototype, functionName);
});
}
else {
// Unable to unwrap function for versions <= 7.9.0. Using _isEnabled flag instead.
}
}));
const module = new instrumentation_1.InstrumentationNodeModuleDefinition(ElasticsearchInstrumentation.component, ['*'], undefined, undefined, apiModuleFiles);
return module;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
patchObject(operationClassName, object) {
Object.keys(object).forEach((functionName) => {
if (typeof object[functionName] === 'object') {
this.patchObject(functionName, object[functionName]);
}
else {
this._wrap(object, functionName, this.wrappedApiRequest(operationClassName, functionName));
}
});
}
wrappedApiRequest(apiClassName, functionName) {
return (original) => {
const instrumentation = this;
return function (...args) {
if (!instrumentation._isEnabled) {
return original.apply(this, args);
}
const [params, options, originalCallback] = (0, utils_1.normalizeArguments)(args[0], args[1], args[2]);
const operation = `${apiClassName}.${functionName}`;
const span = (0, utils_1.startSpan)({
tracer: instrumentation.tracer,
attributes: {
[semantic_conventions_1.SemanticAttributes.DB_OPERATION]: operation,
[AttributeNames.ELASTICSEARCH_INDICES]: (0, utils_1.getIndexName)(params),
[semantic_conventions_1.SemanticAttributes.DB_STATEMENT]: (instrumentation._config.dbStatementSerializer ||
utils_1.defaultDbStatementSerializer)(operation, params, options),
},
});
instrumentation._addModuleVersionIfNeeded(span);
if (originalCallback) {
const wrappedCallback = function (err, result) {
if (err) {
(0, utils_1.onError)(span, err);
}
else {
(0, utils_1.onResponse)(span, result, instrumentation._config.responseHook);
}
return originalCallback.call(this, err, result);
};
return instrumentation._callOriginalFunction(span, () => original.call(this, params, options, wrappedCallback));
}
else {
const promise = instrumentation._callOriginalFunction(span, () => original.apply(this, args));
promise.then((result) => {
(0, utils_1.onResponse)(span, result, instrumentation._config.responseHook);
return result;
}, (err) => {
(0, utils_1.onError)(span, err);
return err;
});
return promise;
}
};
};
}
_callOriginalFunction(span, originalFunction) {
var _a;
if ((_a = this._config) === null || _a === void 0 ? void 0 : _a.suppressInternalInstrumentation) {
return api_1.context.with((0, core_1.suppressTracing)(api_1.context.active()), originalFunction);
}
else {
const activeContextWithSpan = api_1.trace.setSpan(api_1.context.active(), span);
return api_1.context.with(activeContextWithSpan, originalFunction);
}
}
_addModuleVersionIfNeeded(span) {
if (this.moduleVersion === undefined) {
return;
}
if (this._config.moduleVersionAttributeName) {
span.setAttribute(this._config.moduleVersionAttributeName, this.moduleVersion);
}
}
}
exports.ElasticsearchInstrumentation = ElasticsearchInstrumentation;
ElasticsearchInstrumentation.component = '@elastic/elasticsearch';
//# sourceMappingURL=elasticsearch.js.map