@zintrust/trace 0.4.76 → 0.4.77
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 +101 -15
- package/dist/build-manifest.json +78 -38
- package/dist/config.d.ts +1 -0
- package/dist/config.js +123 -4
- package/dist/dashboard/ui.js +80 -23
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/migrations/20260331000001_create_zin_trace_entries_table.js +1 -1
- package/dist/migrations/20260407193000_widen_trace_created_at_for_sql.d.ts +10 -0
- package/dist/migrations/20260407193000_widen_trace_created_at_for_sql.js +34 -0
- package/dist/migrations/index.js +2 -1
- package/dist/register.js +107 -9
- package/dist/storage/TraceContentRedaction.d.ts +4 -0
- package/dist/storage/TraceContentRedaction.js +33 -0
- package/dist/storage/TraceEntryFiltering.d.ts +4 -0
- package/dist/storage/TraceEntryFiltering.js +13 -0
- package/dist/storage/TraceStorage.js +35 -5
- package/dist/storage/TraceWriteDiagnostics.d.ts +19 -0
- package/dist/storage/TraceWriteDiagnostics.js +98 -0
- package/dist/types.d.ts +37 -20
- package/dist/utils/entryFilter.d.ts +4 -0
- package/dist/utils/entryFilter.js +95 -0
- package/dist/utils/redact.d.ts +1 -0
- package/dist/utils/redact.js +43 -9
- package/dist/watchers/CommandWatcher.js +1 -1
- package/dist/watchers/HttpClientWatcher.js +1 -1
- package/dist/watchers/HttpWatcher.js +104 -20
- package/dist/watchers/LogWatcher.js +1 -0
- package/package.json +3 -3
- package/src/config.ts +152 -5
- package/src/dashboard/routes.ts +6 -2
- package/src/dashboard/ui.ts +80 -23
- package/src/index.ts +7 -0
- package/src/register.ts +137 -10
- package/src/storage/TraceContentRedaction.ts +44 -0
- package/src/storage/TraceEntryFiltering.ts +14 -0
- package/src/storage/TraceStorage.ts +52 -5
- package/src/storage/TraceWriteDiagnostics.ts +174 -0
- package/src/types.ts +40 -20
- package/src/utils/entryFilter.ts +108 -0
- package/src/utils/redact.ts +57 -9
- package/src/watchers/CommandWatcher.ts +1 -1
- package/src/watchers/HttpClientWatcher.ts +1 -1
- package/src/watchers/HttpWatcher.ts +132 -21
- package/src/watchers/LogWatcher.ts +27 -27
package/src/config.ts
CHANGED
|
@@ -1,7 +1,107 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TraceConfig — defaults and merge helper for @zintrust/trace
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
ITraceConfig,
|
|
6
|
+
TraceConfigOverrides,
|
|
7
|
+
TraceFilterRule,
|
|
8
|
+
TraceRequestWatcherConfig,
|
|
9
|
+
TraceWatcherToggle,
|
|
10
|
+
} from './types';
|
|
11
|
+
|
|
12
|
+
const mergeStringLists = (base: string[], override?: string[]): string[] => {
|
|
13
|
+
const merged = new Set<string>();
|
|
14
|
+
|
|
15
|
+
for (const value of [...base, ...(override ?? [])]) {
|
|
16
|
+
if (typeof value !== 'string') continue;
|
|
17
|
+
const normalized = value.trim();
|
|
18
|
+
if (normalized !== '') merged.add(normalized);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return [...merged];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const isObjectValue = (value: unknown): value is Record<string, unknown> => {
|
|
25
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const mergeFilterRule = (
|
|
29
|
+
base?: TraceFilterRule,
|
|
30
|
+
override?: TraceFilterRule
|
|
31
|
+
): TraceFilterRule | undefined => {
|
|
32
|
+
const include = mergeStringLists(base?.include ?? [], override?.include);
|
|
33
|
+
const exclude = mergeStringLists(base?.exclude ?? [], override?.exclude);
|
|
34
|
+
|
|
35
|
+
if (include.length === 0 && exclude.length === 0) return undefined;
|
|
36
|
+
|
|
37
|
+
return Object.freeze({
|
|
38
|
+
...(include.length > 0 ? { include } : {}),
|
|
39
|
+
...(exclude.length > 0 ? { exclude } : {}),
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const mergeWatcherToggle = (
|
|
44
|
+
base?: TraceWatcherToggle,
|
|
45
|
+
override?: TraceWatcherToggle
|
|
46
|
+
): TraceWatcherToggle | undefined => {
|
|
47
|
+
if (override === undefined) return base;
|
|
48
|
+
if (override === false || override === true) return override;
|
|
49
|
+
|
|
50
|
+
const baseRule = isObjectValue(base) ? base : undefined;
|
|
51
|
+
return mergeFilterRule(baseRule, override);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const REQUEST_METHOD_KEYS = ['all', 'get', 'post', 'put', 'patch', 'delete'] as const;
|
|
55
|
+
|
|
56
|
+
const mergeRequestWatcherToggle = (
|
|
57
|
+
base?: ITraceConfig['watchers']['request'],
|
|
58
|
+
override?: ITraceConfig['watchers']['request']
|
|
59
|
+
): ITraceConfig['watchers']['request'] | undefined => {
|
|
60
|
+
if (override === undefined) return base;
|
|
61
|
+
if (override === false || override === true) return override;
|
|
62
|
+
|
|
63
|
+
const baseConfig = isObjectValue(base) ? base : undefined;
|
|
64
|
+
const merged: TraceRequestWatcherConfig = mergeFilterRule(baseConfig, override) ?? {};
|
|
65
|
+
|
|
66
|
+
for (const key of REQUEST_METHOD_KEYS) {
|
|
67
|
+
const rule = mergeFilterRule(baseConfig?.[key], override[key]);
|
|
68
|
+
if (rule !== undefined) merged[key] = rule;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return merged;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const mergeWatchers = (
|
|
75
|
+
base: ITraceConfig['watchers'],
|
|
76
|
+
override?: TraceConfigOverrides['watchers']
|
|
77
|
+
): ITraceConfig['watchers'] => {
|
|
78
|
+
if (override === undefined) return { ...base };
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
...base,
|
|
82
|
+
...override,
|
|
83
|
+
request: mergeRequestWatcherToggle(base.request, override.request),
|
|
84
|
+
query: mergeWatcherToggle(base.query, override.query),
|
|
85
|
+
exception: mergeWatcherToggle(base.exception, override.exception),
|
|
86
|
+
log: mergeWatcherToggle(base.log, override.log),
|
|
87
|
+
job: mergeWatcherToggle(base.job, override.job),
|
|
88
|
+
cache: mergeWatcherToggle(base.cache, override.cache),
|
|
89
|
+
schedule: mergeWatcherToggle(base.schedule, override.schedule),
|
|
90
|
+
mail: mergeWatcherToggle(base.mail, override.mail),
|
|
91
|
+
auth: mergeWatcherToggle(base.auth, override.auth),
|
|
92
|
+
event: mergeWatcherToggle(base.event, override.event),
|
|
93
|
+
model: mergeWatcherToggle(base.model, override.model),
|
|
94
|
+
notification: mergeWatcherToggle(base.notification, override.notification),
|
|
95
|
+
redis: mergeWatcherToggle(base.redis, override.redis),
|
|
96
|
+
gate: mergeWatcherToggle(base.gate, override.gate),
|
|
97
|
+
middleware: mergeWatcherToggle(base.middleware, override.middleware),
|
|
98
|
+
command: mergeWatcherToggle(base.command, override.command),
|
|
99
|
+
batch: mergeWatcherToggle(base.batch, override.batch),
|
|
100
|
+
dump: mergeWatcherToggle(base.dump, override.dump),
|
|
101
|
+
view: mergeWatcherToggle(base.view, override.view),
|
|
102
|
+
clientRequest: mergeWatcherToggle(base.clientRequest, override.clientRequest),
|
|
103
|
+
};
|
|
104
|
+
};
|
|
5
105
|
|
|
6
106
|
const DEFAULTS: ITraceConfig = Object.freeze({
|
|
7
107
|
enabled: false,
|
|
@@ -12,6 +112,37 @@ const DEFAULTS: ITraceConfig = Object.freeze({
|
|
|
12
112
|
logMinLevel: 'info',
|
|
13
113
|
watchers: {},
|
|
14
114
|
redaction: {
|
|
115
|
+
keys: [
|
|
116
|
+
'password',
|
|
117
|
+
'pass',
|
|
118
|
+
'passwd',
|
|
119
|
+
'token',
|
|
120
|
+
'accessToken',
|
|
121
|
+
'access_token',
|
|
122
|
+
'refreshToken',
|
|
123
|
+
'refresh_token',
|
|
124
|
+
'secret',
|
|
125
|
+
'secretKey',
|
|
126
|
+
'secret_key',
|
|
127
|
+
'apiKey',
|
|
128
|
+
'api_key',
|
|
129
|
+
'auth',
|
|
130
|
+
'authToken',
|
|
131
|
+
'auth_token',
|
|
132
|
+
'authorization',
|
|
133
|
+
'cookie',
|
|
134
|
+
'session',
|
|
135
|
+
'sessionId',
|
|
136
|
+
'session_id',
|
|
137
|
+
'card',
|
|
138
|
+
'cardNumber',
|
|
139
|
+
'card_number',
|
|
140
|
+
'cardToken',
|
|
141
|
+
'card_token',
|
|
142
|
+
'cvv',
|
|
143
|
+
'cvc',
|
|
144
|
+
'pan',
|
|
145
|
+
],
|
|
15
146
|
headers: ['authorization', 'cookie', 'x-api-key', 'x-auth-token'],
|
|
16
147
|
body: ['password', 'token', 'secret', 'apiKey', 'api_key', 'jwt', 'bearer'],
|
|
17
148
|
query: [],
|
|
@@ -20,7 +151,20 @@ const DEFAULTS: ITraceConfig = Object.freeze({
|
|
|
20
151
|
|
|
21
152
|
const isWatcherEnabled = (config: ITraceConfig, key: keyof ITraceConfig['watchers']): boolean => {
|
|
22
153
|
const override = config.watchers[key];
|
|
23
|
-
|
|
154
|
+
if (override === false) return false;
|
|
155
|
+
if (isObjectValue(override) && override.enabled === false) return false;
|
|
156
|
+
return true; // undefined = enabled by default; explicit false = disabled
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const getRedactionFields = (
|
|
160
|
+
config: ITraceConfig,
|
|
161
|
+
key: keyof ITraceConfig['redaction']
|
|
162
|
+
): string[] => {
|
|
163
|
+
if (key === 'keys') {
|
|
164
|
+
return mergeStringLists([], config.redaction.keys);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return mergeStringLists(config.redaction.keys, config.redaction[key]);
|
|
24
168
|
};
|
|
25
169
|
|
|
26
170
|
export const TraceConfig = Object.freeze({
|
|
@@ -33,14 +177,17 @@ export const TraceConfig = Object.freeze({
|
|
|
33
177
|
return Object.freeze({
|
|
34
178
|
...DEFAULTS,
|
|
35
179
|
...overrides,
|
|
36
|
-
watchers:
|
|
180
|
+
watchers: mergeWatchers(DEFAULTS.watchers, overrides.watchers),
|
|
37
181
|
redaction: {
|
|
38
|
-
|
|
39
|
-
|
|
182
|
+
keys: mergeStringLists(DEFAULTS.redaction.keys, overrides.redaction?.keys),
|
|
183
|
+
headers: mergeStringLists(DEFAULTS.redaction.headers, overrides.redaction?.headers),
|
|
184
|
+
body: mergeStringLists(DEFAULTS.redaction.body, overrides.redaction?.body),
|
|
185
|
+
query: mergeStringLists(DEFAULTS.redaction.query, overrides.redaction?.query),
|
|
40
186
|
},
|
|
41
187
|
ignoreRoutes: overrides.ignoreRoutes ?? DEFAULTS.ignoreRoutes,
|
|
42
188
|
});
|
|
43
189
|
},
|
|
44
190
|
|
|
191
|
+
getRedactionFields,
|
|
45
192
|
isWatcherEnabled,
|
|
46
193
|
});
|
package/src/dashboard/routes.ts
CHANGED
|
@@ -20,6 +20,10 @@ import {
|
|
|
20
20
|
} from './handlers';
|
|
21
21
|
import { buildDashboardHtml } from './ui';
|
|
22
22
|
|
|
23
|
+
type HtmlResponse = {
|
|
24
|
+
html(body: string): void;
|
|
25
|
+
};
|
|
26
|
+
|
|
23
27
|
export type TraceDashboardOptions = {
|
|
24
28
|
/** Base path for the dashboard, e.g. '/trace'. Defaults to '/trace'. */
|
|
25
29
|
basePath?: string;
|
|
@@ -61,7 +65,7 @@ export const registerTraceRoutes = (
|
|
|
61
65
|
Router.get(
|
|
62
66
|
router,
|
|
63
67
|
base,
|
|
64
|
-
(_req, res) => {
|
|
68
|
+
(_req: unknown, res: HtmlResponse) => {
|
|
65
69
|
res.html(buildDashboardHtml(base, appConfig.name));
|
|
66
70
|
},
|
|
67
71
|
routeOptions
|
|
@@ -70,7 +74,7 @@ export const registerTraceRoutes = (
|
|
|
70
74
|
Router.get(
|
|
71
75
|
router,
|
|
72
76
|
`${base}/*`,
|
|
73
|
-
(_req, res) => {
|
|
77
|
+
(_req: unknown, res: HtmlResponse) => {
|
|
74
78
|
res.html(buildDashboardHtml(base, appConfig.name));
|
|
75
79
|
},
|
|
76
80
|
routeOptions
|
package/src/dashboard/ui.ts
CHANGED
|
@@ -77,7 +77,7 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
77
77
|
.section-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:22px 24px 16px}.section-head h3{margin:0;font-size:1.04rem}.section-head p{margin:6px 0 0;color:var(--muted);font-size:.92rem}.toolbar{display:flex;flex-wrap:wrap;gap:10px;padding:0 24px 18px}.control,.toolbar input,.toolbar select{height:44px;border-radius:13px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);padding:0 14px;min-width:0}.toolbar input,.toolbar select{flex:1 1 180px}.toolbar input::placeholder{color:var(--muted)}.btn{height:44px;border:none;border-radius:13px;padding:0 16px;cursor:pointer;font-weight:800}.btn-primary{background:linear-gradient(135deg,var(--accent-strong),var(--accent));color:#fff}.btn-danger{background:rgba(239,68,68,.12);color:var(--danger);border:1px solid rgba(239,68,68,.18)}.btn-ghost{background:var(--surface-soft);color:var(--text);border:1px solid var(--line)}
|
|
78
78
|
.table-wrap{overflow:auto;padding:0 12px 12px}table{width:100%;border-collapse:separate;border-spacing:0;min-width:880px}th{padding:14px;color:var(--muted);font-size:.74rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;text-align:left;border-bottom:1px solid var(--line)}td{padding:15px 14px;border-bottom:1px solid var(--line);vertical-align:top}.row-button{cursor:pointer}.row-button:hover td{background:rgba(56,189,248,.05)}.summary{font-size:.93rem;font-weight:700;line-height:1.4;color:var(--text)}.summary-sub{margin-top:6px;color:var(--muted);font-size:.82rem;line-height:1.4}.mono{font-family:var(--mono)}.empty{padding:44px 24px;color:var(--muted);line-height:1.65;text-align:center}.pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 24px 24px;color:var(--muted);flex-wrap:wrap}.pagination-controls{display:flex;gap:8px}.pagination button{height:40px;min-width:92px;padding:0 14px;border-radius:12px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);cursor:pointer}.pagination button:disabled{opacity:.45;cursor:not-allowed}
|
|
79
79
|
.activity-list{list-style:none;margin:0;padding:0 24px 24px}.activity-item{padding:14px 0;border-top:1px solid var(--line)}.activity-item:first-child{border-top:none}.activity-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.activity-time{color:var(--muted);font-size:.85rem}.activity-summary{margin-top:8px;color:var(--text);line-height:1.48}.back-link{display:inline-flex;align-items:center;gap:8px;margin:0 0 14px;color:var(--accent);font-weight:800;cursor:pointer}.detail-card{padding:24px}.detail-meta{display:flex;flex-wrap:wrap;gap:10px;margin:14px 0 20px;color:var(--muted);font-size:.9rem}.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}.detail-stack{display:grid;gap:16px;margin-top:18px}.detail-box{padding:16px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.detail-box h4{margin:0 0 10px;font-size:.92rem}.detail-box dl{margin:0;display:grid;gap:8px}.detail-box dt{font-size:.76rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:800}.detail-box dd{margin:0;color:var(--text);line-height:1.45}.trace-tabs{display:flex;gap:10px;flex-wrap:wrap;margin:20px 0 16px}.trace-tab{border:none;border-radius:12px;padding:10px 12px;background:transparent;color:var(--muted);cursor:pointer;box-shadow:inset 0 0 0 1px var(--line);font-weight:800}.trace-tab.active{background:rgba(56,189,248,.12);color:var(--text);box-shadow:inset 0 0 0 1px rgba(56,189,248,.28)}.trace-panel{display:grid;gap:14px}.trace-item{padding:18px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.trace-item-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.trace-item-summary{margin-top:10px;display:grid;gap:10px}.trace-note{color:var(--muted);line-height:1.6}
|
|
80
|
-
.tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;background:rgba(56,189,248,.12);color:#bae6fd;font-size:.78rem;font-weight:800;margin:0 6px 6px 0;border:1px solid rgba(56,189,248,.18)}button.tag{cursor:pointer}html[data-theme='light'] .tag{color:#075985}.tag.failed{background:rgba(239,68,68,.14);color:#fecaca;border-color:rgba(239,68,68,.2)}html[data-theme='light'] .tag.failed{color:#b91c1c}.tag.slow{background:rgba(245,158,11,.12);color:#fde68a;border-color:rgba(245,158,11,.18)}html[data-theme='light'] .tag.slow{color:#92400e}.type-pill{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;font-size:.74rem;font-weight:900;text-transform:uppercase;letter-spacing:.08em;border:1px solid transparent}.pill-request{background:rgba(56,189,248,.14);color:#93c5fd}.pill-query{background:rgba(34,197,94,.12);color:#86efac}.pill-exception{background:rgba(239,68,68,.14);color:#fecaca}.pill-log{background:rgba(168,85,247,.14);color:#ddd6fe}.pill-job,.pill-batch{background:rgba(245,158,11,.14);color:#fde68a}.pill-cache{background:rgba(20,184,166,.12);color:#99f6e4}.pill-schedule,.pill-command{background:rgba(14,165,233,.14);color:#bae6fd}.pill-mail,.pill-notification{background:rgba(236,72,153,.14);color:#fbcfe8}.pill-auth{background:rgba(148,163,184,.16);color:#e2e8f0}.pill-event,.pill-model{background:rgba(74,222,128,.14);color:#bbf7d0}.pill-redis{background:rgba(239,68,68,.12);color:#fecaca}.pill-gate{background:rgba(99,102,241,.14);color:#c7d2fe}.pill-middleware{background:rgba(45,212,191,.12);color:#ccfbf1}.pill-dump,.pill-view{background:rgba(148,163,184,.14);color:#e2e8f0}.pill-client-request{background:rgba(59,130,246,.14);color:#bfdbfe}html[data-theme='light'] .pill-request{color:#1d4ed8}html[data-theme='light'] .pill-query{color:#166534}html[data-theme='light'] .pill-exception{color:#b91c1c}html[data-theme='light'] .pill-log{color:#6d28d9}html[data-theme='light'] .pill-job,html[data-theme='light'] .pill-batch{color:#92400e}html[data-theme='light'] .pill-cache{color:#115e59}html[data-theme='light'] .pill-schedule,html[data-theme='light'] .pill-command{color:#0c4a6e}html[data-theme='light'] .pill-mail,html[data-theme='light'] .pill-notification{color:#9d174d}html[data-theme='light'] .pill-auth,html[data-theme='light'] .pill-dump,html[data-theme='light'] .pill-view{color:#334155}html[data-theme='light'] .pill-event,html[data-theme='light'] .pill-model{color:#166534}html[data-theme='light'] .pill-redis{color:#991b1b}html[data-theme='light'] .pill-gate{color:#3730a3}html[data-theme='light'] .pill-middleware{color:#155e75}html[data-theme='light'] .pill-client-request{color:#1d4ed8}
|
|
80
|
+
.tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;background:rgba(56,189,248,.12);color:#bae6fd;font-size:.78rem;font-weight:800;margin:0 6px 6px 0;border:1px solid rgba(56,189,248,.18);text-decoration:none}button.tag{cursor:pointer}html[data-theme='light'] .tag{color:#075985}.tag.failed{background:rgba(239,68,68,.14);color:#fecaca;border-color:rgba(239,68,68,.2)}html[data-theme='light'] .tag.failed{color:#b91c1c}.tag.slow{background:rgba(245,158,11,.12);color:#fde68a;border-color:rgba(245,158,11,.18)}html[data-theme='light'] .tag.slow{color:#92400e}.type-pill{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;font-size:.74rem;font-weight:900;text-transform:uppercase;letter-spacing:.08em;border:1px solid transparent}.pill-request{background:rgba(56,189,248,.14);color:#93c5fd}.pill-request.method-get{background:rgba(34,197,94,.16);color:#bbf7d0}.pill-request.method-post{background:rgba(59,130,246,.16);color:#bfdbfe}.pill-request.method-other{background:rgba(245,158,11,.16);color:#fde68a}.pill-query{background:rgba(34,197,94,.12);color:#86efac}.pill-exception{background:rgba(239,68,68,.14);color:#fecaca}.pill-log{background:rgba(168,85,247,.14);color:#ddd6fe}.pill-job,.pill-batch{background:rgba(245,158,11,.14);color:#fde68a}.pill-cache{background:rgba(20,184,166,.12);color:#99f6e4}.pill-schedule,.pill-command{background:rgba(14,165,233,.14);color:#bae6fd}.pill-mail,.pill-notification{background:rgba(236,72,153,.14);color:#fbcfe8}.pill-auth{background:rgba(148,163,184,.16);color:#e2e8f0}.pill-event,.pill-model{background:rgba(74,222,128,.14);color:#bbf7d0}.pill-redis{background:rgba(239,68,68,.12);color:#fecaca}.pill-gate{background:rgba(99,102,241,.14);color:#c7d2fe}.pill-middleware{background:rgba(45,212,191,.12);color:#ccfbf1}.pill-dump,.pill-view{background:rgba(148,163,184,.14);color:#e2e8f0}.pill-client-request{background:rgba(59,130,246,.14);color:#bfdbfe}html[data-theme='light'] .pill-request{color:#1d4ed8}html[data-theme='light'] .pill-request.method-get{color:#166534}html[data-theme='light'] .pill-request.method-post{color:#1d4ed8}html[data-theme='light'] .pill-request.method-other{color:#92400e}html[data-theme='light'] .pill-query{color:#166534}html[data-theme='light'] .pill-exception{color:#b91c1c}html[data-theme='light'] .pill-log{color:#6d28d9}html[data-theme='light'] .pill-job,html[data-theme='light'] .pill-batch{color:#92400e}html[data-theme='light'] .pill-cache{color:#115e59}html[data-theme='light'] .pill-schedule,html[data-theme='light'] .pill-command{color:#0c4a6e}html[data-theme='light'] .pill-mail,html[data-theme='light'] .pill-notification{color:#9d174d}html[data-theme='light'] .pill-auth,html[data-theme='light'] .pill-dump,html[data-theme='light'] .pill-view{color:#334155}html[data-theme='light'] .pill-event,html[data-theme='light'] .pill-model{color:#166534}html[data-theme='light'] .pill-redis{color:#991b1b}html[data-theme='light'] .pill-gate{color:#3730a3}html[data-theme='light'] .pill-middleware{color:#155e75}html[data-theme='light'] .pill-client-request{color:#1d4ed8}
|
|
81
81
|
.monitoring-wrap{padding:0 24px 24px}.tag-list{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:18px}.tag-item{display:inline-flex;align-items:center;gap:10px;padding:10px 14px;border-radius:999px;border:1px solid var(--line);background:var(--surface-strong)}.tag-remove{border:none;background:rgba(239,68,68,.14);color:var(--danger);border-radius:999px;width:24px;height:24px;cursor:pointer;font-size:1rem;line-height:1}.helper-text{color:var(--muted);line-height:1.6}
|
|
82
82
|
.duration-chip{display:inline-flex;align-items:center;padding:5px 9px;border-radius:999px;border:1px solid transparent;font-size:.8rem;font-weight:700;color:var(--text);white-space:nowrap}.duration-chip.vfast{background:rgba(34,197,94,.14);border-color:rgba(34,197,94,.28);color:#bbf7d0}.duration-chip.fast{background:rgba(56,189,248,.12);border-color:rgba(56,189,248,.24);color:#bae6fd}.duration-chip.slow{background:rgba(245,158,11,.12);border-color:rgba(245,158,11,.22);color:#fde68a}.duration-chip.vslow{background:rgba(239,68,68,.14);border-color:rgba(239,68,68,.24);color:#fecaca}html[data-theme='light'] .duration-chip.vfast{color:#166534}html[data-theme='light'] .duration-chip.fast{color:#1d4ed8}html[data-theme='light'] .duration-chip.slow{color:#92400e}html[data-theme='light'] .duration-chip.vslow{color:#b91c1c}
|
|
83
83
|
.code-card{border-radius:16px;border:1px solid var(--code-border);background:var(--surface-soft);overflow:hidden}.code-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--line)}.code-label{font-size:.76rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:800}.copy-button{display:inline-flex;align-items:center;justify-content:center;gap:8px;width:38px;height:38px;border-radius:12px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);cursor:pointer;transition:border-color .16s ease,color .16s ease}.copy-button:hover{border-color:rgba(56,189,248,.35);color:var(--accent)}.copy-button[data-copied='true']{color:var(--success);border-color:rgba(34,197,94,.28)}.copy-button svg{width:16px;height:16px;display:block}.code-block{margin:0;padding:18px 20px;background:var(--code-bg);color:#dbeafe;border:0;overflow:auto;white-space:pre;line-height:1.72;font-family:var(--mono);font-size:.92rem}.code-block code{font-family:inherit}.tok-key{color:#93c5fd}.tok-string{color:#86efac}.tok-number{color:#f9a8d4}.tok-boolean{color:#facc15}.tok-null{color:#fb7185}.tok-punctuation{color:#94a3b8}.tok-sql-keyword{color:#f472b6;font-weight:700}.tok-sql-identifier{color:#93c5fd}.tok-sql-string{color:#86efac}.tok-sql-number{color:#facc15}.tok-sql-comment{color:#64748b;font-style:italic}html[data-theme='light'] .code-block{color:#0f172a}html[data-theme='light'] .tok-key{color:#1d4ed8}html[data-theme='light'] .tok-string{color:#15803d}html[data-theme='light'] .tok-number{color:#c026d3}html[data-theme='light'] .tok-boolean{color:#b45309}html[data-theme='light'] .tok-null{color:#dc2626}html[data-theme='light'] .tok-punctuation{color:#64748b}html[data-theme='light'] .tok-sql-keyword{color:#db2777}html[data-theme='light'] .tok-sql-identifier{color:#2563eb}html[data-theme='light'] .tok-sql-string{color:#15803d}html[data-theme='light'] .tok-sql-number{color:#b45309}html[data-theme='light'] .tok-sql-comment{color:#6b7280}
|
|
@@ -146,15 +146,27 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
146
146
|
monitoring: { title: 'Monitoring tags', subtitle: 'Pinned tags for trace pivots.' }
|
|
147
147
|
};
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
const createInitialState = () => {
|
|
150
|
+
const search = new URLSearchParams(window.location.search);
|
|
151
|
+
const page = search.get('page');
|
|
152
|
+
const entriesPage = Number.parseInt(search.get('entriesPage') || '1', 10);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
page: page && Object.prototype.hasOwnProperty.call(PAGE_COPY, page) ? page : 'overview',
|
|
156
|
+
entriesPage: Number.isFinite(entriesPage) && entriesPage > 0 ? entriesPage : 1,
|
|
157
|
+
entriesFilter: {
|
|
158
|
+
type: search.get('type') || '',
|
|
159
|
+
tag: search.get('tag') || '',
|
|
160
|
+
batchId: search.get('batchId') || ''
|
|
161
|
+
},
|
|
162
|
+
detail: null,
|
|
163
|
+
detailBatch: null,
|
|
164
|
+
detailTab: 'summary'
|
|
165
|
+
};
|
|
156
166
|
};
|
|
157
167
|
|
|
168
|
+
let state = createInitialState();
|
|
169
|
+
|
|
158
170
|
let copySequence = 0;
|
|
159
171
|
const copyPayloads = new Map();
|
|
160
172
|
|
|
@@ -199,7 +211,19 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
199
211
|
return response.json();
|
|
200
212
|
};
|
|
201
213
|
|
|
202
|
-
const
|
|
214
|
+
const requestMethodClass = (entry) => {
|
|
215
|
+
if (!entry || entry.type !== 'request') return '';
|
|
216
|
+
const method = String(entry.content && entry.content.method || '').toUpperCase();
|
|
217
|
+
if (method === 'GET') return ' method-get';
|
|
218
|
+
if (method === 'POST') return ' method-post';
|
|
219
|
+
return ' method-other';
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const typeClass = (entryOrType, maybeEntry) => {
|
|
223
|
+
const entry = maybeEntry || (typeof entryOrType === 'object' ? entryOrType : null);
|
|
224
|
+
const type = entry && entry.type ? entry.type : entryOrType;
|
|
225
|
+
return 'type-pill pill-' + String(type || '').replace(/_/g, '-') + requestMethodClass(entry);
|
|
226
|
+
};
|
|
203
227
|
|
|
204
228
|
const timeSince = (value) => {
|
|
205
229
|
const createdAt = Number(value);
|
|
@@ -251,11 +275,34 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
251
275
|
return '<span class="duration-chip ' + tone + '" title="' + toneLabel + '">' + escapeHtml(formatDuration(duration)) + '</span>';
|
|
252
276
|
};
|
|
253
277
|
|
|
278
|
+
const buildEntriesHref = (params) => {
|
|
279
|
+
const search = new URLSearchParams();
|
|
280
|
+
search.set('page', 'entries');
|
|
281
|
+
if (params.type) search.set('type', String(params.type));
|
|
282
|
+
if (params.tag) search.set('tag', String(params.tag));
|
|
283
|
+
if (params.batchId) search.set('batchId', String(params.batchId));
|
|
284
|
+
return BASE + '?' + search.toString();
|
|
285
|
+
};
|
|
286
|
+
|
|
254
287
|
const tagsHtml = (tags) => (tags || []).map((tag) => {
|
|
255
288
|
const css = tag === 'failed' ? 'tag failed' : tag === 'slow' ? 'tag slow' : 'tag';
|
|
256
|
-
|
|
289
|
+
const href = buildEntriesHref({ tag });
|
|
290
|
+
return '<a class="' + css + '" data-action="filter-tag" data-tag="' + escapeHtml(tag) + '" href="' + escapeHtml(href) + '">' + escapeHtml(tag) + '</a>';
|
|
257
291
|
}).join('');
|
|
258
292
|
|
|
293
|
+
const syncUrl = () => {
|
|
294
|
+
const search = new URLSearchParams();
|
|
295
|
+
if (state.page !== 'overview') search.set('page', state.page);
|
|
296
|
+
if (state.page === 'entries' || state.entriesFilter.type || state.entriesFilter.tag || state.entriesFilter.batchId) {
|
|
297
|
+
if (state.entriesFilter.type) search.set('type', state.entriesFilter.type);
|
|
298
|
+
if (state.entriesFilter.tag) search.set('tag', state.entriesFilter.tag);
|
|
299
|
+
if (state.entriesFilter.batchId) search.set('batchId', state.entriesFilter.batchId);
|
|
300
|
+
if (state.entriesPage > 1) search.set('entriesPage', String(state.entriesPage));
|
|
301
|
+
}
|
|
302
|
+
const nextUrl = search.toString() === '' ? BASE : BASE + '?' + search.toString();
|
|
303
|
+
window.history.replaceState(null, '', nextUrl);
|
|
304
|
+
};
|
|
305
|
+
|
|
259
306
|
const batchSnippet = (batchId) => {
|
|
260
307
|
const raw = String(batchId || '');
|
|
261
308
|
return raw === '' ? '-' : escapeHtml(raw.slice(0, 8));
|
|
@@ -475,7 +522,7 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
475
522
|
'<section class="trace-item">',
|
|
476
523
|
'<div class="trace-item-head">',
|
|
477
524
|
'<div>',
|
|
478
|
-
'<span class="' + typeClass(entry
|
|
525
|
+
'<span class="' + typeClass(entry) + '">' + escapeHtml(entry.type) + '</span>',
|
|
479
526
|
'</div>',
|
|
480
527
|
'<div class="activity-head">' + durationHtml(entry) + '<span class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</span></div>',
|
|
481
528
|
'</div>',
|
|
@@ -501,10 +548,11 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
501
548
|
{ id: 'logs', label: 'Logs', count: batchEntriesByType('log').length },
|
|
502
549
|
{ id: 'exceptions', label: 'Exceptions', count: batchEntriesByType('exception').length },
|
|
503
550
|
{ id: 'http', label: 'HTTP', count: batchEntriesByType('client_request').length },
|
|
504
|
-
{ id: '
|
|
551
|
+
{ id: 'cache', label: 'Cache', count: batchEntriesByType('cache').length },
|
|
552
|
+
{ id: 'other', label: 'Other', count: batchEntries().filter((item) => !['request','query','log','exception','client_request','cache'].includes(item.type)).length }
|
|
505
553
|
];
|
|
506
554
|
const currentTab = traceTabs.some((tab) => tab.id === state.detailTab) ? state.detailTab : 'summary';
|
|
507
|
-
const otherEntries = batchEntries().filter((item) => !['request','query','log','exception','client_request'].includes(item.type));
|
|
555
|
+
const otherEntries = batchEntries().filter((item) => !['request','query','log','exception','client_request','cache'].includes(item.type));
|
|
508
556
|
const panels = {
|
|
509
557
|
summary: [
|
|
510
558
|
'<div class="detail-grid">',
|
|
@@ -527,18 +575,19 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
527
575
|
].join(''),
|
|
528
576
|
payload: detailJson(content.payload || {}),
|
|
529
577
|
headers: '<div class="detail-stack">' + detailJson(content.headers || {}) + detailJson(content.responseHeaders || {}) + '</div>',
|
|
530
|
-
response: '<div class="detail-stack"><div class="detail-grid">' + renderMetricBox('Status', [{ label: 'Response status', value: escapeHtml(content.responseStatus || '') }, { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) }]) + '</div
|
|
578
|
+
response: '<div class="detail-stack"><div class="detail-grid">' + renderMetricBox('Status', [{ label: 'Response status', value: escapeHtml(content.responseStatus || '') }, { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) }]) + '</div>' + (content.responseBody === undefined ? '<p class="trace-note">No response body was captured for this request.</p>' : detailJson(content.responseBody)) + detailJson(content.responseHeaders || {}) + '</div>',
|
|
531
579
|
queries: renderTraceItems(batchEntriesByType('query')),
|
|
532
580
|
logs: renderTraceItems(batchEntriesByType('log')),
|
|
533
581
|
exceptions: renderTraceItems(batchEntriesByType('exception')),
|
|
534
582
|
http: renderTraceItems(batchEntriesByType('client_request')),
|
|
583
|
+
cache: renderTraceItems(batchEntriesByType('cache')),
|
|
535
584
|
other: renderTraceItems(otherEntries)
|
|
536
585
|
};
|
|
537
586
|
|
|
538
587
|
main.innerHTML = [
|
|
539
588
|
'<span class="back-link" data-action="close-detail"><- Back to entries</span>',
|
|
540
589
|
'<section class="panel detail-card">',
|
|
541
|
-
'<div><span class="' + typeClass(entry
|
|
590
|
+
'<div><span class="' + typeClass(entry) + '">' + escapeHtml(entry.type) + '</span> ' + tagsHtml(entry.tags) + '</div>',
|
|
542
591
|
'<div class="detail-meta"><span>UUID <span class="mono">' + escapeHtml(entry.uuid) + '</span></span><span>Batch <span class="mono">' + escapeHtml(entry.batchId || '-') + '</span></span><span>' + durationHtml(entry) + '</span><span>' + escapeHtml(new Date(Number(entry.createdAt)).toISOString()) + '</span></div>',
|
|
543
592
|
'<div class="trace-tabs">',
|
|
544
593
|
traceTabs.map((tab) => '<button type="button" class="trace-tab' + (tab.id === currentTab ? ' active' : '') + '" data-action="detail-tab" data-tab="' + escapeHtml(tab.id) + '">' + escapeHtml(tab.label) + (tab.count !== undefined ? ' (' + escapeHtml(tab.count) + ')' : '') + '</button>').join(''),
|
|
@@ -564,10 +613,10 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
564
613
|
const recentRows = recent.data || [];
|
|
565
614
|
const recentTable = recentRows.length === 0
|
|
566
615
|
? '<div class="empty">No trace entries recorded.</div>'
|
|
567
|
-
: '<div class="table-wrap"><table><thead><tr><th>Type</th><th>Summary</th><th>Tags</th><th>Duration</th><th>Happened</th></tr></thead><tbody>' + recentRows.map((entry) => '<tr class="row-button" data-action="show-detail" data-uuid="' + escapeHtml(entry.uuid) + '"><td><span class="' + typeClass(entry
|
|
616
|
+
: '<div class="table-wrap"><table><thead><tr><th>Type</th><th>Summary</th><th>Tags</th><th>Duration</th><th>Happened</th></tr></thead><tbody>' + recentRows.map((entry) => '<tr class="row-button" data-action="show-detail" data-uuid="' + escapeHtml(entry.uuid) + '"><td><span class="' + typeClass(entry) + '">' + escapeHtml(entry.type) + '</span></td><td>' + entrySummaryHtml(entry) + '</td><td>' + tagsHtml(entry.tags) + '</td><td>' + durationHtml(entry) + '</td><td class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</td></tr>').join('') + '</tbody></table></div>';
|
|
568
617
|
const activityList = recentRows.length === 0
|
|
569
618
|
? '<div class="empty">No recent activity.</div>'
|
|
570
|
-
: '<ul class="activity-list">' + recentRows.slice(0, 5).map((entry) => '<li class="activity-item"><div class="activity-head"><span class="' + typeClass(entry
|
|
619
|
+
: '<ul class="activity-list">' + recentRows.slice(0, 5).map((entry) => '<li class="activity-item"><div class="activity-head"><span class="' + typeClass(entry) + '">' + escapeHtml(entry.type) + '</span>' + durationHtml(entry) + '<span class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</span></div><div class="activity-summary">' + escapeHtml(entrySummaryText(entry)) + '</div></li>').join('') + '</ul>';
|
|
571
620
|
|
|
572
621
|
main.innerHTML = [
|
|
573
622
|
statsCardsHtml(stats),
|
|
@@ -611,7 +660,7 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
611
660
|
const total = Number(response.total || 0);
|
|
612
661
|
const perPage = Number(response.perPage || 50);
|
|
613
662
|
const totalPages = Math.max(1, Math.ceil(total / perPage));
|
|
614
|
-
const rows = data.map((entry) => '<tr class="row-button" data-action="show-detail" data-uuid="' + escapeHtml(entry.uuid) + '"><td><span class="' + typeClass(entry
|
|
663
|
+
const rows = data.map((entry) => '<tr class="row-button" data-action="show-detail" data-uuid="' + escapeHtml(entry.uuid) + '"><td><span class="' + typeClass(entry) + '">' + escapeHtml(entry.type) + '</span></td><td>' + entrySummaryHtml(entry) + '</td><td>' + tagsHtml(entry.tags) + '</td><td>' + durationHtml(entry) + '</td><td class="mono">' + batchSnippet(entry.batchId) + '</td><td class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</td></tr>').join('');
|
|
615
664
|
|
|
616
665
|
main.innerHTML = [
|
|
617
666
|
'<section class="panel">',
|
|
@@ -647,7 +696,7 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
647
696
|
main.innerHTML = [
|
|
648
697
|
'<span class="back-link" data-action="close-detail"><- Back to entries</span>',
|
|
649
698
|
'<section class="panel detail-card">',
|
|
650
|
-
'<div><span class="' + typeClass(entry
|
|
699
|
+
'<div><span class="' + typeClass(entry) + '">' + escapeHtml(entry.type) + '</span> ' + tagsHtml(entry.tags) + '</div>',
|
|
651
700
|
'<div class="detail-meta"><span>UUID <span class="mono">' + escapeHtml(entry.uuid) + '</span></span><span>Batch <span class="mono">' + escapeHtml(entry.batchId || '-') + '</span></span><span>' + durationHtml(entry) + '</span><span>' + escapeHtml(new Date(Number(entry.createdAt)).toISOString()) + '</span></div>',
|
|
652
701
|
'<div class="detail-stack">',
|
|
653
702
|
renderEntryBody(entry),
|
|
@@ -663,13 +712,14 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
663
712
|
const tags = result.tags || [];
|
|
664
713
|
main.innerHTML = [
|
|
665
714
|
'<section class="panel">',
|
|
666
|
-
'<div class="section-head"><div><h3>Monitoring tags</h3><p>
|
|
715
|
+
'<div class="section-head"><div><h3>Monitoring tags</h3><p>Save the tags you filter by often so they stay one click away.</p></div></div>',
|
|
667
716
|
'<div class="monitoring-wrap">',
|
|
668
717
|
'<div class="tag-list">',
|
|
669
|
-
tags.length === 0 ? '<span class="helper-text">No tags monitored.</span>' : tags.map((tag) => '<span class="tag-item"><
|
|
718
|
+
tags.length === 0 ? '<span class="helper-text">No tags monitored yet. Add tags like auth, checkout, queue:emails, or nightly-sync to pin them here.</span>' : tags.map((tag) => '<span class="tag-item"><a class="tag mono" data-action="filter-tag" data-tag="' + escapeHtml(tag) + '" href="' + escapeHtml(buildEntriesHref({ tag })) + '">' + escapeHtml(tag) + '</a><button type="button" class="tag-remove" data-action="remove-tag" data-tag="' + escapeHtml(tag) + '">x</button></span>').join(''),
|
|
670
719
|
'</div>',
|
|
720
|
+
'<p class="helper-text">Click a saved tag to filter the entries list by that exact tag. Because each tag is a real link, you can also open it in a new tab. Removing a saved tag only removes the shortcut here; it does not delete any trace entries.</p>',
|
|
671
721
|
'<div class="toolbar" style="padding:0;margin-top:8px">',
|
|
672
|
-
'<input id="new-tag" class="control" type="text" placeholder="Add tag">',
|
|
722
|
+
'<input id="new-tag" class="control" type="text" placeholder="Add tag, for example checkout">',
|
|
673
723
|
'<button type="button" class="btn btn-primary" data-action="add-tag">Add tag</button>',
|
|
674
724
|
'</div>',
|
|
675
725
|
'</div>',
|
|
@@ -694,6 +744,8 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
694
744
|
button.classList.toggle('active', button.getAttribute('data-type') === activeShortcut);
|
|
695
745
|
});
|
|
696
746
|
|
|
747
|
+
syncUrl();
|
|
748
|
+
|
|
697
749
|
if (state.page === 'overview') await renderOverview(main);
|
|
698
750
|
if (state.page === 'entries') await renderEntries(main);
|
|
699
751
|
if (state.page === 'monitoring') await renderMonitoring(main);
|
|
@@ -828,7 +880,12 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
|
|
|
828
880
|
const action = target.getAttribute('data-action');
|
|
829
881
|
if (action === 'go-page') { setPage(String(target.getAttribute('data-page') || 'overview')); return; }
|
|
830
882
|
if (action === 'type-shortcut') { setTypeShortcut(String(target.getAttribute('data-type') || '')); return; }
|
|
831
|
-
if (action === 'filter-tag') {
|
|
883
|
+
if (action === 'filter-tag') {
|
|
884
|
+
if (target instanceof HTMLAnchorElement && (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0)) return;
|
|
885
|
+
event.preventDefault();
|
|
886
|
+
filterByTag(String(target.getAttribute('data-tag') || ''));
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
832
889
|
if (action === 'detail-tab') { state = { ...state, detailTab: String(target.getAttribute('data-tab') || 'summary') }; render(); return; }
|
|
833
890
|
if (action === 'clear-all') { clearAll(); return; }
|
|
834
891
|
if (action === 'show-detail') { showDetail(String(target.getAttribute('data-uuid') || '')); return; }
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* import '@zintrust/trace/register';
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { ExceptionWatcher as ExceptionWatcherApi } from './watchers/ExceptionWatcher';
|
|
10
|
+
|
|
9
11
|
// ---------------------------------------------------------------------------
|
|
10
12
|
// Config
|
|
11
13
|
// ---------------------------------------------------------------------------
|
|
@@ -16,6 +18,7 @@ export { TraceConfig } from './config';
|
|
|
16
18
|
// ---------------------------------------------------------------------------
|
|
17
19
|
export { TraceStorage } from './storage';
|
|
18
20
|
export type { ITraceStorage } from './storage';
|
|
21
|
+
export { TraceContentRedaction } from './storage/TraceContentRedaction';
|
|
19
22
|
|
|
20
23
|
// ---------------------------------------------------------------------------
|
|
21
24
|
// Context
|
|
@@ -52,6 +55,10 @@ export { RedisWatcher } from './watchers/RedisWatcher';
|
|
|
52
55
|
export { ScheduleWatcher } from './watchers/ScheduleWatcher';
|
|
53
56
|
export { ViewWatcher } from './watchers/ViewWatcher';
|
|
54
57
|
|
|
58
|
+
export const captureTraceException = (error: unknown): void => {
|
|
59
|
+
ExceptionWatcherApi.capture(error);
|
|
60
|
+
};
|
|
61
|
+
|
|
55
62
|
// ---------------------------------------------------------------------------
|
|
56
63
|
// Types
|
|
57
64
|
// ---------------------------------------------------------------------------
|