marsrs-react-native-xlog 0.1.0-alpha.3 → 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/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 +8 -8
- package/ios/Frameworks/marsrs-xlog.xcframework/ios-arm64/Headers/mars_xlog.h +98 -121
- 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 +98 -121
- 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 +98 -121
- 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 +98 -121
- 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 +98 -121
- package/package.json +1 -1
- package/src/NativeXlog.ts +28 -3
- package/src/index.ts +143 -20
package/ios/include/mars_xlog.h
CHANGED
|
@@ -27,9 +27,13 @@
|
|
|
27
27
|
* caller can include it without running cbindgen. It is kept in sync with
|
|
28
28
|
* `src/abi.rs` by `tests/header_sync.rs`.
|
|
29
29
|
*
|
|
30
|
-
* Threading: every symbol may be called from any thread.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* Threading: every symbol may be called from any thread. The setters and the
|
|
31
|
+
* instance lifecycle mutate shared state (exactly like the C++ globals) and
|
|
32
|
+
* should be called from one place during start-up / shut-down. What no symbol
|
|
33
|
+
* here does is install a process-wide appender: an app opens an instance with
|
|
34
|
+
* `mars_xlog_new_instance` and writes through the handle it answers. Handle
|
|
35
|
+
* `0` names no appender at all — it is what an open that failed answers, and
|
|
36
|
+
* every symbol asked of it is a no-op.
|
|
33
37
|
*
|
|
34
38
|
* Panics: no Rust panic ever crosses this boundary. Every entry point is
|
|
35
39
|
* wrapped in `catch_unwind`; a panic is reported as `MARS_XLOG_ERR_PANIC` (or
|
|
@@ -39,6 +43,17 @@
|
|
|
39
43
|
#ifndef MARS_XLOG_H_
|
|
40
44
|
#define MARS_XLOG_H_
|
|
41
45
|
|
|
46
|
+
/* The symbols this header declares are the ones `#[no_mangle]` exports: plain
|
|
47
|
+
* C names, and a C++ translation unit that includes this and calls
|
|
48
|
+
* `mars_xlog_new_instance()` asks its linker for a mangled one that does not
|
|
49
|
+
* exist.
|
|
50
|
+
* `mars_stn.h` and `mars_sdt.h` carry the same guard, and `mars_xlog.hpp` —
|
|
51
|
+
* the C++ face of this seam — includes this one, so a C++ caller asks for the
|
|
52
|
+
* same names whichever of the two it takes. */
|
|
53
|
+
#ifdef __cplusplus
|
|
54
|
+
extern "C" {
|
|
55
|
+
#endif
|
|
56
|
+
|
|
42
57
|
/* --- enums ------------------------------------------------------------- */
|
|
43
58
|
|
|
44
59
|
/** Mirrors `mars::xlog::TAppenderMode` (appender.h). */
|
|
@@ -60,8 +75,14 @@ typedef enum {
|
|
|
60
75
|
/** Additional level understood only by the filter: disables every record. */
|
|
61
76
|
#define MARS_LEVEL_NONE 6
|
|
62
77
|
|
|
63
|
-
/* --- return codes -------------------------------------------------------
|
|
64
|
-
|
|
78
|
+
/* --- return codes -------------------------------------------------------
|
|
79
|
+
*
|
|
80
|
+
* The five from `NULL_CONFIG` to `APPENDER` are the reasons an open may be
|
|
81
|
+
* refused, and no symbol hands them out: `mars_xlog_new_instance` answers the
|
|
82
|
+
* handle `0` for every one of them, there being no room in a handle for a
|
|
83
|
+
* negative code. They are here for the caller that wants to name the reason in
|
|
84
|
+
* its own diagnostic, not to switch on an answer.
|
|
85
|
+
*/
|
|
65
86
|
#define MARS_XLOG_OK 0
|
|
66
87
|
#define MARS_XLOG_ERR_NULL_CONFIG (-1) /* `config` was NULL */
|
|
67
88
|
#define MARS_XLOG_ERR_BAD_MODE (-2) /* `mode` outside MarsAppenderMode */
|
|
@@ -73,17 +94,6 @@ typedef enum {
|
|
|
73
94
|
#define MARS_XLOG_ERR_NO_PATH (-8) /* no current log file is open */
|
|
74
95
|
#define MARS_XLOG_ERR_PANIC (-99) /* a Rust panic was caught at the boundary */
|
|
75
96
|
|
|
76
|
-
/* --- `mars_xlog_oneshot_flush` result: mars::xlog::TFileIOAction --------- */
|
|
77
|
-
|
|
78
|
-
#define MARS_XLOG_ACTION_NONE 0
|
|
79
|
-
#define MARS_XLOG_ACTION_SUCCESS 1
|
|
80
|
-
#define MARS_XLOG_ACTION_UNNECESSARY 2 /* there was nothing to flush */
|
|
81
|
-
#define MARS_XLOG_ACTION_OPEN_FAILED 3
|
|
82
|
-
#define MARS_XLOG_ACTION_READ_FAILED 4
|
|
83
|
-
#define MARS_XLOG_ACTION_WRITE_FAILED 5
|
|
84
|
-
#define MARS_XLOG_ACTION_CLOSE_FAILED 6
|
|
85
|
-
#define MARS_XLOG_ACTION_REMOVE_FAILED 7
|
|
86
|
-
|
|
87
97
|
/* --- config ------------------------------------------------------------- */
|
|
88
98
|
|
|
89
99
|
/**
|
|
@@ -95,7 +105,7 @@ typedef enum {
|
|
|
95
105
|
typedef struct {
|
|
96
106
|
int mode; /* MarsAppenderMode; default when unset: Async */
|
|
97
107
|
const char* log_dir; /* mandatory, directory is created if missing */
|
|
98
|
-
const char* name_prefix; /* used verbatim; no default
|
|
108
|
+
const char* name_prefix; /* used verbatim; no default; "" is refused */
|
|
99
109
|
const char* pub_key; /* NULL / "" => logs are written unencrypted */
|
|
100
110
|
int compress_mode; /* MarsCompressMode; default when unset: Zlib */
|
|
101
111
|
int compress_level; /* <= 0 => keep the appender default (6) */
|
|
@@ -103,79 +113,44 @@ typedef struct {
|
|
|
103
113
|
int cache_days; /* < 0 => 0; 0 => keep every file */
|
|
104
114
|
} MarsXLogConfig;
|
|
105
115
|
|
|
106
|
-
/* --- lifecycle ---------------------------------------------------------- */
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Replaces `mars::xlog::appender_open(const XLogConfig&)`.
|
|
110
|
-
*
|
|
111
|
-
* @return MARS_XLOG_OK (0) on success, or a negative MARS_XLOG_ERR_* code.
|
|
112
|
-
*/
|
|
113
|
-
int mars_xlog_open(const MarsXLogConfig* config);
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Replaces `mars::xlog::XloggerWrite(...)` / `appender_write`. The record is
|
|
117
|
-
* dropped (cheaply, before any formatting) when `level` is below the level set
|
|
118
|
-
* by `mars_xlog_set_level`.
|
|
119
|
-
*
|
|
120
|
-
* `tag`, `filename`, `func_name` and `message` may be NULL; NULL and invalid
|
|
121
|
-
* UTF-8 are treated as an empty string.
|
|
122
|
-
*/
|
|
123
|
-
void mars_xlog_write(int level,
|
|
124
|
-
const char* tag,
|
|
125
|
-
const char* filename,
|
|
126
|
-
const char* func_name,
|
|
127
|
-
int line,
|
|
128
|
-
const char* message);
|
|
129
|
-
|
|
130
|
-
/** Replaces `mars::xlog::appender_flush()` — signals the writer thread. */
|
|
131
|
-
void mars_xlog_flush(void);
|
|
132
|
-
|
|
133
|
-
/** Replaces `mars::xlog::appender_flush_sync()` — flushes and waits. */
|
|
134
|
-
void mars_xlog_flush_sync(void);
|
|
135
|
-
|
|
136
|
-
/** Replaces `mars::xlog::appender_close()`. */
|
|
137
|
-
void mars_xlog_close(void);
|
|
138
|
-
|
|
139
|
-
/** Replaces `xlogger_SetLevel()`; `level` < 0 clamps to Verbose. */
|
|
140
|
-
void mars_xlog_set_level(int level);
|
|
141
|
-
|
|
142
|
-
/** Replaces `appender_set_console_log(bool)`; `open` != 0 means on. */
|
|
143
|
-
void mars_xlog_set_console_log(int open);
|
|
144
|
-
|
|
145
|
-
/** Replaces `appender_set_max_file_size(uint64_t)`; 0 means "do not split". */
|
|
146
|
-
void mars_xlog_set_max_file_size(unsigned long long bytes);
|
|
147
|
-
|
|
148
|
-
/** Replaces `appender_set_max_alive_duration(long)`; negative clamps to 0. */
|
|
149
|
-
void mars_xlog_set_max_alive_duration(long long seconds);
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Replaces `appender_get_current_log_path(char*, unsigned int)`.
|
|
153
|
-
*
|
|
154
|
-
* Writes the NUL-terminated path of the log file currently being appended to
|
|
155
|
-
* into `out` (which must hold at least `len` bytes).
|
|
156
|
-
*
|
|
157
|
-
* @return the number of bytes written excluding the terminating NUL, or a
|
|
158
|
-
* negative MARS_XLOG_ERR_* code (`MARS_XLOG_ERR_NULL_OUT`,
|
|
159
|
-
* `MARS_XLOG_ERR_NO_SPACE`, `MARS_XLOG_ERR_NO_PATH`).
|
|
160
|
-
*/
|
|
161
|
-
int mars_xlog_current_log_path(char* out, unsigned int len);
|
|
162
|
-
|
|
163
116
|
/* ---- logger instances (mars::xlog::NewXloggerInstance and friends) ----
|
|
164
117
|
*
|
|
165
118
|
* Each instance owns an appender: its own log directory, prefix, key, mode and
|
|
166
|
-
* cache file.
|
|
167
|
-
*
|
|
119
|
+
* cache file. A C caller that wants a logger opens one with
|
|
120
|
+
* mars_xlog_new_instance and holds the handle it answers.
|
|
121
|
+
*
|
|
122
|
+
* Every call that can be asked of an instance exists only in this spelling:
|
|
123
|
+
* the C++ has a free function for its process-wide appender and a
|
|
124
|
+
* handle-taking one beside it, and a second spelling of the same operation
|
|
125
|
+
* here would be two ways to say one thing. There is no process-wide appender
|
|
126
|
+
* to install, so there is no free-function spelling either — and handle `0`,
|
|
127
|
+
* which is what a failed open answers, names no appender: the level, the mode,
|
|
128
|
+
* the console, the two sizes and the drain asked of it do nothing, and the
|
|
129
|
+
* questions answer "no" or `MARS_XLOG_ERR_NO_PATH`.
|
|
168
130
|
*/
|
|
169
131
|
|
|
132
|
+
/* --- writing ------------------------------------------------------------- */
|
|
133
|
+
|
|
170
134
|
/* Returns the instance handle, or 0 on a null/invalid config. */
|
|
171
135
|
long long mars_xlog_new_instance(const MarsXLogConfig* config, int level);
|
|
172
136
|
|
|
173
137
|
/* The handle registered for name_prefix, or 0 when there is none. */
|
|
174
138
|
long long mars_xlog_get_instance(const char* name_prefix);
|
|
175
139
|
|
|
176
|
-
/* Releases the instance and closes its appender.
|
|
140
|
+
/* Releases the instance and closes its appender. Prefer the handle-aware
|
|
141
|
+
* mars_xlog_release_instance_of() when the caller holds an instance handle. */
|
|
177
142
|
void mars_xlog_release_instance(const char* name_prefix);
|
|
178
143
|
|
|
144
|
+
/* The same, and only while name_prefix is still registered under `instance`.
|
|
145
|
+
*
|
|
146
|
+
* Releasing takes the prefix and not the handle, so asking
|
|
147
|
+
* mars_xlog_get_instance and then calling mars_xlog_release_instance is two
|
|
148
|
+
* answers and not one: an open of the same prefix that lands between the two is
|
|
149
|
+
* handed a handle of its own, and the release closes that one. This is the
|
|
150
|
+
* question and the release in one call, which is also what makes a second
|
|
151
|
+
* close of a prefix a no-op however many callers hold its handle. */
|
|
152
|
+
void mars_xlog_release_instance_of(const char* name_prefix, long long instance);
|
|
153
|
+
|
|
179
154
|
/* Writes through an instance; honours the instance's level. */
|
|
180
155
|
void mars_xlog_write_instance(long long instance,
|
|
181
156
|
int level,
|
|
@@ -191,22 +166,31 @@ int mars_xlog_is_enabled_for(long long instance, int level);
|
|
|
191
166
|
/* The instance's level, or -1 for an unknown handle. */
|
|
192
167
|
int mars_xlog_get_level(long long instance);
|
|
193
168
|
|
|
194
|
-
/* SetLevel for an instance
|
|
169
|
+
/* SetLevel for an instance: the level `mars_xlog_get_level` and
|
|
170
|
+
* `mars_xlog_is_enabled_for` answer, and the one a write through the same
|
|
171
|
+
* handle asks. A no-op for handle `0`, which is no instance at all. */
|
|
195
172
|
void mars_xlog_set_level_instance(long long instance, int level);
|
|
196
173
|
|
|
197
|
-
/* appender_setmode
|
|
198
|
-
void mars_xlog_set_mode(int mode);
|
|
199
|
-
|
|
200
|
-
/* SetAppenderMode for an instance. */
|
|
174
|
+
/* appender_setmode / SetAppenderMode for an instance. */
|
|
201
175
|
void mars_xlog_set_mode_instance(long long instance, int mode);
|
|
202
176
|
|
|
203
|
-
/* Drains an instance
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
177
|
+
/* Drains an instance, requested: the writer thread is told it may drain, and
|
|
178
|
+
* this returns at once. Nothing is in the file because this returned, and
|
|
179
|
+
* nothing answers when the drain is over. The C++'s name for this call is
|
|
180
|
+
* `appender_flush`, and "flush" there said which mode the appender was opened
|
|
181
|
+
* in and not what the call does to the thread it was called on: the port names
|
|
182
|
+
* the drain by what the caller gets back, and what this one gives is no answer
|
|
183
|
+
* at all — hence "request", which is the one thing the two drains that do
|
|
184
|
+
* answer, `mars_xlog_flush_now_instance` and the awaited `flush()` every
|
|
185
|
+
* platform carries, do give. */
|
|
186
|
+
void mars_xlog_request_flush_instance(long long instance);
|
|
187
|
+
|
|
188
|
+
/* Drains an instance on the calling thread, which is what
|
|
189
|
+
* `appender_flush_sync` is: its records are on the disk when this returns, and
|
|
190
|
+
* that is a promise the requested call above does not make. */
|
|
191
|
+
void mars_xlog_flush_now_instance(long long instance);
|
|
192
|
+
|
|
193
|
+
/* SetConsoleLogOpen for an instance. */
|
|
210
194
|
void mars_xlog_set_console_log_instance(long long instance, int open);
|
|
211
195
|
|
|
212
196
|
/* SetMaxFileSize for an instance; 0 means "do not split". */
|
|
@@ -215,51 +199,44 @@ void mars_xlog_set_max_file_size_instance(long long instance, unsigned long long
|
|
|
215
199
|
/* SetMaxAliveTime for an instance; negative clamps to 0. */
|
|
216
200
|
void mars_xlog_set_max_alive_duration_instance(long long instance, long long seconds);
|
|
217
201
|
|
|
218
|
-
/**
|
|
219
|
-
* Replaces `mars::xlog::appender_oneshot_flush()`: drains the
|
|
220
|
-
* `<prefix>[_<n>].mmap3` cache files another process left behind, without
|
|
221
|
-
* opening an appender. The ones a live writer of this process — or of any
|
|
222
|
-
* other — still holds are left alone, so it answers
|
|
223
|
-
* MARS_XLOG_ACTION_UNNECESSARY when there is nothing of a dead process's to
|
|
224
|
-
* recover.
|
|
225
|
-
*
|
|
226
|
-
* @return one of the MARS_XLOG_ACTION_* values (0..7), or a negative
|
|
227
|
-
* MARS_XLOG_ERR_* code when `config` is unusable.
|
|
228
|
-
*/
|
|
229
|
-
int mars_xlog_oneshot_flush(const MarsXLogConfig* config);
|
|
230
|
-
|
|
231
202
|
/**
|
|
232
203
|
* Replaces `mars::xlog::appender_make_logfile_name()`: the log file *name* for
|
|
233
204
|
* the day `timespan` days ago (0 = today), whether or not it exists yet.
|
|
234
205
|
*
|
|
235
206
|
* The C++ fills a `std::vector`; a C caller walks the same list with `index`,
|
|
236
|
-
* starting at 0 and stopping at MARS_XLOG_ERR_NO_PATH. `
|
|
237
|
-
*
|
|
207
|
+
* starting at 0 and stopping at MARS_XLOG_ERR_NO_PATH. `instance` is a handle
|
|
208
|
+
* from `mars_xlog_new_instance`; a handle no appender is open for answers
|
|
209
|
+
* MARS_XLOG_ERR_NO_PATH from the first index.
|
|
238
210
|
*
|
|
239
211
|
* @return the number of bytes written excluding the terminating NUL, or a
|
|
240
212
|
* negative MARS_XLOG_ERR_* code.
|
|
241
213
|
*/
|
|
242
|
-
int
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
unsigned int len);
|
|
214
|
+
int mars_xlog_make_logfile_name_instance(long long instance,
|
|
215
|
+
int timespan,
|
|
216
|
+
unsigned int index,
|
|
217
|
+
char* out,
|
|
218
|
+
unsigned int len);
|
|
248
219
|
|
|
249
220
|
/**
|
|
250
221
|
* Replaces `mars::xlog::appender_getfilepath_from_timespan()`: the log files
|
|
251
222
|
* that *exist* for the day `timespan` days ago. Same `index` protocol as
|
|
252
|
-
*
|
|
223
|
+
* mars_xlog_make_logfile_name_instance.
|
|
253
224
|
*/
|
|
254
|
-
int
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
225
|
+
int mars_xlog_getfilepath_from_timespan_instance(long long instance,
|
|
226
|
+
int timespan,
|
|
227
|
+
unsigned int index,
|
|
228
|
+
char* out,
|
|
229
|
+
unsigned int len);
|
|
230
|
+
|
|
231
|
+
/* The directory the appender of `instance` is writing its files to, or a negative
|
|
232
|
+
* MARS_XLOG_ERR_* code. A directory and not a file, which is what the C++'s
|
|
233
|
+
* `XloggerAppender::GetCurrentLogPath` answers — `sg_logdir`. A handle no
|
|
234
|
+
* appender is open for — `0` among them, since no symbol of this ABI installs
|
|
235
|
+
* a process-wide one — answers MARS_XLOG_ERR_NO_PATH. */
|
|
236
|
+
int mars_xlog_current_log_path_instance(long long instance, char* out, unsigned int len);
|
|
237
|
+
|
|
238
|
+
#ifdef __cplusplus
|
|
239
|
+
} /* extern "C" */
|
|
240
|
+
#endif
|
|
264
241
|
|
|
265
242
|
#endif /* MARS_XLOG_H_ */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "marsrs-react-native-xlog",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.4",
|
|
4
4
|
"description": "React Native module for xlog, the logging half of mars-rs: a Rust implementation of Tencent/mars that writes the .xlog the C++ implementation produced.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/orangeboyChen/mars-rs",
|
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`. */
|