diff --git a/Cargo.toml b/Cargo.toml index 72fdbf2..b2e7782 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,10 @@ license = "Apache-2.0" repository = "https://github.com/fast/fasyslog" rust-version = "1.75.0" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [dependencies] jiff = { version = "0.1.14" } diff --git a/README.md b/README.md index 6e1c5cf..dca33f2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,60 @@ -# Fasyslog +# Fasyslog: A fast syslog client written in Rust -A fast syslog client written in Rust. +[![Crates.io][crates-badge]][crates-url] +[![Documentation][docs-badge]][docs-url] +[![MSRV 1.75][msrv-badge]](https://www.whatrustisit.com) +[![Apache 2.0 licensed][license-badge]][license-url] +[![Build Status][actions-badge]][actions-url] + +[crates-badge]: https://img.shields.io/crates/v/fasyslog.svg + +[crates-url]: https://crates.io/crates/fasyslog + +[docs-badge]: https://docs.rs/fasyslog/badge.svg + +[msrv-badge]: https://img.shields.io/badge/MSRV-1.75-green?logo=rust + +[docs-url]: https://docs.rs/fasyslog + +[license-badge]: https://img.shields.io/crates/l/fasyslog + +[license-url]: LICENSE + +[actions-badge]: https://github.com/fast/fasyslog/workflows/CI/badge.svg + +[actions-url]:https://github.com/fast/fasyslog/actions?query=workflow%3ACI + +## Description + +Client library written in Rust to send messages to a Syslog server. Support implementations: + +* RFC-3164 Formatter: [The BSD syslog Protocol](http://tools.ietf.org/html/rfc3164) +* RFC-5424 Formatter: [The Syslog Protocol](http://tools.ietf.org/html/rfc5424) +* `UdpSender`: [RFC 5426 - Transmission of Syslog Messages over UDP](http://tools.ietf.org/html/rfc5426) +* `TcpSender`: [RFC 6587 - Transmission of Syslog Messages over TCP](http://tools.ietf.org/html/rfc6587) + +## Getting Started + +Add `fasyslog` to your `Cargo.toml`: + +```shell +cargo add fasyslog +``` + +## Example + +Check the [examples](examples) directory for more examples. + +## Documentation + +Read the online documents at https://docs.rs/logforth. + +## Minimum Supported Rust Version (MSRV) + +This crate is built against the latest stable release, and its minimum supported rustc version is 1.75.0. + +The policy is that the minimum Rust version required to use this crate can be increased in minor version updates. For example, if Fasyslog 1.0 requires Rust 1.20.0, then Fasyslog 1.0.z for all values of z will also require Rust 1.20.0 or newer. However, Fasyslog 1.y for y > 0 may require a newer minimum version of Rust. + +## License + +This project is licensed under [Apache License, Version 2.0](LICENSE). diff --git a/examples/shared/mod.rs b/examples/shared/mod.rs deleted file mode 100644 index c06566f..0000000 --- a/examples/shared/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2024 FastLabs Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use fasyslog::sender::SyslogSender; -use fasyslog::Severity; - -/// Shared logic to send syslog messages. -pub fn send_syslog_message(mut sender: SyslogSender) { - let mut generator = names::Generator::default(); - for _ in 0..100 { - let name = generator.next().unwrap(); - let message = format!("Hello, {name}!"); - sender.send_rfc3164(Severity::ERROR, message).unwrap(); - } - sender.flush().unwrap(); -} diff --git a/examples/tcp_sender.rs b/examples/tcp_sender.rs index 8e800a9..617bf0d 100644 --- a/examples/tcp_sender.rs +++ b/examples/tcp_sender.rs @@ -12,9 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod shared; +use fasyslog::Severity; fn main() { - let sender = fasyslog::sender::tcp_well_known().unwrap(); - shared::send_syslog_message(sender); + let mut sender = fasyslog::sender::tcp_well_known().unwrap(); + let mut generator = names::Generator::default(); + for _ in 0..100 { + let name = generator.next().unwrap(); + let message = format!("Hello, {name}!"); + let mut element = fasyslog::SDElement::new("exampleSDID@32473").unwrap(); + element.add_param("iut", "3").unwrap(); + sender + .send_rfc5424(Severity::ERROR, Some("TCPIN"), vec![element], message) + .unwrap(); + } + sender.flush().unwrap(); } diff --git a/examples/udp_sender.rs b/examples/udp_sender.rs index db3ed78..ac2f776 100644 --- a/examples/udp_sender.rs +++ b/examples/udp_sender.rs @@ -12,9 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod shared; +use fasyslog::Severity; fn main() { - let sender = fasyslog::sender::udp_well_known().unwrap(); - shared::send_syslog_message(sender); + let mut sender = fasyslog::sender::udp_well_known().unwrap(); + let mut generator = names::Generator::default(); + for _ in 0..100 { + let name = generator.next().unwrap(); + let message = format!("Hello, {name}!"); + let mut element = fasyslog::SDElement::new("exampleSDID@16253").unwrap(); + element.add_param("jno", "sul").unwrap(); + sender + .send_rfc5424(Severity::ERROR, Some("UDPIN"), vec![element], message) + .unwrap(); + } } diff --git a/examples/unix_sender.rs b/examples/unix_sender.rs index 44dc33d..6b27f47 100644 --- a/examples/unix_sender.rs +++ b/examples/unix_sender.rs @@ -12,12 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod shared; +use fasyslog::Severity; #[cfg(unix)] fn main() { - let sender = fasyslog::sender::unix_well_known().unwrap(); - shared::send_syslog_message(sender); + let mut sender = fasyslog::sender::unix_well_known().unwrap(); + let mut generator = names::Generator::default(); + for _ in 0..100 { + let name = generator.next().unwrap(); + let message = format!("Hello, {name}!"); + sender.send_rfc3164(Severity::ERROR, message).unwrap(); + } + sender.flush().unwrap(); } #[cfg(not(unix))] diff --git a/src/format.rs b/src/format.rs index e4e440a..fe091f7 100644 --- a/src/format.rs +++ b/src/format.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Format Syslog messages according to the referred standards. + use std::fmt; use std::fmt::Formatter; @@ -123,28 +125,6 @@ impl SyslogContext { message, } } - - /// Format the Syslog message with the given severity as defined in RFC-5425. - pub fn format_rfc5425( - &self, - severity: Severity, - msgid: Option, - elements: Vec, - message: Option, - ) -> RFC5425Formatter - where - S: Into, - M: fmt::Display, - { - let msgid = msgid.map(|s| s.into()); - RFC5425Formatter { - context: self, - severity, - msgid, - elements, - message, - } - } } /// Shared format logic for nullable value. @@ -188,6 +168,9 @@ where } } +/// Format the Syslog message as [RFC 5424] (The Syslog Protocol) +/// +/// [RFC 5424]: https://tools.ietf.org/html/rfc5424 #[derive(Debug)] pub struct RFC5424Formatter<'a, M> { context: &'a SyslogContext, @@ -232,35 +215,3 @@ where Ok(()) } } - -#[derive(Debug)] -pub struct RFC5425Formatter<'a, M> { - context: &'a SyslogContext, - severity: Severity, - msgid: Option, - elements: Vec, - message: Option, -} - -impl fmt::Display for RFC5425Formatter<'_, M> -where - M: fmt::Display, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - // The RFC-5425 format is defined as 'MSG-LEN SP SYSLOG-MSG', - // where SYSLOG-MSG is defined as in RFC-5424. - // https://datatracker.ietf.org/doc/html/rfc5425#section-4.3 - let rfc5424_payload = format!( - "{}", - RFC5424Formatter { - context: self.context, - severity: self.severity, - msgid: self.msgid.clone(), - elements: self.elements.clone(), - message: self.message.as_ref() - } - ); - let msg_len = rfc5424_payload.len(); - write!(f, "{msg_len} {rfc5424_payload}") - } -} diff --git a/src/internal.rs b/src/internal.rs index c074cc6..2820ef0 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -32,11 +32,6 @@ pub(crate) fn hostname() -> Option { // not the name of the cluster virtual server. pub const COMPUTER_NAME_PHYSICAL_DNS_HOSTNAME: i32 = 5; - // The DNS host name of the local computer. If the local computer is a node - // in a cluster, lpBuffer receives the DNS host name of the local computer, - // not the name of the cluster virtual server. - pub const COMPUTER_NAME_PHYSICAL_DNS_HOSTNAME: i32 = 5; - // https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw ::windows_targets::link!("kernel32.dll" "system" fn GetComputerNameExW(nametype: i32, lpbuffer: *mut u16, nsize: *mut u32) -> i32); diff --git a/src/lib.rs b/src/lib.rs index f75a910..373ea76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,13 +12,51 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod facility; -pub use facility::*; +#![cfg_attr(docsrs, feature(doc_auto_cfg))] -mod format; -pub use format::*; +//! `fasyslog` is a fast syslog client written in Rust. +//! +//! # Overview +//! +//! This crate provides facilities to send log messages via syslog. Support implementations: +//! +//! * [RFC-3164 Formatter]: [The BSD syslog Protocol](http://tools.ietf.org/html/rfc3164) +//! * [RFC-5424 Formatter]: [The Syslog Protocol](http://tools.ietf.org/html/rfc5424) +//! * [`UdpSender`]: [RFC 5426 - Transmission of Syslog Messages over UDP](http://tools.ietf.org/html/rfc5426) +//! * [`TcpSender`]: [RFC 6587 - Transmission of Syslog Messages over TCP](http://tools.ietf.org/html/rfc6587) +//! * (unix only) Unix domain socket sender (datagram or stream) +//! +//! [RFC-3164 Formatter]: format::RFC3164Formatter +//! [RFC-5424 Formatter]: format::RFC5424Formatter +//! [`UdpSender`]: sender::UdpSender +//! [`TcpSender`]: sender::TcpSender +//! +//! # Example +//! +//! Send a message to a remote syslog server: +//! +//! ```rust, no_run +//! let mut sender = fasyslog::sender::tcp_well_known().unwrap(); +//! sender +//! .send_rfc3164(fasyslog::Severity::INFORMATIONAL, "Hello, syslog!") +//! .unwrap(); +//! sender.flush().unwrap(); +//! +//! let mut element = fasyslog::SDElement::new("exampleSDID@32473").unwrap(); +//! element.add_param("iut", "3").unwrap(); +//! sender +//! .send_rfc5424( +//! fasyslog::Severity::NOTICE, +//! Some("TCPIN"), +//! vec![element], +//! "Hello, syslog!", +//! ) +//! .unwrap(); +//! sender.flush().unwrap(); +//! ``` -pub mod sender; +mod facility; +pub use facility::*; mod severity; pub use severity::*; @@ -26,4 +64,7 @@ pub use severity::*; mod structured_data; pub use structured_data::*; +pub mod format; +pub mod sender; + mod internal; diff --git a/src/sender/mod.rs b/src/sender/mod.rs index b0c1005..3591351 100644 --- a/src/sender/mod.rs +++ b/src/sender/mod.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Send syslog messages to a syslog server. + use std::fmt; use std::io; @@ -29,56 +31,7 @@ pub use tcp::*; mod udp; pub use udp::*; -#[macro_export] -macro_rules! impl_syslog_sender { - ($sender:ident, $context:ident, $writer:ident) => { - impl $sender { - /// Send a message with the given severity as defined in RFC-3164. - pub fn send_rfc3164( - &mut self, - severity: $crate::Severity, - message: M, - ) -> std::io::Result<()> { - use std::io::Write; - let message = &self.$context.format_rfc3164(severity, Some(message)); - write!(&mut self.$writer, "{message}") - } - - /// Send a message with the given severity as defined in RFC-5424. - pub fn send_rfc5424, M: std::fmt::Display>( - &mut self, - severity: $crate::Severity, - msgid: Option, - elements: Vec<$crate::SDElement>, - message: M, - ) -> std::io::Result<()> { - use std::io::Write; - let message = - &self - .$context - .format_rfc5424(severity, msgid, elements, Some(message)); - write!(&mut self.$writer, "{message}") - } - - /// Send a message with the given severity as defined in RFC-5425. - pub fn send_rfc5425, M: std::fmt::Display>( - &mut self, - severity: $crate::Severity, - msgid: Option, - elements: Vec<$crate::SDElement>, - message: M, - ) -> std::io::Result<()> { - use std::io::Write; - let message = - &self - .$context - .format_rfc5425(severity, msgid, elements, Some(message)); - write!(&mut self.$writer, "{message}") - } - } - }; -} - +/// Static dispatch for the different sender types. #[derive(Debug)] pub enum SyslogSender { Tcp(TcpSender), @@ -128,37 +81,15 @@ impl SyslogSender { } } - /// Send a message with the given severity as defined in RFC-5425. - pub fn send_rfc5425, M: fmt::Display>( - &mut self, - severity: Severity, - msgid: Option, - elements: Vec, - message: M, - ) -> io::Result<()> { - match self { - SyslogSender::Tcp(sender) => sender.send_rfc5425(severity, msgid, elements, message), - SyslogSender::Udp(sender) => sender.send_rfc5425(severity, msgid, elements, message), - #[cfg(unix)] - SyslogSender::UnixDatagram(sender) => { - sender.send_rfc5425(severity, msgid, elements, message) - } - #[cfg(unix)] - SyslogSender::UnixStream(sender) => { - sender.send_rfc5425(severity, msgid, elements, message) - } - } - } - /// Flush the underlying writer if needed. pub fn flush(&mut self) -> io::Result<()> { match self { SyslogSender::Tcp(sender) => sender.flush(), - SyslogSender::UnixStream(sender) => sender.flush(), - #[cfg(unix)] SyslogSender::Udp(_) => Ok(()), #[cfg(unix)] SyslogSender::UnixDatagram(_) => Ok(()), + #[cfg(unix)] + SyslogSender::UnixStream(sender) => sender.flush(), } } } diff --git a/src/sender/tcp.rs b/src/sender/tcp.rs index e35ec80..04157af 100644 --- a/src/sender/tcp.rs +++ b/src/sender/tcp.rs @@ -18,22 +18,22 @@ use std::io::BufWriter; use std::net::TcpStream; use std::net::ToSocketAddrs; -use crate::impl_syslog_sender; -use crate::sender::SyslogSender; -use crate::SyslogContext; +use crate::format::SyslogContext; +use crate::SDElement; +use crate::Severity; /// Create a TCP sender that sends messages to the well-known port (601). /// /// See also [RFC-3195] §9.2 The System (Well-Known) TCP port number for syslog-conn. /// /// [RFC-3195]: https://datatracker.ietf.org/doc/html/rfc3195#section-9.2 -pub fn tcp_well_known() -> io::Result { +pub fn tcp_well_known() -> io::Result { tcp("127.0.0.1:601") } /// Create a TCP sender that sends messages to the given address. -pub fn tcp(addr: A) -> io::Result { - TcpSender::connect(addr).map(SyslogSender::Tcp) +pub fn tcp(addr: A) -> io::Result { + TcpSender::connect(addr) } /// A syslog sender that sends messages to a TCP socket. @@ -74,11 +74,35 @@ impl TcpSender { &mut self.context } + /// Send a message with the given severity as defined in RFC-3164. + pub fn send_rfc3164( + &mut self, + severity: Severity, + message: M, + ) -> io::Result<()> { + use std::io::Write; + let message = self.context.format_rfc3164(severity, Some(message)); + write!(&mut self.writer, "{}{}", message, self.postfix) + } + + /// Send a message with the given severity as defined in RFC-5424. + pub fn send_rfc5424, M: std::fmt::Display>( + &mut self, + severity: Severity, + msgid: Option, + elements: Vec, + message: M, + ) -> io::Result<()> { + use std::io::Write; + let message = self + .context + .format_rfc5424(severity, msgid, elements, Some(message)); + write!(&mut self.writer, "{}{}", message, self.postfix) + } + /// Flush the writer. pub fn flush(&mut self) -> io::Result<()> { use std::io::Write; self.writer.flush() } } - -impl_syslog_sender!(TcpSender, context, writer); diff --git a/src/sender/udp.rs b/src/sender/udp.rs index fc665ac..d469bec 100644 --- a/src/sender/udp.rs +++ b/src/sender/udp.rs @@ -12,33 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::fmt; use std::io; use std::net::ToSocketAddrs; use std::net::UdpSocket; -use crate::impl_syslog_sender; -use crate::sender::SyslogSender; -use crate::SyslogContext; +use crate::format::SyslogContext; +use crate::SDElement; +use crate::Severity; /// Create a UDP sender that sends messages to the well-known port (514). /// /// See also [RFC-3164] §2 Transport Layer Protocol. /// /// [RFC-3164]: https://datatracker.ietf.org/doc/html/rfc3164#section-2 -pub fn udp_well_known() -> io::Result { +pub fn udp_well_known() -> io::Result { udp("0.0.0.0:0", "127.0.0.1:514") } /// Create a UDP sender that sends messages to the given address. -pub fn udp(local: L, remote: R) -> io::Result { - UdpSender::connect(local, remote).map(SyslogSender::Udp) +pub fn udp(local: L, remote: R) -> io::Result { + UdpSender::connect(local, remote) } /// A syslog sender that sends messages to a UDP socket. #[derive(Debug)] pub struct UdpSender { - socket: UdpSocketWriteAdapter, + socket: UdpSocket, context: SyslogContext, } @@ -48,7 +47,7 @@ impl UdpSender { let socket = UdpSocket::bind(local)?; socket.connect(remote)?; Ok(Self { - socket: UdpSocketWriteAdapter { socket }, + socket, context: SyslogContext::default(), }) } @@ -62,26 +61,30 @@ impl UdpSender { pub fn mut_context(&mut self) -> &mut SyslogContext { &mut self.context } -} - -impl_syslog_sender!(UdpSender, context, socket); - -#[derive(Debug)] -struct UdpSocketWriteAdapter { - socket: UdpSocket, -} - -impl io::Write for UdpSocketWriteAdapter { - fn write(&mut self, bytes: &[u8]) -> io::Result { - self.socket.send(bytes) - } - fn flush(&mut self) -> io::Result<()> { + /// Send a message with the given severity as defined in RFC-3164. + pub fn send_rfc3164( + &mut self, + severity: Severity, + message: M, + ) -> io::Result<()> { + let message = self.context.format_rfc3164(severity, Some(message)); + self.socket.send(message.to_string().as_bytes())?; Ok(()) } - // HACK - without this method, the 'write!' macro will be fragmented into multiple write calls - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - self.write_all(fmt.to_string().as_bytes()) + /// Send a message with the given severity as defined in RFC-5424. + pub fn send_rfc5424, M: std::fmt::Display>( + &mut self, + severity: Severity, + msgid: Option, + elements: Vec, + message: M, + ) -> io::Result<()> { + let message = self + .context + .format_rfc5424(severity, msgid, elements, Some(message)); + self.socket.send(message.to_string().as_bytes())?; + Ok(()) } } diff --git a/src/sender/unix.rs b/src/sender/unix.rs index 830eb62..72e8bec 100644 --- a/src/sender/unix.rs +++ b/src/sender/unix.rs @@ -12,25 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::fmt; use std::io; use std::io::BufWriter; use std::os::unix::net::UnixDatagram; use std::os::unix::net::UnixStream; use std::path::Path; -use crate::impl_syslog_sender; +use crate::format::SyslogContext; use crate::sender::SyslogSender; -use crate::SyslogContext; +use crate::SDElement; +use crate::Severity; /// Create a Unix datagram sender that sends messages to the given path. -pub fn unix_datagram(path: impl AsRef) -> io::Result { - UnixDatagramSender::connect(path).map(SyslogSender::UnixDatagram) +pub fn unix_datagram(path: impl AsRef) -> io::Result { + UnixDatagramSender::connect(path) } /// Create a Unix stream sender that sends messages to the given path. -pub fn unix_stream(path: impl AsRef) -> io::Result { - UnixStreamSender::connect(path).map(SyslogSender::UnixStream) +pub fn unix_stream(path: impl AsRef) -> io::Result { + UnixStreamSender::connect(path) } /// Create a Unix sender that sends messages to the given path. @@ -40,9 +40,9 @@ pub fn unix(path: impl AsRef) -> io::Result { const EPROTOTYPE: i32 = nix::errno::Errno::EPROTOTYPE as i32; let path = path.as_ref(); match unix_datagram(path) { - Ok(sender) => Ok(sender), + Ok(sender) => Ok(SyslogSender::UnixDatagram(sender)), Err(err) => match err.raw_os_error() { - Some(EPROTOTYPE) => unix_stream(path), + Some(EPROTOTYPE) => unix_stream(path).map(SyslogSender::UnixStream), _ => Err(err), }, } @@ -70,7 +70,7 @@ pub fn unix_well_known() -> io::Result { /// `/dev/log`, `/var/run/syslog`, or `/var/run/log`. #[derive(Debug)] pub struct UnixDatagramSender { - socket: UnixDatagramWriteAdapter, + socket: UnixDatagram, context: SyslogContext, } @@ -80,7 +80,7 @@ impl UnixDatagramSender { let socket = UnixDatagram::unbound()?; socket.connect(path)?; Ok(Self { - socket: UnixDatagramWriteAdapter { socket }, + socket, context: SyslogContext::default(), }) } @@ -94,27 +94,31 @@ impl UnixDatagramSender { pub fn mut_context(&mut self) -> &mut SyslogContext { &mut self.context } -} - -impl_syslog_sender!(UnixDatagramSender, context, socket); - -#[derive(Debug)] -struct UnixDatagramWriteAdapter { - socket: UnixDatagram, -} - -impl io::Write for UnixDatagramWriteAdapter { - fn write(&mut self, bytes: &[u8]) -> io::Result { - self.socket.send(bytes) - } - fn flush(&mut self) -> io::Result<()> { + /// Send a message with the given severity as defined in RFC-3164. + pub fn send_rfc3164( + &mut self, + severity: Severity, + message: M, + ) -> io::Result<()> { + let message = self.context.format_rfc3164(severity, Some(message)); + self.socket.send(message.to_string().as_bytes())?; Ok(()) } - // HACK - without this method, the 'write!' macro will be fragmented into multiple write calls - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - self.write_all(fmt.to_string().as_bytes()) + /// Send a message with the given severity as defined in RFC-5424. + pub fn send_rfc5424, M: std::fmt::Display>( + &mut self, + severity: Severity, + msgid: Option, + elements: Vec, + message: M, + ) -> io::Result<()> { + let message = self + .context + .format_rfc5424(severity, msgid, elements, Some(message)); + self.socket.send(message.to_string().as_bytes())?; + Ok(()) } } @@ -148,11 +152,39 @@ impl UnixStreamSender { &mut self.context } + /// Send a message with the given severity as defined in RFC-3164. + pub fn send_rfc3164( + &mut self, + severity: Severity, + message: M, + ) -> io::Result<()> { + use std::io::Write; + let message = self.context.format_rfc3164(severity, Some(message)); + write!(&mut self.writer, "{}", message)?; + self.writer.write_all(&[0; 1])?; + Ok(()) + } + + /// Send a message with the given severity as defined in RFC-5424. + pub fn send_rfc5424, M: std::fmt::Display>( + &mut self, + severity: Severity, + msgid: Option, + elements: Vec, + message: M, + ) -> io::Result<()> { + use std::io::Write; + let message = self + .context + .format_rfc5424(severity, msgid, elements, Some(message)); + write!(&mut self.writer, "{}", message)?; + self.writer.write_all(&[0; 1])?; + Ok(()) + } + /// Flush the writer. pub fn flush(&mut self) -> io::Result<()> { use std::io::Write; self.writer.flush() } } - -impl_syslog_sender!(UnixStreamSender, context, writer);