Skip to content

Commit

Permalink
feat: implement dynamic enum dispatch targets (#6)
Browse files Browse the repository at this point in the history
Signed-off-by: tison <[email protected]>
  • Loading branch information
tisonkun authored Aug 1, 2024
1 parent d00c8f3 commit f5d8b7a
Show file tree
Hide file tree
Showing 12 changed files with 316 additions and 23 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ jobs:
- name: Run examples
run: |
cargo run --example simple_stdio
cargo run --example fn_layout_filter
cargo run --features="no-color" --example no_color_stdio
cargo run --features="json" --example json_stdio
cargo run --features="json,file" --example rolling_file
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ time = { version = "0.3", features = [
"parsing",
], optional = true }

[[example]]
name = "fn_layout_filter"
path = "examples/fn_layout_filter.rs"

[[example]]
name = "simple_stdio"
path = "examples/simple_stdio.rs"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# LogForth Project
# Logforth Project

A versatile and extensible logging implementation.

Expand Down
48 changes: 48 additions & 0 deletions examples/fn_layout_filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2024 tison <[email protected]>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use log::LevelFilter;
use logforth::BoxDynFilter;
use logforth::BoxDynLayout;
use logforth::DispatchAppend;
use logforth::FilterResult;
use logforth::Logger;
use logforth::StdoutAppend;

fn main() {
let layout = BoxDynLayout::new(|record: &log::Record| {
let message = format!("[box dyn] {}", record.args());
Ok(message.into_bytes())
// ...or
// anyhow::bail!("boom: {}", message)
});

let filter = BoxDynFilter::new(|metadata: &log::Metadata| {
if metadata.level() <= LevelFilter::Info {
FilterResult::Accept
} else {
FilterResult::Reject
}
});

let append = StdoutAppend::default().with_layout(layout);
let append = DispatchAppend::new(append).filter(filter);
Logger::new().add_append(append).apply().unwrap();

log::error!("Hello error!");
log::warn!("Hello warn!");
log::info!("Hello info!");
log::debug!("Hello debug!");
log::trace!("Hello trace!");
}
55 changes: 55 additions & 0 deletions src/append/boxdyn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2024 tison <[email protected]>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt::Debug;

use log::Metadata;
use log::Record;

use crate::Append;
use crate::AppendImpl;

pub struct BoxDynAppend(Box<dyn Append + Send + Sync>);

impl BoxDynAppend {
pub fn new(append: impl Append + Send + Sync + 'static) -> Self {
Self(Box::new(append))
}
}

impl Debug for BoxDynAppend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "BoxDynAppend {{ ... }}")
}
}

impl Append for BoxDynAppend {
fn enabled(&self, metadata: &Metadata) -> bool {
(*self.0).enabled(metadata)
}

fn try_append(&self, record: &Record) -> anyhow::Result<()> {
(*self.0).try_append(record)
}

fn flush(&self) {
(*self.0).flush()
}
}

impl From<BoxDynAppend> for AppendImpl {
fn from(append: BoxDynAppend) -> Self {
AppendImpl::BoxDyn(append)
}
}
57 changes: 57 additions & 0 deletions src/append/boxlog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2024 tison <[email protected]>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt::Debug;

use log::Log;
use log::Metadata;
use log::Record;

use crate::Append;
use crate::AppendImpl;

pub struct BoxLogAppend(Box<dyn Log>);

impl Debug for BoxLogAppend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "BoxLogAppend {{ ... }}")
}
}

impl BoxLogAppend {
pub fn new(log: impl Log + 'static) -> Self {
Self(Box::new(log))
}
}

impl Append for BoxLogAppend {
fn enabled(&self, metadata: &Metadata) -> bool {
(*self.0).enabled(metadata)
}

fn try_append(&self, record: &Record) -> anyhow::Result<()> {
(*self.0).log(record);
Ok(())
}

fn flush(&self) {
(*self.0).flush()
}
}

impl From<BoxLogAppend> for AppendImpl {
fn from(append: BoxLogAppend) -> Self {
AppendImpl::BoxLog(append)
}
}
12 changes: 12 additions & 0 deletions src/append/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.

pub use boxdyn::*;
pub use boxlog::*;
pub use dispatch::*;
#[cfg(feature = "fastrace")]
pub use fastrace::*;
#[cfg(feature = "file")]
pub use file::*;
pub use stdio::*;

