@rawnodes/logger 2.11.0 → 2.12.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 +3 -0
- package/dist/index.d.mts +9 -5
- package/dist/index.d.ts +9 -5
- package/dist/index.js +133 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +134 -58
- package/dist/index.mjs.map +1 -1
- package/dist/transports/relay.d.mts +1 -1
- package/dist/transports/relay.d.ts +1 -1
- package/dist/{types-lfJLhC8J.d.mts → types-CD5hHXR4.d.mts} +1 -0
- package/dist/{types-lfJLhC8J.d.ts → types-CD5hHXR4.d.ts} +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -316,6 +316,8 @@ telegram: {
|
|
|
316
316
|
}
|
|
317
317
|
```
|
|
318
318
|
|
|
319
|
+
File open, write, and rotation errors disable only the affected file destination. Console and remote transports continue logging. By default, a single diagnostic with the error code is written to stderr. Set `file.onError` to handle the error yourself; the callback receives the original `Error`.
|
|
320
|
+
|
|
319
321
|
### CloudWatch
|
|
320
322
|
|
|
321
323
|
Send logs to AWS CloudWatch Logs:
|
|
@@ -871,6 +873,7 @@ interface LoggerConfig {
|
|
|
871
873
|
maxFiles?: string;
|
|
872
874
|
maxSize?: string;
|
|
873
875
|
zippedArchive?: boolean;
|
|
876
|
+
onError?: (error: Error) => void;
|
|
874
877
|
};
|
|
875
878
|
}
|
|
876
879
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { L as LoggerContext, a as LoggerConfig, b as LevelOverrideMatch, c as LogLevel, d as LevelOverride, M as Meta, D as DiscordConfig, T as TelegramConfig, C as CloudWatchConfig, e as MaskReplacer, Z as ZohoCliqConfig, f as CallerConfig, g as CallerInfo, h as LogFormat, i as LevelRule, j as ConsoleConfig, F as FileConfig, H as HttpTransportBaseConfig, k as LevelConfigObject, l as LevelConfig, R as RelayConfig, A as AutoShutdownConfig, m as LogStreamPattern, n as LogStreamPatternConfig, o as LogStreamTemplateConfig, p as LogStreamName } from './types-
|
|
2
|
-
export { q as LOG_LEVELS, x as MaskSecretsOptions, t as ZohoCliqBotConfig, s as assertLogLevel, v as createMasker, r as isValidLogLevel, w as maskReplacer, u as maskSecrets } from './types-
|
|
1
|
+
import { L as LoggerContext, a as LoggerConfig, b as LevelOverrideMatch, c as LogLevel, d as LevelOverride, M as Meta, D as DiscordConfig, T as TelegramConfig, C as CloudWatchConfig, e as MaskReplacer, Z as ZohoCliqConfig, f as CallerConfig, g as CallerInfo, h as LogFormat, i as LevelRule, j as ConsoleConfig, F as FileConfig, H as HttpTransportBaseConfig, k as LevelConfigObject, l as LevelConfig, R as RelayConfig, A as AutoShutdownConfig, m as LogStreamPattern, n as LogStreamPatternConfig, o as LogStreamTemplateConfig, p as LogStreamName } from './types-CD5hHXR4.mjs';
|
|
2
|
+
export { q as LOG_LEVELS, x as MaskSecretsOptions, t as ZohoCliqBotConfig, s as assertLogLevel, v as createMasker, r as isValidLogLevel, w as maskReplacer, u as maskSecrets } from './types-CD5hHXR4.mjs';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
declare class LoggerStore<TContext extends LoggerContext = LoggerContext> {
|
|
@@ -218,20 +218,24 @@ declare class TelegramTransport extends BaseHttpTransport {
|
|
|
218
218
|
declare class CloudWatchTransport extends BaseHttpTransport {
|
|
219
219
|
private config;
|
|
220
220
|
private client;
|
|
221
|
-
private sequenceToken;
|
|
222
221
|
private initialized;
|
|
223
222
|
private initPromise;
|
|
223
|
+
private initFailures;
|
|
224
|
+
private nextInitAttempt;
|
|
224
225
|
private resolvedLogStreamName;
|
|
225
226
|
private maskReplacer;
|
|
227
|
+
private static readonly INIT_BACKOFF_BASE_MS;
|
|
228
|
+
private static readonly INIT_BACKOFF_MAX_MS;
|
|
226
229
|
constructor(config: CloudWatchConfig, configHostname?: string, maskReplacerFn?: MaskReplacer);
|
|
227
230
|
protected sendBatch(messages: BufferedMessage[]): Promise<void>;
|
|
231
|
+
private putLogEvents;
|
|
228
232
|
private ensureInitialized;
|
|
229
233
|
private initialize;
|
|
230
234
|
private createLogGroupIfNotExists;
|
|
231
235
|
private createLogStreamIfNotExists;
|
|
232
|
-
private fetchSequenceToken;
|
|
233
236
|
private isResourceAlreadyExistsError;
|
|
234
|
-
private
|
|
237
|
+
private isResourceNotFoundError;
|
|
238
|
+
private isAwsErrorNamed;
|
|
235
239
|
}
|
|
236
240
|
|
|
237
241
|
declare class ZohoCliqTransport extends BaseHttpTransport {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { L as LoggerContext, a as LoggerConfig, b as LevelOverrideMatch, c as LogLevel, d as LevelOverride, M as Meta, D as DiscordConfig, T as TelegramConfig, C as CloudWatchConfig, e as MaskReplacer, Z as ZohoCliqConfig, f as CallerConfig, g as CallerInfo, h as LogFormat, i as LevelRule, j as ConsoleConfig, F as FileConfig, H as HttpTransportBaseConfig, k as LevelConfigObject, l as LevelConfig, R as RelayConfig, A as AutoShutdownConfig, m as LogStreamPattern, n as LogStreamPatternConfig, o as LogStreamTemplateConfig, p as LogStreamName } from './types-
|
|
2
|
-
export { q as LOG_LEVELS, x as MaskSecretsOptions, t as ZohoCliqBotConfig, s as assertLogLevel, v as createMasker, r as isValidLogLevel, w as maskReplacer, u as maskSecrets } from './types-
|
|
1
|
+
import { L as LoggerContext, a as LoggerConfig, b as LevelOverrideMatch, c as LogLevel, d as LevelOverride, M as Meta, D as DiscordConfig, T as TelegramConfig, C as CloudWatchConfig, e as MaskReplacer, Z as ZohoCliqConfig, f as CallerConfig, g as CallerInfo, h as LogFormat, i as LevelRule, j as ConsoleConfig, F as FileConfig, H as HttpTransportBaseConfig, k as LevelConfigObject, l as LevelConfig, R as RelayConfig, A as AutoShutdownConfig, m as LogStreamPattern, n as LogStreamPatternConfig, o as LogStreamTemplateConfig, p as LogStreamName } from './types-CD5hHXR4.js';
|
|
2
|
+
export { q as LOG_LEVELS, x as MaskSecretsOptions, t as ZohoCliqBotConfig, s as assertLogLevel, v as createMasker, r as isValidLogLevel, w as maskReplacer, u as maskSecrets } from './types-CD5hHXR4.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
declare class LoggerStore<TContext extends LoggerContext = LoggerContext> {
|
|
@@ -218,20 +218,24 @@ declare class TelegramTransport extends BaseHttpTransport {
|
|
|
218
218
|
declare class CloudWatchTransport extends BaseHttpTransport {
|
|
219
219
|
private config;
|
|
220
220
|
private client;
|
|
221
|
-
private sequenceToken;
|
|
222
221
|
private initialized;
|
|
223
222
|
private initPromise;
|
|
223
|
+
private initFailures;
|
|
224
|
+
private nextInitAttempt;
|
|
224
225
|
private resolvedLogStreamName;
|
|
225
226
|
private maskReplacer;
|
|
227
|
+
private static readonly INIT_BACKOFF_BASE_MS;
|
|
228
|
+
private static readonly INIT_BACKOFF_MAX_MS;
|
|
226
229
|
constructor(config: CloudWatchConfig, configHostname?: string, maskReplacerFn?: MaskReplacer);
|
|
227
230
|
protected sendBatch(messages: BufferedMessage[]): Promise<void>;
|
|
231
|
+
private putLogEvents;
|
|
228
232
|
private ensureInitialized;
|
|
229
233
|
private initialize;
|
|
230
234
|
private createLogGroupIfNotExists;
|
|
231
235
|
private createLogStreamIfNotExists;
|
|
232
|
-
private fetchSequenceToken;
|
|
233
236
|
private isResourceAlreadyExistsError;
|
|
234
|
-
private
|
|
237
|
+
private isResourceNotFoundError;
|
|
238
|
+
private isAwsErrorNamed;
|
|
235
239
|
}
|
|
236
240
|
|
|
237
241
|
declare class ZohoCliqTransport extends BaseHttpTransport {
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,6 @@ var os = require('os');
|
|
|
4
4
|
var pino = require('pino');
|
|
5
5
|
var async_hooks = require('async_hooks');
|
|
6
6
|
var stream = require('stream');
|
|
7
|
-
var promises = require('fs/promises');
|
|
8
7
|
var rotatingFileStream = require('rotating-file-stream');
|
|
9
8
|
var clientCloudwatchLogs = require('@aws-sdk/client-cloudwatch-logs');
|
|
10
9
|
var crypto = require('crypto');
|
|
@@ -45,6 +44,89 @@ function assertLogLevel(level) {
|
|
|
45
44
|
throw new Error(`Invalid log level: "${level}". Valid levels: ${Object.keys(LOG_LEVELS).join(", ")}`);
|
|
46
45
|
}
|
|
47
46
|
}
|
|
47
|
+
function reportFileFailure(error) {
|
|
48
|
+
const code = "code" in error && typeof error.code === "string" ? error.code : error.name;
|
|
49
|
+
process.stderr.write(
|
|
50
|
+
`[Logger] File logging stopped (${code}); other transports remain active.
|
|
51
|
+
`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
function createFileDestination(config) {
|
|
55
|
+
let failed = false;
|
|
56
|
+
let rotatingStream;
|
|
57
|
+
const fail = (error) => {
|
|
58
|
+
if (failed) return;
|
|
59
|
+
failed = true;
|
|
60
|
+
if (config.onError) {
|
|
61
|
+
try {
|
|
62
|
+
config.onError(error);
|
|
63
|
+
} catch {
|
|
64
|
+
reportFileFailure(error);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
reportFileFailure(error);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
try {
|
|
71
|
+
rotatingStream = rotatingFileStream.createStream(
|
|
72
|
+
(time, index) => {
|
|
73
|
+
const date = time instanceof Date ? time : /* @__PURE__ */ new Date();
|
|
74
|
+
const dateStr = date.toISOString().split("T")[0];
|
|
75
|
+
const base = config.filename.replace(".log", "");
|
|
76
|
+
return `${base}-${dateStr}${index ? `.${index}` : ""}.log`;
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
path: config.dirname,
|
|
80
|
+
size: (config.maxSize || "20M").replace(
|
|
81
|
+
/([kmg])$/i,
|
|
82
|
+
(unit) => unit.toUpperCase()
|
|
83
|
+
),
|
|
84
|
+
interval: config.interval || (config.datePattern === "YYYY-MM-DD-HH" ? "1h" : "1d"),
|
|
85
|
+
compress: config.zippedArchive ? "gzip" : false,
|
|
86
|
+
maxFiles: parseInt(config.maxFiles?.replace("d", "") || "14")
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
rotatingStream.on("error", fail);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
fail(error instanceof Error ? error : new Error(String(error)));
|
|
92
|
+
}
|
|
93
|
+
return new stream.Writable({
|
|
94
|
+
write(chunk, encoding, callback) {
|
|
95
|
+
if (failed || !rotatingStream) {
|
|
96
|
+
callback();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
rotatingStream.write(chunk, encoding, (error) => {
|
|
101
|
+
if (error) fail(error);
|
|
102
|
+
callback();
|
|
103
|
+
});
|
|
104
|
+
} catch (error) {
|
|
105
|
+
fail(error instanceof Error ? error : new Error(String(error)));
|
|
106
|
+
callback();
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
final(callback) {
|
|
110
|
+
if (!rotatingStream || rotatingStream.destroyed) {
|
|
111
|
+
callback();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
rotatingStream.end((error) => {
|
|
116
|
+
if (error) fail(error);
|
|
117
|
+
callback();
|
|
118
|
+
});
|
|
119
|
+
} catch (error) {
|
|
120
|
+
fail(error instanceof Error ? error : new Error(String(error)));
|
|
121
|
+
callback();
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
destroy(_error, callback) {
|
|
125
|
+
rotatingStream?.destroy();
|
|
126
|
+
callback();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
48
130
|
|
|
49
131
|
// src/transports/buffer.ts
|
|
50
132
|
var MessageBuffer = class {
|
|
@@ -651,14 +733,21 @@ function resolveLogStreamName(config, configHostname) {
|
|
|
651
733
|
}
|
|
652
734
|
return hostname;
|
|
653
735
|
}
|
|
654
|
-
var CloudWatchTransport = class extends BaseHttpTransport {
|
|
736
|
+
var CloudWatchTransport = class _CloudWatchTransport extends BaseHttpTransport {
|
|
655
737
|
config;
|
|
656
738
|
client;
|
|
657
|
-
sequenceToken;
|
|
658
739
|
initialized = false;
|
|
659
740
|
initPromise = null;
|
|
741
|
+
initFailures = 0;
|
|
742
|
+
nextInitAttempt = 0;
|
|
660
743
|
resolvedLogStreamName;
|
|
661
744
|
maskReplacer;
|
|
745
|
+
// Backoff bounds for repeated init failures (e.g. IAM not yet propagated,
|
|
746
|
+
// throttling). Without this, a sustained outage makes every flush re-run
|
|
747
|
+
// initialize() back-to-back and hammer the CloudWatch control-plane APIs,
|
|
748
|
+
// which only deepens the throttling.
|
|
749
|
+
static INIT_BACKOFF_BASE_MS = 1e3;
|
|
750
|
+
static INIT_BACKOFF_MAX_MS = 3e4;
|
|
662
751
|
constructor(config, configHostname, maskReplacerFn) {
|
|
663
752
|
super({
|
|
664
753
|
batchSize: config.batchSize ?? 100,
|
|
@@ -694,36 +783,46 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
694
783
|
}, replacer)
|
|
695
784
|
}));
|
|
696
785
|
logEvents.sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
|
|
697
|
-
const command = new clientCloudwatchLogs.PutLogEventsCommand({
|
|
698
|
-
logGroupName: this.config.logGroupName,
|
|
699
|
-
logStreamName: this.resolvedLogStreamName,
|
|
700
|
-
logEvents,
|
|
701
|
-
sequenceToken: this.sequenceToken
|
|
702
|
-
});
|
|
703
786
|
try {
|
|
704
|
-
|
|
705
|
-
this.sequenceToken = response.nextSequenceToken;
|
|
787
|
+
await this.putLogEvents(logEvents);
|
|
706
788
|
} catch (error) {
|
|
707
|
-
if (this.
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
logEvents,
|
|
713
|
-
sequenceToken: this.sequenceToken
|
|
714
|
-
});
|
|
715
|
-
const response = await this.client.send(retryCommand);
|
|
716
|
-
this.sequenceToken = response.nextSequenceToken;
|
|
789
|
+
if (this.isResourceNotFoundError(error)) {
|
|
790
|
+
this.initialized = false;
|
|
791
|
+
this.initPromise = null;
|
|
792
|
+
await this.ensureInitialized();
|
|
793
|
+
await this.putLogEvents(logEvents);
|
|
717
794
|
} else {
|
|
718
795
|
throw error;
|
|
719
796
|
}
|
|
720
797
|
}
|
|
721
798
|
}
|
|
799
|
+
// PutLogEvents no longer requires a sequence token: since 2023 CloudWatch
|
|
800
|
+
// Logs ignores the `sequenceToken` field for sequential writes, so we omit it
|
|
801
|
+
// entirely. This also removes the need for DescribeLogStreams (and the
|
|
802
|
+
// logs:DescribeLogStreams IAM permission) on the hot path.
|
|
803
|
+
async putLogEvents(logEvents) {
|
|
804
|
+
await this.client.send(
|
|
805
|
+
new clientCloudwatchLogs.PutLogEventsCommand({
|
|
806
|
+
logGroupName: this.config.logGroupName,
|
|
807
|
+
logStreamName: this.resolvedLogStreamName,
|
|
808
|
+
logEvents
|
|
809
|
+
})
|
|
810
|
+
);
|
|
811
|
+
}
|
|
722
812
|
async ensureInitialized() {
|
|
723
813
|
if (this.initialized) return;
|
|
814
|
+
if (!this.initPromise && Date.now() < this.nextInitAttempt) {
|
|
815
|
+
throw new Error("CloudWatch transport initialization is backing off");
|
|
816
|
+
}
|
|
724
817
|
if (!this.initPromise) {
|
|
725
818
|
this.initPromise = this.initialize().catch((err) => {
|
|
726
819
|
this.initPromise = null;
|
|
820
|
+
this.initFailures += 1;
|
|
821
|
+
const delay = Math.min(
|
|
822
|
+
_CloudWatchTransport.INIT_BACKOFF_MAX_MS,
|
|
823
|
+
_CloudWatchTransport.INIT_BACKOFF_BASE_MS * 2 ** (this.initFailures - 1)
|
|
824
|
+
);
|
|
825
|
+
this.nextInitAttempt = Date.now() + delay;
|
|
727
826
|
throw err;
|
|
728
827
|
});
|
|
729
828
|
}
|
|
@@ -736,8 +835,9 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
736
835
|
if (this.config.createLogStream !== false) {
|
|
737
836
|
await this.createLogStreamIfNotExists();
|
|
738
837
|
}
|
|
739
|
-
await this.fetchSequenceToken();
|
|
740
838
|
this.initialized = true;
|
|
839
|
+
this.initFailures = 0;
|
|
840
|
+
this.nextInitAttempt = 0;
|
|
741
841
|
}
|
|
742
842
|
async createLogGroupIfNotExists() {
|
|
743
843
|
try {
|
|
@@ -766,22 +866,16 @@ var CloudWatchTransport = class extends BaseHttpTransport {
|
|
|
766
866
|
}
|
|
767
867
|
}
|
|
768
868
|
}
|
|
769
|
-
async fetchSequenceToken() {
|
|
770
|
-
const response = await this.client.send(
|
|
771
|
-
new clientCloudwatchLogs.DescribeLogStreamsCommand({
|
|
772
|
-
logGroupName: this.config.logGroupName,
|
|
773
|
-
logStreamNamePrefix: this.resolvedLogStreamName,
|
|
774
|
-
limit: 1
|
|
775
|
-
})
|
|
776
|
-
);
|
|
777
|
-
const stream = response.logStreams?.find((s) => s.logStreamName === this.resolvedLogStreamName);
|
|
778
|
-
this.sequenceToken = stream?.uploadSequenceToken;
|
|
779
|
-
}
|
|
780
869
|
isResourceAlreadyExistsError(error) {
|
|
781
|
-
return
|
|
870
|
+
return this.isAwsErrorNamed(error, "ResourceAlreadyExistsException");
|
|
871
|
+
}
|
|
872
|
+
isResourceNotFoundError(error) {
|
|
873
|
+
return this.isAwsErrorNamed(error, "ResourceNotFoundException");
|
|
782
874
|
}
|
|
783
|
-
|
|
784
|
-
|
|
875
|
+
isAwsErrorNamed(error, name) {
|
|
876
|
+
if (typeof error !== "object" || error === null) return false;
|
|
877
|
+
if ("name" in error && error.name === name) return true;
|
|
878
|
+
return "__type" in error && typeof error.__type === "string" && error.__type.split("#").at(-1) === name;
|
|
785
879
|
}
|
|
786
880
|
};
|
|
787
881
|
|
|
@@ -943,26 +1037,7 @@ function createStreams(config, state) {
|
|
|
943
1037
|
});
|
|
944
1038
|
if (config.file) {
|
|
945
1039
|
const fileConfig = config.file;
|
|
946
|
-
|
|
947
|
-
});
|
|
948
|
-
const normalizeSize = (size) => {
|
|
949
|
-
return size.replace(/([kmg])$/i, (match) => match.toUpperCase());
|
|
950
|
-
};
|
|
951
|
-
const rotatingStream = rotatingFileStream.createStream(
|
|
952
|
-
(time, index) => {
|
|
953
|
-
const date = time instanceof Date ? time : /* @__PURE__ */ new Date();
|
|
954
|
-
const dateStr = date.toISOString().split("T")[0];
|
|
955
|
-
const base = fileConfig.filename.replace(".log", "");
|
|
956
|
-
return `${base}-${dateStr}${index ? `.${index}` : ""}.log`;
|
|
957
|
-
},
|
|
958
|
-
{
|
|
959
|
-
path: fileConfig.dirname,
|
|
960
|
-
size: normalizeSize(fileConfig.maxSize || "20M"),
|
|
961
|
-
interval: fileConfig.interval || (fileConfig.datePattern === "YYYY-MM-DD-HH" ? "1h" : "1d"),
|
|
962
|
-
compress: fileConfig.zippedArchive ? "gzip" : false,
|
|
963
|
-
maxFiles: parseInt(fileConfig.maxFiles?.replace("d", "") || "14")
|
|
964
|
-
}
|
|
965
|
-
);
|
|
1040
|
+
const rotatingStream = createFileDestination(fileConfig);
|
|
966
1041
|
const fileStream = createFormattedFilterStream(
|
|
967
1042
|
fileConfig.format,
|
|
968
1043
|
fileConfig.level,
|
|
@@ -1354,7 +1429,8 @@ var FileConfigSchema = zod.z.object({
|
|
|
1354
1429
|
interval: zod.z.string().regex(/^\d+[Mdhms]$/).optional(),
|
|
1355
1430
|
zippedArchive: zod.z.boolean().optional(),
|
|
1356
1431
|
maxSize: zod.z.string().optional(),
|
|
1357
|
-
maxFiles: zod.z.string().optional()
|
|
1432
|
+
maxFiles: zod.z.string().optional(),
|
|
1433
|
+
onError: zod.z.function().optional()
|
|
1358
1434
|
});
|
|
1359
1435
|
var HttpTransportBaseConfigSchema = zod.z.object({
|
|
1360
1436
|
level: LogLevelSchema.optional(),
|