@sentry/node
Version:
Sentry Node SDK using OpenTelemetry for performance instrumentation
256 lines (237 loc) • 9.43 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const api = require('@opentelemetry/api');
const core = require('@sentry/core');
const instrumentation = require('@opentelemetry/instrumentation');
const semanticConventions = require('@opentelemetry/semantic-conventions');
const redisCommon = require('./redis-common.js');
const semconv = require('./semconv.js');
/*
* Copyright The OpenTelemetry Authors
*
* 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
*
* https://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.
*
* NOTICE from the Sentry authors:
* - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/instrumentation-ioredis-v0.62.0/packages/instrumentation-ioredis
* - Upstream version: @opentelemetry/instrumentation-ioredis@0.62.0
* - Minor TypeScript adjustments for this repository's compiler settings
*/
/* eslint-disable -- vendored @opentelemetry/instrumentation-ioredis */
const PACKAGE_NAME = '@opentelemetry/instrumentation-ioredis';
const PACKAGE_VERSION = '0.62.0';
// ---- utils ----
function endSpan(span, err) {
if (err) {
span.recordException(err);
span.setStatus({
code: api.SpanStatusCode.ERROR,
message: err.message,
});
}
span.end();
}
// ---- IORedisInstrumentation ----
const DEFAULT_CONFIG = {
requireParentSpan: true,
};
class IORedisInstrumentation extends instrumentation.InstrumentationBase {
constructor(config = {}) {
super(PACKAGE_NAME, PACKAGE_VERSION, { ...DEFAULT_CONFIG, ...config });
this._setSemconvStabilityFromEnv();
}
_setSemconvStabilityFromEnv() {
this._netSemconvStability = instrumentation.semconvStabilityFromStr('http', process.env['OTEL_SEMCONV_STABILITY_OPT_IN']);
this._dbSemconvStability = instrumentation.semconvStabilityFromStr('database', process.env['OTEL_SEMCONV_STABILITY_OPT_IN']);
}
setConfig(config = {}) {
super.setConfig({ ...DEFAULT_CONFIG, ...config });
}
init() {
return [
new instrumentation.InstrumentationNodeModuleDefinition(
'ioredis',
['>=2.0.0 <6'],
(module, moduleVersion) => {
const moduleExports =
module[Symbol.toStringTag] === 'Module'
? module.default // ESM
: module; // CommonJS
if (instrumentation.isWrapped(moduleExports.prototype.sendCommand)) {
this._unwrap(moduleExports.prototype, 'sendCommand');
}
this._wrap(moduleExports.prototype, 'sendCommand', this._patchSendCommand(moduleVersion));
if (instrumentation.isWrapped(moduleExports.prototype.connect)) {
this._unwrap(moduleExports.prototype, 'connect');
}
this._wrap(moduleExports.prototype, 'connect', this._patchConnection());
return module;
},
(module) => {
if (module === undefined) return;
const moduleExports =
module[Symbol.toStringTag] === 'Module'
? module.default // ESM
: module; // CommonJS
this._unwrap(moduleExports.prototype, 'sendCommand');
this._unwrap(moduleExports.prototype, 'connect');
},
),
];
}
_patchSendCommand(moduleVersion) {
return (original) => {
return this._traceSendCommand(original, moduleVersion);
};
}
_patchConnection() {
return (original) => {
return this._traceConnection(original);
};
}
_traceSendCommand(original, moduleVersion) {
const instrumentation$1 = this;
return function ( cmd) {
if (arguments.length < 1 || typeof cmd !== 'object') {
return original.apply(this, arguments);
}
const config = instrumentation$1.getConfig();
const dbStatementSerializer = config.dbStatementSerializer || redisCommon.defaultDbStatementSerializer;
const hasNoParentSpan = api.trace.getSpan(api.context.active()) === undefined;
if (config.requireParentSpan === true && hasNoParentSpan) {
return original.apply(this, arguments);
}
const attributes = {};
const { host, port } = this.options;
const dbQueryText = dbStatementSerializer(cmd.name, cmd.args);
if (instrumentation$1._dbSemconvStability & instrumentation.SemconvStability.OLD) {
attributes[semconv.ATTR_DB_SYSTEM] = semconv.DB_SYSTEM_VALUE_REDIS;
attributes[semconv.ATTR_DB_STATEMENT] = dbQueryText;
attributes[semconv.ATTR_DB_CONNECTION_STRING] = `redis://${host}:${port}`;
}
if (instrumentation$1._dbSemconvStability & instrumentation.SemconvStability.STABLE) {
attributes[semanticConventions.ATTR_DB_SYSTEM_NAME] = semconv.DB_SYSTEM_NAME_VALUE_REDIS;
attributes[semanticConventions.ATTR_DB_QUERY_TEXT] = dbQueryText;
}
if (instrumentation$1._netSemconvStability & instrumentation.SemconvStability.OLD) {
attributes[semconv.ATTR_NET_PEER_NAME] = host;
attributes[semconv.ATTR_NET_PEER_PORT] = port;
}
if (instrumentation$1._netSemconvStability & instrumentation.SemconvStability.STABLE) {
attributes[semanticConventions.ATTR_SERVER_ADDRESS] = host;
attributes[semanticConventions.ATTR_SERVER_PORT] = port;
}
attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = 'auto.db.otel.redis';
const span = instrumentation$1.tracer.startSpan(cmd.name, {
kind: api.SpanKind.CLIENT,
attributes,
});
const { requestHook } = config;
if (requestHook) {
instrumentation.safeExecuteInTheMiddle(
() =>
requestHook(span, {
moduleVersion,
cmdName: cmd.name,
cmdArgs: cmd.args,
}),
(e) => {
if (e) {
api.diag.error('ioredis instrumentation: request hook failed', e);
}
},
true,
);
}
try {
const result = original.apply(this, arguments);
const origResolve = cmd.resolve;
cmd.resolve = function (result) {
instrumentation.safeExecuteInTheMiddle(
() => config.responseHook?.(span, cmd.name, cmd.args, result),
(e) => {
if (e) {
api.diag.error('ioredis instrumentation: response hook failed', e);
}
},
true,
);
endSpan(span, null);
origResolve(result);
};
const origReject = cmd.reject;
cmd.reject = function (err) {
endSpan(span, err);
origReject(err);
};
return result;
} catch (error) {
endSpan(span, error );
throw error;
}
};
}
_traceConnection(original) {
const instrumentation$1 = this;
return function () {
const hasNoParentSpan = api.trace.getSpan(api.context.active()) === undefined;
if (instrumentation$1.getConfig().requireParentSpan === true && hasNoParentSpan) {
return original.apply(this, arguments);
}
const attributes = {};
const { host, port } = this.options;
if (instrumentation$1._dbSemconvStability & instrumentation.SemconvStability.OLD) {
attributes[semconv.ATTR_DB_SYSTEM] = semconv.DB_SYSTEM_VALUE_REDIS;
attributes[semconv.ATTR_DB_STATEMENT] = 'connect';
attributes[semconv.ATTR_DB_CONNECTION_STRING] = `redis://${host}:${port}`;
}
if (instrumentation$1._dbSemconvStability & instrumentation.SemconvStability.STABLE) {
attributes[semanticConventions.ATTR_DB_SYSTEM_NAME] = semconv.DB_SYSTEM_NAME_VALUE_REDIS;
attributes[semanticConventions.ATTR_DB_QUERY_TEXT] = 'connect';
}
if (instrumentation$1._netSemconvStability & instrumentation.SemconvStability.OLD) {
attributes[semconv.ATTR_NET_PEER_NAME] = host;
attributes[semconv.ATTR_NET_PEER_PORT] = port;
}
if (instrumentation$1._netSemconvStability & instrumentation.SemconvStability.STABLE) {
attributes[semanticConventions.ATTR_SERVER_ADDRESS] = host;
attributes[semanticConventions.ATTR_SERVER_PORT] = port;
}
attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = 'auto.db.otel.redis';
const span = instrumentation$1.tracer.startSpan('connect', {
kind: api.SpanKind.CLIENT,
attributes,
});
try {
const result = original.apply(this, arguments);
if (typeof result?.then === 'function') {
return result.then(
(value) => {
endSpan(span, null);
return value;
},
(error) => {
endSpan(span, error);
return Promise.reject(error);
},
);
}
endSpan(span, null);
return result;
} catch (error) {
endSpan(span, error );
throw error;
}
};
}
}
exports.IORedisInstrumentation = IORedisInstrumentation;
//# sourceMappingURL=ioredis-instrumentation.js.map