@richardmcquiston01/error-log-adapter 0.6.0 → 0.8.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/README.md +49 -19
- package/dist/index.cjs +331 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +154 -1
- package/dist/index.d.ts +154 -1
- package/dist/index.mjs +329 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1022,4 +1022,157 @@ declare class EventEmitterErrorAdapter<TCode extends string = string> implements
|
|
|
1022
1022
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
1023
1023
|
}
|
|
1024
1024
|
|
|
1025
|
-
|
|
1025
|
+
/**
|
|
1026
|
+
* The only things this adapter needs from a socket: `send` and `readyState`. The
|
|
1027
|
+
* browser `WebSocket`, Node's global `WebSocket`, Deno, Bun, and the `ws` package
|
|
1028
|
+
* all fit as they are.
|
|
1029
|
+
*/
|
|
1030
|
+
interface WebSocketLike {
|
|
1031
|
+
send(data: string): unknown;
|
|
1032
|
+
readonly readyState: number;
|
|
1033
|
+
}
|
|
1034
|
+
interface WebSocketErrorOptions {
|
|
1035
|
+
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
1036
|
+
prefix?: string;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Sends each entry as one JSON text message (the serialized entry plus its UUID
|
|
1040
|
+
* `id`) over a socket you own. You connect, and reconnect, the socket; this
|
|
1041
|
+
* adapter never opens one and keeps no buffer. If the socket is not open when
|
|
1042
|
+
* `log` is called, the entry is not sent and `log` returns `ok: false`, so
|
|
1043
|
+
* combine it with a durable adapter in a `CompositeErrorAdapter` when entries
|
|
1044
|
+
* must not be lost while the socket is down.
|
|
1045
|
+
*
|
|
1046
|
+
* `ok: true` means the socket accepted the message, not that the server
|
|
1047
|
+
* received it: a WebSocket gives no acknowledgement, and a connection that
|
|
1048
|
+
* drops after `send` loses the message.
|
|
1049
|
+
*
|
|
1050
|
+
* ```ts
|
|
1051
|
+
* const socket = new WebSocket('wss://logs.example.com/errors');
|
|
1052
|
+
* const adapter = new WebSocketErrorAdapter(socket);
|
|
1053
|
+
* ```
|
|
1054
|
+
*/
|
|
1055
|
+
declare class WebSocketErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
1056
|
+
private readonly socket;
|
|
1057
|
+
private readonly prefix;
|
|
1058
|
+
constructor(socket: WebSocketLike, options?: WebSocketErrorOptions);
|
|
1059
|
+
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/** One event as `PutLogEvents` takes it. */
|
|
1063
|
+
interface CloudWatchLogEvent {
|
|
1064
|
+
/** Milliseconds since the epoch. */
|
|
1065
|
+
timestamp: number;
|
|
1066
|
+
message: string;
|
|
1067
|
+
}
|
|
1068
|
+
/** The parameters this adapter passes to `putLogEvents`. */
|
|
1069
|
+
interface CloudWatchPutLogEventsInput {
|
|
1070
|
+
logGroupName: string;
|
|
1071
|
+
logStreamName: string;
|
|
1072
|
+
logEvents: CloudWatchLogEvent[];
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* What an AWS SDK client needs to offer. The AWS SDK v3 aggregated client
|
|
1076
|
+
* (`new CloudWatchLogs({ ... })` from `@aws-sdk/client-cloudwatch-logs`) fits as
|
|
1077
|
+
* it is. `createLogGroup` and `createLogStream` are only used when
|
|
1078
|
+
* `createMissing` is on (the default); leave them out of a hand-written client
|
|
1079
|
+
* that cannot create them.
|
|
1080
|
+
*/
|
|
1081
|
+
interface CloudWatchLogsClientLike {
|
|
1082
|
+
putLogEvents(input: CloudWatchPutLogEventsInput): Promise<unknown>;
|
|
1083
|
+
createLogGroup?(input: {
|
|
1084
|
+
logGroupName: string;
|
|
1085
|
+
}): Promise<unknown>;
|
|
1086
|
+
createLogStream?(input: {
|
|
1087
|
+
logGroupName: string;
|
|
1088
|
+
logStreamName: string;
|
|
1089
|
+
}): Promise<unknown>;
|
|
1090
|
+
}
|
|
1091
|
+
interface CloudWatchBatchOptions {
|
|
1092
|
+
/** Send once this many events are buffered. */
|
|
1093
|
+
batchSize: number;
|
|
1094
|
+
/**
|
|
1095
|
+
* Also send when the oldest buffered event has waited this long (ms). The
|
|
1096
|
+
* timer does not keep the process alive, so call `flush()` before exiting.
|
|
1097
|
+
*/
|
|
1098
|
+
maxWaitMs?: number;
|
|
1099
|
+
}
|
|
1100
|
+
interface CloudWatchLogsErrorOptions {
|
|
1101
|
+
/**
|
|
1102
|
+
* Create the log stream, and the log group if that is missing too, when
|
|
1103
|
+
* `PutLogEvents` says they do not exist, then try again once. This needs the
|
|
1104
|
+
* `logs:CreateLogStream` (and `logs:CreateLogGroup`) permissions and only runs
|
|
1105
|
+
* after a "not found" answer, so a stream that exists costs nothing. A group
|
|
1106
|
+
* created this way never expires its logs; set a retention policy on it.
|
|
1107
|
+
* Defaults to `true`.
|
|
1108
|
+
*/
|
|
1109
|
+
createMissing?: boolean;
|
|
1110
|
+
/**
|
|
1111
|
+
* Buffer events and send them together, up to the 10,000 events and 1 MiB
|
|
1112
|
+
* that one request allows. Off by default: one request per entry.
|
|
1113
|
+
*/
|
|
1114
|
+
batch?: CloudWatchBatchOptions;
|
|
1115
|
+
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
1116
|
+
prefix?: string;
|
|
1117
|
+
/**
|
|
1118
|
+
* How long a request may take before `log` (or `flush`) gives up waiting, in
|
|
1119
|
+
* milliseconds. Defaults to 10000. Pass `Infinity` to wait without limit. The
|
|
1120
|
+
* request cannot be cancelled, so it may still finish afterwards; in batch
|
|
1121
|
+
* mode the events stay buffered and are sent again by the next flush.
|
|
1122
|
+
*/
|
|
1123
|
+
timeoutMs?: number;
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Sends entries to one CloudWatch Logs stream through `PutLogEvents`, each as a
|
|
1127
|
+
* JSON event (the serialized entry plus its UUID `id`) stamped with the entry's
|
|
1128
|
+
* time, so Logs Insights can query its fields. AWS no longer asks for sequence
|
|
1129
|
+
* tokens, so none are tracked.
|
|
1130
|
+
*
|
|
1131
|
+
* By default each entry is its own request and `log` resolves once CloudWatch
|
|
1132
|
+
* accepts it. With `batch`, events are buffered and sent together; `log` then
|
|
1133
|
+
* resolves ok as soon as the event is buffered (it is not stored yet), except
|
|
1134
|
+
* for the call that fills the batch, which resolves with the send result. Call
|
|
1135
|
+
* `flush()` before shutting down.
|
|
1136
|
+
*
|
|
1137
|
+
* CloudWatch can answer ok and still reject events it considers too old (over
|
|
1138
|
+
* 14 days) or too new (over 2 hours ahead); that is reported as `ok: false`. An
|
|
1139
|
+
* event over 256 KiB is refused before sending, since CloudWatch would reject
|
|
1140
|
+
* the whole request.
|
|
1141
|
+
*
|
|
1142
|
+
* In AWS Lambda, ECS and similar runtimes, anything written to stdout already
|
|
1143
|
+
* reaches CloudWatch; this adapter is for apps running elsewhere.
|
|
1144
|
+
*/
|
|
1145
|
+
declare class CloudWatchLogsErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
1146
|
+
private readonly client;
|
|
1147
|
+
private readonly logGroupName;
|
|
1148
|
+
private readonly logStreamName;
|
|
1149
|
+
private readonly createMissing;
|
|
1150
|
+
private readonly batch;
|
|
1151
|
+
private readonly logPrefix;
|
|
1152
|
+
private readonly timeoutMs;
|
|
1153
|
+
private buffer;
|
|
1154
|
+
private timer;
|
|
1155
|
+
private readonly flushQueue;
|
|
1156
|
+
/** @throws {RangeError} if a name is empty, the batch or timeout options are invalid, or `createMissing` is on but the client cannot create streams. */
|
|
1157
|
+
constructor(client: CloudWatchLogsClientLike, logGroupName: string, logStreamName: string, options?: CloudWatchLogsErrorOptions);
|
|
1158
|
+
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
1159
|
+
/**
|
|
1160
|
+
* Sends any buffered events now. Resolves ok when nothing is buffered. Events
|
|
1161
|
+
* from a failed send stay buffered and are retried by the next flush.
|
|
1162
|
+
*/
|
|
1163
|
+
flush(): Promise<ErrorLogResult>;
|
|
1164
|
+
private addToBatch;
|
|
1165
|
+
private scheduleFlush;
|
|
1166
|
+
private sendBuffered;
|
|
1167
|
+
/**
|
|
1168
|
+
* Sends one request, creating the stream first if CloudWatch says it is
|
|
1169
|
+
* missing. Resolves to a description of any events CloudWatch refused, or
|
|
1170
|
+
* undefined when it took them all.
|
|
1171
|
+
*/
|
|
1172
|
+
private send;
|
|
1173
|
+
private putLogEvents;
|
|
1174
|
+
private createStream;
|
|
1175
|
+
private fail;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
export { type CloudWatchBatchOptions, type CloudWatchLogEvent, type CloudWatchLogsClientLike, CloudWatchLogsErrorAdapter, type CloudWatchLogsErrorOptions, type CloudWatchPutLogEventsInput, type CollectionLike, CompositeErrorAdapter, type CompositeErrorOptions, ConsoleErrorAdapter, type ConsoleErrorOptions, type EmitterLike, type ErrorLogAdapter, type ErrorLogEntry, type ErrorLogResult, EventEmitterErrorAdapter, type EventEmitterErrorOptions, type EventEmitterErrorRecord, type FileSystemLike, type FirebaseErrorOptions, type FirebaseErrorRecord, type FirestoreCollectionLike, FirestoreErrorAdapter, FlatFileErrorAdapter, type FlatFileErrorOptions, type IdbDatabaseLike, type IdbObjectStoreLike, type IdbRequestLike, type IdbTransactionLike, type IdbUpgradeDatabaseLike, IndexedDbErrorAdapter, type IndexedDbErrorOptions, type IndexedDbErrorRecord, JsonFileErrorAdapter, type JsonFileErrorOptions, type JsonFileErrorRecord, type LockManagerLike, MongoDbErrorAdapter, type MongoDbErrorOptions, MySqlErrorAdapter, PostgresErrorAdapter, type ReadWriteFileSystemLike, RealtimeDatabaseErrorAdapter, type RealtimeDatabaseRefLike, type RedisClientLike, RedisErrorAdapter, type RedisErrorOptions, type S3BatchOptions, type S3ClientLike, S3ErrorAdapter, type S3ErrorOptions, type S3PutObjectInput, type SerializedErrorLogEntry, type SqlClientLike, type SqlDialect, type SqlErrorOptions, type SqliteClientLike, SqliteErrorAdapter, type SqliteErrorOptions, type StorageLike, type SupabaseClientLike, SupabaseErrorAdapter, type SupabaseErrorLike, type SupabaseErrorOptions, type SupabaseErrorRow, type SupabaseInsertResult, type SupabaseSetupOptions, WebSocketErrorAdapter, type WebSocketErrorOptions, type WebSocketLike, WebStorageErrorAdapter, type WebStorageErrorOptions, type WebStorageErrorRecord, WebhookErrorAdapter, type WebhookErrorOptions, buildCreateErrorLogTableSql, buildErrorLogTableDdl, buildMySqlWidenColumnsSql, buildSupabaseSetupSql, createErrorLogStore, formatErrorLogLine, serializeCause, serializeEntry };
|
package/dist/index.d.ts
CHANGED
|
@@ -1022,4 +1022,157 @@ declare class EventEmitterErrorAdapter<TCode extends string = string> implements
|
|
|
1022
1022
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
1023
1023
|
}
|
|
1024
1024
|
|
|
1025
|
-
|
|
1025
|
+
/**
|
|
1026
|
+
* The only things this adapter needs from a socket: `send` and `readyState`. The
|
|
1027
|
+
* browser `WebSocket`, Node's global `WebSocket`, Deno, Bun, and the `ws` package
|
|
1028
|
+
* all fit as they are.
|
|
1029
|
+
*/
|
|
1030
|
+
interface WebSocketLike {
|
|
1031
|
+
send(data: string): unknown;
|
|
1032
|
+
readonly readyState: number;
|
|
1033
|
+
}
|
|
1034
|
+
interface WebSocketErrorOptions {
|
|
1035
|
+
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
1036
|
+
prefix?: string;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Sends each entry as one JSON text message (the serialized entry plus its UUID
|
|
1040
|
+
* `id`) over a socket you own. You connect, and reconnect, the socket; this
|
|
1041
|
+
* adapter never opens one and keeps no buffer. If the socket is not open when
|
|
1042
|
+
* `log` is called, the entry is not sent and `log` returns `ok: false`, so
|
|
1043
|
+
* combine it with a durable adapter in a `CompositeErrorAdapter` when entries
|
|
1044
|
+
* must not be lost while the socket is down.
|
|
1045
|
+
*
|
|
1046
|
+
* `ok: true` means the socket accepted the message, not that the server
|
|
1047
|
+
* received it: a WebSocket gives no acknowledgement, and a connection that
|
|
1048
|
+
* drops after `send` loses the message.
|
|
1049
|
+
*
|
|
1050
|
+
* ```ts
|
|
1051
|
+
* const socket = new WebSocket('wss://logs.example.com/errors');
|
|
1052
|
+
* const adapter = new WebSocketErrorAdapter(socket);
|
|
1053
|
+
* ```
|
|
1054
|
+
*/
|
|
1055
|
+
declare class WebSocketErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
1056
|
+
private readonly socket;
|
|
1057
|
+
private readonly prefix;
|
|
1058
|
+
constructor(socket: WebSocketLike, options?: WebSocketErrorOptions);
|
|
1059
|
+
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/** One event as `PutLogEvents` takes it. */
|
|
1063
|
+
interface CloudWatchLogEvent {
|
|
1064
|
+
/** Milliseconds since the epoch. */
|
|
1065
|
+
timestamp: number;
|
|
1066
|
+
message: string;
|
|
1067
|
+
}
|
|
1068
|
+
/** The parameters this adapter passes to `putLogEvents`. */
|
|
1069
|
+
interface CloudWatchPutLogEventsInput {
|
|
1070
|
+
logGroupName: string;
|
|
1071
|
+
logStreamName: string;
|
|
1072
|
+
logEvents: CloudWatchLogEvent[];
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* What an AWS SDK client needs to offer. The AWS SDK v3 aggregated client
|
|
1076
|
+
* (`new CloudWatchLogs({ ... })` from `@aws-sdk/client-cloudwatch-logs`) fits as
|
|
1077
|
+
* it is. `createLogGroup` and `createLogStream` are only used when
|
|
1078
|
+
* `createMissing` is on (the default); leave them out of a hand-written client
|
|
1079
|
+
* that cannot create them.
|
|
1080
|
+
*/
|
|
1081
|
+
interface CloudWatchLogsClientLike {
|
|
1082
|
+
putLogEvents(input: CloudWatchPutLogEventsInput): Promise<unknown>;
|
|
1083
|
+
createLogGroup?(input: {
|
|
1084
|
+
logGroupName: string;
|
|
1085
|
+
}): Promise<unknown>;
|
|
1086
|
+
createLogStream?(input: {
|
|
1087
|
+
logGroupName: string;
|
|
1088
|
+
logStreamName: string;
|
|
1089
|
+
}): Promise<unknown>;
|
|
1090
|
+
}
|
|
1091
|
+
interface CloudWatchBatchOptions {
|
|
1092
|
+
/** Send once this many events are buffered. */
|
|
1093
|
+
batchSize: number;
|
|
1094
|
+
/**
|
|
1095
|
+
* Also send when the oldest buffered event has waited this long (ms). The
|
|
1096
|
+
* timer does not keep the process alive, so call `flush()` before exiting.
|
|
1097
|
+
*/
|
|
1098
|
+
maxWaitMs?: number;
|
|
1099
|
+
}
|
|
1100
|
+
interface CloudWatchLogsErrorOptions {
|
|
1101
|
+
/**
|
|
1102
|
+
* Create the log stream, and the log group if that is missing too, when
|
|
1103
|
+
* `PutLogEvents` says they do not exist, then try again once. This needs the
|
|
1104
|
+
* `logs:CreateLogStream` (and `logs:CreateLogGroup`) permissions and only runs
|
|
1105
|
+
* after a "not found" answer, so a stream that exists costs nothing. A group
|
|
1106
|
+
* created this way never expires its logs; set a retention policy on it.
|
|
1107
|
+
* Defaults to `true`.
|
|
1108
|
+
*/
|
|
1109
|
+
createMissing?: boolean;
|
|
1110
|
+
/**
|
|
1111
|
+
* Buffer events and send them together, up to the 10,000 events and 1 MiB
|
|
1112
|
+
* that one request allows. Off by default: one request per entry.
|
|
1113
|
+
*/
|
|
1114
|
+
batch?: CloudWatchBatchOptions;
|
|
1115
|
+
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
1116
|
+
prefix?: string;
|
|
1117
|
+
/**
|
|
1118
|
+
* How long a request may take before `log` (or `flush`) gives up waiting, in
|
|
1119
|
+
* milliseconds. Defaults to 10000. Pass `Infinity` to wait without limit. The
|
|
1120
|
+
* request cannot be cancelled, so it may still finish afterwards; in batch
|
|
1121
|
+
* mode the events stay buffered and are sent again by the next flush.
|
|
1122
|
+
*/
|
|
1123
|
+
timeoutMs?: number;
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Sends entries to one CloudWatch Logs stream through `PutLogEvents`, each as a
|
|
1127
|
+
* JSON event (the serialized entry plus its UUID `id`) stamped with the entry's
|
|
1128
|
+
* time, so Logs Insights can query its fields. AWS no longer asks for sequence
|
|
1129
|
+
* tokens, so none are tracked.
|
|
1130
|
+
*
|
|
1131
|
+
* By default each entry is its own request and `log` resolves once CloudWatch
|
|
1132
|
+
* accepts it. With `batch`, events are buffered and sent together; `log` then
|
|
1133
|
+
* resolves ok as soon as the event is buffered (it is not stored yet), except
|
|
1134
|
+
* for the call that fills the batch, which resolves with the send result. Call
|
|
1135
|
+
* `flush()` before shutting down.
|
|
1136
|
+
*
|
|
1137
|
+
* CloudWatch can answer ok and still reject events it considers too old (over
|
|
1138
|
+
* 14 days) or too new (over 2 hours ahead); that is reported as `ok: false`. An
|
|
1139
|
+
* event over 256 KiB is refused before sending, since CloudWatch would reject
|
|
1140
|
+
* the whole request.
|
|
1141
|
+
*
|
|
1142
|
+
* In AWS Lambda, ECS and similar runtimes, anything written to stdout already
|
|
1143
|
+
* reaches CloudWatch; this adapter is for apps running elsewhere.
|
|
1144
|
+
*/
|
|
1145
|
+
declare class CloudWatchLogsErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
1146
|
+
private readonly client;
|
|
1147
|
+
private readonly logGroupName;
|
|
1148
|
+
private readonly logStreamName;
|
|
1149
|
+
private readonly createMissing;
|
|
1150
|
+
private readonly batch;
|
|
1151
|
+
private readonly logPrefix;
|
|
1152
|
+
private readonly timeoutMs;
|
|
1153
|
+
private buffer;
|
|
1154
|
+
private timer;
|
|
1155
|
+
private readonly flushQueue;
|
|
1156
|
+
/** @throws {RangeError} if a name is empty, the batch or timeout options are invalid, or `createMissing` is on but the client cannot create streams. */
|
|
1157
|
+
constructor(client: CloudWatchLogsClientLike, logGroupName: string, logStreamName: string, options?: CloudWatchLogsErrorOptions);
|
|
1158
|
+
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
1159
|
+
/**
|
|
1160
|
+
* Sends any buffered events now. Resolves ok when nothing is buffered. Events
|
|
1161
|
+
* from a failed send stay buffered and are retried by the next flush.
|
|
1162
|
+
*/
|
|
1163
|
+
flush(): Promise<ErrorLogResult>;
|
|
1164
|
+
private addToBatch;
|
|
1165
|
+
private scheduleFlush;
|
|
1166
|
+
private sendBuffered;
|
|
1167
|
+
/**
|
|
1168
|
+
* Sends one request, creating the stream first if CloudWatch says it is
|
|
1169
|
+
* missing. Resolves to a description of any events CloudWatch refused, or
|
|
1170
|
+
* undefined when it took them all.
|
|
1171
|
+
*/
|
|
1172
|
+
private send;
|
|
1173
|
+
private putLogEvents;
|
|
1174
|
+
private createStream;
|
|
1175
|
+
private fail;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
export { type CloudWatchBatchOptions, type CloudWatchLogEvent, type CloudWatchLogsClientLike, CloudWatchLogsErrorAdapter, type CloudWatchLogsErrorOptions, type CloudWatchPutLogEventsInput, type CollectionLike, CompositeErrorAdapter, type CompositeErrorOptions, ConsoleErrorAdapter, type ConsoleErrorOptions, type EmitterLike, type ErrorLogAdapter, type ErrorLogEntry, type ErrorLogResult, EventEmitterErrorAdapter, type EventEmitterErrorOptions, type EventEmitterErrorRecord, type FileSystemLike, type FirebaseErrorOptions, type FirebaseErrorRecord, type FirestoreCollectionLike, FirestoreErrorAdapter, FlatFileErrorAdapter, type FlatFileErrorOptions, type IdbDatabaseLike, type IdbObjectStoreLike, type IdbRequestLike, type IdbTransactionLike, type IdbUpgradeDatabaseLike, IndexedDbErrorAdapter, type IndexedDbErrorOptions, type IndexedDbErrorRecord, JsonFileErrorAdapter, type JsonFileErrorOptions, type JsonFileErrorRecord, type LockManagerLike, MongoDbErrorAdapter, type MongoDbErrorOptions, MySqlErrorAdapter, PostgresErrorAdapter, type ReadWriteFileSystemLike, RealtimeDatabaseErrorAdapter, type RealtimeDatabaseRefLike, type RedisClientLike, RedisErrorAdapter, type RedisErrorOptions, type S3BatchOptions, type S3ClientLike, S3ErrorAdapter, type S3ErrorOptions, type S3PutObjectInput, type SerializedErrorLogEntry, type SqlClientLike, type SqlDialect, type SqlErrorOptions, type SqliteClientLike, SqliteErrorAdapter, type SqliteErrorOptions, type StorageLike, type SupabaseClientLike, SupabaseErrorAdapter, type SupabaseErrorLike, type SupabaseErrorOptions, type SupabaseErrorRow, type SupabaseInsertResult, type SupabaseSetupOptions, WebSocketErrorAdapter, type WebSocketErrorOptions, type WebSocketLike, WebStorageErrorAdapter, type WebStorageErrorOptions, type WebStorageErrorRecord, WebhookErrorAdapter, type WebhookErrorOptions, buildCreateErrorLogTableSql, buildErrorLogTableDdl, buildMySqlWidenColumnsSql, buildSupabaseSetupSql, createErrorLogStore, formatErrorLogLine, serializeCause, serializeEntry };
|
package/dist/index.mjs
CHANGED
|
@@ -241,10 +241,10 @@ async function hmacSha256Hex(secret, message) {
|
|
|
241
241
|
"signingSecret needs Web Crypto (crypto.subtle), which this runtime does not provide, so the entry was not sent unsigned"
|
|
242
242
|
);
|
|
243
243
|
}
|
|
244
|
-
const
|
|
244
|
+
const encoder2 = new TextEncoder();
|
|
245
245
|
const key = await subtle.importKey(
|
|
246
246
|
"raw",
|
|
247
|
-
|
|
247
|
+
encoder2.encode(secret),
|
|
248
248
|
{ name: "HMAC", hash: "SHA-256" },
|
|
249
249
|
false,
|
|
250
250
|
["sign"]
|
|
@@ -252,7 +252,7 @@ async function hmacSha256Hex(secret, message) {
|
|
|
252
252
|
const signature = await subtle.sign(
|
|
253
253
|
"HMAC",
|
|
254
254
|
key,
|
|
255
|
-
|
|
255
|
+
encoder2.encode(message)
|
|
256
256
|
);
|
|
257
257
|
return Array.from(
|
|
258
258
|
new Uint8Array(signature),
|
|
@@ -1770,7 +1770,332 @@ var EventEmitterErrorAdapter = class {
|
|
|
1770
1770
|
}
|
|
1771
1771
|
}
|
|
1772
1772
|
};
|
|
1773
|
+
|
|
1774
|
+
// src/adapters/websocket-error-adapter.ts
|
|
1775
|
+
var OPEN = 1;
|
|
1776
|
+
var STATE_NAMES = {
|
|
1777
|
+
0: "still connecting",
|
|
1778
|
+
1: "open",
|
|
1779
|
+
2: "closing",
|
|
1780
|
+
3: "closed"
|
|
1781
|
+
};
|
|
1782
|
+
var WebSocketErrorAdapter = class {
|
|
1783
|
+
constructor(socket, options = {}) {
|
|
1784
|
+
this.socket = socket;
|
|
1785
|
+
this.prefix = options.prefix ?? DEFAULT_PREFIX;
|
|
1786
|
+
}
|
|
1787
|
+
async log(entry) {
|
|
1788
|
+
try {
|
|
1789
|
+
const state = this.socket.readyState;
|
|
1790
|
+
if (state !== OPEN) {
|
|
1791
|
+
throw new Error(
|
|
1792
|
+
`the socket is ${STATE_NAMES[state] ?? `in unknown state ${String(state)}`} (readyState ${String(state)}), so the entry was not sent; connect or reconnect it first`
|
|
1793
|
+
);
|
|
1794
|
+
}
|
|
1795
|
+
this.socket.send(
|
|
1796
|
+
JSON.stringify({ id: resolveEntryId(entry), ...serializeEntry(entry) })
|
|
1797
|
+
);
|
|
1798
|
+
return { ok: true };
|
|
1799
|
+
} catch (error) {
|
|
1800
|
+
return reportFailure({
|
|
1801
|
+
adapterName: "WebSocketErrorAdapter",
|
|
1802
|
+
prefix: this.prefix,
|
|
1803
|
+
action: "to send over the socket",
|
|
1804
|
+
error
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
|
|
1810
|
+
// src/adapters/cloudwatch-logs-error-adapter.ts
|
|
1811
|
+
var MAX_EVENT_BYTES = 262144;
|
|
1812
|
+
var EVENT_OVERHEAD_BYTES = 26;
|
|
1813
|
+
var MAX_BATCH_BYTES = 1048576;
|
|
1814
|
+
var MAX_BATCH_EVENTS = 1e4;
|
|
1815
|
+
var MAX_BATCH_SPAN_MS = 24 * 60 * 60 * 1e3;
|
|
1816
|
+
var BUFFER_LIMIT_MULTIPLIER2 = 10;
|
|
1817
|
+
var encoder = new TextEncoder();
|
|
1818
|
+
function eventBytes(message) {
|
|
1819
|
+
return encoder.encode(message).length + EVENT_OVERHEAD_BYTES;
|
|
1820
|
+
}
|
|
1821
|
+
function errorName(error) {
|
|
1822
|
+
return typeof error === "object" && error !== null ? error.name : void 0;
|
|
1823
|
+
}
|
|
1824
|
+
function splitIntoRequests(events) {
|
|
1825
|
+
const requests = [];
|
|
1826
|
+
let current = [];
|
|
1827
|
+
let bytes = 0;
|
|
1828
|
+
for (const event of events) {
|
|
1829
|
+
const size = eventBytes(event.message);
|
|
1830
|
+
const first = current[0];
|
|
1831
|
+
if (first !== void 0 && (current.length >= MAX_BATCH_EVENTS || bytes + size > MAX_BATCH_BYTES || event.timestamp - first.timestamp > MAX_BATCH_SPAN_MS)) {
|
|
1832
|
+
requests.push(current);
|
|
1833
|
+
current = [];
|
|
1834
|
+
bytes = 0;
|
|
1835
|
+
}
|
|
1836
|
+
current.push(event);
|
|
1837
|
+
bytes += size;
|
|
1838
|
+
}
|
|
1839
|
+
if (current.length > 0) {
|
|
1840
|
+
requests.push(current);
|
|
1841
|
+
}
|
|
1842
|
+
return requests;
|
|
1843
|
+
}
|
|
1844
|
+
var CloudWatchLogsErrorAdapter = class {
|
|
1845
|
+
/** @throws {RangeError} if a name is empty, the batch or timeout options are invalid, or `createMissing` is on but the client cannot create streams. */
|
|
1846
|
+
constructor(client, logGroupName, logStreamName, options = {}) {
|
|
1847
|
+
this.client = client;
|
|
1848
|
+
this.logGroupName = logGroupName;
|
|
1849
|
+
this.logStreamName = logStreamName;
|
|
1850
|
+
this.buffer = [];
|
|
1851
|
+
this.timer = null;
|
|
1852
|
+
this.flushQueue = new SerialQueue();
|
|
1853
|
+
this.createMissing = options.createMissing ?? true;
|
|
1854
|
+
this.batch = options.batch;
|
|
1855
|
+
this.logPrefix = options.prefix ?? DEFAULT_PREFIX;
|
|
1856
|
+
this.timeoutMs = resolveTimeoutMs(
|
|
1857
|
+
options.timeoutMs,
|
|
1858
|
+
"CloudWatchLogsErrorAdapter"
|
|
1859
|
+
);
|
|
1860
|
+
if (logGroupName === "") {
|
|
1861
|
+
throw new RangeError(
|
|
1862
|
+
"CloudWatchLogsErrorAdapter: logGroupName must not be empty."
|
|
1863
|
+
);
|
|
1864
|
+
}
|
|
1865
|
+
if (logStreamName === "") {
|
|
1866
|
+
throw new RangeError(
|
|
1867
|
+
"CloudWatchLogsErrorAdapter: logStreamName must not be empty."
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
if (this.createMissing && typeof client.createLogStream !== "function") {
|
|
1871
|
+
throw new RangeError(
|
|
1872
|
+
"CloudWatchLogsErrorAdapter: createMissing is on, but the client has no createLogStream method. Pass createMissing: false, or use an AWS SDK CloudWatchLogs client."
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
if (this.batch !== void 0) {
|
|
1876
|
+
if (!Number.isInteger(this.batch.batchSize) || this.batch.batchSize < 1) {
|
|
1877
|
+
throw new RangeError(
|
|
1878
|
+
`CloudWatchLogsErrorAdapter: batch.batchSize must be a positive integer, received ${String(this.batch.batchSize)}.`
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
if (this.batch.maxWaitMs !== void 0 && (!Number.isFinite(this.batch.maxWaitMs) || this.batch.maxWaitMs <= 0)) {
|
|
1882
|
+
throw new RangeError(
|
|
1883
|
+
`CloudWatchLogsErrorAdapter: batch.maxWaitMs must be a positive number, received ${String(this.batch.maxWaitMs)}.`
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
async log(entry) {
|
|
1889
|
+
try {
|
|
1890
|
+
const id = resolveEntryId(entry);
|
|
1891
|
+
const record = { id, ...serializeEntry(entry) };
|
|
1892
|
+
const timestamp = Date.parse(record.timestamp);
|
|
1893
|
+
if (Number.isNaN(timestamp)) {
|
|
1894
|
+
throw new RangeError(
|
|
1895
|
+
`timestamp "${record.timestamp}" is not a valid ISO 8601 date`
|
|
1896
|
+
);
|
|
1897
|
+
}
|
|
1898
|
+
const message = JSON.stringify(record);
|
|
1899
|
+
const size = eventBytes(message);
|
|
1900
|
+
if (size > MAX_EVENT_BYTES) {
|
|
1901
|
+
throw new RangeError(
|
|
1902
|
+
`the event is ${size} bytes, over the ${MAX_EVENT_BYTES} byte limit CloudWatch Logs puts on one event, so it was not sent; shorten the message or cause`
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
const event = { id, timestamp, message };
|
|
1906
|
+
if (this.batch === void 0) {
|
|
1907
|
+
const rejection = await this.send([event]);
|
|
1908
|
+
if (rejection !== void 0) {
|
|
1909
|
+
throw new Error(rejection);
|
|
1910
|
+
}
|
|
1911
|
+
return { ok: true };
|
|
1912
|
+
}
|
|
1913
|
+
return await this.addToBatch(event, this.batch);
|
|
1914
|
+
} catch (error) {
|
|
1915
|
+
return this.fail(error);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Sends any buffered events now. Resolves ok when nothing is buffered. Events
|
|
1920
|
+
* from a failed send stay buffered and are retried by the next flush.
|
|
1921
|
+
*/
|
|
1922
|
+
flush() {
|
|
1923
|
+
return this.flushQueue.run(
|
|
1924
|
+
() => this.sendBuffered()
|
|
1925
|
+
);
|
|
1926
|
+
}
|
|
1927
|
+
async addToBatch(event, batch) {
|
|
1928
|
+
this.buffer.push(event);
|
|
1929
|
+
const limit = batch.batchSize * BUFFER_LIMIT_MULTIPLIER2;
|
|
1930
|
+
if (this.buffer.length > limit) {
|
|
1931
|
+
const dropped = this.buffer.length - limit;
|
|
1932
|
+
this.buffer = this.buffer.slice(dropped);
|
|
1933
|
+
console.error(
|
|
1934
|
+
`${this.logPrefix} CloudWatchLogsErrorAdapter: dropped the ${dropped} oldest buffered events because sends to log stream "${this.logStreamName}" keep failing and the buffer is full (${limit} events).`
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
if (this.buffer.length >= batch.batchSize) {
|
|
1938
|
+
return this.flush();
|
|
1939
|
+
}
|
|
1940
|
+
this.scheduleFlush(batch);
|
|
1941
|
+
return { ok: true };
|
|
1942
|
+
}
|
|
1943
|
+
scheduleFlush(batch) {
|
|
1944
|
+
if (batch.maxWaitMs === void 0 || this.timer !== null) {
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
const timer = setTimeout(() => {
|
|
1948
|
+
this.timer = null;
|
|
1949
|
+
void this.flush();
|
|
1950
|
+
}, batch.maxWaitMs);
|
|
1951
|
+
timer.unref?.();
|
|
1952
|
+
this.timer = timer;
|
|
1953
|
+
}
|
|
1954
|
+
async sendBuffered() {
|
|
1955
|
+
if (this.timer !== null) {
|
|
1956
|
+
clearTimeout(this.timer);
|
|
1957
|
+
this.timer = null;
|
|
1958
|
+
}
|
|
1959
|
+
const pending = this.buffer.slice();
|
|
1960
|
+
if (pending.length === 0) {
|
|
1961
|
+
return { ok: true };
|
|
1962
|
+
}
|
|
1963
|
+
pending.sort(
|
|
1964
|
+
(a, b) => a.timestamp - b.timestamp
|
|
1965
|
+
);
|
|
1966
|
+
let rejectedTotal = 0;
|
|
1967
|
+
let lastRejection = "";
|
|
1968
|
+
try {
|
|
1969
|
+
for (const request of splitIntoRequests(pending)) {
|
|
1970
|
+
const rejection = await this.send(request);
|
|
1971
|
+
const sent = new Set(request);
|
|
1972
|
+
this.buffer = this.buffer.filter(
|
|
1973
|
+
(event) => !sent.has(event)
|
|
1974
|
+
);
|
|
1975
|
+
if (rejection !== void 0) {
|
|
1976
|
+
rejectedTotal += request.length;
|
|
1977
|
+
lastRejection = rejection;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
} catch (error) {
|
|
1981
|
+
if (this.batch !== void 0) {
|
|
1982
|
+
this.scheduleFlush(this.batch);
|
|
1983
|
+
}
|
|
1984
|
+
return this.fail(error, this.buffer.length);
|
|
1985
|
+
}
|
|
1986
|
+
if (rejectedTotal > 0) {
|
|
1987
|
+
return this.fail(new Error(lastRejection), rejectedTotal);
|
|
1988
|
+
}
|
|
1989
|
+
return { ok: true };
|
|
1990
|
+
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Sends one request, creating the stream first if CloudWatch says it is
|
|
1993
|
+
* missing. Resolves to a description of any events CloudWatch refused, or
|
|
1994
|
+
* undefined when it took them all.
|
|
1995
|
+
*/
|
|
1996
|
+
async send(events) {
|
|
1997
|
+
let output;
|
|
1998
|
+
try {
|
|
1999
|
+
output = await this.putLogEvents(events);
|
|
2000
|
+
} catch (error) {
|
|
2001
|
+
if (!this.createMissing || errorName(error) !== "ResourceNotFoundException") {
|
|
2002
|
+
throw error;
|
|
2003
|
+
}
|
|
2004
|
+
await this.createStream();
|
|
2005
|
+
output = await this.putLogEvents(events);
|
|
2006
|
+
}
|
|
2007
|
+
return describeRejection(output, events.length);
|
|
2008
|
+
}
|
|
2009
|
+
putLogEvents(events) {
|
|
2010
|
+
return withTimeout(
|
|
2011
|
+
() => this.client.putLogEvents({
|
|
2012
|
+
logGroupName: this.logGroupName,
|
|
2013
|
+
logStreamName: this.logStreamName,
|
|
2014
|
+
logEvents: events.map(
|
|
2015
|
+
({ timestamp, message }) => ({
|
|
2016
|
+
timestamp,
|
|
2017
|
+
message
|
|
2018
|
+
})
|
|
2019
|
+
)
|
|
2020
|
+
}),
|
|
2021
|
+
this.timeoutMs,
|
|
2022
|
+
timeoutMessage("CloudWatch Logs", this.timeoutMs)
|
|
2023
|
+
);
|
|
2024
|
+
}
|
|
2025
|
+
async createStream() {
|
|
2026
|
+
const createStream = () => withTimeout(
|
|
2027
|
+
() => this.client.createLogStream({
|
|
2028
|
+
logGroupName: this.logGroupName,
|
|
2029
|
+
logStreamName: this.logStreamName
|
|
2030
|
+
}),
|
|
2031
|
+
this.timeoutMs,
|
|
2032
|
+
timeoutMessage("CloudWatch Logs", this.timeoutMs, "create the stream")
|
|
2033
|
+
);
|
|
2034
|
+
try {
|
|
2035
|
+
await createStream();
|
|
2036
|
+
} catch (error) {
|
|
2037
|
+
const name = errorName(error);
|
|
2038
|
+
if (name === "ResourceAlreadyExistsException") {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
if (name !== "ResourceNotFoundException" || typeof this.client.createLogGroup !== "function") {
|
|
2042
|
+
throw error;
|
|
2043
|
+
}
|
|
2044
|
+
const createGroup = this.client.createLogGroup.bind(this.client);
|
|
2045
|
+
try {
|
|
2046
|
+
await withTimeout(
|
|
2047
|
+
() => createGroup({ logGroupName: this.logGroupName }),
|
|
2048
|
+
this.timeoutMs,
|
|
2049
|
+
timeoutMessage("CloudWatch Logs", this.timeoutMs, "create the group")
|
|
2050
|
+
);
|
|
2051
|
+
} catch (groupError) {
|
|
2052
|
+
if (errorName(groupError) !== "ResourceAlreadyExistsException") {
|
|
2053
|
+
throw groupError;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
try {
|
|
2057
|
+
await createStream();
|
|
2058
|
+
} catch (retryError) {
|
|
2059
|
+
if (errorName(retryError) !== "ResourceAlreadyExistsException") {
|
|
2060
|
+
throw retryError;
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
fail(error, count) {
|
|
2066
|
+
return reportFailure({
|
|
2067
|
+
adapterName: "CloudWatchLogsErrorAdapter",
|
|
2068
|
+
prefix: this.logPrefix,
|
|
2069
|
+
action: `to send to log stream "${this.logStreamName}" in log group "${this.logGroupName}"${count === void 0 ? "" : ` (${count} event${count === 1 ? "" : "s"} affected)`}`,
|
|
2070
|
+
error
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
};
|
|
2074
|
+
function describeRejection(output, eventCount) {
|
|
2075
|
+
const info = typeof output === "object" && output !== null ? output.rejectedLogEventsInfo : void 0;
|
|
2076
|
+
if (info === void 0 || info === null) {
|
|
2077
|
+
return void 0;
|
|
2078
|
+
}
|
|
2079
|
+
const reasons = [];
|
|
2080
|
+
if (info.tooOldLogEventEndIndex !== void 0) {
|
|
2081
|
+
reasons.push(
|
|
2082
|
+
`${info.tooOldLogEventEndIndex} event(s) were older than the 14 days CloudWatch accepts`
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
2085
|
+
if (info.expiredLogEventEndIndex !== void 0) {
|
|
2086
|
+
reasons.push(
|
|
2087
|
+
`${info.expiredLogEventEndIndex} event(s) were older than the log group's retention period`
|
|
2088
|
+
);
|
|
2089
|
+
}
|
|
2090
|
+
if (info.tooNewLogEventStartIndex !== void 0) {
|
|
2091
|
+
reasons.push(
|
|
2092
|
+
`${Math.max(0, eventCount - info.tooNewLogEventStartIndex)} event(s) were more than 2 hours ahead of the current time`
|
|
2093
|
+
);
|
|
2094
|
+
}
|
|
2095
|
+
return reasons.length === 0 ? void 0 : `CloudWatch Logs accepted the request but rejected events: ${reasons.join("; ")}`;
|
|
2096
|
+
}
|
|
1773
2097
|
export {
|
|
2098
|
+
CloudWatchLogsErrorAdapter,
|
|
1774
2099
|
CompositeErrorAdapter,
|
|
1775
2100
|
ConsoleErrorAdapter,
|
|
1776
2101
|
EventEmitterErrorAdapter,
|
|
@@ -1786,6 +2111,7 @@ export {
|
|
|
1786
2111
|
S3ErrorAdapter,
|
|
1787
2112
|
SqliteErrorAdapter,
|
|
1788
2113
|
SupabaseErrorAdapter,
|
|
2114
|
+
WebSocketErrorAdapter,
|
|
1789
2115
|
WebStorageErrorAdapter,
|
|
1790
2116
|
WebhookErrorAdapter,
|
|
1791
2117
|
buildCreateErrorLogTableSql,
|