Skip to content
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

Complete rewrite #16

Open
wants to merge 26 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6302773
Merge pull request #14 from slog-rs/devel
przygienda Sep 23, 2018
b7cb8ee
* fixed broken doc link
przygienda Sep 23, 2018
0f97714
* more permissive API that breaks 0.6.1 however
przygienda Sep 24, 2018
f0376dc
Complete rewrite to support arbitrary logic expressions
dvtomas Sep 27, 2018
74d19eb
Added another showcase: test_complex_example_2()
dvtomas Sep 27, 2018
8578bed
Added .idea to .gitignore
dvtomas Sep 27, 2018
0d9782b
(optional) Serde serialize/deserialize support of the filter configur…
dvtomas Sep 27, 2018
4e8ed15
Improved the test_complex_example_2 test case
dvtomas Sep 27, 2018
334911c
Added the FilterSpec::any_of and FilterSpec::all_of utility methods
dvtomas Sep 27, 2018
78a8289
impl Default for EvaluationOrder
dvtomas Sep 28, 2018
351fb14
make KVFilterConfig fields public
dvtomas Sep 28, 2018
7a1f213
#derive Copy for EvaluationOrder
dvtomas Sep 28, 2018
6a67097
Implemented FilterSpec::MatchAllValues
dvtomas Oct 5, 2018
94b03ac
Fixed compilation when serde feature is disabled
dvtomas Oct 5, 2018
22332a2
Implemented FilterSpec::MatchMsgRegex, some refactoring
dvtomas Oct 5, 2018
e5cb44f
Better module comment
dvtomas Oct 5, 2018
49ff1f5
Better README, version bumped to 0.7.0
dvtomas Oct 5, 2018
4ed5652
Alright, added myself as well to Authors in Cargo.toml :-)
dvtomas Oct 5, 2018
6efb7dd
Make tests pass even in release mode
dvtomas Oct 6, 2018
d19f5cd
Actually construct KVFilter in the module-level example
dvtomas Oct 6, 2018
ff10e5c
MatchAllValues => MatchAnyValue
dvtomas Oct 14, 2018
d0be0a3
Criterion benchmark
dvtomas Oct 14, 2018
f856384
feat: FilterSpec::KeyExists
dvtomas Mar 11, 2021
a1bad7d
feat: Minor test addition
dvtomas Mar 11, 2021
1dc6e13
Merge branch 'devel'
przygienda Apr 23, 2021
e678a67
Merge branch 'master' into complete-rewrite
dvtomas Sep 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/target
/.cargo
/.idea
Cargo.lock
tags
rusty-tags.vi
24 changes: 22 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "slog-kvfilter"
version = "0.7.0"
authors = ["Dawid Ciężarkiewicz <[email protected]>", "Tony Przygienda <[email protected]>"]
authors = ["Dawid Ciężarkiewicz <[email protected]>", "Tony Przygienda <[email protected]>", "Tomáš Dvořák <https://github.com/dvtomas>"]
description = "Key values and Regex based filter Drain for slog-rs"
keywords = ["slog", "logging", "log", "filter"]
license = "MPL-2.0/MIT/Apache-2.0"
Expand All @@ -10,6 +10,26 @@ homepage = "https://github.com/slog-rs/slog"
repository = "https://github.com/slog-rs/kvfilter"
readme = "README.md"

[features]
# serde_regex should be enabled iff serde is enabled.
# I don't know how to express this in Cargo.toml automatically, though.
default=["serde", "serde_regex"]

[dependencies]
slog = "2"
serde = { version = "1", optional = true, features = ["derive"] }
serde_regex = { version = "0", optional = true }
regex = "1"


[dependencies.slog]
version = "2.7"
default-features = false
# So that the tests can reliably use debug and trace macros
features = ["max_level_trace", "release_max_level_trace", "std", "dynamic-keys"]

[dev-dependencies]
criterion = "0.2"

[[bench]]
name = "kvfilter_benchmark"
harness = false
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,22 @@

# slog-kvfilter - Key values and Regex based filter Drain for [slog-rs]

For more information, help, to report issues etc. see [slog-rs][slog-rs].
Filter records by matching their messages, keys and values. Records can be matched

[slog-rs]: https://github.com/slog-rs/slog
- based on logging level (debug, info, warn, ...)
- regular expression on record message
- keys and values
- simple filters can be arbitrarily composed using `And`, `Or` and `Not` expressions

