@@ -60,14 +60,16 @@ mod socket;
6060/// names by translating `.`s into `_`s, stripping leading `_`s and non-ascii-alphanumeric
6161/// characters other than `_`, and upcasing.
6262///
63- /// Levels are mapped losslessly to journald `PRIORITY` values as follows:
63+ /// By default, levels are mapped losslessly to journald `PRIORITY` values as follows:
6464///
6565/// - `ERROR` => Error (3)
6666/// - `WARN` => Warning (4)
6767/// - `INFO` => Notice (5)
6868/// - `DEBUG` => Informational (6)
6969/// - `TRACE` => Debug (7)
7070///
71+ /// These mappings can be changed with [`Subscriber::with_priority_mappings`].
72+ ///
7173/// The standard journald `CODE_LINE` and `CODE_FILE` fields are automatically emitted. A `TARGET`
7274/// field is emitted containing the event's target.
7375///
@@ -84,6 +86,7 @@ pub struct Layer {
8486 field_prefix : Option < String > ,
8587 syslog_identifier : String ,
8688 additional_fields : Vec < u8 > ,
89+ priority_mappings : PriorityMappings ,
8790}
8891
8992#[ cfg( unix) ]
@@ -109,6 +112,7 @@ impl Layer {
109112 // If we fail to get the name of the current executable fall back to an empty string.
110113 . unwrap_or_default ( ) ,
111114 additional_fields : Vec :: new ( ) ,
115+ priority_mappings : PriorityMappings :: new ( ) ,
112116 } ;
113117 // Check that we can talk to journald, by sending empty payload which journald discards.
114118 // However if the socket didn't exist or if none listened we'd get an error here.
@@ -129,6 +133,41 @@ impl Layer {
129133 self
130134 }
131135
136+ /// Sets how [`tracing_core::Level`]s are mapped to [journald priorities](Priority).
137+ ///
138+ /// # Examples
139+ ///
140+ /// ```rust
141+ /// use tracing_journald::{Priority, PriorityMappings};
142+ /// use tracing_subscriber::prelude::*;
143+ /// use tracing::error;
144+ ///
145+ /// let registry = tracing_subscriber::registry();
146+ /// match tracing_journald::layer() {
147+ /// Ok(layer) => {
148+ /// registry.with(
149+ /// layer
150+ /// // We can tweak the mappings between the trace level and
151+ /// // the journal priorities.
152+ /// .with_priority_mappings(PriorityMappings {
153+ /// info: Priority::Informational,
154+ /// ..PriorityMappings::new()
155+ /// }),
156+ /// );
157+ /// }
158+ /// // journald is typically available on Linux systems, but nowhere else. Portable software
159+ /// // should handle its absence gracefully.
160+ /// Err(e) => {
161+ /// registry.init();
162+ /// error!("couldn't connect to journald: {}", e);
163+ /// }
164+ /// }
165+ /// ```
166+ pub fn with_priority_mappings ( mut self , mappings : PriorityMappings ) -> Self {
167+ self . priority_mappings = mappings;
168+ self
169+ }
170+
132171 /// Sets the syslog identifier for this logger.
133172 ///
134173 /// The syslog identifier comes from the classic syslog interface (`openlog()`
@@ -232,6 +271,20 @@ impl Layer {
232271 memfd:: seal_fully ( mem. as_raw_fd ( ) ) ?;
233272 socket:: send_one_fd_to ( & self . socket , mem. as_raw_fd ( ) , JOURNALD_PATH )
234273 }
274+
275+ fn put_priority ( & self , buf : & mut Vec < u8 > , meta : & Metadata ) {
276+ put_field_wellformed (
277+ buf,
278+ "PRIORITY" ,
279+ & [ match * meta. level ( ) {
280+ Level :: ERROR => self . priority_mappings . error as u8 ,
281+ Level :: WARN => self . priority_mappings . warn as u8 ,
282+ Level :: INFO => self . priority_mappings . info as u8 ,
283+ Level :: DEBUG => self . priority_mappings . debug as u8 ,
284+ Level :: TRACE => self . priority_mappings . trace as u8 ,
285+ } ] ,
286+ ) ;
287+ }
235288}
236289
237290/// Construct a journald layer
@@ -286,7 +339,7 @@ where
286339 }
287340
288341 // Record event fields
289- put_priority ( & mut buf, event. metadata ( ) ) ;
342+ self . put_priority ( & mut buf, event. metadata ( ) ) ;
290343 put_metadata ( & mut buf, event. metadata ( ) , None ) ;
291344 put_field_length_encoded ( & mut buf, "SYSLOG_IDENTIFIER" , |buf| {
292345 write ! ( buf, "{}" , self . syslog_identifier) . unwrap ( )
@@ -374,18 +427,114 @@ impl Visit for EventVisitor<'_> {
374427 }
375428}
376429
377- fn put_priority ( buf : & mut Vec < u8 > , meta : & Metadata ) {
378- put_field_wellformed (
379- buf,
380- "PRIORITY" ,
381- match * meta. level ( ) {
382- Level :: ERROR => b"3" ,
383- Level :: WARN => b"4" ,
384- Level :: INFO => b"5" ,
385- Level :: DEBUG => b"6" ,
386- Level :: TRACE => b"7" ,
387- } ,
388- ) ;
430+ /// A priority (called "severity code" by syslog) is used to mark the
431+ /// importance of a message.
432+ ///
433+ /// Descriptions and examples are taken from the [Arch Linux wiki].
434+ /// Priorities are also documented in the
435+ /// [section 6.2.1 of the Syslog protocol RFC][syslog].
436+ ///
437+ /// [Arch Linux wiki]: https://wiki.archlinux.org/title/Systemd/Journal#Priority_level
438+ /// [syslog]: https://www.rfc-editor.org/rfc/rfc5424#section-6.2.1
439+ #[ derive( Debug , Clone , Copy , Hash , PartialEq , Eq ) ]
440+ #[ repr( u8 ) ]
441+ pub enum Priority {
442+ /// System is unusable.
443+ ///
444+ /// Examples:
445+ ///
446+ /// - severe Kernel BUG
447+ /// - systemd dumped core
448+ ///
449+ /// This level should not be used by applications.
450+ Emergency = b'0' ,
451+ /// Should be corrected immediately.
452+ ///
453+ /// Examples:
454+ ///
455+ /// - Vital subsystem goes out of work, data loss:
456+ /// - `kernel: BUG: unable to handle kernel paging request at ffffc90403238ffc`
457+ Alert = b'1' ,
458+ /// Critical conditions
459+ ///
460+ /// Examples:
461+ ///
462+ /// - Crashe, coredumps
463+ /// - `systemd-coredump[25319]: Process 25310 (plugin-container) of user 1000 dumped core`
464+ Critical = b'2' ,
465+ /// Error conditions
466+ ///
467+ /// Examples:
468+ ///
469+ /// - Not severe error reported
470+ /// - `kernel: usb 1-3: 3:1: cannot get freq at ep 0x84, systemd[1]: Failed unmounting /var`
471+ /// - `libvirtd[1720]: internal error: Failed to initialize a valid firewall backend`
472+ Error = b'3' ,
473+ /// May indicate that an error will occur if action is not taken.
474+ ///
475+ /// Examples:
476+ ///
477+ /// - a non-root file system has only 1GB free
478+ /// - `org.freedesktop. Notifications[1860]: (process:5999): Gtk-WARNING **: Locale not supported by C library. Using the fallback 'C' locale`
479+ Warning = b'4' ,
480+ /// Events that are unusual, but not error conditions.
481+ ///
482+ /// Examples:
483+ ///
484+ /// - `systemd[1]: var.mount: Directory /var to mount over is not empty, mounting anyway`
485+ /// - `gcr-prompter[4997]: Gtk: GtkDialog mapped without a transient parent. This is discouraged`
486+ Notice = b'5' ,
487+ /// Normal operational messages that require no action.
488+ ///
489+ /// Example: `lvm[585]: 7 logical volume(s) in volume group "archvg" now active`
490+ Informational = b'6' ,
491+ /// Information useful to developers for debugging the
492+ /// application.
493+ ///
494+ /// Example: `kdeinit5[1900]: powerdevil: Scheduling inhibition from ":1.14" "firefox" with cookie 13 and reason "screen"`
495+ Debug = b'7' ,
496+ }
497+
498+ /// Mappings from tracing [`Level`]s to journald [priorities].
499+ ///
500+ /// [priorities]: Priority
501+ #[ derive( Debug , Clone ) ]
502+ pub struct PriorityMappings {
503+ /// Priority mapped to the `ERROR` level
504+ pub error : Priority ,
505+ /// Priority mapped to the `WARN` level
506+ pub warn : Priority ,
507+ /// Priority mapped to the `INFO` level
508+ pub info : Priority ,
509+ /// Priority mapped to the `DEBUG` level
510+ pub debug : Priority ,
511+ /// Priority mapped to the `TRACE` level
512+ pub trace : Priority ,
513+ }
514+
515+ impl PriorityMappings {
516+ /// Returns the default priority mappings:
517+ ///
518+ /// - [`tracing::Level::ERROR`]: [`Priority::Error`] (3)
519+ /// - [`tracing::Level::WARN`]: [`Priority::Warning`] (4)
520+ /// - [`tracing::Level::INFO`]: [`Priority::Notice`] (5)
521+ /// - [`tracing::Level::DEBUG`]: [`Priority::Informational`] (6)
522+ /// - [`tracing::Level::TRACE`]: [`Priority::Debug`] (7)
523+ pub fn new ( ) -> PriorityMappings {
524+ Self {
525+ error : Priority :: Error ,
526+ warn : Priority :: Warning ,
527+ info : Priority :: Notice ,
528+ debug : Priority :: Informational ,
529+ trace : Priority :: Debug ,
530+ }
531+ }
532+ }
533+
534+ impl Default for PriorityMappings {
535+ fn default ( ) -> Self {
536+ Self :: new ( )
537+ }
389538}
390539
391540fn put_metadata ( buf : & mut Vec < u8 > , meta : & Metadata , prefix : Option < & str > ) {
0 commit comments