@cryptotaxi247 / netdata-1 / commits / 95a9d02f6

Remove unused crates. (#21735)

https://github.com/netdata/netdata/pull/21729 brought a lot of unused code. It was not removed in the aforementioned PR in order to keep git's history clean. Now that the PR is merged we can simply remove it from our repository.

vkalintiris committed Feb 12, 2026 at 00:50 UTC 95a9d02f67bc04e90a63165882990488b61daa2a
28 files changed +392 -6199
src/crates/jf/Cargo.lock
+392 -1781
@@ -2,44 +2,11 @@
2 # It is not intended for manual editing.
3 version = 4
4
5 -[[package]]
6 -name = "addr2line"
7 -version = "0.25.1"
8 -source = "registry+https://github.com/rust-lang/crates.io-index"
9 -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
10 -dependencies = [
11 - "gimli",
12 -]
13 -
14 -[[package]]
15 -name = "adler2"
16 -version = "2.0.1"
17 -source = "registry+https://github.com/rust-lang/crates.io-index"
18 -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
19 -
20 -[[package]]
21 -name = "aho-corasick"
22 -version = "1.1.3"
23 -source = "registry+https://github.com/rust-lang/crates.io-index"
24 -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
25 -dependencies = [
26 - "memchr",
27 -]
28 -
29 -[[package]]
30 -name = "android_system_properties"
31 -version = "0.1.5"
32 -source = "registry+https://github.com/rust-lang/crates.io-index"
33 -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
34 -dependencies = [
35 - "libc",
36 -]
37 -
5 [[package]]
6 name = "anstream"
40 -version = "0.6.20"
7 +version = "0.6.21"
8 source = "registry+https://github.com/rust-lang/crates.io-index"
42 -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
9 +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
10 dependencies = [
11 "anstyle",
12 "anstyle-parse",
@@ -67,167 +34,35 @@ dependencies = [
34
35 [[package]]
36 name = "anstyle-query"
70 -version = "1.1.4"
37 +version = "1.1.5"
38 source = "registry+https://github.com/rust-lang/crates.io-index"
72 -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
39 +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
40 dependencies = [
74 - "windows-sys 0.60.2",
41 + "windows-sys",
42 ]
43
44 [[package]]
45 name = "anstyle-wincon"
79 -version = "3.0.10"
46 +version = "3.0.11"
47 source = "registry+https://github.com/rust-lang/crates.io-index"
81 -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
48 +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
49 dependencies = [
50 "anstyle",
51 "once_cell_polyfill",
85 - "windows-sys 0.60.2",
52 + "windows-sys",
53 ]
54
55 [[package]]
56 name = "anyhow"
90 -version = "1.0.100"
91 -source = "registry+https://github.com/rust-lang/crates.io-index"
92 -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
93 -
94 -[[package]]
95 -name = "async-trait"
96 -version = "0.1.89"
97 -source = "registry+https://github.com/rust-lang/crates.io-index"
98 -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
99 -dependencies = [
100 - "proc-macro2",
101 - "quote",
102 - "syn",
103 -]
104 -
105 -[[package]]
106 -name = "atomic-waker"
107 -version = "1.1.2"
108 -source = "registry+https://github.com/rust-lang/crates.io-index"
109 -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
110 -
111 -[[package]]
112 -name = "atty"
113 -version = "0.2.14"
114 -source = "registry+https://github.com/rust-lang/crates.io-index"
115 -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
116 -dependencies = [
117 - "hermit-abi",
118 - "libc",
119 - "winapi",
120 -]
121 -
122 -[[package]]
123 -name = "autocfg"
124 -version = "1.5.0"
125 -source = "registry+https://github.com/rust-lang/crates.io-index"
126 -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
127 -
128 -[[package]]
129 -name = "axum"
130 -version = "0.8.5"
131 -source = "registry+https://github.com/rust-lang/crates.io-index"
132 -checksum = "98e529aee37b5c8206bb4bf4c44797127566d72f76952c970bd3d1e85de8f4e2"
133 -dependencies = [
134 - "axum-core",
135 - "bytes",
136 - "futures-util",
137 - "http",
138 - "http-body",
139 - "http-body-util",
140 - "itoa",
141 - "matchit",
142 - "memchr",
143 - "mime",
144 - "percent-encoding",
145 - "pin-project-lite",
146 - "serde_core",
147 - "sync_wrapper",
148 - "tower",
149 - "tower-layer",
150 - "tower-service",
151 -]
152 -
153 -[[package]]
154 -name = "axum-core"
155 -version = "0.5.4"
156 -source = "registry+https://github.com/rust-lang/crates.io-index"
157 -checksum = "0ac7a6beb1182c7e30253ee75c3e918080bfb83f5a3023bcdf7209d85fd147e6"
158 -dependencies = [
159 - "bytes",
160 - "futures-core",
161 - "http",
162 - "http-body",
163 - "http-body-util",
164 - "mime",
165 - "pin-project-lite",
166 - "sync_wrapper",
167 - "tower-layer",
168 - "tower-service",
169 -]
170 -
171 -[[package]]
172 -name = "backtrace"
173 -version = "0.3.76"
174 -source = "registry+https://github.com/rust-lang/crates.io-index"
175 -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
176 -dependencies = [
177 - "addr2line",
178 - "cfg-if",
179 - "libc",
180 - "miniz_oxide",
181 - "object",
182 - "rustc-demangle",
183 - "windows-link",
184 -]
185 -
186 -[[package]]
187 -name = "base64"
188 -version = "0.21.7"
189 -source = "registry+https://github.com/rust-lang/crates.io-index"
190 -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
191 -
192 -[[package]]
193 -name = "base64"
194 -version = "0.22.1"
57 +version = "1.0.101"
58 source = "registry+https://github.com/rust-lang/crates.io-index"
196 -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
59 +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
60
61 [[package]]
62 name = "bitflags"
200 -version = "2.9.4"
201 -source = "registry+https://github.com/rust-lang/crates.io-index"
202 -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
203 -
204 -[[package]]
205 -name = "bumpalo"
206 -version = "3.19.0"
207 -source = "registry+https://github.com/rust-lang/crates.io-index"
208 -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
209 -
210 -[[package]]
211 -name = "bytes"
212 -version = "1.10.1"
213 -source = "registry+https://github.com/rust-lang/crates.io-index"
214 -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
215 -
216 -[[package]]
217 -name = "bytesize"
218 -version = "1.3.3"
219 -source = "registry+https://github.com/rust-lang/crates.io-index"
220 -checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659"
221 -
222 -[[package]]
223 -name = "bytesize-serde"
224 -version = "0.2.1"
63 +version = "2.10.0"
64 source = "registry+https://github.com/rust-lang/crates.io-index"
226 -checksum = "86d1eb2fd2668859e9785b99700c66b7ea9fdeda35d99f95827a4e5493440178"
227 -dependencies = [
228 - "bytesize",
229 - "serde",
230 -]
65 +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
66
67 [[package]]
68 name = "cbindgen"
@@ -248,51 +83,26 @@ dependencies = [
83 "toml",
84 ]
85
251 -[[package]]
252 -name = "cc"
253 -version = "1.2.39"
254 -source = "registry+https://github.com/rust-lang/crates.io-index"
255 -checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f"
256 -dependencies = [
257 - "find-msvc-tools",
258 - "shlex",
259 -]
260 -
86 [[package]]
87 name = "cfg-if"
263 -version = "1.0.3"
264 -source = "registry+https://github.com/rust-lang/crates.io-index"
265 -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
266 -
267 -[[package]]
268 -name = "chrono"
269 -version = "0.4.42"
88 +version = "1.0.4"
89 source = "registry+https://github.com/rust-lang/crates.io-index"
271 -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
272 -dependencies = [
273 - "iana-time-zone",
274 - "js-sys",
275 - "num-traits",
276 - "serde",
277 - "wasm-bindgen",
278 - "windows-link",
279 -]
90 +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
91
92 [[package]]
93 name = "clap"
283 -version = "4.5.48"
94 +version = "4.5.57"
95 source = "registry+https://github.com/rust-lang/crates.io-index"
285 -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
96 +checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a"
97 dependencies = [
98 "clap_builder",
288 - "clap_derive",
99 ]
100
101 [[package]]
102 name = "clap_builder"
293 -version = "4.5.48"
103 +version = "4.5.57"
104 source = "registry+https://github.com/rust-lang/crates.io-index"
295 -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
105 +checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238"
106 dependencies = [
107 "anstream",
108 "anstyle",
@@ -300,23 +110,11 @@ dependencies = [
110 "strsim",
111 ]
112
303 -[[package]]
304 -name = "clap_derive"
305 -version = "4.5.47"
306 -source = "registry+https://github.com/rust-lang/crates.io-index"
307 -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
308 -dependencies = [
309 - "heck 0.5.0",
310 - "proc-macro2",
311 - "quote",
312 - "syn",
313 -]
314 -
113 [[package]]
114 name = "clap_lex"
317 -version = "0.7.5"
115 +version = "0.7.7"
116 source = "registry+https://github.com/rust-lang/crates.io-index"
319 -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
117 +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
118
119 [[package]]
120 name = "colorchoice"
@@ -324,27 +122,6 @@ version = "1.0.4"
122 source = "registry+https://github.com/rust-lang/crates.io-index"
123 checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
124
327 -[[package]]
328 -name = "core-foundation-sys"
329 -version = "0.8.7"
330 -source = "registry+https://github.com/rust-lang/crates.io-index"
331 -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
332 -
333 -[[package]]
334 -name = "crc32fast"
335 -version = "1.5.0"
336 -source = "registry+https://github.com/rust-lang/crates.io-index"
337 -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
338 -dependencies = [
339 - "cfg-if",
340 -]
341 -
342 -[[package]]
343 -name = "either"
344 -version = "1.15.0"
345 -source = "registry+https://github.com/rust-lang/crates.io-index"
346 -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
347 -
125 [[package]]
126 name = "equivalent"
127 version = "1.0.2"
@@ -358,7 +135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
135 checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
136 dependencies = [
137 "libc",
361 - "windows-sys 0.61.1",
138 + "windows-sys",
139 ]
140
141 [[package]]
@@ -377,1810 +154,644 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
154 checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
155
156 [[package]]
380 -name = "find-msvc-tools"
381 -version = "0.1.2"
382 -source = "registry+https://github.com/rust-lang/crates.io-index"
383 -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959"
384 -
385 -[[package]]
386 -name = "fixedbitset"
387 -version = "0.5.7"
157 +name = "foldhash"
158 +version = "0.1.5"
159 source = "registry+https://github.com/rust-lang/crates.io-index"
389 -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
160 +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
161
162 [[package]]
392 -name = "flate2"
393 -version = "1.1.2"
163 +name = "getrandom"
164 +version = "0.3.4"
165 source = "registry+https://github.com/rust-lang/crates.io-index"
395 -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
166 +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
167 dependencies = [
397 - "crc32fast",
398 - "miniz_oxide",
168 + "cfg-if",
169 + "libc",
170 + "r-efi",
171 + "wasip2",
172 ]
173
174 [[package]]
402 -name = "flatten-serde-json"
403 -version = "1.22.1"
404 -source = "git+https://github.com/meilisearch/meilisearch?tag=v1.22.1#077ec2ab11bb4daefcb57f89eab9cff16e075fdc"
175 +name = "getrandom"
176 +version = "0.4.1"
177 +source = "registry+https://github.com/rust-lang/crates.io-index"
178 +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
179 dependencies = [
406 - "serde_json",
180 + "cfg-if",
181 + "libc",
182 + "r-efi",
183 + "wasip2",
184 + "wasip3",
185 ]
186
187 [[package]]
410 -name = "flatten_otel"
411 -version = "0.1.0"
188 +name = "hashbrown"
189 +version = "0.15.5"
190 +source = "registry+https://github.com/rust-lang/crates.io-index"
191 +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
192 dependencies = [
413 - "flatten-serde-json",
414 - "opentelemetry-proto",
415 - "serde_json",
193 + "foldhash",
194 ]
195
196 [[package]]
419 -name = "flog-otel"
420 -version = "0.1.0"
421 -dependencies = [
422 - "anyhow",
423 - "chrono",
424 - "clap",
425 - "opentelemetry-proto",
426 - "prost",
427 - "serde",
428 - "serde_json",
429 - "tokio",
430 - "tonic",
431 - "tonic-build",
432 - "tracing",
433 - "tracing-subscriber",
434 - "uuid",
435 -]
197 +name = "hashbrown"
198 +version = "0.16.1"
199 +source = "registry+https://github.com/rust-lang/crates.io-index"
200 +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
201
202 [[package]]
438 -name = "fnv"
439 -version = "1.0.7"
203 +name = "heck"
204 +version = "0.4.1"
205 source = "registry+https://github.com/rust-lang/crates.io-index"
441 -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
206 +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
207
208 [[package]]
444 -name = "futures-channel"
445 -version = "0.3.31"
209 +name = "heck"
210 +version = "0.5.0"
211 source = "registry+https://github.com/rust-lang/crates.io-index"
447 -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
448 -dependencies = [
449 - "futures-core",
450 -]
212 +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
213
214 [[package]]
453 -name = "futures-core"
454 -version = "0.3.31"
215 +name = "hex"
216 +version = "0.4.3"
217 source = "registry+https://github.com/rust-lang/crates.io-index"
456 -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
218 +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
219
220 [[package]]
459 -name = "futures-executor"
460 -version = "0.3.31"
221 +name = "id-arena"
222 +version = "2.3.0"
223 source = "registry+https://github.com/rust-lang/crates.io-index"
462 -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
463 -dependencies = [
464 - "futures-core",
465 - "futures-task",
466 - "futures-util",
467 -]
224 +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
225
226 [[package]]
470 -name = "futures-macro"
471 -version = "0.3.31"
227 +name = "indexmap"
228 +version = "2.13.0"
229 source = "registry+https://github.com/rust-lang/crates.io-index"
473 -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
230 +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
231 dependencies = [
475 - "proc-macro2",
476 - "quote",
477 - "syn",
232 + "equivalent",
233 + "hashbrown 0.16.1",
234 + "serde",
235 + "serde_core",
236 ]
237
238 [[package]]
481 -name = "futures-sink"
482 -version = "0.3.31"
483 -source = "registry+https://github.com/rust-lang/crates.io-index"
484 -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
485 -
486 -[[package]]
487 -name = "futures-task"
488 -version = "0.3.31"
239 +name = "is_terminal_polyfill"
240 +version = "1.70.2"
241 source = "registry+https://github.com/rust-lang/crates.io-index"
490 -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
242 +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
243
244 [[package]]
493 -name = "futures-util"
494 -version = "0.3.31"
245 +name = "itoa"
246 +version = "1.0.17"
247 source = "registry+https://github.com/rust-lang/crates.io-index"
496 -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
497 -dependencies = [
498 - "futures-core",
499 - "futures-macro",
500 - "futures-sink",
501 - "futures-task",
502 - "pin-project-lite",
503 - "pin-utils",
504 - "slab",
505 -]
248 +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
249
250 [[package]]
508 -name = "getrandom"
509 -version = "0.2.16"
510 -source = "registry+https://github.com/rust-lang/crates.io-index"
511 -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
251 +name = "journal_file"
252 +version = "0.1.0"
253 dependencies = [
513 - "cfg-if",
514 - "libc",
515 - "wasi 0.11.1+wasi-snapshot-preview1",
254 + "error",
255 + "hex",
256 + "memmap2",
257 + "rand",
258 + "ruzstd",
259 + "siphasher",
260 + "tempfile",
261 + "twox-hash",
262 + "window_manager",
263 + "zerocopy 0.9.0-alpha.0",
264 ]
265
266 [[package]]
519 -name = "getrandom"
520 -version = "0.3.3"
521 -source = "registry+https://github.com/rust-lang/crates.io-index"
522 -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
267 +name = "journal_reader_ffi"
268 +version = "0.1.0"
269 dependencies = [
524 - "cfg-if",
525 - "libc",
526 - "r-efi",
527 - "wasi 0.14.7+wasi-0.2.4",
270 + "cbindgen",
271 + "error",
272 + "journal_file",
273 + "memmap2",
274 + "serde_json",
275 + "sigbus",
276 ]
277
278 [[package]]
531 -name = "gimli"
532 -version = "0.32.3"
279 +name = "leb128fmt"
280 +version = "0.1.0"
281 source = "registry+https://github.com/rust-lang/crates.io-index"
534 -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
282 +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
283
284 [[package]]
537 -name = "h2"
538 -version = "0.4.12"
285 +name = "libc"
286 +version = "0.2.181"
287 source = "registry+https://github.com/rust-lang/crates.io-index"
540 -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
541 -dependencies = [
542 - "atomic-waker",
543 - "bytes",
544 - "fnv",
545 - "futures-core",
546 - "futures-sink",
547 - "http",
548 - "indexmap",
549 - "slab",
550 - "tokio",
551 - "tokio-util",
552 - "tracing",
553 -]
288 +checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5"
289
290 [[package]]
556 -name = "hashbrown"
557 -version = "0.16.0"
291 +name = "linux-raw-sys"
292 +version = "0.11.0"
293 source = "registry+https://github.com/rust-lang/crates.io-index"
559 -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
294 +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
295
296 [[package]]
562 -name = "heck"
563 -version = "0.4.1"
297 +name = "log"
298 +version = "0.4.29"
299 source = "registry+https://github.com/rust-lang/crates.io-index"
565 -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
300 +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
301
302 [[package]]
568 -name = "heck"
569 -version = "0.5.0"
303 +name = "memchr"
304 +version = "2.8.0"
305 source = "registry+https://github.com/rust-lang/crates.io-index"
571 -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
306 +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
307
308 [[package]]
574 -name = "hermit-abi"
575 -version = "0.1.19"
309 +name = "memmap2"
310 +version = "0.9.9"
311 source = "registry+https://github.com/rust-lang/crates.io-index"
577 -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
312 +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490"
313 dependencies = [
314 "libc",
315 ]
316
317 [[package]]
583 -name = "hex"
584 -version = "0.4.3"
318 +name = "once_cell"
319 +version = "1.21.3"
320 source = "registry+https://github.com/rust-lang/crates.io-index"
586 -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
321 +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
322
323 [[package]]
589 -name = "http"
590 -version = "1.3.1"
324 +name = "once_cell_polyfill"
325 +version = "1.70.2"
326 source = "registry+https://github.com/rust-lang/crates.io-index"
592 -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
593 -dependencies = [
594 - "bytes",
595 - "fnv",
596 - "itoa",
597 -]
327 +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
328
329 [[package]]
600 -name = "http-body"
601 -version = "1.0.1"
330 +name = "ppv-lite86"
331 +version = "0.2.21"
332 source = "registry+https://github.com/rust-lang/crates.io-index"
603 -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
333 +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
334 dependencies = [
605 - "bytes",
606 - "http",
335 + "zerocopy 0.8.39",
336 ]
337
338 [[package]]
610 -name = "http-body-util"
611 -version = "0.1.3"
339 +name = "prettyplease"
340 +version = "0.2.37"
341 source = "registry+https://github.com/rust-lang/crates.io-index"
613 -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
342 +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
343 dependencies = [
615 - "bytes",
616 - "futures-core",
617 - "http",
618 - "http-body",
619 - "pin-project-lite",
344 + "proc-macro2",
345 + "syn",
346 ]
347
348 [[package]]
623 -name = "httparse"
624 -version = "1.10.1"
349 +name = "proc-macro2"
350 +version = "1.0.106"
351 source = "registry+https://github.com/rust-lang/crates.io-index"
626 -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
352 +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
353 +dependencies = [
354 + "unicode-ident",
355 +]
356
357 [[package]]
629 -name = "httpdate"
630 -version = "1.0.3"
358 +name = "quote"
359 +version = "1.0.44"
360 source = "registry+https://github.com/rust-lang/crates.io-index"
632 -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
361 +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
362 +dependencies = [
363 + "proc-macro2",
364 +]
365
366 [[package]]
635 -name = "humantime"
636 -version = "2.3.0"
367 +name = "r-efi"
368 +version = "5.3.0"
369 source = "registry+https://github.com/rust-lang/crates.io-index"
638 -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
370 +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
371
372 [[package]]
641 -name = "humantime-serde"
642 -version = "1.1.1"
643 -source = "registry+https://github.com/rust-lang/crates.io-index"
644 -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c"
645 -dependencies = [
646 - "humantime",
647 - "serde",
648 -]
649 -
650 -[[package]]
651 -name = "hyper"
652 -version = "1.7.0"
653 -source = "registry+https://github.com/rust-lang/crates.io-index"
654 -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
655 -dependencies = [
656 - "atomic-waker",
657 - "bytes",
658 - "futures-channel",
659 - "futures-core",
660 - "h2",
661 - "http",
662 - "http-body",
663 - "httparse",
664 - "httpdate",
665 - "itoa",
666 - "pin-project-lite",
667 - "pin-utils",
668 - "smallvec",
669 - "tokio",
670 - "want",
671 -]
672 -
673 -[[package]]
674 -name = "hyper-timeout"
675 -version = "0.5.2"
676 -source = "registry+https://github.com/rust-lang/crates.io-index"
677 -checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
678 -dependencies = [
679 - "hyper",
680 - "hyper-util",
681 - "pin-project-lite",
682 - "tokio",
683 - "tower-service",
684 -]
685 -
686 -[[package]]
687 -name = "hyper-util"
688 -version = "0.1.17"
689 -source = "registry+https://github.com/rust-lang/crates.io-index"
690 -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
691 -dependencies = [
692 - "bytes",
693 - "futures-channel",
694 - "futures-core",
695 - "futures-util",
696 - "http",
697 - "http-body",
698 - "hyper",
699 - "libc",
700 - "pin-project-lite",
701 - "socket2 0.6.0",
702 - "tokio",
703 - "tower-service",
704 - "tracing",
705 -]
706 -
707 -[[package]]
708 -name = "iana-time-zone"
709 -version = "0.1.64"
710 -source = "registry+https://github.com/rust-lang/crates.io-index"
711 -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
712 -dependencies = [
713 - "android_system_properties",
714 - "core-foundation-sys",
715 - "iana-time-zone-haiku",
716 - "js-sys",
717 - "log",
718 - "wasm-bindgen",
719 - "windows-core",
720 -]
721 -
722 -[[package]]
723 -name = "iana-time-zone-haiku"
724 -version = "0.1.2"
725 -source = "registry+https://github.com/rust-lang/crates.io-index"
726 -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
727 -dependencies = [
728 - "cc",
729 -]
730 -
731 -[[package]]
732 -name = "indexmap"
733 -version = "2.11.4"
734 -source = "registry+https://github.com/rust-lang/crates.io-index"
735 -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
736 -dependencies = [
737 - "equivalent",
738 - "hashbrown",
739 -]
740 -
741 -[[package]]
742 -name = "io-uring"
743 -version = "0.7.10"
744 -source = "registry+https://github.com/rust-lang/crates.io-index"
745 -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
746 -dependencies = [
747 - "bitflags",
748 - "cfg-if",
749 - "libc",
750 -]
751 -
752 -[[package]]
753 -name = "is_terminal_polyfill"
754 -version = "1.70.1"
755 -source = "registry+https://github.com/rust-lang/crates.io-index"
756 -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
757 -
758 -[[package]]
759 -name = "itertools"
760 -version = "0.14.0"
761 -source = "registry+https://github.com/rust-lang/crates.io-index"
762 -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
763 -dependencies = [
764 - "either",
765 -]
766 -
767 -[[package]]
768 -name = "itoa"
769 -version = "1.0.15"
770 -source = "registry+https://github.com/rust-lang/crates.io-index"
771 -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
772 -
773 -[[package]]
774 -name = "journal_file"
775 -version = "0.1.0"
776 -dependencies = [
777 - "error",
778 - "hex",
779 - "memmap2",
780 - "rand",
781 - "ruzstd",
782 - "siphasher",
783 - "tempfile",
784 - "twox-hash",
785 - "window_manager",
786 - "zerocopy 0.9.0-alpha.0",
787 -]
788 -
789 -[[package]]
790 -name = "journal_log"
791 -version = "0.1.0"
792 -dependencies = [
793 - "error",
794 - "journal_file",
795 - "memmap2",
796 - "tempfile",
797 - "uuid",
798 -]
799 -
800 -[[package]]
801 -name = "journal_reader_ffi"
802 -version = "0.1.0"
803 -dependencies = [
804 - "cbindgen",
805 - "error",
806 - "journal_file",
807 - "memmap2",
808 - "serde_json",
809 - "sigbus",
810 -]
811 -
812 -[[package]]
813 -name = "js-sys"
814 -version = "0.3.81"
815 -source = "registry+https://github.com/rust-lang/crates.io-index"
816 -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
817 -dependencies = [
818 - "once_cell",
819 - "wasm-bindgen",
820 -]
821 -
822 -[[package]]
823 -name = "lazy_static"
824 -version = "1.5.0"
825 -source = "registry+https://github.com/rust-lang/crates.io-index"
826 -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
827 -
828 -[[package]]
829 -name = "libc"
830 -version = "0.2.176"
831 -source = "registry+https://github.com/rust-lang/crates.io-index"
832 -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
833 -
834 -[[package]]
835 -name = "linux-raw-sys"
836 -version = "0.11.0"
837 -source = "registry+https://github.com/rust-lang/crates.io-index"
838 -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
839 -
840 -[[package]]
841 -name = "log"
842 -version = "0.4.28"
843 -source = "registry+https://github.com/rust-lang/crates.io-index"
844 -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
845 -
846 -[[package]]
847 -name = "matchit"
848 -version = "0.8.4"
849 -source = "registry+https://github.com/rust-lang/crates.io-index"
850 -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
851 -
852 -[[package]]
853 -name = "memchr"
854 -version = "2.7.6"
855 -source = "registry+https://github.com/rust-lang/crates.io-index"
856 -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
857 -
858 -[[package]]
859 -name = "memmap2"
860 -version = "0.9.8"
861 -source = "registry+https://github.com/rust-lang/crates.io-index"
862 -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7"
863 -dependencies = [
864 - "libc",
865 -]
866 -
867 -[[package]]
868 -name = "mime"
869 -version = "0.3.17"
870 -source = "registry+https://github.com/rust-lang/crates.io-index"
871 -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
872 -
873 -[[package]]
874 -name = "miniz_oxide"
875 -version = "0.8.9"
876 -source = "registry+https://github.com/rust-lang/crates.io-index"
877 -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
878 -dependencies = [
879 - "adler2",
880 -]
881 -
882 -[[package]]
883 -name = "mio"
884 -version = "1.0.4"
885 -source = "registry+https://github.com/rust-lang/crates.io-index"
886 -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
887 -dependencies = [
888 - "libc",
889 - "wasi 0.11.1+wasi-snapshot-preview1",
890 - "windows-sys 0.59.0",
891 -]
892 -
893 -[[package]]
894 -name = "multimap"
895 -version = "0.10.1"
896 -source = "registry+https://github.com/rust-lang/crates.io-index"
897 -checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
898 -
899 -[[package]]
900 -name = "nu-ansi-term"
901 -version = "0.50.1"
902 -source = "registry+https://github.com/rust-lang/crates.io-index"
903 -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
904 -dependencies = [
905 - "windows-sys 0.52.0",
906 -]
907 -
908 -[[package]]
909 -name = "num-traits"
910 -version = "0.2.19"
911 -source = "registry+https://github.com/rust-lang/crates.io-index"
912 -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
913 -dependencies = [
914 - "autocfg",
915 -]
916 -
917 -[[package]]
918 -name = "object"
919 -version = "0.37.3"
920 -source = "registry+https://github.com/rust-lang/crates.io-index"
921 -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
922 -dependencies = [
923 - "memchr",
924 -]
925 -
926 -[[package]]
927 -name = "once_cell"
928 -version = "1.21.3"
929 -source = "registry+https://github.com/rust-lang/crates.io-index"
930 -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
931 -
932 -[[package]]
933 -name = "once_cell_polyfill"
934 -version = "1.70.1"
935 -source = "registry+https://github.com/rust-lang/crates.io-index"
936 -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
937 -
938 -[[package]]
939 -name = "opentelemetry"
940 -version = "0.30.0"
941 -source = "registry+https://github.com/rust-lang/crates.io-index"
942 -checksum = "aaf416e4cb72756655126f7dd7bb0af49c674f4c1b9903e80c009e0c37e552e6"
943 -dependencies = [
944 - "futures-core",
945 - "futures-sink",
946 - "js-sys",
947 - "pin-project-lite",
948 - "thiserror",
949 - "tracing",
950 -]
951 -
952 -[[package]]
953 -name = "opentelemetry-proto"
954 -version = "0.30.0"
955 -source = "registry+https://github.com/rust-lang/crates.io-index"
956 -checksum = "2e046fd7660710fe5a05e8748e70d9058dc15c94ba914e7c4faa7c728f0e8ddc"
957 -dependencies = [
958 - "base64 0.22.1",
959 - "hex",
960 - "opentelemetry",
961 - "opentelemetry_sdk",
962 - "prost",
963 - "serde",
964 - "tonic",
965 -]
966 -
967 -[[package]]
968 -name = "opentelemetry_sdk"
969 -version = "0.30.0"
970 -source = "registry+https://github.com/rust-lang/crates.io-index"
971 -checksum = "11f644aa9e5e31d11896e024305d7e3c98a88884d9f8919dbf37a9991bc47a4b"
972 -dependencies = [
973 - "futures-channel",
974 - "futures-executor",
975 - "futures-util",
976 - "opentelemetry",
977 - "percent-encoding",
978 - "rand",
979 - "serde_json",
980 - "thiserror",
981 -]
982 -
983 -[[package]]
984 -name = "otel-plugin"
985 -version = "0.1.0"
986 -dependencies = [
987 - "anyhow",
988 - "atty",
989 - "base64 0.21.7",
990 - "bytesize",
991 - "bytesize-serde",
992 - "clap",
993 - "flatten-serde-json",
994 - "flatten_otel",
995 - "humantime",
996 - "humantime-serde",
997 - "journal_file",
998 - "journal_log",
999 - "memmap2",
1000 - "opentelemetry-proto",
1001 - "prost",
1002 - "regex",
1003 - "serde",
1004 - "serde_json",
1005 - "serde_regex",
1006 - "serde_yaml",
1007 - "tokio",
1008 - "tonic",
1009 - "uuid",
1010 -]
1011 -
1012 -[[package]]
1013 -name = "percent-encoding"
1014 -version = "2.3.2"
1015 -source = "registry+https://github.com/rust-lang/crates.io-index"
1016 -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
1017 -
1018 -[[package]]
1019 -name = "petgraph"
1020 -version = "0.7.1"
1021 -source = "registry+https://github.com/rust-lang/crates.io-index"
1022 -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
1023 -dependencies = [
1024 - "fixedbitset",
1025 - "indexmap",
1026 -]
1027 -
1028 -[[package]]
1029 -name = "pin-project"
1030 -version = "1.1.10"
1031 -source = "registry+https://github.com/rust-lang/crates.io-index"
1032 -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
1033 -dependencies = [
1034 - "pin-project-internal",
1035 -]
1036 -
1037 -[[package]]
1038 -name = "pin-project-internal"
1039 -version = "1.1.10"
1040 -source = "registry+https://github.com/rust-lang/crates.io-index"
1041 -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
1042 -dependencies = [
1043 - "proc-macro2",
1044 - "quote",
1045 - "syn",
1046 -]
1047 -
1048 -[[package]]
1049 -name = "pin-project-lite"
1050 -version = "0.2.16"
1051 -source = "registry+https://github.com/rust-lang/crates.io-index"
1052 -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
1053 -
1054 -[[package]]
1055 -name = "pin-utils"
1056 -version = "0.1.0"
1057 -source = "registry+https://github.com/rust-lang/crates.io-index"
1058 -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
1059 -
1060 -[[package]]
1061 -name = "ppv-lite86"
1062 -version = "0.2.21"
1063 -source = "registry+https://github.com/rust-lang/crates.io-index"
1064 -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
1065 -dependencies = [
1066 - "zerocopy 0.8.27",
1067 -]
1068 -
1069 -[[package]]
1070 -name = "prettyplease"
1071 -version = "0.2.37"
1072 -source = "registry+https://github.com/rust-lang/crates.io-index"
1073 -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
1074 -dependencies = [
1075 - "proc-macro2",
1076 - "syn",
1077 -]
1078 -
1079 -[[package]]
1080 -name = "proc-macro2"
1081 -version = "1.0.101"
1082 -source = "registry+https://github.com/rust-lang/crates.io-index"
1083 -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
1084 -dependencies = [
1085 - "unicode-ident",
1086 -]
1087 -
1088 -[[package]]
1089 -name = "prost"
1090 -version = "0.13.5"
1091 -source = "registry+https://github.com/rust-lang/crates.io-index"
1092 -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
1093 -dependencies = [
1094 - "bytes",
1095 - "prost-derive",
1096 -]
1097 -
1098 -[[package]]
1099 -name = "prost-build"
1100 -version = "0.13.5"
1101 -source = "registry+https://github.com/rust-lang/crates.io-index"
1102 -checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
1103 -dependencies = [
1104 - "heck 0.5.0",
1105 - "itertools",
1106 - "log",
1107 - "multimap",
1108 - "once_cell",
1109 - "petgraph",
1110 - "prettyplease",
1111 - "prost",
1112 - "prost-types",
1113 - "regex",
1114 - "syn",
1115 - "tempfile",
1116 -]
1117 -
1118 -[[package]]
1119 -name = "prost-derive"
1120 -version = "0.13.5"
1121 -source = "registry+https://github.com/rust-lang/crates.io-index"
1122 -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
1123 -dependencies = [
1124 - "anyhow",
1125 - "itertools",
1126 - "proc-macro2",
1127 - "quote",
1128 - "syn",
1129 -]
1130 -
1131 -[[package]]
1132 -name = "prost-types"
1133 -version = "0.13.5"
1134 -source = "registry+https://github.com/rust-lang/crates.io-index"
1135 -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
1136 -dependencies = [
1137 - "prost",
1138 -]
1139 -
1140 -[[package]]
1141 -name = "quote"
1142 -version = "1.0.41"
1143 -source = "registry+https://github.com/rust-lang/crates.io-index"
1144 -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
1145 -dependencies = [
1146 - "proc-macro2",
1147 -]
1148 -
1149 -[[package]]
1150 -name = "r-efi"
1151 -version = "5.3.0"
1152 -source = "registry+https://github.com/rust-lang/crates.io-index"
1153 -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
1154 -
1155 -[[package]]
1156 -name = "rand"
1157 -version = "0.9.2"
1158 -source = "registry+https://github.com/rust-lang/crates.io-index"
1159 -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
1160 -dependencies = [
1161 - "rand_chacha",
1162 - "rand_core",
1163 -]
1164 -
1165 -[[package]]
1166 -name = "rand_chacha"
1167 -version = "0.9.0"
1168 -source = "registry+https://github.com/rust-lang/crates.io-index"
1169 -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
1170 -dependencies = [
1171 - "ppv-lite86",
1172 - "rand_core",
1173 -]
1174 -
1175 -[[package]]
1176 -name = "rand_core"
1177 -version = "0.9.3"
1178 -source = "registry+https://github.com/rust-lang/crates.io-index"
1179 -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
1180 -dependencies = [
1181 - "getrandom 0.3.3",
1182 -]
1183 -
1184 -[[package]]
1185 -name = "regex"
1186 -version = "1.11.3"
1187 -source = "registry+https://github.com/rust-lang/crates.io-index"
1188 -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c"
1189 -dependencies = [
1190 - "aho-corasick",
1191 - "memchr",
1192 - "regex-automata",
1193 - "regex-syntax",
1194 -]
1195 -
1196 -[[package]]
1197 -name = "regex-automata"
1198 -version = "0.4.11"
1199 -source = "registry+https://github.com/rust-lang/crates.io-index"
1200 -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad"
1201 -dependencies = [
1202 - "aho-corasick",
1203 - "memchr",
1204 - "regex-syntax",
1205 -]
1206 -
1207 -[[package]]
1208 -name = "regex-syntax"
1209 -version = "0.8.6"
1210 -source = "registry+https://github.com/rust-lang/crates.io-index"
1211 -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
1212 -
1213 -[[package]]
1214 -name = "ring"
1215 -version = "0.17.14"
1216 -source = "registry+https://github.com/rust-lang/crates.io-index"
1217 -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
1218 -dependencies = [
1219 - "cc",
1220 - "cfg-if",
1221 - "getrandom 0.2.16",
1222 - "libc",
1223 - "untrusted",
1224 - "windows-sys 0.52.0",
1225 -]
1226 -
1227 -[[package]]
1228 -name = "rustc-demangle"
1229 -version = "0.1.26"
1230 -source = "registry+https://github.com/rust-lang/crates.io-index"
1231 -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
1232 -
1233 -[[package]]
1234 -name = "rustix"
1235 -version = "1.1.2"
1236 -source = "registry+https://github.com/rust-lang/crates.io-index"
1237 -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
1238 -dependencies = [
1239 - "bitflags",
1240 - "errno",
1241 - "libc",
1242 - "linux-raw-sys",
1243 - "windows-sys 0.61.1",
1244 -]
1245 -
1246 -[[package]]
1247 -name = "rustls"
1248 -version = "0.23.32"
1249 -source = "registry+https://github.com/rust-lang/crates.io-index"
1250 -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40"
1251 -dependencies = [
1252 - "log",
1253 - "once_cell",
1254 - "ring",
1255 - "rustls-pki-types",
1256 - "rustls-webpki",
1257 - "subtle",
1258 - "zeroize",
1259 -]
1260 -
1261 -[[package]]
1262 -name = "rustls-pki-types"
1263 -version = "1.12.0"
1264 -source = "registry+https://github.com/rust-lang/crates.io-index"
1265 -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
1266 -dependencies = [
1267 - "zeroize",
1268 -]
1269 -
1270 -[[package]]
1271 -name = "rustls-webpki"
1272 -version = "0.103.6"
1273 -source = "registry+https://github.com/rust-lang/crates.io-index"
1274 -checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb"
1275 -dependencies = [
1276 - "ring",
1277 - "rustls-pki-types",
1278 - "untrusted",
1279 -]
1280 -
1281 -[[package]]
1282 -name = "rustversion"
1283 -version = "1.0.22"
1284 -source = "registry+https://github.com/rust-lang/crates.io-index"
1285 -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
1286 -
1287 -[[package]]
1288 -name = "ruzstd"
1289 -version = "0.8.1"
1290 -source = "registry+https://github.com/rust-lang/crates.io-index"
1291 -checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c"
1292 -dependencies = [
1293 - "twox-hash",
1294 -]
1295 -
1296 -[[package]]
1297 -name = "ryu"
1298 -version = "1.0.20"
1299 -source = "registry+https://github.com/rust-lang/crates.io-index"
1300 -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
1301 -
1302 -[[package]]
1303 -name = "serde"
1304 -version = "1.0.228"
1305 -source = "registry+https://github.com/rust-lang/crates.io-index"
1306 -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
1307 -dependencies = [
1308 - "serde_core",
1309 - "serde_derive",
1310 -]
1311 -
1312 -[[package]]
1313 -name = "serde_core"
1314 -version = "1.0.228"
1315 -source = "registry+https://github.com/rust-lang/crates.io-index"
1316 -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
1317 -dependencies = [
1318 - "serde_derive",
1319 -]
1320 -
1321 -[[package]]
1322 -name = "serde_derive"
1323 -version = "1.0.228"
1324 -source = "registry+https://github.com/rust-lang/crates.io-index"
1325 -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
1326 -dependencies = [
1327 - "proc-macro2",
1328 - "quote",
1329 - "syn",
1330 -]
1331 -
1332 -[[package]]
1333 -name = "serde_json"
1334 -version = "1.0.145"
1335 -source = "registry+https://github.com/rust-lang/crates.io-index"
1336 -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
1337 -dependencies = [
1338 - "indexmap",
1339 - "itoa",
1340 - "memchr",
1341 - "ryu",
1342 - "serde",
1343 - "serde_core",
1344 -]
1345 -
1346 -[[package]]
1347 -name = "serde_regex"
1348 -version = "1.1.0"
1349 -source = "registry+https://github.com/rust-lang/crates.io-index"
1350 -checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
1351 -dependencies = [
1352 - "regex",
1353 - "serde",
1354 -]
1355 -
1356 -[[package]]
1357 -name = "serde_spanned"
1358 -version = "0.6.9"
1359 -source = "registry+https://github.com/rust-lang/crates.io-index"
1360 -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
1361 -dependencies = [
1362 - "serde",
1363 -]
1364 -
1365 -[[package]]
1366 -name = "serde_yaml"
1367 -version = "0.9.34+deprecated"
1368 -source = "registry+https://github.com/rust-lang/crates.io-index"
1369 -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
1370 -dependencies = [
1371 - "indexmap",
1372 - "itoa",
1373 - "ryu",
1374 - "serde",
1375 - "unsafe-libyaml",
1376 -]
1377 -
1378 -[[package]]
1379 -name = "sharded-slab"
1380 -version = "0.1.7"
1381 -source = "registry+https://github.com/rust-lang/crates.io-index"
1382 -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
1383 -dependencies = [
1384 - "lazy_static",
1385 -]
1386 -
1387 -[[package]]
1388 -name = "shlex"
1389 -version = "1.3.0"
1390 -source = "registry+https://github.com/rust-lang/crates.io-index"
1391 -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
1392 -
1393 -[[package]]
1394 -name = "sigbus"
1395 -version = "0.1.0"
1396 -dependencies = [
1397 - "error",
1398 - "libc",
1399 -]
1400 -
1401 -[[package]]
1402 -name = "signal-hook-registry"
1403 -version = "1.4.6"
1404 -source = "registry+https://github.com/rust-lang/crates.io-index"
1405 -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
1406 -dependencies = [
1407 - "libc",
1408 -]
1409 -
1410 -[[package]]
1411 -name = "siphasher"
1412 -version = "1.0.1"
1413 -source = "registry+https://github.com/rust-lang/crates.io-index"
1414 -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
1415 -
1416 -[[package]]
1417 -name = "slab"
1418 -version = "0.4.11"
1419 -source = "registry+https://github.com/rust-lang/crates.io-index"
1420 -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
1421 -
1422 -[[package]]
1423 -name = "smallvec"
1424 -version = "1.15.1"
1425 -source = "registry+https://github.com/rust-lang/crates.io-index"
1426 -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
1427 -
1428 -[[package]]
1429 -name = "socket2"
1430 -version = "0.5.10"
1431 -source = "registry+https://github.com/rust-lang/crates.io-index"
1432 -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
1433 -dependencies = [
1434 - "libc",
1435 - "windows-sys 0.52.0",
1436 -]
1437 -
1438 -[[package]]
1439 -name = "socket2"
1440 -version = "0.6.0"
1441 -source = "registry+https://github.com/rust-lang/crates.io-index"
1442 -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
1443 -dependencies = [
1444 - "libc",
1445 - "windows-sys 0.59.0",
1446 -]
1447 -
1448 -[[package]]
1449 -name = "static_assertions"
1450 -version = "1.1.0"
1451 -source = "registry+https://github.com/rust-lang/crates.io-index"
1452 -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
1453 -
1454 -[[package]]
1455 -name = "strsim"
1456 -version = "0.11.1"
1457 -source = "registry+https://github.com/rust-lang/crates.io-index"
1458 -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
1459 -
1460 -[[package]]
1461 -name = "subtle"
1462 -version = "2.6.1"
1463 -source = "registry+https://github.com/rust-lang/crates.io-index"
1464 -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
1465 -
1466 -[[package]]
1467 -name = "syn"
1468 -version = "2.0.106"
1469 -source = "registry+https://github.com/rust-lang/crates.io-index"
1470 -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
1471 -dependencies = [
1472 - "proc-macro2",
1473 - "quote",
1474 - "unicode-ident",
1475 -]
1476 -
1477 -[[package]]
1478 -name = "sync_wrapper"
1479 -version = "1.0.2"
1480 -source = "registry+https://github.com/rust-lang/crates.io-index"
1481 -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
1482 -
1483 -[[package]]
1484 -name = "tempfile"
1485 -version = "3.23.0"
1486 -source = "registry+https://github.com/rust-lang/crates.io-index"
1487 -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
1488 -dependencies = [
1489 - "fastrand",
1490 - "getrandom 0.3.3",
1491 - "once_cell",
1492 - "rustix",
1493 - "windows-sys 0.61.1",
1494 -]
1495 -
1496 -[[package]]
1497 -name = "thiserror"
1498 -version = "2.0.17"
1499 -source = "registry+https://github.com/rust-lang/crates.io-index"
1500 -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
1501 -dependencies = [
1502 - "thiserror-impl",
1503 -]
1504 -
1505 -[[package]]
1506 -name = "thiserror-impl"
1507 -version = "2.0.17"
1508 -source = "registry+https://github.com/rust-lang/crates.io-index"
1509 -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
1510 -dependencies = [
1511 - "proc-macro2",
1512 - "quote",
1513 - "syn",
1514 -]
1515 -
1516 -[[package]]
1517 -name = "thread_local"
1518 -version = "1.1.9"
1519 -source = "registry+https://github.com/rust-lang/crates.io-index"
1520 -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
1521 -dependencies = [
1522 - "cfg-if",
1523 -]
1524 -
1525 -[[package]]
1526 -name = "tokio"
1527 -version = "1.47.1"
1528 -source = "registry+https://github.com/rust-lang/crates.io-index"
1529 -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
1530 -dependencies = [
1531 - "backtrace",
1532 - "bytes",
1533 - "io-uring",
1534 - "libc",
1535 - "mio",
1536 - "pin-project-lite",
1537 - "signal-hook-registry",
1538 - "slab",
1539 - "socket2 0.6.0",
1540 - "tokio-macros",
1541 - "windows-sys 0.59.0",
1542 -]
1543 -
1544 -[[package]]
1545 -name = "tokio-macros"
1546 -version = "2.5.0"
1547 -source = "registry+https://github.com/rust-lang/crates.io-index"
1548 -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
1549 -dependencies = [
1550 - "proc-macro2",
1551 - "quote",
1552 - "syn",
1553 -]
1554 -
1555 -[[package]]
1556 -name = "tokio-rustls"
1557 -version = "0.26.4"
1558 -source = "registry+https://github.com/rust-lang/crates.io-index"
1559 -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
1560 -dependencies = [
1561 - "rustls",
1562 - "tokio",
1563 -]
1564 -
1565 -[[package]]
1566 -name = "tokio-stream"
1567 -version = "0.1.17"
1568 -source = "registry+https://github.com/rust-lang/crates.io-index"
1569 -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
1570 -dependencies = [
1571 - "futures-core",
1572 - "pin-project-lite",
1573 - "tokio",
1574 -]
1575 -
1576 -[[package]]
1577 -name = "tokio-util"
1578 -version = "0.7.16"
1579 -source = "registry+https://github.com/rust-lang/crates.io-index"
1580 -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
1581 -dependencies = [
1582 - "bytes",
1583 - "futures-core",
1584 - "futures-sink",
1585 - "pin-project-lite",
1586 - "tokio",
1587 -]
1588 -
1589 -[[package]]
1590 -name = "toml"
1591 -version = "0.8.23"
1592 -source = "registry+https://github.com/rust-lang/crates.io-index"
1593 -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
1594 -dependencies = [
1595 - "serde",
1596 - "serde_spanned",
1597 - "toml_datetime",
1598 - "toml_edit",
1599 -]
1600 -
1601 -[[package]]
1602 -name = "toml_datetime"
1603 -version = "0.6.11"
1604 -source = "registry+https://github.com/rust-lang/crates.io-index"
1605 -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
1606 -dependencies = [
1607 - "serde",
1608 -]
1609 -
1610 -[[package]]
1611 -name = "toml_edit"
1612 -version = "0.22.27"
1613 -source = "registry+https://github.com/rust-lang/crates.io-index"
1614 -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
1615 -dependencies = [
1616 - "indexmap",
1617 - "serde",
1618 - "serde_spanned",
1619 - "toml_datetime",
1620 - "toml_write",
1621 - "winnow",
1622 -]
1623 -
1624 -[[package]]
1625 -name = "toml_write"
1626 -version = "0.1.2"
1627 -source = "registry+https://github.com/rust-lang/crates.io-index"
1628 -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
1629 -
1630 -[[package]]
1631 -name = "tonic"
1632 -version = "0.13.1"
1633 -source = "registry+https://github.com/rust-lang/crates.io-index"
1634 -checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9"
1635 -dependencies = [
1636 - "async-trait",
1637 - "axum",
1638 - "base64 0.22.1",
1639 - "bytes",
1640 - "flate2",
1641 - "h2",
1642 - "http",
1643 - "http-body",
1644 - "http-body-util",
1645 - "hyper",
1646 - "hyper-timeout",
1647 - "hyper-util",
1648 - "percent-encoding",
1649 - "pin-project",
1650 - "prost",
1651 - "socket2 0.5.10",
1652 - "tokio",
1653 - "tokio-rustls",
1654 - "tokio-stream",
1655 - "tower",
1656 - "tower-layer",
1657 - "tower-service",
1658 - "tracing",
1659 -]
1660 -
1661 -[[package]]
1662 -name = "tonic-build"
1663 -version = "0.13.1"
1664 -source = "registry+https://github.com/rust-lang/crates.io-index"
1665 -checksum = "eac6f67be712d12f0b41328db3137e0d0757645d8904b4cb7d51cd9c2279e847"
1666 -dependencies = [
1667 - "prettyplease",
1668 - "proc-macro2",
1669 - "prost-build",
1670 - "prost-types",
1671 - "quote",
1672 - "syn",
1673 -]
1674 -
1675 -[[package]]
1676 -name = "tower"
1677 -version = "0.5.2"
1678 -source = "registry+https://github.com/rust-lang/crates.io-index"
1679 -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
1680 -dependencies = [
1681 - "futures-core",
1682 - "futures-util",
1683 - "indexmap",
1684 - "pin-project-lite",
1685 - "slab",
1686 - "sync_wrapper",
1687 - "tokio",
1688 - "tokio-util",
1689 - "tower-layer",
1690 - "tower-service",
1691 - "tracing",
1692 -]
1693 -
1694 -[[package]]
1695 -name = "tower-layer"
1696 -version = "0.3.3"
1697 -source = "registry+https://github.com/rust-lang/crates.io-index"
1698 -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
1699 -
1700 -[[package]]
1701 -name = "tower-service"
1702 -version = "0.3.3"
1703 -source = "registry+https://github.com/rust-lang/crates.io-index"
1704 -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
1705 -
1706 -[[package]]
1707 -name = "tracing"
1708 -version = "0.1.41"
373 +name = "rand"
374 +version = "0.9.2"
375 source = "registry+https://github.com/rust-lang/crates.io-index"
1710 -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
376 +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
377 dependencies = [
1712 - "pin-project-lite",
1713 - "tracing-attributes",
1714 - "tracing-core",
378 + "rand_chacha",
379 + "rand_core",
380 ]
381
382 [[package]]
1718 -name = "tracing-attributes"
1719 -version = "0.1.30"
383 +name = "rand_chacha"
384 +version = "0.9.0"
385 source = "registry+https://github.com/rust-lang/crates.io-index"
1721 -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
386 +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
387 dependencies = [
1723 - "proc-macro2",
1724 - "quote",
1725 - "syn",
388 + "ppv-lite86",
389 + "rand_core",
390 ]
391
392 [[package]]
1729 -name = "tracing-core"
1730 -version = "0.1.34"
393 +name = "rand_core"
394 +version = "0.9.5"
395 source = "registry+https://github.com/rust-lang/crates.io-index"
1732 -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
396 +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
397 dependencies = [
1734 - "once_cell",
1735 - "valuable",
398 + "getrandom 0.3.4",
399 ]
400
401 [[package]]
1739 -name = "tracing-log"
1740 -version = "0.2.0"
402 +name = "rustix"
403 +version = "1.1.3"
404 source = "registry+https://github.com/rust-lang/crates.io-index"
1742 -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
405 +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
406 dependencies = [
1744 - "log",
1745 - "once_cell",
1746 - "tracing-core",
407 + "bitflags",
408 + "errno",
409 + "libc",
410 + "linux-raw-sys",
411 + "windows-sys",
412 ]
413
414 [[package]]
1750 -name = "tracing-subscriber"
1751 -version = "0.3.20"
415 +name = "ruzstd"
416 +version = "0.8.2"
417 source = "registry+https://github.com/rust-lang/crates.io-index"
1753 -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
418 +checksum = "e5ff0cc5e135c8870a775d3320910cd9b564ec036b4dc0b8741629020be63f01"
419 dependencies = [
1755 - "nu-ansi-term",
1756 - "sharded-slab",
1757 - "smallvec",
1758 - "thread_local",
1759 - "tracing-core",
1760 - "tracing-log",
420 + "twox-hash",
421 ]
422
423 [[package]]
1764 -name = "try-lock"
1765 -version = "0.2.5"
424 +name = "semver"
425 +version = "1.0.27"
426 source = "registry+https://github.com/rust-lang/crates.io-index"
1767 -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
427 +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
428
429 [[package]]
1770 -name = "twox-hash"
1771 -version = "2.1.2"
430 +name = "serde"
431 +version = "1.0.228"
432 source = "registry+https://github.com/rust-lang/crates.io-index"
1773 -checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
433 +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
434 +dependencies = [
435 + "serde_core",
436 + "serde_derive",
437 +]
438
439 [[package]]
1776 -name = "unicode-ident"
1777 -version = "1.0.19"
440 +name = "serde_core"
441 +version = "1.0.228"
442 source = "registry+https://github.com/rust-lang/crates.io-index"
1779 -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
443 +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
444 +dependencies = [
445 + "serde_derive",
446 +]
447
448 [[package]]
1782 -name = "unsafe-libyaml"
1783 -version = "0.2.11"
449 +name = "serde_derive"
450 +version = "1.0.228"
451 source = "registry+https://github.com/rust-lang/crates.io-index"
1785 -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
452 +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
453 +dependencies = [
454 + "proc-macro2",
455 + "quote",
456 + "syn",
457 +]
458
459 [[package]]
1788 -name = "untrusted"
1789 -version = "0.9.0"
460 +name = "serde_json"
461 +version = "1.0.149"
462 source = "registry+https://github.com/rust-lang/crates.io-index"
1791 -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
463 +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
464 +dependencies = [
465 + "itoa",
466 + "memchr",
467 + "serde",
468 + "serde_core",
469 + "zmij",
470 +]
471
472 [[package]]
1794 -name = "utf8parse"
1795 -version = "0.2.2"
473 +name = "serde_spanned"
474 +version = "0.6.9"
475 source = "registry+https://github.com/rust-lang/crates.io-index"
1797 -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
476 +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
477 +dependencies = [
478 + "serde",
479 +]
480
481 [[package]]
1800 -name = "uuid"
1801 -version = "1.18.1"
1802 -source = "registry+https://github.com/rust-lang/crates.io-index"
1803 -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
482 +name = "sigbus"
483 +version = "0.1.0"
484 dependencies = [
1805 - "getrandom 0.3.3",
1806 - "js-sys",
1807 - "wasm-bindgen",
485 + "error",
486 + "libc",
487 ]
488
489 [[package]]
1811 -name = "valuable"
1812 -version = "0.1.1"
490 +name = "siphasher"
491 +version = "1.0.2"
492 source = "registry+https://github.com/rust-lang/crates.io-index"
1814 -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
493 +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
494
495 [[package]]
1817 -name = "want"
1818 -version = "0.3.1"
496 +name = "static_assertions"
497 +version = "1.1.0"
498 source = "registry+https://github.com/rust-lang/crates.io-index"
1820 -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
1821 -dependencies = [
1822 - "try-lock",
1823 -]
499 +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
500
501 [[package]]
1826 -name = "wasi"
1827 -version = "0.11.1+wasi-snapshot-preview1"
502 +name = "strsim"
503 +version = "0.11.1"
504 source = "registry+https://github.com/rust-lang/crates.io-index"
1829 -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
505 +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
506
507 [[package]]
1832 -name = "wasi"
1833 -version = "0.14.7+wasi-0.2.4"
508 +name = "syn"
509 +version = "2.0.114"
510 source = "registry+https://github.com/rust-lang/crates.io-index"
1835 -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
511 +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
512 dependencies = [
1837 - "wasip2",
513 + "proc-macro2",
514 + "quote",
515 + "unicode-ident",
516 ]
517
518 [[package]]
1841 -name = "wasip2"
1842 -version = "1.0.1+wasi-0.2.4"
519 +name = "tempfile"
520 +version = "3.25.0"
521 source = "registry+https://github.com/rust-lang/crates.io-index"
1844 -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
522 +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
523 dependencies = [
1846 - "wit-bindgen",
524 + "fastrand",
525 + "getrandom 0.4.1",
526 + "once_cell",
527 + "rustix",
528 + "windows-sys",
529 ]
530
531 [[package]]
1850 -name = "wasm-bindgen"
1851 -version = "0.2.104"
532 +name = "thiserror"
533 +version = "2.0.18"
534 source = "registry+https://github.com/rust-lang/crates.io-index"
1853 -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
535 +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
536 dependencies = [
1855 - "cfg-if",
1856 - "once_cell",
1857 - "rustversion",
1858 - "wasm-bindgen-macro",
1859 - "wasm-bindgen-shared",
537 + "thiserror-impl",
538 ]
539
540 [[package]]
1863 -name = "wasm-bindgen-backend"
1864 -version = "0.2.104"
541 +name = "thiserror-impl"
542 +version = "2.0.18"
543 source = "registry+https://github.com/rust-lang/crates.io-index"
1866 -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
544 +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
545 dependencies = [
1868 - "bumpalo",
1869 - "log",
546 "proc-macro2",
547 "quote",
548 "syn",
1873 - "wasm-bindgen-shared",
549 ]
550
551 [[package]]
1877 -name = "wasm-bindgen-macro"
1878 -version = "0.2.104"
552 +name = "toml"
553 +version = "0.8.23"
554 source = "registry+https://github.com/rust-lang/crates.io-index"
1880 -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
555 +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
556 dependencies = [
1882 - "quote",
1883 - "wasm-bindgen-macro-support",
557 + "serde",
558 + "serde_spanned",
559 + "toml_datetime",
560 + "toml_edit",
561 ]
562
563 [[package]]
1887 -name = "wasm-bindgen-macro-support"
1888 -version = "0.2.104"
564 +name = "toml_datetime"
565 +version = "0.6.11"
566 source = "registry+https://github.com/rust-lang/crates.io-index"
1890 -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
567 +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
568 dependencies = [
1892 - "proc-macro2",
1893 - "quote",
1894 - "syn",
1895 - "wasm-bindgen-backend",
1896 - "wasm-bindgen-shared",
569 + "serde",
570 ]
571
572 [[package]]
1900 -name = "wasm-bindgen-shared"
1901 -version = "0.2.104"
573 +name = "toml_edit"
574 +version = "0.22.27"
575 source = "registry+https://github.com/rust-lang/crates.io-index"
1903 -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
576 +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
577 dependencies = [
1905 - "unicode-ident",
578 + "indexmap",
579 + "serde",
580 + "serde_spanned",
581 + "toml_datetime",
582 + "toml_write",
583 + "winnow",
584 ]
585
586 [[package]]
1909 -name = "winapi"
1910 -version = "0.3.9"
587 +name = "toml_write"
588 +version = "0.1.2"
589 source = "registry+https://github.com/rust-lang/crates.io-index"
1912 -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1913 -dependencies = [
1914 - "winapi-i686-pc-windows-gnu",
1915 - "winapi-x86_64-pc-windows-gnu",
1916 -]
590 +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
591
592 [[package]]
1919 -name = "winapi-i686-pc-windows-gnu"
1920 -version = "0.4.0"
593 +name = "twox-hash"
594 +version = "2.1.2"
595 source = "registry+https://github.com/rust-lang/crates.io-index"
1922 -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
596 +checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
597
598 [[package]]
1925 -name = "winapi-x86_64-pc-windows-gnu"
1926 -version = "0.4.0"
599 +name = "unicode-ident"
600 +version = "1.0.23"
601 source = "registry+https://github.com/rust-lang/crates.io-index"
1928 -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
602 +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
603
604 [[package]]
1931 -name = "window_manager"
1932 -version = "0.1.0"
1933 -dependencies = [
1934 - "error",
1935 - "memmap2",
1936 -]
605 +name = "unicode-xid"
606 +version = "0.2.6"
607 +source = "registry+https://github.com/rust-lang/crates.io-index"
608 +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
609
610 [[package]]
1939 -name = "windows-core"
1940 -version = "0.62.1"
611 +name = "utf8parse"
612 +version = "0.2.2"
613 source = "registry+https://github.com/rust-lang/crates.io-index"
1942 -checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9"
1943 -dependencies = [
1944 - "windows-implement",
1945 - "windows-interface",
1946 - "windows-link",
1947 - "windows-result",
1948 - "windows-strings",
1949 -]
614 +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
615
616 [[package]]
1952 -name = "windows-implement"
1953 -version = "0.60.1"
617 +name = "wasip2"
618 +version = "1.0.2+wasi-0.2.9"
619 source = "registry+https://github.com/rust-lang/crates.io-index"
1955 -checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0"
620 +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
621 dependencies = [
1957 - "proc-macro2",
1958 - "quote",
1959 - "syn",
622 + "wit-bindgen",
623 ]
624
625 [[package]]
1963 -name = "windows-interface"
1964 -version = "0.59.2"
626 +name = "wasip3"
627 +version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
628 source = "registry+https://github.com/rust-lang/crates.io-index"
1966 -checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5"
629 +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
630 dependencies = [
1968 - "proc-macro2",
1969 - "quote",
1970 - "syn",
631 + "wit-bindgen",
632 ]
633
634 [[package]]
1974 -name = "windows-link"
1975 -version = "0.2.0"
1976 -source = "registry+https://github.com/rust-lang/crates.io-index"
1977 -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
1978 -
1979 -[[package]]
1980 -name = "windows-result"
1981 -version = "0.4.0"
635 +name = "wasm-encoder"
636 +version = "0.244.0"
637 source = "registry+https://github.com/rust-lang/crates.io-index"
1983 -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
638 +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
639 dependencies = [
1985 - "windows-link",
640 + "leb128fmt",
641 + "wasmparser",
642 ]
643
644 [[package]]
1989 -name = "windows-strings"
1990 -version = "0.5.0"
645 +name = "wasm-metadata"
646 +version = "0.244.0"
647 source = "registry+https://github.com/rust-lang/crates.io-index"
1992 -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
648 +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
649 dependencies = [
1994 - "windows-link",
650 + "anyhow",
651 + "indexmap",
652 + "wasm-encoder",
653 + "wasmparser",
654 ]
655
656 [[package]]
1998 -name = "windows-sys"
1999 -version = "0.52.0"
657 +name = "wasmparser"
658 +version = "0.244.0"
659 source = "registry+https://github.com/rust-lang/crates.io-index"
2001 -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
660 +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
661 dependencies = [
2003 - "windows-targets 0.52.6",
662 + "bitflags",
663 + "hashbrown 0.15.5",
664 + "indexmap",
665 + "semver",
666 ]
667
668 [[package]]
2007 -name = "windows-sys"
2008 -version = "0.59.0"
2009 -source = "registry+https://github.com/rust-lang/crates.io-index"
2010 -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
669 +name = "window_manager"
670 +version = "0.1.0"
671 dependencies = [
2012 - "windows-targets 0.52.6",
672 + "error",
673 + "memmap2",
674 ]
675
676 [[package]]
2016 -name = "windows-sys"
2017 -version = "0.60.2"
677 +name = "windows-link"
678 +version = "0.2.1"
679 source = "registry+https://github.com/rust-lang/crates.io-index"
2019 -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
2020 -dependencies = [
2021 - "windows-targets 0.53.4",
2022 -]
680 +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
681
682 [[package]]
683 name = "windows-sys"
2026 -version = "0.61.1"
684 +version = "0.61.2"
685 source = "registry+https://github.com/rust-lang/crates.io-index"
2028 -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f"
686 +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
687 dependencies = [
688 "windows-link",
689 ]
690
691 [[package]]
2034 -name = "windows-targets"
2035 -version = "0.52.6"
692 +name = "winnow"
693 +version = "0.7.14"
694 source = "registry+https://github.com/rust-lang/crates.io-index"
2037 -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
695 +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
696 dependencies = [
2039 - "windows_aarch64_gnullvm 0.52.6",
2040 - "windows_aarch64_msvc 0.52.6",
2041 - "windows_i686_gnu 0.52.6",
2042 - "windows_i686_gnullvm 0.52.6",
2043 - "windows_i686_msvc 0.52.6",
2044 - "windows_x86_64_gnu 0.52.6",
2045 - "windows_x86_64_gnullvm 0.52.6",
2046 - "windows_x86_64_msvc 0.52.6",
697 + "memchr",
698 ]
699
700 [[package]]
2050 -name = "windows-targets"
2051 -version = "0.53.4"
701 +name = "wit-bindgen"
702 +version = "0.51.0"
703 source = "registry+https://github.com/rust-lang/crates.io-index"
2053 -checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b"
704 +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
705 dependencies = [
2055 - "windows-link",
2056 - "windows_aarch64_gnullvm 0.53.0",
2057 - "windows_aarch64_msvc 0.53.0",
2058 - "windows_i686_gnu 0.53.0",
2059 - "windows_i686_gnullvm 0.53.0",
2060 - "windows_i686_msvc 0.53.0",
2061 - "windows_x86_64_gnu 0.53.0",
2062 - "windows_x86_64_gnullvm 0.53.0",
2063 - "windows_x86_64_msvc 0.53.0",
706 + "wit-bindgen-rust-macro",
707 ]
708
709 [[package]]
2067 -name = "windows_aarch64_gnullvm"
2068 -version = "0.52.6"
2069 -source = "registry+https://github.com/rust-lang/crates.io-index"
2070 -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
2071 -
2072 -[[package]]
2073 -name = "windows_aarch64_gnullvm"
2074 -version = "0.53.0"
2075 -source = "registry+https://github.com/rust-lang/crates.io-index"
2076 -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
2077 -
2078 -[[package]]
2079 -name = "windows_aarch64_msvc"
2080 -version = "0.52.6"
2081 -source = "registry+https://github.com/rust-lang/crates.io-index"
2082 -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
2083 -
2084 -[[package]]
2085 -name = "windows_aarch64_msvc"
2086 -version = "0.53.0"
2087 -source = "registry+https://github.com/rust-lang/crates.io-index"
2088 -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
2089 -
2090 -[[package]]
2091 -name = "windows_i686_gnu"
2092 -version = "0.52.6"
710 +name = "wit-bindgen-core"
711 +version = "0.51.0"
712 source = "registry+https://github.com/rust-lang/crates.io-index"
2094 -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
2095 -
2096 -[[package]]
2097 -name = "windows_i686_gnu"
2098 -version = "0.53.0"
2099 -source = "registry+https://github.com/rust-lang/crates.io-index"
2100 -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
2101 -
2102 -[[package]]
2103 -name = "windows_i686_gnullvm"
2104 -version = "0.52.6"
2105 -source = "registry+https://github.com/rust-lang/crates.io-index"
2106 -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
2107 -
2108 -[[package]]
2109 -name = "windows_i686_gnullvm"
2110 -version = "0.53.0"
2111 -source = "registry+https://github.com/rust-lang/crates.io-index"
2112 -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
2113 -
2114 -[[package]]
2115 -name = "windows_i686_msvc"
2116 -version = "0.52.6"
2117 -source = "registry+https://github.com/rust-lang/crates.io-index"
2118 -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
2119 -
2120 -[[package]]
2121 -name = "windows_i686_msvc"
2122 -version = "0.53.0"
2123 -source = "registry+https://github.com/rust-lang/crates.io-index"
2124 -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
2125 -
2126 -[[package]]
2127 -name = "windows_x86_64_gnu"
2128 -version = "0.52.6"
2129 -source = "registry+https://github.com/rust-lang/crates.io-index"
2130 -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
2131 -
2132 -[[package]]
2133 -name = "windows_x86_64_gnu"
2134 -version = "0.53.0"
2135 -source = "registry+https://github.com/rust-lang/crates.io-index"
2136 -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
2137 -
2138 -[[package]]
2139 -name = "windows_x86_64_gnullvm"
2140 -version = "0.52.6"
2141 -source = "registry+https://github.com/rust-lang/crates.io-index"
2142 -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
2143 -
2144 -[[package]]
2145 -name = "windows_x86_64_gnullvm"
2146 -version = "0.53.0"
2147 -source = "registry+https://github.com/rust-lang/crates.io-index"
2148 -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
713 +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
714 +dependencies = [
715 + "anyhow",
716 + "heck 0.5.0",
717 + "wit-parser",
718 +]
719
720 [[package]]
2151 -name = "windows_x86_64_msvc"
2152 -version = "0.52.6"
721 +name = "wit-bindgen-rust"
722 +version = "0.51.0"
723 source = "registry+https://github.com/rust-lang/crates.io-index"
2154 -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
724 +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
725 +dependencies = [
726 + "anyhow",
727 + "heck 0.5.0",
728 + "indexmap",
729 + "prettyplease",
730 + "syn",
731 + "wasm-metadata",
732 + "wit-bindgen-core",
733 + "wit-component",
734 +]
735
736 [[package]]
2157 -name = "windows_x86_64_msvc"
2158 -version = "0.53.0"
737 +name = "wit-bindgen-rust-macro"
738 +version = "0.51.0"
739 source = "registry+https://github.com/rust-lang/crates.io-index"
2160 -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
740 +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
741 +dependencies = [
742 + "anyhow",
743 + "prettyplease",
744 + "proc-macro2",
745 + "quote",
746 + "syn",
747 + "wit-bindgen-core",
748 + "wit-bindgen-rust",
749 +]
750
751 [[package]]
2163 -name = "winnow"
2164 -version = "0.7.13"
752 +name = "wit-component"
753 +version = "0.244.0"
754 source = "registry+https://github.com/rust-lang/crates.io-index"
2166 -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
755 +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
756 dependencies = [
2168 - "memchr",
757 + "anyhow",
758 + "bitflags",
759 + "indexmap",
760 + "log",
761 + "serde",
762 + "serde_derive",
763 + "serde_json",
764 + "wasm-encoder",
765 + "wasm-metadata",
766 + "wasmparser",
767 + "wit-parser",
768 ]
769
770 [[package]]
2172 -name = "wit-bindgen"
2173 -version = "0.46.0"
771 +name = "wit-parser"
772 +version = "0.244.0"
773 source = "registry+https://github.com/rust-lang/crates.io-index"
2175 -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
774 +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
775 +dependencies = [
776 + "anyhow",
777 + "id-arena",
778 + "indexmap",
779 + "log",
780 + "semver",
781 + "serde",
782 + "serde_derive",
783 + "serde_json",
784 + "unicode-xid",
785 + "wasmparser",
786 +]
787
788 [[package]]
789 name = "zerocopy"
2179 -version = "0.8.27"
790 +version = "0.8.39"
791 source = "registry+https://github.com/rust-lang/crates.io-index"
2181 -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
792 +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
793 dependencies = [
2183 - "zerocopy-derive 0.8.27",
794 + "zerocopy-derive 0.8.39",
795 ]
796
797 [[package]]
@@ -2194,9 +805,9 @@ dependencies = [
805
806 [[package]]
807 name = "zerocopy-derive"
2197 -version = "0.8.27"
808 +version = "0.8.39"
809 source = "registry+https://github.com/rust-lang/crates.io-index"
2199 -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
810 +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
811 dependencies = [
812 "proc-macro2",
813 "quote",
@@ -2215,7 +826,7 @@ dependencies = [
826 ]
827
828 [[package]]
2218 -name = "zeroize"
2219 -version = "1.8.2"
829 +name = "zmij"
830 +version = "1.0.20"
831 source = "registry+https://github.com/rust-lang/crates.io-index"
2221 -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
832 +checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7"
src/crates/jf/Cargo.toml
-34
@@ -4,12 +4,8 @@ members = [
4 "error",
5 "journal_file",
6 "journal_reader_ffi",
7 - "journal_log",
7 "window_manager",
8 "sigbus",
10 - "otel-plugin",
11 - "flatten_otel",
12 - "flog-otel",
9 ]
10
11 [workspace.package]
@@ -25,41 +21,11 @@ rand = "0.9"
21 siphasher = "1.0"
22 twox-hash = { version = "2.1", default-features = false, features = ["std"] }
23 static_assertions = "1.1"
28 -duct = "0.13"
29 -serde = { version = "1.0", features = ["derive"] }
24 serde_json = "1.0"
31 -walkdir = "2"
25 ruzstd = "0.8"
26 libc = "0.2"
27 hex = "0.4"
28 tempfile = "3"
36 -indexmap = "2.9"
37 -uuid = { version = "1.0", features = ["v4", "rng"] }
38 -chrono = { version = "0.4", features = ["serde"] }
39 -tracing = "0.1"
40 -tracing-subscriber = "0.3"
41 -
42 -# otel-plugin
43 -prost = "0.13"
44 -regex = "1.10"
45 -tokio = { version = "1.45", features = ["rt-multi-thread", "process", "io-util"] }
46 -tokio-stream = "0.1"
47 -tonic = "0.13"
48 -tonic-build = "0.13"
49 -prost-build = "0.13"
50 -hyper-util = "0.1"
51 -tower = "0.5"
52 -opentelemetry-proto = "0.30"
53 -atty = "0.2"
54 -anyhow = "1.0"
55 -humantime-serde = "1.1"
56 -humantime = "2.2"
57 -bytesize = "1.0"
58 -bytesize-serde = "0.2"
59 -
60 -clap = { version = "4.5", features = ["derive", "env"] }
61 -
62 -flatten-serde-json = { git = "https://github.com/meilisearch/meilisearch", tag = "v1.22.1" }
29
30 [profile.release]
31 lto = true
src/crates/jf/flatten_otel/Cargo.toml deleted
-10
@@ -1,10 +0,0 @@
1 -[package]
2 -name = "flatten_otel"
3 -version.workspace = true
4 -edition.workspace = true
5 -rust-version.workspace = true
6 -
7 -[dependencies]
8 -opentelemetry-proto = { workspace = true, features = ["logs", "metrics", "with-serde"] }
9 -flatten-serde-json = { workspace = true }
10 -serde_json = { workspace = true }
src/crates/jf/flatten_otel/src/lib.rs deleted
-86
@@ -1,86 +0,0 @@
1 -use serde_json::{Map as JsonMap, Value as JsonValue};
2 -
3 -use opentelemetry_proto::tonic::{
4 - common::v1::{AnyValue, InstrumentationScope, KeyValue},
5 - resource::v1::Resource,
6 -};
7 -
8 -mod logs;
9 -mod metrics;
10 -
11 -pub use logs::{json_from_export_logs_service_request, json_from_log_record};
12 -pub use metrics::flatten_metrics_request;
13 -
14 -fn json_from_key_value_list(kvl: &Vec<KeyValue>) -> JsonMap<String, JsonValue> {
15 - let mut map = JsonMap::new();
16 -
17 - for kv in kvl {
18 - if let Some(any_value) = &kv.value {
19 - map.insert(kv.key.clone(), json_from_any_value(any_value));
20 - } else {
21 - map.insert(kv.key.clone(), JsonValue::Null);
22 - }
23 - }
24 -
25 - flatten_serde_json::flatten(&map)
26 -}
27 -
28 -fn json_from_any_value(any_value: &AnyValue) -> JsonValue {
29 - use opentelemetry_proto::tonic::common::v1::any_value::Value;
30 -
31 - match &any_value.value {
32 - Some(Value::StringValue(s)) => JsonValue::String(s.clone()),
33 - Some(Value::BoolValue(b)) => JsonValue::Bool(*b),
34 - Some(Value::IntValue(i)) => JsonValue::Number(
35 - serde_json::Number::from_f64(*i as f64).unwrap_or_else(|| serde_json::Number::from(0)),
36 - ),
37 - Some(Value::DoubleValue(d)) => JsonValue::Number(
38 - serde_json::Number::from_f64(*d).unwrap_or_else(|| serde_json::Number::from(0)),
39 - ),
40 - Some(Value::ArrayValue(array)) => {
41 - let values: Vec<JsonValue> = array.values.iter().map(json_from_any_value).collect();
42 - JsonValue::Array(values)
43 - }
44 - Some(Value::KvlistValue(kvl)) => JsonValue::Object(json_from_key_value_list(&kvl.values)),
45 - Some(Value::BytesValue(_bytes)) => {
46 - todo!("Add support for byte values");
47 - }
48 - None => JsonValue::Null,
49 - }
50 -}
51 -
52 -fn json_from_resource(jm: &mut JsonMap<String, JsonValue>, resource: &Resource) {
53 - if !resource.attributes.is_empty() {
54 - let resource_attrs = json_from_key_value_list(&resource.attributes);
55 - for (key, value) in resource_attrs {
56 - jm.insert(format!("resource.attributes.{}", key), value);
57 - }
58 - }
59 -}
60 -
61 -fn json_from_instrumentation_scope(
62 - jm: &mut JsonMap<String, JsonValue>,
63 - scope: &InstrumentationScope,
64 -) {
65 - if !scope.name.is_empty() {
66 - jm.insert(
67 - "scope.name".to_string(),
68 - JsonValue::String(scope.name.clone()),
69 - );
70 - }
71 -
72 - if !scope.version.is_empty() {
73 - jm.insert(
74 - "scope.version".to_string(),
75 - JsonValue::String(scope.version.clone()),
76 - );
77 - }
78 -
79 - if !scope.attributes.is_empty() {
80 - let scope_attrs = json_from_key_value_list(&scope.attributes);
81 -
82 - for (key, value) in scope_attrs {
83 - jm.insert(format!("scope.attributes.{}", key), value);
84 - }
85 - }
86 -}
src/crates/jf/flatten_otel/src/logs.rs deleted
-126
@@ -1,126 +0,0 @@
1 -use serde_json::{Map as JsonMap, Value as JsonValue};
2 -
3 -use opentelemetry_proto::tonic::{
4 - collector::logs::v1::ExportLogsServiceRequest, logs::v1::LogRecord,
5 -};
6 -
7 -use crate::{
8 - json_from_any_value, json_from_instrumentation_scope, json_from_key_value_list,
9 - json_from_resource,
10 -};
11 -
12 -pub fn json_from_log_record(jm: &mut JsonMap<String, JsonValue>, log_record: &LogRecord) {
13 - // Add log record fields with "log." prefix
14 - jm.insert(
15 - "log.time_unix_nano".to_string(),
16 - JsonValue::Number(serde_json::Number::from(log_record.time_unix_nano)),
17 - );
18 - jm.insert(
19 - "log.observed_time_unix_nano".to_string(),
20 - JsonValue::Number(serde_json::Number::from(log_record.observed_time_unix_nano)),
21 - );
22 - jm.insert(
23 - "log.severity_number".to_string(),
24 - JsonValue::Number(serde_json::Number::from(log_record.severity_number)),
25 - );
26 - jm.insert(
27 - "log.severity_text".to_string(),
28 - JsonValue::String(log_record.severity_text.clone()),
29 - );
30 -
31 - // Add body if present
32 - if let Some(body) = &log_record.body {
33 - let mut temp_map = JsonMap::new();
34 - temp_map.insert("body".to_string(), json_from_any_value(body));
35 -
36 - let flattened_body = flatten_serde_json::flatten(&temp_map);
37 - for (key, value) in flattened_body {
38 - if key != "body" {
39 - jm.insert(format!("log.{}", key), value);
40 - }
41 - }
42 - }
43 -
44 - // Add event name
45 - jm.insert(
46 - "log.event_name".to_string(),
47 - JsonValue::String(log_record.event_name.clone()),
48 - );
49 -
50 - // Add log record attributes
51 - let log_attrs = json_from_key_value_list(&log_record.attributes);
52 - for (key, value) in log_attrs {
53 - jm.insert(format!("log.attributes.{}", key), value);
54 - }
55 -
56 - jm.insert(
57 - "log.dropped_attributes_count".to_string(),
58 - JsonValue::Number(serde_json::Number::from(
59 - log_record.dropped_attributes_count,
60 - )),
61 - );
62 - jm.insert(
63 - "log.flags".to_string(),
64 - JsonValue::Number(serde_json::Number::from(log_record.flags)),
65 - );
66 -
67 - // Add trace_id and span_id as hex strings
68 - // if !log_record.trace_id.is_empty() {
69 - // jm.insert(
70 - // "log.trace_id".to_string(),
71 - // JsonValue::String(hex::encode(&log_record.trace_id)),
72 - // );
73 - // }
74 - // if !log_record.span_id.is_empty() {
75 - // jm.insert(
76 - // "log.span_id".to_string(),
77 - // JsonValue::String(hex::encode(&log_record.span_id)),
78 - // );
79 - // }
80 -}
81 -
82 -// TODO: this does not belong here, it should be part of the service.
83 -pub fn json_from_export_logs_service_request(request: &ExportLogsServiceRequest) -> JsonValue {
84 - let mut items = Vec::new();
85 -
86 - for resource_logs in &request.resource_logs {
87 - for scope_logs in &resource_logs.scope_logs {
88 - for log_record in &scope_logs.log_records {
89 - let mut jm = JsonMap::new();
90 -
91 - // Add resource information
92 - if let Some(resource) = resource_logs.resource.as_ref() {
93 - json_from_resource(&mut jm, resource);
94 - }
95 -
96 - // Add resource schema URL
97 - if !resource_logs.schema_url.is_empty() {
98 - jm.insert(
99 - "resource.schema_url".to_string(),
100 - JsonValue::String(resource_logs.schema_url.clone()),
101 - );
102 - }
103 -
104 - // Add scope information
105 - if let Some(scope) = scope_logs.scope.as_ref() {
106 - json_from_instrumentation_scope(&mut jm, scope);
107 - }
108 -
109 - // Add scope schema URL
110 - if !scope_logs.schema_url.is_empty() {
111 - jm.insert(
112 - "scope.schema_url".to_string(),
113 - JsonValue::String(scope_logs.schema_url.clone()),
114 - );
115 - }
116 -
117 - // Add log record information
118 - json_from_log_record(&mut jm, log_record);
119 -
120 - items.push(JsonValue::Object(jm));
121 - }
122 - }
123 - }
124 -
125 - JsonValue::Array(items)
126 -}
src/crates/jf/flatten_otel/src/metrics.rs deleted
-284
@@ -1,284 +0,0 @@
1 -use serde_json::{json, Map as JsonMap, Value as JsonValue};
2 -
3 -use opentelemetry_proto::tonic::{
4 - collector::metrics::v1::ExportMetricsServiceRequest,
5 - metrics::v1::{
6 - metric::Data, AggregationTemporality, Gauge, Histogram, HistogramDataPoint, Metric,
7 - NumberDataPoint, ResourceMetrics, ScopeMetrics, Sum,
8 - },
9 -};
10 -
11 -use crate::{json_from_instrumentation_scope, json_from_key_value_list, json_from_resource};
12 -
13 -pub fn flatten_metrics_request(
14 - req: &ExportMetricsServiceRequest,
15 -) -> Vec<JsonMap<String, JsonValue>> {
16 - req.resource_metrics
17 - .iter()
18 - .flat_map(flatten_resource_metrics)
19 - .collect()
20 -}
21 -
22 -fn flatten_resource_metrics(resource_metrics: &ResourceMetrics) -> Vec<JsonMap<String, JsonValue>> {
23 - resource_metrics
24 - .scope_metrics
25 - .iter()
26 - .flat_map(|scope_metrics| {
27 - let mut flattened_metrics = flatten_scope_metrics(scope_metrics);
28 -
29 - if let Some(resource) = &resource_metrics.resource {
30 - flattened_metrics
31 - .iter_mut()
32 - .for_each(|jm| json_from_resource(jm, resource));
33 - }
34 -
35 - flattened_metrics
36 - })
37 - .collect()
38 -}
39 -
40 -fn flatten_scope_metrics(scope_metrics: &ScopeMetrics) -> Vec<JsonMap<String, JsonValue>> {
41 - scope_metrics
42 - .metrics
43 - .iter()
44 - .flat_map(|metric| {
45 - let mut flattened_metrics = flatten_metric(metric);
46 -
47 - if let Some(scope) = &scope_metrics.scope {
48 - flattened_metrics
49 - .iter_mut()
50 - .for_each(|jm| json_from_instrumentation_scope(jm, scope));
51 - }
52 -
53 - flattened_metrics
54 - })
55 - .collect()
56 -}
57 -
58 -fn flatten_metric(metric: &Metric) -> Vec<JsonMap<String, JsonValue>> {
59 - let Some(data) = metric.data.as_ref() else {
60 - return Vec::new();
61 - };
62 -
63 - let mut flattened_metrics = match data {
64 - Data::Gauge(gauge) => flatten_gauge(gauge),
65 - Data::Sum(sum) => flatten_sum(sum),
66 - Data::Histogram(histogram) => flatten_histogram(histogram),
67 - Data::ExponentialHistogram(_) | Data::Summary(_) => {
68 - eprintln!(
69 - "Summary and exponential histogram metrics are not supported yet ('{}')",
70 - metric.name
71 - );
72 - return Vec::new();
73 - }
74 - };
75 -
76 - for jm in flattened_metrics.iter_mut() {
77 - // Add metric metadata
78 - jm.insert(
79 - "metric.name".to_string(),
80 - JsonValue::String(metric.name.clone()),
81 - );
82 - jm.insert(
83 - "metric.description".to_string(),
84 - JsonValue::String(metric.description.clone()),
85 - );
86 - jm.insert(
87 - "metric.unit".to_string(),
88 - JsonValue::String(metric.unit.clone()),
89 - );
90 -
91 - for (key, value) in json_from_key_value_list(&metric.metadata) {
92 - jm.insert(format!("metric.metadata.{}", key), value);
93 - }
94 - }
95 -
96 - flattened_metrics
97 -}
98 -
99 -fn flatten_gauge(gauge: &Gauge) -> Vec<JsonMap<String, JsonValue>> {
100 - let mut flattened_metrics = Vec::new();
101 -
102 - for data_point in &gauge.data_points {
103 - let mut jm = flatten_number_data_point(data_point);
104 -
105 - if jm.is_empty() {
106 - continue;
107 - }
108 -
109 - jm.insert(
110 - "metric.type".to_string(),
111 - JsonValue::String("gauge".to_string()),
112 - );
113 -
114 - flattened_metrics.push(jm);
115 - }
116 -
117 - flattened_metrics
118 -}
119 -
120 -fn flatten_sum(sum: &Sum) -> Vec<JsonMap<String, JsonValue>> {
121 - let mut flattened_metrics = Vec::new();
122 -
123 - let aggregation_temporality = match sum.aggregation_temporality {
124 - x if x == AggregationTemporality::Unspecified as i32 => "unspecified",
125 - x if x == AggregationTemporality::Delta as i32 => "delta",
126 - x if x == AggregationTemporality::Cumulative as i32 => "cumulative",
127 - _ => "unknown",
128 - };
129 -
130 - for data_point in &sum.data_points {
131 - let mut jm = flatten_number_data_point(data_point);
132 -
133 - if jm.is_empty() {
134 - continue;
135 - }
136 -
137 - jm.insert(
138 - "metric.type".to_string(),
139 - JsonValue::String("sum".to_string()),
140 - );
141 - jm.insert(
142 - "metric.aggregation_temporality".to_string(),
143 - JsonValue::String(aggregation_temporality.to_string()),
144 - );
145 - jm.insert(
146 - "metric.is_monotonic".to_string(),
147 - JsonValue::Bool(sum.is_monotonic),
148 - );
149 -
150 - flattened_metrics.push(jm);
151 - }
152 -
153 - flattened_metrics
154 -}
155 -
156 -fn flatten_histogram(histogram: &Histogram) -> Vec<JsonMap<String, JsonValue>> {
157 - let mut flattened_metrics = Vec::new();
158 -
159 - let aggregation_temporality = match histogram.aggregation_temporality {
160 - x if x == AggregationTemporality::Unspecified as i32 => "unspecified",
161 - x if x == AggregationTemporality::Delta as i32 => "delta",
162 - x if x == AggregationTemporality::Cumulative as i32 => "cumulative",
163 - _ => "unknown",
164 - };
165 -
166 - for data_point in &histogram.data_points {
167 - let mut bucket_maps = flatten_histogram_data_point(data_point);
168 -
169 - for jm in bucket_maps.iter_mut() {
170 - jm.insert(
171 - "metric.type".to_string(),
172 - JsonValue::String("histogram".to_string()),
173 - );
174 - jm.insert(
175 - "metric.aggregation_temporality".to_string(),
176 - JsonValue::String(aggregation_temporality.to_string()),
177 - );
178 - }
179 -
180 - flattened_metrics.extend(bucket_maps);
181 - }
182 -
183 - flattened_metrics
184 -}
185 -
186 -fn flatten_number_data_point(ndp: &NumberDataPoint) -> JsonMap<String, JsonValue> {
187 - let mut jm = JsonMap::new();
188 -
189 - let Some(value) = &ndp.value else {
190 - return jm;
191 - };
192 -
193 - match value {
194 - opentelemetry_proto::tonic::metrics::v1::number_data_point::Value::AsDouble(d) => {
195 - jm.insert("metric.value".to_string(), json!(d));
196 - }
197 - opentelemetry_proto::tonic::metrics::v1::number_data_point::Value::AsInt(i) => {
198 - jm.insert("metric.value".to_string(), JsonValue::Number((*i).into()));
199 - }
200 - };
201 -
202 - jm.insert(
203 - "metric.start_time_unix_nano".to_string(),
204 - JsonValue::Number(ndp.start_time_unix_nano.into()),
205 - );
206 - jm.insert(
207 - "metric.time_unix_nano".to_string(),
208 - JsonValue::Number(ndp.time_unix_nano.into()),
209 - );
210 -
211 - for (key, value) in json_from_key_value_list(&ndp.attributes) {
212 - jm.insert(format!("metric.attributes.{}", key), value);
213 - }
214 -
215 - if !ndp.exemplars.is_empty() {
216 - // todo!...
217 - }
218 -
219 - if ndp.flags != 0 {
220 - jm.insert(
221 - "metric.flags".to_string(),
222 - JsonValue::Number(ndp.flags.into()),
223 - );
224 - }
225 -
226 - jm
227 -}
228 -
229 -fn flatten_histogram_data_point(hdp: &HistogramDataPoint) -> Vec<JsonMap<String, JsonValue>> {
230 - let mut results = Vec::new();
231 -
232 - if hdp.bucket_counts.is_empty() || hdp.explicit_bounds.is_empty() {
233 - return results;
234 - }
235 -
236 - // Create base map with common fields
237 - let mut base_map = JsonMap::new();
238 - base_map.insert(
239 - "metric.start_time_unix_nano".to_string(),
240 - JsonValue::Number(hdp.start_time_unix_nano.into()),
241 - );
242 - base_map.insert(
243 - "metric.time_unix_nano".to_string(),
244 - JsonValue::Number(hdp.time_unix_nano.into()),
245 - );
246 -
247 - // Add attributes
248 - for (key, value) in json_from_key_value_list(&hdp.attributes) {
249 - base_map.insert(format!("metric.attributes.{}", key), value);
250 - }
251 -
252 - // Handle regular buckets
253 - for (&bound, &count) in hdp.explicit_bounds.iter().zip(hdp.bucket_counts.iter()) {
254 - let mut bucket_map = base_map.clone();
255 -
256 - // Set dimension name to bucket identifier
257 - let bucket_name = format!("{}", bound);
258 - bucket_map.insert(
259 - "metric.attributes._nd_dimension".to_string(),
260 - JsonValue::String("bucket".to_string()),
261 - );
262 - bucket_map.insert("bucket".to_string(), JsonValue::String(bucket_name.clone()));
263 - bucket_map.insert("metric.value".to_string(), JsonValue::from(count));
264 -
265 - results.push(bucket_map);
266 - }
267 -
268 - // Handle +Inf bucket if it exists
269 - if hdp.bucket_counts.len() > hdp.explicit_bounds.len() {
270 - let mut inf_map = base_map.clone();
271 - let inf_count = hdp.bucket_counts[hdp.bucket_counts.len() - 1];
272 -
273 - inf_map.insert(
274 - "metric.attributes._nd_dimension".to_string(),
275 - JsonValue::String("bucket".to_string()),
276 - );
277 - inf_map.insert("bucket".to_string(), JsonValue::String("+Inf".to_string()));
278 - inf_map.insert("metric.value".to_string(), JsonValue::from(inf_count));
279 -
280 - results.push(inf_map);
281 - }
282 -
283 - results
284 -}
src/crates/jf/flog-otel/Cargo.toml deleted
-22
@@ -1,22 +0,0 @@
1 -[package]
2 -name = "flog-otel"
3 -version.workspace = true
4 -edition.workspace = true
5 -rust-version.workspace = true
6 -
7 -[dependencies]
8 -tokio.workspace = true
9 -serde.workspace = true
10 -serde_json.workspace = true
11 -chrono.workspace = true
12 -clap.workspace = true
13 -uuid.workspace = true
14 -anyhow.workspace = true
15 -tracing.workspace = true
16 -tracing-subscriber.workspace = true
17 -tonic.workspace = true
18 -prost.workspace = true
19 -opentelemetry-proto.workspace = true
20 -
21 -[build-dependencies]
22 -tonic-build.workspace = true
src/crates/jf/flog-otel/src/main.rs deleted
-312
@@ -1,312 +0,0 @@
1 -use anyhow::Result;
2 -use chrono::{DateTime, Utc};
3 -use clap::Parser;
4 -use opentelemetry_proto::tonic::collector::logs::v1::{
5 - logs_service_client::LogsServiceClient, ExportLogsServiceRequest,
6 -};
7 -use opentelemetry_proto::tonic::common::v1::{any_value, AnyValue, KeyValue};
8 -use opentelemetry_proto::tonic::logs::v1::{LogRecord, ResourceLogs, ScopeLogs};
9 -use opentelemetry_proto::tonic::resource::v1::Resource;
10 -use serde::Deserialize;
11 -use std::process::Stdio;
12 -use std::sync::Arc;
13 -use tokio::io::{AsyncBufReadExt, BufReader};
14 -use tokio::sync::Mutex;
15 -use tokio::time::{Duration, Instant};
16 -use tracing::{error, info, warn};
17 -use tracing_subscriber;
18 -use uuid::Uuid;
19 -
20 -#[derive(Parser, Debug)]
21 -#[command(author, version, about, long_about = None)]
22 -struct Args {
23 - #[arg(short, long, default_value = "1048576")]
24 - rate_limit_bytes: u64,
25 -
26 - #[arg(short, long, default_value = "http://127.0.0.1:4317")]
27 - otel_endpoint: String,
28 -
29 - #[arg(short, long, default_value = "1000")]
30 - log_count: u32,
31 -
32 - #[arg(long, default_value = "false")]
33 - loop_forever: bool,
34 -}
35 -
36 -#[derive(Deserialize, Debug)]
37 -struct FlogEntry {
38 - host: String,
39 - #[serde(rename = "user-identifier")]
40 - user_identifier: String,
41 - datetime: String,
42 - method: String,
43 - request: String,
44 - protocol: String,
45 - status: u16,
46 - bytes: u64,
47 - referer: String,
48 -}
49 -
50 -struct RateLimiter {
51 - bytes_sent: u64,
52 - last_reset: Instant,
53 - limit: u64,
54 -}
55 -
56 -impl RateLimiter {
57 - fn new(limit: u64) -> Self {
58 - Self {
59 - bytes_sent: 0,
60 - last_reset: Instant::now(),
61 - limit,
62 - }
63 - }
64 -
65 - async fn can_send(&mut self, size: u64) -> bool {
66 - if self.last_reset.elapsed() >= Duration::from_secs(1) {
67 - self.bytes_sent = 0;
68 - self.last_reset = Instant::now();
69 - }
70 -
71 - if self.bytes_sent + size <= self.limit {
72 - self.bytes_sent += size;
73 - true
74 - } else {
75 - false
76 - }
77 - }
78 -}
79 -
80 -fn parse_flog_datetime(datetime_str: &str) -> Result<DateTime<Utc>> {
81 - let dt = chrono::DateTime::parse_from_str(datetime_str, "%d/%b/%Y:%H:%M:%S %z")?;
82 - Ok(dt.with_timezone(&Utc))
83 -}
84 -
85 -fn flog_to_otel(entry: FlogEntry) -> Result<LogRecord> {
86 - let dt = parse_flog_datetime(&entry.datetime)?;
87 - let time_unix_nano = dt.timestamp_nanos_opt().unwrap_or(0) as u64;
88 -
89 - let severity_number = match entry.status {
90 - 200..=299 => 9, // INFO
91 - 300..=399 => 5, // DEBUG
92 - 400..=499 => 13, // WARN
93 - 500..=599 => 17, // ERROR
94 - _ => 9, // INFO
95 - };
96 -
97 - let log_message = format!(
98 - "{} {} {} {} {} {}",
99 - entry.host, entry.method, entry.request, entry.protocol, entry.status, entry.bytes
100 - );
101 -
102 - let attributes = vec![
103 - KeyValue {
104 - key: "host".to_string(),
105 - value: Some(AnyValue {
106 - value: Some(any_value::Value::StringValue(entry.host)),
107 - }),
108 - },
109 - KeyValue {
110 - key: "method".to_string(),
111 - value: Some(AnyValue {
112 - value: Some(any_value::Value::StringValue(entry.method)),
113 - }),
114 - },
115 - KeyValue {
116 - key: "request".to_string(),
117 - value: Some(AnyValue {
118 - value: Some(any_value::Value::StringValue(entry.request)),
119 - }),
120 - },
121 - KeyValue {
122 - key: "protocol".to_string(),
123 - value: Some(AnyValue {
124 - value: Some(any_value::Value::StringValue(entry.protocol)),
125 - }),
126 - },
127 - KeyValue {
128 - key: "status".to_string(),
129 - value: Some(AnyValue {
130 - value: Some(any_value::Value::IntValue(entry.status as i64)),
131 - }),
132 - },
133 - KeyValue {
134 - key: "response_bytes".to_string(),
135 - value: Some(AnyValue {
136 - value: Some(any_value::Value::IntValue(entry.bytes as i64)),
137 - }),
138 - },
139 - KeyValue {
140 - key: "referer".to_string(),
141 - value: Some(AnyValue {
142 - value: Some(any_value::Value::StringValue(entry.referer)),
143 - }),
144 - },
145 - KeyValue {
146 - key: "user_identifier".to_string(),
147 - value: Some(AnyValue {
148 - value: Some(any_value::Value::StringValue(entry.user_identifier)),
149 - }),
150 - },
151 - ];
152 -
153 - let trace_id_bytes = Uuid::new_v4().as_u128().to_be_bytes().to_vec();
154 - let span_id_bytes = (Uuid::new_v4().as_u128() as u64).to_be_bytes().to_vec();
155 -
156 - Ok(LogRecord {
157 - time_unix_nano,
158 - severity_number: severity_number as i32,
159 - severity_text: String::new(),
160 - body: Some(AnyValue {
161 - value: Some(any_value::Value::StringValue(log_message)),
162 - }),
163 - attributes,
164 - dropped_attributes_count: 0,
165 - flags: 0,
166 - trace_id: trace_id_bytes,
167 - span_id: span_id_bytes,
168 - ..Default::default()
169 - })
170 -}
171 -
172 -async fn send_otel_logs(logs: Vec<LogRecord>, endpoint: &str) -> Result<()> {
173 - let mut client = LogsServiceClient::connect(endpoint.to_string()).await?;
174 -
175 - let resource_logs = vec![ResourceLogs {
176 - resource: Some(Resource {
177 - attributes: vec![
178 - KeyValue {
179 - key: "service.name".to_string(),
180 - value: Some(AnyValue {
181 - value: Some(any_value::Value::StringValue(
182 - "flog-otel-wrapper".to_string(),
183 - )),
184 - }),
185 - },
186 - KeyValue {
187 - key: "service.version".to_string(),
188 - value: Some(AnyValue {
189 - value: Some(any_value::Value::StringValue("0.1.0".to_string())),
190 - }),
191 - },
192 - ],
193 - dropped_attributes_count: 0,
194 - entity_refs: vec![],
195 - }),
196 - scope_logs: vec![ScopeLogs {
197 - scope: Some(
198 - opentelemetry_proto::tonic::common::v1::InstrumentationScope {
199 - name: "flog-otel-wrapper".to_string(),
200 - version: "0.1.0".to_string(),
201 - attributes: vec![],
202 - dropped_attributes_count: 0,
203 - },
204 - ),
205 - log_records: logs,
206 - schema_url: String::new(),
207 - }],
208 - schema_url: String::new(),
209 - }];
210 -
211 - let request = tonic::Request::new(ExportLogsServiceRequest { resource_logs });
212 -
213 - let response = client.export(request).await?;
214 -
215 - if response.get_ref().partial_success.is_some() {
216 - let partial_success = response.get_ref().partial_success.as_ref().unwrap();
217 - if partial_success.rejected_log_records > 0 {
218 - warn!("Some logs were rejected: {}", partial_success.error_message);
219 - }
220 - }
221 -
222 - info!("Successfully sent logs to OTEL collector via gRPC");
223 - Ok(())
224 -}
225 -
226 -#[tokio::main]
227 -async fn main() -> Result<()> {
228 - tracing_subscriber::fmt::init();
229 -
230 - let args = Args::parse();
231 - let rate_limiter = Arc::new(Mutex::new(RateLimiter::new(args.rate_limit_bytes)));
232 -
233 - info!(
234 - "Starting flog-otel-wrapper with rate limit: {} bytes/sec",
235 - args.rate_limit_bytes
236 - );
237 - info!("OTEL endpoint: {}", args.otel_endpoint);
238 -
239 - loop {
240 - let log_count_str = args.log_count.to_string();
241 - let mut flog_args = vec!["-f", "json", "-t", "stdout"];
242 -
243 - if args.loop_forever {
244 - flog_args.extend(&["-l"]);
245 - } else {
246 - flog_args.extend(&["-n", &log_count_str]);
247 - }
248 -
249 - let mut child = tokio::process::Command::new("flog")
250 - .args(&flog_args)
251 - .stdout(Stdio::piped())
252 - .spawn()?;
253 -
254 - let stdout = child.stdout.take().unwrap();
255 - let reader = BufReader::new(stdout);
256 - let mut lines = reader.lines();
257 -
258 - let mut log_batch = Vec::new();
259 - const BATCH_SIZE: usize = 100;
260 -
261 - while let Some(line) = lines.next_line().await? {
262 - match serde_json::from_str::<FlogEntry>(&line) {
263 - Ok(entry) => match flog_to_otel(entry) {
264 - Ok(otel_log) => {
265 - log_batch.push(otel_log);
266 -
267 - if log_batch.len() >= BATCH_SIZE {
268 - let batch_size = (log_batch.len() * 1024) as u64;
269 -
270 - let mut limiter = rate_limiter.lock().await;
271 - if limiter.can_send(batch_size).await {
272 - drop(limiter);
273 -
274 - if let Err(e) =
275 - send_otel_logs(log_batch.clone(), &args.otel_endpoint).await
276 - {
277 - error!("Failed to send OTEL logs: {}", e);
278 - }
279 - log_batch.clear();
280 - } else {
281 - drop(limiter);
282 - info!("Rate limit reached, waiting 1 second...");
283 - tokio::time::sleep(Duration::from_secs(1)).await;
284 - }
285 - }
286 - }
287 - Err(e) => error!("Failed to convert to OTEL format: {}", e),
288 - },
289 - Err(e) => error!("Failed to parse JSON: {}", e),
290 - }
291 - }
292 -
293 - if !log_batch.is_empty() {
294 - let batch_size = (log_batch.len() * 1024) as u64;
295 - let mut limiter = rate_limiter.lock().await;
296 - if limiter.can_send(batch_size).await {
297 - drop(limiter);
298 - if let Err(e) = send_otel_logs(log_batch, &args.otel_endpoint).await {
299 - error!("Failed to send final OTEL logs: {}", e);
300 - }
301 - }
302 - }
303 -
304 - child.wait().await?;
305 -
306 - if !args.loop_forever {
307 - break;
308 - }
309 - }
310 -
311 - Ok(())
312 -}
src/crates/jf/journal_log/Cargo.toml deleted
-14
@@ -1,14 +0,0 @@
1 -[package]
2 -name = "journal_log"
3 -version.workspace = true
4 -edition.workspace = true
5 -rust-version.workspace = true
6 -
7 -[dependencies]
8 -error = { path = "../error" }
9 -journal_file = { path = "../journal_file" }
10 -memmap2 = { workspace = true }
11 -uuid = { version = "1.0", features = ["v4", "rng"] }
12 -
13 -[dev-dependencies]
14 -tempfile = { workspace = true }
src/crates/jf/journal_log/src/lib.rs deleted
-632
@@ -1,632 +0,0 @@
1 -use error::{JournalError, Result};
2 -use journal_file::{
3 - load_boot_id, BucketUtilization, JournalFile, JournalFileOptions, JournalWriter,
4 -};
5 -use memmap2::MmapMut;
6 -use std::cmp::Ordering;
7 -use std::ffi::OsStr;
8 -use std::path::{Path, PathBuf};
9 -use std::time::{Duration, SystemTime, UNIX_EPOCH};
10 -
11 -#[derive(Debug, Clone, PartialEq, Eq)]
12 -pub struct JournalFileInfo {
13 - pub path: PathBuf,
14 - pub timestamp: SystemTime,
15 - pub counter: u64,
16 - pub size: Option<u64>,
17 -}
18 -
19 -impl JournalFileInfo {
20 - pub fn from_path(path: impl AsRef<Path>) -> Result<Self> {
21 - let path = path.as_ref();
22 - let filename = path
23 - .file_name()
24 - .and_then(OsStr::to_str)
25 - .ok_or(JournalError::InvalidFilename)?;
26 -
27 - let (timestamp, counter) = Self::parse_filename(filename)?;
28 -
29 - Ok(Self {
30 - path: path.to_path_buf(),
31 - timestamp,
32 - counter,
33 - size: None,
34 - })
35 - }
36 -
37 - pub fn from_parts(
38 - timestamp: SystemTime,
39 - counter: u64,
40 - size: Option<u64>,
41 - ) -> Result<JournalFileInfo> {
42 - let duration = timestamp
43 - .duration_since(UNIX_EPOCH)
44 - .map_err(|_| JournalError::SystemTimeError)?;
45 - let micros = duration.as_secs() * 1_000_000 + duration.subsec_micros() as u64;
46 - let path = PathBuf::from(format!("journal-{}-{}.journal", micros, counter));
47 -
48 - Ok(Self {
49 - path,
50 - timestamp,
51 - counter,
52 - size,
53 - })
54 - }
55 -
56 - /// Parse timestamp and counter from filename
57 - /// Expected format: "journal-{timestamp_micros}-{counter}.journal"
58 - fn parse_filename(filename: &str) -> Result<(SystemTime, u64)> {
59 - let name = filename.strip_suffix(".journal").unwrap_or(filename);
60 -
61 - if let Some(stripped) = name.strip_prefix("journal-") {
62 - let parts: Vec<&str> = stripped.split('-').collect();
63 - if parts.len() == 2 {
64 - let timestamp_micros: u64 = parts[0]
65 - .parse()
66 - .map_err(|_| JournalError::InvalidFilename)?;
67 - let counter: u64 = parts[1]
68 - .parse()
69 - .map_err(|_| JournalError::InvalidFilename)?;
70 -
71 - let timestamp = UNIX_EPOCH + Duration::from_micros(timestamp_micros);
72 - return Ok((timestamp, counter));
73 - }
74 - }
75 -
76 - Err(JournalError::InvalidFilename)
77 - }
78 -
79 - /// Get file size, loading from filesystem if not cached
80 - pub fn get_size(&mut self) -> Result<u64> {
81 - if let Some(size) = self.size {
82 - Ok(size)
83 - } else {
84 - let metadata = std::fs::metadata(&self.path)?;
85 - let size = metadata.len();
86 - self.size = Some(size);
87 - Ok(size)
88 - }
89 - }
90 -}
91 -
92 -// Implement ordering based on counter (for detecting duplicates and ordering)
93 -impl Ord for JournalFileInfo {
94 - fn cmp(&self, other: &Self) -> Ordering {
95 - // Order by counter only - this enables duplicate detection
96 - self.counter.cmp(&other.counter)
97 - }
98 -}
99 -
100 -impl PartialOrd for JournalFileInfo {
101 - fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
102 - Some(self.cmp(other))
103 - }
104 -}
105 -
106 -/// Determines when an active journal file should be sealed
107 -#[derive(Debug, Copy, Clone, Default)]
108 -pub struct RotationPolicy {
109 - /// Maximum file size before rotating (in bytes)
110 - pub size_of_journal_file: Option<u64>,
111 - /// Maximum duration that entries in a single file can span
112 - pub duration_of_journal_file: Option<Duration>,
113 -}
114 -
115 -impl RotationPolicy {
116 - pub fn with_size_of_journal_file(mut self, size_of_journal_file: u64) -> Self {
117 - self.size_of_journal_file = Some(size_of_journal_file);
118 - self
119 - }
120 -
121 - pub fn with_duration_of_journal_file(mut self, duration_of_journal_file: Duration) -> Self {
122 - self.duration_of_journal_file = Some(duration_of_journal_file);
123 - self
124 - }
125 -}
126 -
127 -/// Retention policy - determines when files should be removed
128 -#[derive(Debug, Copy, Clone, Default)]
129 -pub struct RetentionPolicy {
130 - /// Maximum number of journal files to keep
131 - pub number_of_journal_files: Option<usize>,
132 - /// Maximum total size of all journal files (in bytes)
133 - pub size_of_journal_files: Option<u64>,
134 - /// Maximum age of files to keep
135 - pub duration_of_journal_files: Option<Duration>,
136 -}
137 -
138 -impl RetentionPolicy {
139 - pub fn with_number_of_journal_files(mut self, number_of_journal_files: usize) -> Self {
140 - self.number_of_journal_files = Some(number_of_journal_files);
141 - self
142 - }
143 -
144 - pub fn with_size_of_journal_files(mut self, size_of_journal_files: u64) -> Self {
145 - self.size_of_journal_files = Some(size_of_journal_files);
146 - self
147 - }
148 -
149 - pub fn with_duration_of_journal_files(mut self, duration_of_journal_files: Duration) -> Self {
150 - self.duration_of_journal_files = Some(duration_of_journal_files);
151 - self
152 - }
153 -}
154 -
155 -/// Configuration for journal directory management
156 -#[derive(Debug, Clone)]
157 -pub struct JournalDirectoryConfig {
158 - /// Directory path where journal files are stored
159 - pub directory: PathBuf,
160 - /// Policy for when to rotate active files
161 - pub rotation_policy: RotationPolicy,
162 - /// Policy for when to remove old files
163 - pub retention_policy: RetentionPolicy,
164 -}
165 -
166 -impl JournalDirectoryConfig {
167 - pub fn new(directory: impl Into<PathBuf>) -> Self {
168 - Self {
169 - directory: directory.into(),
170 - rotation_policy: RotationPolicy::default(),
171 - retention_policy: RetentionPolicy::default(),
172 - }
173 - }
174 -
175 - pub fn with_sealing_policy(mut self, policy: RotationPolicy) -> Self {
176 - self.rotation_policy = policy;
177 - self
178 - }
179 -
180 - pub fn with_retention_policy(mut self, policy: RetentionPolicy) -> Self {
181 - self.retention_policy = policy;
182 - self
183 - }
184 -}
185 -
186 -/// Manages a directory of journal files with automatic cleanup and sealing
187 -#[derive(Debug)]
188 -pub struct JournalDirectory {
189 - config: JournalDirectoryConfig,
190 - /// Files ordered by counter (oldest counter first)
191 - files: Vec<JournalFileInfo>,
192 - /// Next counter value for new files
193 - next_counter: u64,
194 - /// Cached total size of all files
195 - total_size: u64,
196 -}
197 -
198 -impl JournalDirectory {
199 - /// Scan the directory and load existing journal files
200 - pub fn with_config(config: JournalDirectoryConfig) -> Result<Self> {
201 - // Create directory if it does not already exist.
202 - if !config.directory.exists() {
203 - std::fs::create_dir_all(&config.directory)?;
204 - } else if !config.directory.is_dir() {
205 - return Err(JournalError::NotADirectory);
206 - }
207 -
208 - let mut journal_directory = Self {
209 - config,
210 - files: Vec::new(),
211 - next_counter: 0,
212 - total_size: 0,
213 - };
214 -
215 - // Read all .journal files from directory
216 - for entry in std::fs::read_dir(&journal_directory.config.directory)? {
217 - let entry = entry?;
218 - let file_path = entry.path();
219 -
220 - if file_path.extension() != Some(OsStr::new("journal")) {
221 - continue;
222 - }
223 -
224 - match JournalFileInfo::from_path(&file_path) {
225 - Ok(mut file_info) => {
226 - // Load the actual file size from filesystem
227 - let file_size = file_info.get_size().unwrap_or(0);
228 - journal_directory.total_size += file_size;
229 - journal_directory.next_counter =
230 - journal_directory.next_counter.max(file_info.counter + 1);
231 - journal_directory.files.push(file_info);
232 - }
233 - Err(_) => {
234 - // Skip files with invalid names
235 - continue;
236 - }
237 - }
238 - }
239 -
240 - // Sort files by counter to maintain order
241 - journal_directory.files.sort();
242 -
243 - Ok(journal_directory)
244 - }
245 -
246 - pub fn directory_path(&self) -> &Path {
247 - &self.config.directory
248 - }
249 -
250 - pub fn get_full_path(&self, file_info: &JournalFileInfo) -> PathBuf {
251 - if file_info.path.is_absolute() {
252 - file_info.path.clone()
253 - } else {
254 - self.config.directory.join(&file_info.path)
255 - }
256 - }
257 -
258 - // Get information about all the files in the journal directory
259 - pub fn files(&self) -> Vec<JournalFileInfo> {
260 - self.files.clone()
261 - }
262 -
263 - /// Add a new journal file to the directory representation
264 - pub fn new_file(&mut self, existing_file: Option<JournalFileInfo>) -> Result<JournalFileInfo> {
265 - let timestamp = SystemTime::now();
266 - let new_file = JournalFileInfo::from_parts(timestamp, self.next_counter, None)?;
267 -
268 - self.files.push(new_file.clone());
269 - self.next_counter += 1;
270 -
271 - if let Some(existing_file) = existing_file {
272 - self.total_size += existing_file.size.unwrap_or(0);
273 - }
274 -
275 - Ok(new_file)
276 - }
277 -
278 - /// Remove the oldest file (by counter) from both filesystem and tracking
279 - fn remove_oldest_file(&mut self) -> Result<()> {
280 - if let Some(oldest_file) = self.files.first() {
281 - let file_path = self.get_full_path(oldest_file);
282 - let file_size = oldest_file.size.unwrap_or(0);
283 -
284 - // Remove from filesystem
285 - if let Err(e) = std::fs::remove_file(&file_path) {
286 - // Log error but continue cleanup - file might already be deleted
287 - eprintln!(
288 - "Warning: Failed to remove journal file {:?}: {}",
289 - file_path, e
290 - );
291 - }
292 -
293 - // Remove from tracking and update total size
294 - self.files.remove(0);
295 - self.total_size = self.total_size.saturating_sub(file_size);
296 - }
297 -
298 - Ok(())
299 - }
300 -
301 - /// Remove files older than the specified cutoff time
302 - fn remove_files_older_than(&mut self, cutoff_time: SystemTime) -> Result<()> {
303 - // Find files older than cutoff time
304 - let mut files_to_remove = Vec::new();
305 - for (index, file) in self.files.iter().enumerate() {
306 - if file.timestamp <= cutoff_time {
307 - files_to_remove.push(index);
308 - }
309 - }
310 -
311 - // Remove files in reverse order to maintain indices
312 - for &index in files_to_remove.iter().rev() {
313 - let file = &self.files[index];
314 - let file_path = self.get_full_path(file);
315 - let file_size = file.size.unwrap_or(0);
316 -
317 - // Remove from filesystem
318 - if let Err(e) = std::fs::remove_file(&file_path) {
319 - // Log error but continue cleanup
320 - eprintln!(
321 - "Warning: Failed to remove journal file {:?}: {}",
322 - file_path, e
323 - );
324 - }
325 -
326 - // Remove from tracking and update total size
327 - self.files.remove(index);
328 - self.total_size = self.total_size.saturating_sub(file_size);
329 - }
330 -
331 - Ok(())
332 - }
333 -
334 - /// Enforce the retention policy by removing old files
335 - pub fn enforce_retention_policy(&mut self) -> Result<()> {
336 - let policy = self.config.retention_policy;
337 -
338 - // 1. Remove by file count limit
339 - if let Some(max_files) = policy.number_of_journal_files {
340 - while self.files.len() > max_files {
341 - self.remove_oldest_file()?;
342 - }
343 - }
344 -
345 - // 2. Remove by total size limit
346 - if let Some(max_total_size) = policy.size_of_journal_files {
347 - while self.total_size > max_total_size && !self.files.is_empty() {
348 - self.remove_oldest_file()?;
349 - }
350 - }
351 -
352 - // 3. Remove by entry age limit
353 - if let Some(max_entry_age) = policy.duration_of_journal_files {
354 - let cutoff_time = SystemTime::now()
355 - .checked_sub(max_entry_age)
356 - .unwrap_or(SystemTime::UNIX_EPOCH);
357 - self.remove_files_older_than(cutoff_time)?;
358 - }
359 -
360 - Ok(())
361 - }
362 -}
363 -
364 -fn generate_uuid() -> [u8; 16] {
365 - uuid::Uuid::new_v4().into_bytes()
366 -}
367 -
368 -/// Configuration for JournalLog
369 -#[derive(Debug, Clone)]
370 -pub struct JournalLogConfig {
371 - /// Directory where journal files are stored
372 - pub journal_dir: PathBuf,
373 - /// Policy for when to rotate active files
374 - pub rotation_policy: RotationPolicy,
375 - /// Policy for when to remove old files
376 - pub retention_policy: RetentionPolicy,
377 -}
378 -
379 -impl JournalLogConfig {
380 - pub fn new(journal_dir: impl Into<PathBuf>) -> Self {
381 - Self {
382 - journal_dir: journal_dir.into(),
383 - rotation_policy: RotationPolicy::default()
384 - .with_size_of_journal_file(100 * 1024 * 1024) // 100MB
385 - .with_duration_of_journal_file(Duration::from_secs(2 * 3600)), // 2 hours
386 - retention_policy: RetentionPolicy::default()
387 - .with_number_of_journal_files(10) // 10 files
388 - .with_size_of_journal_files(1024 * 1024 * 1024) // 1GB
389 - .with_duration_of_journal_files(Duration::from_secs(7 * 24 * 3600)), // 7 days
390 - }
391 - }
392 -
393 - pub fn with_rotation_policy(mut self, policy: RotationPolicy) -> Self {
394 - self.rotation_policy = policy;
395 - self
396 - }
397 -
398 - pub fn with_retention_policy(mut self, policy: RetentionPolicy) -> Self {
399 - self.retention_policy = policy;
400 - self
401 - }
402 -}
403 -
404 -pub struct JournalLog {
405 - directory: JournalDirectory,
406 - current_file: Option<JournalFile<MmapMut>>,
407 - current_writer: Option<JournalWriter>,
408 - current_file_info: Option<JournalFileInfo>,
409 - machine_id: [u8; 16],
410 - boot_id: [u8; 16],
411 - seqnum_id: [u8; 16],
412 - previous_bucket_utilization: Option<BucketUtilization>,
413 -}
414 -
415 -/// Calculate optimal bucket sizes based on previous file utilization or rotation policy
416 -fn calculate_bucket_sizes(
417 - previous_utilization: Option<&BucketUtilization>,
418 - rotation_policy: &RotationPolicy,
419 -) -> (usize, usize) {
420 - if let Some(utilization) = previous_utilization {
421 - let data_utilization = utilization.data_utilization();
422 - let field_utilization = utilization.field_utilization();
423 -
424 - let data_buckets = if data_utilization > 0.75 {
425 - (utilization.data_total * 2).next_power_of_two()
426 - } else if data_utilization < 0.25 && utilization.data_total > 4096 {
427 - (utilization.data_total / 2).next_power_of_two()
428 - } else {
429 - utilization.data_total
430 - };
431 -
432 - let field_buckets = if field_utilization > 0.75 {
433 - (utilization.field_total * 2).next_power_of_two()
434 - } else if field_utilization < 0.25 && utilization.field_total > 512 {
435 - (utilization.field_total / 2).next_power_of_two()
436 - } else {
437 - utilization.field_total
438 - };
439 -
440 - (data_buckets, field_buckets)
441 - } else {
442 - // Initial sizing based on rotation policy max file size
443 - let max_file_size = rotation_policy
444 - .size_of_journal_file
445 - .unwrap_or(8 * 1024 * 1024);
446 -
447 - // 16 MiB -> 4096 data buckets
448 - let data_buckets = (max_file_size / 4096).max(1024).next_power_of_two() as usize;
449 - let field_buckets = 128; // Assume ~8:1 data:field ratio
450 -
451 - (data_buckets, field_buckets)
452 - }
453 -}
454 -
455 -impl JournalLog {
456 - pub fn new(config: JournalLogConfig) -> Result<Self> {
457 - let journal_config = JournalDirectoryConfig::new(&config.journal_dir)
458 - .with_sealing_policy(config.rotation_policy)
459 - .with_retention_policy(config.retention_policy);
460 -
461 - let mut directory = JournalDirectory::with_config(journal_config)?;
462 -
463 - // Enforce retention policy on startup to clean up any old files
464 - directory.enforce_retention_policy()?;
465 -
466 - let machine_id = journal_file::file::load_machine_id()?;
467 - let boot_id = load_boot_id()?;
468 - // TODO: Use NETDATA_INVOCATION_ID
469 - let seqnum_id = generate_uuid();
470 -
471 - Ok(JournalLog {
472 - directory,
473 - current_file: None,
474 - current_writer: None,
475 - current_file_info: None,
476 - machine_id,
477 - boot_id,
478 - seqnum_id,
479 - previous_bucket_utilization: None,
480 - })
481 - }
482 -
483 - fn ensure_active_journal(&mut self) -> Result<()> {
484 - // Check if rotation is needed before writing
485 - if let Some(writer) = &self.current_writer {
486 - if self.should_rotate(writer) {
487 - self.rotate_current_file()?;
488 - }
489 - }
490 -
491 - if self.current_file.is_none() {
492 - // Create a new journal file
493 - let file_info = self.directory.new_file(None)?;
494 -
495 - // Get the full path for the journal file
496 - let file_path = self.directory.get_full_path(&file_info);
497 -
498 - // Calculate optimal bucket sizes based on previous file utilization
499 - let (data_buckets, field_buckets) = calculate_bucket_sizes(
500 - self.previous_bucket_utilization.as_ref(),
501 - &self.directory.config.rotation_policy,
502 - );
503 -
504 - let options = JournalFileOptions::new(
505 - self.machine_id,
506 - self.boot_id,
507 - self.seqnum_id,
508 - generate_uuid(),
509 - )
510 - .with_window_size(8 * 1024 * 1024)
511 - .with_data_hash_table_buckets(data_buckets)
512 - .with_field_hash_table_buckets(field_buckets)
513 - .with_keyed_hash(true);
514 -
515 - let mut journal_file = JournalFile::create(&file_path, options)?;
516 - let writer = JournalWriter::new(&mut journal_file)?;
517 -
518 - self.current_file = Some(journal_file);
519 - self.current_writer = Some(writer);
520 - self.current_file_info = Some(file_info);
521 -
522 - // Enforce retention policy after creating new file to account for the new file count
523 - self.directory.enforce_retention_policy()?;
524 - }
525 -
526 - Ok(())
527 - }
528 -
529 - /// Checks if we have to rotate. Prioritizes file size over file creation
530 - /// time.
531 - fn should_rotate(&self, writer: &JournalWriter) -> bool {
532 - let policy = self.directory.config.rotation_policy;
533 -
534 - // Check if the file size went over the limit
535 - if let Some(max_size) = policy.size_of_journal_file {
536 - if writer.current_file_size() >= max_size {
537 - return true;
538 - }
539 - }
540 -
541 - // Check if the time span between first and last entries exceeds the limit
542 - let Some(file) = &self.current_file else {
543 - return false;
544 - };
545 - let Some(max_entry_span) = policy.duration_of_journal_file else {
546 - return false;
547 - };
548 - let Some(first_monotonic) = writer.first_entry_monotonic() else {
549 - return false;
550 - };
551 -
552 - let header = file.journal_header_ref();
553 - let last_monotonic = header.tail_entry_monotonic;
554 -
555 - // Convert monotonic timestamps (microseconds) to duration
556 - let entry_span = if last_monotonic >= first_monotonic {
557 - Duration::from_micros(last_monotonic - first_monotonic)
558 - } else {
559 - return false;
560 - };
561 -
562 - if entry_span >= max_entry_span {
563 - return true;
564 - }
565 -
566 - false
567 - }
568 -
569 - fn rotate_current_file(&mut self) -> Result<()> {
570 - // Capture bucket utilization before closing the file
571 - if let Some(file) = &self.current_file {
572 - self.previous_bucket_utilization = file.bucket_utilization();
573 - }
574 -
575 - // Update the current file's size in our tracking before closing
576 - if let (Some(file_info), Some(writer)) = (&mut self.current_file_info, &self.current_writer)
577 - {
578 - let current_size = writer.current_file_size();
579 - file_info.size = Some(current_size);
580 -
581 - // Update the size in the directory's file list
582 - if let Some(tracked_file) = self
583 - .directory
584 - .files
585 - .iter_mut()
586 - .find(|f| f.counter == file_info.counter)
587 - {
588 - let old_size = tracked_file.size.unwrap_or(0);
589 - tracked_file.size = Some(current_size);
590 -
591 - // Update total size tracking
592 - self.directory.total_size = self
593 - .directory
594 - .total_size
595 - .saturating_sub(old_size)
596 - .saturating_add(current_size);
597 - }
598 - }
599 -
600 - // Close current file
601 - self.current_file = None;
602 - self.current_writer = None;
603 - self.current_file_info = None;
604 -
605 - // Next call to ensure_active_journal() will create new file
606 - Ok(())
607 - }
608 -
609 - pub fn write_entry(&mut self, items: &[&[u8]]) -> Result<()> {
610 - if items.is_empty() {
611 - return Ok(());
612 - }
613 -
614 - self.ensure_active_journal()?;
615 -
616 - let journal_file = self.current_file.as_mut().unwrap();
617 - let writer = self.current_writer.as_mut().unwrap();
618 -
619 - let now = SystemTime::now();
620 - let realtime = now
621 - .duration_since(UNIX_EPOCH)
622 - .unwrap_or_default()
623 - .as_micros() as u64;
624 -
625 - // Use realtime for monotonic as well for simplicity
626 - let monotonic = realtime;
627 -
628 - writer.add_entry(journal_file, items, realtime, monotonic, self.boot_id)?;
629 -
630 - Ok(())
631 - }
632 -}
src/crates/jf/journal_reader/Cargo.toml deleted
-10
@@ -1,10 +0,0 @@
1 -[package]
2 -name = "journal_reader"
3 -version.workspace = true
4 -edition.workspace = true
5 -rust-version.workspace = true
6 -
7 -[dependencies]
8 -error = { path = "../error" }
9 -journal_file = { path = "../journal_file" }
10 -window_manager = { path = "../window_manager" }
src/crates/jf/journal_reader/src/journal_cursor.rs deleted
-214
@@ -1,214 +0,0 @@
1 -use crate::journal_filter::FilterExpr;
2 -use error::{JournalError, Result};
3 -use journal_file::{offset_array, offset_array::Direction, JournalFile};
4 -use window_manager::MemoryMap;
5 -
6 -#[derive(Debug, Copy, Clone, PartialEq, Eq)]
7 -pub enum Location {
8 - Head,
9 - Tail,
10 - Realtime(u64),
11 - Monotonic(u64, [u8; 16]),
12 - Seqnum(u64, Option<[u8; 16]>),
13 - XorHash(u64),
14 - ResolvedEntry(u64),
15 -}
16 -
17 -impl Default for Location {
18 - fn default() -> Self {
19 - Self::Head
20 - }
21 -}
22 -
23 -#[derive(Debug)]
24 -pub struct JournalCursor {
25 - pub location: Location,
26 - pub filter_expr: Option<FilterExpr>,
27 - pub array_cursor: Option<offset_array::Cursor>,
28 -}
29 -
30 -impl JournalCursor {
31 - #[allow(clippy::new_without_default)]
32 - pub fn new() -> Self {
33 - Self {
34 - location: Location::Head,
35 - filter_expr: None,
36 - array_cursor: None,
37 - }
38 - }
39 -
40 - pub fn set_location(&mut self, location: Location) {
41 - self.location = location;
42 - self.array_cursor = None;
43 - }
44 -
45 - pub fn set_filter(&mut self, filter_expr: FilterExpr) {
46 - self.filter_expr = Some(filter_expr);
47 - // FIXME: should we set cursor to None?
48 - }
49 -
50 - pub fn clear_filter(&mut self) {
51 - self.filter_expr = None;
52 - self.array_cursor = None;
53 - self.set_location(Location::Head);
54 - }
55 -
56 - pub fn step<M: MemoryMap>(
57 - &mut self,
58 - journal_file: &JournalFile<M>,
59 - direction: Direction,
60 - ) -> Result<bool> {
61 - let new_location = if self.filter_expr.is_some() {
62 - self.resolve_filter_location(journal_file, direction)?
63 - } else {
64 - self.resolve_array_cursor(journal_file, direction)?
65 - };
66 -
67 - if let Some(location) = new_location {
68 - self.location = location;
69 - Ok(true)
70 - } else {
71 - Ok(false)
72 - }
73 - }
74 -
75 - pub fn position(&self) -> Result<u64> {
76 - match self.location {
77 - Location::ResolvedEntry(entry_offset) => Ok(entry_offset),
78 - _ => Err(JournalError::UnsetCursor),
79 - }
80 - }
81 -
82 - fn resolve_array_cursor<M: MemoryMap>(
83 - &mut self,
84 - journal_file: &JournalFile<M>,
85 - direction: Direction,
86 - ) -> Result<Option<Location>> {
87 - let new_location = match (self.location, direction) {
88 - (Location::Head, Direction::Forward) => {
89 - let entry_list = journal_file
90 - .entry_list()
91 - .ok_or(JournalError::InvalidOffsetArrayOffset)?;
92 -
93 - let cursor = entry_list.cursor_head();
94 - let offset = cursor.value(journal_file)?;
95 - self.array_cursor = Some(cursor);
96 - Some(Location::ResolvedEntry(offset))
97 - }
98 - (Location::Head, Direction::Backward) => None,
99 - (Location::Tail, Direction::Forward) => None,
100 - (Location::Tail, Direction::Backward) => {
101 - let entry_list = journal_file
102 - .entry_list()
103 - .ok_or(JournalError::InvalidOffsetArrayOffset)?;
104 -
105 - let cursor = entry_list.cursor_tail(journal_file)?;
106 - let offset = cursor.value(journal_file)?;
107 - self.array_cursor = Some(cursor);
108 - Some(Location::ResolvedEntry(offset))
109 - }
110 - (Location::Realtime(realtime), _) => {
111 - let entry_list = journal_file
112 - .entry_list()
113 - .ok_or(JournalError::InvalidOffsetArrayOffset)?;
114 -
115 - let predicate = |entry_offset| {
116 - let entry_object = journal_file.entry_ref(entry_offset)?;
117 - Ok(entry_object.header.realtime < realtime)
118 - };
119 -
120 - let cursor = entry_list
121 - .directed_partition_point(journal_file, predicate, Direction::Forward)?
122 - .map(Ok)
123 - .unwrap_or_else(|| entry_list.cursor_tail(journal_file))?;
124 -
125 - let offset = cursor.value(journal_file)?;
126 - self.array_cursor = Some(cursor);
127 - Some(Location::ResolvedEntry(offset))
128 - }
129 - (Location::ResolvedEntry(_), Direction::Forward) => {
130 - let Some(cursor) = self.array_cursor.unwrap().next(journal_file)? else {
131 - return Ok(None);
132 - };
133 -
134 - let offset = cursor.value(journal_file)?;
135 - self.array_cursor = Some(cursor);
136 - Some(Location::ResolvedEntry(offset))
137 - }
138 - (Location::ResolvedEntry(_), Direction::Backward) => {
139 - let Some(cursor) = self.array_cursor.unwrap().previous(journal_file)? else {
140 - return Ok(None);
141 - };
142 -
143 - let offset = cursor.value(journal_file)?;
144 - self.array_cursor = Some(cursor);
145 - Some(Location::ResolvedEntry(offset))
146 - }
147 - _ => {
148 - unimplemented!()
149 - }
150 - };
151 -
152 - Ok(new_location)
153 - }
154 -
155 - fn resolve_filter_location<M: MemoryMap>(
156 - &mut self,
157 - journal_file: &JournalFile<M>,
158 - direction: Direction,
159 - ) -> Result<Option<Location>> {
160 - let filter_expr = self.filter_expr.as_mut().unwrap();
161 -
162 - let resolved_location = match (self.location, direction) {
163 - (Location::Head, Direction::Forward) => filter_expr
164 - .head()
165 - .next(journal_file, u64::MIN)?
166 - .map(Location::ResolvedEntry),
167 - (Location::Head, Direction::Backward) => None,
168 - (Location::Tail, Direction::Forward) => None,
169 - (Location::Tail, Direction::Backward) => filter_expr
170 - .tail(journal_file)?
171 - .previous(journal_file, u64::MAX)?
172 - .map(Location::ResolvedEntry),
173 - (Location::Realtime(realtime), direction) => {
174 - let entry_list = journal_file
175 - .entry_list()
176 - .ok_or(JournalError::InvalidOffsetArrayOffset)?;
177 -
178 - let predicate = |entry_offset| {
179 - let entry_object = journal_file.entry_ref(entry_offset)?;
180 - Ok(entry_object.header.realtime < realtime)
181 - };
182 -
183 - let cursor = entry_list
184 - .directed_partition_point(journal_file, predicate, Direction::Forward)?
185 - .map(Ok)
186 - .unwrap_or_else(|| entry_list.cursor_tail(journal_file))?;
187 -
188 - let entry_offset = cursor.value(journal_file)?;
189 -
190 - match direction {
191 - Direction::Forward => filter_expr
192 - .head()
193 - .next(journal_file, entry_offset)?
194 - .map(Location::ResolvedEntry),
195 - Direction::Backward => filter_expr
196 - .tail(journal_file)?
197 - .previous(journal_file, entry_offset)?
198 - .map(Location::ResolvedEntry),
199 - }
200 - }
201 - (Location::ResolvedEntry(location_offset), Direction::Forward) => filter_expr
202 - .next(journal_file, location_offset + 1)?
203 - .map(Location::ResolvedEntry),
204 - (Location::ResolvedEntry(location_offset), Direction::Backward) => filter_expr
205 - .previous(journal_file, location_offset - 1)?
206 - .map(Location::ResolvedEntry),
207 - _ => {
208 - unimplemented!();
209 - }
210 - };
211 -
212 - Ok(resolved_location)
213 - }
214 -}
src/crates/jf/journal_reader/src/journal_filter.rs deleted
-416
@@ -1,416 +0,0 @@
1 -use error::{JournalError, Result};
2 -use journal_file::{
3 - offset_array::{Direction, InlinedCursor},
4 - JournalFile,
5 -};
6 -use window_manager::MemoryMap;
7 -
8 -#[derive(Clone, Debug)]
9 -pub enum FilterExpr {
10 - Match(u64, Option<InlinedCursor>),
11 - Conjunction(Vec<FilterExpr>),
12 - Disjunction(Vec<FilterExpr>),
13 -}
14 -
15 -impl FilterExpr {
16 - pub fn lookup<M: MemoryMap>(
17 - &self,
18 - journal_file: &JournalFile<M>,
19 - needle_offset: u64,
20 - direction: Direction,
21 - ) -> Result<Option<u64>> {
22 - let predicate =
23 - move |entry_offset: u64| -> Result<bool> { Ok(entry_offset < needle_offset) };
24 -
25 - match self {
26 - FilterExpr::Match(data_offset, _) => {
27 - let entry_offset = journal_file.data_object_directed_partition_point(
28 - *data_offset,
29 - predicate,
30 - direction,
31 - )?;
32 - Ok(entry_offset)
33 - }
34 - FilterExpr::Conjunction(filter_exprs) => {
35 - let mut current_offset = needle_offset;
36 -
37 - loop {
38 - let previous_offset = current_offset;
39 -
40 - for filter_expr in filter_exprs {
41 - if direction == Direction::Backward {
42 - current_offset = current_offset.saturating_add(1);
43 - }
44 -
45 - match filter_expr.lookup(journal_file, current_offset, direction)? {
46 - Some(new_offset) => current_offset = new_offset,
47 - None => return Ok(None),
48 - }
49 - }
50 -
51 - if current_offset == previous_offset {
52 - return Ok(Some(current_offset));
53 - }
54 - }
55 - }
56 - FilterExpr::Disjunction(filter_exprs) => {
57 - let cmp = match direction {
58 - Direction::Forward => std::cmp::min,
59 - Direction::Backward => std::cmp::max,
60 - };
61 -
62 - filter_exprs.iter().try_fold(None, |acc, expr| {
63 - let result = expr.lookup(journal_file, needle_offset, direction)?;
64 -
65 - Ok(match (acc, result) {
66 - (None, Some(offset)) => Some(offset),
67 - (Some(best), Some(offset)) => Some(cmp(best, offset)),
68 - (acc, None) => acc,
69 - })
70 - })
71 - }
72 - }
73 - }
74 -
75 - pub fn head(&mut self) -> &mut Self {
76 - match self {
77 - FilterExpr::Match(_, None) => (),
78 - FilterExpr::Match(_, Some(ic)) => {
79 - *ic = ic.head();
80 - }
81 - FilterExpr::Conjunction(filter_exprs) => {
82 - for filter_expr in filter_exprs.iter_mut() {
83 - filter_expr.head();
84 - }
85 - }
86 - FilterExpr::Disjunction(filter_exprs) => {
87 - for filter_expr in filter_exprs.iter_mut() {
88 - filter_expr.head();
89 - }
90 - }
91 - }
92 -
93 - self
94 - }
95 -
96 - pub fn tail<M: MemoryMap>(&mut self, journal_file: &JournalFile<M>) -> Result<&mut Self> {
97 - match self {
98 - FilterExpr::Match(_, None) => (),
99 - FilterExpr::Match(_, Some(ic)) => {
100 - *ic = ic.tail(journal_file)?;
101 - }
102 - FilterExpr::Conjunction(filter_exprs) => {
103 - for filter_expr in filter_exprs.iter_mut() {
104 - filter_expr.tail(journal_file)?;
105 - }
106 - }
107 - FilterExpr::Disjunction(filter_exprs) => {
108 - for filter_expr in filter_exprs.iter_mut() {
109 - filter_expr.tail(journal_file)?;
110 - }
111 - }
112 - }
113 -
114 - Ok(self)
115 - }
116 -
117 - // Returns the offset of the next matching entry, if any, with an offset
118 - // greater or equal to the needle offset.
119 - pub fn next<M: MemoryMap>(
120 - &mut self,
121 - journal_file: &JournalFile<M>,
122 - needle_offset: u64,
123 - ) -> Result<Option<u64>> {
124 - match self {
125 - FilterExpr::Match(_, None) => Ok(None),
126 - FilterExpr::Match(_, Some(ic)) => ic.next_until(journal_file, needle_offset),
127 - FilterExpr::Conjunction(filter_exprs) => {
128 - let mut needle_offset = needle_offset;
129 -
130 - loop {
131 - let previous_offset = needle_offset;
132 -
133 - for fe in filter_exprs.iter_mut() {
134 - if let Some(new_offset) = fe.next(journal_file, needle_offset)? {
135 - needle_offset = new_offset;
136 - } else {
137 - return Ok(None);
138 - }
139 - }
140 -
141 - if needle_offset == previous_offset {
142 - return Ok(Some(needle_offset));
143 - }
144 - }
145 - }
146 - FilterExpr::Disjunction(filter_exprs) => {
147 - let mut best_offset: Option<u64> = None;
148 -
149 - for fe in filter_exprs.iter_mut() {
150 - if let Some(fe_offset) = fe.next(journal_file, needle_offset)? {
151 - best_offset = match best_offset {
152 - Some(offset) => Some(fe_offset.min(offset)),
153 - None => Some(fe_offset),
154 - };
155 - }
156 - }
157 -
158 - Ok(best_offset)
159 - }
160 - }
161 - }
162 -
163 - // Returns the offset of the previous matching entry, if any, with an offset
164 - // less or equal to the needle offset.
165 - pub fn previous<M: MemoryMap>(
166 - &mut self,
167 - journal_file: &JournalFile<M>,
168 - needle_offset: u64,
169 - ) -> Result<Option<u64>> {
170 - match self {
171 - FilterExpr::Match(_, None) => Ok(None),
172 - FilterExpr::Match(_, Some(ic)) => ic.previous_until(journal_file, needle_offset),
173 - FilterExpr::Conjunction(filter_exprs) => {
174 - let mut needle_offset = needle_offset;
175 -
176 - loop {
177 - let previous_offset = needle_offset;
178 -
179 - for fe in filter_exprs.iter_mut().rev() {
180 - if let Some(new_offset) = fe.previous(journal_file, needle_offset)? {
181 - needle_offset = new_offset;
182 - } else {
183 - return Ok(None);
184 - }
185 - }
186 -
187 - if needle_offset == previous_offset {
188 - return Ok(Some(needle_offset));
189 - }
190 - }
191 - }
192 - FilterExpr::Disjunction(filter_exprs) => {
193 - let mut best_offset: Option<u64> = None;
194 -
195 - for fe in filter_exprs.iter_mut() {
196 - if let Some(fe_offset) = fe.previous(journal_file, needle_offset)? {
197 - best_offset = match best_offset {
198 - Some(offset) => Some(fe_offset.max(offset)),
199 - None => Some(fe_offset),
200 - };
201 - }
202 - }
203 -
204 - Ok(best_offset)
205 - }
206 - }
207 - }
208 -
209 - pub fn dump<M: MemoryMap>(&self, journal_file: &JournalFile<M>) -> Result<String> {
210 - let mut output = String::new();
211 - self.dump_internal(journal_file, 0, &mut output)?;
212 - Ok(output)
213 - }
214 -
215 - /// Helper function for format_data_objects that handles nested expressions and indentation
216 - fn dump_internal<M: MemoryMap>(
217 - &self,
218 - journal_file: &JournalFile<M>,
219 - indent_level: usize,
220 - output: &mut String,
221 - ) -> Result<()> {
222 - let indent = " ".repeat(indent_level);
223 -
224 - match self {
225 - FilterExpr::Match(data_offset, inlined_cursor) => {
226 - // Load the data object
227 - let data_object = journal_file.data_ref(*data_offset)?;
228 -
229 - // Get the payload as a string if possible
230 - let payload_bytes = data_object.payload_bytes();
231 - let payload_str = String::from_utf8_lossy(payload_bytes);
232 -
233 - // Format the data offset and payload
234 - output.push_str(&format!(
235 - "{}Match[offset=0x{:x}]: {}\n",
236 - indent, data_offset, payload_str
237 - ));
238 -
239 - // Format cursor information if available
240 - if let Some(ic) = inlined_cursor {
241 - output.push_str(&format!("{} Cursor: {:?}\n", indent, ic));
242 - }
243 - }
244 - FilterExpr::Conjunction(filter_exprs) => {
245 - output.push_str(&format!("{}Conjunction (AND) {{\n", indent));
246 - for expr in filter_exprs {
247 - expr.dump_internal(journal_file, indent_level + 1, output)?;
248 - }
249 - output.push_str(&format!("{}}}\n", indent));
250 - }
251 - FilterExpr::Disjunction(filter_exprs) => {
252 - output.push_str(&format!("{}Disjunction (OR) {{\n", indent));
253 - for expr in filter_exprs {
254 - expr.dump_internal(journal_file, indent_level + 1, output)?;
255 - }
256 - output.push_str(&format!("{}}}\n", indent));
257 - }
258 - }
259 -
260 - Ok(())
261 - }
262 -}
263 -
264 -#[derive(Debug, Clone, Copy, PartialEq, Eq)]
265 -pub enum LogicalOp {
266 - Conjunction,
267 - Disjunction,
268 -}
269 -
270 -#[derive(Debug)]
271 -pub struct JournalFilter {
272 - filter_expr: Option<FilterExpr>,
273 - current_matches: Vec<Vec<u8>>,
274 - current_op: LogicalOp,
275 -}
276 -
277 -impl Default for JournalFilter {
278 - fn default() -> Self {
279 - Self {
280 - filter_expr: None,
281 - current_matches: Vec::new(),
282 - current_op: LogicalOp::Conjunction,
283 - }
284 - }
285 -}
286 -
287 -impl JournalFilter {
288 - fn extract_key(kv_pair: &[u8]) -> Option<&[u8]> {
289 - if let Some(equal_pos) = kv_pair.iter().position(|&b| b == b'=') {
290 - Some(&kv_pair[..equal_pos])
291 - } else {
292 - None
293 - }
294 - }
295 -
296 - fn convert_current_matches<M: MemoryMap>(
297 - &mut self,
298 - journal_file: &JournalFile<M>,
299 - ) -> Result<Option<FilterExpr>> {
300 - if self.current_matches.is_empty() {
301 - return Ok(None);
302 - }
303 -
304 - let mut elements = Vec::new();
305 - let mut i = 0;
306 -
307 - while i < self.current_matches.len() {
308 - let current_key = Self::extract_key(&self.current_matches[i]).unwrap_or(&[]);
309 - let start = i;
310 -
311 - // Find all matches with the same key
312 - while i < self.current_matches.len()
313 - && Self::extract_key(&self.current_matches[i]).unwrap_or(&[]) == current_key
314 - {
315 - i += 1;
316 - }
317 -
318 - // If we have multiple values for this key, create a disjunction
319 - if i - start > 1 {
320 - let mut matches = Vec::with_capacity(i - start);
321 - for idx in start..i {
322 - let data = self.current_matches[idx].as_slice();
323 - let hash = journal_file.hash(data);
324 - let offset = journal_file.find_data_offset_by_payload(data, hash)?;
325 -
326 - let ic = journal_file.data_ref(offset)?.inlined_cursor();
327 - matches.push(FilterExpr::Match(offset, ic));
328 - }
329 - elements.push(FilterExpr::Disjunction(matches));
330 - } else {
331 - let data = self.current_matches[start].as_slice();
332 - let hash = journal_file.hash(data);
333 - let offset = journal_file.find_data_offset_by_payload(data, hash)?;
334 -
335 - let ic = journal_file.data_ref(offset)?.inlined_cursor();
336 - elements.push(FilterExpr::Match(offset, ic));
337 - }
338 - }
339 -
340 - self.current_matches.clear();
341 -
342 - match elements.len() {
343 - 0 => panic!("Could not create filter elements from current matches"),
344 - 1 => Ok(Some(elements.remove(0))),
345 - _ => Ok(Some(FilterExpr::Conjunction(elements))),
346 - }
347 - }
348 -
349 - pub fn add_match(&mut self, kv_pair: &[u8]) {
350 - if kv_pair.contains(&b'=') {
351 - let new_item = kv_pair.to_vec();
352 - let new_key = Self::extract_key(&new_item).unwrap_or(&[]);
353 -
354 - // Find the insertion position using binary search
355 - let pos = self
356 - .current_matches
357 - .binary_search_by(|item| {
358 - let key = Self::extract_key(item).unwrap_or(&[]);
359 - key.cmp(new_key)
360 - })
361 - .unwrap_or_else(|e| e);
362 -
363 - // Insert at the found position
364 - self.current_matches.insert(pos, new_item);
365 - }
366 - }
367 -
368 - pub fn set_operation<M: MemoryMap>(
369 - &mut self,
370 - journal_file: &JournalFile<M>,
371 - op: LogicalOp,
372 - ) -> Result<()> {
373 - let new_expr = self.convert_current_matches(journal_file)?;
374 - if new_expr.is_none() {
375 - self.current_op = op;
376 - return Ok(());
377 - }
378 -
379 - if self.filter_expr.is_none() {
380 - self.filter_expr = new_expr;
381 - self.current_op = op;
382 - return Ok(());
383 - }
384 -
385 - let new_expr = new_expr.unwrap();
386 - let current_expr = self.filter_expr.take().unwrap();
387 -
388 - self.filter_expr = Some(match (current_expr, self.current_op) {
389 - (FilterExpr::Disjunction(mut exprs), LogicalOp::Disjunction) => {
390 - exprs.push(new_expr);
391 - FilterExpr::Disjunction(exprs)
392 - }
393 - (FilterExpr::Conjunction(mut exprs), LogicalOp::Conjunction) => {
394 - exprs.push(new_expr);
395 - FilterExpr::Conjunction(exprs)
396 - }
397 - (current_expr, LogicalOp::Disjunction) => {
398 - FilterExpr::Disjunction(vec![current_expr, new_expr])
399 - }
400 - (current_expr, LogicalOp::Conjunction) => {
401 - FilterExpr::Conjunction(vec![current_expr, new_expr])
402 - }
403 - });
404 -
405 - self.current_op = op;
406 - Ok(())
407 - }
408 -
409 - pub fn build<M: MemoryMap>(&mut self, journal_file: &JournalFile<M>) -> Result<FilterExpr> {
410 - self.set_operation(journal_file, self.current_op)?;
411 -
412 - self.current_matches.clear();
413 - self.current_op = LogicalOp::Conjunction;
414 - self.filter_expr.take().ok_or(JournalError::MalformedFilter)
415 - }
416 -}
src/crates/jf/journal_reader/src/lib.rs deleted
-197
@@ -1,197 +0,0 @@
1 -use error::Result;
2 -use journal_file::{
3 - offset_array, DataObject, EntryDataIterator, FieldDataIterator, FieldIterator, FieldObject,
4 - JournalFile, ValueGuard,
5 -};
6 -use window_manager::MemoryMap;
7 -
8 -pub mod journal_filter;
9 -use journal_filter::{JournalFilter, LogicalOp};
10 -pub mod journal_cursor;
11 -use journal_cursor::JournalCursor;
12 -pub use journal_cursor::Location;
13 -
14 -pub use offset_array::Direction;
15 -
16 -pub struct JournalReader<'a, M: MemoryMap> {
17 - cursor: JournalCursor,
18 -
19 - filter: Option<JournalFilter>,
20 - field_iterator: Option<FieldIterator<'a, M>>,
21 - field_data_iterator: Option<FieldDataIterator<'a, M>>,
22 - entry_data_iterator: Option<EntryDataIterator<'a, M>>,
23 -
24 - field_guard: Option<ValueGuard<'a, FieldObject<&'a [u8]>>>,
25 - data_guard: Option<ValueGuard<'a, DataObject<&'a [u8]>>>,
26 -}
27 -
28 -impl<M: MemoryMap> std::fmt::Debug for JournalReader<'_, M> {
29 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 - f.debug_struct("JournalReader")
31 - // .field("cursor", &self.cursor)
32 - .field("field_guard", &self.field_guard)
33 - .field("data_guard", &self.data_guard)
34 - .finish()
35 - }
36 -}
37 -
38 -impl<M: MemoryMap> Default for JournalReader<'_, M> {
39 - fn default() -> Self {
40 - Self {
41 - cursor: JournalCursor::new(),
42 - filter: None,
43 - field_iterator: None,
44 - field_data_iterator: None,
45 - entry_data_iterator: None,
46 - field_guard: None,
47 - data_guard: None,
48 - }
49 - }
50 -}
51 -
52 -impl<'a, M: MemoryMap> JournalReader<'a, M> {
53 - pub fn dump(&self, journal_file: &'a JournalFile<M>) -> Result<String> {
54 - if let Some(filter_expr) = self.cursor.filter_expr.as_ref() {
55 - filter_expr.dump(journal_file)
56 - } else {
57 - Ok(String::from("no filter expr"))
58 - }
59 - }
60 -
61 - pub fn set_location(&mut self, location: Location) {
62 - self.cursor.set_location(location)
63 - }
64 -
65 - pub fn step(&mut self, journal_file: &'a JournalFile<M>, direction: Direction) -> Result<bool> {
66 - self.drop_guards();
67 -
68 - if let Some(filter) = self.filter.as_mut() {
69 - let filter_expr = filter.build(journal_file)?;
70 - self.cursor.set_filter(filter_expr);
71 - self.filter = None;
72 - }
73 -
74 - self.cursor.step(journal_file, direction)
75 - }
76 -
77 - pub fn add_match(&mut self, data: &[u8]) {
78 - self.filter.get_or_insert_default().add_match(data);
79 - }
80 -
81 - pub fn add_conjunction(&mut self, journal_file: &'a JournalFile<M>) -> Result<()> {
82 - self.filter
83 - .get_or_insert_default()
84 - .set_operation(journal_file, LogicalOp::Conjunction)
85 - }
86 -
87 - pub fn add_disjunction(&mut self, journal_file: &'a JournalFile<M>) -> Result<()> {
88 - self.filter
89 - .get_or_insert_default()
90 - .set_operation(journal_file, LogicalOp::Disjunction)
91 - }
92 -
93 - pub fn flush_matches(&mut self) {
94 - self.cursor.clear_filter();
95 - self.filter = None;
96 - }
97 -
98 - pub fn get_realtime_usec(&self, journal_file: &'a JournalFile<M>) -> Result<u64> {
99 - let entry_offset = self.cursor.position()?;
100 - let entry_object = journal_file.entry_ref(entry_offset)?;
101 - Ok(entry_object.header.realtime)
102 - }
103 -
104 - pub fn get_seqnum(&self, journal_file: &'a JournalFile<M>) -> Result<(u64, [u8; 16])> {
105 - let entry_offset = self.cursor.position()?;
106 - let entry_object = journal_file.entry_ref(entry_offset)?;
107 - Ok((
108 - entry_object.header.seqnum,
109 - journal_file.journal_header_ref().seqnum_id,
110 - ))
111 - }
112 -
113 - pub fn get_entry_offset(&self) -> Result<u64> {
114 - self.cursor.position()
115 - }
116 -
117 - fn drop_guards(&mut self) {
118 - self.field_guard.take();
119 - self.data_guard.take();
120 - }
121 -
122 - pub fn fields_restart(&mut self) {
123 - self.drop_guards();
124 - self.field_iterator = None;
125 - }
126 -
127 - pub fn fields_enumerate(
128 - &mut self,
129 - journal_file: &'a JournalFile<M>,
130 - ) -> Result<Option<&ValueGuard<FieldObject<&'a [u8]>>>> {
131 - self.drop_guards();
132 -
133 - if self.field_iterator.is_none() {
134 - self.field_iterator = Some(journal_file.fields());
135 - }
136 -
137 - if let Some(iter) = &mut self.field_iterator {
138 - self.field_guard = iter.next().transpose()?;
139 - Ok(self.field_guard.as_ref())
140 - } else {
141 - Ok(None)
142 - }
143 - }
144 -
145 - pub fn field_data_query_unique(
146 - &mut self,
147 - journal_file: &'a JournalFile<M>,
148 - field_name: &'a [u8],
149 - ) -> Result<()> {
150 - self.drop_guards();
151 -
152 - self.field_data_iterator = Some(journal_file.field_data_objects(field_name)?);
153 - Ok(())
154 - }
155 -
156 - pub fn field_data_restart(&mut self) {
157 - self.drop_guards();
158 - }
159 -
160 - pub fn field_data_enumerate(
161 - &mut self,
162 - _: &'a JournalFile<M>,
163 - ) -> Result<Option<&ValueGuard<DataObject<&'a [u8]>>>> {
164 - self.drop_guards();
165 -
166 - if let Some(iter) = &mut self.field_data_iterator {
167 - self.data_guard = iter.next().transpose()?;
168 - Ok(self.data_guard.as_ref())
169 - } else {
170 - Ok(None)
171 - }
172 - }
173 -
174 - pub fn entry_data_restart(&mut self) {
175 - self.drop_guards();
176 - self.entry_data_iterator = None;
177 - }
178 -
179 - pub fn entry_data_enumerate(
180 - &mut self,
181 - journal_file: &'a JournalFile<M>,
182 - ) -> Result<Option<&ValueGuard<DataObject<&'a [u8]>>>> {
183 - self.drop_guards();
184 -
185 - if self.entry_data_iterator.is_none() {
186 - let entry_offset = self.cursor.position()?;
187 - self.entry_data_iterator = Some(journal_file.entry_data_objects(entry_offset)?);
188 - }
189 -
190 - if let Some(iter) = &mut self.entry_data_iterator {
191 - self.data_guard = iter.next().transpose()?;
192 - Ok(self.data_guard.as_ref())
193 - } else {
194 - Ok(None)
195 - }
196 - }
197 -}
src/crates/jf/otel-plugin/Cargo.toml deleted
-31
@@ -1,31 +0,0 @@
1 -[package]
2 -name = "otel-plugin"
3 -version.workspace = true
4 -edition.workspace = true
5 -rust-version.workspace = true
6 -
7 -[dependencies]
8 -tokio = { workspace = true }
9 -prost = { workspace = true }
10 -tonic = { workspace = true, features = ["gzip", "tls-ring"] }
11 -opentelemetry-proto = { workspace = true, features = ["metrics", "logs"] }
12 -
13 -flatten_otel = { path = "../flatten_otel" }
14 -journal_file = { path = "../journal_file" }
15 -journal_log = { path = "../journal_log" }
16 -memmap2 = { workspace = true }
17 -serde_json = { workspace = true, features = ["preserve_order"] }
18 -uuid = { version = "1.0", features = ["v4", "rng"] }
19 -flatten-serde-json = { workspace = true }
20 -base64 = "0.21"
21 -regex = { workspace = true }
22 -serde_yaml = "0.9.34"
23 -serde_regex = "1.1.0"
24 -serde = { version = "1.0.219", features = ["derive"] }
25 -atty = { workspace = true }
26 -clap = { workspace = true }
27 -anyhow = { workspace = true }
28 -humantime-serde = { workspace = true }
29 -humantime = { workspace = true }
30 -bytesize = { workspace = true }
31 -bytesize-serde = { workspace = true }
src/crates/jf/otel-plugin/README.md deleted
-82
@@ -1,82 +0,0 @@
1 -# OpenTelemetry Metrics (otel.plugin)
2 -
3 -`otel.plugin` is a [Netdata](https://github.com/netdata/netdata) external plugin,
4 -enabling users to ingest, store and visualize OpenTelemetry metrics in charts.
5 -
6 -## Configuration
7 -
8 -Edit the [otel.yml](https://github.com/netdata/netdata/blob/master/src/crates/jf/otel-plugin/configs/otel.yml)
9 -configuration file using `edit-config` from the Netdata
10 -[config directory](/docs/netdata-agent/configuration/README.md#locate-your-config-directory),
11 -which is typically located under `/etc/netdata`.
12 -
13 -```bash
14 -cd /etc/netdata # Replace this path with your Netdata config directory
15 -sudo ./edit-config otel.yml
16 -```
17 -
18 -### gRPC Endpoint
19 -
20 -By default `otel.plugin` listens for incoming OTLP-formatted metrics on
21 -`localhost:4317` via gRPC. Users can set up a secure TLS connection by
22 -updating the TLS configuration in the `endpoint` section:
23 -
24 -```yaml
25 -endpoint:
26 - # gRPC endpoint to listen on for OpenTelemetry data
27 - path: "127.0.0.1:4317"
28 -
29 - # Path to TLS certificate file (enables TLS when provided)
30 - tls_cert_path: null
31 -
32 - # Path to TLS private key file (required when TLS certificate is provided)
33 - tls_key_path: null
34 -
35 - # Path to TLS CA certificate file for client authentication (optional)
36 - tls_ca_cert_path: null
37 -```
38 -
39 -### Metrics
40 -
41 -The `metrics` section allows users to specify the directory containing
42 -configuration files for mapping OpenTelemetry metrics to Netdata chart
43 -instances, and the number of metric samples the `otel.plugin` will use for
44 -detecting their collection interval:
45 -
46 -```yaml
47 -metrics:
48 - # Directory with configuration files for mapping OTEL metrics to Netdata charts
49 - # (relative paths are resolved based on Netdata's user configuration directory)
50 - chart_configs_dir: otel.d/v1/metrics/
51 -
52 - # Number of samples to buffer for collection interval detection
53 - buffer_samples: 10
54 -```
55 -
56 -## Mapping OpenTelemetry metrics to Netdata chart instances
57 -
58 -Without an explicit mapping, the `otel.plugin` defaults to creating distinct
59 -chart instances based on the attributes of each data point in a metric. Users
60 -can place their YAML chart configuration files under `otel.d/v1/metrics` to
61 -override, or fine-tune, the default mapping.
62 -
63 -For each instrumentation scope and metric name, the configuration defines
64 -the attributes that the `otel.plugin` will use when creating new chart
65 -instances and dimension names.
66 -
67 -For example, the following bit from the
68 -[otel.d/v1/metrics/hostmetrics.yml](https://github.com/netdata/netdata/blob/master/src/crates/jf/otel-plugin/configs/otel.d/v1/metrics/hostmetrics-receiver.yml)
69 - configuration file for the [hostmetrics](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/hostmetricsreceiver/internal/scraper/networkscraper/documentation.md) receiver:
70 -```yaml
71 -select:
72 - instrumentation_scope_name: hostmetricsreceiver.*networkscraper
73 - metric_name: system.network.connections
74 -extract:
75 - chart_instance_pattern: metric.attributes.protocol
76 - dimension_name: metric.attributes.state
77 -```
78 -will apply to metrics whose instrumentation scope and metric names match the
79 -corresponding regular expressions specified in the values of the
80 -`instrumentation_scope_name` and `metric_name` keys. Similarly, the values of
81 -the `protocol` and `state` attributes of each data point in the matched metric
82 -will be used to create a new chart instance with the proper dimension names.
src/crates/jf/otel-plugin/configs/otel.d/v1/metrics/hostmetrics-receiver.yml deleted
-172
@@ -1,172 +0,0 @@
1 -configs:
2 - - select:
3 - instrumentation_scope_name: .*hostmetricsreceiver.*networkscraper$
4 - metric_name: system\.network\.connections
5 - extract:
6 - chart_instance_pattern: metric.attributes.protocol
7 - dimension_name: metric.attributes.state
8 - - select:
9 - instrumentation_scope_name: .*hostmetricsreceiver.*networkscraper$
10 - metric_name: system\.network\.dropped
11 - extract:
12 - chart_instance_pattern: metric.attributes.device
13 - dimension_name: metric.attributes.direction
14 - - select:
15 - instrumentation_scope_name: .*hostmetricsreceiver.*networkscraper$
16 - metric_name: system\.network\.errors
17 - extract:
18 - chart_instance_pattern: metric.attributes.device
19 - dimension_name: metric.attributes.direction
20 - - select:
21 - instrumentation_scope_name: .*hostmetricsreceiver.*networkscraper$
22 - metric_name: system\.network\.io
23 - extract:
24 - chart_instance_pattern: metric.attributes.device
25 - dimension_name: metric.attributes.direction
26 - - select:
27 - instrumentation_scope_name: .*hostmetricsreceiver.*networkscraper$
28 - metric_name: system\.network\.packets
29 - extract:
30 - chart_instance_pattern: metric.attributes.device
31 - dimension_name: metric.attributes.direction
32 - - select:
33 - instrumentation_scope_name: .*hostmetricsreceiver.*cpuscraper$
34 - metric_name: system\.cpu\.time
35 - extract:
36 - chart_instance_pattern: metric.attributes.cpu
37 - dimension_name: metric.attributes.state
38 - - select:
39 - instrumentation_scope_name: .*hostmetricsreceiver.*cpuscraper$
40 - metric_name: system\.cpu\.frequency
41 - extract:
42 - chart_instance_pattern: metric.attributes.cpu
43 - - select:
44 - instrumentation_scope_name: .*hostmetricsreceiver.*cpuscraper$
45 - metric_name: system\.cpu\.utilization
46 - extract:
47 - chart_instance_pattern: metric.attributes.cpu
48 - dimension_name: metric.attributes.state
49 - - select:
50 - instrumentation_scope_name: .*hostmetricsreceiver.*diskscraper$
51 - metric_name: system\.disk\.io$
52 - extract:
53 - chart_instance_pattern: metric.attributes.device
54 - dimension_name: metric.attributes.direction
55 - - select:
56 - instrumentation_scope_name: .*hostmetricsreceiver.*diskscraper$
57 - metric_name: system\.disk\.io_time
58 - extract:
59 - chart_instance_pattern: metric.attributes.device
60 - - select:
61 - instrumentation_scope_name: .*hostmetricsreceiver.*diskscraper$
62 - metric_name: system\.disk\.merged
63 - extract:
64 - chart_instance_pattern: metric.attributes.device
65 - dimension_name: metric.attributes.direction
66 - - select:
67 - instrumentation_scope_name: .*hostmetricsreceiver.*diskscraper$
68 - metric_name: system\.disk\.operation_time
69 - extract:
70 - chart_instance_pattern: metric.attributes.device
71 - dimension_name: metric.attributes.direction
72 - - select:
73 - instrumentation_scope_name: .*hostmetricsreceiver.*diskscraper$
74 - metric_name: system\.disk\.operations
75 - extract:
76 - chart_instance_pattern: metric.attributes.device
77 - dimension_name: metric.attributes.direction
78 - - select:
79 - instrumentation_scope_name: .*hostmetricsreceiver.*diskscraper$
80 - metric_name: system\.disk\.pending_operations
81 - extract:
82 - chart_instance_pattern: metric.attributes.device
83 - - select:
84 - instrumentation_scope_name: .*hostmetricsreceiver.*diskscraper$
85 - metric_name: system\.disk\.weighted_io
86 - extract:
87 - chart_instance_pattern: metric.attributes.device
88 - - select:
89 - instrumentation_scope_name: .*hostmetricsreceiver.*filesystemscraper$
90 - metric_name: system\.filesystem\.inodes\.usage
91 - extract:
92 - chart_instance_pattern: metric.attributes.mountpoint
93 - dimension_name: metric.attributes.state
94 - - select:
95 - instrumentation_scope_name: .*hostmetricsreceiver.*filesystemscraper$
96 - metric_name: system\.filesystem\.usage
97 - extract:
98 - chart_instance_pattern: metric.attributes.mountpoint
99 - dimension_name: metric.attributes.state
100 - - select:
101 - instrumentation_scope_name: .*hostmetricsreceiver.*filesystemscraper$
102 - metric_name: system\.filesystem\.utilization
103 - extract:
104 - chart_instance_pattern: metric.attributes.mountpoint
105 - - select:
106 - instrumentation_scope_name: .*hostmetricsreceiver.*memoryscraper$
107 - metric_name: system\.memory\.utilization
108 - extract:
109 - dimension_name: metric.attributes.state
110 - - select:
111 - instrumentation_scope_name: .*hostmetricsreceiver.*pagingscraper$
112 - metric_name: system\.paging\.faults
113 - extract:
114 - dimension_name: metric.attributes.type
115 - - select:
116 - instrumentation_scope_name: .*hostmetricsreceiver.*pagingscraper$
117 - metric_name: system\.paging\.operations
118 - extract:
119 - chart_instance_pattern: metric.attributes.type
120 - dimension_name: metric.attributes.direction
121 - - select:
122 - instrumentation_scope_name: .*hostmetricsreceiver.*pagingscraper$
123 - metric_name: system\.paging\.usage
124 - extract:
125 - chart_instance_pattern: metric.attributes.device
126 - dimension_name: metric.attributes.state
127 - - select:
128 - instrumentation_scope_name: .*hostmetricsreceiver.*pagingscraper$
129 - metric_name: system\.paging\.utilization
130 - extract:
131 - chart_instance_pattern: metric.attributes.device
132 - dimension_name: metric.attributes.state
133 - - select:
134 - instrumentation_scope_name: .*hostmetricsreceiver.*processesscraper$
135 - metric_name: system\.processes\.count
136 - extract:
137 - dimension_name: metric.attributes.status
138 - - select:
139 - instrumentation_scope_name: .*hostmetricsreceiver.*processscraper$
140 - metric_name: process\.cpu\.time
141 - extract:
142 - dimension_name: metric.attributes.state
143 - - select:
144 - instrumentation_scope_name: .*hostmetricsreceiver.*processscraper$
145 - metric_name: process\.disk\.io
146 - extract:
147 - dimension_name: metric.attributes.direction
148 - - select:
149 - instrumentation_scope_name: .*hostmetricsreceiver.*processscraper$
150 - metric_name: process\.context_switches
151 - extract:
152 - dimension_name: metric.attributes.type
153 - - select:
154 - instrumentation_scope_name: .*hostmetricsreceiver.*processscraper$
155 - metric_name: process\.cpu\.utilization
156 - extract:
157 - dimension_name: metric.attributes.state
158 - - select:
159 - instrumentation_scope_name: .*hostmetricsreceiver.*processscraper$
160 - metric_name: process\.disk\.operations
161 - extract:
162 - dimension_name: metric.attributes.direction
163 - - select:
164 - instrumentation_scope_name: .*hostmetricsreceiver.*processscraper$
165 - metric_name: process\.paging\.faults
166 - extract:
167 - dimension_name: metric.attributes.type
168 - - select:
169 - instrumentation_scope_name: .*hostmetricsreceiver.*processscraper$
170 - metric_name: process\.paging\.faults
171 - extract:
172 - dimension_name: metric.attributes.type
src/crates/jf/otel-plugin/configs/otel.yml deleted
-49
@@ -1,49 +0,0 @@
1 -# OpenTelemetry Plugin Configuration
2 -# This file configures the OpenTelemetry metrics and logs ingestion for Netdata
3 -
4 -endpoint:
5 - # gRPC endpoint to listen on for OpenTelemetry data
6 - path: "127.0.0.1:4317"
7 -
8 - # Path to TLS certificate file (enables TLS when provided)
9 - tls_cert_path: null
10 -
11 - # Path to TLS private key file (required when TLS certificate is provided)
12 - tls_key_path: null
13 -
14 - # Path to TLS CA certificate file for client authentication (optional)
15 - tls_ca_cert_path: null
16 -
17 -metrics:
18 - # Print flattened metrics to stdout for debugging, instead of ingesting them
19 - print_flattened: false
20 -
21 - # Number of samples to buffer for collection interval detection
22 - buffer_samples: 10
23 -
24 - # Maximum number of new charts to create per collection interval
25 - throttle_charts: 100
26 -
27 - # Directory with configuration files for mapping OTEL metrics to Netdata charts
28 - # (relative paths are resolved based on Netdata's user configuration directory)
29 - chart_configs_dir: otel.d/v1/metrics
30 -
31 -logs:
32 - # Directory to store journal files for logs.
33 - # (relative paths are resolved based on Netdata's log directory)
34 - journal_dir: otel/v1
35 -
36 - # Maximum file size for individual journal files (e.g., "100MB", "1.5GB").
37 - size_of_journal_file: "100MB"
38 -
39 - # Maximum number of journal files to keep.
40 - number_of_journal_files: 10
41 -
42 - # Maximum total size for all journal files combined (e.g., "1GB", "500MB")
43 - size_of_journal_files: "1GB"
44 -
45 - # Maximum age for journal entries (e.g., "7 days", "1 week", "168h").
46 - duration_of_journal_files: "7 days"
47 -
48 - # Maximum time span that entries in a single journal file can cover (e.g., "2 hours", "1h", "30m").
49 - duration_of_journal_file: "2 hours"
src/crates/jf/otel-plugin/src/chart_config.rs deleted
-171
@@ -1,171 +0,0 @@
1 -use anyhow::{Context, Result};
2 -use regex::Regex;
3 -use serde::{Deserialize, Serialize};
4 -use serde_json::{Map as JsonMap, Value as JsonValue};
5 -use std::fs;
6 -use std::path::Path;
7 -
8 -#[derive(Debug, Clone, Serialize, Deserialize)]
9 -pub struct SelectCriteria {
10 - #[serde(with = "serde_regex", skip_serializing_if = "Option::is_none", default)]
11 - pub instrumentation_scope_name: Option<Regex>,
12 -
13 - #[serde(with = "serde_regex", skip_serializing_if = "Option::is_none", default)]
14 - pub instrumentation_scope_version: Option<Regex>,
15 -
16 - #[serde(with = "serde_regex")]
17 - pub metric_name: Regex,
18 -}
19 -
20 -#[derive(Debug, Clone, Serialize, Deserialize)]
21 -pub struct ExtractPattern {
22 - #[serde(skip_serializing_if = "Option::is_none")]
23 - pub chart_instance_pattern: Option<String>,
24 -
25 - #[serde(skip_serializing_if = "Option::is_none")]
26 - pub dimension_name: Option<String>,
27 -}
28 -
29 -#[derive(Debug, Clone, Serialize, Deserialize)]
30 -pub struct ChartConfig {
31 - pub select: SelectCriteria,
32 - pub extract: ExtractPattern,
33 -}
34 -
35 -impl ChartConfig {
36 - pub fn matches(&self, json_map: &JsonMap<String, JsonValue>) -> bool {
37 - if let Some(scope_regex) = &self.select.instrumentation_scope_name {
38 - if let Some(JsonValue::String(scope_name)) = json_map.get("scope.name") {
39 - if !scope_regex.is_match(scope_name) {
40 - return false;
41 - }
42 - } else {
43 - return false;
44 - }
45 - }
46 -
47 - if let Some(version_regex) = &self.select.instrumentation_scope_version {
48 - if let Some(JsonValue::String(scope_version)) = json_map.get("scope.version") {
49 - if !version_regex.is_match(scope_version) {
50 - return false;
51 - }
52 - } else {
53 - return false;
54 - }
55 - }
56 -
57 - if let Some(JsonValue::String(metric_name)) = json_map.get("metric.name") {
58 - self.select.metric_name.is_match(metric_name)
59 - } else {
60 - false
61 - }
62 - }
63 -}
64 -
65 -#[derive(Debug, Clone, Default, Serialize, Deserialize)]
66 -pub struct ChartConfigs {
67 - configs: Vec<ChartConfig>,
68 -}
69 -
70 -#[derive(Debug, Default, Clone)]
71 -pub struct ChartConfigManager {
72 - stock: ChartConfigs,
73 - user: ChartConfigs,
74 -}
75 -
76 -impl ChartConfigManager {
77 - pub fn with_default_configs() -> Self {
78 - let mut manager = Self::default();
79 - manager.load_stock_config();
80 - manager
81 - }
82 -
83 - pub fn find_matching_config(
84 - &self,
85 - json_map: &JsonMap<String, JsonValue>,
86 - ) -> Option<&ChartConfig> {
87 - // Chaining-order is important. We want to priority user configurations
88 - // and fall back to stock configurations if they are missing.
89 - self.user
90 - .configs
91 - .iter()
92 - .chain(self.stock.configs.iter())
93 - .find(|config| config.matches(json_map))
94 - }
95 -
96 - fn load_stock_config(&mut self) {
97 - const DEFAULT_CONFIGS_YAML: &str =
98 - include_str!("../configs/otel.d/v1/metrics/hostmetrics-receiver.yml");
99 -
100 - match serde_yaml::from_str::<ChartConfigs>(DEFAULT_CONFIGS_YAML) {
101 - Ok(configs) => {
102 - self.stock = configs;
103 - }
104 - Err(e) => {
105 - eprintln!("Failed to parse default configs YAML: {}", e);
106 - }
107 - }
108 - }
109 -
110 - pub fn load_user_configs<P: AsRef<Path>>(&mut self, config_dir: P) -> Result<()> {
111 - // check dir
112 - let config_path = config_dir.as_ref();
113 - if !config_path.exists() {
114 - return Err(anyhow::anyhow!(
115 - "Configuration directory does not exist: {}",
116 - config_path.display()
117 - ));
118 - }
119 - if !config_path.is_dir() {
120 - return Err(anyhow::anyhow!(
121 - "Configuration path is not a directory: {}",
122 - config_path.display()
123 - ));
124 - }
125 -
126 - // collect the yaml files
127 - let mut config_files: Vec<_> = std::fs::read_dir(config_path)
128 - .with_context(|| {
129 - format!(
130 - "Failed to read chart config directory: {}",
131 - config_path.display()
132 - )
133 - })?
134 - .filter_map(|entry| {
135 - let entry = entry.ok()?;
136 - let path = entry.path();
137 - if path.is_file()
138 - && matches!(
139 - path.extension().and_then(|s| s.to_str()),
140 - Some("yml" | "yaml")
141 - )
142 - {
143 - Some(path)
144 - } else {
145 - None
146 - }
147 - })
148 - .collect();
149 - config_files.sort();
150 -
151 - // deserialize them
152 - self.user = ChartConfigs::default();
153 - for path in config_files {
154 - match fs::read_to_string(&path) {
155 - Ok(contents) => match serde_yaml::from_str::<ChartConfigs>(&contents) {
156 - Ok(chart_configs) => {
157 - self.user.configs.extend(chart_configs.configs);
158 - }
159 - Err(e) => {
160 - eprintln!("Failed to parse YAML file {}: {}", path.display(), e);
161 - }
162 - },
163 - Err(e) => {
164 - eprintln!("Failed to read file {}: {}", path.display(), e);
165 - }
166 - }
167 - }
168 -
169 - Ok(())
170 - }
171 -}
src/crates/jf/otel-plugin/src/flattened_point.rs deleted
-171
@@ -1,171 +0,0 @@
1 -use serde_json::{Map as JsonMap, Value as JsonValue};
2 -use std::hash::{Hash, Hasher};
3 -
4 -use crate::regex_cache::RegexCache;
5 -
6 -#[derive(Default, Debug)]
7 -pub struct FlattenedPoint {
8 - pub attributes: JsonMap<String, JsonValue>,
9 -
10 - pub nd_instance_name: String,
11 - pub nd_dimension_name: String,
12 -
13 - pub metric_name: String,
14 - pub metric_description: String,
15 - pub metric_unit: String,
16 - pub metric_type: String,
17 -
18 - pub metric_time_unix_nano: u64,
19 - pub metric_value: f64,
20 -
21 - pub metric_is_monotonic: Option<bool>,
22 -}
23 -
24 -use crate::chart_config::ChartConfig;
25 -
26 -impl FlattenedPoint {
27 - pub fn new(
28 - mut json_map: JsonMap<String, JsonValue>,
29 - chart_config: Option<&ChartConfig>,
30 - regex_cache: &RegexCache,
31 - ) -> Option<Self> {
32 - let Some(JsonValue::String(metric_name)) = json_map.remove("metric.name") else {
33 - debug_assert!(false, "metric.name missing from json map");
34 - return None;
35 - };
36 -
37 - let Some(JsonValue::String(metric_description)) = json_map.remove("metric.description")
38 - else {
39 - debug_assert!(false, "metric.description missing from json map");
40 - return None;
41 - };
42 -
43 - let Some(JsonValue::String(metric_unit)) = json_map.remove("metric.unit") else {
44 - debug_assert!(false, "metric.unit missing from json map");
45 - return None;
46 - };
47 -
48 - let Some(JsonValue::String(metric_type)) = json_map.remove("metric.type") else {
49 - debug_assert!(false, "metric.type missing from json map");
50 - return None;
51 - };
52 -
53 - // Ignore start_time_unix for the time being.
54 - json_map.remove("metric.start_time_unix_nano");
55 -
56 - let Some(metric_time_unix_nano) = json_map
57 - .remove("metric.time_unix_nano")
58 - .and_then(|v| v.as_u64())
59 - else {
60 - debug_assert!(false, "metric.time_unix_nano missing from json map");
61 - return None;
62 - };
63 -
64 - let Some(metric_value) = json_map.remove("metric.value").and_then(|v| v.as_f64()) else {
65 - debug_assert!(false, "metric.value missing from json map");
66 - return None;
67 - };
68 -
69 - let metric_is_monotonic = json_map
70 - .remove("metric.is_monotonic")
71 - .and_then(|v| v.as_bool());
72 -
73 - if let Some(config) = chart_config {
74 - if let Some(chart_instance_pattern) = &config.extract.chart_instance_pattern {
75 - if !json_map.contains_key("metric.attributes._nd_chart_instance") {
76 - json_map.insert(
77 - "metric.attributes._nd_chart_instance".to_string(),
78 - JsonValue::String(chart_instance_pattern.clone()),
79 - );
80 - }
81 - }
82 -
83 - if let Some(dimension_name) = &config.extract.dimension_name {
84 - if !json_map.contains_key("metric.attributes._nd_dimension") {
85 - json_map.insert(
86 - "metric.attributes._nd_dimension".to_string(),
87 - JsonValue::String(dimension_name.clone()),
88 - );
89 - }
90 - }
91 - }
92 -
93 - let nd_dimension_name = {
94 - let nd_dimension_key = json_map
95 - .remove("metric.attributes._nd_dimension")
96 - .and_then(|v| v.as_str().map(String::from));
97 -
98 - if let Some(key) = nd_dimension_key {
99 - match json_map.remove(&key) {
100 - Some(JsonValue::String(s)) => s.clone(),
101 - Some(JsonValue::Number(n)) => n.to_string(),
102 - Some(JsonValue::Bool(b)) => b.to_string(),
103 - Some(value) => {
104 - eprintln!("Only strings/number/bool values can be used for dimension name >>>{:#?}<<<", value);
105 - return None;
106 - }
107 - _ => {
108 - eprintln!(
109 - "Dimension key >>>{:?}<<< not found in flattened representation.",
110 - key
111 - );
112 - return None;
113 - }
114 - }
115 - } else {
116 - String::from("value")
117 - }
118 - };
119 -
120 - let nd_instance_name = {
121 - let nd_chart_instance = json_map
122 - .remove("metric.attributes._nd_chart_instance")
123 - .and_then(|v| v.as_str().map(String::from))
124 - .and_then(|s| regex_cache.get(&s).ok());
125 -
126 - let mut matched_values = vec![metric_name.clone()];
127 - if let Some(pattern) = nd_chart_instance {
128 - for (key, value) in &json_map {
129 - if pattern.is_match(key) {
130 - let value_str = match value {
131 - JsonValue::String(s) => s.clone(),
132 - JsonValue::Number(n) => n.to_string(),
133 - JsonValue::Bool(b) => b.to_string(),
134 - JsonValue::Null => "null".to_string(),
135 - _ => serde_json::to_string(value).unwrap_or_default(),
136 - };
137 - matched_values.push(value_str);
138 - }
139 - }
140 - }
141 -
142 - let name = matched_values.join(".");
143 -
144 - let hash = {
145 - use std::hash::DefaultHasher;
146 -
147 - let mut state = DefaultHasher::new();
148 - name.hash(&mut state);
149 - json_map.hash(&mut state);
150 - metric_unit.hash(&mut state);
151 - metric_type.hash(&mut state);
152 - state.finish()
153 - };
154 -
155 - format!("{name}.{hash:016x}")
156 - };
157 -
158 - Some(Self {
159 - attributes: json_map,
160 - nd_instance_name,
161 - nd_dimension_name,
162 - metric_name,
163 - metric_description: metric_description.replace('\'', "\""),
164 - metric_unit,
165 - metric_type,
166 - metric_time_unix_nano,
167 - metric_value,
168 - metric_is_monotonic,
169 - })
170 - }
171 -}
src/crates/jf/otel-plugin/src/logs_service.rs deleted
-99
@@ -1,99 +0,0 @@
1 -use anyhow::{Context, Result};
2 -use flatten_otel::json_from_export_logs_service_request;
3 -use journal_log::{JournalLog, JournalLogConfig, RetentionPolicy, RotationPolicy};
4 -use opentelemetry_proto::tonic::collector::logs::v1::{
5 - logs_service_server::LogsService, ExportLogsServiceRequest, ExportLogsServiceResponse,
6 -};
7 -use serde_json::Value;
8 -use std::sync::{Arc, Mutex};
9 -use tonic::{Request, Response, Status};
10 -
11 -use crate::plugin_config::PluginConfig;
12 -
13 -pub struct NetdataLogsService {
14 - journal_log: Arc<Mutex<JournalLog>>,
15 -}
16 -
17 -impl NetdataLogsService {
18 - pub fn new(plugin_config: PluginConfig) -> Result<Self> {
19 - let logs_config = plugin_config.logs;
20 -
21 - let rotation_policy = RotationPolicy::default()
22 - .with_size_of_journal_file(logs_config.size_of_journal_file.as_u64())
23 - .with_duration_of_journal_file(logs_config.duration_of_journal_file);
24 -
25 - let retention_policy = RetentionPolicy::default()
26 - .with_number_of_journal_files(logs_config.number_of_journal_files)
27 - .with_size_of_journal_files(logs_config.size_of_journal_files.as_u64())
28 - .with_duration_of_journal_files(logs_config.duration_of_journal_files);
29 -
30 - let journal_config = JournalLogConfig::new(&logs_config.journal_dir)
31 - .with_rotation_policy(rotation_policy)
32 - .with_retention_policy(retention_policy);
33 -
34 - let journal_log = Arc::new(Mutex::new(JournalLog::new(journal_config).with_context(
35 - || {
36 - format!(
37 - "Failed to create journal log for directory: {}",
38 - logs_config.journal_dir
39 - )
40 - },
41 - )?));
42 - Ok(NetdataLogsService { journal_log })
43 - }
44 -
45 - fn json_to_entry_data(&self, json_value: &Value) -> Vec<Vec<u8>> {
46 - let mut entry_data = Vec::new();
47 -
48 - if let Value::Object(obj) = json_value {
49 - for (key, value) in obj {
50 - let value_str = match value {
51 - Value::String(s) => s.clone(),
52 - Value::Number(n) => n.to_string(),
53 - Value::Bool(b) => b.to_string(),
54 - Value::Null => "null".to_string(),
55 - _ => serde_json::to_string(value).unwrap_or_default(),
56 - };
57 -
58 - let kv_pair = format!("{}={}", key, value_str);
59 - entry_data.push(kv_pair.into_bytes());
60 - }
61 - }
62 -
63 - entry_data
64 - }
65 -}
66 -
67 -#[tonic::async_trait]
68 -impl LogsService for NetdataLogsService {
69 - async fn export(
70 - &self,
71 - request: Request<ExportLogsServiceRequest>,
72 - ) -> Result<Response<ExportLogsServiceResponse>, Status> {
73 - let req = request.into_inner();
74 -
75 - let json_array = json_from_export_logs_service_request(&req);
76 -
77 - if let Value::Array(entries) = json_array {
78 - for entry in entries {
79 - let entry_data = self.json_to_entry_data(&entry);
80 - if !entry_data.is_empty() {
81 - let entry_refs: Vec<&[u8]> = entry_data.iter().map(|v| v.as_slice()).collect();
82 - if let Err(e) = self.journal_log.lock().unwrap().write_entry(&entry_refs) {
83 - eprintln!("Failed to write log entry: {}", e);
84 - return Err(Status::internal(format!(
85 - "Failed to write log entry: {}",
86 - e
87 - )));
88 - }
89 - }
90 - }
91 - }
92 -
93 - let reply = ExportLogsServiceResponse {
94 - partial_success: None,
95 - };
96 -
97 - Ok(Response::new(reply))
98 - }
99 -}
src/crates/jf/otel-plugin/src/main.rs deleted
-104
@@ -1,104 +0,0 @@
1 -use anyhow::{Context, Result};
2 -use opentelemetry_proto::tonic::collector::{
3 - logs::v1::logs_service_server::LogsServiceServer,
4 - metrics::v1::metrics_service_server::MetricsServiceServer,
5 -};
6 -use tonic::transport::{Identity, Server, ServerTlsConfig};
7 -
8 -mod chart_config;
9 -mod flattened_point;
10 -mod netdata_chart;
11 -mod netdata_env;
12 -mod regex_cache;
13 -mod samples_table;
14 -
15 -mod plugin_config;
16 -use crate::plugin_config::PluginConfig;
17 -
18 -mod logs_service;
19 -use crate::logs_service::NetdataLogsService;
20 -
21 -mod metrics_service;
22 -use crate::metrics_service::NetdataMetricsService;
23 -
24 -async fn send_keepalive_periodically() {
25 - let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(60));
26 -
27 - loop {
28 - interval.tick().await;
29 - println!("PLUGIN_KEEPALIVE");
30 - }
31 -}
32 -
33 -#[tokio::main]
34 -async fn main() -> Result<()> {
35 - let config = PluginConfig::new().context("Failed to initialize plugin configuration")?;
36 -
37 - let addr =
38 - config.endpoint.path.parse().with_context(|| {
39 - format!("Failed to parse endpoint address: {}", config.endpoint.path)
40 - })?;
41 - let metrics_service =
42 - NetdataMetricsService::new(config.clone()).context("Failed to create metrics service")?;
43 - let logs_service =
44 - NetdataLogsService::new(config.clone()).context("Failed to create logs service")?;
45 -
46 - println!("TRUST_DURATIONS 1");
47 -
48 - let mut server_builder = Server::builder();
49 -
50 - // Configure TLS if provided
51 - if let (Some(cert_path), Some(key_path)) = (
52 - &config.endpoint.tls_cert_path,
53 - &config.endpoint.tls_key_path,
54 - ) {
55 - let cert = std::fs::read(cert_path)
56 - .with_context(|| format!("Failed to read TLS certificate from: {}", cert_path))?;
57 - let key = std::fs::read(key_path)
58 - .with_context(|| format!("Failed to read TLS private key from: {}", key_path))?;
59 - let identity = Identity::from_pem(cert, key);
60 -
61 - let mut tls_config_builder = ServerTlsConfig::new().identity(identity);
62 -
63 - // If CA certificate is provided, enable client authentication
64 - if let Some(ref ca_cert_path) = config.endpoint.tls_ca_cert_path {
65 - let ca_cert = std::fs::read(ca_cert_path)
66 - .with_context(|| format!("Failed to read CA certificate from: {}", ca_cert_path))?;
67 - tls_config_builder =
68 - tls_config_builder.client_ca_root(tonic::transport::Certificate::from_pem(ca_cert));
69 - }
70 -
71 - server_builder = server_builder
72 - .tls_config(tls_config_builder)
73 - .context("Failed to configure TLS")?;
74 - } else {
75 - eprintln!(
76 - "TLS disabled, using insecure connection on endpoint: {}",
77 - config.endpoint.path
78 - );
79 - }
80 -
81 - let server = server_builder
82 - .add_service(
83 - MetricsServiceServer::new(metrics_service)
84 - .accept_compressed(tonic::codec::CompressionEncoding::Gzip),
85 - )
86 - .add_service(
87 - LogsServiceServer::new(logs_service)
88 - .accept_compressed(tonic::codec::CompressionEncoding::Gzip),
89 - )
90 - .serve(addr);
91 -
92 - let keepalive = send_keepalive_periodically();
93 -
94 - tokio::select! {
95 - result = server => {
96 - result.with_context(|| format!("Failed to serve gRPC server on {}", addr))?;
97 - }
98 - _ = keepalive => {
99 - // This should never complete
100 - }
101 - }
102 -
103 - Ok(())
104 -}
src/crates/jf/otel-plugin/src/metrics_service.rs deleted
-140
@@ -1,140 +0,0 @@
1 -use anyhow::{Context, Result};
2 -use flatten_otel::flatten_metrics_request;
3 -use opentelemetry_proto::tonic::collector::metrics::v1::{
4 - metrics_service_server::MetricsService, ExportMetricsServiceRequest,
5 - ExportMetricsServiceResponse,
6 -};
7 -use std::collections::HashMap;
8 -use std::sync::Arc;
9 -use tokio::sync::RwLock;
10 -use tonic::{Request, Response, Status};
11 -
12 -use crate::chart_config::ChartConfigManager;
13 -use crate::flattened_point::FlattenedPoint;
14 -use crate::netdata_chart::NetdataChart;
15 -use crate::plugin_config::PluginConfig;
16 -use crate::regex_cache::RegexCache;
17 -
18 -#[derive(Default)]
19 -pub struct NetdataMetricsService {
20 - regex_cache: RegexCache,
21 - charts: Arc<RwLock<HashMap<String, NetdataChart>>>,
22 - config: Arc<PluginConfig>,
23 - chart_config_manager: ChartConfigManager,
24 - call_count: std::sync::atomic::AtomicU64,
25 -}
26 -
27 -impl NetdataMetricsService {
28 - pub fn new(config: PluginConfig) -> Result<Self> {
29 - let mut chart_config_manager = ChartConfigManager::with_default_configs();
30 -
31 - // Load user chart configs if directory is specified
32 - if let Some(chart_configs_dir) = &config.metrics.chart_configs_dir {
33 - chart_config_manager
34 - .load_user_configs(chart_configs_dir)
35 - .with_context(|| {
36 - format!(
37 - "Failed to load chart configs from directory: {}",
38 - chart_configs_dir
39 - )
40 - })?;
41 - }
42 -
43 - Ok(Self {
44 - regex_cache: RegexCache::default(),
45 - charts: Arc::default(),
46 - config: Arc::new(config),
47 - chart_config_manager,
48 - call_count: std::sync::atomic::AtomicU64::new(0),
49 - })
50 - }
51 -
52 - async fn cleanup_stale_charts(&self, max_age: std::time::Duration) {
53 - let now = std::time::SystemTime::now();
54 -
55 - let mut guard = self.charts.write().await;
56 - guard.retain(|_, chart| {
57 - let Some(chart_time) = chart.last_collection_time() else {
58 - return true;
59 - };
60 -
61 - now.duration_since(chart_time)
62 - .unwrap_or(std::time::Duration::ZERO)
63 - < max_age
64 - });
65 - }
66 -}
67 -
68 -#[tonic::async_trait]
69 -impl MetricsService for NetdataMetricsService {
70 - async fn export(
71 - &self,
72 - request: Request<ExportMetricsServiceRequest>,
73 - ) -> Result<Response<ExportMetricsServiceResponse>, Status> {
74 - let req = request.into_inner();
75 -
76 - let flattened_points = flatten_metrics_request(&req)
77 - .into_iter()
78 - .filter_map(|jm| {
79 - let cfg = self.chart_config_manager.find_matching_config(&jm);
80 - FlattenedPoint::new(jm, cfg, &self.regex_cache)
81 - })
82 - .collect::<Vec<_>>();
83 -
84 - if self.config.metrics.print_flattened {
85 - // Just print the flattened points
86 - for fp in &flattened_points {
87 - println!("{:#?}", fp);
88 - }
89 -
90 - return Ok(Response::new(ExportMetricsServiceResponse {
91 - partial_success: None,
92 - }));
93 - }
94 -
95 - // ingest
96 - {
97 - let mut newly_created_charts = 0;
98 -
99 - for fp in flattened_points.iter() {
100 - let mut guard = self.charts.write().await;
101 -
102 - if let Some(netdata_chart) = guard.get_mut(&fp.nd_instance_name) {
103 - netdata_chart.ingest(fp);
104 - } else if newly_created_charts < self.config.metrics.throttle_charts {
105 - let mut netdata_chart =
106 - NetdataChart::from_flattened_point(fp, self.config.metrics.buffer_samples);
107 - netdata_chart.ingest(fp);
108 - guard.insert(fp.nd_instance_name.clone(), netdata_chart);
109 -
110 - newly_created_charts += 1;
111 - }
112 - }
113 - }
114 -
115 - // process
116 - {
117 - let mut guard = self.charts.write().await;
118 -
119 - for netdata_chart in guard.values_mut() {
120 - netdata_chart.process();
121 - }
122 - }
123 -
124 - // cleanup stale charts
125 - {
126 - let prev_count = self
127 - .call_count
128 - .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
129 -
130 - if prev_count % 60 == 0 {
131 - let one_hour = std::time::Duration::from_secs(3600);
132 - self.cleanup_stale_charts(one_hour).await;
133 - }
134 - }
135 -
136 - Ok(Response::new(ExportMetricsServiceResponse {
137 - partial_success: None,
138 - }))
139 - }
140 -}
src/crates/jf/otel-plugin/src/netdata_chart.rs deleted
-296
@@ -1,296 +0,0 @@
1 -use serde_json::{Map as JsonMap, Value as JsonValue};
2 -
3 -use crate::flattened_point::FlattenedPoint;
4 -use crate::samples_table::{CollectionInterval, SamplesTable};
5 -
6 -#[derive(Debug, Default, Clone)]
7 -enum ChartState {
8 - #[default]
9 - Uninitialized,
10 - InGap,
11 - Initialized,
12 - Empty,
13 -}
14 -
15 -#[derive(Debug)]
16 -pub struct NetdataChart {
17 - chart_id: String,
18 - metric_name: String,
19 - metric_description: String,
20 - metric_unit: String,
21 - metric_type: String,
22 - is_monotonic: Option<bool>,
23 - attributes: JsonMap<String, JsonValue>,
24 -
25 - samples_table: SamplesTable,
26 - last_samples_table_interval: Option<CollectionInterval>,
27 - last_collection_interval: Option<CollectionInterval>,
28 - chart_state: ChartState,
29 - samples_threshold: usize,
30 -
31 - multiplier: i32,
32 - divisor: i32,
33 -}
34 -
35 -impl NetdataChart {
36 - pub fn from_flattened_point(fp: &FlattenedPoint, samples_threshold: usize) -> Self {
37 - Self {
38 - chart_id: fp.nd_instance_name.clone(),
39 - metric_name: fp.metric_name.clone(),
40 - metric_description: fp.metric_description.clone(),
41 - metric_unit: fp.metric_unit.clone(),
42 - metric_type: fp.metric_type.clone(),
43 - attributes: fp.attributes.clone(),
44 - is_monotonic: fp.metric_is_monotonic,
45 -
46 - samples_table: SamplesTable::default(),
47 - last_samples_table_interval: None,
48 - last_collection_interval: None,
49 - chart_state: ChartState::Uninitialized,
50 -
51 - samples_threshold,
52 -
53 - multiplier: 1,
54 - divisor: 1,
55 - }
56 - }
57 -
58 - fn is_histogram(&self) -> bool {
59 - self.metric_type == "histogram"
60 - }
61 -
62 - pub fn ingest(&mut self, fp: &FlattenedPoint) {
63 - let dimension_name = &fp.nd_dimension_name;
64 - let value = fp.metric_value;
65 - let unix_time = fp.metric_time_unix_nano;
66 -
67 - let new_dimension = self.samples_table.insert(dimension_name, unix_time, value);
68 -
69 - if new_dimension {
70 - self.chart_state = ChartState::Uninitialized;
71 - self.last_samples_table_interval = None;
72 - self.last_collection_interval = None;
73 - }
74 - }
75 -
76 - fn initialize(&mut self) -> bool {
77 - // Clean up stale samples if we have a previous interval
78 - if let Some(ci) = &self.last_samples_table_interval {
79 - self.samples_table.drop_stale_samples(ci);
80 - }
81 -
82 - // Check if we have enough samples to determine frequency
83 - if self.samples_table.total_samples() < self.samples_threshold {
84 - return false;
85 - }
86 -
87 - // Store the old interval before calculating the new one
88 - let old_lci = self.last_collection_interval;
89 -
90 - // Set up collection intervals
91 - self.last_samples_table_interval =
92 - self.samples_table
93 - .collection_interval()
94 - .map(|ci| CollectionInterval {
95 - end_time: ci.end_time - ci.update_every.get(),
96 - update_every: ci.update_every,
97 - });
98 -
99 - self.last_collection_interval = self
100 - .last_samples_table_interval
101 - .and_then(|ci| ci.aligned_interval());
102 -
103 - (self.multiplier, self.divisor) = self.samples_table.scaling_factors();
104 -
105 - // Check if we need to emit a chart definition
106 - if let Some(new_lci) = &self.last_collection_interval {
107 - if let Some(old_lci) = old_lci {
108 - if old_lci.update_every != new_lci.update_every {
109 - // Update every changed, emit the chart definition again
110 - self.emit_chart_definition();
111 - }
112 - } else {
113 - // No previous collection interval, we need to emit the
114 - // chart definition first
115 - self.emit_chart_definition();
116 - }
117 - }
118 -
119 - true
120 - }
121 -
122 - pub fn process(&mut self) {
123 - loop {
124 - match &self.chart_state {
125 - ChartState::Uninitialized | ChartState::InGap => {
126 - if !self.initialize() {
127 - return;
128 - }
129 -
130 - self.chart_state = ChartState::Initialized;
131 - }
132 - ChartState::Initialized => {
133 - self.chart_state = self.process_next_interval();
134 - }
135 - ChartState::Empty => {
136 - self.chart_state = ChartState::Initialized;
137 - return;
138 - }
139 - }
140 - }
141 - }
142 -
143 - fn emit_chart_definition(&self) {
144 - let ci = self.last_collection_interval.unwrap();
145 - let ue = ci.update_every;
146 -
147 - let type_id = &self.chart_id;
148 - let name = "";
149 - let title = &self.metric_description;
150 - let units = &self.metric_unit;
151 - let context = format!("otel.{}", &self.metric_name);
152 - let family = self.metric_name.replace('.', "/");
153 - let chart_type = if self.is_histogram() {
154 - "heatmap"
155 - } else {
156 - "line"
157 - };
158 - let priority = 1;
159 - let update_every = std::time::Duration::from_nanos(ue.get()).as_secs();
160 -
161 - println!(
162 - "CHART {type_id} '{name}' '{title}' '{units}' '{family}' '{context}' {chart_type} {priority} {update_every}"
163 - );
164 -
165 - for (key, value) in self.attributes.iter() {
166 - let value_str = match value {
167 - JsonValue::String(s) => s.clone(),
168 - JsonValue::Number(n) => n.to_string(),
169 - JsonValue::Bool(b) => b.to_string(),
170 - _ => continue,
171 - };
172 -
173 - println!("CLABEL '{key}' '{value_str}' 1");
174 - }
175 - println!("CLABEL_COMMIT");
176 -
177 - // Emit dimensions
178 - if self.is_histogram() {
179 - let mut dimension_names = self.samples_table.iter_dimensions().collect::<Vec<_>>();
180 -
181 - dimension_names.sort_by(|a, b| {
182 - let a_val = if *a == "+Inf" {
183 - f64::INFINITY
184 - } else {
185 - a.parse::<f64>().unwrap()
186 - };
187 - let b_val = if *b == "+Inf" {
188 - f64::INFINITY
189 - } else {
190 - b.parse::<f64>().unwrap()
191 - };
192 - a_val.partial_cmp(&b_val).unwrap()
193 - });
194 -
195 - for dimension_name in dimension_names {
196 - let algorithm = match self.is_monotonic {
197 - Some(true) => "incremental",
198 - _ => "absolute",
199 - };
200 - println!(
201 - "DIMENSION {} {} {} 1 {}",
202 - dimension_name, dimension_name, algorithm, self.divisor
203 - );
204 - }
205 - } else {
206 - for dimension_name in self.samples_table.iter_dimensions() {
207 - let algorithm = match self.is_monotonic {
208 - Some(true) => "incremental",
209 - _ => "absolute",
210 - };
211 - println!(
212 - "DIMENSION {} {} {} 1 {}",
213 - dimension_name, dimension_name, algorithm, self.divisor
214 - );
215 - }
216 - }
217 - }
218 -
219 - fn process_next_interval(&mut self) -> ChartState {
220 - let lsti = match &self.last_samples_table_interval {
221 - Some(interval) => interval,
222 - None => return ChartState::Empty,
223 - };
224 -
225 - let lci = match &self.last_collection_interval {
226 - Some(interval) => interval,
227 - None => return ChartState::Empty,
228 - };
229 -
230 - // Clean stale samples
231 - self.samples_table.drop_stale_samples(lsti);
232 - if self.samples_table.is_empty() {
233 - return ChartState::Empty;
234 - }
235 -
236 - // Check for gaps
237 - let have_gap = self
238 - .samples_table
239 - .iter_samples_buffers()
240 - .all(|sb| sb.first().is_none_or(|sp| lsti.is_in_gap(sp)));
241 -
242 - if have_gap {
243 - return ChartState::InGap;
244 - }
245 -
246 - // Collect samples to emit
247 - let mut samples_to_emit = Vec::new();
248 - for (dimension_name, sb) in &mut self.samples_table.iter_mut() {
249 - if let Some(sp) = sb.first() {
250 - if lsti.is_on_time(sp) {
251 - if let Some(sample) = sb.pop() {
252 - samples_to_emit.push((dimension_name.clone(), sample.value));
253 - }
254 - }
255 - }
256 - }
257 -
258 - // Emit data if we have samples
259 - if !samples_to_emit.is_empty() {
260 - self.emit_begin(lci.update_every.get());
261 - for (dimension_name, value) in samples_to_emit {
262 - self.emit_set(&dimension_name, value);
263 - }
264 - self.emit_end();
265 - }
266 -
267 - // Move to next interval
268 - self.last_samples_table_interval = Some(lsti.next_interval());
269 - self.last_collection_interval = Some(lci.next_interval());
270 -
271 - ChartState::Initialized
272 - }
273 -
274 - fn emit_begin(&self, update_every: u64) {
275 - let ue = std::time::Duration::from_nanos(update_every).as_micros() as u64;
276 - println!("BEGIN {} {}", self.chart_id, ue);
277 - }
278 -
279 - fn emit_set(&self, dimension_name: &str, value: f64) {
280 - println!("SET {} {}", dimension_name, value * self.divisor as f64);
281 - }
282 -
283 - fn emit_end(&self) {
284 - let collection_time = std::time::Duration::from_nanos(
285 - self.last_collection_interval.unwrap().collection_time(),
286 - )
287 - .as_secs();
288 - println!("END {collection_time}");
289 - }
290 -
291 - pub fn last_collection_time(&self) -> Option<std::time::SystemTime> {
292 - self.last_collection_interval.as_ref().map(|lci| {
293 - std::time::UNIX_EPOCH + std::time::Duration::from_nanos(lci.collection_time())
294 - })
295 - }
296 -}
src/crates/jf/otel-plugin/src/netdata_env.rs deleted
-204
@@ -1,204 +0,0 @@
1 -#![allow(dead_code)]
2 -
3 -use std::env;
4 -use std::path::PathBuf;
5 -
6 -#[derive(Debug, Clone, Default)]
7 -pub struct NetdataEnv {
8 - pub user_config_dir: Option<PathBuf>,
9 - pub stock_config_dir: Option<PathBuf>,
10 - pub plugins_dir: Option<PathBuf>,
11 - pub user_plugins_dirs: Option<Vec<PathBuf>>,
12 - pub web_dir: Option<PathBuf>,
13 - pub cache_dir: Option<PathBuf>,
14 - pub log_dir: Option<PathBuf>,
15 - pub host_prefix: Option<String>,
16 - pub debug_flags: Option<String>,
17 - pub update_every: Option<u64>,
18 - pub invocation_id: Option<String>,
19 - pub log_method: Option<LogMethod>,
20 - pub log_format: Option<LogFormat>,
21 - pub log_level: Option<LogLevel>,
22 - pub syslog_facility: Option<SyslogFacility>,
23 - pub errors_throttle_period: Option<u64>,
24 - pub errors_per_period: Option<u64>,
25 - pub systemd_journal_path: Option<PathBuf>,
26 -}
27 -
28 -#[derive(Debug, Clone)]
29 -pub enum LogMethod {
30 - Syslog,
31 - Journal,
32 - Stderr,
33 - None,
34 -}
35 -
36 -#[derive(Debug, Clone)]
37 -pub enum LogFormat {
38 - Journal,
39 - Logfmt,
40 - Json,
41 -}
42 -
43 -#[derive(Debug, Clone)]
44 -pub enum LogLevel {
45 - Emergency,
46 - Alert,
47 - Critical,
48 - Error,
49 - Warning,
50 - Notice,
51 - Info,
52 - Debug,
53 -}
54 -
55 -#[derive(Debug, Clone)]
56 -pub enum SyslogFacility {
57 - Auth,
58 - Authpriv,
59 - Cron,
60 - Daemon,
61 - Ftp,
62 - Kern,
63 - Lpr,
64 - Mail,
65 - News,
66 - Syslog,
67 - User,
68 - Uucp,
69 - Local0,
70 - Local1,
71 - Local2,
72 - Local3,
73 - Local4,
74 - Local5,
75 - Local6,
76 - Local7,
77 -}
78 -
79 -impl NetdataEnv {
80 - pub fn from_environment() -> Self {
81 - Self {
82 - user_config_dir: env::var("NETDATA_USER_CONFIG_DIR").ok().map(PathBuf::from),
83 - stock_config_dir: env::var("NETDATA_STOCK_CONFIG_DIR").ok().map(PathBuf::from),
84 - plugins_dir: env::var("NETDATA_PLUGINS_DIR").ok().map(PathBuf::from),
85 - user_plugins_dirs: env::var("NETDATA_USER_PLUGINS_DIRS")
86 - .ok()
87 - .map(|s| s.split(':').map(PathBuf::from).collect()),
88 - web_dir: env::var("NETDATA_WEB_DIR").ok().map(PathBuf::from),
89 - cache_dir: env::var("NETDATA_CACHE_DIR").ok().map(PathBuf::from),
90 - log_dir: env::var("NETDATA_LOG_DIR").ok().map(PathBuf::from),
91 - host_prefix: env::var("NETDATA_HOST_PREFIX").ok(),
92 - debug_flags: env::var("NETDATA_DEBUG_FLAGS").ok(),
93 - update_every: env::var("NETDATA_UPDATE_EVERY")
94 - .ok()
95 - .and_then(|s| s.parse().ok()),
96 - invocation_id: env::var("NETDATA_INVOCATION_ID").ok(),
97 - log_method: env::var("NETDATA_LOG_METHOD")
98 - .ok()
99 - .and_then(|s| s.parse().ok()),
100 - log_format: env::var("NETDATA_LOG_FORMAT")
101 - .ok()
102 - .and_then(|s| s.parse().ok()),
103 - log_level: env::var("NETDATA_LOG_LEVEL")
104 - .ok()
105 - .and_then(|s| s.parse().ok()),
106 - syslog_facility: env::var("NETDATA_SYSLOG_FACILITY")
107 - .ok()
108 - .and_then(|s| s.parse().ok()),
109 - errors_throttle_period: env::var("NETDATA_ERRORS_THROTTLE_PERIOD")
110 - .ok()
111 - .and_then(|s| s.parse().ok()),
112 - errors_per_period: env::var("NETDATA_ERRORS_PER_PERIOD")
113 - .ok()
114 - .and_then(|s| s.parse().ok()),
115 - systemd_journal_path: env::var("NETDATA_SYSTEMD_JOURNAL_PATH")
116 - .ok()
117 - .map(PathBuf::from),
118 - }
119 - }
120 -
121 - pub fn running_under_netdata(&self) -> bool {
122 - // we are overtly cautious, just one check would suffice
123 - self.user_config_dir.is_some()
124 - || self.stock_config_dir.is_some()
125 - || self.plugins_dir.is_some()
126 - || self.invocation_id.is_some()
127 - }
128 -}
129 -
130 -// Implement FromStr for the enums
131 -impl std::str::FromStr for LogMethod {
132 - type Err = String;
133 -
134 - fn from_str(s: &str) -> Result<Self, Self::Err> {
135 - match s.to_lowercase().as_str() {
136 - "syslog" => Ok(LogMethod::Syslog),
137 - "journal" => Ok(LogMethod::Journal),
138 - "stderr" => Ok(LogMethod::Stderr),
139 - "none" => Ok(LogMethod::None),
140 - _ => Err(format!("Invalid log method: {}", s)),
141 - }
142 - }
143 -}
144 -
145 -impl std::str::FromStr for LogFormat {
146 - type Err = String;
147 -
148 - fn from_str(s: &str) -> Result<Self, Self::Err> {
149 - match s.to_lowercase().as_str() {
150 - "journal" => Ok(LogFormat::Journal),
151 - "logfmt" => Ok(LogFormat::Logfmt),
152 - "json" => Ok(LogFormat::Json),
153 - _ => Err(format!("Invalid log format: {}", s)),
154 - }
155 - }
156 -}
157 -
158 -impl std::str::FromStr for LogLevel {
159 - type Err = String;
160 -
161 - fn from_str(s: &str) -> Result<Self, Self::Err> {
162 - match s.to_lowercase().as_str() {
163 - "emergency" => Ok(LogLevel::Emergency),
164 - "alert" => Ok(LogLevel::Alert),
165 - "critical" => Ok(LogLevel::Critical),
166 - "error" => Ok(LogLevel::Error),
167 - "warning" => Ok(LogLevel::Warning),
168 - "notice" => Ok(LogLevel::Notice),
169 - "info" => Ok(LogLevel::Info),
170 - "debug" => Ok(LogLevel::Debug),
171 - _ => Err(format!("Invalid log level: {}", s)),
172 - }
173 - }
174 -}
175 -
176 -impl std::str::FromStr for SyslogFacility {
177 - type Err = String;
178 -
179 - fn from_str(s: &str) -> Result<Self, Self::Err> {
180 - match s.to_lowercase().as_str() {
181 - "auth" => Ok(SyslogFacility::Auth),
182 - "authpriv" => Ok(SyslogFacility::Authpriv),
183 - "cron" => Ok(SyslogFacility::Cron),
184 - "daemon" => Ok(SyslogFacility::Daemon),
185 - "ftp" => Ok(SyslogFacility::Ftp),
186 - "kern" => Ok(SyslogFacility::Kern),
187 - "lpr" => Ok(SyslogFacility::Lpr),
188 - "mail" => Ok(SyslogFacility::Mail),
189 - "news" => Ok(SyslogFacility::News),
190 - "syslog" => Ok(SyslogFacility::Syslog),
191 - "user" => Ok(SyslogFacility::User),
192 - "uucp" => Ok(SyslogFacility::Uucp),
193 - "local0" => Ok(SyslogFacility::Local0),
194 - "local1" => Ok(SyslogFacility::Local1),
195 - "local2" => Ok(SyslogFacility::Local2),
196 - "local3" => Ok(SyslogFacility::Local3),
197 - "local4" => Ok(SyslogFacility::Local4),
198 - "local5" => Ok(SyslogFacility::Local5),
199 - "local6" => Ok(SyslogFacility::Local6),
200 - "local7" => Ok(SyslogFacility::Local7),
201 - _ => Err(format!("Invalid syslog facility: {}", s)),
202 - }
203 - }
204 -}
src/crates/jf/otel-plugin/src/plugin_config.rs deleted
-299
@@ -1,299 +0,0 @@
1 -use crate::netdata_env::NetdataEnv;
2 -
3 -use anyhow::{Context, Result};
4 -use bytesize::ByteSize;
5 -use clap::Parser;
6 -use serde::{Deserialize, Serialize};
7 -use std::fs;
8 -use std::path::Path;
9 -use std::time::Duration;
10 -
11 -#[derive(Parser, Debug, Clone, Serialize, Deserialize)]
12 -#[serde(deny_unknown_fields)]
13 -pub struct EndpointConfig {
14 - /// gRPC endpoint to listen on
15 - #[arg(long = "otel-endpoint", default_value = "127.0.0.1:4317")]
16 - pub path: String,
17 -
18 - /// Path to TLS certificate file (enables TLS when provided)
19 - #[arg(long = "otel-tls-cert-path")]
20 - pub tls_cert_path: Option<String>,
21 -
22 - /// Path to TLS private key file (required when TLS certificate is provided)
23 - #[arg(long = "otel-tls-key-path")]
24 - pub tls_key_path: Option<String>,
25 -
26 - /// Path to TLS CA certificate file for client authentication (optional)
27 - #[arg(long = "otel-tls-ca-cert-path")]
28 - pub tls_ca_cert_path: Option<String>,
29 -}
30 -
31 -impl Default for EndpointConfig {
32 - fn default() -> Self {
33 - Self {
34 - path: String::from("127.0.0.1:4317"),
35 - tls_cert_path: None,
36 - tls_key_path: None,
37 - tls_ca_cert_path: None,
38 - }
39 - }
40 -}
41 -
42 -#[derive(Parser, Debug, Clone, Serialize, Deserialize)]
43 -#[serde(deny_unknown_fields)]
44 -pub struct MetricsConfig {
45 - /// Print flattened metrics to stdout for debugging
46 - #[arg(long = "otel-metrics-print-flattened")]
47 - pub print_flattened: bool,
48 -
49 - /// Number of samples to buffer for collection interval detection
50 - #[arg(long = "otel-metrics-buffer-samples", default_value = "10")]
51 - pub buffer_samples: usize,
52 -
53 - /// Maximum number of new charts to create per collection interval
54 - #[arg(long = "otel-metrics-throttle-charts", default_value = "100")]
55 - pub throttle_charts: usize,
56 -
57 - /// Directory with configuration files for mapping OTEL metrics to Netdata charts
58 - #[arg(long = "otel-metrics-charts-configs-dir")]
59 - pub chart_configs_dir: Option<String>,
60 -}
61 -
62 -impl Default for MetricsConfig {
63 - fn default() -> Self {
64 - Self {
65 - print_flattened: false,
66 - buffer_samples: 10,
67 - throttle_charts: 100,
68 - chart_configs_dir: None,
69 - }
70 - }
71 -}
72 -
73 -/// Parse a duration string for clap (e.g., "7 days", "1 week", "168h")
74 -fn parse_duration(s: &str) -> Result<Duration, String> {
75 - humantime::parse_duration(s).map_err(|e| {
76 - format!(
77 - "Invalid duration format: '{}'. Use formats like '7 days', '1 week', '168h'. Error: {}",
78 - s, e
79 - )
80 - })
81 -}
82 -
83 -/// Parse a bytesize string for clap (e.g., "100MB", "1.5GB", "512MiB")
84 -fn parse_bytesize(s: &str) -> Result<ByteSize, String> {
85 - s.parse().map_err(|e| {
86 - format!(
87 - "Invalid size format: '{}'. Use formats like '100MB', '1.5GB', '512MiB'. Error: {}",
88 - s, e
89 - )
90 - })
91 -}
92 -
93 -#[derive(Parser, Debug, Clone, Serialize, Deserialize)]
94 -#[serde(deny_unknown_fields)]
95 -pub struct LogsConfig {
96 - /// Directory to store journal files for logs
97 - #[arg(long = "otel-logs-journal-dir")]
98 - pub journal_dir: String,
99 -
100 - /// Maximum file size for journal files (accepts human-readable sizes like "100MB", "1.5GB")
101 - #[arg(
102 - long = "otel-logs-rotation-size-of-journal-file",
103 - default_value = "100MB",
104 - value_parser = parse_bytesize
105 - )]
106 - #[serde(with = "bytesize_serde")]
107 - pub size_of_journal_file: ByteSize,
108 -
109 - /// Maximum number of journal files to keep
110 - #[arg(
111 - long = "otel-logs-retention-number-of-journal-files",
112 - default_value = "10"
113 - )]
114 - pub number_of_journal_files: usize,
115 -
116 - /// Maximum total size for all journal files (accepts human-readable sizes like "1GB", "500MB")
117 - #[arg(
118 - long = "otel-logs-retention-size-of-journal-files",
119 - default_value = "1GB",
120 - value_parser = parse_bytesize
121 - )]
122 - #[serde(with = "bytesize_serde")]
123 - pub size_of_journal_files: ByteSize,
124 -
125 - /// Maximum age for journal entries (accepts human-readable durations like "7 days", "1 week", "168h")
126 - #[arg(
127 - long = "otel-logs-retention-duration-of-journal-files",
128 - default_value = "7 days",
129 - value_parser = parse_duration
130 - )]
131 - #[serde(with = "humantime_serde")]
132 - pub duration_of_journal_files: Duration,
133 -
134 - /// Maximum duration that entries in a single journal file can span (accepts human-readable durations like "2 hours", "1h", "30m")
135 - #[arg(
136 - long = "otel-logs-rotation-duration-of-journal-file",
137 - default_value = "2 hours",
138 - value_parser = parse_duration
139 - )]
140 - #[serde(with = "humantime_serde")]
141 - pub duration_of_journal_file: Duration,
142 -}
143 -
144 -impl Default for LogsConfig {
145 - fn default() -> Self {
146 - Self {
147 - journal_dir: String::from("/tmp/netdata-journals"),
148 - size_of_journal_file: ByteSize::mb(100),
149 - number_of_journal_files: 10,
150 - size_of_journal_files: ByteSize::gb(1),
151 - duration_of_journal_files: Duration::from_secs(7 * 24 * 60 * 60), // 7 days
152 - duration_of_journal_file: Duration::from_secs(2 * 60 * 60), // 2 hours
153 - }
154 - }
155 -}
156 -
157 -#[derive(Default, Debug, Parser, Clone, Serialize, Deserialize)]
158 -#[command(name = "otel-plugin")]
159 -#[command(about = "OpenTelemetry metrics and logs plugin.")]
160 -#[command(version = "0.1")]
161 -#[serde(deny_unknown_fields)]
162 -pub struct PluginConfig {
163 - // endpoint configuration (includes grpc endpoint and tls)
164 - #[command(flatten)]
165 - #[serde(rename = "endpoint")]
166 - pub endpoint: EndpointConfig,
167 -
168 - // metrics
169 - #[command(flatten)]
170 - #[serde(rename = "metrics")]
171 - pub metrics: MetricsConfig,
172 -
173 - // logs
174 - #[command(flatten)]
175 - #[serde(rename = "logs")]
176 - pub logs: LogsConfig,
177 -
178 - /// Collection interval (ignored)
179 - #[arg(hide = true, help = "Collection interval in seconds (ignored)")]
180 - #[serde(skip)]
181 - pub _update_frequency: Option<u32>,
182 -
183 - // netdata env variables
184 - #[arg(skip)]
185 - #[serde(skip)]
186 - pub _netdata_env: NetdataEnv,
187 -}
188 -
189 -impl PluginConfig {
190 - pub fn new() -> Result<Self> {
191 - let netdata_env = NetdataEnv::from_environment();
192 -
193 - let mut config = if netdata_env.running_under_netdata() {
194 - // Try user config first, fallback to stock config
195 - let user_config = netdata_env
196 - .user_config_dir
197 - .as_ref()
198 - .map(|path| path.join("otel.yml"))
199 - .and_then(|path| {
200 - Self::from_yaml_file(&path)
201 - .with_context(|| format!("Loading user config from {}", path.display()))
202 - .ok()
203 - });
204 -
205 - if let Some(config) = user_config {
206 - config
207 - } else if let Some(stock_path) = netdata_env
208 - .stock_config_dir
209 - .as_ref()
210 - .map(|p| p.join("otel.yml"))
211 - {
212 - Self::from_yaml_file(&stock_path).with_context(|| {
213 - format!("Loading stock config from {}", stock_path.display())
214 - })?
215 - } else {
216 - anyhow::bail!("No configuration directories available");
217 - }
218 - } else {
219 - // load from CLI args
220 - Self::parse()
221 - };
222 -
223 - // Resolve relative paths
224 - if let Some(charts_config_dir) = &mut config.metrics.chart_configs_dir {
225 - *charts_config_dir =
226 - resolve_relative_path(charts_config_dir, netdata_env.user_config_dir.as_deref());
227 - }
228 - config.logs.journal_dir =
229 - resolve_relative_path(&config.logs.journal_dir, netdata_env.log_dir.as_deref());
230 -
231 - // Validate configuration
232 - if config.metrics.buffer_samples == 0 {
233 - anyhow::bail!("buffer_samples must be greater than 0");
234 - }
235 -
236 - if config.metrics.throttle_charts == 0 {
237 - anyhow::bail!("throttle_charts must be greater than 0");
238 - }
239 -
240 - // Validate endpoint format (basic check)
241 - if !config.endpoint.path.contains(':') {
242 - anyhow::bail!(
243 - "endpoint must be in format host:port, got: {}",
244 - config.endpoint.path
245 - );
246 - }
247 -
248 - // Validate TLS configuration
249 - match (
250 - &config.endpoint.tls_cert_path,
251 - &config.endpoint.tls_key_path,
252 - ) {
253 - (Some(cert_path), Some(key_path)) => {
254 - if cert_path.is_empty() {
255 - anyhow::bail!("TLS certificate path cannot be empty when provided");
256 - }
257 - if key_path.is_empty() {
258 - anyhow::bail!("TLS private key path cannot be empty when provided");
259 - }
260 - }
261 - (Some(_), None) => {
262 - anyhow::bail!(
263 - "TLS private key path must be provided when TLS certificate is provided"
264 - );
265 - }
266 - (None, Some(_)) => {
267 - anyhow::bail!(
268 - "TLS certificate path must be provided when TLS private key is provided"
269 - );
270 - }
271 - (None, None) => {
272 - // TLS disabled, which is fine
273 - }
274 - }
275 -
276 - Ok(config)
277 - }
278 -
279 - pub fn from_yaml_file<P: AsRef<Path>>(path: P) -> Result<Self> {
280 - let path = path.as_ref();
281 - let contents = fs::read_to_string(path)
282 - .with_context(|| format!("Failed to read config file: {}", path.display()))?;
283 - let config: PluginConfig = serde_yaml::from_str(&contents)
284 - .with_context(|| format!("Failed to parse YAML config file: {}", path.display()))?;
285 - Ok(config)
286 - }
287 -}
288 -
289 -// Helper function to resolve relative paths
290 -fn resolve_relative_path(path: &str, base_dir: Option<&Path>) -> String {
291 - let path = Path::new(path);
292 - if path.is_absolute() {
293 - path.to_string_lossy().to_string()
294 - } else if let Some(base) = base_dir {
295 - base.join(path).to_string_lossy().to_string()
296 - } else {
297 - path.to_string_lossy().to_string()
298 - }
299 -}
src/crates/jf/otel-plugin/src/regex_cache.rs deleted
-22
@@ -1,22 +0,0 @@
1 -use regex::Regex;
2 -use std::collections::HashMap;
3 -use std::sync::{Arc, Mutex};
4 -
5 -#[derive(Default, Debug)]
6 -pub struct RegexCache {
7 - cache: Arc<Mutex<HashMap<String, Regex>>>,
8 -}
9 -
10 -impl RegexCache {
11 - pub fn get(&self, pattern: &str) -> Result<Regex, regex::Error> {
12 - let mut cache = self.cache.lock().unwrap();
13 -
14 - if let Some(regex) = cache.get(pattern) {
15 - return Ok(regex.clone());
16 - }
17 -
18 - let compiled_regex = Regex::new(pattern)?;
19 - cache.insert(pattern.to_string(), compiled_regex.clone());
20 - Ok(compiled_regex)
21 - }
22 -}
src/crates/jf/otel-plugin/src/samples_table.rs deleted
-221
@@ -1,221 +0,0 @@
1 -use std::collections::HashMap;
2 -use std::num::NonZeroU64;
3 -
4 -#[derive(Default, Clone, Copy, PartialEq, Debug)]
5 -pub struct SamplePoint {
6 - unix_time: u64,
7 - pub value: f64,
8 -}
9 -
10 -#[derive(Copy, Clone, Debug)]
11 -pub struct CollectionInterval {
12 - pub end_time: u64,
13 - pub update_every: NonZeroU64,
14 -}
15 -
16 -impl CollectionInterval {
17 - fn from_samples(sample_points: &[SamplePoint]) -> Option<Self> {
18 - if sample_points.len() < 2 {
19 - return None;
20 - }
21 -
22 - let collection_time = sample_points[0].unix_time;
23 - let mut update_every = u64::MAX;
24 -
25 - for w in sample_points.windows(2) {
26 - update_every = update_every.min(w[1].unix_time - w[0].unix_time);
27 - }
28 -
29 - NonZeroU64::new(update_every).map(|update_every| Self {
30 - end_time: collection_time,
31 - update_every,
32 - })
33 - }
34 -
35 - pub fn next_interval(&self) -> Self {
36 - Self {
37 - end_time: self.end_time + self.update_every.get(),
38 - update_every: self.update_every,
39 - }
40 - }
41 -
42 - pub fn collection_time(&self) -> u64 {
43 - self.end_time + self.update_every.get()
44 - }
45 -
46 - fn is_stale(&self, sp: &SamplePoint) -> bool {
47 - sp.unix_time < self.end_time
48 - }
49 -
50 - pub fn is_on_time(&self, sp: &SamplePoint) -> bool {
51 - let window = self.update_every.get() / 4;
52 - let window_start = self.end_time + self.update_every.get() - window;
53 - let window_end = self.end_time + self.update_every.get() + window;
54 -
55 - sp.unix_time >= window_start && sp.unix_time <= window_end
56 - }
57 -
58 - pub fn is_in_gap(&self, sp: &SamplePoint) -> bool {
59 - !self.is_stale(sp) && !self.is_on_time(sp)
60 - }
61 -
62 - pub fn aligned_interval(&self) -> Option<Self> {
63 - let dur = std::time::Duration::from_nanos(self.end_time);
64 - let end_time = dur.as_secs() + u64::from(dur.subsec_millis() >= 500);
65 -
66 - let dur = std::time::Duration::from_nanos(self.update_every.get());
67 - let update_every = dur.as_secs() + u64::from(dur.subsec_millis() >= 500);
68 -
69 - Self::from_secs(end_time, update_every)
70 - }
71 -
72 - fn from_secs(end_time: u64, update_every: u64) -> Option<Self> {
73 - let end_time = std::time::Duration::from_secs(end_time).as_nanos() as u64;
74 - let update_every = std::time::Duration::from_secs(update_every).as_nanos() as u64;
75 -
76 - NonZeroU64::new(update_every).map(|update_every| Self {
77 - end_time,
78 - update_every,
79 - })
80 - }
81 -}
82 -
83 -#[derive(Debug, Default, Clone)]
84 -pub struct SamplesBuffer(Vec<SamplePoint>);
85 -
86 -impl SamplesBuffer {
87 - pub fn push(&mut self, sp: SamplePoint) {
88 - match self.0.binary_search_by_key(&sp.unix_time, |p| p.unix_time) {
89 - Ok(idx) => self.0[idx] = sp,
90 - Err(idx) => self.0.insert(idx, sp),
91 - }
92 - }
93 -
94 - pub fn pop(&mut self) -> Option<SamplePoint> {
95 - if self.0.is_empty() {
96 - None
97 - } else {
98 - Some(self.0.remove(0))
99 - }
100 - }
101 -
102 - fn is_empty(&self) -> bool {
103 - self.0.is_empty()
104 - }
105 -
106 - pub fn first(&self) -> Option<&SamplePoint> {
107 - self.0.first()
108 - }
109 -
110 - pub fn len(&self) -> usize {
111 - self.0.len()
112 - }
113 -
114 - pub fn drop_stale_samples(&mut self, ci: &CollectionInterval) -> usize {
115 - let split_idx = self
116 - .0
117 - .iter()
118 - .position(|sp| !ci.is_stale(sp))
119 - .unwrap_or(self.0.len());
120 -
121 - self.0.drain(..split_idx);
122 -
123 - split_idx
124 - }
125 -
126 - pub fn collection_interval(&self) -> Option<CollectionInterval> {
127 - CollectionInterval::from_samples(&self.0)
128 - }
129 -}
130 -
131 -#[derive(Debug, Default)]
132 -pub struct SamplesTable {
133 - dimensions: HashMap<String, SamplesBuffer>,
134 -}
135 -
136 -impl SamplesTable {
137 - pub fn insert(&mut self, dimension: &str, unix_time: u64, value: f64) -> bool {
138 - let sp = SamplePoint { unix_time, value };
139 -
140 - let is_new_dimension = if let Some(sb) = self.dimensions.get_mut(dimension) {
141 - sb.push(sp);
142 - false
143 - } else {
144 - let mut sb = SamplesBuffer::default();
145 - sb.push(sp);
146 - self.dimensions.insert(dimension.to_string(), sb);
147 - true
148 - };
149 -
150 - is_new_dimension
151 - }
152 -
153 - pub fn is_empty(&self) -> bool {
154 - self.dimensions.values().all(|sb| sb.is_empty())
155 - }
156 -
157 - pub fn total_samples(&self) -> usize {
158 - self.dimensions
159 - .values()
160 - .map(|sb| sb.len())
161 - .max()
162 - .unwrap_or(0)
163 - }
164 -
165 - pub fn drop_stale_samples(&mut self, ci: &CollectionInterval) -> usize {
166 - let mut dropped_samples = 0;
167 -
168 - for sb in self.dimensions.values_mut() {
169 - dropped_samples += sb.drop_stale_samples(ci);
170 - }
171 -
172 - dropped_samples
173 - }
174 -
175 - pub fn collection_interval(&self) -> Option<CollectionInterval> {
176 - self.dimensions
177 - .values()
178 - .filter_map(|sb| sb.collection_interval())
179 - .min_by_key(|ci| ci.collection_time())
180 - }
181 -
182 - pub fn iter_mut(&mut self) -> impl Iterator<Item = (&String, &mut SamplesBuffer)> {
183 - self.dimensions.iter_mut()
184 - }
185 -
186 - pub fn iter_dimensions(&self) -> impl Iterator<Item = &String> {
187 - self.dimensions.keys()
188 - }
189 -
190 - pub fn iter_samples_buffers(&self) -> impl Iterator<Item = &SamplesBuffer> {
191 - self.dimensions.values()
192 - }
193 -
194 - // returns multiplier/divisor
195 - pub fn scaling_factors(&self) -> (i32, i32) {
196 - let mut has_nonzero = false;
197 -
198 - for buffer in self.dimensions.values() {
199 - for sample in &buffer.0 {
200 - let value = sample.value;
201 -
202 - // Check if value is outside the -100 to 100 range
203 - if !(-100.0..=100.0).contains(&value) {
204 - return (1, 1);
205 - }
206 -
207 - // Check for non-zero values
208 - if value != 0.0 {
209 - has_nonzero = true;
210 - }
211 - }
212 - }
213 -
214 - // Return 1/1000 scaling if all values are in range and at least one is non-zero
215 - if has_nonzero {
216 - (1, 1000)
217 - } else {
218 - (1, 1)
219 - }
220 - }
221 -}