move syslog stuff to a submoudle of signal-gateway-bin and make it optional
This commit is contained in:
+12
-117
@@ -2,61 +2,15 @@ use conf::Conf;
|
|||||||
use hyper::service::service_fn;
|
use hyper::service::service_fn;
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use hyper_util::server::conn::auto;
|
use hyper_util::server::conn::auto;
|
||||||
use signal_gateway::{Gateway, GatewayConfig, Level, LogMessage};
|
use signal_gateway::{Gateway, GatewayConfig};
|
||||||
use std::{net::SocketAddr, str::FromStr, sync::Arc, time::Duration};
|
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||||
use syslog_rfc5424::{SyslogMessage, SyslogSeverity};
|
use tokio::net::TcpListener;
|
||||||
use tokio::net::{TcpListener, UdpSocket};
|
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
/// Convert SyslogSeverity to our Level enum
|
mod syslog;
|
||||||
fn severity_to_level(sev: SyslogSeverity) -> Level {
|
use syslog::SyslogUdpConfig;
|
||||||
match sev {
|
|
||||||
SyslogSeverity::SEV_EMERG => Level::EMERGENCY,
|
|
||||||
SyslogSeverity::SEV_ALERT => Level::ALERT,
|
|
||||||
SyslogSeverity::SEV_CRIT => Level::CRITICAL,
|
|
||||||
SyslogSeverity::SEV_ERR => Level::ERROR,
|
|
||||||
SyslogSeverity::SEV_WARNING => Level::WARNING,
|
|
||||||
SyslogSeverity::SEV_NOTICE => Level::NOTICE,
|
|
||||||
SyslogSeverity::SEV_INFO => Level::INFO,
|
|
||||||
SyslogSeverity::SEV_DEBUG => Level::DEBUG,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a SyslogMessage to a LogMessage, extracting structured data for tracing metadata
|
|
||||||
fn syslog_to_log_message(msg: SyslogMessage, sd_id: &str) -> LogMessage {
|
|
||||||
let level = severity_to_level(msg.severity);
|
|
||||||
let mut builder = LogMessage::builder(level, msg.msg);
|
|
||||||
|
|
||||||
if let Some(ts) = msg.timestamp {
|
|
||||||
builder = builder.timestamp(ts);
|
|
||||||
}
|
|
||||||
if let Some(nanos) = msg.timestamp_nanos {
|
|
||||||
builder = builder.timestamp_nanos(nanos);
|
|
||||||
}
|
|
||||||
if let Some(hostname) = msg.hostname {
|
|
||||||
builder = builder.hostname(hostname);
|
|
||||||
}
|
|
||||||
if let Some(appname) = msg.appname {
|
|
||||||
builder = builder.appname(appname);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract tracing metadata from structured data
|
|
||||||
if let Some(sd_element) = msg.sd.find_sdid(sd_id) {
|
|
||||||
if let Some(module) = sd_element.get("module") {
|
|
||||||
builder = builder.module_path(module.clone());
|
|
||||||
}
|
|
||||||
if let Some(file) = sd_element.get("file") {
|
|
||||||
builder = builder.file(file.clone());
|
|
||||||
}
|
|
||||||
if let Some(line) = sd_element.get("line") {
|
|
||||||
builder = builder.line(line.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Conf, Debug)]
|
#[derive(Conf, Debug)]
|
||||||
struct Config {
|
struct Config {
|
||||||
@@ -66,12 +20,8 @@ struct Config {
|
|||||||
/// Socket to listen for HTTP requests (GET /health, POST /alert)
|
/// Socket to listen for HTTP requests (GET /health, POST /alert)
|
||||||
#[conf(long, env, default_value = "0.0.0.0:8000")]
|
#[conf(long, env, default_value = "0.0.0.0:8000")]
|
||||||
http_listen_addr: SocketAddr,
|
http_listen_addr: SocketAddr,
|
||||||
/// Socket to listen for UDP messages, in syslog RFC 5424 format
|
#[conf(flatten, prefix)]
|
||||||
#[conf(long, env, default_value = "0.0.0.0:5424")]
|
syslog_udp: Option<SyslogUdpConfig>,
|
||||||
udp_listen_addr: SocketAddr,
|
|
||||||
/// Structured data ID for tracing metadata (module, file, line) in syslog messages
|
|
||||||
#[conf(long, env, default_value = "tracing-meta@64700")]
|
|
||||||
sd_id: String,
|
|
||||||
#[conf(flatten)]
|
#[conf(flatten)]
|
||||||
gateway: GatewayConfig,
|
gateway: GatewayConfig,
|
||||||
}
|
}
|
||||||
@@ -123,9 +73,6 @@ async fn main() {
|
|||||||
let listener = TcpListener::bind(config.http_listen_addr).await.unwrap();
|
let listener = TcpListener::bind(config.http_listen_addr).await.unwrap();
|
||||||
info!("Listening for http on {}", config.http_listen_addr);
|
info!("Listening for http on {}", config.http_listen_addr);
|
||||||
|
|
||||||
let udp_socket = UdpSocket::bind(config.udp_listen_addr).await.unwrap();
|
|
||||||
info!("Listening for udp on {}", config.udp_listen_addr);
|
|
||||||
|
|
||||||
// Listen for ctrl-c
|
// Listen for ctrl-c
|
||||||
let thread_token = token.clone();
|
let thread_token = token.clone();
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
@@ -136,7 +83,11 @@ async fn main() {
|
|||||||
|
|
||||||
// Start the two server tasks
|
// Start the two server tasks
|
||||||
let _http_task = start_http_task(listener, gateway.clone());
|
let _http_task = start_http_task(listener, gateway.clone());
|
||||||
let _udp_task = start_udp_task(udp_socket, gateway.clone(), config.sd_id);
|
let _udp_task = if let Some(syslog_udp) = &config.syslog_udp {
|
||||||
|
Some(syslog_udp.start_udp_task(gateway.clone()).await.unwrap())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Run gateway task and block on it returning. Note that it exits if the token is canceled.
|
// Run gateway task and block on it returning. Note that it exits if the token is canceled.
|
||||||
gateway.run().await;
|
gateway.run().await;
|
||||||
@@ -178,59 +129,3 @@ fn start_http_task(listener: TcpListener, gateway: Arc<Gateway>) -> tokio::task:
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_udp_task(
|
|
||||||
udp_socket: UdpSocket,
|
|
||||||
gateway: Arc<Gateway>,
|
|
||||||
sd_id: String,
|
|
||||||
) -> tokio::task::JoinHandle<()> {
|
|
||||||
// Loop waiting for UDP syslog messages
|
|
||||||
tokio::task::spawn(async move {
|
|
||||||
let mut buf = vec![0u8; 8192];
|
|
||||||
loop {
|
|
||||||
let Ok((len, _addr)) = udp_socket
|
|
||||||
.recv_from(&mut buf)
|
|
||||||
.await
|
|
||||||
.inspect_err(|err| error!("Error receiving UDP packet: {err}"))
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(text) = str::from_utf8(&buf[0..len])
|
|
||||||
.inspect_err(|err| error!("UDP packet was not utf8: {err}"))
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(syslog_msg) = SyslogMessage::from_str(text)
|
|
||||||
.inspect_err(|err| error!("UDP packet was not valid syslog: {err}:\n{text}"))
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let log_msg = syslog_to_log_message(syslog_msg, &sd_id);
|
|
||||||
gateway.handle_log_message(log_msg).await;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_syslog_parsing() {
|
|
||||||
SyslogMessage::from_str(
|
|
||||||
"<12>1 2025-11-08T02:24:10.815221698+00:00 ip-172-31-5-8 app 92748 - - Dropped 3/4 reports",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
SyslogMessage::from_str(
|
|
||||||
"<12>1 2025-11-08T02:24:10.815221698+00:00 ip-172-31-5-8 app 92748 - - Dropped 3/4 reports",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
SyslogMessage::from_str(
|
|
||||||
"<12>1 2025-11-08T02:24:10.815+00:00 ip-172-31-5-8 app 92748 - - Dropped 3/4 reports due to staleness"
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
//! Syslog UDP listener for receiving RFC 5424 syslog messages
|
||||||
|
|
||||||
|
use conf::Conf;
|
||||||
|
use signal_gateway::{Gateway, Level, LogMessage};
|
||||||
|
use std::{net::SocketAddr, str::FromStr, sync::Arc};
|
||||||
|
use syslog_rfc5424::{SyslogMessage, SyslogSeverity};
|
||||||
|
use tokio::net::UdpSocket;
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
/// Configuration for the syslog UDP listener
|
||||||
|
#[derive(Conf, Debug)]
|
||||||
|
pub struct SyslogUdpConfig {
|
||||||
|
/// Socket to listen for UDP messages, in syslog RFC 5424 format
|
||||||
|
#[conf(long, env)]
|
||||||
|
pub listen_addr: SocketAddr,
|
||||||
|
/// Structured data ID for tracing metadata (module, file, line) in syslog messages
|
||||||
|
#[conf(long, env, default_value = "tracing-meta@64700")]
|
||||||
|
pub sd_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyslogUdpConfig {
|
||||||
|
/// Bind a UDP socket and start a background task to handle incoming syslog messages.
|
||||||
|
///
|
||||||
|
/// Returns a join handle for the background task.
|
||||||
|
pub async fn start_udp_task(
|
||||||
|
&self,
|
||||||
|
gateway: Arc<Gateway>,
|
||||||
|
) -> std::io::Result<tokio::task::JoinHandle<()>> {
|
||||||
|
let udp_socket = UdpSocket::bind(self.listen_addr).await?;
|
||||||
|
info!("Listening for syslog UDP on {}", self.listen_addr);
|
||||||
|
|
||||||
|
let sd_id = self.sd_id.clone();
|
||||||
|
|
||||||
|
Ok(tokio::task::spawn(async move {
|
||||||
|
let mut buf = vec![0u8; 8192];
|
||||||
|
loop {
|
||||||
|
let Ok((len, _addr)) = udp_socket
|
||||||
|
.recv_from(&mut buf)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| error!("Error receiving UDP packet: {err}"))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(text) = std::str::from_utf8(&buf[0..len])
|
||||||
|
.inspect_err(|err| error!("UDP packet was not utf8: {err}"))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(syslog_msg) = SyslogMessage::from_str(text)
|
||||||
|
.inspect_err(|err| error!("UDP packet was not valid syslog: {err}:\n{text}"))
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let log_msg = syslog_to_log_message(syslog_msg, &sd_id);
|
||||||
|
gateway.handle_log_message(log_msg).await;
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert SyslogSeverity to our Level enum
|
||||||
|
fn severity_to_level(sev: SyslogSeverity) -> Level {
|
||||||
|
match sev {
|
||||||
|
SyslogSeverity::SEV_EMERG => Level::EMERGENCY,
|
||||||
|
SyslogSeverity::SEV_ALERT => Level::ALERT,
|
||||||
|
SyslogSeverity::SEV_CRIT => Level::CRITICAL,
|
||||||
|
SyslogSeverity::SEV_ERR => Level::ERROR,
|
||||||
|
SyslogSeverity::SEV_WARNING => Level::WARNING,
|
||||||
|
SyslogSeverity::SEV_NOTICE => Level::NOTICE,
|
||||||
|
SyslogSeverity::SEV_INFO => Level::INFO,
|
||||||
|
SyslogSeverity::SEV_DEBUG => Level::DEBUG,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a SyslogMessage to a LogMessage, extracting structured data for tracing metadata
|
||||||
|
fn syslog_to_log_message(msg: SyslogMessage, sd_id: &str) -> LogMessage {
|
||||||
|
let level = severity_to_level(msg.severity);
|
||||||
|
let mut builder = LogMessage::builder(level, msg.msg);
|
||||||
|
|
||||||
|
if let Some(ts) = msg.timestamp {
|
||||||
|
builder = builder.timestamp(ts);
|
||||||
|
}
|
||||||
|
if let Some(nanos) = msg.timestamp_nanos {
|
||||||
|
builder = builder.timestamp_nanos(nanos);
|
||||||
|
}
|
||||||
|
if let Some(hostname) = msg.hostname {
|
||||||
|
builder = builder.hostname(hostname);
|
||||||
|
}
|
||||||
|
if let Some(appname) = msg.appname {
|
||||||
|
builder = builder.appname(appname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract tracing metadata from structured data
|
||||||
|
if let Some(sd_element) = msg.sd.find_sdid(sd_id) {
|
||||||
|
if let Some(module) = sd_element.get("module") {
|
||||||
|
builder = builder.module_path(module.clone());
|
||||||
|
}
|
||||||
|
if let Some(file) = sd_element.get("file") {
|
||||||
|
builder = builder.file(file.clone());
|
||||||
|
}
|
||||||
|
if let Some(line) = sd_element.get("line") {
|
||||||
|
builder = builder.line(line.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_syslog_parsing() {
|
||||||
|
SyslogMessage::from_str(
|
||||||
|
"<12>1 2025-11-08T02:24:10.815221698+00:00 ip-172-31-5-8 app 92748 - - Dropped 3/4 reports",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
SyslogMessage::from_str(
|
||||||
|
"<12>1 2025-11-08T02:24:10.815221698+00:00 ip-172-31-5-8 app 92748 - - Dropped 3/4 reports",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
SyslogMessage::from_str(
|
||||||
|
"<12>1 2025-11-08T02:24:10.815+00:00 ip-172-31-5-8 app 92748 - - Dropped 3/4 reports due to staleness"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user