Skip to content

Commit 308bd73

Browse files
feat: Collect the containerdebug logs (#912)
* feat: Collect the containerdebug logs * chore: Upgrade serde-saphyr * fix: Do not hard-code the environment variable name "FILE_LOG_DIRECTORY" * fix: Create the log directories of the info-fetcher sidecars * chore: Update changelog * fix: Do not create the log directories of the info-fetcher sidecars * chore: Sort the FILE_LOG_* constants like in the other operators
1 parent bbf3751 commit 308bd73

9 files changed

Lines changed: 413 additions & 51 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ All notable changes to this project will be documented in this file.
2222
- A `PodDisruptionBudget` is now written out for the `servers` role when it runs as a `Deployment`,
2323
with `maxUnavailable: 1`. Configurable via `spec.servers.roleConfig.podDisruptionBudget` ([#873]).
2424
- Add `/ready` endpoint to the operator Deployment, which reports the CRD installation status ([#894]).
25+
- The logs of the `containerdebug` process are now collected by the Vector agent and forwarded to
26+
the Vector aggregator ([#912]).
2527

2628
### Changed
2729

@@ -101,14 +103,21 @@ All notable changes to this project will be documented in this file.
101103
containing a line break, is rejected with `400` ([#909]).
102104
- The user-info-fetcher Keycloak backend no longer drops a configured `rootPath` from the URLs it
103105
requests, so a Keycloak served below a sub-path, like `/auth`, can now be reached ([#909]).
106+
- The `containerdebug` process writes its log file again. The operator set
107+
`CONTAINERDEBUG_LOG_DIRECTORY` on the OPA container, which containerdebug stopped reading in its
108+
version 0.2.0 when it moved to `stackable-telemetry`; the directory is now set via
109+
`FILE_LOG_DIRECTORY`, with an hourly rotation that keeps at most two files ([#912]).
110+
- The `tracing-rs` transform of the Vector agent no longer discards log events. A message which is
111+
not a string is kept among the other fields, a `fields` object which is not an object falls back
112+
to the raw line, and a missing timestamp is recorded as an error ([#912]).
104113

105-
[#873]: https://github.com/stackabletech/opa-operator/pull/873
106114
[#852]: https://github.com/stackabletech/opa-operator/pull/852
107115
[#861]: https://github.com/stackabletech/opa-operator/pull/861
108116
[#863]: https://github.com/stackabletech/opa-operator/pull/863
109117
[#867]: https://github.com/stackabletech/opa-operator/pull/867
110118
[#871]: https://github.com/stackabletech/opa-operator/pull/871
111119
[#872]: https://github.com/stackabletech/opa-operator/pull/872
120+
[#873]: https://github.com/stackabletech/opa-operator/pull/873
112121
[#880]: https://github.com/stackabletech/opa-operator/pull/880
113122
[#882]: https://github.com/stackabletech/opa-operator/pull/882
114123
[#886]: https://github.com/stackabletech/opa-operator/pull/886
@@ -123,6 +132,7 @@ All notable changes to this project will be documented in this file.
123132
[#906]: https://github.com/stackabletech/opa-operator/pull/906
124133
[#908]: https://github.com/stackabletech/opa-operator/pull/908
125134
[#909]: https://github.com/stackabletech/opa-operator/pull/909
135+
[#912]: https://github.com/stackabletech/opa-operator/pull/912
126136

127137
## [26.7.0] - 2026-07-21
128138

‎Cargo.nix‎

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎docs/modules/opa/pages/usage-guide/logging.adoc‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,13 @@ spec:
5656
The decision logs are still filtered by the log level set for the console and file appenders.
5757
Therefore, the configuration above would result in decision logs being present on file but not on console.
5858

59+
NOTE: The logs of the xref:concepts:observability/containerdebug.adoc[`containerdebug` process] are collected as well.
60+
They are labelled `container: containerdebug`, although no such Kubernetes container exists.
61+
The process runs in the background of the `opa` container.
62+
The label comes from `/stackable/log/containerdebug`, the directory which the operator sets with the `FILE_LOG_DIRECTORY` environment variable and from which the Vector agent derives the container name.
63+
Overriding `FILE_LOG_DIRECTORY` with `envOverrides` therefore changes how these logs are aggregated.
64+
The agent only collects files one level below `/stackable/log`, so a sibling directory such as `/stackable/log/opa` relabels the events instead of dropping them, while any deeper or shallower path stops the aggregation.
65+
A path outside `/stackable/log` also leaves the size-capped log volume.
66+
5967
Further information on how to configure logging, can be found in
6068
xref:concepts:logging.adoc[].

‎rust/operator-binary/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ tracing.workspace = true
3232
built.workspace = true
3333

3434
[dev-dependencies]
35+
stackable-operator = { workspace = true, features = ["test-support"] }
3536
yaml_serde.workspace = true

‎rust/operator-binary/src/controller/build/properties/product_logging/vector-test.yaml‎

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,285 @@ tests:
107107
source: |
108108
assert!(exists(.errors), "unparsable timestamp should be recorded as an error")
109109
110+
- name: Containerdebug tracing-rs log
111+
inputs:
112+
- type: log
113+
insert_at: processed_files_tracing_rs
114+
log_fields:
115+
file: /stackable/log/containerdebug/containerdebug.2026-11-02-14.tracing-rs.json
116+
message: '{"timestamp":"2026-11-02T14:49:28.028922Z","level":"INFO","fields":{"message":"found disk","disk.name":"overlay"},"target":"containerdebug::system_information::disk"}'
117+
pod: test-opa-server-default-0
118+
source_type: file
119+
timestamp: 2026-11-02T14:49:29.473487331Z
120+
outputs:
121+
- extract_from: extended_logs
122+
expected_event_count: 1
123+
conditions:
124+
- type: vrl
125+
source: |
126+
expected_log_event = {
127+
"cluster": "opa",
128+
"container": "containerdebug",
129+
"file": "containerdebug.2026-11-02-14.tracing-rs.json",
130+
"level": "INFO",
131+
"logger": "containerdebug::system_information::disk",
132+
"message": "found disk\n\ndisk.name=overlay",
133+
"timestamp": t'2026-11-02T14:49:28.028922Z',
134+
"namespace": "default",
135+
"pod": "test-opa-server-default-0",
136+
"role": "server",
137+
"roleGroup": "default"
138+
}
139+
140+
assert_eq!(expected_log_event, .)
141+
- name: Containerdebug tracing-rs log which is unparsable
142+
inputs:
143+
- type: log
144+
insert_at: processed_files_tracing_rs
145+
log_fields:
146+
file: /stackable/log/containerdebug/containerdebug.2026-11-02-14.tracing-rs.json
147+
message: '{ unparsable'
148+
pod: test-opa-server-default-0
149+
source_type: file
150+
timestamp: 2026-11-02T14:49:29.473487331Z
151+
outputs:
152+
- extract_from: extended_logs
153+
expected_event_count: 1
154+
conditions:
155+
- type: vrl
156+
source: |
157+
expected_log_event = {
158+
"cluster": "opa",
159+
"container": "containerdebug",
160+
"errors": [
161+
.errors[0]
162+
],
163+
"file": "containerdebug.2026-11-02-14.tracing-rs.json",
164+
"level": "INFO",
165+
"logger": "",
166+
"message": "{ unparsable",
167+
"timestamp": .timestamp,
168+
"namespace": "default",
169+
"pod": "test-opa-server-default-0",
170+
"role": "server",
171+
"roleGroup": "default"
172+
}
173+
174+
assert_eq!(expected_log_event, .)
175+
176+
# The remainder of the message contains character offsets into
177+
# the VRL source, so only the stable prefix is asserted.
178+
assert!(starts_with!(.errors[0], "JSON not parsable: "))
179+
- name: Containerdebug tracing-rs log which is not a JSON object
180+
inputs:
181+
- type: log
182+
insert_at: processed_files_tracing_rs
183+
log_fields:
184+
file: /stackable/log/containerdebug/containerdebug.2026-11-02-14.tracing-rs.json
185+
message: 'false'
186+
pod: test-opa-server-default-0
187+
source_type: file
188+
timestamp: 2026-11-02T14:49:29.473487331Z
189+
outputs:
190+
- extract_from: extended_logs
191+
expected_event_count: 1
192+
conditions:
193+
- type: vrl
194+
source: |
195+
expected_log_event = {
196+
"cluster": "opa",
197+
"container": "containerdebug",
198+
"errors": [
199+
"Parsed event is not a JSON object."
200+
],
201+
"file": "containerdebug.2026-11-02-14.tracing-rs.json",
202+
"level": "INFO",
203+
"logger": "",
204+
"message": "false",
205+
"timestamp": .timestamp,
206+
"namespace": "default",
207+
"pod": "test-opa-server-default-0",
208+
"role": "server",
209+
"roleGroup": "default"
210+
}
211+
212+
assert_eq!(expected_log_event, .)
213+
- name: Containerdebug tracing-rs log with invalid fields
214+
inputs:
215+
- type: log
216+
insert_at: processed_files_tracing_rs
217+
log_fields:
218+
file: /stackable/log/containerdebug/containerdebug.2026-11-02-14.tracing-rs.json
219+
message: '{"timestamp":"unparsable","level":"INVALID","fields":"none","target":null}'
220+
pod: test-opa-server-default-0
221+
source_type: file
222+
timestamp: 2026-11-02T14:49:29.473487331Z
223+
outputs:
224+
- extract_from: extended_logs
225+
expected_event_count: 1
226+
conditions:
227+
- type: vrl
228+
source: |
229+
expected_log_event = {
230+
"cluster": "opa",
231+
"container": "containerdebug",
232+
"errors": [
233+
.errors[0],
234+
"Logger/target not found.",
235+
"Level \"INVALID\" unknown, using \"INFO\" instead.",
236+
"Fields are not an object."
237+
],
238+
"file": "containerdebug.2026-11-02-14.tracing-rs.json",
239+
"level": "INFO",
240+
"logger": "",
241+
"message": "{\"timestamp\":\"unparsable\",\"level\":\"INVALID\",\"fields\":\"none\",\"target\":null}",
242+
"timestamp": .timestamp,
243+
"namespace": "default",
244+
"pod": "test-opa-server-default-0",
245+
"role": "server",
246+
"roleGroup": "default"
247+
}
248+
249+
assert_eq!(expected_log_event, .)
250+
251+
# The remainder of the message contains character offsets into
252+
# the VRL source, so only the stable prefix is asserted.
253+
assert!(starts_with!(.errors[0], "Timestamp not parsable, using current time instead: "))
254+
- name: Containerdebug tracing-rs log without a timestamp
255+
inputs:
256+
- type: log
257+
insert_at: processed_files_tracing_rs
258+
log_fields:
259+
file: /stackable/log/containerdebug/containerdebug.2026-11-02-14.tracing-rs.json
260+
message: '{"level":"INFO","fields":{"message":"found disk"},"target":"containerdebug::system_information::disk"}'
261+
pod: test-opa-server-default-0
262+
source_type: file
263+
timestamp: 2026-11-02T14:49:29.473487331Z
264+
outputs:
265+
- extract_from: extended_logs
266+
expected_event_count: 1
267+
conditions:
268+
- type: vrl
269+
source: |
270+
expected_log_event = {
271+
"cluster": "opa",
272+
"container": "containerdebug",
273+
"errors": [
274+
"Timestamp not found, using current time instead."
275+
],
276+
"file": "containerdebug.2026-11-02-14.tracing-rs.json",
277+
"level": "INFO",
278+
"logger": "containerdebug::system_information::disk",
279+
"message": "found disk",
280+
"timestamp": .timestamp,
281+
"namespace": "default",
282+
"pod": "test-opa-server-default-0",
283+
"role": "server",
284+
"roleGroup": "default"
285+
}
286+
287+
assert_eq!(expected_log_event, .)
288+
289+
# The current time is used and not the timestamp which the file
290+
# source attached to the event. The latter reaches the transform
291+
# as a string, whereas now() yields a timestamp.
292+
assert!(is_timestamp(.timestamp))
293+
- name: Containerdebug tracing-rs log without a message but with other fields
294+
inputs:
295+
- type: log
296+
insert_at: processed_files_tracing_rs
297+
log_fields:
298+
file: /stackable/log/containerdebug/containerdebug.2026-11-02-14.tracing-rs.json
299+
message: '{"timestamp":"2026-11-02T14:49:28.028922Z","level":"INFO","fields":{"disk.name":"overlay"},"target":"containerdebug::system_information::disk"}'
300+
pod: test-opa-server-default-0
301+
source_type: file
302+
timestamp: 2026-11-02T14:49:29.473487331Z
303+
outputs:
304+
- extract_from: extended_logs
305+
expected_event_count: 1
306+
conditions:
307+
- type: vrl
308+
source: |
309+
expected_log_event = {
310+
"cluster": "opa",
311+
"container": "containerdebug",
312+
"file": "containerdebug.2026-11-02-14.tracing-rs.json",
313+
"level": "INFO",
314+
"logger": "containerdebug::system_information::disk",
315+
"message": "disk.name=overlay",
316+
"timestamp": t'2026-11-02T14:49:28.028922Z',
317+
"namespace": "default",
318+
"pod": "test-opa-server-default-0",
319+
"role": "server",
320+
"roleGroup": "default"
321+
}
322+
323+
assert_eq!(expected_log_event, .)
324+
- name: Containerdebug tracing-rs log with a message which is not a string
325+
inputs:
326+
- type: log
327+
insert_at: processed_files_tracing_rs
328+
log_fields:
329+
file: /stackable/log/containerdebug/containerdebug.2026-11-02-14.tracing-rs.json
330+
message: '{"timestamp":"2026-11-02T14:49:28.028922Z","level":"INFO","fields":{"message":42,"disk.name":"overlay"},"target":"containerdebug::system_information::disk"}'
331+
pod: test-opa-server-default-0
332+
source_type: file
333+
timestamp: 2026-11-02T14:49:29.473487331Z
334+
outputs:
335+
- extract_from: extended_logs
336+
expected_event_count: 1
337+
conditions:
338+
- type: vrl
339+
source: |
340+
expected_log_event = {
341+
"cluster": "opa",
342+
"container": "containerdebug",
343+
"file": "containerdebug.2026-11-02-14.tracing-rs.json",
344+
"level": "INFO",
345+
"logger": "containerdebug::system_information::disk",
346+
"message": "disk.name=overlay\nmessage=42",
347+
"timestamp": t'2026-11-02T14:49:28.028922Z',
348+
"namespace": "default",
349+
"pod": "test-opa-server-default-0",
350+
"role": "server",
351+
"roleGroup": "default"
352+
}
353+
354+
assert_eq!(expected_log_event, .)
355+
- name: Containerdebug tracing-rs log without any fields
356+
inputs:
357+
- type: log
358+
insert_at: processed_files_tracing_rs
359+
log_fields:
360+
file: /stackable/log/containerdebug/containerdebug.2026-11-02-14.tracing-rs.json
361+
message: '{"timestamp":"2026-11-02T14:49:28.028922Z","level":"INFO","fields":{},"target":"containerdebug::system_information::disk"}'
362+
pod: test-opa-server-default-0
363+
source_type: file
364+
timestamp: 2026-11-02T14:49:29.473487331Z
365+
outputs:
366+
- extract_from: extended_logs
367+
expected_event_count: 1
368+
conditions:
369+
- type: vrl
370+
source: |
371+
expected_log_event = {
372+
"cluster": "opa",
373+
"container": "containerdebug",
374+
"errors": [
375+
"Message not found."
376+
],
377+
"file": "containerdebug.2026-11-02-14.tracing-rs.json",
378+
"level": "INFO",
379+
"logger": "containerdebug::system_information::disk",
380+
"message": "",
381+
"timestamp": t'2026-11-02T14:49:28.028922Z',
382+
"namespace": "default",
383+
"pod": "test-opa-server-default-0",
384+
"role": "server",
385+
"roleGroup": "default"
386+
}
387+
388+
assert_eq!(expected_log_event, .)
110389
- name: Prepare stdout log
111390
inputs:
112391
- type: log

0 commit comments

Comments
 (0)