@adonis-agora/telescope 0.1.0
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/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/configure.d.ts +16 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +75 -0
- package/dist/configure.js.map +1 -0
- package/dist/providers/telescope_ai_provider.d.ts +20 -0
- package/dist/providers/telescope_ai_provider.d.ts.map +1 -0
- package/dist/providers/telescope_ai_provider.js +45 -0
- package/dist/providers/telescope_ai_provider.js.map +1 -0
- package/dist/providers/telescope_alerts_provider.d.ts +23 -0
- package/dist/providers/telescope_alerts_provider.d.ts.map +1 -0
- package/dist/providers/telescope_alerts_provider.js +72 -0
- package/dist/providers/telescope_alerts_provider.js.map +1 -0
- package/dist/providers/telescope_provider.d.ts +43 -0
- package/dist/providers/telescope_provider.d.ts.map +1 -0
- package/dist/providers/telescope_provider.js +103 -0
- package/dist/providers/telescope_provider.js.map +1 -0
- package/dist/providers/telescope_ui_provider.d.ts +21 -0
- package/dist/providers/telescope_ui_provider.d.ts.map +1 -0
- package/dist/providers/telescope_ui_provider.js +119 -0
- package/dist/providers/telescope_ui_provider.js.map +1 -0
- package/dist/providers/telescope_watchers_provider.d.ts +31 -0
- package/dist/providers/telescope_watchers_provider.d.ts.map +1 -0
- package/dist/providers/telescope_watchers_provider.js +116 -0
- package/dist/providers/telescope_watchers_provider.js.map +1 -0
- package/dist/src/ai/define_config.d.ts +56 -0
- package/dist/src/ai/define_config.d.ts.map +1 -0
- package/dist/src/ai/define_config.js +39 -0
- package/dist/src/ai/define_config.js.map +1 -0
- package/dist/src/ai/diagnoser.d.ts +34 -0
- package/dist/src/ai/diagnoser.d.ts.map +1 -0
- package/dist/src/ai/diagnoser.js +74 -0
- package/dist/src/ai/diagnoser.js.map +1 -0
- package/dist/src/ai/diagnosis_cache.d.ts +43 -0
- package/dist/src/ai/diagnosis_cache.d.ts.map +1 -0
- package/dist/src/ai/diagnosis_cache.js +56 -0
- package/dist/src/ai/diagnosis_cache.js.map +1 -0
- package/dist/src/ai/factory.d.ts +15 -0
- package/dist/src/ai/factory.d.ts.map +1 -0
- package/dist/src/ai/factory.js +24 -0
- package/dist/src/ai/factory.js.map +1 -0
- package/dist/src/ai/index.d.ts +14 -0
- package/dist/src/ai/index.d.ts.map +1 -0
- package/dist/src/ai/index.js +15 -0
- package/dist/src/ai/index.js.map +1 -0
- package/dist/src/ai/prompt.d.ts +31 -0
- package/dist/src/ai/prompt.d.ts.map +1 -0
- package/dist/src/ai/prompt.js +66 -0
- package/dist/src/ai/prompt.js.map +1 -0
- package/dist/src/ai/telescope_ai_diagnoser.d.ts +79 -0
- package/dist/src/ai/telescope_ai_diagnoser.d.ts.map +1 -0
- package/dist/src/ai/telescope_ai_diagnoser.js +111 -0
- package/dist/src/ai/telescope_ai_diagnoser.js.map +1 -0
- package/dist/src/alerts/alert_channel.d.ts +69 -0
- package/dist/src/alerts/alert_channel.d.ts.map +1 -0
- package/dist/src/alerts/alert_channel.js +114 -0
- package/dist/src/alerts/alert_channel.js.map +1 -0
- package/dist/src/alerts/alert_rule.d.ts +86 -0
- package/dist/src/alerts/alert_rule.d.ts.map +1 -0
- package/dist/src/alerts/alert_rule.js +2 -0
- package/dist/src/alerts/alert_rule.js.map +1 -0
- package/dist/src/alerts/alerter.d.ts +72 -0
- package/dist/src/alerts/alerter.d.ts.map +1 -0
- package/dist/src/alerts/alerter.js +248 -0
- package/dist/src/alerts/alerter.js.map +1 -0
- package/dist/src/alerts/define_config.d.ts +68 -0
- package/dist/src/alerts/define_config.d.ts.map +1 -0
- package/dist/src/alerts/define_config.js +57 -0
- package/dist/src/alerts/define_config.js.map +1 -0
- package/dist/src/alerts/exception_source.d.ts +44 -0
- package/dist/src/alerts/exception_source.d.ts.map +1 -0
- package/dist/src/alerts/exception_source.js +79 -0
- package/dist/src/alerts/exception_source.js.map +1 -0
- package/dist/src/alerts/index.d.ts +16 -0
- package/dist/src/alerts/index.d.ts.map +1 -0
- package/dist/src/alerts/index.js +17 -0
- package/dist/src/alerts/index.js.map +1 -0
- package/dist/src/alerts/new_exception_tracker.d.ts +50 -0
- package/dist/src/alerts/new_exception_tracker.d.ts.map +1 -0
- package/dist/src/alerts/new_exception_tracker.js +74 -0
- package/dist/src/alerts/new_exception_tracker.js.map +1 -0
- package/dist/src/alerts/parse_duration.d.ts +10 -0
- package/dist/src/alerts/parse_duration.d.ts.map +1 -0
- package/dist/src/alerts/parse_duration.js +27 -0
- package/dist/src/alerts/parse_duration.js.map +1 -0
- package/dist/src/alerts/slack_format.d.ts +60 -0
- package/dist/src/alerts/slack_format.d.ts.map +1 -0
- package/dist/src/alerts/slack_format.js +122 -0
- package/dist/src/alerts/slack_format.js.map +1 -0
- package/dist/src/context_accessor.d.ts +30 -0
- package/dist/src/context_accessor.d.ts.map +1 -0
- package/dist/src/context_accessor.js +20 -0
- package/dist/src/context_accessor.js.map +1 -0
- package/dist/src/define_config.d.ts +109 -0
- package/dist/src/define_config.d.ts.map +1 -0
- package/dist/src/define_config.js +38 -0
- package/dist/src/define_config.js.map +1 -0
- package/dist/src/diagnostics_registry.d.ts +46 -0
- package/dist/src/diagnostics_registry.d.ts.map +1 -0
- package/dist/src/diagnostics_registry.js +34 -0
- package/dist/src/diagnostics_registry.js.map +1 -0
- package/dist/src/diagnostics_watcher.d.ts +72 -0
- package/dist/src/diagnostics_watcher.d.ts.map +1 -0
- package/dist/src/diagnostics_watcher.js +119 -0
- package/dist/src/diagnostics_watcher.js.map +1 -0
- package/dist/src/entry.d.ts +81 -0
- package/dist/src/entry.d.ts.map +1 -0
- package/dist/src/entry.js +34 -0
- package/dist/src/entry.js.map +1 -0
- package/dist/src/exception_family_hash.d.ts +29 -0
- package/dist/src/exception_family_hash.d.ts.map +1 -0
- package/dist/src/exception_family_hash.js +30 -0
- package/dist/src/exception_family_hash.js.map +1 -0
- package/dist/src/exception_watcher.d.ts +66 -0
- package/dist/src/exception_watcher.d.ts.map +1 -0
- package/dist/src/exception_watcher.js +94 -0
- package/dist/src/exception_watcher.js.map +1 -0
- package/dist/src/extension/registry.d.ts +17 -0
- package/dist/src/extension/registry.d.ts.map +1 -0
- package/dist/src/extension/registry.js +56 -0
- package/dist/src/extension/registry.js.map +1 -0
- package/dist/src/extension/types.d.ts +158 -0
- package/dist/src/extension/types.d.ts.map +1 -0
- package/dist/src/extension/types.js +5 -0
- package/dist/src/extension/types.js.map +1 -0
- package/dist/src/index.d.ts +36 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +28 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/redaction/redact.d.ts +93 -0
- package/dist/src/redaction/redact.d.ts.map +1 -0
- package/dist/src/redaction/redact.js +184 -0
- package/dist/src/redaction/redact.js.map +1 -0
- package/dist/src/redaction/redacting_store.d.ts +28 -0
- package/dist/src/redaction/redacting_store.d.ts.map +1 -0
- package/dist/src/redaction/redacting_store.js +49 -0
- package/dist/src/redaction/redacting_store.js.map +1 -0
- package/dist/src/registry.d.ts +26 -0
- package/dist/src/registry.d.ts.map +1 -0
- package/dist/src/registry.js +28 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/request_watcher.d.ts +44 -0
- package/dist/src/request_watcher.d.ts.map +1 -0
- package/dist/src/request_watcher.js +37 -0
- package/dist/src/request_watcher.js.map +1 -0
- package/dist/src/service.d.ts +36 -0
- package/dist/src/service.d.ts.map +1 -0
- package/dist/src/service.js +65 -0
- package/dist/src/service.js.map +1 -0
- package/dist/src/store.d.ts +56 -0
- package/dist/src/store.d.ts.map +1 -0
- package/dist/src/store.js +2 -0
- package/dist/src/store.js.map +1 -0
- package/dist/src/stores/factory.d.ts +61 -0
- package/dist/src/stores/factory.d.ts.map +1 -0
- package/dist/src/stores/factory.js +42 -0
- package/dist/src/stores/factory.js.map +1 -0
- package/dist/src/stores/lucid.d.ts +138 -0
- package/dist/src/stores/lucid.d.ts.map +1 -0
- package/dist/src/stores/lucid.js +257 -0
- package/dist/src/stores/lucid.js.map +1 -0
- package/dist/src/stores/memory.d.ts +31 -0
- package/dist/src/stores/memory.d.ts.map +1 -0
- package/dist/src/stores/memory.js +117 -0
- package/dist/src/stores/memory.js.map +1 -0
- package/dist/src/telescope_middleware.d.ts +19 -0
- package/dist/src/telescope_middleware.d.ts.map +1 -0
- package/dist/src/telescope_middleware.js +56 -0
- package/dist/src/telescope_middleware.js.map +1 -0
- package/dist/src/ui/api.d.ts +49 -0
- package/dist/src/ui/api.d.ts.map +1 -0
- package/dist/src/ui/api.js +155 -0
- package/dist/src/ui/api.js.map +1 -0
- package/dist/src/ui/dashboard.d.ts +8 -0
- package/dist/src/ui/dashboard.d.ts.map +1 -0
- package/dist/src/ui/dashboard.html +626 -0
- package/dist/src/ui/dashboard.js +29 -0
- package/dist/src/ui/dashboard.js.map +1 -0
- package/dist/src/ui/define_config.d.ts +87 -0
- package/dist/src/ui/define_config.d.ts.map +1 -0
- package/dist/src/ui/define_config.js +104 -0
- package/dist/src/ui/define_config.js.map +1 -0
- package/dist/src/ui/extension_api.d.ts +23 -0
- package/dist/src/ui/extension_api.d.ts.map +1 -0
- package/dist/src/ui/extension_api.js +50 -0
- package/dist/src/ui/extension_api.js.map +1 -0
- package/dist/src/ui/guard.d.ts +33 -0
- package/dist/src/ui/guard.d.ts.map +1 -0
- package/dist/src/ui/guard.js +47 -0
- package/dist/src/ui/guard.js.map +1 -0
- package/dist/src/ui/http.d.ts +47 -0
- package/dist/src/ui/http.d.ts.map +1 -0
- package/dist/src/ui/http.js +43 -0
- package/dist/src/ui/http.js.map +1 -0
- package/dist/src/ui/index.d.ts +12 -0
- package/dist/src/ui/index.d.ts.map +1 -0
- package/dist/src/ui/index.js +13 -0
- package/dist/src/ui/index.js.map +1 -0
- package/dist/src/watchers/cache_watcher.d.ts +60 -0
- package/dist/src/watchers/cache_watcher.d.ts.map +1 -0
- package/dist/src/watchers/cache_watcher.js +72 -0
- package/dist/src/watchers/cache_watcher.js.map +1 -0
- package/dist/src/watchers/define_config.d.ts +38 -0
- package/dist/src/watchers/define_config.d.ts.map +1 -0
- package/dist/src/watchers/define_config.js +17 -0
- package/dist/src/watchers/define_config.js.map +1 -0
- package/dist/src/watchers/emitter.d.ts +32 -0
- package/dist/src/watchers/emitter.d.ts.map +1 -0
- package/dist/src/watchers/emitter.js +2 -0
- package/dist/src/watchers/emitter.js.map +1 -0
- package/dist/src/watchers/http_client_watcher.d.ts +74 -0
- package/dist/src/watchers/http_client_watcher.d.ts.map +1 -0
- package/dist/src/watchers/http_client_watcher.js +168 -0
- package/dist/src/watchers/http_client_watcher.js.map +1 -0
- package/dist/src/watchers/index.d.ts +19 -0
- package/dist/src/watchers/index.d.ts.map +1 -0
- package/dist/src/watchers/index.js +19 -0
- package/dist/src/watchers/index.js.map +1 -0
- package/dist/src/watchers/logs_watcher.d.ts +82 -0
- package/dist/src/watchers/logs_watcher.d.ts.map +1 -0
- package/dist/src/watchers/logs_watcher.js +145 -0
- package/dist/src/watchers/logs_watcher.js.map +1 -0
- package/dist/src/watchers/lucid_query_watcher.d.ts +64 -0
- package/dist/src/watchers/lucid_query_watcher.d.ts.map +1 -0
- package/dist/src/watchers/lucid_query_watcher.js +84 -0
- package/dist/src/watchers/lucid_query_watcher.js.map +1 -0
- package/dist/src/watchers/mail_watcher.d.ts +51 -0
- package/dist/src/watchers/mail_watcher.d.ts.map +1 -0
- package/dist/src/watchers/mail_watcher.js +93 -0
- package/dist/src/watchers/mail_watcher.js.map +1 -0
- package/dist/src/watchers/normalize_http_target.d.ts +17 -0
- package/dist/src/watchers/normalize_http_target.d.ts.map +1 -0
- package/dist/src/watchers/normalize_http_target.js +41 -0
- package/dist/src/watchers/normalize_http_target.js.map +1 -0
- package/dist/src/watchers/query_family_hash.d.ts +8 -0
- package/dist/src/watchers/query_family_hash.d.ts.map +1 -0
- package/dist/src/watchers/query_family_hash.js +31 -0
- package/dist/src/watchers/query_family_hash.js.map +1 -0
- package/dist/src/watchers/record.d.ts +22 -0
- package/dist/src/watchers/record.d.ts.map +1 -0
- package/dist/src/watchers/record.js +48 -0
- package/dist/src/watchers/record.js.map +1 -0
- package/dist/stubs/config/telescope.stub +56 -0
- package/dist/stubs/config/telescope_ai.stub +36 -0
- package/dist/stubs/config/telescope_alerts.stub +47 -0
- package/dist/stubs/config/telescope_ui.stub +40 -0
- package/dist/stubs/config/telescope_watchers.stub +30 -0
- package/dist/stubs/database/migrations/create_telescope_entries_table.stub +39 -0
- package/dist/stubs/main.d.ts +6 -0
- package/dist/stubs/main.d.ts.map +1 -0
- package/dist/stubs/main.js +7 -0
- package/dist/stubs/main.js.map +1 -0
- package/package.json +140 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { currentTraceId } from '../context_accessor.js';
|
|
2
|
+
import { EntryType } from '../entry.js';
|
|
3
|
+
import { safeRecord } from './record.js';
|
|
4
|
+
/** The standard pino/AdonisJS log levels this watcher tees, lowest → highest. */
|
|
5
|
+
export const LOG_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
|
|
6
|
+
/** Marks a wrapped method so a logger instance is teed exactly once even if two
|
|
7
|
+
* watchers (or two package copies) wrap it. */
|
|
8
|
+
const TEED = Symbol.for('@agora/telescope:logMethodTeed');
|
|
9
|
+
/**
|
|
10
|
+
* Reentrancy guard: while recording a log we may ourselves log on failure, which
|
|
11
|
+
* would re-enter the tee. The flag makes a nested log call through to the
|
|
12
|
+
* original WITHOUT recording, breaking the record→log→record loop.
|
|
13
|
+
*/
|
|
14
|
+
let recording = false;
|
|
15
|
+
/**
|
|
16
|
+
* Records AdonisJS `Logger` output as `EntryType.Log` entries, correlated to the
|
|
17
|
+
* active request via the trace id.
|
|
18
|
+
*
|
|
19
|
+
* ## How it taps the logger (non-invasive, reversible)
|
|
20
|
+
* Rather than monkey-patching a global or a prototype, `start(logger)` tees the
|
|
21
|
+
* level methods on the ONE logger instance resolved from the container. Each teed
|
|
22
|
+
* method calls the original first (host output is untouched) and then records.
|
|
23
|
+
* `stop()` restores the originals, so the tap fully unwinds on shutdown.
|
|
24
|
+
*
|
|
25
|
+
* Pino has no supported post-construction transport/hook for a live instance, so
|
|
26
|
+
* instance-scoped method-teeing is the least-invasive option that captures logs
|
|
27
|
+
* written through the app logger without owning its construction. The tee is
|
|
28
|
+
* idempotent per instance (a `Symbol.for` marker) and a reentrancy guard prevents
|
|
29
|
+
* an infinite record→log loop.
|
|
30
|
+
*
|
|
31
|
+
* Recording is fire-and-forget and fully guarded — a telescope failure can never
|
|
32
|
+
* break the host's logging.
|
|
33
|
+
*/
|
|
34
|
+
export class LogsWatcher {
|
|
35
|
+
type = EntryType.Log;
|
|
36
|
+
minLevelIndex;
|
|
37
|
+
extraTags;
|
|
38
|
+
logger = null;
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.minLevelIndex = LOG_LEVELS.indexOf(options.minLevel ?? 'trace');
|
|
41
|
+
this.extraTags = options.tags ?? [];
|
|
42
|
+
}
|
|
43
|
+
/** Tee the level methods on `logger`. Idempotent per instance. */
|
|
44
|
+
start(logger) {
|
|
45
|
+
if (this.logger)
|
|
46
|
+
return;
|
|
47
|
+
this.logger = logger;
|
|
48
|
+
const watcher = this;
|
|
49
|
+
for (let index = 0; index < LOG_LEVELS.length; index++) {
|
|
50
|
+
const level = LOG_LEVELS[index];
|
|
51
|
+
const original = logger[level];
|
|
52
|
+
if (typeof original !== 'function' || original[TEED])
|
|
53
|
+
continue;
|
|
54
|
+
const teed = function teedLevel(...args) {
|
|
55
|
+
// Nested log (e.g. our own failure logging) — call the original but
|
|
56
|
+
// don't record, breaking the record→log→record loop.
|
|
57
|
+
if (recording) {
|
|
58
|
+
original.apply(this, args);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
original.apply(this, args);
|
|
62
|
+
if (index < watcher.minLevelIndex)
|
|
63
|
+
return;
|
|
64
|
+
recording = true;
|
|
65
|
+
try {
|
|
66
|
+
watcher.record(level, args);
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
recording = false;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
teed[TEED] = true;
|
|
73
|
+
teed.__telescopeOriginal__ = original;
|
|
74
|
+
logger[level] = teed;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Restore the original level methods on the tapped instance. */
|
|
78
|
+
stop() {
|
|
79
|
+
const logger = this.logger;
|
|
80
|
+
if (!logger)
|
|
81
|
+
return;
|
|
82
|
+
this.logger = null;
|
|
83
|
+
for (const level of LOG_LEVELS) {
|
|
84
|
+
const current = logger[level];
|
|
85
|
+
if (current?.[TEED] && current.__telescopeOriginal__) {
|
|
86
|
+
logger[level] = current.__telescopeOriginal__;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Build + record a log entry from the level method's arguments. */
|
|
91
|
+
record(level, args) {
|
|
92
|
+
safeRecord(buildLogEntry(level, args, this.extraTags), 'LogsWatcher');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** A plain object literal (`{}` / `Object.create(null)`) — the only `mergingObject`
|
|
96
|
+
* shape we copy as structured context. Class instances, arrays, etc. are skipped. */
|
|
97
|
+
function isPlainObject(value) {
|
|
98
|
+
if (typeof value !== 'object' || value === null)
|
|
99
|
+
return false;
|
|
100
|
+
const proto = Object.getPrototypeOf(value);
|
|
101
|
+
return proto === Object.prototype || proto === null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Parse a pino-style level call into `{ message, context }`, mirroring pino's own
|
|
105
|
+
* argument convention: either `(message, ...)` or `(mergingObject, message?, ...)`.
|
|
106
|
+
* The context object is shallow-copied and bounded to a handful of keys here; the
|
|
107
|
+
* central redaction layer scrubs any sensitive values before storage.
|
|
108
|
+
*/
|
|
109
|
+
export function extractLog(args) {
|
|
110
|
+
const [first, second] = args;
|
|
111
|
+
if (isPlainObject(first)) {
|
|
112
|
+
const context = boundContext(first);
|
|
113
|
+
const message = typeof second === 'string' ? second : null;
|
|
114
|
+
return { message, context };
|
|
115
|
+
}
|
|
116
|
+
return { message: typeof first === 'string' ? first : null, context: null };
|
|
117
|
+
}
|
|
118
|
+
/** Shallow-copy at most the first 50 own keys of a merging object — a bound so a
|
|
119
|
+
* single log entry can't retain an unbounded structured payload. */
|
|
120
|
+
function boundContext(object) {
|
|
121
|
+
const out = {};
|
|
122
|
+
let count = 0;
|
|
123
|
+
for (const key of Object.keys(object)) {
|
|
124
|
+
if (count >= 50)
|
|
125
|
+
break;
|
|
126
|
+
out[key] = object[key];
|
|
127
|
+
count++;
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
/** Map a level + raw method args to a telescope {@link RecordInput}. Exported so
|
|
132
|
+
* the entry shape can be unit-tested without teeing a logger. */
|
|
133
|
+
export function buildLogEntry(level, args, extraTags = []) {
|
|
134
|
+
const { message, context } = extractLog(args);
|
|
135
|
+
const traceId = currentTraceId();
|
|
136
|
+
const content = { level, message, context, traceId };
|
|
137
|
+
return {
|
|
138
|
+
type: EntryType.Log,
|
|
139
|
+
familyHash: `log:${level}`,
|
|
140
|
+
content,
|
|
141
|
+
traceId,
|
|
142
|
+
tags: ['log', `log:${level}`, ...extraTags],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=logs_watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs_watcher.js","sourceRoot":"","sources":["../../../src/watchers/logs_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAoB,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,iFAAiF;AACjF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAU,CAAC;AAsCxF;gDACgD;AAChD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;AAO1D;;;;GAIG;AACH,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,WAAW;IACb,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC;IACb,aAAa,CAAS;IACtB,SAAS,CAAoB;IACtC,MAAM,GAAsB,IAAI,CAAC;IAEzC,YAAY,UAA8B,EAAE;QAC1C,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,CAAC;QACrE,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;IACtC,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,MAAkB;QACtB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,MAAM,OAAO,GAAG,IAAI,CAAC;QAErB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YACvD,MAAM,KAAK,GAAa,UAAU,CAAC,KAAK,CAAa,CAAC;YACtD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAC;YACzD,IAAI,OAAO,QAAQ,KAAK,UAAU,IAAI,QAAQ,CAAC,IAAI,CAAC;gBAAE,SAAS;YAE/D,MAAM,IAAI,GAAG,SAAS,SAAS,CAAgB,GAAG,IAAe;gBAC/D,oEAAoE;gBACpE,qDAAqD;gBACrD,IAAI,SAAS,EAAE,CAAC;oBACd,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBAC3B,OAAO;gBACT,CAAC;gBACD,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC3B,IAAI,KAAK,GAAG,OAAO,CAAC,aAAa;oBAAE,OAAO;gBAC1C,SAAS,GAAG,IAAI,CAAC;gBACjB,IAAI,CAAC;oBACH,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;gBAC9B,CAAC;wBAAS,CAAC;oBACT,SAAS,GAAG,KAAK,CAAC;gBACpB,CAAC;YACH,CAAe,CAAC;YAChB,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,qBAAqB,GAAG,QAAQ,CAAC;YACtC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QACvB,CAAC;IACH,CAAC;IAED,iEAAiE;IACjE,IAAI;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAA2B,CAAC;YACxD,IAAI,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC;gBACrD,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,qBAAqB,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IAED,oEAAoE;IAC5D,MAAM,CAAC,KAAe,EAAE,IAAe;QAC7C,UAAU,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;IACxE,CAAC;CACF;AAED;sFACsF;AACtF,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC3C,OAAO,KAAK,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAe;IAIxC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC7B,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC9E,CAAC;AAED;qEACqE;AACrE,SAAS,YAAY,CAAC,MAA+B;IACnD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,IAAI,KAAK,IAAI,EAAE;YAAE,MAAM;QACvB,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,KAAK,EAAE,CAAC;IACV,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;kEACkE;AAClE,MAAM,UAAU,aAAa,CAC3B,KAAe,EACf,IAAe,EACf,YAA+B,EAAE;IAEjC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,MAAM,OAAO,GAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACtE,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,GAAG;QACnB,UAAU,EAAE,OAAO,KAAK,EAAE;QAC1B,OAAO;QACP,OAAO;QACP,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,EAAE,EAAE,GAAG,SAAS,CAAC;KAC5C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type RecordInput } from '../entry.js';
|
|
2
|
+
import type { EmitterLike, Watcher } from './emitter.js';
|
|
3
|
+
/** The Lucid event name this watcher subscribes to. */
|
|
4
|
+
export declare const DB_QUERY_EVENT = "db:query";
|
|
5
|
+
/**
|
|
6
|
+
* The shape of the data `@adonisjs/lucid` emits on `db:query`. Mirrored
|
|
7
|
+
* structurally from `@adonisjs/lucid`'s `DbQueryEventNode` (verified against the
|
|
8
|
+
* installed `@adonisjs/lucid` types — `src/types/database.d.ts`) so the watcher
|
|
9
|
+
* has no build-time dependency on Lucid. `duration` is a `process.hrtime()` tuple
|
|
10
|
+
* `[seconds, nanoseconds]`.
|
|
11
|
+
*/
|
|
12
|
+
export interface DbQueryEventLike {
|
|
13
|
+
connection: string;
|
|
14
|
+
sql: string;
|
|
15
|
+
method: string;
|
|
16
|
+
bindings?: unknown[];
|
|
17
|
+
duration?: [number, number];
|
|
18
|
+
model?: string;
|
|
19
|
+
ddl?: boolean;
|
|
20
|
+
inTransaction?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** The recorded body of a `query` entry. */
|
|
23
|
+
export interface QueryEntryContent {
|
|
24
|
+
/** The executed SQL. */
|
|
25
|
+
sql: string;
|
|
26
|
+
/** The bound parameter values, in order. */
|
|
27
|
+
bindings: unknown[];
|
|
28
|
+
/** Query duration in milliseconds, or `null` when Lucid did not report one. */
|
|
29
|
+
durationMs: number | null;
|
|
30
|
+
/** The Lucid connection name (e.g. `'primary'`). */
|
|
31
|
+
connection: string;
|
|
32
|
+
/** The query method (`'select'`, `'insert'`, …), or `null` when absent. */
|
|
33
|
+
method: string | null;
|
|
34
|
+
/** Whether the query ran inside a transaction, when Lucid reported it. */
|
|
35
|
+
inTransaction: boolean | null;
|
|
36
|
+
/** The active trace id at query time, or `null`. */
|
|
37
|
+
traceId: string | null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Records every SQL query Lucid executes as a `query` telescope entry — the
|
|
41
|
+
* headline watcher of this package. It subscribes to Lucid's `db:query` event
|
|
42
|
+
* (the same event `db.prettyPrint` consumes) and captures the SQL, bindings,
|
|
43
|
+
* duration, and connection, correlated to the active request via the trace id.
|
|
44
|
+
*
|
|
45
|
+
* Recording is fire-and-forget and fully guarded: a telescope failure can never
|
|
46
|
+
* break or block a query. Grouping is by SQL template (see
|
|
47
|
+
* {@link queryFamilyHash}) so a dashboard can roll up "the same query".
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* Lucid only emits `db:query` when the connection's `debug` flag is on (or a
|
|
51
|
+
* `db:query` listener exists at query-report start). Subscribing here is enough to
|
|
52
|
+
* make Lucid report; enabling `debug` in your DB config guarantees it.
|
|
53
|
+
*/
|
|
54
|
+
export declare class LucidQueryWatcher implements Watcher {
|
|
55
|
+
readonly type: "query";
|
|
56
|
+
private unsubscribe;
|
|
57
|
+
start(emitter: EmitterLike): void;
|
|
58
|
+
stop(): void;
|
|
59
|
+
/** Validate + record a single `db:query` payload, never throwing. */
|
|
60
|
+
private handle;
|
|
61
|
+
}
|
|
62
|
+
/** Map a Lucid `db:query` payload to a telescope {@link RecordInput}. */
|
|
63
|
+
export declare function buildQueryEntry(event: DbQueryEventLike): RecordInput<QueryEntryContent>;
|
|
64
|
+
//# sourceMappingURL=lucid_query_watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lucid_query_watcher.d.ts","sourceRoot":"","sources":["../../../src/watchers/lucid_query_watcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAIzD,uDAAuD;AACvD,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,+EAA+E;IAC/E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,0EAA0E;IAC1E,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,oDAAoD;IACpD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAiBD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAkB,YAAW,OAAO;IAC/C,QAAQ,CAAC,IAAI,UAAmB;IAChC,OAAO,CAAC,WAAW,CAA6B;IAEhD,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAKjC,IAAI,IAAI,IAAI;IAKZ,qEAAqE;IACrE,OAAO,CAAC,MAAM;CAIf;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAyBvF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { currentTraceId } from '../context_accessor.js';
|
|
2
|
+
import { EntryType } from '../entry.js';
|
|
3
|
+
import { queryFamilyHash } from './query_family_hash.js';
|
|
4
|
+
import { safeRecord } from './record.js';
|
|
5
|
+
/** The Lucid event name this watcher subscribes to. */
|
|
6
|
+
export const DB_QUERY_EVENT = 'db:query';
|
|
7
|
+
/** Convert a `process.hrtime()` `[seconds, nanoseconds]` tuple to milliseconds. */
|
|
8
|
+
function hrtimeToMs(duration) {
|
|
9
|
+
if (!Array.isArray(duration) || duration.length !== 2)
|
|
10
|
+
return null;
|
|
11
|
+
const [seconds, nanoseconds] = duration;
|
|
12
|
+
if (typeof seconds !== 'number' || typeof nanoseconds !== 'number')
|
|
13
|
+
return null;
|
|
14
|
+
return seconds * 1000 + nanoseconds / 1e6;
|
|
15
|
+
}
|
|
16
|
+
/** Narrow an unknown emitter payload to a {@link DbQueryEventLike}. */
|
|
17
|
+
function isDbQueryEvent(data) {
|
|
18
|
+
if (typeof data !== 'object' || data === null)
|
|
19
|
+
return false;
|
|
20
|
+
const candidate = data;
|
|
21
|
+
return typeof candidate.sql === 'string' && typeof candidate.connection === 'string';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Records every SQL query Lucid executes as a `query` telescope entry — the
|
|
25
|
+
* headline watcher of this package. It subscribes to Lucid's `db:query` event
|
|
26
|
+
* (the same event `db.prettyPrint` consumes) and captures the SQL, bindings,
|
|
27
|
+
* duration, and connection, correlated to the active request via the trace id.
|
|
28
|
+
*
|
|
29
|
+
* Recording is fire-and-forget and fully guarded: a telescope failure can never
|
|
30
|
+
* break or block a query. Grouping is by SQL template (see
|
|
31
|
+
* {@link queryFamilyHash}) so a dashboard can roll up "the same query".
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* Lucid only emits `db:query` when the connection's `debug` flag is on (or a
|
|
35
|
+
* `db:query` listener exists at query-report start). Subscribing here is enough to
|
|
36
|
+
* make Lucid report; enabling `debug` in your DB config guarantees it.
|
|
37
|
+
*/
|
|
38
|
+
export class LucidQueryWatcher {
|
|
39
|
+
type = EntryType.Query;
|
|
40
|
+
unsubscribe = null;
|
|
41
|
+
start(emitter) {
|
|
42
|
+
if (this.unsubscribe)
|
|
43
|
+
return;
|
|
44
|
+
this.unsubscribe = emitter.on(DB_QUERY_EVENT, (data) => this.handle(data));
|
|
45
|
+
}
|
|
46
|
+
stop() {
|
|
47
|
+
this.unsubscribe?.();
|
|
48
|
+
this.unsubscribe = null;
|
|
49
|
+
}
|
|
50
|
+
/** Validate + record a single `db:query` payload, never throwing. */
|
|
51
|
+
handle(data) {
|
|
52
|
+
if (!isDbQueryEvent(data))
|
|
53
|
+
return;
|
|
54
|
+
safeRecord(buildQueryEntry(data), 'LucidQueryWatcher');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Map a Lucid `db:query` payload to a telescope {@link RecordInput}. */
|
|
58
|
+
export function buildQueryEntry(event) {
|
|
59
|
+
const durationMs = hrtimeToMs(event.duration);
|
|
60
|
+
const method = typeof event.method === 'string' ? event.method : null;
|
|
61
|
+
const traceId = currentTraceId();
|
|
62
|
+
const content = {
|
|
63
|
+
sql: event.sql,
|
|
64
|
+
bindings: Array.isArray(event.bindings) ? event.bindings : [],
|
|
65
|
+
durationMs,
|
|
66
|
+
connection: event.connection,
|
|
67
|
+
method,
|
|
68
|
+
inTransaction: typeof event.inTransaction === 'boolean' ? event.inTransaction : null,
|
|
69
|
+
traceId,
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
type: EntryType.Query,
|
|
73
|
+
familyHash: queryFamilyHash(event.sql),
|
|
74
|
+
content,
|
|
75
|
+
durationMs,
|
|
76
|
+
traceId,
|
|
77
|
+
tags: [
|
|
78
|
+
`connection:${event.connection}`,
|
|
79
|
+
...(method ? [`method:${method}`] : []),
|
|
80
|
+
...(event.model ? [`model:${event.model}`] : []),
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=lucid_query_watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lucid_query_watcher.js","sourceRoot":"","sources":["../../../src/watchers/lucid_query_watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type RecordInput } from '../entry.js';
|
|
2
|
+
import type { EmitterLike, Watcher } from './emitter.js';
|
|
3
|
+
/** The `@adonisjs/mail` event name this watcher subscribes to. */
|
|
4
|
+
export declare const MAIL_SENT_EVENT = "mail:sent";
|
|
5
|
+
/**
|
|
6
|
+
* The structural slice of the `mail:sent` payload this watcher reads. The exact
|
|
7
|
+
* runtime shape is owned by `@adonisjs/mail` (not installed in this repo, so it
|
|
8
|
+
* could not be verified against its types — see this package's README). The
|
|
9
|
+
* watcher therefore reads every field defensively and degrades to `null`/`[]` for
|
|
10
|
+
* anything missing, rather than assuming a precise shape.
|
|
11
|
+
*
|
|
12
|
+
* `@adonisjs/mail` carries the composed message under `message` — typically a
|
|
13
|
+
* `Message` instance exposing `toJSON()`/`toObject()` whose `.message` holds the
|
|
14
|
+
* envelope (`to`/`from`/`subject`/…). Both the instance form and a plain-object
|
|
15
|
+
* form are handled.
|
|
16
|
+
*/
|
|
17
|
+
export interface MailSentEventLike {
|
|
18
|
+
mailerName?: string;
|
|
19
|
+
message?: unknown;
|
|
20
|
+
}
|
|
21
|
+
/** The recorded body of a `mail` entry. */
|
|
22
|
+
export interface MailEntryContent {
|
|
23
|
+
/** The mailer that sent the message (e.g. `'smtp'`), or `null`. */
|
|
24
|
+
mailer: string | null;
|
|
25
|
+
/** The sender address, or `null`. */
|
|
26
|
+
from: string | null;
|
|
27
|
+
/** The recipient addresses (`to`), flattened to strings. */
|
|
28
|
+
to: string[];
|
|
29
|
+
/** The subject line, or `null`. */
|
|
30
|
+
subject: string | null;
|
|
31
|
+
/** The active trace id at send time, or `null`. */
|
|
32
|
+
traceId: string | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Records every email AdonisJS sends as a `mail` telescope entry. Subscribes to
|
|
36
|
+
* `@adonisjs/mail`'s `mail:sent` event and captures the mailer, sender, recipients
|
|
37
|
+
* and subject, correlated to the active request via the trace id.
|
|
38
|
+
*
|
|
39
|
+
* Recording is fire-and-forget and fully guarded — a telescope failure can never
|
|
40
|
+
* break a send.
|
|
41
|
+
*/
|
|
42
|
+
export declare class MailWatcher implements Watcher {
|
|
43
|
+
readonly type: "mail";
|
|
44
|
+
private unsubscribe;
|
|
45
|
+
start(emitter: EmitterLike): void;
|
|
46
|
+
stop(): void;
|
|
47
|
+
private handle;
|
|
48
|
+
}
|
|
49
|
+
/** Map a `mail:sent` payload to a telescope {@link RecordInput}. */
|
|
50
|
+
export declare function buildMailEntry(event: MailSentEventLike): RecordInput<MailEntryContent>;
|
|
51
|
+
//# sourceMappingURL=mail_watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail_watcher.d.ts","sourceRoot":"","sources":["../../../src/watchers/mail_watcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGzD,kEAAkE;AAClE,eAAO,MAAM,eAAe,cAAc,CAAC;AAQ3C;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,qCAAqC;IACrC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,4DAA4D;IAC5D,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,mCAAmC;IACnC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,mDAAmD;IACnD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AA2CD;;;;;;;GAOG;AACH,qBAAa,WAAY,YAAW,OAAO;IACzC,QAAQ,CAAC,IAAI,SAAkB;IAC/B,OAAO,CAAC,WAAW,CAA6B;IAEhD,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAKjC,IAAI,IAAI,IAAI;IAKZ,OAAO,CAAC,MAAM;CAIf;AAED,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,WAAW,CAAC,gBAAgB,CAAC,CAkBtF"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { currentTraceId } from '../context_accessor.js';
|
|
2
|
+
import { EntryType } from '../entry.js';
|
|
3
|
+
import { safeRecord } from './record.js';
|
|
4
|
+
/** The `@adonisjs/mail` event name this watcher subscribes to. */
|
|
5
|
+
export const MAIL_SENT_EVENT = 'mail:sent';
|
|
6
|
+
/** Normalize a mail address (string | `{address}`) to a plain address string. */
|
|
7
|
+
function addressToString(value) {
|
|
8
|
+
if (typeof value === 'string')
|
|
9
|
+
return value;
|
|
10
|
+
if (value && typeof value === 'object' && typeof value.address === 'string')
|
|
11
|
+
return value.address;
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
/** Normalize a `to` field (one address, or many) to a flat string[]. */
|
|
15
|
+
function normalizeRecipients(to) {
|
|
16
|
+
const list = Array.isArray(to) ? to : to === undefined || to === null ? [] : [to];
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const item of list) {
|
|
19
|
+
const address = addressToString(item);
|
|
20
|
+
if (address)
|
|
21
|
+
out.push(address);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
/** Pull the message envelope object out of the (instance-or-plain) `message`. */
|
|
26
|
+
function readEnvelope(message) {
|
|
27
|
+
if (message === null || typeof message !== 'object')
|
|
28
|
+
return null;
|
|
29
|
+
const candidate = message;
|
|
30
|
+
// `Message` instances expose `toJSON()` returning `{ message, views }`.
|
|
31
|
+
if (typeof candidate.toJSON === 'function') {
|
|
32
|
+
try {
|
|
33
|
+
const json = candidate.toJSON();
|
|
34
|
+
if (json && typeof json === 'object') {
|
|
35
|
+
const inner = json.message;
|
|
36
|
+
if (inner && typeof inner === 'object')
|
|
37
|
+
return inner;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// fall through to the structural reads below
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Plain-object form: either the envelope itself, or `{ message: envelope }`.
|
|
45
|
+
if (candidate.message && typeof candidate.message === 'object') {
|
|
46
|
+
return candidate.message;
|
|
47
|
+
}
|
|
48
|
+
return candidate;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Records every email AdonisJS sends as a `mail` telescope entry. Subscribes to
|
|
52
|
+
* `@adonisjs/mail`'s `mail:sent` event and captures the mailer, sender, recipients
|
|
53
|
+
* and subject, correlated to the active request via the trace id.
|
|
54
|
+
*
|
|
55
|
+
* Recording is fire-and-forget and fully guarded — a telescope failure can never
|
|
56
|
+
* break a send.
|
|
57
|
+
*/
|
|
58
|
+
export class MailWatcher {
|
|
59
|
+
type = EntryType.Mail;
|
|
60
|
+
unsubscribe = null;
|
|
61
|
+
start(emitter) {
|
|
62
|
+
if (this.unsubscribe)
|
|
63
|
+
return;
|
|
64
|
+
this.unsubscribe = emitter.on(MAIL_SENT_EVENT, (data) => this.handle(data));
|
|
65
|
+
}
|
|
66
|
+
stop() {
|
|
67
|
+
this.unsubscribe?.();
|
|
68
|
+
this.unsubscribe = null;
|
|
69
|
+
}
|
|
70
|
+
handle(data) {
|
|
71
|
+
if (typeof data !== 'object' || data === null)
|
|
72
|
+
return;
|
|
73
|
+
safeRecord(buildMailEntry(data), 'MailWatcher');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Map a `mail:sent` payload to a telescope {@link RecordInput}. */
|
|
77
|
+
export function buildMailEntry(event) {
|
|
78
|
+
const mailer = typeof event.mailerName === 'string' ? event.mailerName : null;
|
|
79
|
+
const envelope = readEnvelope(event.message);
|
|
80
|
+
const from = envelope ? (addressToString(envelope.from) ?? null) : null;
|
|
81
|
+
const to = envelope ? normalizeRecipients(envelope.to) : [];
|
|
82
|
+
const subject = envelope && typeof envelope.subject === 'string' ? envelope.subject : null;
|
|
83
|
+
const traceId = currentTraceId();
|
|
84
|
+
const content = { mailer, from, to, subject, traceId };
|
|
85
|
+
return {
|
|
86
|
+
type: EntryType.Mail,
|
|
87
|
+
familyHash: mailer,
|
|
88
|
+
content,
|
|
89
|
+
traceId,
|
|
90
|
+
tags: [...(mailer ? [`mailer:${mailer}`] : [])],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=mail_watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail_watcher.js","sourceRoot":"","sources":["../../../src/watchers/mail_watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a stable family for an OUTGOING HTTP call from a method + (possibly
|
|
3
|
+
* absolute) url. Keeps the host and normalizes the path's id-like segments
|
|
4
|
+
* (UUID / all-digits / long hex) to `:id`, so calls to the same external
|
|
5
|
+
* endpoint group together regardless of ids.
|
|
6
|
+
*
|
|
7
|
+
* Ported from `nestjs-telescope`'s `query/normalize-route.ts`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* normalizeHttpTarget('GET', 'https://api.stripe.com/v1/charges/ch_123?foo=1')
|
|
11
|
+
* // => 'GET api.stripe.com/v1/charges/:id'
|
|
12
|
+
*
|
|
13
|
+
* Falls back to normalizing the raw url (query string stripped) when it can't be
|
|
14
|
+
* parsed as absolute (e.g. a relative target), so a family is always produced.
|
|
15
|
+
*/
|
|
16
|
+
export declare function normalizeHttpTarget(method: string, url: string): string;
|
|
17
|
+
//# sourceMappingURL=normalize_http_target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize_http_target.d.ts","sourceRoot":"","sources":["../../../src/watchers/normalize_http_target.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAgBvE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2
|
+
const ALL_DIGITS_PATTERN = /^\d+$/;
|
|
3
|
+
const LONG_HEX_PATTERN = /^[0-9a-f]{16,}$/i;
|
|
4
|
+
/** A path segment is an id when it is a UUID, all digits, or a long hex string. */
|
|
5
|
+
function isIdSegment(segment) {
|
|
6
|
+
return (UUID_PATTERN.test(segment) || ALL_DIGITS_PATTERN.test(segment) || LONG_HEX_PATTERN.test(segment));
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Build a stable family for an OUTGOING HTTP call from a method + (possibly
|
|
10
|
+
* absolute) url. Keeps the host and normalizes the path's id-like segments
|
|
11
|
+
* (UUID / all-digits / long hex) to `:id`, so calls to the same external
|
|
12
|
+
* endpoint group together regardless of ids.
|
|
13
|
+
*
|
|
14
|
+
* Ported from `nestjs-telescope`'s `query/normalize-route.ts`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* normalizeHttpTarget('GET', 'https://api.stripe.com/v1/charges/ch_123?foo=1')
|
|
18
|
+
* // => 'GET api.stripe.com/v1/charges/:id'
|
|
19
|
+
*
|
|
20
|
+
* Falls back to normalizing the raw url (query string stripped) when it can't be
|
|
21
|
+
* parsed as absolute (e.g. a relative target), so a family is always produced.
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeHttpTarget(method, url) {
|
|
24
|
+
let host = '';
|
|
25
|
+
let path = url;
|
|
26
|
+
try {
|
|
27
|
+
const parsed = new URL(url);
|
|
28
|
+
host = parsed.host;
|
|
29
|
+
path = parsed.pathname;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
const queryStart = url.indexOf('?');
|
|
33
|
+
path = queryStart === -1 ? url : url.slice(0, queryStart);
|
|
34
|
+
}
|
|
35
|
+
const normalizedPath = path
|
|
36
|
+
.split('/')
|
|
37
|
+
.map((segment) => (isIdSegment(segment) ? ':id' : segment))
|
|
38
|
+
.join('/');
|
|
39
|
+
return `${method} ${host}${normalizedPath}`;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=normalize_http_target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize_http_target.js","sourceRoot":"","sources":["../../../src/watchers/normalize_http_target.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,iEAAiE,CAAC;AACvF,MAAM,kBAAkB,GAAG,OAAO,CAAC;AACnC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAE5C,mFAAmF;AACnF,SAAS,WAAW,CAAC,OAAe;IAClC,OAAO,CACL,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CACjG,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,GAAW;IAC7D,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,IAAI,GAAG,GAAG,CAAC;IACf,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACnB,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,cAAc,GAAG,IAAI;SACxB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;SAC1D,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,OAAO,GAAG,MAAM,IAAI,IAAI,GAAG,cAAc,EAAE,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grouping key for "the same SQL query" — same template, any bound values. Ported
|
|
3
|
+
* from `nestjs-telescope`'s query family-hash so a dashboard can roll up every
|
|
4
|
+
* execution of `select * from users where id = ?` regardless of the id.
|
|
5
|
+
*/
|
|
6
|
+
/** Grouping key for "the same query" — same template, any bound values. */
|
|
7
|
+
export declare function queryFamilyHash(sql: string): string;
|
|
8
|
+
//# sourceMappingURL=query_family_hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query_family_hash.d.ts","sourceRoot":"","sources":["../../../src/watchers/query_family_hash.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyBH,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grouping key for "the same SQL query" — same template, any bound values. Ported
|
|
3
|
+
* from `nestjs-telescope`'s query family-hash so a dashboard can roll up every
|
|
4
|
+
* execution of `select * from users where id = ?` regardless of the id.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Normalize an SQL string into a template: lowercase, collapse whitespace, and
|
|
8
|
+
* replace string/number literals with `?` so only the query shape remains.
|
|
9
|
+
*/
|
|
10
|
+
function normalizeSql(sql) {
|
|
11
|
+
return sql
|
|
12
|
+
.toLowerCase()
|
|
13
|
+
.replace(/'(?:[^']|'')*'/g, '?') // string literals
|
|
14
|
+
.replace(/\b\d+(?:\.\d+)?\b/g, '?') // numeric literals
|
|
15
|
+
.replace(/\s+/g, ' ')
|
|
16
|
+
.trim();
|
|
17
|
+
}
|
|
18
|
+
/** Stable 32-bit FNV-1a hash, hex-encoded. Deterministic across processes. */
|
|
19
|
+
function fnv1a(input) {
|
|
20
|
+
let hash = 0x811c9dc5;
|
|
21
|
+
for (let i = 0; i < input.length; i++) {
|
|
22
|
+
hash ^= input.charCodeAt(i);
|
|
23
|
+
hash = Math.imul(hash, 0x01000193);
|
|
24
|
+
}
|
|
25
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
26
|
+
}
|
|
27
|
+
/** Grouping key for "the same query" — same template, any bound values. */
|
|
28
|
+
export function queryFamilyHash(sql) {
|
|
29
|
+
return fnv1a(normalizeSql(sql));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=query_family_hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query_family_hash.js","sourceRoot":"","sources":["../../../src/watchers/query_family_hash.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,OAAO,GAAG;SACP,WAAW,EAAE;SACb,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC,kBAAkB;SAClD,OAAO,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC,mBAAmB;SACtD,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,8EAA8E;AAC9E,SAAS,KAAK,CAAC,KAAa;IAC1B,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACpD,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RecordInput } from '../entry.js';
|
|
2
|
+
import type { TelescopeStore } from '../store.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the active telescope store WITHOUT dependency injection. `@adonis-agora/telescope`'s
|
|
5
|
+
* provider publishes the live store on a cross-copy-stable global slot at boot
|
|
6
|
+
* (see its `registry.ts`); the request middleware records through that same handle.
|
|
7
|
+
* Reusing it here lets these watchers record from inside synchronous emitter
|
|
8
|
+
* subscribers — where there is no container to `inject()` from — and degrade to a
|
|
9
|
+
* no-op when telescope is disabled or not booted.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveStore(): TelescopeStore | null;
|
|
12
|
+
/**
|
|
13
|
+
* Record one entry through the runtime store, swallowing every failure. Watchers
|
|
14
|
+
* run inside emitter subscribers on the host's hot paths, so this is strictly
|
|
15
|
+
* fire-and-forget: a missing store, a throwing `record`, or a rejected promise can
|
|
16
|
+
* never break (or block) the emit it is observing.
|
|
17
|
+
*
|
|
18
|
+
* The active `@adonis-agora/context` trace id is backfilled when the caller did not set
|
|
19
|
+
* `traceId` explicitly, mirroring how the core watchers correlate entries.
|
|
20
|
+
*/
|
|
21
|
+
export declare function safeRecord(input: RecordInput, source: string): void;
|
|
22
|
+
//# sourceMappingURL=record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../../src/watchers/record.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,cAAc,GAAG,IAAI,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAmBnE"}
|