marsrs-react-native-xlog 0.1.0-alpha.2 → 0.1.0-alpha.4
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/LICENSE +21 -0
- package/README.md +12 -8
- package/android/build.gradle.kts +1 -1
- package/android/src/main/kotlin/io/github/orangeboychen/marsrs/xlog/reactnative/XlogModule.kt +248 -43
- package/ios/Frameworks/marsrs-xlog.xcframework/Info.plist +83 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/ios-arm64/Headers/mars_xlog.h +242 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/ios-arm64/Headers/module.modulemap +4 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/ios-arm64/libmars_ffi.a +0 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/ios-arm64_x86_64-simulator/Headers/mars_xlog.h +242 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap +4 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/ios-arm64_x86_64-simulator/libmars_ffi.a +0 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/watchos-arm64-simulator/Headers/mars_xlog.h +242 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/watchos-arm64-simulator/Headers/module.modulemap +4 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/watchos-arm64-simulator/libmars_ffi.a +0 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/watchos-arm64_arm64_32/Headers/mars_xlog.h +242 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/watchos-arm64_arm64_32/Headers/module.modulemap +4 -0
- package/ios/Frameworks/marsrs-xlog.xcframework/watchos-arm64_arm64_32/libmars_ffi.a +0 -0
- package/ios/Xlog.mm +2 -2
- package/ios/Xlog.swift +263 -37
- package/ios/include/mars_xlog.h +242 -0
- package/package.json +1 -1
- package/src/NativeXlog.ts +28 -3
- package/src/index.ts +143 -20
- package/android/.gradle/9.2.0/checksums/checksums.lock +0 -0
- package/android/.gradle/9.2.0/fileChanges/last-build.bin +0 -0
- package/android/.gradle/9.2.0/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/9.2.0/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/9.2.0/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
package/src/NativeXlog.ts
CHANGED
|
@@ -37,8 +37,32 @@ export interface Spec extends TurboModule {
|
|
|
37
37
|
* holds. */
|
|
38
38
|
getLevel(namePrefix: string): number;
|
|
39
39
|
|
|
40
|
-
/** `
|
|
41
|
-
|
|
40
|
+
/** `mars_xlog_request_flush_instance`: tells the writer thread it may take what is in
|
|
41
|
+
* the cache to the file, and returns at once — nothing waits, and nothing is
|
|
42
|
+
* in the file because this returned. */
|
|
43
|
+
requestFlush(namePrefix: string): void;
|
|
44
|
+
|
|
45
|
+
/** `mars_xlog_flush_now_instance`: the drain is the calling thread's, so the cache
|
|
46
|
+
* is in the log file, and the file's buffer is the OS's, when this returns. */
|
|
47
|
+
flushNow(namePrefix: string): void;
|
|
48
|
+
|
|
49
|
+
/** `flushNow` on a thread of the module's own, and a `Promise` settled when
|
|
50
|
+
* the drain is over: the one of the three that leaves the JS thread alone.
|
|
51
|
+
* A `Promise<void>` and not a `Promise` of a value — there is nothing to
|
|
52
|
+
* answer, only a drain to be over. */
|
|
53
|
+
flush(namePrefix: string): Promise<void>;
|
|
54
|
+
|
|
55
|
+
/** `mars_xlog_current_log_path_instance`: the directory this appender writes
|
|
56
|
+
* its files into, or `undefined` once it is closed. */
|
|
57
|
+
currentLogPath(namePrefix: string): string | undefined;
|
|
58
|
+
|
|
59
|
+
/** `mars_xlog_getfilepath_from_timespan_instance`: the day's files that are
|
|
60
|
+
* there — `0` is today, `1` is yesterday. */
|
|
61
|
+
logFiles(namePrefix: string, daysAgo: number): string[];
|
|
62
|
+
|
|
63
|
+
/** `mars_xlog_make_logfile_name_instance`: the day's names, whether or not the
|
|
64
|
+
* files are there yet. */
|
|
65
|
+
logFileNames(namePrefix: string, daysAgo: number): string[];
|
|
42
66
|
|
|
43
67
|
/** `mars_xlog_set_level_instance`. */
|
|
44
68
|
setLevel(namePrefix: string, level: number): void;
|
|
@@ -56,7 +80,8 @@ export interface Spec extends TurboModule {
|
|
|
56
80
|
/** `mars_xlog_set_max_alive_duration_instance`. */
|
|
57
81
|
setMaxAliveTime(namePrefix: string, seconds: number): void;
|
|
58
82
|
|
|
59
|
-
/**
|
|
83
|
+
/** Releases the appender `open` made: by handle on iOS, by prefix on
|
|
84
|
+
* Android. */
|
|
60
85
|
close(namePrefix: string): void;
|
|
61
86
|
}
|
|
62
87
|
|
package/src/index.ts
CHANGED
|
@@ -37,7 +37,8 @@ export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];
|
|
|
37
37
|
/** `TAppenderMode`: whether a record reaches the file before the call returns. */
|
|
38
38
|
export const AppenderMode = {
|
|
39
39
|
/** The record goes into the memory-mapped cache and a writer thread takes it
|
|
40
|
-
* to the file — what the C++ opens with, and what `
|
|
40
|
+
* to the file — what the C++ opens with, and what `flushNow()` and
|
|
41
|
+
* `await flush()` drain. */
|
|
41
42
|
async: 0,
|
|
42
43
|
/** The record is in the file before the call returns, at a write and a lock
|
|
43
44
|
* per line. */
|
|
@@ -62,15 +63,16 @@ export type CompressMode = (typeof CompressMode)[keyof typeof CompressMode];
|
|
|
62
63
|
* gives it: what an `Xlog` is opened with.
|
|
63
64
|
*
|
|
64
65
|
* Every field has the default the C++'s own `XLogConfig` carries, so the one an
|
|
65
|
-
* app has to give is `logDir` — the appender
|
|
66
|
-
*
|
|
66
|
+
* app has to give is `logDir` — the C ABI opens no appender
|
|
67
|
+
* without it. */
|
|
67
68
|
export interface XlogConfig {
|
|
68
69
|
/** Where the log files are written; created if it is missing, and the one
|
|
69
70
|
* field with no default. */
|
|
70
71
|
logDir: string;
|
|
71
72
|
/** What every log file starts with (`marsrs_20260927.xlog`), and the name the
|
|
72
73
|
* appender is known by — an app that writes through two of them gives them
|
|
73
|
-
* two. `xlog` when left out
|
|
74
|
+
* two. `xlog` when left out, when handed over empty, and when what was
|
|
75
|
+
* handed over is nothing but whitespace. */
|
|
74
76
|
namePrefix?: string;
|
|
75
77
|
/** The level the appender is opened at; `info` when left out. */
|
|
76
78
|
level?: LogLevel;
|
|
@@ -89,9 +91,29 @@ export interface XlogConfig {
|
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
/** `XlogConfig.namePrefix` of the Kotlin and of the Swift: what an appender is
|
|
92
|
-
* opened with when a caller gives none
|
|
94
|
+
* opened with when a caller gives none — and when it gives a blank one, which
|
|
95
|
+
* the two halves do not agree about: the Kotlin answers its own default for a
|
|
96
|
+
* prefix of nothing but whitespace, and the Swift opens that prefix as it is.
|
|
97
|
+
* Naming the default here is what keeps the `Xlog` an app holds and the
|
|
98
|
+
* appender the halves hold the same one, whichever half it is — a prefix of
|
|
99
|
+
* spaces that one half substituted and the other did not is an appender this
|
|
100
|
+
* `Xlog` cannot ask about afterwards. */
|
|
93
101
|
const DEFAULT_NAME_PREFIX = 'xlog';
|
|
94
102
|
|
|
103
|
+
/** The shortest lifetime of a file the appender takes, in seconds: one below it
|
|
104
|
+
* — `0` among them — is refused, and the appender keeps the lifetime it had.
|
|
105
|
+
* `MIN_LOG_ALIVE_TIME` of the Rust, which is the one place the number is
|
|
106
|
+
* written down. */
|
|
107
|
+
const MIN_ALIVE_TIME_SECONDS = 86400;
|
|
108
|
+
|
|
109
|
+
/** The prefix an appender opened with `config` is known by: `namePrefix`, or
|
|
110
|
+
* [DEFAULT_NAME_PREFIX] when it is missing or blank. What is handed to the two
|
|
111
|
+
* halves is this and not the string the caller gave, so the two of them agree
|
|
112
|
+
* on one name for one appender. */
|
|
113
|
+
function namePrefixOf(config: XlogConfig): string {
|
|
114
|
+
return config.namePrefix?.trim() || DEFAULT_NAME_PREFIX;
|
|
115
|
+
}
|
|
116
|
+
|
|
95
117
|
/** The appender of every `namePrefix` `Xlog.open` has opened and `close` has
|
|
96
118
|
* not closed, by the prefix: what makes two `Xlog.open`s of one prefix one
|
|
97
119
|
* appender, which the native side already is — one module, one appender per
|
|
@@ -125,7 +147,7 @@ export class Xlog {
|
|
|
125
147
|
private open = true;
|
|
126
148
|
|
|
127
149
|
private constructor(config: XlogConfig) {
|
|
128
|
-
this.namePrefix = config
|
|
150
|
+
this.namePrefix = namePrefixOf(config);
|
|
129
151
|
this.currentLevel = config.level ?? LogLevel.info;
|
|
130
152
|
this.currentMode = config.mode ?? AppenderMode.async;
|
|
131
153
|
}
|
|
@@ -138,16 +160,25 @@ export class Xlog {
|
|
|
138
160
|
* second one over the first would be a handle nothing releases. So two calls
|
|
139
161
|
* of one prefix are one `Xlog`, and `close` on it is `close` on both.
|
|
140
162
|
*
|
|
163
|
+
* The native side is asked either way, and not only the first time: one
|
|
164
|
+
* appender per prefix is one for the whole app, so one the app closed
|
|
165
|
+
* through its own Kotlin or Swift is gone from under this map, and asking
|
|
166
|
+
* again is what opens it again.
|
|
167
|
+
*
|
|
141
168
|
* Throws when the appender would not take the configuration — an empty
|
|
142
|
-
* `logDir
|
|
169
|
+
* `logDir`, or a directory it cannot write to. */
|
|
143
170
|
static open(config: XlogConfig): Xlog {
|
|
144
|
-
const namePrefix = config
|
|
171
|
+
const namePrefix = namePrefixOf(config);
|
|
145
172
|
const alreadyOpen = openAppenders.get(namePrefix);
|
|
146
|
-
|
|
173
|
+
// The appender of a prefix is one for the whole process, so one another
|
|
174
|
+
// part of the app closed is gone from under this map: the open is asked
|
|
175
|
+
// again instead of trusted, and what it answers is the appender that is
|
|
176
|
+
// there now — the same one when nothing closed it.
|
|
177
|
+
if (alreadyOpen && NativeXlog.open({ ...config, namePrefix })) {
|
|
147
178
|
return alreadyOpen;
|
|
148
179
|
}
|
|
149
180
|
const xlog = new Xlog(config);
|
|
150
|
-
if (!NativeXlog.open(config)) {
|
|
181
|
+
if (!NativeXlog.open({ ...config, namePrefix })) {
|
|
151
182
|
throw new Error(
|
|
152
183
|
`marsrs-react-native-xlog: the appender of '${xlog.namePrefix}' refused ${config.logDir}`
|
|
153
184
|
);
|
|
@@ -219,11 +250,21 @@ export class Xlog {
|
|
|
219
250
|
return;
|
|
220
251
|
}
|
|
221
252
|
NativeXlog.setMaxFileSize(this.namePrefix, bytes);
|
|
222
|
-
|
|
253
|
+
// Mirrored once it is a size the native side took, and not before: that
|
|
254
|
+
// side reads a `number` as an `unsigned long long`, and a negative one or
|
|
255
|
+
// a `NaN` is not one it hands over. `0` is a size and not an absence —
|
|
256
|
+
// "never split" — so it is mirrored like any other.
|
|
257
|
+
if (Number.isFinite(bytes) && bytes >= 0) {
|
|
258
|
+
this.currentMaxFileSize = bytes;
|
|
259
|
+
}
|
|
223
260
|
}
|
|
224
261
|
|
|
225
|
-
/** How
|
|
226
|
-
*
|
|
262
|
+
/** How many seconds a log file of this appender is kept before the sweep
|
|
263
|
+
* deletes it — and not how long one is written to, which is what the day's
|
|
264
|
+
* turn and [maxFileSizeBytes] decide: `0` is the lifetime an appender
|
|
265
|
+
* opened with none keeps, the C++'s own ten days, and one below a day is a
|
|
266
|
+
* lifetime the appender refuses, so what this answers is the one it has and
|
|
267
|
+
* not the last one it was asked for. */
|
|
227
268
|
get maxAliveTimeSeconds(): number {
|
|
228
269
|
return this.currentMaxAliveTime;
|
|
229
270
|
}
|
|
@@ -233,7 +274,12 @@ export class Xlog {
|
|
|
233
274
|
return;
|
|
234
275
|
}
|
|
235
276
|
NativeXlog.setMaxAliveTime(this.namePrefix, seconds);
|
|
236
|
-
|
|
277
|
+
// Mirrored once it is a lifetime the appender took, and not before: a day
|
|
278
|
+
// is the shortest one it takes, and `0` is below it, so a mirror that took
|
|
279
|
+
// the number would answer a lifetime no appender is writing under.
|
|
280
|
+
if (seconds >= MIN_ALIVE_TIME_SECONDS) {
|
|
281
|
+
this.currentMaxAliveTime = seconds;
|
|
282
|
+
}
|
|
237
283
|
}
|
|
238
284
|
|
|
239
285
|
/** Whether a record at `level` is written: the appender's own answer, and the
|
|
@@ -288,17 +334,94 @@ export class Xlog {
|
|
|
288
334
|
this.log(LogLevel.fatal, tag, message);
|
|
289
335
|
}
|
|
290
336
|
|
|
291
|
-
/** `
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
|
|
337
|
+
/** `mars_xlog_request_flush_instance`: tells the writer thread it may take what is in
|
|
338
|
+
* the cache to the file, and returns at once. Nothing waits, and nothing is
|
|
339
|
+
* in the file because this returned — a record still in the cache is in a file
|
|
340
|
+
* the kernel holds, so one whose process dies keeps it. What it is for is a
|
|
341
|
+
* drain an app wants soon and does not want to wait for; before the file is
|
|
342
|
+
* read, uploaded or left behind, it is `flushNow()` or `await flush()`
|
|
343
|
+
* that an app wants and not this. */
|
|
344
|
+
requestFlush(): void {
|
|
345
|
+
if (!this.open) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
NativeXlog.requestFlush(this.namePrefix);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** `mars_xlog_flush_now_instance`: the drain is the calling thread's, so the
|
|
352
|
+
* records are in the log file — handed to the OS, and not left in the file's
|
|
353
|
+
* own `FILE*` — when it returns, and what that costs is the time the drain
|
|
354
|
+
* takes, on the thread that asked for it. It answers no `Promise` and takes
|
|
355
|
+
* no `await`, which is the point of it: the line after the call is a line
|
|
356
|
+
* that runs after the drain, and there is nothing to get wrong in between. */
|
|
357
|
+
flushNow(): void {
|
|
358
|
+
if (!this.open) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
NativeXlog.flushNow(this.namePrefix);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** `flushNow()` off the JS thread: the same drain, on a thread of the
|
|
365
|
+
* module's own, and a `Promise` that is settled when it is over. The records
|
|
366
|
+
* are on disk when it resolves, and what waited for them is that thread and
|
|
367
|
+
* not the one the app runs JS on — the call to make before reading or
|
|
368
|
+
* uploading a log file whose drain is longer than the app can sit through. */
|
|
369
|
+
async flush(): Promise<void> {
|
|
295
370
|
if (!this.open) {
|
|
296
371
|
return;
|
|
297
372
|
}
|
|
298
|
-
NativeXlog.flush(this.namePrefix
|
|
373
|
+
await NativeXlog.flush(this.namePrefix);
|
|
299
374
|
}
|
|
300
375
|
|
|
301
|
-
/** `
|
|
376
|
+
/** `mars_xlog_current_log_path_instance`: the directory this appender writes
|
|
377
|
+
* its files into, or `undefined` once it is closed.
|
|
378
|
+
*
|
|
379
|
+
* A directory and not a file, because that is what the C++'s
|
|
380
|
+
* `GetCurrentLogPath` answers, and there is no "not yet" state: an open
|
|
381
|
+
* appender has a directory from the moment it is opened. What an app that
|
|
382
|
+
* uploads a whole day asks for is [`logFiles`]. */
|
|
383
|
+
get currentLogPath(): string | undefined {
|
|
384
|
+
if (!this.open) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
return NativeXlog.currentLogPath(this.namePrefix);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** `mars_xlog_getfilepath_from_timespan_instance`: the log files of `daysAgo`
|
|
391
|
+
* days ago that are *there* — what an app that uploads yesterday's opens. `[]`
|
|
392
|
+
* when the directory holds none of that day's. `0` is today, `1` is yesterday,
|
|
393
|
+
* and so on.
|
|
394
|
+
*
|
|
395
|
+
* This is a day of files and not the directory they are in: what
|
|
396
|
+
* `currentLogPath` answers is that, and this is this appender's own prefix and
|
|
397
|
+
* directory. */
|
|
398
|
+
logFiles(daysAgo: number): string[] {
|
|
399
|
+
if (!this.open) {
|
|
400
|
+
return [];
|
|
401
|
+
}
|
|
402
|
+
return NativeXlog.logFiles(this.namePrefix, daysAgo);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** `mars_xlog_make_logfile_name_instance`: the paths of the log files of
|
|
406
|
+
* `daysAgo` days ago whether or not they are *there yet* — the name an app
|
|
407
|
+
* that is about to write, or that is naming a file to someone else, asks for.
|
|
408
|
+
*
|
|
409
|
+
* A day's answer can be two where [`logFiles`] answers one, when a cache dir
|
|
410
|
+
* is given and the file is there. */
|
|
411
|
+
logFileNames(daysAgo: number): string[] {
|
|
412
|
+
if (!this.open) {
|
|
413
|
+
return [];
|
|
414
|
+
}
|
|
415
|
+
return NativeXlog.logFileNames(this.namePrefix, daysAgo);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** Closes the appender `Xlog.open` made. `mars_xlog_release_instance_of` on
|
|
419
|
+
* iOS, which is given the handle beside the prefix — a release by prefix
|
|
420
|
+
* alone closes whichever appender the prefix answers at that moment — and
|
|
421
|
+
* `mars_xlog_release_instance` on Android, which releases the prefix and is
|
|
422
|
+
* asked once per `Xlog`, by whichever takes its handle out of the table
|
|
423
|
+
* first.
|
|
424
|
+
*
|
|
302
425
|
* Nothing is closed twice: an `Xlog` that is already closed answers `false`
|
|
303
426
|
* from `isOpen` and drops what it is asked to write — and an appender two
|
|
304
427
|
* names hold is closed for both, because `Xlog.open` gave them one `Xlog`. */
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
File without changes
|