dspx 0.1.1-alpha.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/.github/workflows/ci.yml +185 -0
- package/.vscode/c_cpp_properties.json +17 -0
- package/.vscode/settings.json +68 -0
- package/.vscode/tasks.json +28 -0
- package/DISCLAIMER.md +32 -0
- package/LICENSE +21 -0
- package/README.md +1803 -0
- package/ROADMAP.md +192 -0
- package/TECHNICAL_DEBT.md +165 -0
- package/binding.gyp +65 -0
- package/docs/ADVANCED_LOGGER_FEATURES.md +598 -0
- package/docs/AUTHENTICATION_SECURITY.md +396 -0
- package/docs/BACKEND_IMPROVEMENTS.md +399 -0
- package/docs/CHEBYSHEV_BIQUAD_EQ_IMPLEMENTATION.md +405 -0
- package/docs/FFT_IMPLEMENTATION.md +490 -0
- package/docs/FFT_IMPROVEMENTS_SUMMARY.md +387 -0
- package/docs/FFT_USER_GUIDE.md +494 -0
- package/docs/FILTERS_IMPLEMENTATION.md +260 -0
- package/docs/FILTER_API_GUIDE.md +418 -0
- package/docs/FIR_SIMD_OPTIMIZATION.md +175 -0
- package/docs/LOGGER_API_REFERENCE.md +350 -0
- package/docs/NOTCH_FILTER_QUICK_REF.md +121 -0
- package/docs/PHASE2_TESTS_AND_NOTCH_FILTER.md +341 -0
- package/docs/PHASES_5_7_SUMMARY.md +403 -0
- package/docs/PIPELINE_FILTER_INTEGRATION.md +446 -0
- package/docs/SIMD_OPTIMIZATIONS.md +211 -0
- package/docs/TEST_MIGRATION_SUMMARY.md +173 -0
- package/docs/TIMESERIES_IMPLEMENTATION_SUMMARY.md +322 -0
- package/docs/TIMESERIES_QUICK_REF.md +85 -0
- package/docs/advanced.md +559 -0
- package/docs/time-series-guide.md +617 -0
- package/docs/time-series-migration.md +376 -0
- package/jest.config.js +37 -0
- package/package.json +42 -0
- package/prebuilds/linux-x64/dsp-ts-redis.node +0 -0
- package/prebuilds/win32-x64/dsp-ts-redis.node +0 -0
- package/scripts/test.js +24 -0
- package/src/build/dsp-ts-redis.node +0 -0
- package/src/native/DspPipeline.cc +675 -0
- package/src/native/DspPipeline.h +44 -0
- package/src/native/FftBindings.cc +817 -0
- package/src/native/FilterBindings.cc +1001 -0
- package/src/native/IDspStage.h +53 -0
- package/src/native/adapters/InterpolatorStage.h +201 -0
- package/src/native/adapters/MeanAbsoluteValueStage.h +289 -0
- package/src/native/adapters/MovingAverageStage.h +306 -0
- package/src/native/adapters/RectifyStage.h +88 -0
- package/src/native/adapters/ResamplerStage.h +238 -0
- package/src/native/adapters/RmsStage.h +299 -0
- package/src/native/adapters/SscStage.h +121 -0
- package/src/native/adapters/VarianceStage.h +307 -0
- package/src/native/adapters/WampStage.h +114 -0
- package/src/native/adapters/WaveformLengthStage.h +115 -0
- package/src/native/adapters/ZScoreNormalizeStage.h +326 -0
- package/src/native/core/FftEngine.cc +441 -0
- package/src/native/core/FftEngine.h +224 -0
- package/src/native/core/FirFilter.cc +324 -0
- package/src/native/core/FirFilter.h +149 -0
- package/src/native/core/IirFilter.cc +576 -0
- package/src/native/core/IirFilter.h +210 -0
- package/src/native/core/MovingAbsoluteValueFilter.cc +17 -0
- package/src/native/core/MovingAbsoluteValueFilter.h +135 -0
- package/src/native/core/MovingAverageFilter.cc +18 -0
- package/src/native/core/MovingAverageFilter.h +135 -0
- package/src/native/core/MovingFftFilter.cc +291 -0
- package/src/native/core/MovingFftFilter.h +203 -0
- package/src/native/core/MovingVarianceFilter.cc +194 -0
- package/src/native/core/MovingVarianceFilter.h +114 -0
- package/src/native/core/MovingZScoreFilter.cc +215 -0
- package/src/native/core/MovingZScoreFilter.h +113 -0
- package/src/native/core/Policies.h +352 -0
- package/src/native/core/RmsFilter.cc +18 -0
- package/src/native/core/RmsFilter.h +131 -0
- package/src/native/core/SscFilter.cc +16 -0
- package/src/native/core/SscFilter.h +137 -0
- package/src/native/core/WampFilter.cc +16 -0
- package/src/native/core/WampFilter.h +101 -0
- package/src/native/core/WaveformLengthFilter.cc +17 -0
- package/src/native/core/WaveformLengthFilter.h +98 -0
- package/src/native/utils/CircularBufferArray.cc +336 -0
- package/src/native/utils/CircularBufferArray.h +62 -0
- package/src/native/utils/CircularBufferVector.cc +145 -0
- package/src/native/utils/CircularBufferVector.h +45 -0
- package/src/native/utils/NapiUtils.cc +53 -0
- package/src/native/utils/NapiUtils.h +21 -0
- package/src/native/utils/SimdOps.h +870 -0
- package/src/native/utils/SlidingWindowFilter.cc +239 -0
- package/src/native/utils/SlidingWindowFilter.h +159 -0
- package/src/native/utils/TimeSeriesBuffer.cc +205 -0
- package/src/native/utils/TimeSeriesBuffer.h +140 -0
- package/src/ts/CircularLogBuffer.ts +87 -0
- package/src/ts/DriftDetector.ts +331 -0
- package/src/ts/TopicRouter.ts +428 -0
- package/src/ts/__tests__/AdvancedDsp.test.ts +585 -0
- package/src/ts/__tests__/AuthAndEdgeCases.test.ts +241 -0
- package/src/ts/__tests__/Chaining.test.ts +387 -0
- package/src/ts/__tests__/ChebyshevBiquad.test.ts +229 -0
- package/src/ts/__tests__/CircularLogBuffer.test.ts +158 -0
- package/src/ts/__tests__/DriftDetector.test.ts +389 -0
- package/src/ts/__tests__/Fft.test.ts +484 -0
- package/src/ts/__tests__/ListState.test.ts +153 -0
- package/src/ts/__tests__/Logger.test.ts +208 -0
- package/src/ts/__tests__/LoggerAdvanced.test.ts +319 -0
- package/src/ts/__tests__/LoggerMinor.test.ts +247 -0
- package/src/ts/__tests__/MeanAbsoluteValue.test.ts +398 -0
- package/src/ts/__tests__/MovingAverage.test.ts +322 -0
- package/src/ts/__tests__/RMS.test.ts +315 -0
- package/src/ts/__tests__/Rectify.test.ts +272 -0
- package/src/ts/__tests__/Redis.test.ts +456 -0
- package/src/ts/__tests__/SlopeSignChange.test.ts +166 -0
- package/src/ts/__tests__/Tap.test.ts +164 -0
- package/src/ts/__tests__/TimeBasedExpiration.test.ts +124 -0
- package/src/ts/__tests__/TimeBasedRmsAndMav.test.ts +231 -0
- package/src/ts/__tests__/TimeBasedVarianceAndZScore.test.ts +284 -0
- package/src/ts/__tests__/TimeSeries.test.ts +254 -0
- package/src/ts/__tests__/TopicRouter.test.ts +332 -0
- package/src/ts/__tests__/TopicRouterAdvanced.test.ts +483 -0
- package/src/ts/__tests__/TopicRouterPriority.test.ts +487 -0
- package/src/ts/__tests__/Variance.test.ts +509 -0
- package/src/ts/__tests__/WaveformLength.test.ts +147 -0
- package/src/ts/__tests__/WillisonAmplitude.test.ts +197 -0
- package/src/ts/__tests__/ZScoreNormalize.test.ts +459 -0
- package/src/ts/advanced-dsp.ts +566 -0
- package/src/ts/backends.ts +1137 -0
- package/src/ts/bindings.ts +1225 -0
- package/src/ts/easter-egg.ts +42 -0
- package/src/ts/examples/MeanAbsoluteValue/test-state.ts +99 -0
- package/src/ts/examples/MeanAbsoluteValue/test-streaming.ts +269 -0
- package/src/ts/examples/MovingAverage/test-state.ts +85 -0
- package/src/ts/examples/MovingAverage/test-streaming.ts +188 -0
- package/src/ts/examples/RMS/test-state.ts +97 -0
- package/src/ts/examples/RMS/test-streaming.ts +253 -0
- package/src/ts/examples/Rectify/test-state.ts +107 -0
- package/src/ts/examples/Rectify/test-streaming.ts +242 -0
- package/src/ts/examples/Variance/test-state.ts +195 -0
- package/src/ts/examples/Variance/test-streaming.ts +260 -0
- package/src/ts/examples/ZScoreNormalize/test-state.ts +277 -0
- package/src/ts/examples/ZScoreNormalize/test-streaming.ts +306 -0
- package/src/ts/examples/advanced-dsp-examples.ts +397 -0
- package/src/ts/examples/callbacks/advanced-router-features.ts +326 -0
- package/src/ts/examples/callbacks/benchmark-circular-buffer.ts +109 -0
- package/src/ts/examples/callbacks/monitoring-example.ts +265 -0
- package/src/ts/examples/callbacks/pipeline-callbacks-example.ts +137 -0
- package/src/ts/examples/callbacks/pooled-callbacks-example.ts +274 -0
- package/src/ts/examples/callbacks/priority-routing-example.ts +277 -0
- package/src/ts/examples/callbacks/production-topic-router.ts +214 -0
- package/src/ts/examples/callbacks/topic-based-logging.ts +161 -0
- package/src/ts/examples/chaining/test-chaining-redis.ts +113 -0
- package/src/ts/examples/chaining/test-chaining.ts +52 -0
- package/src/ts/examples/emg-features-example.ts +284 -0
- package/src/ts/examples/fft-example.ts +309 -0
- package/src/ts/examples/fft-examples.ts +349 -0
- package/src/ts/examples/filter-examples.ts +320 -0
- package/src/ts/examples/list-state-example.ts +131 -0
- package/src/ts/examples/logger-example.ts +91 -0
- package/src/ts/examples/notch-filter-examples.ts +243 -0
- package/src/ts/examples/phase5/drift-detection-example.ts +290 -0
- package/src/ts/examples/phase6-7/production-observability.ts +476 -0
- package/src/ts/examples/phase6-7/redis-timeseries-integration.ts +446 -0
- package/src/ts/examples/redis/redis-example.ts +202 -0
- package/src/ts/examples/redis-example.ts +202 -0
- package/src/ts/examples/simd-benchmark.ts +126 -0
- package/src/ts/examples/tap-debugging.ts +230 -0
- package/src/ts/examples/timeseries/comparison-example.ts +290 -0
- package/src/ts/examples/timeseries/iot-sensor-example.ts +143 -0
- package/src/ts/examples/timeseries/redis-streaming-example.ts +233 -0
- package/src/ts/examples/waveform-length-example.ts +139 -0
- package/src/ts/fft.ts +722 -0
- package/src/ts/filters.ts +1078 -0
- package/src/ts/index.ts +120 -0
- package/src/ts/types.ts +589 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for unified Logger class and backend utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { test } from "node:test";
|
|
6
|
+
import assert from "node:assert";
|
|
7
|
+
import {
|
|
8
|
+
Logger,
|
|
9
|
+
createConsoleHandler,
|
|
10
|
+
createMockHandler,
|
|
11
|
+
type LogEntry,
|
|
12
|
+
} from "../index.js";
|
|
13
|
+
|
|
14
|
+
test("Logger - basic logging", async () => {
|
|
15
|
+
const mock = createMockHandler();
|
|
16
|
+
const logger = new Logger([mock.handler]);
|
|
17
|
+
|
|
18
|
+
await logger.info("Test message", "test.topic", { key: "value" });
|
|
19
|
+
|
|
20
|
+
const logs = mock.getLogs();
|
|
21
|
+
assert.strictEqual(logs.length, 1);
|
|
22
|
+
assert.strictEqual(logs[0].level, "info");
|
|
23
|
+
assert.strictEqual(logs[0].message, "Test message");
|
|
24
|
+
assert.strictEqual(logs[0].topic, "test.topic");
|
|
25
|
+
assert.deepStrictEqual(logs[0].context, { key: "value" });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("Logger - multiple handlers dispatch", async () => {
|
|
29
|
+
const mock1 = createMockHandler();
|
|
30
|
+
const mock2 = createMockHandler();
|
|
31
|
+
const logger = new Logger([mock1.handler, mock2.handler]);
|
|
32
|
+
|
|
33
|
+
await logger.error("Error message");
|
|
34
|
+
|
|
35
|
+
assert.strictEqual(mock1.getLogs().length, 1);
|
|
36
|
+
assert.strictEqual(mock2.getLogs().length, 1);
|
|
37
|
+
assert.strictEqual(mock1.getLogs()[0].level, "error");
|
|
38
|
+
assert.strictEqual(mock2.getLogs()[0].level, "error");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("Logger - error isolation (one handler fails)", async () => {
|
|
42
|
+
const mock = createMockHandler();
|
|
43
|
+
const failingHandler = async () => {
|
|
44
|
+
throw new Error("Handler failure");
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Should not throw despite failing handler
|
|
48
|
+
const logger = new Logger([mock.handler, failingHandler]);
|
|
49
|
+
await logger.warn("Test warning");
|
|
50
|
+
|
|
51
|
+
// Successful handler should still receive log
|
|
52
|
+
assert.strictEqual(mock.getLogs().length, 1);
|
|
53
|
+
assert.strictEqual(mock.getLogs()[0].message, "Test warning");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("Logger - structured error logging to fallback", async () => {
|
|
57
|
+
const fallbackMock = createMockHandler();
|
|
58
|
+
const failingHandler = async () => {
|
|
59
|
+
throw new Error("Network timeout");
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const logger = new Logger([failingHandler], {
|
|
63
|
+
fallbackHandler: fallbackMock.handler,
|
|
64
|
+
});
|
|
65
|
+
await logger.info("Original message");
|
|
66
|
+
|
|
67
|
+
// Fallback should receive error log
|
|
68
|
+
const fallbackLogs = fallbackMock.getLogs();
|
|
69
|
+
assert.strictEqual(fallbackLogs.length, 1);
|
|
70
|
+
assert.strictEqual(fallbackLogs[0].level, "error");
|
|
71
|
+
assert.ok(fallbackLogs[0].message.includes("Handler error"));
|
|
72
|
+
assert.ok(fallbackLogs[0].message.includes("Network timeout"));
|
|
73
|
+
assert.strictEqual(fallbackLogs[0].topic, "logger.handler.error");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("Logger - child logger with topic prefix", async () => {
|
|
77
|
+
const mock = createMockHandler();
|
|
78
|
+
const logger = new Logger([mock.handler]);
|
|
79
|
+
const childLogger = logger.child("pipeline.stage1");
|
|
80
|
+
|
|
81
|
+
await childLogger.debug("Stage initialized", "init");
|
|
82
|
+
|
|
83
|
+
const logs = mock.getLogs();
|
|
84
|
+
assert.strictEqual(logs.length, 1);
|
|
85
|
+
assert.strictEqual(logs[0].topic, "pipeline.stage1.init");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("Logger - all log levels", async () => {
|
|
89
|
+
const mock = createMockHandler();
|
|
90
|
+
const logger = new Logger([mock.handler]);
|
|
91
|
+
|
|
92
|
+
await logger.debug("Debug message");
|
|
93
|
+
await logger.info("Info message");
|
|
94
|
+
await logger.warn("Warn message");
|
|
95
|
+
await logger.error("Error message");
|
|
96
|
+
|
|
97
|
+
const logs = mock.getLogs();
|
|
98
|
+
assert.strictEqual(logs.length, 4);
|
|
99
|
+
assert.strictEqual(logs[0].level, "debug");
|
|
100
|
+
assert.strictEqual(logs[1].level, "info");
|
|
101
|
+
assert.strictEqual(logs[2].level, "warn");
|
|
102
|
+
assert.strictEqual(logs[3].level, "error");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("Logger - timestamp generation", async () => {
|
|
106
|
+
const mock = createMockHandler();
|
|
107
|
+
const logger = new Logger([mock.handler]);
|
|
108
|
+
|
|
109
|
+
const before = Date.now();
|
|
110
|
+
await logger.info("Timestamp test");
|
|
111
|
+
const after = Date.now();
|
|
112
|
+
|
|
113
|
+
const logs = mock.getLogs();
|
|
114
|
+
assert.strictEqual(logs.length, 1);
|
|
115
|
+
assert.ok(logs[0].timestamp >= before);
|
|
116
|
+
assert.ok(logs[0].timestamp <= after);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("Logger - default topic", async () => {
|
|
120
|
+
const mock = createMockHandler();
|
|
121
|
+
const logger = new Logger([mock.handler]);
|
|
122
|
+
|
|
123
|
+
await logger.info("No topic specified");
|
|
124
|
+
|
|
125
|
+
const logs = mock.getLogs();
|
|
126
|
+
assert.strictEqual(logs[0].topic, "default");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("Logger - complex context object", async () => {
|
|
130
|
+
const mock = createMockHandler();
|
|
131
|
+
const logger = new Logger([mock.handler]);
|
|
132
|
+
|
|
133
|
+
const complexContext = {
|
|
134
|
+
user: { id: 123, name: "Alice" },
|
|
135
|
+
metrics: { latency: 45, throughput: 1000 },
|
|
136
|
+
tags: ["production", "critical"],
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
await logger.info("Complex context", "app.event", complexContext);
|
|
140
|
+
|
|
141
|
+
const logs = mock.getLogs();
|
|
142
|
+
assert.deepStrictEqual(logs[0].context, complexContext);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("MockHandler - clear functionality", () => {
|
|
146
|
+
const mock = createMockHandler();
|
|
147
|
+
|
|
148
|
+
mock.handler({
|
|
149
|
+
level: "info",
|
|
150
|
+
message: "Test",
|
|
151
|
+
topic: "test",
|
|
152
|
+
timestamp: Date.now(),
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
assert.strictEqual(mock.getLogs().length, 1);
|
|
156
|
+
mock.clear();
|
|
157
|
+
assert.strictEqual(mock.getLogs().length, 0);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("MockHandler - onLog callback", () => {
|
|
161
|
+
let callbackInvoked = false;
|
|
162
|
+
const mock = createMockHandler((log: LogEntry) => {
|
|
163
|
+
callbackInvoked = true;
|
|
164
|
+
assert.strictEqual(log.message, "Callback test");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
mock.handler({
|
|
168
|
+
level: "info",
|
|
169
|
+
message: "Callback test",
|
|
170
|
+
topic: "test",
|
|
171
|
+
timestamp: Date.now(),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
assert.ok(callbackInvoked);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("Logger - synchronous handler support", async () => {
|
|
178
|
+
const logs: LogEntry[] = [];
|
|
179
|
+
const syncHandler = (log: LogEntry) => {
|
|
180
|
+
logs.push(log);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const logger = new Logger([syncHandler]);
|
|
184
|
+
await logger.info("Sync handler test");
|
|
185
|
+
|
|
186
|
+
assert.strictEqual(logs.length, 1);
|
|
187
|
+
assert.strictEqual(logs[0].message, "Sync handler test");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("Logger - mixed sync/async handlers", async () => {
|
|
191
|
+
const syncLogs: LogEntry[] = [];
|
|
192
|
+
const asyncLogs: LogEntry[] = [];
|
|
193
|
+
|
|
194
|
+
const syncHandler = (log: LogEntry) => {
|
|
195
|
+
syncLogs.push(log);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const asyncHandler = async (log: LogEntry) => {
|
|
199
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
200
|
+
asyncLogs.push(log);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const logger = new Logger([syncHandler, asyncHandler]);
|
|
204
|
+
await logger.info("Mixed handlers");
|
|
205
|
+
|
|
206
|
+
assert.strictEqual(syncLogs.length, 1);
|
|
207
|
+
assert.strictEqual(asyncLogs.length, 1);
|
|
208
|
+
});
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for advanced Logger features:
|
|
3
|
+
* - Extended log levels (trace/fatal)
|
|
4
|
+
* - Distributed tracing
|
|
5
|
+
* - Graceful shutdown (flushAll)
|
|
6
|
+
* - Performance metrics
|
|
7
|
+
* - Severity mappings
|
|
8
|
+
* - Formatters
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { test } from "node:test";
|
|
12
|
+
import assert from "node:assert";
|
|
13
|
+
import {
|
|
14
|
+
Logger,
|
|
15
|
+
createMockHandler,
|
|
16
|
+
JSONFormatter,
|
|
17
|
+
TextFormatter,
|
|
18
|
+
SEVERITY_MAPPINGS,
|
|
19
|
+
tracingContext,
|
|
20
|
+
getTracingContext,
|
|
21
|
+
withTracingContext,
|
|
22
|
+
type LogEntry,
|
|
23
|
+
type HandlerWithFlush,
|
|
24
|
+
} from "../index.js";
|
|
25
|
+
|
|
26
|
+
// Test 1: Extended log levels (trace and fatal)
|
|
27
|
+
test("Logger - trace level (most verbose)", async () => {
|
|
28
|
+
const mock = createMockHandler();
|
|
29
|
+
const logger = new Logger([mock.handler]);
|
|
30
|
+
|
|
31
|
+
await logger.trace("Trace message", "debug.trace");
|
|
32
|
+
|
|
33
|
+
const logs = mock.getLogs();
|
|
34
|
+
assert.strictEqual(logs.length, 1);
|
|
35
|
+
assert.strictEqual(logs[0].level, "trace");
|
|
36
|
+
assert.strictEqual(logs[0].message, "Trace message");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("Logger - fatal level (most critical)", async () => {
|
|
40
|
+
const mock = createMockHandler();
|
|
41
|
+
const logger = new Logger([mock.handler]);
|
|
42
|
+
|
|
43
|
+
await logger.fatal("Fatal error", "system.critical");
|
|
44
|
+
|
|
45
|
+
const logs = mock.getLogs();
|
|
46
|
+
assert.strictEqual(logs.length, 1);
|
|
47
|
+
assert.strictEqual(logs[0].level, "fatal");
|
|
48
|
+
assert.strictEqual(logs[0].message, "Fatal error");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Test 2: Distributed tracing - auto-inject context
|
|
52
|
+
test("Logger - auto-inject tracing context", async () => {
|
|
53
|
+
const mock = createMockHandler();
|
|
54
|
+
const logger = new Logger([mock.handler]);
|
|
55
|
+
|
|
56
|
+
await withTracingContext(
|
|
57
|
+
{
|
|
58
|
+
traceId: "trace-12345",
|
|
59
|
+
spanId: "span-67890",
|
|
60
|
+
correlationId: "corr-abcde",
|
|
61
|
+
},
|
|
62
|
+
async () => {
|
|
63
|
+
await logger.info("Message with tracing");
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const logs = mock.getLogs();
|
|
68
|
+
assert.strictEqual(logs.length, 1);
|
|
69
|
+
assert.strictEqual(logs[0].traceId, "trace-12345");
|
|
70
|
+
assert.strictEqual(logs[0].spanId, "span-67890");
|
|
71
|
+
assert.strictEqual(logs[0].correlationId, "corr-abcde");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("Logger - getTracingContext outside of context", () => {
|
|
75
|
+
const ctx = getTracingContext();
|
|
76
|
+
assert.deepStrictEqual(ctx, {});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("Logger - getTracingContext inside context", () => {
|
|
80
|
+
withTracingContext({ traceId: "test-trace" }, () => {
|
|
81
|
+
const ctx = getTracingContext();
|
|
82
|
+
assert.strictEqual(ctx.traceId, "test-trace");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Test 3: Graceful shutdown (flushAll)
|
|
87
|
+
test("Logger - flushAll calls handler flush methods", async () => {
|
|
88
|
+
let flushCalled = false;
|
|
89
|
+
|
|
90
|
+
const handlerWithFlush: HandlerWithFlush = async (log: LogEntry) => {
|
|
91
|
+
// Handler logic
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
handlerWithFlush.flush = async () => {
|
|
95
|
+
flushCalled = true;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const logger = new Logger([handlerWithFlush]);
|
|
99
|
+
|
|
100
|
+
await logger.info("Test message");
|
|
101
|
+
await logger.flushAll();
|
|
102
|
+
|
|
103
|
+
assert.ok(flushCalled, "Handler flush should be called");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("Logger - flushAll handles missing flush gracefully", async () => {
|
|
107
|
+
const mock = createMockHandler();
|
|
108
|
+
const logger = new Logger([mock.handler]);
|
|
109
|
+
|
|
110
|
+
await logger.info("Test message");
|
|
111
|
+
|
|
112
|
+
// Should not throw even though handler has no flush method
|
|
113
|
+
await assert.doesNotReject(async () => {
|
|
114
|
+
await logger.flushAll();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("Logger - flushAll handles flush errors gracefully", async () => {
|
|
119
|
+
const handlerWithFlush: HandlerWithFlush = async (log: LogEntry) => {};
|
|
120
|
+
handlerWithFlush.flush = async () => {
|
|
121
|
+
throw new Error("Flush failed");
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const logger = new Logger([handlerWithFlush]);
|
|
125
|
+
|
|
126
|
+
// Should not throw even when flush fails
|
|
127
|
+
await assert.doesNotReject(async () => {
|
|
128
|
+
await logger.flushAll();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Test 4: Performance metrics
|
|
133
|
+
test("Logger - metrics tracking when enabled", async () => {
|
|
134
|
+
const mock = createMockHandler();
|
|
135
|
+
const logger = new Logger([mock.handler], { enableMetrics: true });
|
|
136
|
+
|
|
137
|
+
await logger.info("Message 1");
|
|
138
|
+
await logger.warn("Message 2");
|
|
139
|
+
await logger.error("Message 3");
|
|
140
|
+
|
|
141
|
+
const metrics = logger.getMetrics();
|
|
142
|
+
assert.strictEqual(metrics.logsProcessed, 3);
|
|
143
|
+
assert.strictEqual(metrics.logsFailed, 0);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("Logger - metrics track failures", async () => {
|
|
147
|
+
const failingHandler = async () => {
|
|
148
|
+
throw new Error("Handler error");
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const logger = new Logger([failingHandler], { enableMetrics: true });
|
|
152
|
+
|
|
153
|
+
await logger.info("Test");
|
|
154
|
+
await logger.info("Test2");
|
|
155
|
+
|
|
156
|
+
const metrics = logger.getMetrics();
|
|
157
|
+
assert.strictEqual(metrics.logsProcessed, 2);
|
|
158
|
+
assert.strictEqual(metrics.logsFailed, 2);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("Logger - metrics track flush count and time", async () => {
|
|
162
|
+
const handlerWithFlush: HandlerWithFlush = async (log: LogEntry) => {};
|
|
163
|
+
handlerWithFlush.flush = async () => {
|
|
164
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const logger = new Logger([handlerWithFlush], { enableMetrics: true });
|
|
168
|
+
|
|
169
|
+
await logger.info("Test");
|
|
170
|
+
await logger.flushAll();
|
|
171
|
+
await logger.flushAll();
|
|
172
|
+
|
|
173
|
+
const metrics = logger.getMetrics();
|
|
174
|
+
assert.strictEqual(metrics.flushCount, 2);
|
|
175
|
+
assert.ok(metrics.averageFlushTimeMs > 0);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("Logger - resetMetrics clears counters", async () => {
|
|
179
|
+
const mock = createMockHandler();
|
|
180
|
+
const logger = new Logger([mock.handler], { enableMetrics: true });
|
|
181
|
+
|
|
182
|
+
await logger.info("Test");
|
|
183
|
+
let metrics = logger.getMetrics();
|
|
184
|
+
assert.strictEqual(metrics.logsProcessed, 1);
|
|
185
|
+
|
|
186
|
+
logger.resetMetrics();
|
|
187
|
+
metrics = logger.getMetrics();
|
|
188
|
+
assert.strictEqual(metrics.logsProcessed, 0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Test 5: Severity mappings
|
|
192
|
+
test("Logger - getSeverityMapping returns configured mapping", () => {
|
|
193
|
+
const logger = new Logger([], {
|
|
194
|
+
severityMapping: SEVERITY_MAPPINGS.pagerduty,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const mapping = logger.getSeverityMapping();
|
|
198
|
+
assert.deepStrictEqual(mapping, SEVERITY_MAPPINGS.pagerduty);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("SEVERITY_MAPPINGS - PagerDuty mapping", () => {
|
|
202
|
+
assert.strictEqual(SEVERITY_MAPPINGS.pagerduty.trace, "info");
|
|
203
|
+
assert.strictEqual(SEVERITY_MAPPINGS.pagerduty.error, "error");
|
|
204
|
+
assert.strictEqual(SEVERITY_MAPPINGS.pagerduty.fatal, "critical");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("SEVERITY_MAPPINGS - Datadog mapping", () => {
|
|
208
|
+
assert.strictEqual(SEVERITY_MAPPINGS.datadog.debug, "debug");
|
|
209
|
+
assert.strictEqual(SEVERITY_MAPPINGS.datadog.warn, "warn");
|
|
210
|
+
assert.strictEqual(SEVERITY_MAPPINGS.datadog.fatal, "emergency");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("SEVERITY_MAPPINGS - Syslog mapping", () => {
|
|
214
|
+
assert.strictEqual(SEVERITY_MAPPINGS.syslog.trace, "7");
|
|
215
|
+
assert.strictEqual(SEVERITY_MAPPINGS.syslog.info, "6");
|
|
216
|
+
assert.strictEqual(SEVERITY_MAPPINGS.syslog.fatal, "0");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Test 6: Formatters
|
|
220
|
+
test("JSONFormatter - formats log as object", () => {
|
|
221
|
+
const formatter = new JSONFormatter();
|
|
222
|
+
const log: LogEntry = {
|
|
223
|
+
level: "info",
|
|
224
|
+
message: "Test",
|
|
225
|
+
timestamp: Date.now(),
|
|
226
|
+
context: { key: "value" },
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const result = formatter.format(log);
|
|
230
|
+
assert.deepStrictEqual(result, log);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("TextFormatter - formats log as human-readable string", () => {
|
|
234
|
+
const formatter = new TextFormatter();
|
|
235
|
+
const log: LogEntry = {
|
|
236
|
+
level: "error",
|
|
237
|
+
message: "Connection failed",
|
|
238
|
+
topic: "redis",
|
|
239
|
+
timestamp: Date.now(),
|
|
240
|
+
context: { host: "localhost" },
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const result = formatter.format(log);
|
|
244
|
+
assert.ok(typeof result === "string");
|
|
245
|
+
assert.ok(result.includes("ERROR"));
|
|
246
|
+
assert.ok(result.includes("Connection failed"));
|
|
247
|
+
assert.ok(result.includes("redis"));
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("TextFormatter - includes trace ID when present", () => {
|
|
251
|
+
const formatter = new TextFormatter();
|
|
252
|
+
const log: LogEntry = {
|
|
253
|
+
level: "info",
|
|
254
|
+
message: "Test",
|
|
255
|
+
timestamp: Date.now(),
|
|
256
|
+
traceId: "trace-12345678abcd",
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const result = formatter.format(log);
|
|
260
|
+
assert.ok(result.includes("[trace:trace-12"));
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("TextFormatter - omits context when empty", () => {
|
|
264
|
+
const formatter = new TextFormatter();
|
|
265
|
+
const log: LogEntry = {
|
|
266
|
+
level: "info",
|
|
267
|
+
message: "Test",
|
|
268
|
+
timestamp: Date.now(),
|
|
269
|
+
context: {},
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const result = formatter.format(log);
|
|
273
|
+
assert.ok(!result.includes("Context:"));
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Test 7: Child logger with options preservation
|
|
277
|
+
test("Logger - child logger preserves metrics setting", async () => {
|
|
278
|
+
const mock = createMockHandler();
|
|
279
|
+
const logger = new Logger([mock.handler], { enableMetrics: true });
|
|
280
|
+
const childLogger = logger.child("child");
|
|
281
|
+
|
|
282
|
+
await childLogger.info("Test");
|
|
283
|
+
|
|
284
|
+
// Child should inherit metrics capability
|
|
285
|
+
const metrics = childLogger.getMetrics();
|
|
286
|
+
assert.ok(metrics.logsProcessed >= 0); // Should be accessible
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("Logger - child logger preserves severity mapping", () => {
|
|
290
|
+
const logger = new Logger([], {
|
|
291
|
+
severityMapping: SEVERITY_MAPPINGS.datadog,
|
|
292
|
+
});
|
|
293
|
+
const childLogger = logger.child("child");
|
|
294
|
+
|
|
295
|
+
const mapping = childLogger.getSeverityMapping();
|
|
296
|
+
assert.deepStrictEqual(mapping, SEVERITY_MAPPINGS.datadog);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// Test 8: All log levels in order
|
|
300
|
+
test("Logger - all six log levels", async () => {
|
|
301
|
+
const mock = createMockHandler();
|
|
302
|
+
const logger = new Logger([mock.handler]);
|
|
303
|
+
|
|
304
|
+
await logger.trace("Trace");
|
|
305
|
+
await logger.debug("Debug");
|
|
306
|
+
await logger.info("Info");
|
|
307
|
+
await logger.warn("Warn");
|
|
308
|
+
await logger.error("Error");
|
|
309
|
+
await logger.fatal("Fatal");
|
|
310
|
+
|
|
311
|
+
const logs = mock.getLogs();
|
|
312
|
+
assert.strictEqual(logs.length, 6);
|
|
313
|
+
assert.strictEqual(logs[0].level, "trace");
|
|
314
|
+
assert.strictEqual(logs[1].level, "debug");
|
|
315
|
+
assert.strictEqual(logs[2].level, "info");
|
|
316
|
+
assert.strictEqual(logs[3].level, "warn");
|
|
317
|
+
assert.strictEqual(logs[4].level, "error");
|
|
318
|
+
assert.strictEqual(logs[5].level, "fatal");
|
|
319
|
+
});
|