feat(main): Pass log levels along to Stackdriver

If a priority is present, it is passed as-is into the Stackdriver API.
This allows filtering by severity in the logs UI. Conveniently, the
levels are the same between journald and Stackdriver.

Fixes #11.
This commit is contained in:
Thomas ten Cate 2018-09-24 15:10:13 +02:00 committed by Vincent Ambo
parent d9569ad80a
commit a89be8d715
2 changed files with 37 additions and 2 deletions

View file

@ -339,6 +339,27 @@ fn parse_microseconds(input: String) -> Option<DateTime<Utc>> {
} }
} }
/// Converts a journald log message priority (using levels 0/emerg through
/// 7/debug, see "man journalctl" and "man systemd.journal-fields") to a
/// Stackdriver-compatible severity number (see
/// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity).
/// Conveniently, the names are the same. Inconveniently, the numbers are not.
///
/// Any unknown values are returned as an empty option.
fn priority_to_severity(priority: String) -> Option<u32> {
match priority.as_ref() {
"0" => Some(800), // emerg
"1" => Some(700), // alert
"2" => Some(600), // crit
"3" => Some(500), // err
"4" => Some(400), // warning
"5" => Some(300), // notice
"6" => Some(200), // info
"7" => Some(100), // debug
_ => None,
}
}
/// This structure represents a log entry in the format expected by /// This structure represents a log entry in the format expected by
/// the Stackdriver API. /// the Stackdriver API.
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -351,6 +372,10 @@ struct LogEntry {
#[serde(flatten)] #[serde(flatten)]
payload: Payload, payload: Payload,
// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
#[serde(skip_serializing_if = "Option::is_none")]
severity: Option<u32>,
} }
impl From<JournalRecord> for LogEntry { impl From<JournalRecord> for LogEntry {
@ -382,6 +407,13 @@ impl From<JournalRecord> for LogEntry {
.remove("_SOURCE_REALTIME_TIMESTAMP") .remove("_SOURCE_REALTIME_TIMESTAMP")
.and_then(parse_microseconds); .and_then(parse_microseconds);
// Journald uses syslogd's concept of priority. No idea if this is
// always present, but it's optional in the Stackdriver API, so we just
// omit it if we can't find or parse it.
let severity = record
.remove("PRIORITY")
.and_then(priority_to_severity);
LogEntry { LogEntry {
payload, payload,
timestamp, timestamp,
@ -389,6 +421,7 @@ impl From<JournalRecord> for LogEntry {
"host": hostname, "host": hostname,
"unit": unit.unwrap_or_else(|| "syslog".into()), "unit": unit.unwrap_or_else(|| "syslog".into()),
}), }),
severity,
} }
} }
} }

View file

@ -8,7 +8,8 @@ fn test_text_entry_serialization() {
timestamp: None, timestamp: None,
payload: Payload::TextPayload { payload: Payload::TextPayload {
text_payload: "test entry".into(), text_payload: "test entry".into(),
} },
severity: None,
}; };
let expected = "{\"labels\":null,\"textPayload\":\"test entry\"}"; let expected = "{\"labels\":null,\"textPayload\":\"test entry\"}";
@ -26,7 +27,8 @@ fn test_json_entry_serialization() {
json_payload: json!({ json_payload: json!({
"message": "JSON test" "message": "JSON test"
}) })
} },
severity: None,
}; };
let expected = "{\"labels\":null,\"jsonPayload\":{\"message\":\"JSON test\"}}"; let expected = "{\"labels\":null,\"jsonPayload\":{\"message\":\"JSON test\"}}";