Skip to content

Commit

Permalink
Merge branch '2018' into master
Browse files Browse the repository at this point in the history
Closes #17
  • Loading branch information
Roguelazer committed Sep 25, 2020
2 parents c2c9479 + acd007c commit b942bf5
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 216 deletions.
7 changes: 3 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
[package]
name = "syslog_rfc5424"
version = "0.6.1"
version = "0.7.0"
authors = ["James Brown <[email protected]>"]
description = "Parser for RFC5424 (IETF-format) syslog messages"
documentation = "https://docs.rs/syslog_rfc5424/"
homepage = "https://github.com/Roguelazer/rust-syslog-rfc5424"
repository = "https://github.com/Roguelazer/rust-syslog-rfc5424"
license = "ISC"
readme = "README.md"
edition = "2018"

[dependencies]
time = "^0.1"
# this should be a dev dependency, but there's some kind of issue with
# dev-only macro crates
assert_matches = "1.0"
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }
thiserror = "1.0"

[dev-dependencies]
timeit = "0.1"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ This module implements an [RFC 5424](https://tools.ietf.org/html/rfc5424) IETF S

This tool supports serializing the parsed messages using serde if it's built with the `serde-serialize` feature.


This library is licensed under the ISC license, a copy of which can be found in [LICENSE.txt](LICENSE.txt)

The minimum supported Rust version for this library is 1.34.

## Performance

On a recent system<sup>[1](#sysfootnote)</sup>, a release build takes approximately 8µs to parse an average message and approximately 300ns to parse the smallest legal message. Debug timings are a bit worse -- about 60µs for an average message and about 8µs for the minimal message. A single-threaded Syslog server should be able to parse at least 100,000 messages/s, as long as you run a separate thread for the parser.
Expand Down
81 changes: 49 additions & 32 deletions src/facility.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#[cfg(feature="serde-serialize")]
use serde::{Serializer, Serialize};
#[cfg(feature = "serde-serialize")]
use serde::{Serialize, Serializer};

use std::convert::TryFrom;

use thiserror::Error;

#[derive(Copy,Clone,Debug,PartialEq, Eq, Ord, PartialOrd)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
#[allow(non_camel_case_types)]
/// Syslog facilities. Taken From RFC 5424, but I've heard that some platforms mix these around.
/// Names are from Linux.
Expand Down Expand Up @@ -32,36 +36,51 @@ pub enum SyslogFacility {
LOG_LOCAL7 = 23,
}

#[derive(Debug, Error)]
pub enum SyslogFacilityError {
#[error("integer does not correspond to a known facility")]
InvalidInteger,
}

impl TryFrom<i32> for SyslogFacility {
type Error = SyslogFacilityError;

#[inline(always)]
fn try_from(i: i32) -> Result<SyslogFacility, Self::Error> {
Ok(match i {
0 => SyslogFacility::LOG_KERN,
1 => SyslogFacility::LOG_USER,
2 => SyslogFacility::LOG_MAIL,
3 => SyslogFacility::LOG_DAEMON,
4 => SyslogFacility::LOG_AUTH,
5 => SyslogFacility::LOG_SYSLOG,
6 => SyslogFacility::LOG_LPR,
7 => SyslogFacility::LOG_NEWS,
8 => SyslogFacility::LOG_UUCP,
9 => SyslogFacility::LOG_CRON,
10 => SyslogFacility::LOG_AUTHPRIV,
11 => SyslogFacility::LOG_FTP,
12 => SyslogFacility::LOG_NTP,
13 => SyslogFacility::LOG_AUDIT,
14 => SyslogFacility::LOG_ALERT,
15 => SyslogFacility::LOG_CLOCKD,
16 => SyslogFacility::LOG_LOCAL0,
17 => SyslogFacility::LOG_LOCAL1,
18 => SyslogFacility::LOG_LOCAL2,
19 => SyslogFacility::LOG_LOCAL3,
20 => SyslogFacility::LOG_LOCAL4,
21 => SyslogFacility::LOG_LOCAL5,
22 => SyslogFacility::LOG_LOCAL6,
23 => SyslogFacility::LOG_LOCAL7,
_ => return Err(SyslogFacilityError::InvalidInteger),
})
}
}

impl SyslogFacility {
/// Convert an int (as used in the wire serialization) into a `SyslogFacility`
pub(crate) fn from_int(i: i32) -> Option<Self> {
match i {
0 => Some(SyslogFacility::LOG_KERN),
1 => Some(SyslogFacility::LOG_USER),
2 => Some(SyslogFacility::LOG_MAIL),
3 => Some(SyslogFacility::LOG_DAEMON),
4 => Some(SyslogFacility::LOG_AUTH),
5 => Some(SyslogFacility::LOG_SYSLOG),
6 => Some(SyslogFacility::LOG_LPR),
7 => Some(SyslogFacility::LOG_NEWS),
8 => Some(SyslogFacility::LOG_UUCP),
9 => Some(SyslogFacility::LOG_CRON),
10 => Some(SyslogFacility::LOG_AUTHPRIV),
11 => Some(SyslogFacility::LOG_FTP),
12 => Some(SyslogFacility::LOG_NTP),
13 => Some(SyslogFacility::LOG_AUDIT),
14 => Some(SyslogFacility::LOG_ALERT),
15 => Some(SyslogFacility::LOG_CLOCKD),
16 => Some(SyslogFacility::LOG_LOCAL0),
17 => Some(SyslogFacility::LOG_LOCAL1),
18 => Some(SyslogFacility::LOG_LOCAL2),
19 => Some(SyslogFacility::LOG_LOCAL3),
20 => Some(SyslogFacility::LOG_LOCAL4),
21 => Some(SyslogFacility::LOG_LOCAL5),
22 => Some(SyslogFacility::LOG_LOCAL6),
23 => Some(SyslogFacility::LOG_LOCAL7),
_ => None,
}
Self::try_from(i).ok()
}

/// Convert a syslog facility into a unique string representation
Expand Down Expand Up @@ -95,15 +114,13 @@ impl SyslogFacility {
}
}


#[cfg(feature = "serde-serialize")]
impl Serialize for SyslogFacility {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
ser.serialize_str(self.as_str())
}
}


#[cfg(test)]
mod tests {
use super::SyslogFacility;
Expand Down
15 changes: 4 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,17 @@
//! message. Rust doesn't have a convenient way to only treat *some* of a buffer as utf-8,
//! so I'm just not supporting that. Most "real" syslog servers barf on it anway.
//!
#[cfg(test)]
extern crate assert_matches;
extern crate time;
#[cfg(feature = "serde-serialize")]
extern crate serde;
#[cfg(feature = "serde-serialize")]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde-serialize")]
extern crate serde_json;

pub mod message;
mod severity;
mod facility;
pub mod message;
pub mod parser;
mod severity;

pub use severity::SyslogSeverity;
pub use facility::SyslogFacility;
pub use severity::SyslogSeverity;

pub use parser::parse_message;
pub use message::SyslogMessage;
pub use parser::parse_message;
89 changes: 46 additions & 43 deletions src/message.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//! In-memory representation of a single Syslog message.
use std::string::String;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::convert::Into;
use std::ops;
use std::cmp::Ordering;
use std::str::FromStr;
use std::string::String;

#[cfg(feature="serde-serialize")]
use serde::{Serializer, Serialize};
#[cfg(feature = "serde-serialize")]
use serde::{Serialize, Serializer};

#[allow(non_camel_case_types)]
pub type time_t = i64;
Expand All @@ -17,19 +17,17 @@ pub type pid_t = i32;
#[allow(non_camel_case_types)]
pub type msgid_t = String;

use severity;
use facility;
use parser;

use crate::facility;
use crate::parser;
use crate::severity;

#[derive(Clone,Debug,PartialEq,Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
/// `ProcID`s are usually numeric PIDs; however, on some systems, they may be something else
pub enum ProcId {
PID(pid_t),
Name(String),
}


impl PartialOrd for ProcId {
fn partial_cmp(&self, other: &ProcId) -> Option<Ordering> {
match (self, other) {
Expand All @@ -40,7 +38,6 @@ impl PartialOrd for ProcId {
}
}


#[cfg(feature = "serde-serialize")]
impl Serialize for ProcId {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
Expand All @@ -55,11 +52,9 @@ pub type SDIDType = String;
pub type SDParamIDType = String;
pub type SDParamValueType = String;


pub type StructuredDataElement = BTreeMap<SDParamIDType, SDParamValueType>;


#[derive(Clone,Debug,PartialEq,Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
/// Container for the `StructuredData` component of a syslog message.
///
/// This is a map from `SD_ID` to pairs of `SD_ParamID`, `SD_ParamValue`
Expand All @@ -81,7 +76,6 @@ impl ops::Deref for StructuredData {
}
}


#[cfg(feature = "serde-serialize")]
impl Serialize for StructuredData {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
Expand All @@ -91,29 +85,37 @@ impl Serialize for StructuredData {

impl StructuredData {
pub fn new_empty() -> Self {
StructuredData { elements: BTreeMap::new() }
StructuredData {
elements: BTreeMap::new(),
}
}

/// Insert a new (sd_id, sd_param_id) -> sd_value mapping into the StructuredData
pub fn insert_tuple<SI, SPI, SPV>(&mut self,
sd_id: SI,
sd_param_id: SPI,
sd_param_value: SPV)
-> ()
where SI: Into<SDIDType>,
SPI: Into<SDParamIDType>,
SPV: Into<SDParamValueType>
pub fn insert_tuple<SI, SPI, SPV>(
&mut self,
sd_id: SI,
sd_param_id: SPI,
sd_param_value: SPV,
) -> ()
where
SI: Into<SDIDType>,
SPI: Into<SDParamIDType>,
SPV: Into<SDParamValueType>,
{
let sub_map = self.elements.entry(sd_id.into()).or_insert_with(BTreeMap::new);
let sub_map = self
.elements
.entry(sd_id.into())
.or_insert_with(BTreeMap::new);
sub_map.insert(sd_param_id.into(), sd_param_value.into());
}

/// Lookup by SDID, SDParamID pair
pub fn find_tuple<'b>(&'b self,
sd_id: &str,
sd_param_id: &str)
-> Option<&'b SDParamValueType> {
// TODO: use traits to make these based on the public types isntead of &str
pub fn find_tuple<'b>(
&'b self,
sd_id: &str,
sd_param_id: &str,
) -> Option<&'b SDParamValueType> {
// TODO: use traits to make these based on the public types instead of &str
if let Some(sub_map) = self.elements.get(sd_id) {
if let Some(value) = sub_map.get(sd_param_id) {
Some(value)
Expand Down Expand Up @@ -141,9 +143,8 @@ impl StructuredData {
}
}


#[cfg_attr(feature = "serde-serialize", derive(Serialize))]
#[derive(Clone,Debug,PartialEq,Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
/// A RFC5424-protocol syslog message
pub struct SyslogMessage {
pub severity: severity::SyslogSeverity,
Expand All @@ -159,7 +160,6 @@ pub struct SyslogMessage {
pub msg: String,
}


impl FromStr for SyslogMessage {
type Err = parser::ParseErr;

Expand All @@ -171,17 +171,16 @@ impl FromStr for SyslogMessage {
}
}


#[cfg(test)]
mod tests {
#[cfg(feature = "serde-serialize")]
use serde_json;
use super::StructuredData;
use super::SyslogMessage;
#[cfg(feature="serde-serialize")]
use severity::SyslogSeverity::*;
#[cfg(feature="serde-serialize")]
use facility::SyslogFacility::*;
#[cfg(feature = "serde-serialize")]
use crate::facility::SyslogFacility::*;
#[cfg(feature = "serde-serialize")]
use crate::severity::SyslogSeverity::*;
#[cfg(feature = "serde-serialize")]
use serde_json;

#[test]
fn test_structured_data_basic() {
Expand All @@ -200,8 +199,10 @@ mod tests {
s.insert_tuple("foo", "baz", "bar");
s.insert_tuple("faa", "bar", "baz");
let encoded = serde_json::to_string(&s).expect("Should encode to JSON");
assert_eq!(encoded,
r#"{"faa":{"bar":"baz"},"foo":{"bar":"baz","baz":"bar"}}"#);
assert_eq!(
encoded,
r#"{"faa":{"bar":"baz"},"foo":{"bar":"baz","baz":"bar"}}"#
);
}

#[cfg(feature = "serde-serialize")]
Expand Down Expand Up @@ -241,7 +242,9 @@ mod tests {

#[test]
fn test_fromstr() {
let msg = "<1>1 1985-04-12T23:20:50.52Z host - - - -".parse::<SyslogMessage>().expect("Should parse empty message");
let msg = "<1>1 1985-04-12T23:20:50.52Z host - - - -"
.parse::<SyslogMessage>()
.expect("Should parse empty message");
assert_eq!(msg.timestamp, Some(482196050));
}
}
Loading

0 comments on commit b942bf5

Please sign in to comment.