Skip to content

Commit e9e013d

Browse files
feat: add rust server logs to the formats list (#1426)
1 parent e980283 commit e9e013d

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

resources/formats.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,5 +1464,40 @@
14641464
]
14651465
}
14661466
]
1467+
},
1468+
{
1469+
"name": "rust_server_logs",
1470+
"regex": [
1471+
{
1472+
"pattern": "^(?P<timestamp>\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d+Z)\\s+(?P<level>\\w+)\\s+(?P<logger_context>\\S+)\\s+(?P<thread_id>ThreadId\\(\\d+\\))\\s+(?P<module>.*?):(?P<line_number>\\d+):\\s+(?P<body>.*)",
1473+
"fields": [
1474+
"timestamp",
1475+
"level",
1476+
"logger_context",
1477+
"thread_id",
1478+
"module",
1479+
"line_number",
1480+
"body"
1481+
]
1482+
},
1483+
{
1484+
"pattern": "^\\[(?P<timestamp>\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[^ ]+) (?P<level>\\w+)\\s+(?P<module>[^\\]]+)\\]\\s+(?P<body>.*)",
1485+
"fields": [
1486+
"timestamp",
1487+
"level",
1488+
"module",
1489+
"body"
1490+
]
1491+
},
1492+
{
1493+
"pattern": "^(?P<timestamp>\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d+Z?)\\s+(?P<level>\\w+)\\s+(?P<module>\\S+?):\\s+(?P<body>.*)",
1494+
"fields": [
1495+
"timestamp",
1496+
"level",
1497+
"module",
1498+
"body"
1499+
]
1500+
}
1501+
]
14671502
}
14681503
]

src/event/format/known_schema.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,4 +513,82 @@ mod tests {
513513
assert!(!obj.contains_key("level"));
514514
assert!(!obj.contains_key("timestamp"));
515515
}
516+
517+
#[test]
518+
fn test_rust_server_logs() {
519+
let processor = EventProcessor::new(FORMATS_JSON);
520+
let schema = processor
521+
.schema_definitions
522+
.get("rust_server_logs")
523+
.unwrap();
524+
525+
let test_logs = vec![
526+
// Current parseable format with ThreadId
527+
"2025-09-06T10:43:01.628980875Z WARN main ThreadId(01) parseable::handlers::http::cluster:919: node http://0.0.0.0:8010/ is not live",
528+
"2025-09-06T10:44:12.62276265Z ERROR actix-rt|system:0|arbiter:17 ThreadId(163) parseable_enterprise::http::handlers::query:43: JsonParse(\"Datafusion Error: Schema error: No field named a. Valid fields are serverlogs.log\")",
529+
"2025-09-06T05:16:46.092071318Z ERROR actix-rt|system:0|arbiter:21 ThreadId(167) parseable_enterprise::http::handlers::query:43: JsonParse(\"Datafusion Error: Schema error: No field named ansible.host.ip\")",
530+
"2025-09-06T11:22:07.500864363Z WARN main ThreadId(01) parseable_enterprise:70: Received shutdown signal, notifying server to shut down...",
531+
// env_logger format
532+
"[2025-09-06T10:43:01.628980875Z INFO parseable::storage] Initializing storage backend",
533+
"[2025-09-06T10:43:01.628980875Z ERROR parseable::http::ingest] Failed to parse JSON",
534+
// Simple tracing format (no ThreadId)
535+
"2025-09-06T10:43:01.628980875Z INFO parseable::storage::s3: Storage configured successfully",
536+
"2025-09-06T10:43:01.628980875Z DEBUG parseable::query::engine: Query executed in 45ms",
537+
];
538+
539+
for (i, log_text) in test_logs.iter().enumerate() {
540+
let mut obj = Map::new();
541+
let log_field = "raw_log";
542+
obj.insert(log_field.to_string(), Value::String(log_text.to_string()));
543+
544+
let result = schema.check_or_extract(&mut obj, Some(log_field));
545+
assert!(
546+
result.is_some(),
547+
"Failed to extract fields from rust server log {}: {}",
548+
i + 1,
549+
log_text
550+
);
551+
552+
// Verify basic fields that should be present in all formats
553+
assert!(
554+
obj.contains_key("timestamp"),
555+
"Missing timestamp field for log {}",
556+
i + 1
557+
);
558+
assert!(
559+
obj.contains_key("level"),
560+
"Missing level field for log {}",
561+
i + 1
562+
);
563+
assert!(
564+
obj.contains_key("body"),
565+
"Missing body field for log {}",
566+
i + 1
567+
);
568+
assert!(
569+
obj.contains_key("module"),
570+
"Missing module field for log {}",
571+
i + 1
572+
);
573+
574+
// ThreadId and line_number are only present in the first format (logs 0-3)
575+
if i < 4 {
576+
assert!(
577+
obj.contains_key("logger_context"),
578+
"Missing logger_context field for log {}",
579+
i + 1
580+
);
581+
assert!(
582+
obj.contains_key("thread_id"),
583+
"Missing thread_id field for log {}",
584+
i + 1
585+
);
586+
assert!(
587+
obj.contains_key("line_number"),
588+
"Missing line_number field for log {}",
589+
i + 1
590+
);
591+
}
592+
}
593+
}
516594
}

0 commit comments

Comments
 (0)