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/ios/Xlog.swift
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The iOS half of `marsrs-react-native-xlog`: the
|
|
1
|
+
// The iOS half of `marsrs-react-native-xlog`: the sixteen methods of the `Xlog`
|
|
2
2
|
// native module, each of them a straight call of a `mars_xlog_*` symbol — the
|
|
3
3
|
// C ABI of `crates/marsrs-ffi`, in the `marsrs-xlog.xcframework` the pod carries.
|
|
4
4
|
//
|
|
@@ -50,8 +50,10 @@ internal final class Xlog: NSObject {
|
|
|
50
50
|
///
|
|
51
51
|
/// `false` is a configuration it refused — an empty `logDir` or
|
|
52
52
|
/// `namePrefix`, or a directory it cannot write to — and it is what the JS
|
|
53
|
-
/// caller turns into a throw rather than a handle
|
|
54
|
-
///
|
|
53
|
+
/// caller turns into a throw rather than a handle nothing was opened
|
|
54
|
+
/// for. `true` for a prefix that is already open says which appender the
|
|
55
|
+
/// prefix has, and not that the configuration was taken again: the C ABI
|
|
56
|
+
/// answers the appender that is already there.
|
|
55
57
|
@objc(open:)
|
|
56
58
|
internal func openAppender(_ config: [AnyHashable: Any]) -> Bool {
|
|
57
59
|
let logDir = string(config, "logDir")
|
|
@@ -61,14 +63,14 @@ internal final class Xlog: NSObject {
|
|
|
61
63
|
let namePrefix = string(config, "namePrefix").isEmpty
|
|
62
64
|
? Self.defaultNamePrefix
|
|
63
65
|
: string(config, "namePrefix")
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
// The C ABI is what keeps one appender per prefix: it answers the
|
|
67
|
+
// handle of the appender this prefix already has rather than opening a
|
|
68
|
+
// second one over its files, so asking again is how a prefix another
|
|
69
|
+
// part of the app closed is opened again — and the handle this stores
|
|
70
|
+
// is the one the registry answers, and not a dead one the last `open`
|
|
71
|
+
// left behind. `src/index.ts` keeps the same map, so the two names an
|
|
72
|
+
// app holds for one prefix are one appender, and `close` on either is
|
|
73
|
+
// `close` on both.
|
|
72
74
|
let level = int(config, "level", Int32(MarsLevelInfo.rawValue))
|
|
73
75
|
let mode = int(config, "mode", Int32(MarsAppenderAsync.rawValue))
|
|
74
76
|
let compressMode = int(config, "compressMode", Int32(MarsCompressZlib.rawValue))
|
|
@@ -102,67 +104,161 @@ internal final class Xlog: NSObject {
|
|
|
102
104
|
/// empty: there is no JS frame to name, and the C++ writes an empty one too.
|
|
103
105
|
@objc(log:level:tag:message:)
|
|
104
106
|
internal func log(_ namePrefix: String, level: Double, tag: String, message: String) {
|
|
105
|
-
guard let handle =
|
|
107
|
+
guard let handle = openHandle(of: namePrefix), let native = int32(level) else {
|
|
106
108
|
return
|
|
107
109
|
}
|
|
108
110
|
tag.withCString { cTag in
|
|
109
111
|
message.withCString { cMessage in
|
|
110
|
-
mars_xlog_write_instance(handle,
|
|
112
|
+
mars_xlog_write_instance(handle, native, cTag, nil, nil, 0, cMessage)
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
/// `mars_xlog_current_log_path_instance`: the directory this appender
|
|
118
|
+
/// writes its files into, or `nil` once it is closed.
|
|
119
|
+
///
|
|
120
|
+
/// A directory and not a file, because that is what the C++'s
|
|
121
|
+
/// `GetCurrentLogPath` answers; there is no "before the day's first record"
|
|
122
|
+
/// state, and the day's file is the question `logFiles` asks.
|
|
123
|
+
@objc(currentLogPath:)
|
|
124
|
+
internal func currentLogPath(of namePrefix: String) -> String? {
|
|
125
|
+
guard let handle = openHandle(of: namePrefix) else {
|
|
126
|
+
return nil
|
|
127
|
+
}
|
|
128
|
+
return path { out, len in
|
|
129
|
+
mars_xlog_current_log_path_instance(handle, out, len)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// `mars_xlog_getfilepath_from_timespan_instance`: the log files of
|
|
134
|
+
/// `daysAgo` days ago that are *there* — what an app that uploads
|
|
135
|
+
/// yesterday's opens. `0` is today, `1` is yesterday, and so on.
|
|
136
|
+
@objc(logFiles:daysAgo:)
|
|
137
|
+
internal func logFiles(of namePrefix: String, daysAgo: Double) -> [String] {
|
|
138
|
+
dayPaths(of: namePrefix, daysAgo: daysAgo) { handle, timespan, index, out, len in
|
|
139
|
+
mars_xlog_getfilepath_from_timespan_instance(handle, timespan, index, out, len)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// `mars_xlog_make_logfile_name_instance`: the paths of the log files of
|
|
144
|
+
/// `daysAgo` days ago, whether or not they are there yet.
|
|
145
|
+
@objc(logFileNames:daysAgo:)
|
|
146
|
+
internal func logFileNames(of namePrefix: String, daysAgo: Double) -> [String] {
|
|
147
|
+
dayPaths(of: namePrefix, daysAgo: daysAgo) { handle, timespan, index, out, len in
|
|
148
|
+
mars_xlog_make_logfile_name_instance(handle, timespan, index, out, len)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
115
152
|
/// `mars_xlog_is_enabled_for`: whether a record of the level would be
|
|
116
153
|
/// written, which an app asks before it builds a message that is expensive
|
|
117
154
|
/// to build.
|
|
118
155
|
@objc(isLoggable:level:)
|
|
119
156
|
internal func isLoggable(_ namePrefix: String, level: Double) -> Bool {
|
|
120
|
-
guard let handle =
|
|
157
|
+
guard let handle = openHandle(of: namePrefix), let native = int32(level) else {
|
|
121
158
|
return false
|
|
122
159
|
}
|
|
123
|
-
return mars_xlog_is_enabled_for(handle,
|
|
160
|
+
return mars_xlog_is_enabled_for(handle, native) != 0
|
|
124
161
|
}
|
|
125
162
|
|
|
126
163
|
/// `mars_xlog_get_level`: what the appender answers, and not what JS holds.
|
|
164
|
+
///
|
|
165
|
+
/// Asked of the handle [openHandle] answered, and not of the one [handles]
|
|
166
|
+
/// cached: a prefix is one appender to the C ABI, so an appender another
|
|
167
|
+
/// part of the app closed — a `MarsRSXlog.Xlog` of the same prefix — took
|
|
168
|
+
/// it out of the registry and left the handle cached here, and
|
|
169
|
+
/// `mars_xlog_get_level` answers `-1` for a handle it does not know.
|
|
127
170
|
@objc(getLevel:)
|
|
128
171
|
internal func level(of namePrefix: String) -> Double {
|
|
129
|
-
guard let handle =
|
|
172
|
+
guard let handle = openHandle(of: namePrefix) else {
|
|
130
173
|
return Double(MARS_LEVEL_NONE)
|
|
131
174
|
}
|
|
132
|
-
|
|
175
|
+
let native = mars_xlog_get_level(handle)
|
|
176
|
+
// A level `MarsLogLevel` does not carry — the `-1` above among them —
|
|
177
|
+
// is answered as `MARS_LEVEL_NONE` and not as it stands, which is the
|
|
178
|
+
// answer the Kotlin half of this module gives for the same numbers:
|
|
179
|
+
// `0`, `MarsLevelVerbose`, is the level that logs everything, so a
|
|
180
|
+
// level that is not one read as `0` is the opposite of what an
|
|
181
|
+
// appender that writes nothing was asked.
|
|
182
|
+
guard native >= 0, native <= MARS_LEVEL_NONE else {
|
|
183
|
+
return Double(MARS_LEVEL_NONE)
|
|
184
|
+
}
|
|
185
|
+
return Double(native)
|
|
133
186
|
}
|
|
134
187
|
|
|
135
|
-
/// `
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
188
|
+
/// `mars_xlog_request_flush_instance`: tells the writer thread it may take what is
|
|
189
|
+
/// in the cache to the log file, and returns at once — nothing waits, and
|
|
190
|
+
/// nothing is in the file because this returned. A record still in the cache
|
|
191
|
+
/// is in a file the kernel holds, so one whose process dies keeps it; what
|
|
192
|
+
/// an app wants before it reads the file or uploads it is `flushNow` or
|
|
193
|
+
/// `flush`.
|
|
194
|
+
@objc(requestFlush:)
|
|
195
|
+
internal func requestFlush(_ namePrefix: String) {
|
|
196
|
+
guard let handle = openHandle(of: namePrefix) else {
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
mars_xlog_request_flush_instance(handle)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// `mars_xlog_flush_now_instance`: the drain is this thread's, so the records are
|
|
203
|
+
/// in the log file — handed to the OS, and not left in the file's own
|
|
204
|
+
/// `FILE*` — when it returns, and what it costs is the time the drain takes,
|
|
205
|
+
/// on the JS thread and nowhere else.
|
|
206
|
+
@objc(flushNow:)
|
|
207
|
+
internal func flushNow(_ namePrefix: String) {
|
|
208
|
+
guard let handle = openHandle(of: namePrefix) else {
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
mars_xlog_flush_now_instance(handle)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// `flushNow` off the JS thread: the same drain, on a queue of this
|
|
215
|
+
/// module's own, and `resolve` called when it is over.
|
|
216
|
+
///
|
|
217
|
+
/// The handle and not `self`: the closure runs on another thread.
|
|
218
|
+
/// `resolve` and `reject` are `RCTPromiseResolveBlock` and
|
|
219
|
+
/// `RCTPromiseRejectBlock`, spelled out because the bridging header shows
|
|
220
|
+
/// this file the C ABI and nothing else — a TurboModule method codegen
|
|
221
|
+
/// answers a `Promise` from is given the two blocks and resolves one of
|
|
222
|
+
/// them, and there is nothing here that can fail, so `reject` is never
|
|
223
|
+
/// called.
|
|
224
|
+
@objc(flush:resolve:reject:)
|
|
225
|
+
internal func flush(
|
|
226
|
+
_ namePrefix: String,
|
|
227
|
+
resolve: @escaping (Any?) -> Void,
|
|
228
|
+
reject: @escaping (String?, String?, Error?) -> Void
|
|
229
|
+
) {
|
|
230
|
+
guard let handle = openHandle(of: namePrefix) else {
|
|
231
|
+
resolve(nil)
|
|
139
232
|
return
|
|
140
233
|
}
|
|
141
|
-
|
|
234
|
+
Self.flushQueue.async {
|
|
235
|
+
mars_xlog_flush_now_instance(handle)
|
|
236
|
+
resolve(nil)
|
|
237
|
+
}
|
|
142
238
|
}
|
|
143
239
|
|
|
144
240
|
/// `mars_xlog_set_level_instance`.
|
|
145
241
|
@objc(setLevel:level:)
|
|
146
242
|
internal func setLevel(_ namePrefix: String, level: Double) {
|
|
147
|
-
guard let handle =
|
|
243
|
+
guard let handle = openHandle(of: namePrefix), let native = int32(level) else {
|
|
148
244
|
return
|
|
149
245
|
}
|
|
150
|
-
mars_xlog_set_level_instance(handle,
|
|
246
|
+
mars_xlog_set_level_instance(handle, native)
|
|
151
247
|
}
|
|
152
248
|
|
|
153
249
|
/// `mars_xlog_set_mode_instance`.
|
|
154
250
|
@objc(setMode:mode:)
|
|
155
251
|
internal func setMode(_ namePrefix: String, mode: Double) {
|
|
156
|
-
guard let handle =
|
|
252
|
+
guard let handle = openHandle(of: namePrefix), let native = int32(mode) else {
|
|
157
253
|
return
|
|
158
254
|
}
|
|
159
|
-
mars_xlog_set_mode_instance(handle,
|
|
255
|
+
mars_xlog_set_mode_instance(handle, native)
|
|
160
256
|
}
|
|
161
257
|
|
|
162
258
|
/// `mars_xlog_set_console_log_instance`.
|
|
163
259
|
@objc(setConsoleLogEnabled:enabled:)
|
|
164
260
|
internal func setConsoleLogEnabled(_ namePrefix: String, enabled: Bool) {
|
|
165
|
-
guard let handle =
|
|
261
|
+
guard let handle = openHandle(of: namePrefix) else {
|
|
166
262
|
return
|
|
167
263
|
}
|
|
168
264
|
mars_xlog_set_console_log_instance(handle, enabled ? 1 : 0)
|
|
@@ -173,36 +269,55 @@ internal final class Xlog: NSObject {
|
|
|
173
269
|
/// 2^53.
|
|
174
270
|
@objc(setMaxFileSize:bytes:)
|
|
175
271
|
internal func setMaxFileSize(_ namePrefix: String, bytes: Double) {
|
|
176
|
-
guard let handle =
|
|
272
|
+
guard let handle = openHandle(of: namePrefix), let native = uint64(bytes) else {
|
|
177
273
|
return
|
|
178
274
|
}
|
|
179
|
-
mars_xlog_set_max_file_size_instance(handle,
|
|
275
|
+
mars_xlog_set_max_file_size_instance(handle, native)
|
|
180
276
|
}
|
|
181
277
|
|
|
182
278
|
/// `mars_xlog_set_max_alive_duration_instance`.
|
|
183
279
|
@objc(setMaxAliveTime:seconds:)
|
|
184
280
|
internal func setMaxAliveTime(_ namePrefix: String, seconds: Double) {
|
|
185
|
-
guard let handle =
|
|
281
|
+
guard let handle = openHandle(of: namePrefix), let native = int64(seconds) else {
|
|
186
282
|
return
|
|
187
283
|
}
|
|
188
|
-
mars_xlog_set_max_alive_duration_instance(handle,
|
|
284
|
+
mars_xlog_set_max_alive_duration_instance(handle, native)
|
|
189
285
|
}
|
|
190
286
|
|
|
191
|
-
/// `
|
|
287
|
+
/// `mars_xlog_release_instance_of`: closes the appender `open` made.
|
|
288
|
+
///
|
|
289
|
+
/// The prefix is released only while the appender of it is still the one
|
|
290
|
+
/// `open` was answered with: a release is given a *prefix*, so one another
|
|
291
|
+
/// part of the app opened after this one was closed would be theirs and
|
|
292
|
+
/// would be closed by this. Naming the handle is what makes the question
|
|
293
|
+
/// and the release one call.
|
|
294
|
+
///
|
|
295
|
+
/// Off the JS thread, the way `flush` is: a method codegen answers no
|
|
296
|
+
/// promise from is a call made on the JS thread, and a release is a drain
|
|
297
|
+
/// of everything the appender still holds and a write of the banner that
|
|
298
|
+
/// ends the file. The handle is taken out of `handles` here and not in the
|
|
299
|
+
/// block, so a reopen of the prefix that lands while the drain is queued
|
|
300
|
+
/// is a reopen of an appender this close never held.
|
|
192
301
|
@objc(close:)
|
|
193
302
|
internal func close(_ namePrefix: String) {
|
|
194
|
-
guard handles.removeValue(forKey: namePrefix)
|
|
303
|
+
guard let handle = handles.removeValue(forKey: namePrefix) else {
|
|
195
304
|
return
|
|
196
305
|
}
|
|
197
|
-
|
|
306
|
+
Self.flushQueue.async {
|
|
307
|
+
namePrefix.withCString { prefix in
|
|
308
|
+
mars_xlog_release_instance_of(prefix, handle)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
198
311
|
}
|
|
199
312
|
|
|
200
313
|
/// Closes what `open` opened and `close` was not asked about: the module
|
|
201
314
|
/// goes away with the bridge, and an appender is a file its writer thread
|
|
202
315
|
/// holds open.
|
|
203
316
|
deinit {
|
|
204
|
-
for namePrefix in handles
|
|
205
|
-
namePrefix.withCString {
|
|
317
|
+
for (namePrefix, handle) in handles {
|
|
318
|
+
namePrefix.withCString { prefix in
|
|
319
|
+
mars_xlog_release_instance_of(prefix, handle)
|
|
320
|
+
}
|
|
206
321
|
}
|
|
207
322
|
handles.removeAll()
|
|
208
323
|
}
|
|
@@ -214,7 +329,12 @@ internal final class Xlog: NSObject {
|
|
|
214
329
|
/// What the C++ passes on, which is the appender's own 6.
|
|
215
330
|
private static let defaultCompressLevel: Int32 = 0
|
|
216
331
|
|
|
217
|
-
/// `
|
|
332
|
+
/// The queue `flush` drains on, and why it is one queue and not the JS
|
|
333
|
+
/// thread: a drain blocks the thread it runs on, and serial is what keeps
|
|
334
|
+
/// two drains of one appender from being two writers in one file.
|
|
335
|
+
private static let flushQueue = DispatchQueue(label: "io.github.orangeboychen.marsrs.reactnative.xlog.flush")
|
|
336
|
+
|
|
337
|
+
/// `0` keeps every cache file, which is what the C++'s default is.
|
|
218
338
|
private static let keepEveryFile: Int32 = 0
|
|
219
339
|
|
|
220
340
|
/// The four fields of `MarsXLogConfig` that are C strings, in the order the
|
|
@@ -227,6 +347,27 @@ internal final class Xlog: NSObject {
|
|
|
227
347
|
case cacheDir = 3
|
|
228
348
|
}
|
|
229
349
|
|
|
350
|
+
/// The handle [handles] holds for `namePrefix`, and `nil` when it is not
|
|
351
|
+
/// the appender of that prefix any more: the registry is asked, and the
|
|
352
|
+
/// handle it answers is the one that is open.
|
|
353
|
+
///
|
|
354
|
+
/// The registry and not the handle alone, which is the question the Swift
|
|
355
|
+
/// `Xlog` of the port asks in its own `openHandle`:
|
|
356
|
+
/// `mars_xlog_release_instance` releases the appender of a *prefix* and
|
|
357
|
+
/// not of a handle, so a handle whose appender something else closed is
|
|
358
|
+
/// still the number this dictionary holds, and every symbol of the C ABI
|
|
359
|
+
/// answers a handle it does not know — `mars_xlog_get_level` with `-1`,
|
|
360
|
+
/// which is a level of no `MarsLogLevel`.
|
|
361
|
+
private func openHandle(of namePrefix: String) -> Int64? {
|
|
362
|
+
guard let handle = handles[namePrefix] else {
|
|
363
|
+
return nil
|
|
364
|
+
}
|
|
365
|
+
let owns = namePrefix.withCString { prefix in
|
|
366
|
+
mars_xlog_get_instance(prefix) == handle
|
|
367
|
+
}
|
|
368
|
+
return owns ? handle : nil
|
|
369
|
+
}
|
|
370
|
+
|
|
230
371
|
/// Runs `body` with every one of `strings` as a C string, and frees the
|
|
231
372
|
/// copies it made: `MarsXLogConfig` holds pointers rather than copies, and a
|
|
232
373
|
/// Swift `String` has no C string of its own to hand out — `withCString` is
|
|
@@ -250,4 +391,89 @@ internal final class Xlog: NSObject {
|
|
|
250
391
|
private func int(_ config: [AnyHashable: Any], _ key: String, _ fallback: Int32) -> Int32 {
|
|
251
392
|
(config[key] as? NSNumber)?.int32Value ?? fallback
|
|
252
393
|
}
|
|
394
|
+
|
|
395
|
+
/// What the caller sent for a level or a mode, and `nil` when it is not one:
|
|
396
|
+
/// a `Double`, because JS has one number type, and `Int32(_:)` traps on a
|
|
397
|
+
/// `NaN`, an infinity and a number out of `Int32`'s range — a trap on the
|
|
398
|
+
/// JS thread is the app dying, where a level nobody asked for is a record
|
|
399
|
+
/// that is not written. What it answers for a number with a fraction in it
|
|
400
|
+
/// is the number without one, which is the same truncation the Kotlin half
|
|
401
|
+
/// of this module does.
|
|
402
|
+
private func int32(_ value: Double) -> Int32? {
|
|
403
|
+
guard value.isFinite, value >= Double(Int32.min), value <= Double(Int32.max) else {
|
|
404
|
+
return nil
|
|
405
|
+
}
|
|
406
|
+
return Int32(value)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/// What the caller sent for a file size, and `nil` when it is not one: a
|
|
410
|
+
/// `UInt64` a `NaN`, a negative number or one above `UInt64.max` is not,
|
|
411
|
+
/// and `UInt64(_:)` traps on all three.
|
|
412
|
+
private func uint64(_ value: Double) -> UInt64? {
|
|
413
|
+
guard value.isFinite, value >= 0, value < 18446744073709551616.0 else {
|
|
414
|
+
return nil
|
|
415
|
+
}
|
|
416
|
+
return UInt64(value)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/// What the caller sent for a file lifetime, and `nil` when it is not one:
|
|
420
|
+
/// the same trap in `Int64(_:)`, and `0` is the lifetime an appender opened
|
|
421
|
+
/// with none keeps — the C++'s own ten days, and not one a setter can ask
|
|
422
|
+
/// for, since a lifetime below a day is refused — so a lifetime of `NaN`
|
|
423
|
+
/// was one the app never asked for rather than one it was told had been
|
|
424
|
+
/// refused.
|
|
425
|
+
private func int64(_ value: Double) -> Int64? {
|
|
426
|
+
guard value.isFinite,
|
|
427
|
+
value >= -9223372036854775808.0, value < 9223372036854775808.0 else {
|
|
428
|
+
return nil
|
|
429
|
+
}
|
|
430
|
+
return Int64(value)
|
|
431
|
+
}
|
|
432
|
+
/// What `read` writes into the buffer it is handed, as a string; `nil` when
|
|
433
|
+
/// it wrote nothing — a negative code, or a path of no length.
|
|
434
|
+
///
|
|
435
|
+
/// A negative code is `nil` whatever it is, `MARS_XLOG_ERR_NO_SPACE` among
|
|
436
|
+
/// them: a path that does not fit 1024 bytes ends the walk the way the end
|
|
437
|
+
/// of the list does. A symbol that answers a length rather than a pointer
|
|
438
|
+
/// is the C ABI's way of saying the caller decides how much it can hold.
|
|
439
|
+
private func path(of read: (UnsafeMutablePointer<CChar>, UInt32) -> Int32) -> String? {
|
|
440
|
+
var buffer = [CChar](repeating: 0, count: 1024)
|
|
441
|
+
let written = read(&buffer, UInt32(buffer.count))
|
|
442
|
+
guard written > 0 else {
|
|
443
|
+
return nil
|
|
444
|
+
}
|
|
445
|
+
return String(cString: buffer)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/// The paths of one day, walked index by index until the symbol answers that
|
|
449
|
+
/// there is nothing at that index: the list the C++ fills a `std::vector`
|
|
450
|
+
/// with, asked one at a time.
|
|
451
|
+
///
|
|
452
|
+
/// `daysAgo` is a `Double` because JS has one number type, and it is read
|
|
453
|
+
/// with [`int32`] and not `Int32(_:)`: the latter traps on a value it
|
|
454
|
+
/// cannot represent — `NaN`, `Infinity`, a number out of `Int32`'s range
|
|
455
|
+
/// — and a trap on the JS thread is the app dying, where a day nobody
|
|
456
|
+
/// asked for is an empty list. [`int32`] truncates a fraction the way the
|
|
457
|
+
/// Kotlin half of this module does, so `1.5` days ago is yesterday on both
|
|
458
|
+
/// of them and not yesterday on one and nothing on the other.
|
|
459
|
+
private func dayPaths(
|
|
460
|
+
of namePrefix: String,
|
|
461
|
+
daysAgo: Double,
|
|
462
|
+
at symbol: (Int64, Int32, UInt32, UnsafeMutablePointer<CChar>, UInt32) -> Int32
|
|
463
|
+
) -> [String] {
|
|
464
|
+
guard let handle = openHandle(of: namePrefix), let timespan = int32(daysAgo) else {
|
|
465
|
+
return []
|
|
466
|
+
}
|
|
467
|
+
var walked: [String] = []
|
|
468
|
+
var index: UInt32 = 0
|
|
469
|
+
while let found = path(of: { out, len in
|
|
470
|
+
symbol(handle, timespan, index, out, len)
|
|
471
|
+
}) {
|
|
472
|
+
walked.append(found)
|
|
473
|
+
index += 1
|
|
474
|
+
}
|
|
475
|
+
return walked
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
|
|
253
479
|
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/* Tencent is pleased to support the open source community by making Mars available.
|
|
2
|
+
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the MIT License (the "License"); you may not use this file except in
|
|
5
|
+
* compliance with the License. You may obtain a copy of the License at
|
|
6
|
+
* http://opensource.org/licenses/MIT
|
|
7
|
+
*
|
|
8
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
|
9
|
+
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
|
10
|
+
* either express or implied. See the License for the specific language governing permissions and
|
|
11
|
+
* limitations under the License.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* mars_xlog.h — C ABI of the Rust xlog implementation (`marsrs-ffi`).
|
|
16
|
+
*
|
|
17
|
+
* This header is the drop-in seam for the existing C++ / JNI / ObjC layers of
|
|
18
|
+
* Mars. It mirrors the C++ surface of
|
|
19
|
+
*
|
|
20
|
+
* mars/xlog/appender.h (mars::xlog::appender_open / flush / close ...)
|
|
21
|
+
* mars/xlog/xlogger_interface.h (mars::xlog::XloggerWrite / SetLevel ...)
|
|
22
|
+
* mars/comm/xlogger/xloggerbase.h (TLogLevel)
|
|
23
|
+
*
|
|
24
|
+
* and is the C equivalent of the JNI bridge in mars/xlog/jni/Java2C_Xlog.cc.
|
|
25
|
+
*
|
|
26
|
+
* The header is hand-written and checked in next to the crate so that a C/C++
|
|
27
|
+
* caller can include it without running cbindgen. It is kept in sync with
|
|
28
|
+
* `src/abi.rs` by `tests/header_sync.rs`.
|
|
29
|
+
*
|
|
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.
|
|
37
|
+
*
|
|
38
|
+
* Panics: no Rust panic ever crosses this boundary. Every entry point is
|
|
39
|
+
* wrapped in `catch_unwind`; a panic is reported as `MARS_XLOG_ERR_PANIC` (or
|
|
40
|
+
* silently swallowed for the `void` symbols).
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
#ifndef MARS_XLOG_H_
|
|
44
|
+
#define MARS_XLOG_H_
|
|
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
|
+
|
|
57
|
+
/* --- enums ------------------------------------------------------------- */
|
|
58
|
+
|
|
59
|
+
/** Mirrors `mars::xlog::TAppenderMode` (appender.h). */
|
|
60
|
+
typedef enum { MarsAppenderAsync = 0, MarsAppenderSync = 1 } MarsAppenderMode;
|
|
61
|
+
|
|
62
|
+
/** Mirrors `mars::xlog::TCompressMode` (appender.h). */
|
|
63
|
+
typedef enum { MarsCompressZlib = 0, MarsCompressZstd = 1 } MarsCompressMode;
|
|
64
|
+
|
|
65
|
+
/** Mirrors `TLogLevel` (xloggerbase.h); `kLevelAll == kLevelVerbose == 0`. */
|
|
66
|
+
typedef enum {
|
|
67
|
+
MarsLevelVerbose = 0,
|
|
68
|
+
MarsLevelDebug = 1,
|
|
69
|
+
MarsLevelInfo = 2,
|
|
70
|
+
MarsLevelWarn = 3,
|
|
71
|
+
MarsLevelError = 4,
|
|
72
|
+
MarsLevelFatal = 5
|
|
73
|
+
} MarsLogLevel;
|
|
74
|
+
|
|
75
|
+
/** Additional level understood only by the filter: disables every record. */
|
|
76
|
+
#define MARS_LEVEL_NONE 6
|
|
77
|
+
|
|
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
|
+
*/
|
|
86
|
+
#define MARS_XLOG_OK 0
|
|
87
|
+
#define MARS_XLOG_ERR_NULL_CONFIG (-1) /* `config` was NULL */
|
|
88
|
+
#define MARS_XLOG_ERR_BAD_MODE (-2) /* `mode` outside MarsAppenderMode */
|
|
89
|
+
#define MARS_XLOG_ERR_BAD_COMPRESS (-3) /* `compress_mode` outside MarsCompressMode */
|
|
90
|
+
#define MARS_XLOG_ERR_EMPTY_LOG_DIR (-4) /* `log_dir` was NULL or "" */
|
|
91
|
+
#define MARS_XLOG_ERR_APPENDER (-5) /* the Rust appender refused the config */
|
|
92
|
+
#define MARS_XLOG_ERR_NULL_OUT (-6) /* `out` was NULL */
|
|
93
|
+
#define MARS_XLOG_ERR_NO_SPACE (-7) /* output buffer too small (0 or < need) */
|
|
94
|
+
#define MARS_XLOG_ERR_NO_PATH (-8) /* no current log file is open */
|
|
95
|
+
#define MARS_XLOG_ERR_PANIC (-99) /* a Rust panic was caught at the boundary */
|
|
96
|
+
|
|
97
|
+
/* --- config ------------------------------------------------------------- */
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Mirrors `mars::xlog::XLogConfig` (appender.h) with C strings instead of
|
|
101
|
+
* `std::string`. Every pointer must be NUL-terminated UTF-8 or NULL; NULL is
|
|
102
|
+
* treated as "empty" (see the field docs) except for `log_dir`, which is
|
|
103
|
+
* mandatory.
|
|
104
|
+
*/
|
|
105
|
+
typedef struct {
|
|
106
|
+
int mode; /* MarsAppenderMode; default when unset: Async */
|
|
107
|
+
const char* log_dir; /* mandatory, directory is created if missing */
|
|
108
|
+
const char* name_prefix; /* used verbatim; no default; "" is refused */
|
|
109
|
+
const char* pub_key; /* NULL / "" => logs are written unencrypted */
|
|
110
|
+
int compress_mode; /* MarsCompressMode; default when unset: Zlib */
|
|
111
|
+
int compress_level; /* <= 0 => keep the appender default (6) */
|
|
112
|
+
const char* cache_dir; /* NULL / "" => the mmap cache lives in log_dir */
|
|
113
|
+
int cache_days; /* < 0 => 0; 0 => keep every file */
|
|
114
|
+
} MarsXLogConfig;
|
|
115
|
+
|
|
116
|
+
/* ---- logger instances (mars::xlog::NewXloggerInstance and friends) ----
|
|
117
|
+
*
|
|
118
|
+
* Each instance owns an appender: its own log directory, prefix, key, mode and
|
|
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`.
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
/* --- writing ------------------------------------------------------------- */
|
|
133
|
+
|
|
134
|
+
/* Returns the instance handle, or 0 on a null/invalid config. */
|
|
135
|
+
long long mars_xlog_new_instance(const MarsXLogConfig* config, int level);
|
|
136
|
+
|
|
137
|
+
/* The handle registered for name_prefix, or 0 when there is none. */
|
|
138
|
+
long long mars_xlog_get_instance(const char* name_prefix);
|
|
139
|
+
|
|
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. */
|
|
142
|
+
void mars_xlog_release_instance(const char* name_prefix);
|
|
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
|
+
|
|
154
|
+
/* Writes through an instance; honours the instance's level. */
|
|
155
|
+
void mars_xlog_write_instance(long long instance,
|
|
156
|
+
int level,
|
|
157
|
+
const char* tag,
|
|
158
|
+
const char* filename,
|
|
159
|
+
const char* func_name,
|
|
160
|
+
int line,
|
|
161
|
+
const char* log);
|
|
162
|
+
|
|
163
|
+
/* 1 when the instance would write this level, 0 otherwise. */
|
|
164
|
+
int mars_xlog_is_enabled_for(long long instance, int level);
|
|
165
|
+
|
|
166
|
+
/* The instance's level, or -1 for an unknown handle. */
|
|
167
|
+
int mars_xlog_get_level(long long instance);
|
|
168
|
+
|
|
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. */
|
|
172
|
+
void mars_xlog_set_level_instance(long long instance, int level);
|
|
173
|
+
|
|
174
|
+
/* appender_setmode / SetAppenderMode for an instance. */
|
|
175
|
+
void mars_xlog_set_mode_instance(long long instance, int mode);
|
|
176
|
+
|
|
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. */
|
|
194
|
+
void mars_xlog_set_console_log_instance(long long instance, int open);
|
|
195
|
+
|
|
196
|
+
/* SetMaxFileSize for an instance; 0 means "do not split". */
|
|
197
|
+
void mars_xlog_set_max_file_size_instance(long long instance, unsigned long long bytes);
|
|
198
|
+
|
|
199
|
+
/* SetMaxAliveTime for an instance; negative clamps to 0. */
|
|
200
|
+
void mars_xlog_set_max_alive_duration_instance(long long instance, long long seconds);
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Replaces `mars::xlog::appender_make_logfile_name()`: the log file *name* for
|
|
204
|
+
* the day `timespan` days ago (0 = today), whether or not it exists yet.
|
|
205
|
+
*
|
|
206
|
+
* The C++ fills a `std::vector`; a C caller walks the same list with `index`,
|
|
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.
|
|
210
|
+
*
|
|
211
|
+
* @return the number of bytes written excluding the terminating NUL, or a
|
|
212
|
+
* negative MARS_XLOG_ERR_* code.
|
|
213
|
+
*/
|
|
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);
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Replaces `mars::xlog::appender_getfilepath_from_timespan()`: the log files
|
|
222
|
+
* that *exist* for the day `timespan` days ago. Same `index` protocol as
|
|
223
|
+
* mars_xlog_make_logfile_name_instance.
|
|
224
|
+
*/
|
|
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
|
|
241
|
+
|
|
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",
|