39 releases
new 0.11.2 | Dec 9, 2024 |
---|---|
0.11.0 | Oct 7, 2024 |
0.10.2 | Jul 9, 2024 |
0.9.11 | Feb 21, 2024 |
0.1.2 | Jul 13, 2020 |
#116 in Debugging
3,209 downloads per month
Used in perimeterx-fastly-enforce…
37KB
379 lines
Implementation of the [log
] logging façade for Fastly Compute.
With this logger configured, log
's logging statements will send log messages to
your chosen Real-Time Log Streaming endpoints. You should initialize the logger as soon
as your program starts. Logging statements will not do anything before initialization.
See the Fastly documentation for more information about configuring logging endpoints for your service.
Getting started
All you need to get started is your endpoint name and the level of log messages you want to
emit. For example, if you have an endpoint called my_endpoint
, and you only want to emit log
messages at the "Warn" or "Error" level, you can use [init_simple()
]:
log_fastly::init_simple("my_endpoint", log::LevelFilter::Warn);
log::warn!("This will be written to my_endpoint...");
log::info!("...but this won't");
Advanced configuration
For more precise control, including multiple endpoints and default endpoints for different
logging levels, use the Builder
interface. The first example is equivalent to:
log_fastly::Logger::builder()
.max_level(log::LevelFilter::Warn)
.default_endpoint("my_endpoint")
.init();
The [Builder::max_level()
] option sets the most verbose level of logging that will be
emitted. Logging statements above that level, like log::info!()
in the first example, will do
nothing.
Note: The default level is LevelFilter::Off
, which emits no logging at all. You'll want to
change this for most configurations.
[Builder::default_endpoint()
] sets the endpoint used whenever a logging statement is called
without a target
field to specify its endpoint. With the default endpoint set to
my_endpoint
, the logging statements in the first example are equivalent to:
log::warn!(target: "my_endpoint", "This will be written to my_endpoint...");
log::info!(target: "my_endpoint", "...but this won't");
Use with Compute Log Tailing
Compute Log Tailing is helpful for getting debugging output quickly from a
Compute program under development by capturing output from stdout
or stderr
. To
configure logging to output to stdout
or stderr
in addition to the specified log endpoint,
enable echoing when building the logger:
log_fastly::Logger::builder()
.max_level(log::LevelFilter::Warn)
.default_endpoint("my_endpoint")
.echo_stdout(true)
.init();
Multiple endpoints
Setting an endpoint as the default will automatically register it for use with the logger, but
you can register additional endpoints with [Builder::endpoint()
]:
log_fastly::Logger::builder()
.max_level(log::LevelFilter::Warn)
.default_endpoint("my_endpoint")
.endpoint("my_other_endpoint")
.init();
log::warn!(target: "my_endpoint", "This will be written to my_endpoint...");
log::warn!(target: "my_other_endpoint", "...but this will be written to my_other_endpoint");
Per-endpoint logging levels
You can also set a per-endpoint logging level, though levels higher than max_level
are always
ignored:
log_fastly::Logger::builder()
.max_level(log::LevelFilter::Warn)
.default_endpoint("my_endpoint")
.endpoint_level("my_other_endpoint", log::LevelFilter::Trace)
.endpoint_level("error_only", log::LevelFilter::Error)
.init();
log::warn!(target: "my_other_endpoint", "This will be written to my_other_endpoint...");
log::trace!(target: "my_other_endpoint", "...but this won't, because max_level wins");
log::error!(target: "error_only", "This will be written to error_only...");
log::warn!(target: "error_only", "...but this won't, because the endpoint's level is lower");
Per-level default endpoints
In the previous examples, the same endpoint is set as the default for all logging levels. You
can also specify default endpoints for individual levels using
[Builder::default_level_endpoint()
]. The defaults are combined in order, so you can specify an
overall default endpoint, and then as many level-specific endpoints as you need:
log_fastly::Logger::builder()
.max_level(log::LevelFilter::Info)
.default_endpoint("my_endpoint")
.default_level_endpoint("error_only", log::Level::Error)
.init();
log::info!("This will be written to my_endpoint...");
log::warn!(".. and this will too.");
log::error!("But this will be written to error_only");
Module name filters
In addition to per-endpoint logging levels, you can set logging levels based on the name of the Rust module that contains the logging statement.
No filtering is done based on the module name by default, but if any module name patterns are
specified, only log statements that match one of the patterns will be emitted. For example, if
your application is a crate called my_app
, you can filter out any messages other than
my_app
's :
log_fastly::Logger::builder()
.max_level(log::LevelFilter::Info)
.default_endpoint("my_endpoint")
.filter_module("my_app", log::LevelFilter::Warn)
.init();
log::warn!("This will be written to my_endpoint");
// This won't emit any log messages, because no patterns match `some_dependency`
some_dependency::function_that_logs();
The filter expressions support the full syntax of the regex
crate. This is
particularly useful if your patterns overlap, but you'd like to still treat them distinctly. For
example, suppose you want to set the log level of the module my_app::my_module
to be more
restrictive than the top level my_app
module. You can use $
to make sure the
less-restrictive pattern matches to the end of the module name, instead of matching any module
name that contains my_app
:
mod my_module {
pub fn do_a_thing() {
log::warn!("This won't be written, because this module's max level is Error");
}
}
log_fastly::Logger::builder()
.max_level(log::LevelFilter::Info)
.default_endpoint("my_endpoint")
.filter_module("my_app$", log::LevelFilter::Warn)
.filter_module("my_app::my_module", log::LevelFilter::Error)
.init();
log::warn!("This will be written to my_endpoint");
// This won't emit any log messages, because "my_app$" doesn't match, and "my_app::my_module"
// is limited to Error
my_module::do_a_thing();
Registering endpoints
All endpoints used by your logging statements must be registered when the logger is created. The following functions automatically register an endpoint if it is not already registered.
- [
init_simple()
] - [
Builder::endpoint()
] - [
Builder::endpoint_level()
] - [
Builder::default_endpoint()
] - [
Builder::default_level_endpoint()
]
You can pass the endpoint name as a string, or an explicit fastly::log::Endpoint
value. The
following examples are equivalent:
log_fastly::init_simple("my_endpoint", log::LevelFilter::Info);
log_fastly::init_simple(
fastly::log::Endpoint::from_name("my_endpoint"),
log::LevelFilter::Info,
);
If a logging statement uses target: "my_endpoint"
but my_endpoint
is not registered, the
message will be logged to the default endpoint for that level, if one exists.
Endpoint and module name collisions
Due to a limitation of log
, logging from a module with the same name as one of
your endpoints will cause logs to be sent to that endpoint, even if no target
is specified,
and the endpoint is not a default. For example, if you have an endpoint named my_app
, and your
application is a crate also called my_app
:
log_fastly::Logger::builder()
.max_level(log::LevelFilter::Info)
.default_endpoint("my_endpoint")
.endpoint("my_app")
.init();
log::info!("This will be written to my_app, even though my_endpoint is the default");
log::info!(
target: "my_endpoint",
"This will be written to my_endpoint, because the target is explicit",
);
We hope to address this issue in future releases, but the current workarounds are to either make sure your endpoint names and crate name are distinct, or use the explicit target syntax whenever logging from your top-level module.
Dependencies
~9–12MB
~206K SLoC