@superblocksteam/sdk 2.0.128-next.0 → 2.0.128-next.2
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/.turbo/turbo-build.log +1 -1
- package/dist/telemetry/logging.d.ts +12 -0
- package/dist/telemetry/logging.d.ts.map +1 -1
- package/dist/telemetry/logging.js +23 -1
- package/dist/telemetry/logging.js.map +1 -1
- package/dist/telemetry/logging.test.js +62 -1
- package/dist/telemetry/logging.test.js.map +1 -1
- package/package.json +6 -6
- package/src/telemetry/logging.test.ts +77 -0
- package/src/telemetry/logging.ts +37 -0
- package/tsconfig.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -5,10 +5,22 @@ export interface ErrorMeta {
|
|
|
5
5
|
stack?: string;
|
|
6
6
|
};
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Primitive-only attribute map. Forwarded to OTel log attributes (Datadog
|
|
10
|
+
* facets), so values must stay low-cardinality-friendly (enums/ids/numbers),
|
|
11
|
+
* not free-form text.
|
|
12
|
+
*/
|
|
13
|
+
export type LogAttributes = Record<string, string | number | boolean>;
|
|
8
14
|
export interface Logger {
|
|
9
15
|
debug: (...messages: unknown[]) => void;
|
|
10
16
|
info: (...messages: unknown[]) => void;
|
|
11
17
|
warn: (...messages: unknown[]) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Emit a WARN-level log whose structured attributes are exported as OTel log
|
|
20
|
+
* attributes (Datadog facets) instead of being flattened into the message
|
|
21
|
+
* body. Use for machine-queryable warn events such as stream stalls.
|
|
22
|
+
*/
|
|
23
|
+
warnStructured?: (message: string, attributes: LogAttributes) => void;
|
|
12
24
|
error: (message: string, meta?: ErrorMeta) => void;
|
|
13
25
|
}
|
|
14
26
|
export declare function getLogger(loggerOverride?: (...messages: unknown[]) => void): Logger;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../src/telemetry/logging.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../src/telemetry/logging.ts"],"names":[],"mappings":"AA8DA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEtE,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,CAAC,GAAG,QAAQ,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACxC,IAAI,EAAE,CAAC,GAAG,QAAQ,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACvC,IAAI,EAAE,CAAC,GAAG,QAAQ,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACvC;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,KAAK,IAAI,CAAC;IACtE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;CACpD;AAyED,wBAAgB,SAAS,CACvB,cAAc,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,OAAO,EAAE,KAAK,IAAI,GAChD,MAAM,CAiBR;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAgBtD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
2
2
|
import { createLogger, format, transports } from "winston";
|
|
3
|
-
import { sanitizeLogError, sanitizeLogMessage, } from "@superblocksteam/telemetry";
|
|
3
|
+
import { sanitizeLogError, sanitizeLogMessage, sanitizeLogObject, } from "@superblocksteam/telemetry";
|
|
4
4
|
import { getLogger as getTracedLogger } from "./index.js";
|
|
5
5
|
import { safeStringify } from "./safe-stringify.js";
|
|
6
6
|
const activeTransports = [];
|
|
@@ -70,6 +70,23 @@ const logger = Object.freeze({
|
|
|
70
70
|
});
|
|
71
71
|
winstonLogger.warn(body);
|
|
72
72
|
},
|
|
73
|
+
warnStructured: (message, attributes) => {
|
|
74
|
+
const body = sanitizeLogMessage(message);
|
|
75
|
+
// sanitizeLogObject strips secret-named keys and redacts secret values,
|
|
76
|
+
// matching the vite-plugin wrapLogger path so both implementations of this
|
|
77
|
+
// contract enforce the same invariant.
|
|
78
|
+
const safeAttributes = sanitizeLogObject(attributes);
|
|
79
|
+
getTracedLogger().emit({
|
|
80
|
+
severityNumber: SeverityNumber.WARN,
|
|
81
|
+
severityText: "WARN",
|
|
82
|
+
body,
|
|
83
|
+
attributes: safeAttributes,
|
|
84
|
+
});
|
|
85
|
+
// OTel is the canonical sink for the structured attributes; Winston gets
|
|
86
|
+
// the body only (matching warn() above) so attributes are not also shipped
|
|
87
|
+
// to Datadog via stdout log shippers as a second, duplicate copy.
|
|
88
|
+
winstonLogger.warn(body);
|
|
89
|
+
},
|
|
73
90
|
error: (message, meta) => {
|
|
74
91
|
const safeMessage = sanitizeLogMessage(message);
|
|
75
92
|
const safeError = meta?.error
|
|
@@ -98,6 +115,11 @@ export function getLogger(loggerOverride) {
|
|
|
98
115
|
debug: loggerOverride,
|
|
99
116
|
info: loggerOverride,
|
|
100
117
|
warn: loggerOverride,
|
|
118
|
+
// The override path is for test/debug sinks only: like debug/info/warn
|
|
119
|
+
// above, it forwards raw args to the supplied function and does NOT
|
|
120
|
+
// sanitize or emit OTel attributes. Production callers use the default
|
|
121
|
+
// logger (and the vite-plugin wrapLogger) which sanitize before emitting.
|
|
122
|
+
warnStructured: (message, attributes) => loggerOverride(message, attributes),
|
|
101
123
|
error: loggerOverride,
|
|
102
124
|
};
|
|
103
125
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../../src/telemetry/logging.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG3D,OAAO,EACL,gBAAgB,EAChB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../../src/telemetry/logging.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
const { emitMock, winstonErrorMock, winstonLoggerMock, sanitizeLogMessageMock, sanitizeLogErrorMock, } = vi.hoisted(() => {
|
|
2
|
+
const { emitMock, winstonErrorMock, winstonLoggerMock, sanitizeLogMessageMock, sanitizeLogErrorMock, sanitizeLogObjectMock, } = vi.hoisted(() => {
|
|
3
3
|
const emitMock = vi.fn();
|
|
4
4
|
const winstonErrorMock = vi.fn();
|
|
5
5
|
const winstonLoggerMock = {
|
|
@@ -14,17 +14,31 @@ const { emitMock, winstonErrorMock, winstonLoggerMock, sanitizeLogMessageMock, s
|
|
|
14
14
|
message: `sanitized-error:${error.message}`,
|
|
15
15
|
stack: error.stack ? `sanitized-error:${error.stack}` : undefined,
|
|
16
16
|
}));
|
|
17
|
+
// Faithful stand-in for the real sanitizeLogObject: strips secret-named keys
|
|
18
|
+
// and redacts string values; numbers/booleans pass through.
|
|
19
|
+
const sanitizeLogObjectMock = vi.fn((obj) => {
|
|
20
|
+
const out = {};
|
|
21
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
22
|
+
if (key === "password" || key === "token" || key === "api_key")
|
|
23
|
+
continue;
|
|
24
|
+
out[key] =
|
|
25
|
+
typeof value === "string" ? `sanitized-object:${value}` : value;
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
});
|
|
17
29
|
return {
|
|
18
30
|
emitMock,
|
|
19
31
|
winstonErrorMock,
|
|
20
32
|
winstonLoggerMock,
|
|
21
33
|
sanitizeLogMessageMock,
|
|
22
34
|
sanitizeLogErrorMock,
|
|
35
|
+
sanitizeLogObjectMock,
|
|
23
36
|
};
|
|
24
37
|
});
|
|
25
38
|
vi.mock("@superblocksteam/telemetry", () => ({
|
|
26
39
|
sanitizeLogMessage: sanitizeLogMessageMock,
|
|
27
40
|
sanitizeLogError: sanitizeLogErrorMock,
|
|
41
|
+
sanitizeLogObject: sanitizeLogObjectMock,
|
|
28
42
|
}));
|
|
29
43
|
vi.mock("./index.js", () => ({
|
|
30
44
|
getLogger: vi.fn(() => ({
|
|
@@ -101,4 +115,51 @@ describe("telemetry logger error sanitization", () => {
|
|
|
101
115
|
expect(winstonErrorMock).toHaveBeenCalledWith("sanitized-message:plain failure");
|
|
102
116
|
});
|
|
103
117
|
});
|
|
118
|
+
describe("telemetry logger warnStructured", () => {
|
|
119
|
+
beforeEach(() => {
|
|
120
|
+
vi.clearAllMocks();
|
|
121
|
+
});
|
|
122
|
+
it("emits a WARN record carrying structured attributes (not flattened into the body)", () => {
|
|
123
|
+
const logger = getLogger();
|
|
124
|
+
logger.warnStructured?.("Idle timeout exceeded", {
|
|
125
|
+
event: "stream_stall",
|
|
126
|
+
provider: "snowflake",
|
|
127
|
+
model: "claude-opus-4-6",
|
|
128
|
+
idleMs: 180001,
|
|
129
|
+
attempt: 0,
|
|
130
|
+
});
|
|
131
|
+
expect(emitMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
132
|
+
severityText: "WARN",
|
|
133
|
+
body: "sanitized-message:Idle timeout exceeded",
|
|
134
|
+
attributes: {
|
|
135
|
+
event: "sanitized-object:stream_stall",
|
|
136
|
+
provider: "sanitized-object:snowflake",
|
|
137
|
+
model: "sanitized-object:claude-opus-4-6",
|
|
138
|
+
idleMs: 180001,
|
|
139
|
+
attempt: 0,
|
|
140
|
+
},
|
|
141
|
+
}));
|
|
142
|
+
// Winston gets the body only (structured attributes go to OTel, not also
|
|
143
|
+
// to stdout log shippers as a duplicate).
|
|
144
|
+
expect(winstonLoggerMock.warn).toHaveBeenCalledWith("sanitized-message:Idle timeout exceeded");
|
|
145
|
+
});
|
|
146
|
+
it("strips secret-named attribute keys and leaves numbers untouched", () => {
|
|
147
|
+
const logger = getLogger();
|
|
148
|
+
logger.warnStructured?.("stall", {
|
|
149
|
+
provider: "snowflake",
|
|
150
|
+
password: "sekret",
|
|
151
|
+
idleMs: 42,
|
|
152
|
+
});
|
|
153
|
+
expect(emitMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
154
|
+
attributes: expect.objectContaining({
|
|
155
|
+
provider: "sanitized-object:snowflake",
|
|
156
|
+
idleMs: 42,
|
|
157
|
+
}),
|
|
158
|
+
}));
|
|
159
|
+
// Secret-named keys are dropped before reaching the OTel record.
|
|
160
|
+
expect(emitMock).not.toHaveBeenCalledWith(expect.objectContaining({
|
|
161
|
+
attributes: expect.objectContaining({ password: expect.anything() }),
|
|
162
|
+
}));
|
|
163
|
+
});
|
|
164
|
+
});
|
|
104
165
|
//# sourceMappingURL=logging.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.test.js","sourceRoot":"","sources":["../../src/telemetry/logging.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9D,MAAM,EACJ,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"logging.test.js","sourceRoot":"","sources":["../../src/telemetry/logging.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superblocksteam/sdk",
|
|
3
|
-
"version": "2.0.128-next.
|
|
3
|
+
"version": "2.0.128-next.2",
|
|
4
4
|
"description": "Superblocks JS SDK",
|
|
5
5
|
"homepage": "https://www.superblocks.com",
|
|
6
6
|
"license": "Superblocks Community Software License",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"vite-tsconfig-paths": "^6.0.4",
|
|
49
49
|
"winston": "^3.17.0",
|
|
50
50
|
"yaml": "^2.7.1",
|
|
51
|
-
"@superblocksteam/
|
|
52
|
-
"@superblocksteam/
|
|
53
|
-
"@superblocksteam/
|
|
54
|
-
"@superblocksteam/
|
|
55
|
-
"@superblocksteam/
|
|
51
|
+
"@superblocksteam/shared": "0.9590.8",
|
|
52
|
+
"@superblocksteam/telemetry": "2.0.128-next.2",
|
|
53
|
+
"@superblocksteam/util": "2.0.128-next.2",
|
|
54
|
+
"@superblocksteam/vite-plugin-file-sync": "2.0.128-next.2",
|
|
55
|
+
"@superblocksteam/library-shared": "2.0.128-next.2"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@eslint/js": "^9.39.2",
|
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
winstonLoggerMock,
|
|
7
7
|
sanitizeLogMessageMock,
|
|
8
8
|
sanitizeLogErrorMock,
|
|
9
|
+
sanitizeLogObjectMock,
|
|
9
10
|
} = vi.hoisted(() => {
|
|
10
11
|
const emitMock = vi.fn();
|
|
11
12
|
const winstonErrorMock = vi.fn();
|
|
@@ -27,18 +28,32 @@ const {
|
|
|
27
28
|
}),
|
|
28
29
|
);
|
|
29
30
|
|
|
31
|
+
// Faithful stand-in for the real sanitizeLogObject: strips secret-named keys
|
|
32
|
+
// and redacts string values; numbers/booleans pass through.
|
|
33
|
+
const sanitizeLogObjectMock = vi.fn((obj: Record<string, unknown>) => {
|
|
34
|
+
const out: Record<string, unknown> = {};
|
|
35
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
36
|
+
if (key === "password" || key === "token" || key === "api_key") continue;
|
|
37
|
+
out[key] =
|
|
38
|
+
typeof value === "string" ? `sanitized-object:${value}` : value;
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
});
|
|
42
|
+
|
|
30
43
|
return {
|
|
31
44
|
emitMock,
|
|
32
45
|
winstonErrorMock,
|
|
33
46
|
winstonLoggerMock,
|
|
34
47
|
sanitizeLogMessageMock,
|
|
35
48
|
sanitizeLogErrorMock,
|
|
49
|
+
sanitizeLogObjectMock,
|
|
36
50
|
};
|
|
37
51
|
});
|
|
38
52
|
|
|
39
53
|
vi.mock("@superblocksteam/telemetry", () => ({
|
|
40
54
|
sanitizeLogMessage: sanitizeLogMessageMock,
|
|
41
55
|
sanitizeLogError: sanitizeLogErrorMock,
|
|
56
|
+
sanitizeLogObject: sanitizeLogObjectMock,
|
|
42
57
|
}));
|
|
43
58
|
|
|
44
59
|
vi.mock("./index.js", () => ({
|
|
@@ -140,3 +155,65 @@ describe("telemetry logger error sanitization", () => {
|
|
|
140
155
|
);
|
|
141
156
|
});
|
|
142
157
|
});
|
|
158
|
+
|
|
159
|
+
describe("telemetry logger warnStructured", () => {
|
|
160
|
+
beforeEach(() => {
|
|
161
|
+
vi.clearAllMocks();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("emits a WARN record carrying structured attributes (not flattened into the body)", () => {
|
|
165
|
+
const logger = getLogger();
|
|
166
|
+
|
|
167
|
+
logger.warnStructured?.("Idle timeout exceeded", {
|
|
168
|
+
event: "stream_stall",
|
|
169
|
+
provider: "snowflake",
|
|
170
|
+
model: "claude-opus-4-6",
|
|
171
|
+
idleMs: 180001,
|
|
172
|
+
attempt: 0,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(emitMock).toHaveBeenCalledWith(
|
|
176
|
+
expect.objectContaining({
|
|
177
|
+
severityText: "WARN",
|
|
178
|
+
body: "sanitized-message:Idle timeout exceeded",
|
|
179
|
+
attributes: {
|
|
180
|
+
event: "sanitized-object:stream_stall",
|
|
181
|
+
provider: "sanitized-object:snowflake",
|
|
182
|
+
model: "sanitized-object:claude-opus-4-6",
|
|
183
|
+
idleMs: 180001,
|
|
184
|
+
attempt: 0,
|
|
185
|
+
},
|
|
186
|
+
}),
|
|
187
|
+
);
|
|
188
|
+
// Winston gets the body only (structured attributes go to OTel, not also
|
|
189
|
+
// to stdout log shippers as a duplicate).
|
|
190
|
+
expect(winstonLoggerMock.warn).toHaveBeenCalledWith(
|
|
191
|
+
"sanitized-message:Idle timeout exceeded",
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("strips secret-named attribute keys and leaves numbers untouched", () => {
|
|
196
|
+
const logger = getLogger();
|
|
197
|
+
|
|
198
|
+
logger.warnStructured?.("stall", {
|
|
199
|
+
provider: "snowflake",
|
|
200
|
+
password: "sekret",
|
|
201
|
+
idleMs: 42,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(emitMock).toHaveBeenCalledWith(
|
|
205
|
+
expect.objectContaining({
|
|
206
|
+
attributes: expect.objectContaining({
|
|
207
|
+
provider: "sanitized-object:snowflake",
|
|
208
|
+
idleMs: 42,
|
|
209
|
+
}),
|
|
210
|
+
}),
|
|
211
|
+
);
|
|
212
|
+
// Secret-named keys are dropped before reaching the OTel record.
|
|
213
|
+
expect(emitMock).not.toHaveBeenCalledWith(
|
|
214
|
+
expect.objectContaining({
|
|
215
|
+
attributes: expect.objectContaining({ password: expect.anything() }),
|
|
216
|
+
}),
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
});
|
package/src/telemetry/logging.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type winston from "winston";
|
|
|
6
6
|
import {
|
|
7
7
|
sanitizeLogError,
|
|
8
8
|
sanitizeLogMessage,
|
|
9
|
+
sanitizeLogObject,
|
|
9
10
|
} from "@superblocksteam/telemetry";
|
|
10
11
|
|
|
11
12
|
import { getLogger as getTracedLogger } from "./index.js";
|
|
@@ -67,10 +68,23 @@ export interface ErrorMeta {
|
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Primitive-only attribute map. Forwarded to OTel log attributes (Datadog
|
|
73
|
+
* facets), so values must stay low-cardinality-friendly (enums/ids/numbers),
|
|
74
|
+
* not free-form text.
|
|
75
|
+
*/
|
|
76
|
+
export type LogAttributes = Record<string, string | number | boolean>;
|
|
77
|
+
|
|
70
78
|
export interface Logger {
|
|
71
79
|
debug: (...messages: unknown[]) => void;
|
|
72
80
|
info: (...messages: unknown[]) => void;
|
|
73
81
|
warn: (...messages: unknown[]) => void;
|
|
82
|
+
/**
|
|
83
|
+
* Emit a WARN-level log whose structured attributes are exported as OTel log
|
|
84
|
+
* attributes (Datadog facets) instead of being flattened into the message
|
|
85
|
+
* body. Use for machine-queryable warn events such as stream stalls.
|
|
86
|
+
*/
|
|
87
|
+
warnStructured?: (message: string, attributes: LogAttributes) => void;
|
|
74
88
|
error: (message: string, meta?: ErrorMeta) => void;
|
|
75
89
|
}
|
|
76
90
|
|
|
@@ -106,6 +120,23 @@ const logger: Logger = Object.freeze({
|
|
|
106
120
|
});
|
|
107
121
|
winstonLogger.warn(body);
|
|
108
122
|
},
|
|
123
|
+
warnStructured: (message: string, attributes: LogAttributes) => {
|
|
124
|
+
const body = sanitizeLogMessage(message);
|
|
125
|
+
// sanitizeLogObject strips secret-named keys and redacts secret values,
|
|
126
|
+
// matching the vite-plugin wrapLogger path so both implementations of this
|
|
127
|
+
// contract enforce the same invariant.
|
|
128
|
+
const safeAttributes = sanitizeLogObject(attributes);
|
|
129
|
+
getTracedLogger().emit({
|
|
130
|
+
severityNumber: SeverityNumber.WARN,
|
|
131
|
+
severityText: "WARN",
|
|
132
|
+
body,
|
|
133
|
+
attributes: safeAttributes,
|
|
134
|
+
});
|
|
135
|
+
// OTel is the canonical sink for the structured attributes; Winston gets
|
|
136
|
+
// the body only (matching warn() above) so attributes are not also shipped
|
|
137
|
+
// to Datadog via stdout log shippers as a second, duplicate copy.
|
|
138
|
+
winstonLogger.warn(body);
|
|
139
|
+
},
|
|
109
140
|
error: (message: string, meta?: ErrorMeta) => {
|
|
110
141
|
const safeMessage = sanitizeLogMessage(message);
|
|
111
142
|
const safeError = meta?.error
|
|
@@ -139,6 +170,12 @@ export function getLogger(
|
|
|
139
170
|
debug: loggerOverride,
|
|
140
171
|
info: loggerOverride,
|
|
141
172
|
warn: loggerOverride,
|
|
173
|
+
// The override path is for test/debug sinks only: like debug/info/warn
|
|
174
|
+
// above, it forwards raw args to the supplied function and does NOT
|
|
175
|
+
// sanitize or emit OTel attributes. Production callers use the default
|
|
176
|
+
// logger (and the vite-plugin wrapLogger) which sanitize before emitting.
|
|
177
|
+
warnStructured: (message: string, attributes: LogAttributes) =>
|
|
178
|
+
loggerOverride(message, attributes),
|
|
142
179
|
error: loggerOverride as (message: string, meta?: ErrorMeta) => void,
|
|
143
180
|
};
|
|
144
181
|
}
|