mod boxdyn;
mod boxlog;
mod dispatch;
#[cfg(feature = "fastrace")]
mod fastrace;
Expand All @@ -41,6 +45,8 @@ pub trait Append {

#[derive(Debug)]
pub enum AppendImpl {
BoxDyn(BoxDynAppend),
BoxLog(BoxLogAppend),
Dispatch(DispatchAppend),
#[cfg(feature = "fastrace")]
Fastrace(FastraceAppend),
Expand All @@ -53,6 +59,8 @@ pub enum AppendImpl {
impl Append for AppendImpl {
fn enabled(&self, metadata: &log::Metadata) -> bool {
match self {
AppendImpl::BoxDyn(append) => append.enabled(metadata),
AppendImpl::BoxLog(append) => append.enabled(metadata),
AppendImpl::Dispatch(append) => append.enabled(metadata),
#[cfg(feature = "fastrace")]
AppendImpl::Fastrace(append) => append.enabled(metadata),
Expand All @@ -65,6 +73,8 @@ impl Append for AppendImpl {

fn try_append(&self, record: &log::Record) -> anyhow::Result<()> {
match self {
AppendImpl::BoxDyn(append) => append.try_append(record),
AppendImpl::BoxLog(append) => append.try_append(record),
AppendImpl::Dispatch(append) => append.try_append(record),
#[cfg(feature = "fastrace")]
AppendImpl::Fastrace(append) => append.try_append(record),
Expand All @@ -77,6 +87,8 @@ impl Append for AppendImpl {

fn flush(&self) {
match self {
AppendImpl::BoxDyn(append) => append.flush(),
AppendImpl::BoxLog(append) => append.flush(),
AppendImpl::Dispatch(append) => append.flush(),
#[cfg(feature = "fastrace")]
AppendImpl::Fastrace(append) => append.flush(),
Expand Down
58 changes: 58 additions & 0 deletions src/filter/boxdyn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2024 tison <[email protected]>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt::Debug;

use log::Metadata;
use log::Record;

use crate::Filter;
use crate::FilterImpl;
use crate::FilterResult;

pub struct BoxDynFilter(Box<dyn Filter + Send + Sync>);

impl Debug for BoxDynFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "BoxDynFilter {{ ... }}")
}
}

impl BoxDynFilter {
pub fn new(filter: impl Filter + Send + Sync + 'static) -> Self {
Self(Box::new(filter))
}
}

impl Filter for BoxDynFilter {
fn filter(&self, record: &Record) -> FilterResult {
(*self.0).filter(record)
}

fn filter_metadata(&self, metadata: &Metadata) -> FilterResult {
(*self.0).filter_metadata(metadata)
}
}

impl From<BoxDynFilter> for FilterImpl {
fn from(filter: BoxDynFilter) -> Self {
FilterImpl::BoxDyn(filter)
}
}

impl<T: Fn(&Metadata) -> FilterResult> Filter for T {
fn filter_metadata(&self, metadata: &Metadata) -> FilterResult {
self(metadata)
}
}
15 changes: 9 additions & 6 deletions src/filter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use log::Metadata;
use log::Record;
pub use boxdyn::BoxDynFilter;
pub use log_level::LogLevelFilter;

mod boxdyn;
mod log_level;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand All @@ -29,27 +29,30 @@ pub enum FilterResult {
}

pub trait Filter {
fn filter(&self, _record: &Record) -> FilterResult {
fn filter(&self, _record: &log::Record) -> FilterResult {
FilterResult::Neutral
}

fn filter_metadata(&self, metadata: &Metadata) -> FilterResult;
fn filter_metadata(&self, metadata: &log::Metadata) -> FilterResult;
}

#[derive(Debug)]
pub enum FilterImpl {
BoxDyn(BoxDynFilter),
LogLevel(LogLevelFilter),
}

impl Filter for FilterImpl {
fn filter(&self, record: &Record) -> FilterResult {
fn filter(&self, record: &log::Record) -> FilterResult {
match self {
FilterImpl::BoxDyn(filter) => filter.filter(record),
FilterImpl::LogLevel(filter) => filter.filter(record),
}
}

fn filter_metadata(&self, metadata: &Metadata) -> FilterResult {
fn filter_metadata(&self, metadata: &log::Metadata) -> FilterResult {
match self {
FilterImpl::BoxDyn(filter) => filter.filter_metadata(metadata),
FilterImpl::LogLevel(filter) => filter.filter_metadata(metadata),
}
}
Expand Down
Loading

0 comments on commit f5d8b7a

Please sign in to comment.