@oxog/log 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -1
- package/dist/index.cjs +126 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +133 -20
- package/dist/index.js.map +1 -1
- package/dist/plugins/index.cjs +12 -4
- package/dist/plugins/index.cjs.map +1 -1
- package/dist/plugins/index.js +12 -4
- package/dist/plugins/index.js.map +1 -1
- package/dist/transports/index.cjs +57 -9
- package/dist/transports/index.cjs.map +1 -1
- package/dist/transports/index.js +64 -9
- package/dist/transports/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -214,13 +214,57 @@ const child = log.child({ component: 'Auth' });
|
|
|
214
214
|
child.info('Checking token');
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
+
## Transport Error Handling
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// Listen for transport failures
|
|
221
|
+
log.on('error', ({ transport, error, entry }) => {
|
|
222
|
+
console.error(`Transport ${transport} failed:`, error.message);
|
|
223
|
+
// Optionally alert ops team or use fallback
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Sync vs Async Logging
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
const log = createLogger({
|
|
231
|
+
// fatal and error are sync by default (written before process exit)
|
|
232
|
+
sync: {
|
|
233
|
+
fatal: true, // default
|
|
234
|
+
error: true, // default
|
|
235
|
+
warn: false,
|
|
236
|
+
info: false,
|
|
237
|
+
debug: false,
|
|
238
|
+
trace: false,
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Fatal logs are written synchronously - safe before process.exit()
|
|
243
|
+
process.on('uncaughtException', (err) => {
|
|
244
|
+
log.fatal(err, 'Uncaught exception');
|
|
245
|
+
process.exit(1); // Log is guaranteed to be written
|
|
246
|
+
});
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Efficient Redaction
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { stringifyWithRedaction } from '@oxog/log';
|
|
253
|
+
|
|
254
|
+
// More efficient than redactFields + JSON.stringify for large objects
|
|
255
|
+
const json = stringifyWithRedaction(
|
|
256
|
+
largeObject,
|
|
257
|
+
['password', 'token', 'headers.authorization']
|
|
258
|
+
);
|
|
259
|
+
```
|
|
260
|
+
|
|
217
261
|
## Graceful Shutdown
|
|
218
262
|
|
|
219
263
|
```typescript
|
|
220
264
|
// Flush buffered logs before shutdown
|
|
221
265
|
await log.flush();
|
|
222
266
|
|
|
223
|
-
// Cleanup and destroy
|
|
267
|
+
// Cleanup and destroy (also flushes buffer)
|
|
224
268
|
await log.close();
|
|
225
269
|
```
|
|
226
270
|
|
package/dist/index.cjs
CHANGED
|
@@ -272,7 +272,7 @@ function getCwd() {
|
|
|
272
272
|
|
|
273
273
|
// src/utils/format.ts
|
|
274
274
|
function formatJson(entry) {
|
|
275
|
-
return
|
|
275
|
+
return safeStringify(entry);
|
|
276
276
|
}
|
|
277
277
|
function jsonReplacer(_key, value) {
|
|
278
278
|
if (typeof value === "bigint") {
|
|
@@ -436,27 +436,45 @@ function consoleTransport(options = {}) {
|
|
|
436
436
|
} = options;
|
|
437
437
|
const mergedColors = { ...DEFAULT_LEVEL_COLORS, ...levelColors };
|
|
438
438
|
let pigment2;
|
|
439
|
+
function formatEntry(entry) {
|
|
440
|
+
if (colors && pigment2) {
|
|
441
|
+
return formatPretty(entry, pigment2, { timestamp, source: true });
|
|
442
|
+
} else if (colors) {
|
|
443
|
+
return formatWithAnsi(entry, mergedColors, timestamp);
|
|
444
|
+
} else {
|
|
445
|
+
return formatJson(entry);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
439
448
|
return {
|
|
440
449
|
name: "console",
|
|
441
450
|
write(entry) {
|
|
442
|
-
let output;
|
|
443
451
|
if (isBrowser()) {
|
|
444
452
|
writeToBrowserConsole(entry, colors);
|
|
445
453
|
return;
|
|
446
454
|
}
|
|
447
|
-
|
|
448
|
-
output = formatPretty(entry, pigment2, { timestamp, source: true });
|
|
449
|
-
} else if (colors) {
|
|
450
|
-
output = formatWithAnsi(entry, mergedColors, timestamp);
|
|
451
|
-
} else {
|
|
452
|
-
output = formatJson(entry);
|
|
453
|
-
}
|
|
455
|
+
const output = formatEntry(entry);
|
|
454
456
|
if (entry.level >= 50) {
|
|
455
457
|
process.stderr.write(output + "\n");
|
|
456
458
|
} else {
|
|
457
459
|
process.stdout.write(output + "\n");
|
|
458
460
|
}
|
|
459
461
|
},
|
|
462
|
+
writeSync(entry) {
|
|
463
|
+
if (isBrowser()) {
|
|
464
|
+
writeToBrowserConsole(entry, colors);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const output = formatEntry(entry) + "\n";
|
|
468
|
+
const fd = entry.level >= 50 ? 2 : 1;
|
|
469
|
+
try {
|
|
470
|
+
if (entry.level >= 50) {
|
|
471
|
+
process.stderr.write(output);
|
|
472
|
+
} else {
|
|
473
|
+
process.stdout.write(output);
|
|
474
|
+
}
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
},
|
|
460
478
|
flush() {
|
|
461
479
|
},
|
|
462
480
|
close() {
|
|
@@ -779,6 +797,31 @@ function createLogger(options = {}) {
|
|
|
779
797
|
kernel.use(plugin);
|
|
780
798
|
}
|
|
781
799
|
let closed = false;
|
|
800
|
+
function emitTransportError(transportName, error, entry) {
|
|
801
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
802
|
+
emitter.emit("error", { transport: transportName, error: err, entry });
|
|
803
|
+
if (isNode()) {
|
|
804
|
+
process.stderr.write(`[LOG TRANSPORT ERROR] ${transportName}: ${err.message}
|
|
805
|
+
`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function writeToTransportsSync(entry) {
|
|
809
|
+
if (closed) return;
|
|
810
|
+
for (const transport of activeTransports) {
|
|
811
|
+
try {
|
|
812
|
+
if (transport.writeSync) {
|
|
813
|
+
transport.writeSync(entry);
|
|
814
|
+
} else {
|
|
815
|
+
const result = transport.write(entry);
|
|
816
|
+
if (result instanceof Promise) {
|
|
817
|
+
result.catch((err) => emitTransportError(transport.name, err, entry));
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
} catch (err) {
|
|
821
|
+
emitTransportError(transport.name, err, entry);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
782
825
|
async function writeToTransports(entry) {
|
|
783
826
|
if (closed) return;
|
|
784
827
|
const promises = [];
|
|
@@ -786,14 +829,18 @@ function createLogger(options = {}) {
|
|
|
786
829
|
try {
|
|
787
830
|
const result = transport.write(entry);
|
|
788
831
|
if (result instanceof Promise) {
|
|
789
|
-
promises.push(
|
|
832
|
+
promises.push(
|
|
833
|
+
result.catch((err) => {
|
|
834
|
+
emitTransportError(transport.name, err, entry);
|
|
835
|
+
})
|
|
836
|
+
);
|
|
790
837
|
}
|
|
791
838
|
} catch (err) {
|
|
839
|
+
emitTransportError(transport.name, err, entry);
|
|
792
840
|
}
|
|
793
841
|
}
|
|
794
842
|
if (promises.length > 0) {
|
|
795
|
-
await Promise.all(promises)
|
|
796
|
-
});
|
|
843
|
+
await Promise.all(promises);
|
|
797
844
|
}
|
|
798
845
|
}
|
|
799
846
|
function createEntry(levelNum, levelName, msg, data, error) {
|
|
@@ -844,10 +891,10 @@ function createLogger(options = {}) {
|
|
|
844
891
|
emitter.emit("log", entry);
|
|
845
892
|
emitter.emit(`log:${levelName}`, entry);
|
|
846
893
|
if (shouldSync(levelName)) {
|
|
847
|
-
|
|
848
|
-
});
|
|
894
|
+
writeToTransportsSync(entry);
|
|
849
895
|
} else {
|
|
850
|
-
writeToTransports(entry).catch(() => {
|
|
896
|
+
writeToTransports(entry).catch((err) => {
|
|
897
|
+
emitTransportError("logger", err, entry);
|
|
851
898
|
});
|
|
852
899
|
}
|
|
853
900
|
}
|
|
@@ -968,9 +1015,10 @@ function createLogger(options = {}) {
|
|
|
968
1015
|
try {
|
|
969
1016
|
const result = transport.flush();
|
|
970
1017
|
if (result instanceof Promise) {
|
|
971
|
-
promises.push(result);
|
|
1018
|
+
promises.push(result.catch((err) => emitTransportError(transport.name, err)));
|
|
972
1019
|
}
|
|
973
|
-
} catch {
|
|
1020
|
+
} catch (err) {
|
|
1021
|
+
emitTransportError(transport.name, err);
|
|
974
1022
|
}
|
|
975
1023
|
}
|
|
976
1024
|
}
|
|
@@ -980,6 +1028,31 @@ function createLogger(options = {}) {
|
|
|
980
1028
|
async close() {
|
|
981
1029
|
if (closed) return;
|
|
982
1030
|
closed = true;
|
|
1031
|
+
const ctx = kernel.getContext();
|
|
1032
|
+
if (ctx.flushTimerId) {
|
|
1033
|
+
clearInterval(ctx.flushTimerId);
|
|
1034
|
+
ctx.flushTimerId = void 0;
|
|
1035
|
+
}
|
|
1036
|
+
if (ctx.buffer && ctx.buffer.length > 0) {
|
|
1037
|
+
const bufferedEntries = ctx.buffer;
|
|
1038
|
+
ctx.buffer = [];
|
|
1039
|
+
for (const entry of bufferedEntries) {
|
|
1040
|
+
for (const transport of activeTransports) {
|
|
1041
|
+
try {
|
|
1042
|
+
if (transport.writeSync) {
|
|
1043
|
+
transport.writeSync(entry);
|
|
1044
|
+
} else {
|
|
1045
|
+
const result = transport.write(entry);
|
|
1046
|
+
if (result instanceof Promise) {
|
|
1047
|
+
await result.catch((err) => emitTransportError(transport.name, err, entry));
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
} catch (err) {
|
|
1051
|
+
emitTransportError(transport.name, err, entry);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
983
1056
|
await logger.flush();
|
|
984
1057
|
const promises = [];
|
|
985
1058
|
for (const transport of activeTransports) {
|
|
@@ -987,9 +1060,10 @@ function createLogger(options = {}) {
|
|
|
987
1060
|
try {
|
|
988
1061
|
const result = transport.close();
|
|
989
1062
|
if (result instanceof Promise) {
|
|
990
|
-
promises.push(result);
|
|
1063
|
+
promises.push(result.catch((err) => emitTransportError(transport.name, err)));
|
|
991
1064
|
}
|
|
992
|
-
} catch {
|
|
1065
|
+
} catch (err) {
|
|
1066
|
+
emitTransportError(transport.name, err);
|
|
993
1067
|
}
|
|
994
1068
|
}
|
|
995
1069
|
}
|
|
@@ -1145,6 +1219,36 @@ function fileTransport(options) {
|
|
|
1145
1219
|
});
|
|
1146
1220
|
});
|
|
1147
1221
|
},
|
|
1222
|
+
writeSync(entry) {
|
|
1223
|
+
if (!fs) {
|
|
1224
|
+
try {
|
|
1225
|
+
const fsSync = require("fs");
|
|
1226
|
+
const pathSync = require("path");
|
|
1227
|
+
const dir = pathSync.dirname(filePath);
|
|
1228
|
+
if (!fsSync.existsSync(dir)) {
|
|
1229
|
+
fsSync.mkdirSync(dir, { recursive: true });
|
|
1230
|
+
}
|
|
1231
|
+
const line = formatJson(entry) + "\n";
|
|
1232
|
+
fsSync.appendFileSync(filePath, line, "utf8");
|
|
1233
|
+
currentSize += Buffer.byteLength(line, "utf8");
|
|
1234
|
+
} catch {
|
|
1235
|
+
process.stderr.write(formatJson(entry) + "\n");
|
|
1236
|
+
}
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
try {
|
|
1240
|
+
const line = formatJson(entry) + "\n";
|
|
1241
|
+
fs.appendFileSync(filePath, line, "utf8");
|
|
1242
|
+
currentSize += Buffer.byteLength(line, "utf8");
|
|
1243
|
+
} catch (err) {
|
|
1244
|
+
process.stderr.write(formatJson(entry) + "\n");
|
|
1245
|
+
throw new TransportError(
|
|
1246
|
+
`Failed to write sync to file: ${err instanceof Error ? err.message : String(err)}`,
|
|
1247
|
+
"file",
|
|
1248
|
+
err instanceof Error ? err : void 0
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
},
|
|
1148
1252
|
async flush() {
|
|
1149
1253
|
if (writeStream && "flush" in writeStream) {
|
|
1150
1254
|
return new Promise((resolve) => {
|
|
@@ -1794,7 +1898,9 @@ async function flushBuffer(ctx) {
|
|
|
1794
1898
|
await Promise.all(
|
|
1795
1899
|
entries.flatMap(
|
|
1796
1900
|
(entry) => ctx.transports.map(
|
|
1797
|
-
(transport) => Promise.resolve(transport.write(entry)).catch(() => {
|
|
1901
|
+
(transport) => Promise.resolve(transport.write(entry)).catch((err) => {
|
|
1902
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1903
|
+
ctx.emitter.emit("error", { transport: transport.name, error, entry });
|
|
1798
1904
|
})
|
|
1799
1905
|
)
|
|
1800
1906
|
)
|