@cryptotaxi247 / netdata-1 / commits / 5b422dd84

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 }