-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: Logging #47
feature: Logging #47
Conversation
6cf53a9
to
4a5c79d
Compare
Adding |
@str4d I have a PR up that switches the That seems to be enough of an improvement over |
Rebased on |
// The structs and enums in this file are extracted from the `tracing-core` crate with | ||
// adaptions to Furi. The original code is copyright (c) 2019 Tokio Contributors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems a lot of code for what should mostly be just some macros around sys::furi_log_print_format
.
The log
crate has no-std
support and only needs about 15 lines to implement. As a bonus, we'd immediately support any crate that used the log
crate as a logging facade.
The tracing
crate is quite powerful, but the main reason for using it is to be able to understand asynchronous code where tasks can jump between threads. While it looks like it could be possible to implement a tracing::Subscriber
, there's a lot of code that needs to be generated to support spans, which I worry would blow out binary sizes quite quickly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually started this effort by trying to write a log
backend, and I very quickly ran into the problem that log
assumes that you will be writing and formatting with &str
, while sys::furi_log_print_format
requires &CStr
. This would force inline allocations at every individual log point, in order to copy the format string and append a nul
byte.
tracing
has the same issue here. It's a fantastic library and I use it wherever I can (it's useful for way more than just understanding async code), but it also is built around &str
, and thus doesn't match up well with sys::furi_log_print_format
.
My next thought was that we can use the c_string!
macro to ensure that the format string is statically null-terminated, but this requires writing our own macros. Once we've done that, the next step is to represent the FuriLevel
enum on the Rust side, which is where I turned to tracing
's implementation as it is heavily optimised for low code overhead, which we want here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All that being said: if we're going to use ufmt
and FuriString
for formatting log output before calling sys::furi_log_print_format
, then we could provide a log::Log
and/or tracing::Subscriber
implementation that users can choose to install.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think FuriString
is going to be the right approach. We may be able to do something clever like using a static FuriString
buffer for formatting, so we're not constantly doing allocations (the logger is already serialized on a mutex).
I just realized another issue with the log
crate. It expects records to be formatted as core::fmt::Arguments
which pretty much forces the use of core::fmt
which we're trying to avoid. Tracing is a bit better since it can keep track of individual fields, but generic messages are still formatted using core::fmt
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We now use FuriString
(allocating each time), and ufmt
for formatting. So our arguments are whatever ufmt::uwrite
takes instead of core::fmt::Arguments
.
We could maybe use the rt
macros to set up a static, and then free it on exit. That seems like an approach that would be generally useful for various sub-systems; opened #64.
Rebased on current |
Rebased on |
Source: https://github.com/tokio-rs/tracing/blob/tracing-core-0.1.30/tracing-core/src/metadata.rs License: MIT Copyright (c) 2019 Tokio Contributors
We don't need these for the Furi Logging system.
We aren't constrained by the const fn conversions of the `tracing` crate, so we can avoid the edge cases caused by that representation.
This enables us to greatly simplify the ordering implementations: - We have a dedicated `LevelFilterInner` enum, so we don't need to rely on the niche optimization of `Option<Level>` like `tracing-core` does. - We will be placing the logging macros in the same crate as `Level` and `LevelFilter`, so we don't need to add `#[inline(always)]` annotations to the comparison methods. - The Flipper Zero SDK orders their discriminants in verbosity ordering, so we don't need to invert the ordering.
The Flipper Zero SDK's log level numbers don't line up with the ones `tracing` used, and are offset by `FuriLogLevelDefault`, so they would just make the API confusing.
Rebased on |
Force-pushed because I forgot I'd renamed |
The `tracing` ecosystem uses this to set a global maximum based on all uses of log macros within the binary. FAPs are instead loaded within the wider Flipper Zero environment, and `sys::furi_log_set_level` configures the global log level that affects other apps.
The
Level
andLevelFilter
types are based on the equivalenttracing-core
types. We then use them to implement wrapper macros aroundsys::furi_log_print_format
.