+TITLE: syslog-tracing

+DESCRIPTION: tracing layer that writes to syslog

+AUTHOR: Michael Herstine

+EMAIL: sp1ff@pobox.com

+DATE: <2022-10-07 Fri 17:11>

+AUTODATE: t

+OPTIONS: toc:nil org-md-headline-style:setext *:t ^:nil

+STARTUP: overview

[[https://github.com/sp1ff/syslog-tracing/tracing-rfc-5424][tracing-rfc-5424]] is a [[https://docs.rs/tracing-subscriber/latest/tracingsubscriber/index.html][tracing-subscriber]] [[https://docs.rs/tracing-subscriber/latest/tracingsubscriber/layer/trait.Layer.html][Layer]] implementation that sends [[https://docs.rs/tracing/latest/tracing/index.html][tracing ]][[https://docs.rs/tracing/latest/tracing/struct.Event.html][Event]]s & [[https://docs.rs/tracing/latest/tracing/struct.Span.html][Span]]s to a [[https://en.wikipedia.org/wiki/Syslog][syslog]] [[https://en.wikipedia.org/wiki/Daemon_(computing)][daemon]].

The [[https://docs.rs/tracing/latest/tracing/index.html][tracing]] crate is a "scoped, structured logging and diagnostics system". It provides a superset of the features offered by logging crates such as [[https://docs.rs/fern/latest/fern/index.html][fern]] and [[https://docs.rs/log4rs/latest/log4rs/][log4rs]] particularly suited to asynchronous programming. Of particular interest is that it makes a very clear distinction between producers of events & their consumers ([[https://docs.rs/tracing/0.1.34/tracing/trait.Subscriber.html][Subscriber]]s, in [[https://docs.rs/tracing/latest/tracing/index.html][tracing]] parlance); so much so that the [[https://docs.rs/tracing/latest/tracing/index.html][tracing]] crate provides no support for consuming events, other than the definition of the [[https://docs.rs/tracing/0.1.34/tracing/trait.Subscriber.html][Subscriber]] trait.

The [[https://docs.rs/tracing-subscriber/0.3.11/tracingsubscriber/index.html][tracing-subscriber]] crate (also part of the [[https://tokio.rs/][Tokio]] project) provides a few basic implementations along with "utilities for implementing and composing tracing subscribers." A key notion introduced by this crate is the idea of a [[https://docs.rs/tracing-subscriber/0.3.11/tracingsubscriber/layer/trait.Layer.html][Layer]]. "Unlike Subscribers, which implement a complete strategy for how trace data is collected, Layers provide modular implementations of specific behaviors." The concern here is that, in general, a consumer of [[https://docs.rs/tracing/latest/tracing/index.html][tracing]] event data may want to do various things at the same time with that data: write it to a log file, just write it to =stdout=, shuttle it off to a log collection daemon, produce metrics based on it, and so on.

This could easily give rise to a geometric explosion in types: =LogFile=, =LogFileWithStdout=, =LogFileWithLogStash=, =LogFileWithLogStashAndStdout=, and so forth. The idea behind [[https://docs.rs/tracing-subscriber/0.3.11/tracingsubscriber/layer/trait.Layer.html][Layer]]s is to decompose each facet of event handling into its own type, and then "stack them up" in a [[https://docs.rs/tracing/0.1.34/tracing/trait.Subscriber.html][Subscriber]] as the application developer desires. In a sense, this design pattern is reminiscent of [[https://en.wikipedia.org/wiki/AndreiAlexandrescu][Alexandrescu]]'s concept of [[https://erdani.com/publications/traits.html][traits classes]] in C++-- a way of decomposing functionality into orthogonal facets and composing them linearly rather than geometrically. * Prerequisites

Other than [[https://github.com/tokio-rs/tracing][tracing]] and a syslog daemon, none. [[https://github.com/sp1ff/syslog-tracing/tracing-rfc-5424][tracing-rfc-5424]] was developed against rust 1.58.1, although [[https://github.com/tokio-rs/tracing][tracing]] requires 1.49. * Usage

I'm in the process of submitting [[https://github.com/sp1ff/syslog-tracing][tracing-rfc-5424]] to [[https://crates.io][crates.io]] at the moment, so you'll have to pull it directly from here:

+BEGIN_SRC toml

[dependencies] tracing-rfc-5424 = { git = "https://github.com/sp1ff/syslog-tracing" }

+END_SRC

** Examples

Then, to talk to a local syslog daemon over UDP:

+BEGIN_SRC rust

use tracing::info; use tracingrfc5424::{ layer::Layer, rfc5424::Rfc5424, tracing::TrivialTracingFormatter, transport::UdpTransport, }; use tracing_subscriber::{ layer::SubscriberExt, // Needed to get with() registry::Registry, };

// Setup the subsriber... let subscriber = Registry::default() .with(Layer::::trydefault().unwrap()); // and install it. let _guard = tracing::subscriber::setdefault(subscriber);

info!("Hello, world!"); // Will produce a syslog line something like: // Jun 23 16:10:55 my-host my-app[pid] Hello, world!

+END_SRC

This is a preliminary release; the version number (0.0.x) is intended to convey that. Additional features to be implemented:

The name =tracing-syslog= seemed most natural to me, but has been already claimed (by an empty project) on [[https://crates.io][crates.io]]. I've reached-out to the author, but haven't heard anything back. I moved on to =syslog-tracing=, but before I published the crate, that was claimed, [[https://crates.io/crates/syslog-tracing][too]] (by an implementation with a very different implementation apporach-- FFI straight to the =libc= support for syslog). I wound-up re-factoring the repo into a library package & a test package and I've taken the opportunity to rename the library crate to =tracing-rfc-5424= (after the RFC governing the modern syslog message format).

This project got started when I needed to log to a local syslog daemon from an app that uses [[https://github.com/tokio-rs/tracing][tracing]]. I started down the same path recommended by [[https://users.rust-lang.org/u/endsofthreads/summary][David Barsky]] in this [[https://users.rust-lang.org/][rust-lang]] [[https://users.rust-lang.org/t/using-tracing-with-syslog/64499][thread]]: "Hey, there's this [[https://github.com/Geal/rust-syslog][syslog]] [[https://docs.rs/syslog/latest/syslog/][crate]], why not just call it from a Layer you write?"

I started down that path and found that that crate's =Logger= abstraction takes =&mut self= on all it's methods, which makes it difficult to use in a =Layer= implementation (which only gets =&self=). I opened an issue [[https://github.com/Geal/rust-syslog/issues/68][here]]. "No problem," I thought, "the transport piece is easy. I can still make make use of the underlying components. Hmmmm.... what the heck are RFCs 5424 & 3164? The crate gives no guidance on which to use, so I need to understand them. Oh... [[https://docs.rs/syslog/latest/syslog/struct.Formatter5424.html][Formatter5424]] represents the hostname as a =String=, well having read the RFC I know that's not right... oh the heck with it."

Bugs, comments, problems, criticism, PRs, feature requests &c welcome at [[mailto:sp1ff@pobox.com][sp1ff@pobox.com]] and in the [[https://github.com/sp1ff/syslog-tracing/issues][issues]].