Reputation: 305
The only available filter is a level threshold, but say I wanted to filter by a specific target: $expr
instead. How would I programmatically (or in the yml file) let log4rs know that I need a different kind of filter? Would I need to add a custom feature to accommodate the addition of my new filter type that implements the filter trait?
I have tried implementing my own filter type with all of the associated implementations as per the source code in the documentation of the threshold filter type. I even have the custom filter name specified in the - kind:
section of a filter the the .yml
config file. What more could log4rs need to implement something like this?
In addition, I am receiving the following error when I run let handle = log4rs::load_config_file("../log4rs.yml", Default::default()).unwrap();
log4rs: error deserializing filter attached to appender custom_filter_appender: no filter deserializer for kind custom_filter
registered
Even though I have explicitly defined a deserializer for said custom filter.
Upvotes: 2
Views: 1479
Reputation: 305
There are a few point to respond to with this question:
target: $expr
, you can get away with merely creating a new logger that shares the same name as the target(s) you'd like to filter by. This is because log4rs
only sends macros from the log
crate to loggers with the same target string as the logger name. As such, there is a kind of built in filtering going on here.First the configuration log4rs.yml file:
appenders:
custom_filter_appender:
kind: file
path: "log/custom_filter.log"
filters:
- kind: custom_filter
interesting_field: interesting_value
encoder:
pattern: "{d} {m}{n}"
root:
level: error
loggers:
filter_logger:
level: <level you want to filter by>
appenders:
- custom_filter_appender
additive: false
What I did above illustrates what would be necessary to configure a custom filter with log4rs
using a configuration file. Observe that filters are attached to appenders and appenders to loggers. This is the only way to attach things. Next I will detail the trait implementations needed in custom_filter.rs for a custom filter and how they will affect the fields representing the custom_filter
.
// First remember to include the appropriate log4rs or whatever other libraries you want in the custom_filter.rs file
#[derive(Deserialize)]
pub struct CustomFilterConfig {
interesting_field: InterestingFieldType,
}
#[derive(Debug)]
pub struct CustomFilter {
interesting_field: InterestingFieldType,
}
impl CustomFilter {
/// Creates a new `CustomFilter` with the specified interesting field.
pub fn new(interesting_field: InterestingFieldType,) -> CustomFilter {
CustomFilter { interesting_field }
}
}
impl Filter for CustomFilter {
fn filter(&self, record: &Record) -> Response {
if <Some comparison about self.interesting_field> {
Response::Accept
} else {
Response::Reject
}
}
}
pub struct CustomFilterDeserializer;
impl Deserialize for CustomFilterDeserializer {
type Trait = dyn Filter;
type Config = CustomFilterConfig;
fn deserialize(
&self,
config: CustomFilterConfig,
_: &Deserializers,
) -> Result<Box<dyn Filter>, Box<dyn Error + Sync + Send>> {
Ok(Box::new(CustomFilter::new(config.interesting_field)))
}
}
All of these must be implemented if log4rs
is to have a prayer of recognizing and running your filter. And remember that only log
crate macros that have the same target as the name filter_logger
(in this case) will go to this filter for filtering. Now to finish this off we need to know how to set up the log4rs
configuration from a file in main.rs with the correct filter added so it can be used.
let mut custom_filter_deserializer = log4rs::file::Deserializers::new();
custom_filter_deserializer.insert(
"custom_filter",
crate::custom_filter::CustomFilterDeserializer,
);
log4rs::init_file("log4rs.yml", custom_filter_deserializer).unwrap();
And that should be everything you need to configure a custom filter yourself using log4rs
.
Upvotes: 2