@mongrov/core 0.5.0 → 0.6.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/dist/context/logging-provider.d.ts.map +1 -1
- package/dist/context/logging-provider.js +27 -6
- package/dist/context/logging-provider.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal-log.d.ts +31 -0
- package/dist/internal-log.d.ts.map +1 -0
- package/dist/internal-log.js +50 -0
- package/dist/internal-log.js.map +1 -0
- package/dist/logger.d.ts +15 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +228 -28
- package/dist/logger.js.map +1 -1
- package/dist/network-state.d.ts +6 -0
- package/dist/network-state.d.ts.map +1 -1
- package/dist/network-state.js +43 -5
- package/dist/network-state.js.map +1 -1
- package/dist/offline-queue.d.ts +6 -0
- package/dist/offline-queue.d.ts.map +1 -1
- package/dist/offline-queue.js +50 -9
- package/dist/offline-queue.js.map +1 -1
- package/dist/transports/file.d.ts +22 -2
- package/dist/transports/file.d.ts.map +1 -1
- package/dist/transports/file.js +101 -20
- package/dist/transports/file.js.map +1 -1
- package/dist/transports/webhook.d.ts +11 -1
- package/dist/transports/webhook.d.ts.map +1 -1
- package/dist/transports/webhook.js +44 -8
- package/dist/transports/webhook.js.map +1 -1
- package/package.json +1 -1
package/dist/network-state.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { devConsoleLogger, FailureGate } from './internal-log';
|
|
1
2
|
let cachedState = {
|
|
2
3
|
isConnected: true,
|
|
3
4
|
type: null,
|
|
@@ -5,6 +6,16 @@ let cachedState = {
|
|
|
5
6
|
};
|
|
6
7
|
const listeners = new Set();
|
|
7
8
|
let pollingInterval = null;
|
|
9
|
+
// Module state has no logger of its own; `LoggingProvider` sets the app's.
|
|
10
|
+
let log = devConsoleLogger();
|
|
11
|
+
const pollFailures = new FailureGate();
|
|
12
|
+
/**
|
|
13
|
+
* Where connectivity changes and polling failures are reported. Called by
|
|
14
|
+
* `LoggingProvider`; `null` restores the dev-console fallback.
|
|
15
|
+
*/
|
|
16
|
+
export function setNetworkStateLogger(logger) {
|
|
17
|
+
log = logger ?? devConsoleLogger();
|
|
18
|
+
}
|
|
8
19
|
function getExpoNetwork() {
|
|
9
20
|
try {
|
|
10
21
|
return require('expo-network');
|
|
@@ -28,19 +39,43 @@ async function pollAndNotify() {
|
|
|
28
39
|
const changed = newState.isConnected !== cachedState.isConnected
|
|
29
40
|
|| newState.type !== cachedState.type
|
|
30
41
|
|| newState.isInternetReachable !== cachedState.isInternetReachable;
|
|
42
|
+
const previous = cachedState;
|
|
31
43
|
cachedState = newState;
|
|
44
|
+
const failed = pollFailures.recover();
|
|
45
|
+
if (failed > 0)
|
|
46
|
+
log.info('[Network] state readable again', { failedPolls: failed });
|
|
32
47
|
if (changed) {
|
|
33
|
-
|
|
48
|
+
log.info('[Network] state changed', {
|
|
49
|
+
isConnected: newState.isConnected,
|
|
50
|
+
type: newState.type,
|
|
51
|
+
isInternetReachable: newState.isInternetReachable,
|
|
52
|
+
wasConnected: previous.isConnected,
|
|
53
|
+
});
|
|
54
|
+
notify(newState);
|
|
34
55
|
}
|
|
35
56
|
}
|
|
36
|
-
catch {
|
|
37
|
-
//
|
|
57
|
+
catch (error) {
|
|
58
|
+
// The cached state stays as it was; listeners hear nothing until a poll
|
|
59
|
+
// succeeds. Reported once per run of failures (polls every 15s).
|
|
60
|
+
if (pollFailures.fail())
|
|
61
|
+
log.warn('[Network] state poll failed; keeping last known state', { error: String(error) });
|
|
38
62
|
}
|
|
39
63
|
}
|
|
64
|
+
function notify(state) {
|
|
65
|
+
listeners.forEach((cb) => {
|
|
66
|
+
// One throwing listener must not starve the others.
|
|
67
|
+
try {
|
|
68
|
+
cb(state);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
log.error('[Network] listener threw', { error: String(error) });
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
40
75
|
function startPolling() {
|
|
41
76
|
if (pollingInterval)
|
|
42
77
|
return;
|
|
43
|
-
pollAndNotify();
|
|
78
|
+
void pollAndNotify(); // never rejects: failures are reported inside
|
|
44
79
|
pollingInterval = setInterval(pollAndNotify, 15000);
|
|
45
80
|
}
|
|
46
81
|
function stopPolling() {
|
|
@@ -74,7 +109,10 @@ export function useNetworkState() {
|
|
|
74
109
|
const { useState, useEffect } = React;
|
|
75
110
|
const [state, setState] = useState(cachedState);
|
|
76
111
|
useEffect(() => {
|
|
77
|
-
getNetworkState().then(setState)
|
|
112
|
+
getNetworkState().then(setState, (error) => {
|
|
113
|
+
// Keeps the cached state; the listener below picks up the next poll.
|
|
114
|
+
log.warn('[Network] initial state read failed', { error: String(error) });
|
|
115
|
+
});
|
|
78
116
|
const subscription = addNetworkStateListener(setState);
|
|
79
117
|
return () => subscription.remove();
|
|
80
118
|
}, []);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-state.js","sourceRoot":"","sources":["../src/network-state.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"network-state.js","sourceRoot":"","sources":["../src/network-state.ts"],"names":[],"mappings":"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"}
|
package/dist/offline-queue.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InternalLogger } from './internal-log';
|
|
1
2
|
import type { LogEntry } from './types';
|
|
2
3
|
interface MMKVStorage {
|
|
3
4
|
getString: (key: string) => string | undefined;
|
|
@@ -12,10 +13,15 @@ export declare class OfflineQueue {
|
|
|
12
13
|
private storage;
|
|
13
14
|
private networkSubscription;
|
|
14
15
|
private flushing;
|
|
16
|
+
private readonly log;
|
|
17
|
+
private overflowDropped;
|
|
18
|
+
private readonly storageFailures;
|
|
15
19
|
constructor(sendFn: (entries: LogEntry[]) => Promise<void>, options?: {
|
|
16
20
|
maxSize?: number;
|
|
17
21
|
maxRetries?: number;
|
|
18
22
|
storage?: MMKVStorage;
|
|
23
|
+
/** Receives the queue's own failures; dev console when omitted. */
|
|
24
|
+
logger?: InternalLogger;
|
|
19
25
|
});
|
|
20
26
|
enqueue(entries: LogEntry[]): void;
|
|
21
27
|
flush(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"offline-queue.d.ts","sourceRoot":"","sources":["../src/offline-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"offline-queue.d.ts","sourceRoot":"","sources":["../src/offline-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AASvC,UAAU,WAAW;IACnB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAC9C,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAC9B;AAcD,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,mBAAmB,CAAsC;IACjE,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IAEpC,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoB;gBAGlD,MAAM,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EAC9C,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,OAAO,CAAC,EAAE,WAAW,CAAA;QACrB,mEAAmE;QACnE,MAAM,CAAC,EAAE,cAAc,CAAA;KACxB;IAWH,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI;IAgB5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8D5B,YAAY,IAAI,MAAM;IAItB,OAAO,IAAI,IAAI;YAOD,aAAa;IAmB3B,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,qBAAqB;CAS9B"}
|
package/dist/offline-queue.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { devConsoleLogger, FailureGate } from './internal-log';
|
|
1
2
|
import { addNetworkStateListener, getNetworkState } from './network-state';
|
|
2
3
|
const QUEUE_KEY = '@mongrov/log-queue';
|
|
3
4
|
const DEFAULT_MAX_SIZE = 500;
|
|
@@ -17,6 +18,10 @@ export class OfflineQueue {
|
|
|
17
18
|
this.queue = [];
|
|
18
19
|
this.networkSubscription = null;
|
|
19
20
|
this.flushing = false;
|
|
21
|
+
// Entries trimmed off the head since the last report.
|
|
22
|
+
this.overflowDropped = 0;
|
|
23
|
+
this.storageFailures = new FailureGate();
|
|
24
|
+
this.log = options?.logger ?? devConsoleLogger();
|
|
20
25
|
this.sendFn = sendFn;
|
|
21
26
|
this.maxSize = options?.maxSize ?? DEFAULT_MAX_SIZE;
|
|
22
27
|
this.maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
@@ -28,20 +33,34 @@ export class OfflineQueue {
|
|
|
28
33
|
this.queue.push(...entries);
|
|
29
34
|
// Drop oldest entries if over max size (FIFO)
|
|
30
35
|
if (this.queue.length > this.maxSize) {
|
|
31
|
-
|
|
36
|
+
const dropped = this.queue.length - this.maxSize;
|
|
37
|
+
this.queue = this.queue.slice(dropped);
|
|
38
|
+
// Once per overflow run; the total is reported when the queue drains.
|
|
39
|
+
if (this.overflowDropped === 0)
|
|
40
|
+
this.log.warn('[OfflineQueue] full; dropping oldest entries', { maxSize: this.maxSize });
|
|
41
|
+
this.overflowDropped += dropped;
|
|
32
42
|
}
|
|
33
43
|
this.saveToStorage();
|
|
34
44
|
}
|
|
35
45
|
async flush() {
|
|
36
46
|
if (this.flushing || this.queue.length === 0)
|
|
37
47
|
return;
|
|
38
|
-
|
|
48
|
+
let networkState;
|
|
49
|
+
try {
|
|
50
|
+
networkState = await getNetworkState();
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
// No reading (expo-network missing or failing): keep the entries for
|
|
54
|
+
// the next attempt rather than throwing out of a background flush.
|
|
55
|
+
this.log.warn('[OfflineQueue] network state unavailable; flush skipped', { error: String(error) });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
39
58
|
if (!networkState.isConnected)
|
|
40
59
|
return;
|
|
41
60
|
this.flushing = true;
|
|
61
|
+
const batch = [...this.queue];
|
|
62
|
+
const startedAt = Date.now();
|
|
42
63
|
try {
|
|
43
|
-
// Take a snapshot of entries to send
|
|
44
|
-
const batch = [...this.queue];
|
|
45
64
|
await this.sendWithRetry(batch);
|
|
46
65
|
// Remove the delivered entries BY ID, not by slicing off `batch.length`
|
|
47
66
|
// from the head.
|
|
@@ -59,9 +78,21 @@ export class OfflineQueue {
|
|
|
59
78
|
const deliveredIds = new Set(batch.map(entry => entry.id));
|
|
60
79
|
this.queue = this.queue.filter(entry => !deliveredIds.has(entry.id));
|
|
61
80
|
this.saveToStorage();
|
|
81
|
+
this.log.info('[OfflineQueue] flushed', {
|
|
82
|
+
sent: batch.length,
|
|
83
|
+
remaining: this.queue.length,
|
|
84
|
+
droppedOnOverflow: this.overflowDropped,
|
|
85
|
+
durationMs: Date.now() - startedAt,
|
|
86
|
+
});
|
|
87
|
+
this.overflowDropped = 0;
|
|
62
88
|
}
|
|
63
|
-
catch {
|
|
89
|
+
catch (error) {
|
|
64
90
|
// Retries exhausted — entries stay in queue for next flush attempt
|
|
91
|
+
this.log.warn('[OfflineQueue] flush failed; entries kept for next attempt', {
|
|
92
|
+
error: String(error),
|
|
93
|
+
attempts: this.maxRetries,
|
|
94
|
+
queued: this.queue.length,
|
|
95
|
+
});
|
|
65
96
|
this.saveToStorage();
|
|
66
97
|
}
|
|
67
98
|
finally {
|
|
@@ -105,7 +136,9 @@ export class OfflineQueue {
|
|
|
105
136
|
}
|
|
106
137
|
}
|
|
107
138
|
}
|
|
108
|
-
catch {
|
|
139
|
+
catch (error) {
|
|
140
|
+
// A corrupt saved queue is discarded: those entries are lost.
|
|
141
|
+
this.log.warn('[OfflineQueue] saved queue unreadable; discarded', { error: String(error) });
|
|
109
142
|
this.queue = [];
|
|
110
143
|
}
|
|
111
144
|
}
|
|
@@ -117,15 +150,23 @@ export class OfflineQueue {
|
|
|
117
150
|
else {
|
|
118
151
|
this.storage.set(QUEUE_KEY, JSON.stringify(this.queue));
|
|
119
152
|
}
|
|
153
|
+
const failed = this.storageFailures.recover();
|
|
154
|
+
if (failed > 0)
|
|
155
|
+
this.log.info('[OfflineQueue] storage writes recovered', { failedWrites: failed });
|
|
120
156
|
}
|
|
121
|
-
catch {
|
|
122
|
-
//
|
|
157
|
+
catch (error) {
|
|
158
|
+
// Non-critical while the app runs (the queue is in memory), but the
|
|
159
|
+
// queue will not survive a restart. Once per run of failures.
|
|
160
|
+
if (this.storageFailures.fail())
|
|
161
|
+
this.log.warn('[OfflineQueue] persist failed', { error: String(error) });
|
|
123
162
|
}
|
|
124
163
|
}
|
|
125
164
|
listenForConnectivity() {
|
|
126
165
|
this.networkSubscription = addNetworkStateListener((state) => {
|
|
127
166
|
if (state.isConnected) {
|
|
128
|
-
this.flush().catch(() => {
|
|
167
|
+
this.flush().catch((error) => {
|
|
168
|
+
this.log.error('[OfflineQueue] flush on reconnect threw', { error: String(error) });
|
|
169
|
+
});
|
|
129
170
|
}
|
|
130
171
|
});
|
|
131
172
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"offline-queue.js","sourceRoot":"","sources":["../src/offline-queue.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"offline-queue.js","sourceRoot":"","sources":["../src/offline-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +1,38 @@
|
|
|
1
|
+
import type { InternalLogger } from '../internal-log';
|
|
1
2
|
import type { FileConfig, LogEntry, LogTransport } from '../types';
|
|
2
3
|
export declare class FileTransport implements LogTransport {
|
|
3
4
|
readonly name = "file";
|
|
4
5
|
private readonly directory;
|
|
5
6
|
private readonly maxSizeMB;
|
|
6
7
|
private readonly retentionDays;
|
|
8
|
+
private readonly log;
|
|
9
|
+
private readonly writeFailures;
|
|
7
10
|
private initialized;
|
|
8
|
-
private
|
|
11
|
+
private pending;
|
|
12
|
+
private pendingWaiters;
|
|
13
|
+
private writing;
|
|
9
14
|
private lastCleanup;
|
|
10
15
|
private readonly cleanupIntervalMs;
|
|
11
|
-
|
|
16
|
+
/**
|
|
17
|
+
* `logger` receives this transport's own failures. `createLogger` passes
|
|
18
|
+
* one that skips this transport; standalone use falls back to the dev
|
|
19
|
+
* console.
|
|
20
|
+
*/
|
|
21
|
+
constructor(config?: FileConfig, logger?: InternalLogger);
|
|
22
|
+
/** Resolves once these entries are on disk (or the write failed). */
|
|
12
23
|
send(entries: LogEntry[]): Promise<void>;
|
|
24
|
+
/** Resolves once everything sent so far has been written. */
|
|
25
|
+
flush(): Promise<void>;
|
|
26
|
+
private drain;
|
|
13
27
|
private doWrite;
|
|
14
28
|
getLogFiles(): Promise<string[]>;
|
|
15
29
|
readFile(filename: string): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Every retained day file, oldest first: one JSON entry per line, oldest
|
|
32
|
+
* line first. Unlike the ring buffer this survives an app restart or crash.
|
|
33
|
+
* Waits for pending writes first, so the result includes everything sent.
|
|
34
|
+
*/
|
|
35
|
+
readAll(): Promise<string>;
|
|
16
36
|
private ensureDirectory;
|
|
17
37
|
private cleanupIfNeeded;
|
|
18
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/transports/file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/transports/file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAoDlE,qBAAa,aAAc,YAAW,YAAY;IAChD,QAAQ,CAAC,IAAI,UAAS;IAEtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,WAAW,CAAQ;IAQ3B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,OAAO,CAA6B;IAG5C,OAAO,CAAC,WAAW,CAAI;IACvB,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C;;;;OAIG;gBACS,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,cAAc;IASxD,qEAAqE;IACrE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxC,6DAA6D;IACvD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAKd,KAAK;YAaL,OAAO;IAwCf,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAehC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKjD;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;YAelB,eAAe;YAWf,eAAe;CA4D9B"}
|
package/dist/transports/file.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { devConsoleLogger, FailureGate } from '../internal-log';
|
|
1
2
|
let FileSystemModule = null;
|
|
2
3
|
function getFileSystem() {
|
|
3
4
|
if (!FileSystemModule) {
|
|
@@ -17,32 +18,70 @@ function getFileSystem() {
|
|
|
17
18
|
}
|
|
18
19
|
return FileSystemModule;
|
|
19
20
|
}
|
|
21
|
+
// UTC, the same clock as every entry's `timestamp`, so a file's name and the
|
|
22
|
+
// lines inside it agree on which day they belong to.
|
|
20
23
|
function formatDate(date) {
|
|
21
|
-
const y = date.
|
|
22
|
-
const m = String(date.
|
|
23
|
-
const d = String(date.
|
|
24
|
+
const y = date.getUTCFullYear();
|
|
25
|
+
const m = String(date.getUTCMonth() + 1).padStart(2, '0');
|
|
26
|
+
const d = String(date.getUTCDate()).padStart(2, '0');
|
|
24
27
|
return `${y}-${m}-${d}`;
|
|
25
28
|
}
|
|
29
|
+
function todayFilename() {
|
|
30
|
+
return `logs-${formatDate(new Date())}.txt`;
|
|
31
|
+
}
|
|
26
32
|
export class FileTransport {
|
|
27
|
-
|
|
33
|
+
/**
|
|
34
|
+
* `logger` receives this transport's own failures. `createLogger` passes
|
|
35
|
+
* one that skips this transport; standalone use falls back to the dev
|
|
36
|
+
* console.
|
|
37
|
+
*/
|
|
38
|
+
constructor(config, logger) {
|
|
28
39
|
this.name = 'file';
|
|
40
|
+
this.writeFailures = new FailureGate();
|
|
29
41
|
this.initialized = false;
|
|
30
|
-
//
|
|
31
|
-
|
|
42
|
+
// Entries waiting for the next write, and the callers waiting on them.
|
|
43
|
+
// expo-file-system has no append, so every write reads and rewrites the
|
|
44
|
+
// whole day file. Writing once per `send` made that cost per log line — a
|
|
45
|
+
// full ring sync logs hundreds of lines in seconds, each rewriting a file of
|
|
46
|
+
// up to `maxSizeMB`. Lines that arrive while a write is in flight are
|
|
47
|
+
// coalesced into the next one instead.
|
|
48
|
+
this.pending = [];
|
|
49
|
+
this.pendingWaiters = [];
|
|
50
|
+
this.writing = null;
|
|
32
51
|
// Throttle cleanup to once per minute
|
|
33
52
|
this.lastCleanup = 0;
|
|
34
53
|
this.cleanupIntervalMs = 60000;
|
|
54
|
+
this.log = logger ?? devConsoleLogger();
|
|
35
55
|
const fs = getFileSystem();
|
|
36
56
|
this.directory
|
|
37
57
|
= config?.directory ?? `${fs.documentDirectory ?? ''}logs/`;
|
|
38
58
|
this.maxSizeMB = config?.maxSizeMB ?? 5;
|
|
39
59
|
this.retentionDays = config?.retentionDays ?? 7;
|
|
40
60
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
/** Resolves once these entries are on disk (or the write failed). */
|
|
62
|
+
send(entries) {
|
|
63
|
+
this.pending.push(...entries);
|
|
64
|
+
const done = new Promise(resolve => this.pendingWaiters.push(resolve));
|
|
65
|
+
if (!this.writing)
|
|
66
|
+
this.writing = this.drain();
|
|
67
|
+
return done;
|
|
68
|
+
}
|
|
69
|
+
/** Resolves once everything sent so far has been written. */
|
|
70
|
+
async flush() {
|
|
71
|
+
while (this.writing)
|
|
72
|
+
await this.writing;
|
|
73
|
+
}
|
|
74
|
+
async drain() {
|
|
75
|
+
while (this.pending.length > 0) {
|
|
76
|
+
const batch = this.pending;
|
|
77
|
+
const waiters = this.pendingWaiters;
|
|
78
|
+
this.pending = [];
|
|
79
|
+
this.pendingWaiters = [];
|
|
80
|
+
await this.doWrite(batch);
|
|
81
|
+
for (const resolve of waiters)
|
|
82
|
+
resolve();
|
|
83
|
+
}
|
|
84
|
+
this.writing = null;
|
|
46
85
|
}
|
|
47
86
|
async doWrite(entries) {
|
|
48
87
|
try {
|
|
@@ -53,8 +92,7 @@ export class FileTransport {
|
|
|
53
92
|
this.lastCleanup = now;
|
|
54
93
|
}
|
|
55
94
|
const lines = `${entries.map(e => JSON.stringify(e)).join('\n')}\n`;
|
|
56
|
-
const
|
|
57
|
-
const filePath = `${this.directory}${filename}`;
|
|
95
|
+
const filePath = `${this.directory}${todayFilename()}`;
|
|
58
96
|
const fs = getFileSystem();
|
|
59
97
|
const info = await fs.getInfoAsync(filePath);
|
|
60
98
|
if (info.exists) {
|
|
@@ -65,10 +103,19 @@ export class FileTransport {
|
|
|
65
103
|
else {
|
|
66
104
|
await fs.writeAsStringAsync(filePath, lines);
|
|
67
105
|
}
|
|
106
|
+
const failed = this.writeFailures.recover();
|
|
107
|
+
if (failed > 0)
|
|
108
|
+
this.log.info('[FileTransport] writes recovered', { failedWrites: failed });
|
|
68
109
|
}
|
|
69
110
|
catch (error) {
|
|
70
|
-
// Logging
|
|
71
|
-
|
|
111
|
+
// Logging must never crash the app. The lines are lost from the file
|
|
112
|
+
// (the ring buffer still has them); report once per run of failures.
|
|
113
|
+
if (this.writeFailures.fail()) {
|
|
114
|
+
this.log.error('[FileTransport] write failed; file log is missing lines', {
|
|
115
|
+
error: String(error),
|
|
116
|
+
lost: entries.length,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
72
119
|
}
|
|
73
120
|
}
|
|
74
121
|
async getLogFiles() {
|
|
@@ -81,6 +128,7 @@ export class FileTransport {
|
|
|
81
128
|
.reverse();
|
|
82
129
|
}
|
|
83
130
|
catch {
|
|
131
|
+
// expected: no directory yet means no files have been written.
|
|
84
132
|
return [];
|
|
85
133
|
}
|
|
86
134
|
}
|
|
@@ -88,6 +136,25 @@ export class FileTransport {
|
|
|
88
136
|
const fs = getFileSystem();
|
|
89
137
|
return fs.readAsStringAsync(`${this.directory}${filename}`);
|
|
90
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Every retained day file, oldest first: one JSON entry per line, oldest
|
|
141
|
+
* line first. Unlike the ring buffer this survives an app restart or crash.
|
|
142
|
+
* Waits for pending writes first, so the result includes everything sent.
|
|
143
|
+
*/
|
|
144
|
+
async readAll() {
|
|
145
|
+
await this.flush();
|
|
146
|
+
const files = (await this.getLogFiles()).reverse();
|
|
147
|
+
let out = '';
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
try {
|
|
150
|
+
out += await this.readFile(file);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// A file removed by cleanup mid-read is simply skipped.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
91
158
|
async ensureDirectory() {
|
|
92
159
|
if (this.initialized)
|
|
93
160
|
return;
|
|
@@ -115,7 +182,10 @@ export class FileTransport {
|
|
|
115
182
|
await fs.deleteAsync(`${this.directory}${file}`, { idempotent: true });
|
|
116
183
|
}
|
|
117
184
|
}
|
|
118
|
-
// Check total size and delete oldest if exceeding maxSizeMB
|
|
185
|
+
// Check total size and delete oldest if exceeding maxSizeMB. Today's
|
|
186
|
+
// file is never deleted — it holds the session being diagnosed. If it
|
|
187
|
+
// alone is over the cap, its oldest lines are dropped instead.
|
|
188
|
+
const today = todayFilename();
|
|
119
189
|
let totalSize = 0;
|
|
120
190
|
const remaining = (await fs.readDirectoryAsync(this.directory))
|
|
121
191
|
.filter(f => f.startsWith('logs-') && f.endsWith('.txt'))
|
|
@@ -125,17 +195,28 @@ export class FileTransport {
|
|
|
125
195
|
totalSize += info.size ?? 0;
|
|
126
196
|
}
|
|
127
197
|
const maxSizeBytes = this.maxSizeMB * 1024 * 1024;
|
|
198
|
+
const older = remaining.filter(f => f !== today);
|
|
128
199
|
let i = 0;
|
|
129
|
-
while (totalSize > maxSizeBytes && i <
|
|
130
|
-
const file =
|
|
200
|
+
while (totalSize > maxSizeBytes && i < older.length) {
|
|
201
|
+
const file = older[i];
|
|
131
202
|
const info = await fs.getInfoAsync(`${this.directory}${file}`);
|
|
132
203
|
await fs.deleteAsync(`${this.directory}${file}`, { idempotent: true });
|
|
133
204
|
totalSize -= info.size ?? 0;
|
|
134
205
|
i++;
|
|
135
206
|
}
|
|
207
|
+
if (totalSize > maxSizeBytes && remaining.includes(today)) {
|
|
208
|
+
const path = `${this.directory}${today}`;
|
|
209
|
+
const content = await fs.readAsStringAsync(path);
|
|
210
|
+
// Keep the newest half of the cap, cut at a line boundary.
|
|
211
|
+
const tail = content.slice(content.length - Math.floor(maxSizeBytes / 2));
|
|
212
|
+
const firstBreak = tail.indexOf('\n');
|
|
213
|
+
await fs.writeAsStringAsync(path, firstBreak >= 0 ? tail.slice(firstBreak + 1) : '');
|
|
214
|
+
}
|
|
136
215
|
}
|
|
137
|
-
catch {
|
|
138
|
-
//
|
|
216
|
+
catch (error) {
|
|
217
|
+
// Non-critical: the next cleanup (a minute on) tries again, but a
|
|
218
|
+
// cleanup that keeps failing lets the log directory grow unbounded.
|
|
219
|
+
this.log.warn('[FileTransport] cleanup failed', { error: String(error) });
|
|
139
220
|
}
|
|
140
221
|
}
|
|
141
222
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/transports/file.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/transports/file.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAc/D,IAAI,gBAAgB,GAAsB,IAAI,CAAA;AAE9C,SAAS,aAAa;IACpB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,mFAAmF;YACnF,mDAAmD;YAEnD,gBAAgB,GAAG,OAAO,CAAC,yBAAyB,CAAe,CAAA;QACrE,CAAC;QACD,MAAM,CAAC;YACL,IAAI,CAAC;gBACH,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAe,CAAA;YAC9D,CAAC;YACD,MAAM,CAAC;gBACL,MAAM,IAAI,KAAK,CACb,4EAA4E,CAC7E,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,gBAAgB,CAAA;AACzB,CAAC;AAED,6EAA6E;AAC7E,qDAAqD;AACrD,SAAS,UAAU,CAAC,IAAU;IAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;IAC/B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACzD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACpD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;AACzB,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,QAAQ,UAAU,CAAC,IAAI,IAAI,EAAE,CAAC,MAAM,CAAA;AAC7C,CAAC;AAED,MAAM,OAAO,aAAa;IAwBxB;;;;OAIG;IACH,YAAY,MAAmB,EAAE,MAAuB;QA5B/C,SAAI,GAAG,MAAM,CAAA;QAML,kBAAa,GAAG,IAAI,WAAW,EAAE,CAAA;QAC1C,gBAAW,GAAG,KAAK,CAAA;QAE3B,uEAAuE;QACvE,wEAAwE;QACxE,0EAA0E;QAC1E,6EAA6E;QAC7E,sEAAsE;QACtE,uCAAuC;QAC/B,YAAO,GAAe,EAAE,CAAA;QACxB,mBAAc,GAAsB,EAAE,CAAA;QACtC,YAAO,GAAyB,IAAI,CAAA;QAE5C,sCAAsC;QAC9B,gBAAW,GAAG,CAAC,CAAA;QACN,sBAAiB,GAAG,KAAM,CAAA;QAQzC,IAAI,CAAC,GAAG,GAAG,MAAM,IAAI,gBAAgB,EAAE,CAAA;QACvC,MAAM,EAAE,GAAG,aAAa,EAAE,CAAA;QAC1B,IAAI,CAAC,SAAS;cACV,MAAM,EAAE,SAAS,IAAI,GAAG,EAAE,CAAC,iBAAiB,IAAI,EAAE,OAAO,CAAA;QAC7D,IAAI,CAAC,SAAS,GAAG,MAAM,EAAE,SAAS,IAAI,CAAC,CAAA;QACvC,IAAI,CAAC,aAAa,GAAG,MAAM,EAAE,aAAa,IAAI,CAAC,CAAA;IACjD,CAAC;IAED,qEAAqE;IACrE,IAAI,CAAC,OAAmB;QACtB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAA;QAC7B,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;QAC5E,IAAI,CAAC,IAAI,CAAC,OAAO;YACf,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,CAAA;QAC7B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,6DAA6D;IAC7D,KAAK,CAAC,KAAK;QACT,OAAO,IAAI,CAAC,OAAO;YACjB,MAAM,IAAI,CAAC,OAAO,CAAA;IACtB,CAAC;IAEO,KAAK,CAAC,KAAK;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAA;YAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAA;YACnC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;YACjB,IAAI,CAAC,cAAc,GAAG,EAAE,CAAA;YACxB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACzB,KAAK,MAAM,OAAO,IAAI,OAAO;gBAC3B,OAAO,EAAE,CAAA;QACb,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;IACrB,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,OAAmB;QACvC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,EAAE,CAAA;YAE5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACtB,IAAI,GAAG,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACrD,MAAM,IAAI,CAAC,eAAe,EAAE,CAAA;gBAC5B,IAAI,CAAC,WAAW,GAAG,GAAG,CAAA;YACxB,CAAC;YAED,MAAM,KAAK,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;YACnE,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,SAAS,GAAG,aAAa,EAAE,EAAE,CAAA;YAEtD,MAAM,EAAE,GAAG,aAAa,EAAE,CAAA;YAC1B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAA;YAE5C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,iDAAiD;gBACjD,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAA;gBACrD,MAAM,EAAE,CAAC,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAA;YACzD,CAAC;iBACI,CAAC;gBACJ,MAAM,EAAE,CAAC,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;YAC9C,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAA;YAC3C,IAAI,MAAM,GAAG,CAAC;gBACZ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,kCAAkC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAA;QAC/E,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACb,qEAAqE;YACrE,qEAAqE;YACrE,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC9B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,yDAAyD,EAAE;oBACxE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;oBACpB,IAAI,EAAE,OAAO,CAAC,MAAM;iBACrB,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,aAAa,EAAE,CAAA;YAC1B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACzD,OAAO,KAAK;iBACT,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;iBACxD,IAAI,EAAE;iBACN,OAAO,EAAE,CAAA;QACd,CAAC;QACD,MAAM,CAAC;YACL,+DAA+D;YAC/D,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAC7B,MAAM,EAAE,GAAG,aAAa,EAAE,CAAA;QAC1B,OAAO,EAAE,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC,SAAS,GAAG,QAAQ,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;QAClB,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAA;QAClD,IAAI,GAAG,GAAG,EAAE,CAAA;QACZ,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,GAAG,IAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;YAClC,CAAC;YACD,MAAM,CAAC;gBACL,wDAAwD;YAC1D,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IAEO,KAAK,CAAC,eAAe;QAC3B,IAAI,IAAI,CAAC,WAAW;YAClB,OAAM;QACR,MAAM,EAAE,GAAG,aAAa,EAAE,CAAA;QAC1B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAClD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;IACzB,CAAC;IAEO,KAAK,CAAC,eAAe;QAC3B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,aAAa,EAAE,CAAA;YAC1B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACzD,MAAM,QAAQ,GAAG,KAAK;iBACnB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;iBACxD,IAAI,EAAE,CAAA;YAET,wCAAwC;YACxC,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAA;YAC7B,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAA;YAC7D,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,CAAC,CAAA;YAExC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;gBAC7D,IAAI,OAAO,GAAG,SAAS,EAAE,CAAC;oBACxB,MAAM,EAAE,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;gBACxE,CAAC;YACH,CAAC;YAED,qEAAqE;YACrE,sEAAsE;YACtE,+DAA+D;YAC/D,MAAM,KAAK,GAAG,aAAa,EAAE,CAAA;YAC7B,IAAI,SAAS,GAAG,CAAC,CAAA;YACjB,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;iBAC5D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;iBACxD,IAAI,EAAE,CAAA;YAET,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,EAAE,CAAC,CAAA;gBAC9D,SAAS,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,CAAA;YAC7B,CAAC;YAED,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,IAAI,CAAA;YACjD,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAA;YAChD,IAAI,CAAC,GAAG,CAAC,CAAA;YACT,OAAO,SAAS,GAAG,YAAY,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;gBACpD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;gBACrB,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,EAAE,CAAC,CAAA;gBAC9D,MAAM,EAAE,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;gBACtE,SAAS,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,CAAA;gBAC3B,CAAC,EAAE,CAAA;YACL,CAAC;YAED,IAAI,SAAS,GAAG,YAAY,IAAI,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1D,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,GAAG,KAAK,EAAE,CAAA;gBACxC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAA;gBAChD,2DAA2D;gBAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAA;gBACzE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;gBACrC,MAAM,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YACtF,CAAC;QACH,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACb,kEAAkE;YAClE,oEAAoE;YACpE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,gCAAgC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC3E,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InternalLogger } from '../internal-log';
|
|
1
2
|
import type { LogEntry, LogTransport, WebhookConfig } from '../types';
|
|
2
3
|
export declare class WebhookTransport implements LogTransport {
|
|
3
4
|
readonly name = "webhook";
|
|
@@ -6,7 +7,16 @@ export declare class WebhookTransport implements LogTransport {
|
|
|
6
7
|
private timer;
|
|
7
8
|
private offlineQueue;
|
|
8
9
|
private flushing;
|
|
9
|
-
|
|
10
|
+
private readonly log;
|
|
11
|
+
private readonly rejections;
|
|
12
|
+
private droppedEntries;
|
|
13
|
+
private readonly postFailures;
|
|
14
|
+
/**
|
|
15
|
+
* `logger` receives this transport's own failures. `createLogger` passes
|
|
16
|
+
* one that skips this transport (a report about a failing webhook must not
|
|
17
|
+
* be posted to that webhook); standalone use falls back to the dev console.
|
|
18
|
+
*/
|
|
19
|
+
constructor(config: WebhookConfig, logger?: InternalLogger);
|
|
10
20
|
send(entries: LogEntry[]): Promise<void>;
|
|
11
21
|
flush(): Promise<void>;
|
|
12
22
|
destroy(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/transports/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/transports/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAIrE,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,IAAI,aAAY;IAEzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAG2B;IAElD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IAGpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IAEjD;;;;OAIG;gBACS,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,cAAc;IAmBpD,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,SAAS;YAOH,UAAU;YA8BV,WAAW;CAwC1B"}
|