Skip field remapping entries when querying logs. (#21755)
Skip ND_REMAPPING entries in extract_entry_data to fix column-name-as-value bug Field remapping entries (tagged with ND_REMAPPING=1) are internal bookkeeping that map systemd field names back to OTEL names. Their data objects store "SYSTEMD_NAME=otel_name", so when processed as regular log entries the reverse-mapping produces field="otel_name", value="otel_name" — the column name appears as the cell value. Detect the ND_REMAPPING=1 marker while iterating data objects and skip the entire entry. Use flatten() instead of unwrap() to handle the resulting None slots in the result vector.
vkalintiris committed
Feb 13, 2026 at 16:24 UTC
5b422dd84cceec5b66ceba2e7adeca79681acee9
1 file changed
+19
-3
src/crates/journal-engine/src/logs/query.rs
+19
-3
@@ -5,6 +5,7 @@
5
//! functions for extracting raw field data from journal entries.
6
7
use crate::error::Result;
8
+use journal_core::field_map::REMAPPING_MARKER;
9
use journal_core::file::{JournalFile, Mmap};
10
use journal_index::{
11
Anchor, Direction, FieldName, FieldValuePair, FileIndex, Filter, LogEntryId, LogQueryParams,
@@ -555,8 +556,13 @@ fn extract_entry_data(log_entries: &[LogEntryId]) -> Result<Vec<LogEntryData>> {
556
entry_guard.collect_offsets(&mut data_offsets)?;
557
drop(entry_guard);
558
558
- // Extract all field=value pairs
559
+ // Extract all field=value pairs, skipping field remapping entries.
560
+ // Remapping entries are internal bookkeeping (ND_REMAPPING=1) that map
561
+ // systemd field names back to OTEL names. Their data objects contain
562
+ // "SYSTEMD_NAME=otel.name", so if processed as log data the OTEL name
563
+ // would appear as the cell value — which is exactly the column name.
564
let mut fields = Vec::new();
565
+ let mut is_remapping_entry = false;
566
for data_offset in data_offsets.iter().copied() {
567
let data_guard = journal_file.data_ref(data_offset)?;
568
let payload_bytes = if data_guard.is_compressed() {
@@ -565,6 +571,12 @@ fn extract_entry_data(log_entries: &[LogEntryId]) -> Result<Vec<LogEntryData>> {
571
} else {
572
data_guard.raw_payload()
573
};
574
+
575
+ if payload_bytes == REMAPPING_MARKER {
576
+ is_remapping_entry = true;
577
+ break;
578
+ }
579
+
580
let payload_str = String::from_utf8_lossy(payload_bytes);
581
582
if let Some(mut pair) = FieldValuePair::parse(&payload_str) {
@@ -579,6 +591,10 @@ fn extract_entry_data(log_entries: &[LogEntryId]) -> Result<Vec<LogEntryData>> {
591
}
592
}
593
594
+ if is_remapping_entry {
595
+ continue;
596
+ }
597
+
598
result[original_idx] = Some(LogEntryData {
599
timestamp: entry.timestamp.get(),
600
fields,
@@ -586,6 +602,6 @@ fn extract_entry_data(log_entries: &[LogEntryId]) -> Result<Vec<LogEntryData>> {
602
}
603
}
604
589
- // Unwrap all Options (they're all Some at this point)
590
- Ok(result.into_iter().map(|opt| opt.unwrap()).collect())
605
+ // Filter out None entries (remapping entries are skipped and left as None)
606
+ Ok(result.into_iter().flatten().collect())
607
}