@monocle.sh/adonisjs-agent 1.0.0-beta.9 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/dist/configure.d.mts +0 -1
- package/dist/configure.mjs +1 -3
- package/dist/decorators.d.mts +83 -0
- package/dist/decorators.mjs +108 -0
- package/dist/helpers.d.mts +49 -0
- package/dist/helpers.mjs +84 -0
- package/dist/index.d.mts +4 -1
- package/dist/index.mjs +4 -2
- package/dist/init.mjs +32 -49
- package/dist/mcp.d.mts +2 -0
- package/dist/mcp.mjs +2 -0
- package/dist/monocle_middleware.mjs +1 -3
- package/dist/monocle_provider.d.mts +0 -1
- package/dist/monocle_provider.mjs +2 -4
- package/dist/src/cli_instrumentation.mjs +48 -11
- package/dist/src/define_config.d.mts +0 -2
- package/dist/src/define_config.mjs +31 -6
- package/dist/src/exception_reporter.mjs +2 -40
- package/dist/src/host_metrics.mjs +1 -3
- package/dist/src/instrumentations/cache/instrumentation.mjs +53 -0
- package/dist/src/instrumentations/mail/instrumentation.mjs +157 -0
- package/dist/src/instrumentations/mail/types.mjs +20 -0
- package/dist/src/monocle.d.mts +20 -4
- package/dist/src/monocle.mjs +49 -6
- package/dist/src/types.d.mts +36 -2
- package/dist/stubs/main.mjs +1 -3
- package/dist/types.d.mts +5 -0
- package/dist/types.mjs +1 -0
- package/package.json +58 -24
- package/dist/src/context_lines.mjs +0 -269
- package/dist/src/error_serializer.mjs +0 -77
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import { destinations } from "@adonisjs/otel";
|
|
1
2
|
//#region src/define_config.ts
|
|
3
|
+
const DEFAULT_BATCH_CONFIG = {
|
|
4
|
+
maxExportBatchSize: 512,
|
|
5
|
+
scheduledDelayMillis: 5e3,
|
|
6
|
+
exportTimeoutMillis: 3e4,
|
|
7
|
+
maxQueueSize: 2048
|
|
8
|
+
};
|
|
2
9
|
/**
|
|
3
10
|
* Extracts a header value from either ServerResponse (getHeader) or IncomingMessage (headers object).
|
|
4
11
|
*/
|
|
@@ -42,15 +49,17 @@ function extractUserResponseHook(httpConfig) {
|
|
|
42
49
|
}
|
|
43
50
|
/**
|
|
44
51
|
* Define and validate Monocle agent configuration.
|
|
45
|
-
* Sets up environment variables for OTEL exporters to point to Monocle.
|
|
46
52
|
* Returns undefined if no API key is provided (telemetry will be disabled).
|
|
47
53
|
*/
|
|
48
54
|
function defineConfig(config) {
|
|
49
55
|
if (!config.apiKey) return;
|
|
50
56
|
const endpoint = config.endpoint || "https://ingest.monocle.sh";
|
|
51
57
|
const environment = config.environment || process.env.NODE_ENV || "development";
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
const compression = config.compression === false ? "none" : "gzip";
|
|
59
|
+
const batchConfig = {
|
|
60
|
+
...DEFAULT_BATCH_CONFIG,
|
|
61
|
+
...config.batch
|
|
62
|
+
};
|
|
54
63
|
const httpConfig = config.instrumentations?.["@opentelemetry/instrumentation-http"];
|
|
55
64
|
const userResponseHook = extractUserResponseHook(httpConfig);
|
|
56
65
|
const instrumentations = {
|
|
@@ -60,13 +69,29 @@ function defineConfig(config) {
|
|
|
60
69
|
responseHook: createConnectionTypeHook(userResponseHook)
|
|
61
70
|
}
|
|
62
71
|
};
|
|
72
|
+
const monocleDestination = destinations.otlp({
|
|
73
|
+
endpoint,
|
|
74
|
+
signals: "all",
|
|
75
|
+
compression,
|
|
76
|
+
headers: {
|
|
77
|
+
"x-api-key": config.apiKey,
|
|
78
|
+
"x-monocle-env": environment
|
|
79
|
+
},
|
|
80
|
+
maxExportBatchSize: batchConfig.maxExportBatchSize,
|
|
81
|
+
scheduledDelayMillis: batchConfig.scheduledDelayMillis,
|
|
82
|
+
exportTimeoutMillis: batchConfig.exportTimeoutMillis,
|
|
83
|
+
maxQueueSize: batchConfig.maxQueueSize
|
|
84
|
+
});
|
|
63
85
|
return {
|
|
64
86
|
...config,
|
|
65
87
|
endpoint,
|
|
66
88
|
environment,
|
|
67
|
-
instrumentations
|
|
89
|
+
instrumentations,
|
|
90
|
+
destinations: {
|
|
91
|
+
...config.destinations,
|
|
92
|
+
monocle: monocleDestination
|
|
93
|
+
}
|
|
68
94
|
};
|
|
69
95
|
}
|
|
70
|
-
|
|
71
96
|
//#endregion
|
|
72
|
-
export { defineConfig };
|
|
97
|
+
export { defineConfig };
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { buildCauseChain, stackWithCauses } from "./error_serializer.mjs";
|
|
2
|
-
import { buildCauseChainWithContext } from "./context_lines.mjs";
|
|
3
|
-
import { SpanStatusCode } from "@opentelemetry/api";
|
|
4
1
|
import is from "@sindresorhus/is";
|
|
5
|
-
|
|
2
|
+
import { ExceptionReporter } from "@monocle.sh/otel-utils";
|
|
6
3
|
//#region src/exception_reporter.ts
|
|
7
|
-
const DEFAULT_CONTEXT_LINES = 7;
|
|
8
4
|
/**
|
|
9
5
|
* Convert any unknown error to an HttpError-like object.
|
|
10
6
|
*/
|
|
@@ -14,39 +10,5 @@ function toHttpError(error) {
|
|
|
14
10
|
if (!httpError.status) httpError.status = 500;
|
|
15
11
|
return httpError;
|
|
16
12
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Reports exceptions to OpenTelemetry spans with context lines and cause chains.
|
|
19
|
-
*/
|
|
20
|
-
var ExceptionReporter = class {
|
|
21
|
-
/**
|
|
22
|
-
* Report an exception on the given span.
|
|
23
|
-
*/
|
|
24
|
-
async report(options) {
|
|
25
|
-
const { span, shouldReport, contextLines = DEFAULT_CONTEXT_LINES } = options;
|
|
26
|
-
const error = toHttpError(options.error);
|
|
27
|
-
span.setStatus({
|
|
28
|
-
code: SpanStatusCode.ERROR,
|
|
29
|
-
message: error.message
|
|
30
|
-
});
|
|
31
|
-
span.setAttribute("monocle.exception.should_report", shouldReport);
|
|
32
|
-
const causeChain = shouldReport ? await buildCauseChainWithContext(error, contextLines) : buildCauseChain(error);
|
|
33
|
-
const attributes = this.#buildAttributes(error, causeChain);
|
|
34
|
-
span.addEvent("exception", attributes);
|
|
35
|
-
}
|
|
36
|
-
#buildAttributes(error, causeChain) {
|
|
37
|
-
const hasCauses = causeChain.length > 1;
|
|
38
|
-
const attributes = {
|
|
39
|
-
"exception.type": error.name || "Error",
|
|
40
|
-
"exception.message": error.message || "",
|
|
41
|
-
"exception.stacktrace": hasCauses ? stackWithCauses(error) : error.stack || ""
|
|
42
|
-
};
|
|
43
|
-
const actualCauses = causeChain.slice(1);
|
|
44
|
-
if (actualCauses.length > 0) attributes["monocle.exception.cause_chain"] = JSON.stringify(actualCauses);
|
|
45
|
-
const mainFrames = causeChain[0]?.frames;
|
|
46
|
-
if (mainFrames && mainFrames.length > 0) attributes["monocle.exception.frames"] = JSON.stringify(mainFrames);
|
|
47
|
-
return attributes;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
13
|
//#endregion
|
|
52
|
-
export { ExceptionReporter, toHttpError };
|
|
14
|
+
export { ExceptionReporter, toHttpError };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { metrics } from "@opentelemetry/api";
|
|
2
2
|
import { HostMetrics } from "@opentelemetry/host-metrics";
|
|
3
|
-
|
|
4
3
|
//#region src/host_metrics.ts
|
|
5
4
|
let hostMetricsInstance = null;
|
|
6
5
|
/**
|
|
@@ -22,6 +21,5 @@ function startHostMetrics(config = {}) {
|
|
|
22
21
|
hostMetricsInstance.start();
|
|
23
22
|
return hostMetricsInstance;
|
|
24
23
|
}
|
|
25
|
-
|
|
26
24
|
//#endregion
|
|
27
|
-
export { startHostMetrics };
|
|
25
|
+
export { startHostMetrics };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { BentoCacheInstrumentation } from "@bentocache/otel";
|
|
4
|
+
//#region src/instrumentations/cache/instrumentation.ts
|
|
5
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6
|
+
const numericPattern = /^\d+$/;
|
|
7
|
+
const hexPattern = /^[0-9a-f]{8,}$/i;
|
|
8
|
+
/**
|
|
9
|
+
* Replaces dynamic-looking segments (UUIDs, numeric IDs, hex hashes)
|
|
10
|
+
* with `*` to reduce cardinality in span attributes.
|
|
11
|
+
*/
|
|
12
|
+
function defaultKeySanitizer(key) {
|
|
13
|
+
if (!key) return key;
|
|
14
|
+
return key.split(":").map((segment) => {
|
|
15
|
+
if (uuidPattern.test(segment)) return "*";
|
|
16
|
+
if (numericPattern.test(segment)) return "*";
|
|
17
|
+
if (hexPattern.test(segment)) return "*";
|
|
18
|
+
return segment;
|
|
19
|
+
}).join(":");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Auto-instruments @adonisjs/cache (bentocache) with OpenTelemetry spans
|
|
23
|
+
* for cache operations (get, set, getOrSet, delete, etc.).
|
|
24
|
+
* Internal Redis/L2/bus spans are suppressed via `suppressInternalOperations`.
|
|
25
|
+
*/
|
|
26
|
+
async function instrumentCache(config, appRoot) {
|
|
27
|
+
if (config.enabled === false) return void 0;
|
|
28
|
+
const appRequire = createRequire(appRoot ? pathToFileURL(`${appRoot}/package.json`).href : import.meta.url);
|
|
29
|
+
try {
|
|
30
|
+
appRequire.resolve("@adonisjs/cache");
|
|
31
|
+
} catch {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const instrumentation = new BentoCacheInstrumentation({
|
|
35
|
+
requireParentSpan: config.requireParentSpan ?? false,
|
|
36
|
+
includeKeys: config.includeKeys ?? true,
|
|
37
|
+
keySanitizer: config.keySanitizer ?? defaultKeySanitizer,
|
|
38
|
+
suppressInternalOperations: true
|
|
39
|
+
});
|
|
40
|
+
instrumentation.enable();
|
|
41
|
+
/**
|
|
42
|
+
* Eagerly import bentocache to trigger the ESM hook.
|
|
43
|
+
* If the hook didn't patch BentoCache (e.g. race condition),
|
|
44
|
+
* fall back to manual registration.
|
|
45
|
+
*/
|
|
46
|
+
try {
|
|
47
|
+
const bentocache = await import("bentocache");
|
|
48
|
+
if (bentocache.BentoCache.name !== "BentoCachePatched") instrumentation.manuallyRegister(bentocache);
|
|
49
|
+
} catch {}
|
|
50
|
+
return instrumentation;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { instrumentCache };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { ExceptionReporter } from "../../exception_reporter.mjs";
|
|
2
|
+
import { EmailAttributes } from "./types.mjs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { InstrumentationBase } from "@opentelemetry/instrumentation";
|
|
7
|
+
//#region src/instrumentations/mail/instrumentation.ts
|
|
8
|
+
/**
|
|
9
|
+
* OpenTelemetry instrumentation for AdonisJS Mail.
|
|
10
|
+
*
|
|
11
|
+
* Patches Mailer.prototype methods to wrap email operations with spans.
|
|
12
|
+
* Each email send creates a CLIENT span with email metadata attributes.
|
|
13
|
+
*
|
|
14
|
+
* Span names: `email send {mailerName}`, `email queue {mailerName}`
|
|
15
|
+
* Attributes: See EmailAttributes
|
|
16
|
+
*/
|
|
17
|
+
var MailInstrumentation = class extends InstrumentationBase {
|
|
18
|
+
patched = false;
|
|
19
|
+
mailerClass;
|
|
20
|
+
originalSendCompiled;
|
|
21
|
+
originalSendLaterCompiled;
|
|
22
|
+
constructor(config = {}) {
|
|
23
|
+
super("@monocle.sh/instrumentation-mail", "1.0.0", config);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Required by InstrumentationBase. Returns undefined since we use
|
|
27
|
+
* monkey-patching instead of module patching.
|
|
28
|
+
*/
|
|
29
|
+
init() {}
|
|
30
|
+
/**
|
|
31
|
+
* Extract email address from a Recipient.
|
|
32
|
+
*/
|
|
33
|
+
extractAddress(recipient) {
|
|
34
|
+
return typeof recipient === "string" ? recipient : recipient.address;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Extract attributes from a mail message.
|
|
38
|
+
*/
|
|
39
|
+
extractMessageAttributes(message, mailerName, operation) {
|
|
40
|
+
const toAddresses = message.to?.map((r) => this.extractAddress(r)) ?? [];
|
|
41
|
+
const ccCount = message.cc?.length ?? 0;
|
|
42
|
+
const bccCount = message.bcc?.length ?? 0;
|
|
43
|
+
return {
|
|
44
|
+
[EmailAttributes.SYSTEM]: "adonisjs-mail",
|
|
45
|
+
[EmailAttributes.TRANSPORT]: mailerName,
|
|
46
|
+
[EmailAttributes.OPERATION]: operation,
|
|
47
|
+
[EmailAttributes.MESSAGE_SUBJECT]: message.subject ?? "",
|
|
48
|
+
[EmailAttributes.MESSAGE_FROM]: message.from ? this.extractAddress(message.from) : "",
|
|
49
|
+
[EmailAttributes.MESSAGE_TO]: toAddresses,
|
|
50
|
+
[EmailAttributes.MESSAGE_CC_COUNT]: ccCount,
|
|
51
|
+
[EmailAttributes.MESSAGE_BCC_COUNT]: bccCount,
|
|
52
|
+
[EmailAttributes.MESSAGE_HAS_ATTACHMENTS]: (message.attachments?.length ?? 0) > 0,
|
|
53
|
+
[EmailAttributes.RECIPIENT_COUNT]: toAddresses.length + ccCount + bccCount
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Wraps an async function with an OpenTelemetry span.
|
|
58
|
+
*/
|
|
59
|
+
async wrapWithSpan(options) {
|
|
60
|
+
const { spanName, attributes, fn, onSuccess } = options;
|
|
61
|
+
const span = this.tracer.startSpan(spanName, {
|
|
62
|
+
kind: SpanKind.CLIENT,
|
|
63
|
+
attributes
|
|
64
|
+
});
|
|
65
|
+
const ctx = trace.setSpan(context.active(), span);
|
|
66
|
+
return context.with(ctx, async () => {
|
|
67
|
+
try {
|
|
68
|
+
const result = await fn();
|
|
69
|
+
if (onSuccess) onSuccess(span, result);
|
|
70
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
71
|
+
return result;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
await new ExceptionReporter().report({
|
|
74
|
+
span,
|
|
75
|
+
error,
|
|
76
|
+
shouldReport: true
|
|
77
|
+
});
|
|
78
|
+
throw error;
|
|
79
|
+
} finally {
|
|
80
|
+
span.end();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Wraps sendCompiled with tracing span.
|
|
86
|
+
*/
|
|
87
|
+
#createSendCompiledWrapper(original) {
|
|
88
|
+
const instrumentation = this;
|
|
89
|
+
return async function(mail, sendConfig) {
|
|
90
|
+
return instrumentation.wrapWithSpan({
|
|
91
|
+
spanName: `email send ${this.name}`,
|
|
92
|
+
attributes: instrumentation.extractMessageAttributes(mail.message, this.name, "send"),
|
|
93
|
+
fn: () => original.call(this, mail, sendConfig),
|
|
94
|
+
onSuccess: (span, response) => {
|
|
95
|
+
if (response?.messageId) span.setAttribute(EmailAttributes.MESSAGE_ID, response.messageId);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Wraps sendLaterCompiled with tracing span.
|
|
102
|
+
*/
|
|
103
|
+
#createSendLaterCompiledWrapper(original) {
|
|
104
|
+
const instrumentation = this;
|
|
105
|
+
return async function(mail, sendConfig) {
|
|
106
|
+
return instrumentation.wrapWithSpan({
|
|
107
|
+
spanName: `email queue ${this.name}`,
|
|
108
|
+
attributes: instrumentation.extractMessageAttributes(mail.message, this.name, "sendLater"),
|
|
109
|
+
fn: () => original.call(this, mail, sendConfig)
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Patches Mailer.prototype methods with tracing wrappers.
|
|
115
|
+
*/
|
|
116
|
+
async enable() {
|
|
117
|
+
if (this.patched) return;
|
|
118
|
+
const appRoot = this.getConfig().appRoot;
|
|
119
|
+
const require = createRequire(appRoot ? pathToFileURL(`${appRoot}/package.json`).href : import.meta.url);
|
|
120
|
+
let mailerPath;
|
|
121
|
+
try {
|
|
122
|
+
mailerPath = require.resolve("@adonisjs/mail");
|
|
123
|
+
} catch {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
this.mailerClass = (await import(pathToFileURL(mailerPath).href)).Mailer;
|
|
127
|
+
this.originalSendCompiled = this.mailerClass.prototype.sendCompiled;
|
|
128
|
+
this.originalSendLaterCompiled = this.mailerClass.prototype.sendLaterCompiled;
|
|
129
|
+
this.mailerClass.prototype.sendCompiled = this.#createSendCompiledWrapper(this.originalSendCompiled);
|
|
130
|
+
this.mailerClass.prototype.sendLaterCompiled = this.#createSendLaterCompiledWrapper(this.originalSendLaterCompiled);
|
|
131
|
+
this.patched = true;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Restores original Mailer.prototype methods.
|
|
135
|
+
*/
|
|
136
|
+
disable() {
|
|
137
|
+
if (!this.patched || !this.mailerClass) return;
|
|
138
|
+
if (this.originalSendCompiled) this.mailerClass.prototype.sendCompiled = this.originalSendCompiled;
|
|
139
|
+
if (this.originalSendLaterCompiled) this.mailerClass.prototype.sendLaterCompiled = this.originalSendLaterCompiled;
|
|
140
|
+
this.patched = false;
|
|
141
|
+
this.mailerClass = void 0;
|
|
142
|
+
this.originalSendCompiled = void 0;
|
|
143
|
+
this.originalSendLaterCompiled = void 0;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Helper function for backward compatibility.
|
|
148
|
+
* Creates and enables a MailInstrumentation instance.
|
|
149
|
+
*/
|
|
150
|
+
async function instrumentMail(config, appRoot) {
|
|
151
|
+
if (config.enabled === false) return void 0;
|
|
152
|
+
const instrumentation = new MailInstrumentation({ appRoot: appRoot ?? config.appRoot });
|
|
153
|
+
await instrumentation.enable();
|
|
154
|
+
return instrumentation;
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
export { instrumentMail };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/instrumentations/mail/types.ts
|
|
2
|
+
/**
|
|
3
|
+
* Semantic convention attributes for email operations.
|
|
4
|
+
* @see docs/semantic-conventions/email.md
|
|
5
|
+
*/
|
|
6
|
+
const EmailAttributes = {
|
|
7
|
+
SYSTEM: "email.system",
|
|
8
|
+
TRANSPORT: "email.transport",
|
|
9
|
+
OPERATION: "email.operation",
|
|
10
|
+
MESSAGE_SUBJECT: "email.message.subject",
|
|
11
|
+
MESSAGE_FROM: "email.message.from",
|
|
12
|
+
MESSAGE_TO: "email.message.to",
|
|
13
|
+
MESSAGE_CC_COUNT: "email.message.cc_count",
|
|
14
|
+
MESSAGE_BCC_COUNT: "email.message.bcc_count",
|
|
15
|
+
MESSAGE_HAS_ATTACHMENTS: "email.message.has_attachments",
|
|
16
|
+
MESSAGE_ID: "email.message.id",
|
|
17
|
+
RECIPIENT_COUNT: "email.recipient_count"
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { EmailAttributes };
|
package/dist/src/monocle.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UserContextResult } from "@adonisjs/otel/types";
|
|
2
2
|
|
|
3
3
|
//#region src/monocle.d.ts
|
|
4
|
-
type MessageLevel = 'debug' | 'info' | 'warning' | 'error' | 'fatal';
|
|
4
|
+
type MessageLevel = 'debug' | 'info' | 'log' | 'warning' | 'error' | 'fatal';
|
|
5
5
|
interface CaptureContext {
|
|
6
6
|
user?: {
|
|
7
7
|
id: string;
|
|
@@ -15,6 +15,14 @@ interface CaptureMessageContext extends CaptureContext {
|
|
|
15
15
|
level?: MessageLevel;
|
|
16
16
|
}
|
|
17
17
|
type CaptureExceptionContext = CaptureContext;
|
|
18
|
+
/**
|
|
19
|
+
* User information for tracing. When displayed in the Monocle UI,
|
|
20
|
+
* `name` takes priority over `email` as the display label, with
|
|
21
|
+
* `email` used as fallback.
|
|
22
|
+
*/
|
|
23
|
+
interface MonocleUser extends UserContextResult {
|
|
24
|
+
name?: string;
|
|
25
|
+
}
|
|
18
26
|
/**
|
|
19
27
|
* Monocle helper class for manual instrumentation.
|
|
20
28
|
*/
|
|
@@ -31,12 +39,20 @@ declare class Monocle {
|
|
|
31
39
|
/**
|
|
32
40
|
* Capture a message and record it on the current active span.
|
|
33
41
|
* If no span is active, the message is silently ignored.
|
|
42
|
+
*
|
|
43
|
+
* Messages are stored using the same format as exceptions for unified querying.
|
|
44
|
+
* They are identified by exception.type = 'CapturedMessage'.
|
|
45
|
+
*
|
|
46
|
+
* This method is async to allow for source context extraction.
|
|
47
|
+
* You can choose to await it or fire-and-forget.
|
|
34
48
|
*/
|
|
35
|
-
static captureMessage(message: string, levelOrContext?: MessageLevel | CaptureMessageContext): void
|
|
49
|
+
static captureMessage(message: string, levelOrContext?: MessageLevel | CaptureMessageContext): Promise<void>;
|
|
36
50
|
/**
|
|
37
51
|
* Set user information on the current active span.
|
|
52
|
+
* `name` is displayed with priority over `email` in the Monocle UI.
|
|
53
|
+
* Falls back to `email`, then `id`.
|
|
38
54
|
*/
|
|
39
|
-
static setUser(user:
|
|
55
|
+
static setUser(user: MonocleUser): void;
|
|
40
56
|
}
|
|
41
57
|
//#endregion
|
|
42
|
-
export { Monocle };
|
|
58
|
+
export { Monocle, MonocleUser };
|
package/dist/src/monocle.mjs
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { ExceptionReporter } from "./exception_reporter.mjs";
|
|
2
2
|
import { trace } from "@opentelemetry/api";
|
|
3
3
|
import { setUser } from "@adonisjs/otel/helpers";
|
|
4
|
-
|
|
4
|
+
import { extractContextLines } from "@monocle.sh/otel-utils";
|
|
5
5
|
//#region src/monocle.ts
|
|
6
|
+
const DEFAULT_CONTEXT_LINES = 7;
|
|
7
|
+
/**
|
|
8
|
+
* Internal patterns to filter from stack traces.
|
|
9
|
+
* These are frames from the Monocle SDK itself.
|
|
10
|
+
*/
|
|
11
|
+
const INTERNAL_FRAME_PATTERNS = [
|
|
12
|
+
"/monocle.ts",
|
|
13
|
+
"/monocle.js",
|
|
14
|
+
"@monocle.sh/"
|
|
15
|
+
];
|
|
6
16
|
/**
|
|
7
17
|
* Monocle helper class for manual instrumentation.
|
|
8
18
|
*/
|
|
@@ -20,6 +30,22 @@ var Monocle = class {
|
|
|
20
30
|
return attributes;
|
|
21
31
|
}
|
|
22
32
|
/**
|
|
33
|
+
* Filter out internal Monocle SDK frames from context frames.
|
|
34
|
+
*/
|
|
35
|
+
static #filterInternalFrames(frames) {
|
|
36
|
+
return frames.filter((frame) => {
|
|
37
|
+
return !INTERNAL_FRAME_PATTERNS.some((pattern) => frame.filename.includes(pattern));
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Filter out internal Monocle SDK lines from a stack trace string.
|
|
42
|
+
*/
|
|
43
|
+
static #filterInternalStack(stack) {
|
|
44
|
+
return stack.split("\n").filter((line) => {
|
|
45
|
+
return !INTERNAL_FRAME_PATTERNS.some((pattern) => line.includes(pattern));
|
|
46
|
+
}).join("\n");
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
23
49
|
* Capture an exception and record it on the current active span.
|
|
24
50
|
* If no span is active, the exception is silently ignored.
|
|
25
51
|
*
|
|
@@ -41,27 +67,44 @@ var Monocle = class {
|
|
|
41
67
|
/**
|
|
42
68
|
* Capture a message and record it on the current active span.
|
|
43
69
|
* If no span is active, the message is silently ignored.
|
|
70
|
+
*
|
|
71
|
+
* Messages are stored using the same format as exceptions for unified querying.
|
|
72
|
+
* They are identified by exception.type = 'CapturedMessage'.
|
|
73
|
+
*
|
|
74
|
+
* This method is async to allow for source context extraction.
|
|
75
|
+
* You can choose to await it or fire-and-forget.
|
|
44
76
|
*/
|
|
45
|
-
static captureMessage(message, levelOrContext) {
|
|
77
|
+
static async captureMessage(message, levelOrContext) {
|
|
46
78
|
const span = trace.getActiveSpan();
|
|
47
79
|
if (!span) return;
|
|
48
80
|
const ctx = typeof levelOrContext === "string" ? { level: levelOrContext } : levelOrContext;
|
|
49
81
|
const level = ctx?.level ?? "info";
|
|
50
82
|
this.#applyUserToSpan(span, ctx?.user);
|
|
83
|
+
span.setAttribute("monocle.exception.should_report", true);
|
|
84
|
+
const syntheticError = new Error(message);
|
|
85
|
+
let frames = [];
|
|
86
|
+
if (syntheticError.stack) try {
|
|
87
|
+
const extracted = await extractContextLines(syntheticError.stack, DEFAULT_CONTEXT_LINES);
|
|
88
|
+
frames = this.#filterInternalFrames(extracted);
|
|
89
|
+
} catch {}
|
|
51
90
|
const attributes = {
|
|
52
|
-
"
|
|
91
|
+
"exception.type": "CapturedMessage",
|
|
92
|
+
"exception.message": message,
|
|
53
93
|
"monocle.message.level": level,
|
|
54
94
|
...this.#buildContextAttributes(ctx)
|
|
55
95
|
};
|
|
56
|
-
|
|
96
|
+
if (syntheticError.stack) attributes["exception.stacktrace"] = this.#filterInternalStack(syntheticError.stack);
|
|
97
|
+
if (frames.length > 0) attributes["monocle.exception.frames"] = JSON.stringify(frames);
|
|
98
|
+
span.addEvent("exception", attributes);
|
|
57
99
|
}
|
|
58
100
|
/**
|
|
59
101
|
* Set user information on the current active span.
|
|
102
|
+
* `name` is displayed with priority over `email` in the Monocle UI.
|
|
103
|
+
* Falls back to `email`, then `id`.
|
|
60
104
|
*/
|
|
61
105
|
static setUser(user) {
|
|
62
106
|
setUser(user);
|
|
63
107
|
}
|
|
64
108
|
};
|
|
65
|
-
|
|
66
109
|
//#endregion
|
|
67
|
-
export { Monocle };
|
|
110
|
+
export { Monocle };
|
package/dist/src/types.d.mts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BentoCacheInstrumentationConfig } from "@bentocache/otel/types";
|
|
2
|
+
import { DestinationMap, OtelConfig } from "@adonisjs/otel/types";
|
|
2
3
|
|
|
3
4
|
//#region src/types.d.ts
|
|
4
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for cache instrumentation
|
|
7
|
+
*/
|
|
8
|
+
interface CacheInstrumentationConfig extends BentoCacheInstrumentationConfig {}
|
|
9
|
+
/**
|
|
10
|
+
* Configuration for mail instrumentation
|
|
11
|
+
*/
|
|
12
|
+
interface MailInstrumentationConfig {
|
|
13
|
+
/**
|
|
14
|
+
* Enable mail instrumentation.
|
|
15
|
+
* @default true
|
|
16
|
+
*/
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
}
|
|
5
19
|
/**
|
|
6
20
|
* Configuration for CLI command tracing
|
|
7
21
|
*/
|
|
@@ -86,6 +100,12 @@ interface MonocleConfig extends Omit<OtelConfig, 'traceExporter' | 'metricExport
|
|
|
86
100
|
* @default process.env.NODE_ENV || 'development'
|
|
87
101
|
*/
|
|
88
102
|
environment?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Additional OTLP destinations (Grafana, Honeycomb, etc.).
|
|
105
|
+
*
|
|
106
|
+
* Monocle destination is always injected automatically and cannot be removed.
|
|
107
|
+
*/
|
|
108
|
+
destinations?: DestinationMap;
|
|
89
109
|
/**
|
|
90
110
|
* Host metrics configuration (CPU, Memory, Network, etc.).
|
|
91
111
|
* Set to `false` to disable, or pass config object.
|
|
@@ -109,6 +129,20 @@ interface MonocleConfig extends Omit<OtelConfig, 'traceExporter' | 'metricExport
|
|
|
109
129
|
* @default false
|
|
110
130
|
*/
|
|
111
131
|
cli?: false | CliTracingConfig;
|
|
132
|
+
/**
|
|
133
|
+
* Mail instrumentation configuration.
|
|
134
|
+
* Automatically instruments @adonisjs/mail if installed.
|
|
135
|
+
* Set to `false` to disable.
|
|
136
|
+
* @default { enabled: true }
|
|
137
|
+
*/
|
|
138
|
+
mail?: false | MailInstrumentationConfig;
|
|
139
|
+
/**
|
|
140
|
+
* Cache instrumentation configuration.
|
|
141
|
+
* Automatically instruments @adonisjs/cache (bentocache) if installed.
|
|
142
|
+
* Set to `false` to disable.
|
|
143
|
+
* @default { enabled: true }
|
|
144
|
+
*/
|
|
145
|
+
cache?: false | CacheInstrumentationConfig;
|
|
112
146
|
}
|
|
113
147
|
//#endregion
|
|
114
148
|
export { BatchConfig, CliTracingConfig, HostMetricsConfig, MonocleConfig };
|
package/dist/stubs/main.mjs
CHANGED
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { BatchConfig, CliTracingConfig, HostMetricsConfig, MonocleConfig } from "./src/types.mjs";
|
|
2
|
+
import { MonocleUser } from "./src/monocle.mjs";
|
|
3
|
+
import { SpanAllOptions, SpanOptions } from "./decorators.mjs";
|
|
4
|
+
import { DestinationConfig, DestinationMap, DestinationSignal, DestinationSignals, HeadersCarrier, OtelLoggingPresetOptions, OtlpDestinationConfig, OtlpDestinationOptions, UserContextResult } from "@adonisjs/otel/types";
|
|
5
|
+
export { type BatchConfig, type CliTracingConfig, type DestinationConfig, type DestinationMap, type DestinationSignal, type DestinationSignals, type HeadersCarrier, type HostMetricsConfig, type MonocleConfig, type MonocleUser, type OtelLoggingPresetOptions, type OtlpDestinationConfig, type OtlpDestinationOptions, type SpanAllOptions, type SpanOptions, type UserContextResult };
|
package/dist/types.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|