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.
Files changed (205) hide show
  1. package/LICENSE +20 -0
  2. package/NitroLogger.podspec +29 -0
  3. package/README.md +214 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +124 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/cpp-adapter.cpp +11 -0
  8. package/android/src/main/java/com/margelo/nitro/nitrologger/AndroidPlatformIo.kt +100 -0
  9. package/android/src/main/java/com/margelo/nitro/nitrologger/HybridFileSink.kt +292 -0
  10. package/android/src/main/java/com/margelo/nitro/nitrologger/HybridNativeConsoleSink.kt +23 -0
  11. package/android/src/main/java/com/margelo/nitro/nitrologger/LogFileWriter.kt +1328 -0
  12. package/android/src/main/java/com/margelo/nitro/nitrologger/LogRotationPolicy.kt +186 -0
  13. package/android/src/main/java/com/margelo/nitro/nitrologger/LogSecureFile.kt +80 -0
  14. package/android/src/main/java/com/margelo/nitro/nitrologger/LogWriterRegistry.kt +420 -0
  15. package/android/src/main/java/com/margelo/nitro/nitrologger/NativeConsoleWriter.kt +237 -0
  16. package/android/src/main/java/com/margelo/nitro/nitrologger/NitroLoggerPackage.kt +22 -0
  17. package/android/src/main/java/com/margelo/nitro/nitrologger/PlatformIo.kt +165 -0
  18. package/docs/PARITY.md +330 -0
  19. package/docs/PRIVACY.md +341 -0
  20. package/eslint-plugin/README.md +238 -0
  21. package/eslint-plugin/index.js +62 -0
  22. package/eslint-plugin/rules/literal-subsystem.js +102 -0
  23. package/eslint-plugin/rules/no-computed-metadata-key.js +174 -0
  24. package/eslint-plugin/rules/no-derived-correlation.js +171 -0
  25. package/eslint-plugin/rules/no-dynamic-message.js +60 -0
  26. package/eslint-plugin/shared.js +1948 -0
  27. package/ios/Gzip.swift +203 -0
  28. package/ios/HybridFileSink.swift +291 -0
  29. package/ios/HybridNativeConsoleSink.swift +20 -0
  30. package/ios/LogFileWriter.swift +1280 -0
  31. package/ios/LogSecureFile.swift +296 -0
  32. package/ios/LogWriterRegistry.swift +444 -0
  33. package/ios/NativeConsoleWriter.swift +180 -0
  34. package/lib/module/Logger.js +381 -0
  35. package/lib/module/Logger.js.map +1 -0
  36. package/lib/module/ScopedLogger.js +70 -0
  37. package/lib/module/ScopedLogger.js.map +1 -0
  38. package/lib/module/config.js +23 -0
  39. package/lib/module/config.js.map +1 -0
  40. package/lib/module/destinations/Batcher.js +647 -0
  41. package/lib/module/destinations/Batcher.js.map +1 -0
  42. package/lib/module/destinations/ConsoleDestination.js +51 -0
  43. package/lib/module/destinations/ConsoleDestination.js.map +1 -0
  44. package/lib/module/destinations/FileDestination.js +360 -0
  45. package/lib/module/destinations/FileDestination.js.map +1 -0
  46. package/lib/module/destinations/NativeConsoleDestination.js +170 -0
  47. package/lib/module/destinations/NativeConsoleDestination.js.map +1 -0
  48. package/lib/module/destinations/types.js +4 -0
  49. package/lib/module/destinations/types.js.map +1 -0
  50. package/lib/module/formatters/DefaultFormatter.js +136 -0
  51. package/lib/module/formatters/DefaultFormatter.js.map +1 -0
  52. package/lib/module/formatters/JsonLinesFormatter.js +162 -0
  53. package/lib/module/formatters/JsonLinesFormatter.js.map +1 -0
  54. package/lib/module/formatters/timestamp.js +15 -0
  55. package/lib/module/formatters/timestamp.js.map +1 -0
  56. package/lib/module/formatters/types.js +4 -0
  57. package/lib/module/formatters/types.js.map +1 -0
  58. package/lib/module/index.js +31 -0
  59. package/lib/module/index.js.map +1 -0
  60. package/lib/module/integrations/appState.js +81 -0
  61. package/lib/module/integrations/appState.js.map +1 -0
  62. package/lib/module/integrations/errorHandler.js +166 -0
  63. package/lib/module/integrations/errorHandler.js.map +1 -0
  64. package/lib/module/integrations/sanitizeError.js +238 -0
  65. package/lib/module/integrations/sanitizeError.js.map +1 -0
  66. package/lib/module/levels.js +35 -0
  67. package/lib/module/levels.js.map +1 -0
  68. package/lib/module/metadata.js +52 -0
  69. package/lib/module/metadata.js.map +1 -0
  70. package/lib/module/package.json +1 -0
  71. package/lib/module/privacy.js +349 -0
  72. package/lib/module/privacy.js.map +1 -0
  73. package/lib/module/specs/FileSink.nitro.js +4 -0
  74. package/lib/module/specs/FileSink.nitro.js.map +1 -0
  75. package/lib/module/specs/NativeConsoleSink.nitro.js +4 -0
  76. package/lib/module/specs/NativeConsoleSink.nitro.js.map +1 -0
  77. package/lib/module/types.js +4 -0
  78. package/lib/module/types.js.map +1 -0
  79. package/lib/module/utf8.js +42 -0
  80. package/lib/module/utf8.js.map +1 -0
  81. package/lib/typescript/package.json +1 -0
  82. package/lib/typescript/src/Logger.d.ts +122 -0
  83. package/lib/typescript/src/Logger.d.ts.map +1 -0
  84. package/lib/typescript/src/ScopedLogger.d.ts +36 -0
  85. package/lib/typescript/src/ScopedLogger.d.ts.map +1 -0
  86. package/lib/typescript/src/config.d.ts +12 -0
  87. package/lib/typescript/src/config.d.ts.map +1 -0
  88. package/lib/typescript/src/destinations/Batcher.d.ts +244 -0
  89. package/lib/typescript/src/destinations/Batcher.d.ts.map +1 -0
  90. package/lib/typescript/src/destinations/ConsoleDestination.d.ts +27 -0
  91. package/lib/typescript/src/destinations/ConsoleDestination.d.ts.map +1 -0
  92. package/lib/typescript/src/destinations/FileDestination.d.ts +200 -0
  93. package/lib/typescript/src/destinations/FileDestination.d.ts.map +1 -0
  94. package/lib/typescript/src/destinations/NativeConsoleDestination.d.ts +86 -0
  95. package/lib/typescript/src/destinations/NativeConsoleDestination.d.ts.map +1 -0
  96. package/lib/typescript/src/destinations/types.d.ts +19 -0
  97. package/lib/typescript/src/destinations/types.d.ts.map +1 -0
  98. package/lib/typescript/src/formatters/DefaultFormatter.d.ts +22 -0
  99. package/lib/typescript/src/formatters/DefaultFormatter.d.ts.map +1 -0
  100. package/lib/typescript/src/formatters/JsonLinesFormatter.d.ts +73 -0
  101. package/lib/typescript/src/formatters/JsonLinesFormatter.d.ts.map +1 -0
  102. package/lib/typescript/src/formatters/timestamp.d.ts +6 -0
  103. package/lib/typescript/src/formatters/timestamp.d.ts.map +1 -0
  104. package/lib/typescript/src/formatters/types.d.ts +27 -0
  105. package/lib/typescript/src/formatters/types.d.ts.map +1 -0
  106. package/lib/typescript/src/index.d.ts +34 -0
  107. package/lib/typescript/src/index.d.ts.map +1 -0
  108. package/lib/typescript/src/integrations/appState.d.ts +33 -0
  109. package/lib/typescript/src/integrations/appState.d.ts.map +1 -0
  110. package/lib/typescript/src/integrations/errorHandler.d.ts +55 -0
  111. package/lib/typescript/src/integrations/errorHandler.d.ts.map +1 -0
  112. package/lib/typescript/src/integrations/sanitizeError.d.ts +89 -0
  113. package/lib/typescript/src/integrations/sanitizeError.d.ts.map +1 -0
  114. package/lib/typescript/src/levels.d.ts +9 -0
  115. package/lib/typescript/src/levels.d.ts.map +1 -0
  116. package/lib/typescript/src/metadata.d.ts +23 -0
  117. package/lib/typescript/src/metadata.d.ts.map +1 -0
  118. package/lib/typescript/src/privacy.d.ts +103 -0
  119. package/lib/typescript/src/privacy.d.ts.map +1 -0
  120. package/lib/typescript/src/specs/FileSink.nitro.d.ts +131 -0
  121. package/lib/typescript/src/specs/FileSink.nitro.d.ts.map +1 -0
  122. package/lib/typescript/src/specs/NativeConsoleSink.nitro.d.ts +21 -0
  123. package/lib/typescript/src/specs/NativeConsoleSink.nitro.d.ts.map +1 -0
  124. package/lib/typescript/src/types.d.ts +37 -0
  125. package/lib/typescript/src/types.d.ts.map +1 -0
  126. package/lib/typescript/src/utf8.d.ts +15 -0
  127. package/lib/typescript/src/utf8.d.ts.map +1 -0
  128. package/nitro.json +33 -0
  129. package/nitrogen/generated/android/c++/JAppendResult.hpp +79 -0
  130. package/nitrogen/generated/android/c++/JClearOutcome.hpp +88 -0
  131. package/nitrogen/generated/android/c++/JFlushOutcome.hpp +81 -0
  132. package/nitrogen/generated/android/c++/JHybridFileSinkSpec.cpp +120 -0
  133. package/nitrogen/generated/android/c++/JHybridFileSinkSpec.hpp +69 -0
  134. package/nitrogen/generated/android/c++/JHybridNativeConsoleSinkSpec.cpp +71 -0
  135. package/nitrogen/generated/android/c++/JHybridNativeConsoleSinkSpec.hpp +64 -0
  136. package/nitrogen/generated/android/c++/JRejectReason.hpp +64 -0
  137. package/nitrogen/generated/android/c++/JRotationConfig.hpp +77 -0
  138. package/nitrogen/generated/android/c++/JSinkStatus.hpp +69 -0
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/AppendResult.kt +76 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/ClearOutcome.kt +66 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/FlushOutcome.kt +81 -0
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/HybridFileSinkSpec.kt +80 -0
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/HybridNativeConsoleSinkSpec.kt +58 -0
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/RejectReason.kt +25 -0
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/RotationConfig.kt +76 -0
  146. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/SinkStatus.kt +66 -0
  147. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/nitrologgerOnLoad.kt +35 -0
  148. package/nitrogen/generated/android/nitrologger+autolinking.cmake +83 -0
  149. package/nitrogen/generated/android/nitrologger+autolinking.gradle +27 -0
  150. package/nitrogen/generated/android/nitrologgerOnLoad.cpp +70 -0
  151. package/nitrogen/generated/android/nitrologgerOnLoad.hpp +34 -0
  152. package/nitrogen/generated/ios/NitroLogger+autolinking.rb +62 -0
  153. package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Bridge.cpp +50 -0
  154. package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Bridge.hpp +216 -0
  155. package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Umbrella.hpp +69 -0
  156. package/nitrogen/generated/ios/NitroLoggerAutolinking.mm +41 -0
  157. package/nitrogen/generated/ios/NitroLoggerAutolinking.swift +38 -0
  158. package/nitrogen/generated/ios/c++/HybridFileSinkSpecSwift.cpp +11 -0
  159. package/nitrogen/generated/ios/c++/HybridFileSinkSpecSwift.hpp +150 -0
  160. package/nitrogen/generated/ios/c++/HybridNativeConsoleSinkSpecSwift.cpp +11 -0
  161. package/nitrogen/generated/ios/c++/HybridNativeConsoleSinkSpecSwift.hpp +87 -0
  162. package/nitrogen/generated/ios/swift/AppendResult.swift +60 -0
  163. package/nitrogen/generated/ios/swift/ClearOutcome.swift +50 -0
  164. package/nitrogen/generated/ios/swift/FlushOutcome.swift +59 -0
  165. package/nitrogen/generated/ios/swift/HybridFileSinkSpec.swift +61 -0
  166. package/nitrogen/generated/ios/swift/HybridFileSinkSpec_cxx.swift +227 -0
  167. package/nitrogen/generated/ios/swift/HybridNativeConsoleSinkSpec.swift +56 -0
  168. package/nitrogen/generated/ios/swift/HybridNativeConsoleSinkSpec_cxx.swift +148 -0
  169. package/nitrogen/generated/ios/swift/RejectReason.swift +48 -0
  170. package/nitrogen/generated/ios/swift/RotationConfig.swift +93 -0
  171. package/nitrogen/generated/ios/swift/SinkStatus.swift +44 -0
  172. package/nitrogen/generated/shared/c++/AppendResult.hpp +105 -0
  173. package/nitrogen/generated/shared/c++/ClearOutcome.hpp +96 -0
  174. package/nitrogen/generated/shared/c++/FlushOutcome.hpp +107 -0
  175. package/nitrogen/generated/shared/c++/HybridFileSinkSpec.cpp +28 -0
  176. package/nitrogen/generated/shared/c++/HybridFileSinkSpec.hpp +84 -0
  177. package/nitrogen/generated/shared/c++/HybridNativeConsoleSinkSpec.cpp +22 -0
  178. package/nitrogen/generated/shared/c++/HybridNativeConsoleSinkSpec.hpp +64 -0
  179. package/nitrogen/generated/shared/c++/RejectReason.hpp +84 -0
  180. package/nitrogen/generated/shared/c++/RotationConfig.hpp +103 -0
  181. package/nitrogen/generated/shared/c++/SinkStatus.hpp +95 -0
  182. package/package.json +151 -0
  183. package/src/Logger.ts +417 -0
  184. package/src/ScopedLogger.ts +94 -0
  185. package/src/config.ts +25 -0
  186. package/src/destinations/Batcher.ts +805 -0
  187. package/src/destinations/ConsoleDestination.ts +60 -0
  188. package/src/destinations/FileDestination.ts +473 -0
  189. package/src/destinations/NativeConsoleDestination.ts +220 -0
  190. package/src/destinations/types.ts +19 -0
  191. package/src/formatters/DefaultFormatter.ts +150 -0
  192. package/src/formatters/JsonLinesFormatter.ts +195 -0
  193. package/src/formatters/timestamp.ts +12 -0
  194. package/src/formatters/types.ts +29 -0
  195. package/src/index.tsx +106 -0
  196. package/src/integrations/appState.ts +111 -0
  197. package/src/integrations/errorHandler.ts +223 -0
  198. package/src/integrations/sanitizeError.ts +287 -0
  199. package/src/levels.ts +35 -0
  200. package/src/metadata.ts +52 -0
  201. package/src/privacy.ts +389 -0
  202. package/src/specs/FileSink.nitro.ts +144 -0
  203. package/src/specs/NativeConsoleSink.nitro.ts +21 -0
  204. package/src/types.ts +45 -0
  205. package/src/utf8.ts +39 -0
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ import { DefaultFormatter } from "../formatters/DefaultFormatter.js";
4
+ import { LEVEL_ORDER } from "../levels.js";
5
+
6
+ /**
7
+ * Renders to the JS console (Metro in dev). warning → console.warn and
8
+ * error/todo → console.error deliberately: LogBox surfacing errors in dev is
9
+ * a feature, not noise.
10
+ *
11
+ * `outputSink` mirrors SwiftLogger's test hook: when set, lines are also
12
+ * handed to the sink, and `isEnabled` stays true even with printing off.
13
+ */
14
+ export class ConsoleDestination {
15
+ label = 'console';
16
+ printEnabled = true;
17
+ constructor(options) {
18
+ this.minimumLevel = options?.minimumLevel;
19
+ this.formatter = options?.formatter ?? new DefaultFormatter();
20
+ }
21
+ get isEnabled() {
22
+ return this.printEnabled || this.outputSink !== undefined;
23
+ }
24
+ write(entry) {
25
+ const line = this.formatter.format(entry);
26
+ if (this.outputSink) {
27
+ // Guarded: a faulty observer must not suppress console output or get
28
+ // the whole destination disabled as repeatedly failing.
29
+ try {
30
+ this.outputSink(line);
31
+ } catch {
32
+ // observer-only failure; console printing below still runs
33
+ }
34
+ }
35
+ if (!this.printEnabled) return;
36
+ if (LEVEL_ORDER[entry.level] >= LEVEL_ORDER.error) {
37
+ console.error(line);
38
+ } else if (entry.level === 'warning') {
39
+ console.warn(line);
40
+ } else {
41
+ console.log(line);
42
+ }
43
+ }
44
+ flush() {
45
+ // console writes are synchronous; nothing buffers here
46
+ }
47
+ dispose() {
48
+ this.outputSink = undefined;
49
+ }
50
+ }
51
+ //# sourceMappingURL=ConsoleDestination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["DefaultFormatter","LEVEL_ORDER","ConsoleDestination","label","printEnabled","constructor","options","minimumLevel","formatter","isEnabled","outputSink","undefined","write","entry","line","format","level","error","console","warn","log","flush","dispose"],"sourceRoot":"../../../src","sources":["destinations/ConsoleDestination.ts"],"mappings":";;AAGA,SAASA,gBAAgB,QAAQ,mCAAgC;AACjE,SAASC,WAAW,QAAQ,cAAW;;AAEvC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,kBAAkB,CAA2B;EAC/CC,KAAK,GAAG,SAAS;EAE1BC,YAAY,GAAG,IAAI;EAKnBC,WAAWA,CAACC,OAA+D,EAAE;IAC3E,IAAI,CAACC,YAAY,GAAGD,OAAO,EAAEC,YAAY;IACzC,IAAI,CAACC,SAAS,GAAGF,OAAO,EAAEE,SAAS,IAAI,IAAIR,gBAAgB,CAAC,CAAC;EAC/D;EAEA,IAAIS,SAASA,CAAA,EAAY;IACvB,OAAO,IAAI,CAACL,YAAY,IAAI,IAAI,CAACM,UAAU,KAAKC,SAAS;EAC3D;EAEAC,KAAKA,CAACC,KAAe,EAAQ;IAC3B,MAAMC,IAAI,GAAG,IAAI,CAACN,SAAS,CAACO,MAAM,CAACF,KAAK,CAAC;IACzC,IAAI,IAAI,CAACH,UAAU,EAAE;MACnB;MACA;MACA,IAAI;QACF,IAAI,CAACA,UAAU,CAACI,IAAI,CAAC;MACvB,CAAC,CAAC,MAAM;QACN;MAAA;IAEJ;IACA,IAAI,CAAC,IAAI,CAACV,YAAY,EAAE;IACxB,IAAIH,WAAW,CAACY,KAAK,CAACG,KAAK,CAAC,IAAIf,WAAW,CAACgB,KAAK,EAAE;MACjDC,OAAO,CAACD,KAAK,CAACH,IAAI,CAAC;IACrB,CAAC,MAAM,IAAID,KAAK,CAACG,KAAK,KAAK,SAAS,EAAE;MACpCE,OAAO,CAACC,IAAI,CAACL,IAAI,CAAC;IACpB,CAAC,MAAM;MACLI,OAAO,CAACE,GAAG,CAACN,IAAI,CAAC;IACnB;EACF;EAEAO,KAAKA,CAAA,EAAS;IACZ;EAAA;EAGFC,OAAOA,CAAA,EAAS;IACd,IAAI,CAACZ,UAAU,GAAGC,SAAS;EAC7B;AACF","ignoreList":[]}
@@ -0,0 +1,360 @@
1
+ "use strict";
2
+
3
+ import { Batcher } from "./Batcher.js";
4
+ import { JsonLinesFormatter } from "../formatters/JsonLinesFormatter.js";
5
+ import { utf8Length } from "../utf8.js";
6
+
7
+ /**
8
+ * The part of `FileSink` a `FileDestination` uses.
9
+ *
10
+ * Structural rather than the Nitro interface itself, so the destination can
11
+ * be driven by a scripted double in tests. The real `FileSink` satisfies it.
12
+ */
13
+
14
+ const DEFAULT_LABEL = 'file';
15
+ const DEFAULT_FILENAME = 'app.log';
16
+ const DEFAULT_MAX_ENTRY_BYTES = 64 * 1024;
17
+ const DEFAULT_DEADLINE_MS = 2000;
18
+
19
+ /** Fixed text: a diagnostic that interpolates caller data is a log-injection
20
+ * hole and a privacy hole at once. Only counts travel with it. */
21
+ const LOSS_MESSAGE = 'log entries were dropped';
22
+ const OVERSIZE_MESSAGE = 'a log entry was too large to record';
23
+
24
+ /**
25
+ * Writes formatted records to a native file sink, batched and backpressured.
26
+ *
27
+ * The division of labour is the whole design: this side decides what to write
28
+ * and what to give up on, the sink only appends text and reports counters.
29
+ * Everything interesting — batching, drop policy, loss notices, the purge
30
+ * handshake — is therefore testable without a device.
31
+ *
32
+ * Three behaviours are worth knowing about before using it.
33
+ *
34
+ * **Oversized entries are never sliced.** A formatter that can shed structure
35
+ * is asked to re-render within the limit; one that cannot has its output
36
+ * replaced whole by a fixed notice. Cutting a rendered record to length is
37
+ * how a log file stops being parseable, and an unparseable file is worth less
38
+ * than an honestly incomplete one.
39
+ *
40
+ * **Framing is declared, not assumed.** A formatter guaranteeing one record
41
+ * per line lets the native side trim a torn trailing record after a crash.
42
+ * Without that guarantee the file is left as the crash left it, because a
43
+ * writer cannot tell a torn record from an intentional newline.
44
+ *
45
+ * **A fenced handle stays fenced.** When the sink reports that this handle's
46
+ * generation is stale — someone purged the file underneath it — the buffer is
47
+ * discarded rather than replayed into the new file, and the destination
48
+ * disables itself. Pre-purge records must not reappear after a compliance
49
+ * deletion. The handle that invoked the purge rebinds; any other one does
50
+ * not, and reacquisition is the native registry's job.
51
+ */
52
+ export class FileDestination {
53
+ /** Held for the M5 registry's reacquisition path, which needs the same
54
+ * config the handle was opened with. */
55
+
56
+ fenced = false;
57
+ disposed = false;
58
+ constructor(sink, options = {}) {
59
+ this.sink = sink;
60
+ this.label = options.label ?? DEFAULT_LABEL;
61
+ this.minimumLevel = options.minimumLevel;
62
+ this.formatter = options.formatter ?? new JsonLinesFormatter();
63
+ this.maxEntryBytes = positive(options.maxEntryBytes, DEFAULT_MAX_ENTRY_BYTES);
64
+ this.path = options.path ?? `${sink.defaultLogDirectory}/${DEFAULT_FILENAME}`;
65
+ this.rotation = options.rotation;
66
+ this.batcher = new Batcher(sink, {
67
+ renderNotice: lost => this.renderLossNotice(lost),
68
+ onFenced: reason => this.onFenced(reason),
69
+ batchBytes: options.batchBytes,
70
+ flushIntervalMs: options.flushIntervalMs,
71
+ maxPendingEntries: options.maxPendingEntries,
72
+ maxPendingBytes: options.maxPendingBytes,
73
+ watermarkBytes: options.watermarkBytes
74
+ });
75
+
76
+ // Throws on open failure or on a config conflict with an existing writer,
77
+ // and deliberately so: a file destination that silently writes nowhere is
78
+ // worse than one that refuses to be constructed.
79
+ sink.open(this.path, this.rotation, this.lineFramed);
80
+ }
81
+
82
+ /** True only while this handle may write. */
83
+ get isEnabled() {
84
+ return !this.fenced && !this.disposed;
85
+ }
86
+
87
+ /** Where records are going. */
88
+ get filePath() {
89
+ return this.path;
90
+ }
91
+
92
+ /**
93
+ * Whether the native side may trim a torn trailing record after a crash.
94
+ * Follows the formatter's declaration; nothing infers it.
95
+ */
96
+ get lineFramed() {
97
+ return this.formatter.framing === 'line';
98
+ }
99
+ write(entry) {
100
+ if (!this.isEnabled) return;
101
+ const record = this.renderRecord(entry);
102
+ if (record === undefined) return;
103
+ this.batcher.add(record);
104
+ }
105
+
106
+ /**
107
+ * Drain to disk and fsync, bounded by a wall-clock deadline.
108
+ *
109
+ * Returns the outcome even though `LogDestination` asks only for `void`: a
110
+ * caller flushing before backgrounding or on a crash path needs to know
111
+ * whether it worked, and `durable: false` is the answer that matters.
112
+ */
113
+ flush(deadlineMs = DEFAULT_DEADLINE_MS) {
114
+ return this.batcher.flush(deadlineMs);
115
+ }
116
+
117
+ /** Current file and archives, newest first; empty if the sink refuses. */
118
+ getLogFilePaths() {
119
+ try {
120
+ return this.sink.getLogFilePaths();
121
+ } catch {
122
+ return [];
123
+ }
124
+ }
125
+
126
+ /** Losses with no notice in the file yet. */
127
+ unreportedLoss() {
128
+ return this.batcher.unreported();
129
+ }
130
+
131
+ /**
132
+ * What has stopped working underneath, as a payload-free bitmask —
133
+ * rotation, gzip, prune, sidecar, protection.
134
+ *
135
+ * The mask exists because the alternative is a message, and a message built
136
+ * from an `errno` string or a path is exactly the kind of thing that carries
137
+ * a username into a log file. Bits carry no payload, so an app can surface
138
+ * "log rotation is failing" to support without surfacing anything else.
139
+ *
140
+ * Non-zero does not mean records are being lost — a failed compression or a
141
+ * failed prune costs disk, not data. Check {@link unreportedLoss} for that.
142
+ */
143
+ degradation() {
144
+ return this.batcher.degradation();
145
+ }
146
+
147
+ /**
148
+ * Delete every log artifact, then rebind.
149
+ *
150
+ * The ordering is the point. This handle is fenced first, so nothing can be
151
+ * written into the window where deletion is in flight; the JS buffer is
152
+ * discarded rather than flushed, because flushing it would write pre-purge
153
+ * records into the file a moment before deleting it — or worse, a moment
154
+ * after. Only a durable deletion lifts the fence. A partial or timed-out
155
+ * one leaves this handle disabled until an explicit retry, so a late
156
+ * deletion can never race a fresh write.
157
+ *
158
+ * Discarded counts come back here rather than going into a loss notice: a
159
+ * "4,182 entries dropped" line at the top of a file that was just cleared
160
+ * for compliance describes the clearing, which the caller already knows
161
+ * about.
162
+ */
163
+ purge(deadlineMs = DEFAULT_DEADLINE_MS) {
164
+ // A disposed destination has closed its sink, and a closed sink has no
165
+ // handle left to delete through — so this call cannot do the one thing it
166
+ // exists to do. Saying so is the entire point: the alternative is a
167
+ // `durable: true` over files that are still on disk, told to the one caller
168
+ // who asked because they are legally obliged to know. `removeDestination()`
169
+ // then a compliance purge is an ordinary sequence, not a contrived one.
170
+ if (this.disposed) {
171
+ return {
172
+ durable: false,
173
+ rebound: false,
174
+ deletedCount: 0,
175
+ failedPaths: [],
176
+ discardedEntries: 0,
177
+ discardedBytes: 0
178
+ };
179
+ }
180
+ this.fenced = true;
181
+ const discarded = this.batcher.discard();
182
+ let outcome;
183
+ try {
184
+ outcome = this.sink.clearLogs(deadlineMs);
185
+ } catch {
186
+ return {
187
+ durable: false,
188
+ // The call threw, so nothing is known about the sink's state and the
189
+ // fence set above stays up. Reporting a rebind here would lift it.
190
+ rebound: false,
191
+ deletedCount: 0,
192
+ failedPaths: [],
193
+ discardedEntries: discarded.entries,
194
+ discardedBytes: discarded.bytes
195
+ };
196
+ }
197
+
198
+ // Both facts, not just the first. `durable` says the artifacts are gone;
199
+ // `rebound` says the sink has a usable file again. Lifting the fence on
200
+ // `durable` alone resumes writing into a handle the sink never rebound —
201
+ // every record accepted, rejected as `staleGeneration`, and dropped. The
202
+ // deletion is still complete and still reported as such; the destination
203
+ // just stays disabled until an explicit retry gets a live file back.
204
+ //
205
+ // `=== true`, not `!== false`. An absent field is not a promise, and the
206
+ // sink most likely to omit it is one that predates it — including a native
207
+ // build that deletes by closing and never reopens at all. A JS bundle can
208
+ // be updated over the air without the binary underneath it changing, so
209
+ // "old native, new JS" is a real pairing, and reading silence as success
210
+ // there rebuilds exactly the silent post-purge loss this flag exists to
211
+ // stop. Staying fenced until a retry is the failure that announces itself.
212
+ const resumed = outcome.durable && outcome.rebound === true;
213
+ if (resumed) {
214
+ this.fenced = this.disposed;
215
+ this.batcher.rebind();
216
+ }
217
+ return {
218
+ durable: outcome.durable,
219
+ rebound: resumed,
220
+ deletedCount: count(outcome.deletedCount),
221
+ failedPaths: Array.isArray(outcome.failedPaths) ? outcome.failedPaths : [],
222
+ discardedEntries: discarded.entries,
223
+ discardedBytes: discarded.bytes
224
+ };
225
+ }
226
+
227
+ /** Idempotent: flush what is buffered, release the timer, close the sink. */
228
+ dispose() {
229
+ if (this.disposed) return;
230
+ try {
231
+ this.batcher.flush(DEFAULT_DEADLINE_MS);
232
+ } catch {
233
+ // a failing drain must not leak the handle below
234
+ }
235
+ this.disposed = true;
236
+ this.batcher.dispose();
237
+ try {
238
+ this.sink.close(DEFAULT_DEADLINE_MS);
239
+ } catch {
240
+ // nothing left to salvage
241
+ }
242
+ }
243
+
244
+ // ── Internals ───────────────────────────────────────────────────────────
245
+
246
+ /**
247
+ * Render one entry, or account for why it could not be rendered.
248
+ *
249
+ * Returns undefined when nothing writable came out; the loss is recorded
250
+ * before returning, so an entry is either in the file or in the counters
251
+ * and never in neither.
252
+ */
253
+ renderRecord(entry) {
254
+ const record = this.formatOrUndefined(entry);
255
+ if (record === undefined) {
256
+ this.batcher.noteLoss(1, 0);
257
+ return undefined;
258
+ }
259
+
260
+ // The ORIGINAL size, held onto for the rest of this method. What gets
261
+ // discarded is the entry the caller wrote, so that is the number the
262
+ // counters and the notice have to carry — not the size of whatever
263
+ // undersized floor the formatter came back with instead.
264
+ const bytes = utf8Length(record);
265
+ if (bytes <= this.maxEntryBytes) return record;
266
+
267
+ // Structural shedding first: a formatter that knows its own shape can
268
+ // drop whole fields and truncate one at code-point boundaries, and what
269
+ // comes back is still valid in that format.
270
+ if (this.formatter.formatWithin) {
271
+ const shorter = this.formatWithinOrUndefined(entry);
272
+ if (shorter !== undefined && utf8Length(shorter) <= this.maxEntryBytes) {
273
+ return shorter;
274
+ }
275
+ // Still over: a record has a floor below which it identifies nothing.
276
+ }
277
+
278
+ // Nothing renderable fits, so the entry is replaced whole by a notice in
279
+ // the same format. Never sliced: a fragment of a record is not a record.
280
+ this.batcher.noteLoss(1, bytes);
281
+ return this.boundedNotice(noticeEntry(entry.timestamp, OVERSIZE_MESSAGE, {
282
+ droppedBytes: bytes
283
+ }));
284
+ }
285
+ renderLossNotice(lost) {
286
+ return this.boundedNotice(noticeEntry(Date.now(), LOSS_MESSAGE, {
287
+ droppedEntries: lost.entries,
288
+ droppedBytes: lost.bytes
289
+ }));
290
+ }
291
+
292
+ /**
293
+ * Render one of the pipeline's own notices under the same per-entry limit
294
+ * as everything else.
295
+ *
296
+ * The limit exists so a batch always fits the sink. A notice exempt from it
297
+ * could be refused forever by a sink that will never take it, and the loss
298
+ * it describes would sit undelivered behind it — the pipeline's own
299
+ * diagnostics jamming the pipeline. Returning undefined when nothing fits
300
+ * leaves the loss counted and owed, which is the honest outcome.
301
+ */
302
+ boundedNotice(entry) {
303
+ const text = this.formatOrUndefined(entry);
304
+ if (text === undefined) return undefined;
305
+ if (utf8Length(text) <= this.maxEntryBytes) return text;
306
+ if (this.formatter.formatWithin) {
307
+ const shorter = this.formatWithinOrUndefined(entry);
308
+ if (shorter !== undefined && utf8Length(shorter) <= this.maxEntryBytes) {
309
+ return shorter;
310
+ }
311
+ }
312
+ return undefined;
313
+ }
314
+ formatOrUndefined(entry) {
315
+ try {
316
+ const text = this.formatter.format(entry);
317
+ return typeof text === 'string' ? text : undefined;
318
+ } catch {
319
+ return undefined;
320
+ }
321
+ }
322
+ formatWithinOrUndefined(entry) {
323
+ try {
324
+ const text = this.formatter.formatWithin(entry, this.maxEntryBytes);
325
+ return typeof text === 'string' ? text : undefined;
326
+ } catch {
327
+ return undefined;
328
+ }
329
+ }
330
+ onFenced(_reason) {
331
+ this.fenced = true;
332
+ }
333
+ }
334
+
335
+ /**
336
+ * A synthetic entry describing the pipeline's own state.
337
+ *
338
+ * Built directly rather than routed through the logger: it carries only
339
+ * counts, so there is nothing for redaction to protect, and going through the
340
+ * logger would put it behind the very destination that is trying to report a
341
+ * problem.
342
+ */
343
+ function noticeEntry(timestamp, message, metadata) {
344
+ return Object.freeze({
345
+ timestamp,
346
+ level: 'warning',
347
+ message,
348
+ metadata: Object.freeze({
349
+ ...metadata
350
+ }),
351
+ subsystem: 'logger'
352
+ });
353
+ }
354
+ function positive(value, fallback) {
355
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
356
+ }
357
+ function count(value) {
358
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
359
+ }
360
+ //# sourceMappingURL=FileDestination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Batcher","JsonLinesFormatter","utf8Length","DEFAULT_LABEL","DEFAULT_FILENAME","DEFAULT_MAX_ENTRY_BYTES","DEFAULT_DEADLINE_MS","LOSS_MESSAGE","OVERSIZE_MESSAGE","FileDestination","fenced","disposed","constructor","sink","options","label","minimumLevel","formatter","maxEntryBytes","positive","path","defaultLogDirectory","rotation","batcher","renderNotice","lost","renderLossNotice","onFenced","reason","batchBytes","flushIntervalMs","maxPendingEntries","maxPendingBytes","watermarkBytes","open","lineFramed","isEnabled","filePath","framing","write","entry","record","renderRecord","undefined","add","flush","deadlineMs","getLogFilePaths","unreportedLoss","unreported","degradation","purge","durable","rebound","deletedCount","failedPaths","discardedEntries","discardedBytes","discarded","discard","outcome","clearLogs","entries","bytes","resumed","rebind","count","Array","isArray","dispose","close","formatOrUndefined","noteLoss","formatWithin","shorter","formatWithinOrUndefined","boundedNotice","noticeEntry","timestamp","droppedBytes","Date","now","droppedEntries","text","format","_reason","message","metadata","Object","freeze","level","subsystem","value","fallback","Number","isFinite","Math","floor"],"sourceRoot":"../../../src","sources":["destinations/FileDestination.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+
3
+ import { DefaultFormatter } from "../formatters/DefaultFormatter.js";
4
+ import { LEVEL_ORDER } from "../levels.js";
5
+
6
+ /**
7
+ * The part of `NativeConsoleSink` this destination uses.
8
+ *
9
+ * Structural rather than the Nitro interface itself, so tests can drive it
10
+ * with a double. The real `NativeConsoleSink` satisfies it.
11
+ */
12
+
13
+ const DEFAULT_BATCH_SIZE = 64;
14
+ const DEFAULT_FLUSH_INTERVAL_MS = 100;
15
+ const DEFAULT_MAX_PENDING = 1000;
16
+
17
+ /**
18
+ * Number of consecutive failed `logBatch` calls before the destination stops
19
+ * trying. A native sink that is throwing every time will not start working
20
+ * because we asked once more, and a try/catch per drain forever is a cost the
21
+ * app pays for nothing.
22
+ */
23
+ const FAILURE_LIMIT = 3;
24
+
25
+ /**
26
+ * Renders entries into the platform log stream — os_log on iOS, logcat on
27
+ * Android — so JS logs interleave with native ones in Console.app and Xcode.
28
+ *
29
+ * Batched, unlike {@link ConsoleDestination}: one bridge crossing per drain
30
+ * with two parallel primitive arrays, rather than a crossing per entry.
31
+ *
32
+ * Deliberately simpler than {@link FileDestination}. There is no backpressure
33
+ * and no loss accounting because there is nothing to push back on: os_log
34
+ * accepts what it is given and never blocks on a disk. What this destination
35
+ * does account for is its own buffer — entries dropped at the ceiling, and
36
+ * entries lost to a throwing sink — through {@link dropped}, because a
37
+ * diagnostic channel that quietly loses records is worse than one that says
38
+ * it did.
39
+ *
40
+ * The durable copy is the file destination's. Nothing here is a system of
41
+ * record, which is what makes best-effort the right posture.
42
+ */
43
+ export class NativeConsoleDestination {
44
+ levels = [];
45
+ messages = [];
46
+ disposed = false;
47
+ failures = 0;
48
+ droppedEntries = 0;
49
+ constructor(sink, options) {
50
+ this.sink = sink;
51
+ this.label = options?.label ?? 'native-console';
52
+ this.minimumLevel = options?.minimumLevel;
53
+ this.formatter = options?.formatter ?? new DefaultFormatter();
54
+ this.batchSize = positive(options?.batchSize, DEFAULT_BATCH_SIZE);
55
+ this.flushIntervalMs = positive(options?.flushIntervalMs, DEFAULT_FLUSH_INTERVAL_MS);
56
+ this.maxPending = positive(options?.maxPendingEntries, DEFAULT_MAX_PENDING);
57
+
58
+ // An empty subsystem is legal and produces a logger nobody can find in
59
+ // Console; the native side substitutes the bundle identifier for it.
60
+ this.sink.install(options?.subsystem ?? '', options?.category ?? 'log');
61
+ }
62
+
63
+ /**
64
+ * False once disposed, or once the native sink has failed enough times in a
65
+ * row to be considered gone. The Logger checks this before formatting, so a
66
+ * dead sink stops costing render work as well as bridge calls.
67
+ */
68
+ get isEnabled() {
69
+ return !this.disposed && this.failures < FAILURE_LIMIT;
70
+ }
71
+
72
+ /** Entries this destination never handed to the platform log. */
73
+ dropped() {
74
+ return this.droppedEntries;
75
+ }
76
+ write(entry) {
77
+ if (!this.isEnabled) return;
78
+ if (this.messages.length >= this.maxPending) {
79
+ // Drop-newest, matching the Batcher. Under a burst the oldest entries
80
+ // are the ones that explain how it started, which is what a reader is
81
+ // looking for; keeping the newest would leave only the aftermath.
82
+ this.droppedEntries += 1;
83
+ return;
84
+ }
85
+
86
+ // Render first, then append both halves together. The two arrays are
87
+ // parallel and are read by index natively, so pushing the level before the
88
+ // message that might not arrive leaves them one apart — and from that point
89
+ // on every entry in the batch is logged at its predecessor's severity. The
90
+ // Logger catches a throwing destination and carries on, which is exactly
91
+ // what would let the misalignment survive unnoticed.
92
+ let rendered;
93
+ try {
94
+ rendered = this.formatter.format(entry);
95
+ } catch {
96
+ // Not counted against the sink's failure run: the sink is fine, the
97
+ // formatter is not, and disabling the channel over someone else's bug
98
+ // would take the console down for every entry a working formatter could
99
+ // still have rendered.
100
+ this.droppedEntries += 1;
101
+ return;
102
+ }
103
+ this.levels.push(LEVEL_ORDER[entry.level]);
104
+ this.messages.push(rendered);
105
+ if (this.messages.length >= this.batchSize) {
106
+ this.drain();
107
+ return;
108
+ }
109
+ this.arm();
110
+ }
111
+
112
+ /**
113
+ * Hands everything buffered to the platform log.
114
+ *
115
+ * The deadline is accepted and ignored, deliberately: `logBatch` does not
116
+ * wait on anything, so there is no wait to bound. Taking the parameter keeps
117
+ * the destination interchangeable with the ones that do need it.
118
+ */
119
+ flush(_deadlineMs) {
120
+ this.drain();
121
+ }
122
+ dispose() {
123
+ if (this.disposed) return;
124
+ // Flush before the flag, not after: entries buffered up to this moment
125
+ // were accepted, and disposing is not a reason to drop them.
126
+ this.drain();
127
+ this.disposed = true;
128
+ this.cancel();
129
+ }
130
+ drain() {
131
+ this.cancel();
132
+ if (this.messages.length === 0) return;
133
+ const levels = this.levels;
134
+ const messages = this.messages;
135
+ this.levels = [];
136
+ this.messages = [];
137
+ if (this.disposed) return;
138
+ try {
139
+ this.sink.logBatch(levels, messages);
140
+ this.failures = 0;
141
+ } catch {
142
+ // Payload-free: whatever the native side threw is not worth risking on
143
+ // the way back through a logger. The batch is gone either way, so it is
144
+ // counted rather than retried — replaying it against a sink that just
145
+ // rejected it only doubles the chance of the same failure.
146
+ this.failures += 1;
147
+ this.droppedEntries += messages.length;
148
+ }
149
+ }
150
+ arm() {
151
+ if (this.timer !== undefined) return;
152
+ this.timer = setTimeout(() => {
153
+ this.timer = undefined;
154
+ this.drain();
155
+ }, this.flushIntervalMs);
156
+ }
157
+ cancel() {
158
+ if (this.timer === undefined) return;
159
+ clearTimeout(this.timer);
160
+ this.timer = undefined;
161
+ }
162
+ }
163
+
164
+ /** Finite positive integers only; anything else falls back. */
165
+ function positive(value, fallback) {
166
+ if (value === undefined) return fallback;
167
+ if (!Number.isSafeInteger(value) || value < 1) return fallback;
168
+ return value;
169
+ }
170
+ //# sourceMappingURL=NativeConsoleDestination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["DefaultFormatter","LEVEL_ORDER","DEFAULT_BATCH_SIZE","DEFAULT_FLUSH_INTERVAL_MS","DEFAULT_MAX_PENDING","FAILURE_LIMIT","NativeConsoleDestination","levels","messages","disposed","failures","droppedEntries","constructor","sink","options","label","minimumLevel","formatter","batchSize","positive","flushIntervalMs","maxPending","maxPendingEntries","install","subsystem","category","isEnabled","dropped","write","entry","length","rendered","format","push","level","drain","arm","flush","_deadlineMs","dispose","cancel","logBatch","timer","undefined","setTimeout","clearTimeout","value","fallback","Number","isSafeInteger"],"sourceRoot":"../../../src","sources":["destinations/NativeConsoleDestination.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../../src","sources":["destinations/types.ts"],"mappings":"","ignoreList":[]}