Skip to content

Add one more log level (notice/verbose?) #334

Open

Description

@intgr

When writing command line programs, I often find myself wanting two distinct "info"-like log levels: one for regular output and one when a --verbose flag is requested by the user. Then implementing --verbose is simply a matter of changing the log level. I think the Debug level is wrong for this purpose because it's oriented towards developers, whereas --verbose is typically intended for troubleshooting by users.

There would be two ways to go about it:

  • Warn > Notice > Info > Debug
  • Warn > Info > Verbose > Debug

I realize that similar things have been requested before (#240, #229), but I think my use case is actually a quite helpful one.

If your consideration is to keep the number of built-in log levels low, I actually don't find a distinct Trace level to be useful; seems like debug output to me. But perhaps other people do? What was the justification for that in the first place? :)

Activity

  1. KodrAus commented on May 26, 2019

    @KodrAus
    Contributor

    Hi @intgr 👋

    This doesn’t sound unreasonable, however I would still argue against another info-like level for this purpose, because the levels are really arbitrary already any changes aren’t necessary going to result in quality filtering from libraries.

    If you’re looking for a way to do nice optional verbose output for your own CLI then you could define whatever semantics you need for the log levels you use (as an example, using something like env_logger’s filtering to make —verbose mean logging all events raised by your CLI, but only info or higher level events from dependencies).

  2. sfackler commented on May 26, 2019

    @sfackler
    Member

    Trace is used often in low-level libraries like hyper and tokio for very verbose logging information that can be useful to diagnose issues.

  3. TyPR124 commented on Apr 12, 2020

    @TyPR124

    I think a "Notice" log level and a "Critical" log level would both be very nice to have. I would argue "Critical" in particular is a very necessary addition.

    I was recently integrating logging for an application with Google Cloud. They define several more log levels that log does: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity

    Personally I am thinking that (to an extent) the more levels log offers, the better. Libraries that wrap some external logging API can choose how they want to map log's levels to the API, however log doesn't currently have enough levels to meaningfully make use of the levels provided in Google's API (or any similar API with more levels than log provides).

    Google says that "Info" is general runtime info, such as performance stats, while "Notice" would be more significant events, like process startup, shutdown, or config changes. I think this is a meaningful difference that is worth supporting.

    In terms of a "Critical" event class, I think this is especially useful (much more than "Notice") for differentiating between "this is an error that by itself isn't a big deal" and "this is an error that by itself is likely impacting my production environment negatively right now". Google offers a few more levels worse than Critical, though I I don't see those as quite as necessary in general (but, again, more levels means logging libraries can have a more fine-grained mapping of levels, and it's not like you are forced to use every log level in your application if you don't want/need them).

    I don't think either Notice or especially Critical log levels are that uncommon. I know (for example) Windows supports a Critical log level.

    Given this is (AFAIK) the de-facto logging facade for Rust, it is currently disappointing that I can't utilize these additional levels in Google's or anyone else's API.

  4. sfackler commented on Apr 12, 2020

    @sfackler
    Member

    Adding a log level is a breaking change. The existing levels match slf4j (with the exception of a fatal level for historical reasons).

  5. TyPR124 commented on Apr 12, 2020

    @TyPR124

    Adding a log level is a breaking change.

    Breaking changes happen. The de-facto logging crate shouldn't hinder itself by refusing to make breaking changes.

    The existing levels match slf4j

    The existing levels clearly do NOT match many other logging APIs. I don't see why a single Java library is considered the authority on logging levels. Why not Google? Or Microsoft? Or Amazon? All three of these entities provide a "Critical" (or "Fatal") log level that is impossible to map from log in a generic and meaningful way. Surely having the ability to better support these platforms is worthwhile.

    I don't mean to sound hostile (I probably do, sorry), but I don't see either as valid reasons to avoid change.

  6. ijackson commented on Sep 6, 2020

    @ijackson

    I also agree that log should have both Notice and Critical. Unix syslog has them and they are very useful. I'm currently writing an app where I'm really feeling the lack of both of these.

  7. Thomasdezeeuw commented on Oct 21, 2020

    @Thomasdezeeuw
    Collaborator

    Any more movement on this? I could also use some more severity levels.

  8. rudolphfroger commented on Dec 28, 2020

    @rudolphfroger

    I think the log crate needs to support the default eight Unix syslog severity levels. See RFC3164 and RFC 5424. Many systems have used these levels as a standard to build upon and/or actually use syslog.

  9. shutton commented on May 28, 2021

    @shutton

    Minimally, please add 'notice'. It's a key level in enterprise applications that's necessary for situations where you want low-noise logs, but need to see certain interesting events. 'Warning' is too strong, meriting attention, whereas 'notice' is something you'd log in case you needed to look back for something that might hint at a problem, but not until you notice something is awry.

    For others, here's the overlap between syslog and log:

    log      syslog
    -------  ---------
    -        emergency
    -        alert
    -        critical
    Error    error
    Warn     warning
    -        notice
    Info     info
    Debug    debug
    Trace    -
    
  10. jo-so commented on Jun 9, 2021

    @jo-so

    Having a level between Warn and Info would allow addressing messages

    • like user login/logout, account creation, device came online, ... for normal service inspection – Notice
    • like user saved file, user started job ... for deeper service inspection (i.e. up-on request) – Info

    Having a level higher than Error would allow distinguishing messages

    • of graceful handled errors (»db connection lost«) ­– Warn
    • of unhandled errors (»failed to write file«) – Error
    • of errors they render the service unusable (panic, »failed to listen to socket«) – Critical
  11. KodrAus commented on Nov 15, 2021

    @KodrAus
    Contributor

    In general, I would argue against a proliferation of extra log levels in favor of better filtering on other properties of the log event. Levels are at best a coarse-grained proxy for the likelihood of an event being important to surface/retain. The crate currently offers a reasonable coarse-grained set of levels that cover the majority of cases. Adding more of them doesn't necessarily result in better diagnostics, and having to map them one-to-one to the levels of any other system isn't necessarily a path to a useful set of levels in log.

    We've been steadily working towards better structured logging support over the last few years (there is also the tracing crate that already supports it) that would better serve users that want to work with some custom log levels, such as an associated syslog level.

  12. Sfinx commented on Feb 22, 2022

    @Sfinx

    Sad that there is no custom levels - I'd like to use the:

    • 'fatal' level for critical messages
    • 'always' level for banners that must be printed with any verbosity level set (app version data or licensing for ex.)
  13. ta32 commented on Apr 8, 2022

    @ta32

    I wish there as an always level. Sometimes you want to print some stuff like what port and address a web server is running on. I just use warning but it looks kind of weird...

    I suppose you could just make a crate and prints with the same format used by the logging system.

  14. KodrAus commented on Apr 20, 2022

    @KodrAus
    Contributor

    I think communicating end-user messages is really orthogonal to log, which is targeted at collecting diagnostic output. There might be some overlapping technical concerns but the details of end-user rendering I think are different enough that a dedicated domain-specific implementation would be better.

  15. Billy-Sheppard commented on Aug 4, 2022

    @Billy-Sheppard

    I would also love to see a new level added.

    External crates especially love to fill up INFO with lots of useful messaging but sometimes I find wanting a MSG or NOTICE level for targeted important messages that don't make sense in ERROR/WARN. It is possible to get around this by using crate level log level filters but that's clunky imo, and I often want my scope all encompassing.

    Custom levels would do some to alleviate this, I haven't looked into it, but would a PR be welcome/well received?

    CRITICAL, IMPORTANT, ALERT, and ALWAYS all feel like they could be useful.

  16. 19 remaining items

  17. Thomasdezeeuw commented on Oct 3, 2025

    @Thomasdezeeuw
    Collaborator

    @alextes how do you propose to make it configurable?

  18. alextes commented on Oct 3, 2025

    @alextes

    i don't think this is the issue to discuss this @Thomasdezeeuw . if you think its within the realm of possibility how about i create a new issue for it?

    if you really want to discuss approaches here:

    first steps custom level exploration i don't know this crate well. taking a look for 5m together with AI. two possible approaches:

    Custom Variant Registry

    • Replace the fixed enum at src/lib.rs:475 with pub enum Level { Error, …, Trace, Custom(&'static LevelDef) } and introduce a LevelDef { name: &'static str, ordinal: u16, fallback: Level }
      registry guarded by an RwLock. Add log::register_level(LevelDef) that interns defs and returns a &'static LevelDef, so call-sites can write log!(Level::Custom(MY_VERBOSE), …).
    • Adjust lookups that currently index LOG_LEVEL_NAMES (src/lib.rs:456) to first check the built-ins and then fall back to the registry. Level::iter()/Level::from_str() emit built-ins first
      and append registered entries so existing logic keeps its ordering guarantees.
    • Teach LevelFilter (src/lib.rs:637) to accept either built-ins or LevelDef handles by comparing ordinals; keep the existing constants but add LevelFilter::Custom(u16) for per-registry
      filters.
    • The macros (src/macros.rs:23) stay source-compatible because they already accept any Level expression; they just need to resolve string names via the registry for formatting.
      STATIC_MAX_LEVEL keeps compile-time pruning for the shipped variants, while custom levels are always decided at runtime.
    • Pros: existing code keeps matching on built-in variants, custom levels get real names/ordering and surface everywhere that prints a level. Cons: bumps MSRV/API because of the new payload
      variant and drops the current #[repr(usize)], so this is a “next major” story and needs a migration plan for consumers relying on mem::size_of::() or integer casts.

    Metadata Sidecar Level

    • Leave Level/LevelFilter exactly as-is, but extend Metadata (src/lib.rs:1157) and Record (src/lib.rs:841) with an optional custom_level: Option<&'static CustomLevel>, where CustomLevel
      stores { name: &'static str, ordinal: u16 }. Add MetadataBuilder::custom_level() so backends (or new helper macros) can stamp records with richer level signals while still picking the
      nearest built-in Level as the fallback.
    • Extend the private logging entry point in src/macros.rs:59 to call a new helper like __private_api::resolve_level($lvl) that returns (Level, Option<&'static CustomLevel>). For built-in
      macros nothing changes; a new log_custom! (or user-provided macro) can pass a CustomLevel handle together with the fallback Level.
    • Logger implementors who care about the new levels read record.custom_level() and compare against their own configuration (e.g., a HashMap<&'static str, LevelFilter>); those who don’t opt
      in just keep using record.level() and remain unaware.
    • Pros: entirely backward compatible—no change to Level, no impact on STATIC_MAX_LEVEL, and existing consumers keep compiling untouched. Lets downstream crates experiment today by shipping
      new macros or using MetadataBuilder directly. Cons: filtering for the new levels is opt-in and purely runtime; the compile-time max_level_* features cannot prune custom levels, and the
      fallback Level has to be chosen carefully so legacy backends don’t mis-route records.

    again, v quick reading but seems there's some tough trade-offs around what macros would be available, maybe users with custom levels would have to roll their own or call a non-macro version. another tough trade-off seems to be around performance. what can we prune at compile time. i feel one would have to dive deeper to call those. like maybe there is a real perf hit, maybe not, for log probably any hit is unacceptable, but then would you still let app users set a mode where they take the hit, or suggest to create another logging crate for this entirely to wrap log. idk. tough trade-offs. would have to explore and discuss together.

  19. Thomasdezeeuw commented on Oct 3, 2025

    @Thomasdezeeuw
    Collaborator

    Your suggesting as a solution for this issue so I think place is fine to discuss it. If you want to create a new issue that's also fine.

    I'm not going to read an AI answer though, I have no interest in that. Please consider other people time and don't waste it. If you have a though through proposal that's great, but letting AI generate something isn't that.

  20. psztoch commented on Oct 3, 2025

    @psztoch

    We shouldn't blindly look only at syslog. But it certainly has additional levels that we don't have: CRITICAL (something worse than ERROR) and NOTICE (something between WARNING and INFO).
    And in some applications, these additional levels are very necessary. I believe they should be added as soon as possible. They can always be flattened if the log destination doesn't support our granularity.

  21. KodrAus commented on Oct 3, 2025

    @KodrAus
    Contributor

    The point of using exactly syslog's levels would be to sidestep any discussion on where to draw the line on adding more levels to log.

    The issue with an extensible system is that it would make any level not directly mapped by log second-class. If we were to explore it, I think something like this might be the best:

    impl<'a> RecordBuilder<'a> {
        pub fn level_custom(&mut self, level: &'a impl AsRef<Level> + Any) -> &mut Self;
    }
    
    impl<'a> Record<'a> {
        pub fn level(&self) -> Option<Level> {
            self.level.or_else(|| self.level_custom.as_ref().map(AsRef::as_ref).copied())
        }
    
        pub fn level_custom(&self) -> Option<impl Any + 'a> {
            self.level_custom.as_ref()
        }
    }

    So you can set a custom level and get it, but can always reduce it down to a regular Level. That also avoids us needing to figure out how to order custom levels with predefined ones.

  22. kanru commented on Oct 4, 2025

    @kanru

    If a breaking change will be introduced, couldn't it use numerical levels and add utility to clamp values to predefined levels?

    pub struct Level(u8);
    

    For example, standard levels are modeled after syslog with 10 steps in between. 0=Critical, 10=Error, 20=Warning, etc.

    Or it can use 100 steps if the repr can be larger than u8.

    Numerical levels are pretty common and it allows customization.

    It might even be possible to make it ABI compatible with the current version. Just reserve the already used numbers.

  23. Sajjon commented on Oct 4, 2025

    @Sajjon

    If a breaking change will be introduced, couldn't it use numerical levels and add utility to clamp values to predefined levels?

    pub struct Level(u8);
    

    For example, standard levels are modeled after syslog with 10 steps in between. 0=Critical, 10=Error, 20=Warning, etc.

    Or it can use 100 steps if the repr can be larger than u8.

    Numerical levels are pretty common and it allows customization.

    It might even be possible to make it ABI compatible with the current version. Just reserve the already used numbers.

    I was thinking the exact same thing. Using steps of 10 should be enough. E.g:

    Trace = 10,
    Debug = 20,
    Info = 30,
    Notice = 40,
    Warn = 50,
    Error = 60,
    Critical = 70,
    
  24. BurntSushi commented on Oct 4, 2025

    @BurntSushi
    Member
  25. Thomasdezeeuw commented on Oct 4, 2025

    @Thomasdezeeuw
    Collaborator

    One problem I've had in the past with numerical log levels is that two systems can disagree what level a numerical value actually is, e.g. what does 30 mean? It makes string options a lot safer as no (sane) system would parse "critical" as "info" if it didn't have a critical log level (like log currently). However, if we go with (public) numerical values we lose the option to convert it to string in all cases.

  26. KodrAus commented on Oct 5, 2025

    @KodrAus
    Contributor

    I would also not want to introduce numerical levels. Log levels are already z-indexes, but that’s largely offset by their semantics and coarse granularity. I don’t want to open the door to discussions like should this log be a 102 or a 103. This also leaves the problem of components needing to agree on what numbers any levels between levels, such as Notice, mean if they want to interpret them.

  27. KodrAus commented on Oct 5, 2025

    @KodrAus
    Contributor

    Having said that, if we were to introduce numerical log levels though, I think we could adopt the same scheme as OpenTelemetry: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber

    That leaves just a few values per level for intermediate severities, and is already compatible with our current set of levels (plus a critical/fatal level).

  28. tisonkun commented on Oct 29, 2025

    @tisonkun
    Contributor

    That's similar to what Go's log/slog package does.

    Having said that, if we were to introduce numerical log levels though, I think we could adopt the same scheme as OpenTelemetry: https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber

    Ref -

    I'm recently reading Go's log/slog package and would consider a similar design a possible way to go:

    pub struct Level(u8); // or u16? I don't think we need more
    
    impl Level {
      const TRACE: Level = Level(1);
      const DEBUG: Level = Level(5);
      const INFO: Level = Level(9);
      const WARN: Level = Level(13);
      const ERROR: Level = Level(17);
      const CRIT: Level = Level(21); // or FATAL? or alias to FATAL?
    }

    Other logics may be similar to log/slog

    The overall logic should be able to be adapted into our current Level design. I'll try to implement logforth's Level in this way to demonstrate the idea.

    UPDATE: However, I noticed that opentelemetry-rust defines the severity as an enum:

    https://github.com/open-telemetry/opentelemetry-rust/blob/c54d2b84d0828183a3ba347bc66fa8b8268854f9/opentelemetry/src/logs/record.rs#L141-L230

    (Out of the thread, I borrowed a new LevelFilter design from spdlog that may also be helpful, but it should be a different thread if anyone is interested in it.)

  29. SteveLauC commented on Nov 27, 2025

    @SteveLauC

    IMHO, log levels to use and their semantics should be specific to applications, and crates like log should provide a sufficient set of levels so that applications can choose the ones they want to use.

  30. filmil commented on Dec 5, 2025

    @filmil

    In case it is helpful, perhaps one could take a look at the "glog" API, e.g. as implemented by https://abseil.io/docs/cpp/guides/logging or https://github.com/golang/glog.

    There are named log levels (FATAL, ERROR, WARNING, INFO, DEBUG), but also "verbose" logs which use an integer to denote verbosity. To avoid misunderstandings about what verbose log levels mean, verbose logging can be turned off or on per module, as well as for everything. (I also think that named log levels are given negative log level values, while verbose logs ended up being positive, but that's a detail.)

    I personally found this approach rather useful over the years: have a few commonly-agreed upon logging levels, and then a large sea of "verbose" logs with arbitrary level of detail. This avoids needing to litigate the log levels that something should be logged at. It also avoids needing to agree on the "correct" meaning of log levels across modules or programs. And in the end allows the user to provide debug information with varying levels of granularity, when needed.

  31. VinnieThePooh commented on Dec 10, 2025

    @VinnieThePooh

    Fatal/Critical log level is a must here.
    Coming from .NET ecosystem, i don't get how that could be an oversight for such sort of essential log level.
    Regarding automation of business processes fatal/critical log level usually means some unrecoverable error that requires immediate SRE/System Administrator intervention (event itself might be extremely expensive for business).

    Currently it looks like the basic logging facade is designed not accurately enough (i would say even carelessly).

    Just for reference how it looks in .NET ecosystem.
    LogLevel from MS Logging Facade (please note the Critical log level):

    namespace Microsoft.Extensions.Logging
    {
        /// <summary>
        /// Defines logging severity levels.
        /// </summary>
        public enum LogLevel
        {
            /// <summary>
            /// Logs that contain the most detailed messages. These messages may contain sensitive application data.
            /// These messages are disabled by default and should never be enabled in a production environment.
            /// </summary>
            Trace = 0,
    
            /// <summary>
            /// Logs that are used for interactive investigation during development.  These logs should primarily contain
            /// information useful for debugging and have no long-term value.
            /// </summary>
            Debug = 1,
    
            /// <summary>
            /// Logs that track the general flow of the application. These logs should have long-term value.
            /// </summary>
            Information = 2,
    
            /// <summary>
            /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the
            /// application execution to stop.
            /// </summary>
            Warning = 3,
    
            /// <summary>
            /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a
            /// failure in the current activity, not an application-wide failure.
            /// </summary>
            Error = 4,
    
            /// <summary>
            /// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires
            /// immediate attention.
            /// </summary>
            Critical = 5,
    
            /// <summary>
            /// Not used for writing log messages. Specifies that a logging category should not write any messages.
            /// </summary>
            None = 6,
        }
    }
    

    Serilog's LogLevel enum definition (.NET's most popular 3rd-party logging provider, please notice the Fatal variant, it is aligned one-to-one with Critical from logging facade):

    namespace Serilog.Events;
    
    /// <summary>
    /// Specifies the meaning and relative importance of a log event.
    /// </summary>
    public enum LogEventLevel
    {
        /// <summary>
        /// Anything and everything you might want to know about
        /// a running block of code.
        /// </summary>
        Verbose,
    
        /// <summary>
        /// Internal system events that aren't necessarily
        /// observable from the outside.
        /// </summary>
        Debug,
    
        /// <summary>
        /// The lifeblood of operational intelligence - things
        /// happen.
        /// </summary>
        Information,
    
        /// <summary>
        /// Service is degraded or endangered.
        /// </summary>
        Warning,
    
        /// <summary>
        /// Functionality is unavailable, invariants are broken
        /// or data is lost.
        /// </summary>
        Error,
    
        /// <summary>
        /// If you have a pager, it goes off when one of these
        /// occurs.
        /// </summary>
        Fatal
    }
    

    So, i am voting for fatal variant.
    Looking at previous comments the majority of different stacks have well aligned logging levels in their ecosystem, except of Rust.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions