react-native-nitro-logger 0.1.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/LICENSE +20 -0
- package/NitroLogger.podspec +29 -0
- package/README.md +214 -0
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +124 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +11 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/AndroidPlatformIo.kt +100 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/HybridFileSink.kt +292 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/HybridNativeConsoleSink.kt +23 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/LogFileWriter.kt +1328 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/LogRotationPolicy.kt +186 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/LogSecureFile.kt +80 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/LogWriterRegistry.kt +420 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/NativeConsoleWriter.kt +237 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/NitroLoggerPackage.kt +22 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/PlatformIo.kt +165 -0
- package/docs/PARITY.md +330 -0
- package/docs/PRIVACY.md +341 -0
- package/eslint-plugin/README.md +238 -0
- package/eslint-plugin/index.js +62 -0
- package/eslint-plugin/rules/literal-subsystem.js +102 -0
- package/eslint-plugin/rules/no-computed-metadata-key.js +174 -0
- package/eslint-plugin/rules/no-derived-correlation.js +171 -0
- package/eslint-plugin/rules/no-dynamic-message.js +60 -0
- package/eslint-plugin/shared.js +1948 -0
- package/ios/Gzip.swift +203 -0
- package/ios/HybridFileSink.swift +291 -0
- package/ios/HybridNativeConsoleSink.swift +20 -0
- package/ios/LogFileWriter.swift +1280 -0
- package/ios/LogSecureFile.swift +296 -0
- package/ios/LogWriterRegistry.swift +444 -0
- package/ios/NativeConsoleWriter.swift +180 -0
- package/lib/module/Logger.js +381 -0
- package/lib/module/Logger.js.map +1 -0
- package/lib/module/ScopedLogger.js +70 -0
- package/lib/module/ScopedLogger.js.map +1 -0
- package/lib/module/config.js +23 -0
- package/lib/module/config.js.map +1 -0
- package/lib/module/destinations/Batcher.js +647 -0
- package/lib/module/destinations/Batcher.js.map +1 -0
- package/lib/module/destinations/ConsoleDestination.js +51 -0
- package/lib/module/destinations/ConsoleDestination.js.map +1 -0
- package/lib/module/destinations/FileDestination.js +360 -0
- package/lib/module/destinations/FileDestination.js.map +1 -0
- package/lib/module/destinations/NativeConsoleDestination.js +170 -0
- package/lib/module/destinations/NativeConsoleDestination.js.map +1 -0
- package/lib/module/destinations/types.js +4 -0
- package/lib/module/destinations/types.js.map +1 -0
- package/lib/module/formatters/DefaultFormatter.js +136 -0
- package/lib/module/formatters/DefaultFormatter.js.map +1 -0
- package/lib/module/formatters/JsonLinesFormatter.js +162 -0
- package/lib/module/formatters/JsonLinesFormatter.js.map +1 -0
- package/lib/module/formatters/timestamp.js +15 -0
- package/lib/module/formatters/timestamp.js.map +1 -0
- package/lib/module/formatters/types.js +4 -0
- package/lib/module/formatters/types.js.map +1 -0
- package/lib/module/index.js +31 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/integrations/appState.js +81 -0
- package/lib/module/integrations/appState.js.map +1 -0
- package/lib/module/integrations/errorHandler.js +166 -0
- package/lib/module/integrations/errorHandler.js.map +1 -0
- package/lib/module/integrations/sanitizeError.js +238 -0
- package/lib/module/integrations/sanitizeError.js.map +1 -0
- package/lib/module/levels.js +35 -0
- package/lib/module/levels.js.map +1 -0
- package/lib/module/metadata.js +52 -0
- package/lib/module/metadata.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/privacy.js +349 -0
- package/lib/module/privacy.js.map +1 -0
- package/lib/module/specs/FileSink.nitro.js +4 -0
- package/lib/module/specs/FileSink.nitro.js.map +1 -0
- package/lib/module/specs/NativeConsoleSink.nitro.js +4 -0
- package/lib/module/specs/NativeConsoleSink.nitro.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utf8.js +42 -0
- package/lib/module/utf8.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/Logger.d.ts +122 -0
- package/lib/typescript/src/Logger.d.ts.map +1 -0
- package/lib/typescript/src/ScopedLogger.d.ts +36 -0
- package/lib/typescript/src/ScopedLogger.d.ts.map +1 -0
- package/lib/typescript/src/config.d.ts +12 -0
- package/lib/typescript/src/config.d.ts.map +1 -0
- package/lib/typescript/src/destinations/Batcher.d.ts +244 -0
- package/lib/typescript/src/destinations/Batcher.d.ts.map +1 -0
- package/lib/typescript/src/destinations/ConsoleDestination.d.ts +27 -0
- package/lib/typescript/src/destinations/ConsoleDestination.d.ts.map +1 -0
- package/lib/typescript/src/destinations/FileDestination.d.ts +200 -0
- package/lib/typescript/src/destinations/FileDestination.d.ts.map +1 -0
- package/lib/typescript/src/destinations/NativeConsoleDestination.d.ts +86 -0
- package/lib/typescript/src/destinations/NativeConsoleDestination.d.ts.map +1 -0
- package/lib/typescript/src/destinations/types.d.ts +19 -0
- package/lib/typescript/src/destinations/types.d.ts.map +1 -0
- package/lib/typescript/src/formatters/DefaultFormatter.d.ts +22 -0
- package/lib/typescript/src/formatters/DefaultFormatter.d.ts.map +1 -0
- package/lib/typescript/src/formatters/JsonLinesFormatter.d.ts +73 -0
- package/lib/typescript/src/formatters/JsonLinesFormatter.d.ts.map +1 -0
- package/lib/typescript/src/formatters/timestamp.d.ts +6 -0
- package/lib/typescript/src/formatters/timestamp.d.ts.map +1 -0
- package/lib/typescript/src/formatters/types.d.ts +27 -0
- package/lib/typescript/src/formatters/types.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +34 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/integrations/appState.d.ts +33 -0
- package/lib/typescript/src/integrations/appState.d.ts.map +1 -0
- package/lib/typescript/src/integrations/errorHandler.d.ts +55 -0
- package/lib/typescript/src/integrations/errorHandler.d.ts.map +1 -0
- package/lib/typescript/src/integrations/sanitizeError.d.ts +89 -0
- package/lib/typescript/src/integrations/sanitizeError.d.ts.map +1 -0
- package/lib/typescript/src/levels.d.ts +9 -0
- package/lib/typescript/src/levels.d.ts.map +1 -0
- package/lib/typescript/src/metadata.d.ts +23 -0
- package/lib/typescript/src/metadata.d.ts.map +1 -0
- package/lib/typescript/src/privacy.d.ts +103 -0
- package/lib/typescript/src/privacy.d.ts.map +1 -0
- package/lib/typescript/src/specs/FileSink.nitro.d.ts +131 -0
- package/lib/typescript/src/specs/FileSink.nitro.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeConsoleSink.nitro.d.ts +21 -0
- package/lib/typescript/src/specs/NativeConsoleSink.nitro.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +37 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utf8.d.ts +15 -0
- package/lib/typescript/src/utf8.d.ts.map +1 -0
- package/nitro.json +33 -0
- package/nitrogen/generated/android/c++/JAppendResult.hpp +79 -0
- package/nitrogen/generated/android/c++/JClearOutcome.hpp +88 -0
- package/nitrogen/generated/android/c++/JFlushOutcome.hpp +81 -0
- package/nitrogen/generated/android/c++/JHybridFileSinkSpec.cpp +120 -0
- package/nitrogen/generated/android/c++/JHybridFileSinkSpec.hpp +69 -0
- package/nitrogen/generated/android/c++/JHybridNativeConsoleSinkSpec.cpp +71 -0
- package/nitrogen/generated/android/c++/JHybridNativeConsoleSinkSpec.hpp +64 -0
- package/nitrogen/generated/android/c++/JRejectReason.hpp +64 -0
- package/nitrogen/generated/android/c++/JRotationConfig.hpp +77 -0
- package/nitrogen/generated/android/c++/JSinkStatus.hpp +69 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/AppendResult.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/ClearOutcome.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/FlushOutcome.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/HybridFileSinkSpec.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/HybridNativeConsoleSinkSpec.kt +58 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/RejectReason.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/RotationConfig.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/SinkStatus.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/nitrologgerOnLoad.kt +35 -0
- package/nitrogen/generated/android/nitrologger+autolinking.cmake +83 -0
- package/nitrogen/generated/android/nitrologger+autolinking.gradle +27 -0
- package/nitrogen/generated/android/nitrologgerOnLoad.cpp +70 -0
- package/nitrogen/generated/android/nitrologgerOnLoad.hpp +34 -0
- package/nitrogen/generated/ios/NitroLogger+autolinking.rb +62 -0
- package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Bridge.cpp +50 -0
- package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Bridge.hpp +216 -0
- package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Umbrella.hpp +69 -0
- package/nitrogen/generated/ios/NitroLoggerAutolinking.mm +41 -0
- package/nitrogen/generated/ios/NitroLoggerAutolinking.swift +38 -0
- package/nitrogen/generated/ios/c++/HybridFileSinkSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridFileSinkSpecSwift.hpp +150 -0
- package/nitrogen/generated/ios/c++/HybridNativeConsoleSinkSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNativeConsoleSinkSpecSwift.hpp +87 -0
- package/nitrogen/generated/ios/swift/AppendResult.swift +60 -0
- package/nitrogen/generated/ios/swift/ClearOutcome.swift +50 -0
- package/nitrogen/generated/ios/swift/FlushOutcome.swift +59 -0
- package/nitrogen/generated/ios/swift/HybridFileSinkSpec.swift +61 -0
- package/nitrogen/generated/ios/swift/HybridFileSinkSpec_cxx.swift +227 -0
- package/nitrogen/generated/ios/swift/HybridNativeConsoleSinkSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridNativeConsoleSinkSpec_cxx.swift +148 -0
- package/nitrogen/generated/ios/swift/RejectReason.swift +48 -0
- package/nitrogen/generated/ios/swift/RotationConfig.swift +93 -0
- package/nitrogen/generated/ios/swift/SinkStatus.swift +44 -0
- package/nitrogen/generated/shared/c++/AppendResult.hpp +105 -0
- package/nitrogen/generated/shared/c++/ClearOutcome.hpp +96 -0
- package/nitrogen/generated/shared/c++/FlushOutcome.hpp +107 -0
- package/nitrogen/generated/shared/c++/HybridFileSinkSpec.cpp +28 -0
- package/nitrogen/generated/shared/c++/HybridFileSinkSpec.hpp +84 -0
- package/nitrogen/generated/shared/c++/HybridNativeConsoleSinkSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridNativeConsoleSinkSpec.hpp +64 -0
- package/nitrogen/generated/shared/c++/RejectReason.hpp +84 -0
- package/nitrogen/generated/shared/c++/RotationConfig.hpp +103 -0
- package/nitrogen/generated/shared/c++/SinkStatus.hpp +95 -0
- package/package.json +151 -0
- package/src/Logger.ts +417 -0
- package/src/ScopedLogger.ts +94 -0
- package/src/config.ts +25 -0
- package/src/destinations/Batcher.ts +805 -0
- package/src/destinations/ConsoleDestination.ts +60 -0
- package/src/destinations/FileDestination.ts +473 -0
- package/src/destinations/NativeConsoleDestination.ts +220 -0
- package/src/destinations/types.ts +19 -0
- package/src/formatters/DefaultFormatter.ts +150 -0
- package/src/formatters/JsonLinesFormatter.ts +195 -0
- package/src/formatters/timestamp.ts +12 -0
- package/src/formatters/types.ts +29 -0
- package/src/index.tsx +106 -0
- package/src/integrations/appState.ts +111 -0
- package/src/integrations/errorHandler.ts +223 -0
- package/src/integrations/sanitizeError.ts +287 -0
- package/src/levels.ts +35 -0
- package/src/metadata.ts +52 -0
- package/src/privacy.ts +389 -0
- package/src/specs/FileSink.nitro.ts +144 -0
- package/src/specs/NativeConsoleSink.nitro.ts +21 -0
- package/src/types.ts +45 -0
- package/src/utf8.ts +39 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrologger
|
|
2
|
+
|
|
3
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
4
|
+
import com.margelo.nitro.NitroModules
|
|
5
|
+
import java.io.File
|
|
6
|
+
import java.util.concurrent.locks.ReentrantLock
|
|
7
|
+
import kotlin.concurrent.withLock
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The Nitro adapter. Everything interesting is in [LogFileWriter]; this
|
|
11
|
+
* translates the bridge's types and holds the handle.
|
|
12
|
+
*
|
|
13
|
+
* The split is deliberate and mirrors iOS: the writer imports nothing from
|
|
14
|
+
* Nitro or Android, so the whole of it runs under plain JUnit. What is left
|
|
15
|
+
* here is argument marshalling, which is the part that cannot be tested off
|
|
16
|
+
* device anyway.
|
|
17
|
+
*/
|
|
18
|
+
@DoNotStrip
|
|
19
|
+
class HybridFileSink : HybridFileSinkSpec() {
|
|
20
|
+
/**
|
|
21
|
+
* Guards [handle] and [mayHaveArtifacts] together, matching the iOS adapter.
|
|
22
|
+
*
|
|
23
|
+
* They have to move under one lock rather than being individually volatile:
|
|
24
|
+
* the question `clearLogs` asks is the *pair* — no handle AND nothing was
|
|
25
|
+
* ever created. Reading them separately lets a close land in between and
|
|
26
|
+
* produce "no handle, never opened", which is the one answer that is never
|
|
27
|
+
* true and the one that reports a durable purge over surviving files.
|
|
28
|
+
*/
|
|
29
|
+
private val lock = ReentrantLock()
|
|
30
|
+
private var handle: LogFileHandle? = null
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether this sink may have put anything on disk.
|
|
34
|
+
*
|
|
35
|
+
* A null handle conflates two states that owe the caller opposite answers:
|
|
36
|
+
* nothing was ever created, and the files are still there but out of reach.
|
|
37
|
+
*
|
|
38
|
+
* Set *before* the acquisition attempt, not after it succeeds. Acquiring
|
|
39
|
+
* creates the log directory and can then fail on the file itself, so a throw
|
|
40
|
+
* is not evidence that nothing was written — and this flag exists precisely
|
|
41
|
+
* to keep a later purge from claiming otherwise. Never cleared: closing
|
|
42
|
+
* releases the handle, it does not unmake the files.
|
|
43
|
+
*/
|
|
44
|
+
private var mayHaveArtifacts = false
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* An [open] past its check and inside `acquire`.
|
|
48
|
+
*
|
|
49
|
+
* Published so a second open is refused instead of racing to install a handle
|
|
50
|
+
* over the first one's, and so [close] can tell "nothing here" from
|
|
51
|
+
* "something is on its way".
|
|
52
|
+
*/
|
|
53
|
+
private var opening = false
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A [close] that arrived while [opening] was true.
|
|
57
|
+
*
|
|
58
|
+
* That close had no handle to flush and has already returned; this tells the
|
|
59
|
+
* in-flight open to throw away what it acquired rather than install it.
|
|
60
|
+
*/
|
|
61
|
+
private var closePending = false
|
|
62
|
+
|
|
63
|
+
private fun current(): LogFileHandle? = lock.withLock { handle }
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The manual release, which JavaScript can call and usually does not.
|
|
67
|
+
*
|
|
68
|
+
* `@CallSuper`, so `super.dispose()` is not optional — it is what frees the
|
|
69
|
+
* C++ side.
|
|
70
|
+
*/
|
|
71
|
+
override fun dispose() {
|
|
72
|
+
releaseHandle()
|
|
73
|
+
super.dispose()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The release that happens whether or not anything asked for one, and the
|
|
78
|
+
* reason this class needs both.
|
|
79
|
+
*
|
|
80
|
+
* A Metro reload tears the JavaScript context down without running any of
|
|
81
|
+
* it, so a JS `dispose()` is never a guarantee. Without this the writer
|
|
82
|
+
* survives the reload holding the registry slot and the descriptor, and the
|
|
83
|
+
* next `open` with a different rotation config fails `CONFIG_CONFLICT`
|
|
84
|
+
* against a sink nothing can reach to close — an every-reload failure during
|
|
85
|
+
* development. iOS gets this from `deinit`; on ART the equivalent is
|
|
86
|
+
* `finalize`, deprecated and still the only hook there is.
|
|
87
|
+
*
|
|
88
|
+
* Best effort by nature: the finalizer thread may never run this. It is a
|
|
89
|
+
* backstop under `dispose`, not a replacement for it.
|
|
90
|
+
*/
|
|
91
|
+
@Suppress("removal", "DEPRECATION")
|
|
92
|
+
protected fun finalize() {
|
|
93
|
+
releaseHandle()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Deliberately routed through [close] rather than reaching for the handle
|
|
98
|
+
* directly. `close` is the one place that also records [closePending], and a
|
|
99
|
+
* `dispose` racing an in-flight `open` is exactly when that matters: JS can
|
|
100
|
+
* call `dispose` from another thread while `open` is inside `acquire`, and
|
|
101
|
+
* without the flag that acquisition installs a live writer into a sink
|
|
102
|
+
* nothing can reach to release. (`finalize` cannot hit that race — a thread
|
|
103
|
+
* inside `open` keeps the object reachable — but it costs nothing to be
|
|
104
|
+
* right by construction rather than by argument.)
|
|
105
|
+
*
|
|
106
|
+
* Idempotent: `close` detaches under the lock, so a second call finds
|
|
107
|
+
* nothing. Both callers above can fire for the same object.
|
|
108
|
+
*
|
|
109
|
+
* Zero deadline: a teardown must not wait on a wedged disk, and on the
|
|
110
|
+
* finalizer thread blocking would stall every other object's release.
|
|
111
|
+
*/
|
|
112
|
+
private fun releaseHandle() {
|
|
113
|
+
runCatching { close(0.0) }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
override val defaultLogDirectory: String
|
|
117
|
+
get() {
|
|
118
|
+
val context = NitroModules.applicationContext
|
|
119
|
+
// `noBackupFilesDir`, not `filesDir`: the latter is eligible for Android
|
|
120
|
+
// Auto Backup, which would upload the log file to Google Drive. For a
|
|
121
|
+
// package whose entire privacy design is about what may leave the device,
|
|
122
|
+
// shipping the logs off it by default is the one unacceptable default.
|
|
123
|
+
val base = context?.noBackupFilesDir
|
|
124
|
+
?: File(System.getProperty("java.io.tmpdir") ?: "/tmp")
|
|
125
|
+
return File(base, "logs").absolutePath
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
override fun open(path: String, rotation: RotationConfig?, lineFramed: Boolean?) {
|
|
129
|
+
lock.withLock {
|
|
130
|
+
// `opening` counts as open. Acquisition does real I/O — creates the
|
|
131
|
+
// directory, opens the file, may scan a torn tail — so the lock is not
|
|
132
|
+
// held across it, or every `getStatus` would queue behind disk latency,
|
|
133
|
+
// which is the one thing that call must not do. The in-flight attempt is
|
|
134
|
+
// published instead, and a second one is refused rather than allowed to
|
|
135
|
+
// acquire a writer that would overwrite the first: the loser's handle
|
|
136
|
+
// would be unreachable, and unreachable means a later purge never
|
|
137
|
+
// deletes its files.
|
|
138
|
+
if (handle != null || opening) {
|
|
139
|
+
throw LogWriterException(
|
|
140
|
+
LogWriterException.Kind.CONFIG_CONFLICT,
|
|
141
|
+
"this sink is already open"
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
opening = true
|
|
145
|
+
closePending = false
|
|
146
|
+
// Before the attempt: see [mayHaveArtifacts].
|
|
147
|
+
mayHaveArtifacts = true
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
val acquired = try {
|
|
151
|
+
LogWriterRegistry.shared.acquire(
|
|
152
|
+
path = path,
|
|
153
|
+
policy = policyOf(rotation),
|
|
154
|
+
// Absent means absent: without a declared one-record-per-line contract
|
|
155
|
+
// the startup scan must not trim a trailing record, because it cannot
|
|
156
|
+
// tell a torn one from an intentional newline.
|
|
157
|
+
lineFramed = lineFramed ?: false,
|
|
158
|
+
platform = AndroidPlatformIo
|
|
159
|
+
)
|
|
160
|
+
} catch (e: Throwable) {
|
|
161
|
+
// Failed attempts have to clear the flag, or a retry is refused forever.
|
|
162
|
+
lock.withLock { opening = false }
|
|
163
|
+
throw e
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
val abandon = lock.withLock {
|
|
167
|
+
opening = false
|
|
168
|
+
val pending = closePending
|
|
169
|
+
closePending = false
|
|
170
|
+
// A close that arrived mid-acquisition found nothing to hand back and has
|
|
171
|
+
// already returned. Installing now would leave a live writer holding a
|
|
172
|
+
// descriptor nothing can reach or release.
|
|
173
|
+
if (!pending) handle = acquired
|
|
174
|
+
pending
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Zero deadline: the caller has already been told this sink is closed.
|
|
178
|
+
if (abandon) acquired.close(0.0)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private fun policyOf(rotation: RotationConfig?): LogRotationPolicy {
|
|
182
|
+
if (rotation == null) return LogRotationPolicy.of()
|
|
183
|
+
return LogRotationPolicy.of(
|
|
184
|
+
maxFileSizeBytes = rotation.maxFileSizeBytes,
|
|
185
|
+
maxArchivedFilesCount = rotation.maxArchivedFilesCount,
|
|
186
|
+
maxFileAgeSeconds = rotation.maxFileAgeSeconds,
|
|
187
|
+
compressArchives = rotation.compressArchives,
|
|
188
|
+
maxArchiveAgeSeconds = rotation.maxArchiveAgeSeconds,
|
|
189
|
+
maxTotalLogBytes = rotation.maxTotalLogBytes
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
override fun appendBatch(batch: String, entryCount: Double): AppendResult {
|
|
194
|
+
val live = current() ?: return AppendResult(
|
|
195
|
+
false, RejectReason.CLOSED, 0.0, 0.0, 0.0, 0.0
|
|
196
|
+
)
|
|
197
|
+
// Refused rather than coerced, matching `Int(exactly:)` on iOS. A count
|
|
198
|
+
// that does not survive the round trip cannot be trusted to describe the
|
|
199
|
+
// batch it arrived with. See [BridgeNumber.exactLong].
|
|
200
|
+
val exact = BridgeNumber.exactLong(entryCount)
|
|
201
|
+
?: return wireResult(
|
|
202
|
+
LogAppendResult(false, LogRejectReason.FAILED, live.status())
|
|
203
|
+
)
|
|
204
|
+
return wireResult(live.appendBatch(batch, exact))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private fun wireResult(result: LogAppendResult): AppendResult = AppendResult(
|
|
208
|
+
result.accepted,
|
|
209
|
+
result.rejectReason?.let { wireReason(it) },
|
|
210
|
+
result.status.queuedBytes.toDouble(),
|
|
211
|
+
result.status.lostBytes.toDouble(),
|
|
212
|
+
result.status.lostEntries.toDouble(),
|
|
213
|
+
result.status.degraded.toDouble()
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
private fun wireReason(reason: LogRejectReason): RejectReason = when (reason) {
|
|
217
|
+
LogRejectReason.FULL -> RejectReason.FULL
|
|
218
|
+
LogRejectReason.STALE_GENERATION -> RejectReason.STALEGENERATION
|
|
219
|
+
LogRejectReason.CLOSED -> RejectReason.CLOSED
|
|
220
|
+
LogRejectReason.FAILED -> RejectReason.FAILED
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
override fun getStatus(): SinkStatus {
|
|
224
|
+
val live = current() ?: return SinkStatus(0.0, 0.0, 0.0, 0.0)
|
|
225
|
+
val status = live.status()
|
|
226
|
+
return SinkStatus(
|
|
227
|
+
status.queuedBytes.toDouble(),
|
|
228
|
+
status.lostBytes.toDouble(),
|
|
229
|
+
status.lostEntries.toDouble(),
|
|
230
|
+
status.degraded.toDouble()
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
override fun flush(deadlineMs: Double): FlushOutcome {
|
|
235
|
+
val live = current() ?: return FlushOutcome(false, false, 0.0, 0.0, 0.0, 0.0, 0.0)
|
|
236
|
+
return outcomeOf(live.flush(deadlineMs))
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
override fun close(deadlineMs: Double): FlushOutcome {
|
|
240
|
+
// Detached before the close runs, so a concurrent caller cannot pick up a
|
|
241
|
+
// handle that is on its way out.
|
|
242
|
+
val live = lock.withLock {
|
|
243
|
+
val current = handle
|
|
244
|
+
handle = null
|
|
245
|
+
// Nothing to close, but an acquisition is in flight and will finish after
|
|
246
|
+
// this returns. Recording the intent keeps that writer from being
|
|
247
|
+
// installed into a sink the caller has already closed.
|
|
248
|
+
if (current == null && opening) closePending = true
|
|
249
|
+
current
|
|
250
|
+
} ?: return FlushOutcome(false, false, 0.0, 0.0, 0.0, 0.0, 0.0)
|
|
251
|
+
return outcomeOf(live.close(deadlineMs))
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private fun outcomeOf(outcome: LogFlushOutcome) = FlushOutcome(
|
|
255
|
+
outcome.durable,
|
|
256
|
+
outcome.timedOut,
|
|
257
|
+
outcome.pendingBytes.toDouble(),
|
|
258
|
+
outcome.status.queuedBytes.toDouble(),
|
|
259
|
+
outcome.status.lostBytes.toDouble(),
|
|
260
|
+
outcome.status.lostEntries.toDouble(),
|
|
261
|
+
outcome.status.degraded.toDouble()
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
override fun getLogFilePaths(): Array<String> =
|
|
265
|
+
current()?.logFilePaths()?.toTypedArray() ?: emptyArray()
|
|
266
|
+
|
|
267
|
+
override fun clearLogs(deadlineMs: Double): ClearOutcome {
|
|
268
|
+
// Never opened: nothing was created, so "every artifact is gone" holds
|
|
269
|
+
// vacuously. Opened and since closed: the files are still on disk and this
|
|
270
|
+
// object cannot reach them, so the only honest answer is `durable = false`.
|
|
271
|
+
// The registry draws the same distinction for a released handle, but
|
|
272
|
+
// `close` nils the handle below before that branch is reachable.
|
|
273
|
+
//
|
|
274
|
+
// Reporting a durable purge over surviving files is the worst lie this API
|
|
275
|
+
// can tell: the caller asking is the one deleting patient data on request.
|
|
276
|
+
//
|
|
277
|
+
// Both fields are read in one critical section: a close landing between two
|
|
278
|
+
// separate reads would show "no handle, nothing created", which is the one
|
|
279
|
+
// combination that is never true and the one that lies in the dangerous
|
|
280
|
+
// direction.
|
|
281
|
+
val live = lock.withLock {
|
|
282
|
+
handle ?: return ClearOutcome(0.0, emptyArray(), !mayHaveArtifacts, false)
|
|
283
|
+
}
|
|
284
|
+
val outcome = live.clearLogs(deadlineMs)
|
|
285
|
+
return ClearOutcome(
|
|
286
|
+
outcome.deletedCount.toDouble(),
|
|
287
|
+
outcome.failedPaths.toTypedArray(),
|
|
288
|
+
outcome.durable,
|
|
289
|
+
outcome.rebound
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrologger
|
|
2
|
+
|
|
3
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Routes pre-formatted lines into logcat so JS entries interleave with
|
|
7
|
+
* native ones. Tag = the installed category.
|
|
8
|
+
*
|
|
9
|
+
* Marshalling only. The level map and the chunking that keeps logcat from
|
|
10
|
+
* silently eating the tail of a long line live in [NativeConsoleWriter], which
|
|
11
|
+
* imports nothing from Nitro and is therefore testable — the same split the
|
|
12
|
+
* file sink uses, and for the same reason.
|
|
13
|
+
*/
|
|
14
|
+
@DoNotStrip
|
|
15
|
+
class HybridNativeConsoleSink : HybridNativeConsoleSinkSpec() {
|
|
16
|
+
private val writer = NativeConsoleWriter()
|
|
17
|
+
|
|
18
|
+
override fun install(subsystem: String, category: String) =
|
|
19
|
+
writer.install(subsystem, category)
|
|
20
|
+
|
|
21
|
+
override fun logBatch(levels: DoubleArray, messages: Array<String>) =
|
|
22
|
+
writer.logBatch(levels, messages)
|
|
23
|
+
}
|