Documentation can be found on
[`slog-kvfilter` type](https://docs.rs/slog-kvfilter/*/slog-kvfilter/index.html).

**slog-kvfilter has undergone a complete rewrite as of version 0.7.0.
The API has changed. See [this issue](https://github.com/slog-rs/kvfilter/issues/15)
for details.**

slog-kvfilter documentation can be found [here](https://docs.rs/slog-kvfilter/*/slog_kvfilter/).

For more information, help, to report issues etc. see
[slog-kvfilter][slog-kvfilter] and [slog-rs][slog-rs].

[slog-kvfilter]: https://github.com/slog-rs/kvfilter/
[slog-rs]: https://github.com/slog-rs/slog
264 changes: 264 additions & 0 deletions benches/kvfilter_benchmark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
#[macro_use]
extern crate criterion;
#[macro_use]
extern crate slog;
extern crate slog_kvfilter;

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

use criterion::Criterion;
use slog::{Drain, Logger, Never, Record, OwnedKVList, Level};
use slog_kvfilter::{EvaluationOrder, FilterSpec, KVFilter};

#[derive(Debug, Clone)]
pub struct CountingDrain {
count: Arc<AtomicUsize>
}

impl Drain for CountingDrain {
type Ok = ();
type Err = Never;

fn log(&self, _: &Record, _: &OwnedKVList) -> Result<(), Never> {
self.count.fetch_add(1, Ordering::Relaxed);
Ok(())
}

#[inline]
fn is_enabled(&self, _: Level) -> bool {
true
}
}

struct Tester {
log: Logger,
count: Arc<AtomicUsize>,
}

impl Tester {
fn assert_count(&self, expected_count: usize) {
let actual_count = self.count.load(Ordering::Relaxed);
assert_eq!(expected_count, actual_count)
}
}

fn new_tester(spec: FilterSpec, evaluation_order: EvaluationOrder) -> Tester {
let count = Arc::new(AtomicUsize::new(0));
let drain = KVFilter::new(
CountingDrain { count: Arc::clone(&count) },
spec,
evaluation_order,
);
Tester {
log: Logger::root(drain.fuse(), o!("key_foo" => "value_foo")),
count,
}
}


// simple AND use_case - useful for comparison with original KVFilter in simple cases
fn simple_and_benchmark(c: &mut Criterion) {
let tester = {
let match_kv = FilterSpec::match_kv;

let filter_spec = FilterSpec::all_of(&[
match_kv("some_key", "some_value"),
match_kv("another_key", "another_value"),
]);

new_tester(filter_spec, EvaluationOrder::LoggerAndMessage)
};

let mut first_iteration = true;
c.bench_function("simple AND", move |b| {
b.iter(|| {
info!(tester.log, "ACCEPT";
"some_key" => "some_value",
"another_key" => "another_value",
);

debug!(tester.log, "REJECT";
"some_key" => "some_value",
);

trace!(tester.log, "REJECT";
"another_key" => "another_value",
"bad_key" => "bad_key",
);

if first_iteration {
tester.assert_count(1);
first_iteration = false;
}
})
});
}

// simple OR use_case - not expressible in original KVFilter
fn simple_or_benchmark(c: &mut Criterion) {
let tester = {
let match_kv = FilterSpec::match_kv;
let level_at_least = FilterSpec::LevelAtLeast;

let filter_spec = FilterSpec::any_of(&[
match_kv("debug_key", "debug_value").and(level_at_least(Level::Debug)),
match_kv("trace_key", "trace_value").and(level_at_least(Level::Trace)),
]);

new_tester(filter_spec, EvaluationOrder::LoggerAndMessage)
};

let mut first_iteration = true;
c.bench_function("simple OR", move |b| {
b.iter(|| {
info!(tester.log, "REJECT";
"some_key" => "some_value",
"another_key" => "another_value",
);

debug!(tester.log, "ACCEPT";
"another_key" => "another_value",
"debug_key" => "debug_value",
);

trace!(tester.log, "REJECT";
"debug_key" => "debug_value",
"another_key" => "another_value",
);

trace!(tester.log, "ACCEPT";
"trace_key" => "trace_value",
"another_key" => "another_value",
);

if first_iteration {
tester.assert_count(2);
first_iteration = false;
}
})
});
}

// @przygienda use-case
fn przygienda_tester() -> Tester {
let match_any = FilterSpec::match_any_value;

// (key1 must be either value1a or value1b) AND key2 must be value2
let positive_filter = FilterSpec::all_of(&[
match_any(
"some_key",
&[
"some_value_1",
"some_value_2",
"some_value_3",
"some_value_4",
"foo",
],
),
match_any(
"another_key",
&[
"another_value_1",
"another_value_2",
"another_value_3",
"another_value_4",
"bar",
],
),
match_any(
"key_foo",
&[
"foo_value_1",
"foo_value_2",
"foo_value_3",
"foo_value_4",
"value_foo",
],
),
match_any(
"bar_key",
&[
"bar_value_1",
"bar_value_2",
"bar_value_3",
"bar_value_4",
"xyz",
],
),
match_any(
"ultimate_key",
&[
"ultimate_value_1",
"ultimate_value_2",
"ultimate_value_3",
"ultimate_value_4",
"xyz",
],
),
]);

// neg_key1 must be neg_value1 OR neg_key2 must be neg_value2a OR neg_key2 must be neg_value2b
let negative_filter = FilterSpec::any_of(&[
match_any(
"some_negative_key",
&[
"some_value_1",
"some_value_2",
"some_value_3",
"some_value_4",
"foo",
],
),
match_any(
"another_negative_key",
&[
"some_value_1",
"some_value_2",
"some_value_3",
"some_value_4",
"foo",
],
),
]);

let filter_spec = positive_filter.and(negative_filter.not());
new_tester(filter_spec, EvaluationOrder::LoggerAndMessage)
}


fn przygienda_benchmark(c: &mut Criterion) {
let tester = przygienda_tester();
let mut first_iteration = true;
c.bench_function("przygienda", move |b| {
b.iter(|| {
info!(tester.log, "ACCEPT";
"some_key" => "some_value_4",
"another_key" => "another_value_1",
"bar_key" => "bar_value_3",
"ultimate_key" => "ultimate_value_3",
);

info!(tester.log, "REJECT - negative filter";
"some_key" => "some_value_4",
"another_key" => "another_value_1",
"bar_key" => "bar_value_3",
"ultimate_key" => "ultimate_value_3",
"some_negative_key" => "foo"
);

info!(tester.log, "REJECT - not all keys present";
"some_key" => "some_value_4",
"another_key" => "another_value_1",
);

if first_iteration {
tester.assert_count(1);
first_iteration = false;
}
})
});
}

criterion_group!(benches, simple_and_benchmark, przygienda_benchmark, simple_or_benchmark);
criterion_main!(benches);
Loading