Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
237 lines
6.9 KiB
Plaintext
237 lines
6.9 KiB
Plaintext
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
|
const api = require('@opentelemetry/api');
|
|
const instrumentation = require('@opentelemetry/instrumentation');
|
|
const core = require('@sentry/core');
|
|
const constants = require('./constants.js');
|
|
|
|
const PACKAGE_NAME = '@sentry/instrumentation-hono';
|
|
const PACKAGE_VERSION = '0.0.1';
|
|
|
|
/**
|
|
* Hono instrumentation for OpenTelemetry
|
|
*/
|
|
class HonoInstrumentation extends instrumentation.InstrumentationBase {
|
|
constructor(config = {}) {
|
|
super(PACKAGE_NAME, PACKAGE_VERSION, config);
|
|
}
|
|
|
|
/**
|
|
* Initialize the instrumentation.
|
|
*/
|
|
init() {
|
|
return [
|
|
new instrumentation.InstrumentationNodeModuleDefinition('hono', ['>=4.0.0 <5'], moduleExports => this._patch(moduleExports)),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Patches the module exports to instrument Hono.
|
|
*/
|
|
_patch(moduleExports) {
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
const instrumentation = this;
|
|
|
|
class WrappedHono extends moduleExports.Hono {
|
|
constructor(...args) {
|
|
super(...args);
|
|
|
|
instrumentation._wrap(this, 'get', instrumentation._patchHandler());
|
|
instrumentation._wrap(this, 'post', instrumentation._patchHandler());
|
|
instrumentation._wrap(this, 'put', instrumentation._patchHandler());
|
|
instrumentation._wrap(this, 'delete', instrumentation._patchHandler());
|
|
instrumentation._wrap(this, 'options', instrumentation._patchHandler());
|
|
instrumentation._wrap(this, 'patch', instrumentation._patchHandler());
|
|
instrumentation._wrap(this, 'all', instrumentation._patchHandler());
|
|
instrumentation._wrap(this, 'on', instrumentation._patchOnHandler());
|
|
instrumentation._wrap(this, 'use', instrumentation._patchMiddlewareHandler());
|
|
}
|
|
}
|
|
|
|
try {
|
|
moduleExports.Hono = WrappedHono;
|
|
} catch {
|
|
// This is a workaround for environments where direct assignment is not allowed.
|
|
return { ...moduleExports, Hono: WrappedHono };
|
|
}
|
|
|
|
return moduleExports;
|
|
}
|
|
|
|
/**
|
|
* Patches the route handler to instrument it.
|
|
*/
|
|
_patchHandler() {
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
const instrumentation = this;
|
|
|
|
return function (original) {
|
|
return function wrappedHandler( ...args) {
|
|
if (typeof args[0] === 'string') {
|
|
const path = args[0];
|
|
if (args.length === 1) {
|
|
return original.apply(this, [path]);
|
|
}
|
|
|
|
const handlers = args.slice(1);
|
|
return original.apply(this, [
|
|
path,
|
|
...handlers.map(handler => instrumentation._wrapHandler(handler )),
|
|
]);
|
|
}
|
|
|
|
return original.apply(
|
|
this,
|
|
args.map(handler => instrumentation._wrapHandler(handler )),
|
|
);
|
|
};
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Patches the 'on' handler to instrument it.
|
|
*/
|
|
_patchOnHandler() {
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
const instrumentation = this;
|
|
|
|
return function (original) {
|
|
return function wrappedHandler( ...args) {
|
|
const handlers = args.slice(2);
|
|
return original.apply(this, [
|
|
...args.slice(0, 2),
|
|
...handlers.map(handler => instrumentation._wrapHandler(handler )),
|
|
]);
|
|
};
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Patches the middleware handler to instrument it.
|
|
*/
|
|
_patchMiddlewareHandler() {
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
const instrumentation = this;
|
|
|
|
return function (original) {
|
|
return function wrappedHandler( ...args) {
|
|
if (typeof args[0] === 'string') {
|
|
const path = args[0];
|
|
if (args.length === 1) {
|
|
return original.apply(this, [path]);
|
|
}
|
|
|
|
const handlers = args.slice(1);
|
|
return original.apply(this, [
|
|
path,
|
|
...handlers.map(handler => instrumentation._wrapHandler(handler )),
|
|
]);
|
|
}
|
|
|
|
return original.apply(
|
|
this,
|
|
args.map(handler => instrumentation._wrapHandler(handler )),
|
|
);
|
|
};
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Wraps a handler or middleware handler to apply instrumentation.
|
|
*/
|
|
_wrapHandler(handler) {
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
const instrumentation = this;
|
|
|
|
return function ( c, next) {
|
|
if (!instrumentation.isEnabled()) {
|
|
return handler.apply(this, [c, next]);
|
|
}
|
|
|
|
const path = c.req.path;
|
|
const span = instrumentation.tracer.startSpan(path);
|
|
|
|
return api.context.with(api.trace.setSpan(api.context.active(), span), () => {
|
|
return instrumentation._safeExecute(
|
|
() => {
|
|
const result = handler.apply(this, [c, next]);
|
|
if (core.isThenable(result)) {
|
|
return result.then(result => {
|
|
const type = instrumentation._determineHandlerType(result);
|
|
span.setAttributes({
|
|
[constants.AttributeNames.HONO_TYPE]: type,
|
|
[constants.AttributeNames.HONO_NAME]: type === constants.HonoTypes.REQUEST_HANDLER ? path : handler.name || 'anonymous',
|
|
});
|
|
instrumentation.getConfig().responseHook?.(span);
|
|
return result;
|
|
});
|
|
} else {
|
|
const type = instrumentation._determineHandlerType(result);
|
|
span.setAttributes({
|
|
[constants.AttributeNames.HONO_TYPE]: type,
|
|
[constants.AttributeNames.HONO_NAME]: type === constants.HonoTypes.REQUEST_HANDLER ? path : handler.name || 'anonymous',
|
|
});
|
|
instrumentation.getConfig().responseHook?.(span);
|
|
return result;
|
|
}
|
|
},
|
|
() => span.end(),
|
|
error => {
|
|
instrumentation._handleError(span, error);
|
|
span.end();
|
|
},
|
|
);
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Safely executes a function and handles errors.
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
_safeExecute(execute, onSuccess, onFailure) {
|
|
try {
|
|
const result = execute();
|
|
|
|
if (core.isThenable(result)) {
|
|
result.then(
|
|
() => onSuccess(),
|
|
(error) => onFailure(error),
|
|
);
|
|
} else {
|
|
onSuccess();
|
|
}
|
|
|
|
return result;
|
|
} catch (error) {
|
|
onFailure(error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines the handler type based on the result.
|
|
* @param result
|
|
* @private
|
|
*/
|
|
_determineHandlerType(result) {
|
|
return result === undefined ? constants.HonoTypes.MIDDLEWARE : constants.HonoTypes.REQUEST_HANDLER;
|
|
}
|
|
|
|
/**
|
|
* Handles errors by setting the span status and recording the exception.
|
|
*/
|
|
_handleError(span, error) {
|
|
if (error instanceof Error) {
|
|
span.setStatus({
|
|
code: api.SpanStatusCode.ERROR,
|
|
message: error.message,
|
|
});
|
|
span.recordException(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.HonoInstrumentation = HonoInstrumentation;
|
|
//# sourceMappingURL=instrumentation.js.map
|