From 378fab41eab7e2e599a22592a8d5a2142aff2bdf Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 9 Oct 2025 06:44:43 +0800 Subject: [PATCH 1/5] feat: new version and tokio local key diags Signed-off-by: tison --- Cargo.toml | 26 ++++++++++++------------- appenders/async/Cargo.toml | 2 +- appenders/fastrace/Cargo.toml | 2 +- appenders/file/Cargo.toml | 2 +- appenders/journald/Cargo.toml | 2 +- appenders/opentelemetry/Cargo.toml | 2 +- appenders/syslog/Cargo.toml | 2 +- bridges/log/Cargo.toml | 2 +- core/Cargo.toml | 2 +- diagnostics/fastrace/Cargo.toml | 2 +- layouts/google-cloud-logging/Cargo.toml | 2 +- layouts/json/Cargo.toml | 2 +- layouts/logfmt/Cargo.toml | 2 +- layouts/text/Cargo.toml | 2 +- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 873ca57..58a7b32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,19 +34,19 @@ rust-version = "1.85.0" [workspace.dependencies] # Workspace dependencies -logforth-append-async = { version = "0.2.1", path = "appenders/async" } -logforth-append-fastrace = { version = "0.2.1", path = "appenders/fastrace" } -logforth-append-file = { version = "0.2.1", path = "appenders/file" } -logforth-append-journald = { version = "0.2.1", path = "appenders/journald" } -logforth-append-opentelemetry = { version = "0.2.1", path = "appenders/opentelemetry" } -logforth-append-syslog = { version = "0.2.1", path = "appenders/syslog" } -logforth-bridge-log = { version = "0.2.1", path = "bridges/log" } -logforth-core = { version = "0.2.1", path = "core" } -logforth-diagnostic-fastrace = { version = "0.2.1", path = "diagnostics/fastrace" } -logforth-layout-google-cloud-logging = { version = "0.2.1", path = "layouts/google-cloud-logging" } -logforth-layout-json = { version = "0.2.1", path = "layouts/json" } -logforth-layout-logfmt = { version = "0.2.1", path = "layouts/logfmt" } -logforth-layout-text = { version = "0.2.1", path = "layouts/text" } +logforth-append-async = { version = "0.3.0", path = "appenders/async" } +logforth-append-fastrace = { version = "0.3.0", path = "appenders/fastrace" } +logforth-append-file = { version = "0.3.0", path = "appenders/file" } +logforth-append-journald = { version = "0.3.0", path = "appenders/journald" } +logforth-append-opentelemetry = { version = "0.3.0", path = "appenders/opentelemetry" } +logforth-append-syslog = { version = "0.3.0", path = "appenders/syslog" } +logforth-bridge-log = { version = "0.3.0", path = "bridges/log" } +logforth-core = { version = "0.3.0", path = "core" } +logforth-diagnostic-fastrace = { version = "0.3.0", path = "diagnostics/fastrace" } +logforth-layout-google-cloud-logging = { version = "0.3.0", path = "layouts/google-cloud-logging" } +logforth-layout-json = { version = "0.3.0", path = "layouts/json" } +logforth-layout-logfmt = { version = "0.3.0", path = "layouts/logfmt" } +logforth-layout-text = { version = "0.3.0", path = "layouts/text" } # Crates.io dependencies anyhow = { version = "1.0" } diff --git a/appenders/async/Cargo.toml b/appenders/async/Cargo.toml index 8cda614..2f39ae7 100644 --- a/appenders/async/Cargo.toml +++ b/appenders/async/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-append-async" -version = "0.2.1" +version = "0.3.0" description = "Asynchronous appender for Logforth." keywords = ["logging", "log", "async"] diff --git a/appenders/fastrace/Cargo.toml b/appenders/fastrace/Cargo.toml index 4fcaaf2..3fb9758 100644 --- a/appenders/fastrace/Cargo.toml +++ b/appenders/fastrace/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-append-fastrace" -version = "0.2.1" +version = "0.3.0" description = "Fastrace appender for Logforth." keywords = ["logging", "log", "fastrace"] diff --git a/appenders/file/Cargo.toml b/appenders/file/Cargo.toml index 12500c0..9fdde2a 100644 --- a/appenders/file/Cargo.toml +++ b/appenders/file/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-append-file" -version = "0.2.1" +version = "0.3.0" description = "File appender for Logforth with optional rollover strategy." keywords = ["logging", "log", "file-appender"] diff --git a/appenders/journald/Cargo.toml b/appenders/journald/Cargo.toml index d3ad5c3..8aace1f 100644 --- a/appenders/journald/Cargo.toml +++ b/appenders/journald/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-append-journald" -version = "0.2.1" +version = "0.3.0" description = "Journald appender for Logforth." keywords = ["logging", "log", "journald"] diff --git a/appenders/opentelemetry/Cargo.toml b/appenders/opentelemetry/Cargo.toml index 1255c8e..097dff7 100644 --- a/appenders/opentelemetry/Cargo.toml +++ b/appenders/opentelemetry/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-append-opentelemetry" -version = "0.2.1" +version = "0.3.0" description = "Opemtelemetry appender for Logforth." keywords = ["logging", "log", "opentelemtry"] diff --git a/appenders/syslog/Cargo.toml b/appenders/syslog/Cargo.toml index b2d7e8d..4096561 100644 --- a/appenders/syslog/Cargo.toml +++ b/appenders/syslog/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-append-syslog" -version = "0.2.1" +version = "0.3.0" description = "Syslog appender for Logforth." keywords = ["logging", "log", "syslog"] diff --git a/bridges/log/Cargo.toml b/bridges/log/Cargo.toml index 818296d..a21408e 100644 --- a/bridges/log/Cargo.toml +++ b/bridges/log/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-bridge-log" -version = "0.2.1" +version = "0.3.0" description = "Bridge Logforth with the log crate." keywords = ["logging", "log"] diff --git a/core/Cargo.toml b/core/Cargo.toml index bf05af1..85e8557 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-core" -version = "0.2.1" +version = "0.3.0" description = "Core structs and functions for Logforth." keywords = ["logging", "log"] diff --git a/diagnostics/fastrace/Cargo.toml b/diagnostics/fastrace/Cargo.toml index 7ba3525..84b126f 100644 --- a/diagnostics/fastrace/Cargo.toml +++ b/diagnostics/fastrace/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-diagnostic-fastrace" -version = "0.2.1" +version = "0.3.0" description = "Fastrace diagnostic for Logforth." keywords = ["logging", "log", "fastrace"] diff --git a/layouts/google-cloud-logging/Cargo.toml b/layouts/google-cloud-logging/Cargo.toml index b883aab..b834699 100644 --- a/layouts/google-cloud-logging/Cargo.toml +++ b/layouts/google-cloud-logging/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-layout-google-cloud-logging" -version = "0.2.1" +version = "0.3.0" description = "Google Cloud Structured Logging layout for Logforth." keywords = ["logging", "log", "google-cloud-logging"] diff --git a/layouts/json/Cargo.toml b/layouts/json/Cargo.toml index e84c300..df32123 100644 --- a/layouts/json/Cargo.toml +++ b/layouts/json/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-layout-json" -version = "0.2.1" +version = "0.3.0" description = "JSON layout for Logforth." keywords = ["logging", "log", "json"] diff --git a/layouts/logfmt/Cargo.toml b/layouts/logfmt/Cargo.toml index 4b1fb6c..bbe1c03 100644 --- a/layouts/logfmt/Cargo.toml +++ b/layouts/logfmt/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-layout-logfmt" -version = "0.2.1" +version = "0.3.0" description = "Logfmt layout for Logforth." keywords = ["logging", "log", "logfmt"] diff --git a/layouts/text/Cargo.toml b/layouts/text/Cargo.toml index e514e93..fe25634 100644 --- a/layouts/text/Cargo.toml +++ b/layouts/text/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "logforth-layout-text" -version = "0.2.1" +version = "0.3.0" description = "Optionally colored text layout for Logforth." keywords = ["logging", "log", "colored", "terminal"] From 35da3e68ff255e830c31bfe6c57f150b01bab209 Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 9 Oct 2025 09:13:37 +0800 Subject: [PATCH 2/5] impl Signed-off-by: tison --- Cargo.toml | 1 + diagnostics/tokio-task-local/Cargo.toml | 42 +++++++++++++ diagnostics/tokio-task-local/src/lib.rs | 63 ++++++++++++++++++++ diagnostics/tokio-task-local/tests/simple.rs | 46 ++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 diagnostics/tokio-task-local/Cargo.toml create mode 100644 diagnostics/tokio-task-local/src/lib.rs create mode 100644 diagnostics/tokio-task-local/tests/simple.rs diff --git a/Cargo.toml b/Cargo.toml index 58a7b32..0c07da9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,7 @@ rand = { version = "0.9" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0" } tempfile = { version = "3.16" } +tokio = { version = "1.47.1" } value-bag = { version = "1.11.1", features = ["inline-i128", "owned", "sval"] } [workspace.lints.rust] diff --git a/diagnostics/tokio-task-local/Cargo.toml b/diagnostics/tokio-task-local/Cargo.toml new file mode 100644 index 0000000..d2f1ce2 --- /dev/null +++ b/diagnostics/tokio-task-local/Cargo.toml @@ -0,0 +1,42 @@ +# Copyright 2024 FastLabs Developers +# +# 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. + +[package] +name = "logforth-diagnostic-tokio-task-local" +version = "0.3.0" + +description = "Tokio task-local diagnostic for Logforth." +keywords = ["logging", "log", "tokio"] + +categories.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +rust-version.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +logforth-core = { workspace = true } +tokio = { workspace = true, features = ["rt"] } + +[dev-dependencies] +tokio = { workspace = true, features = ["full"] } + +[lints] +workspace = true diff --git a/diagnostics/tokio-task-local/src/lib.rs b/diagnostics/tokio-task-local/src/lib.rs new file mode 100644 index 0000000..be34475 --- /dev/null +++ b/diagnostics/tokio-task-local/src/lib.rs @@ -0,0 +1,63 @@ +// Copyright 2024 FastLabs Developers +// +// 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. + +//! A diagnostic that enriches log records with tokio task-local context. + +use logforth_core::Diagnostic; +use logforth_core::Error; +use logforth_core::kv::Key; +use logforth_core::kv::Value; +use logforth_core::kv::Visitor; +use tokio::task_local; + +task_local! { + static CONTEXT: Vec<(String, String)>; +} + +/// A diagnostic that enriches log records with tokio task-local context. +#[derive(Default, Debug, Clone, Copy)] +#[non_exhaustive] +pub struct TaskLocalDiagnostic {} + +impl Diagnostic for TaskLocalDiagnostic { + fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> { + CONTEXT.with(|map| { + for (k, v) in map { + let key = Key::new_ref(k.as_str()); + let value = Value::from(v); + visitor.visit(key, value)?; + } + Ok(()) + }) + } +} + +/// blablabla +pub trait FutureExt: Future { + /// Run a future with a task-local context. + fn with_task_local_context( + self, + kvs: impl IntoIterator, + ) -> impl Future + where + Self: Sized, + Self::Output: 'static, + { + let mut context = CONTEXT.try_with(|v| v.clone()).unwrap_or_default(); + context.extend(kvs); + CONTEXT.scope(context, self) + } +} + +impl FutureExt for F {} diff --git a/diagnostics/tokio-task-local/tests/simple.rs b/diagnostics/tokio-task-local/tests/simple.rs new file mode 100644 index 0000000..5924f2d --- /dev/null +++ b/diagnostics/tokio-task-local/tests/simple.rs @@ -0,0 +1,46 @@ +// Copyright 2024 FastLabs Developers +// +// 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. + +//! sss + +#[test] +fn test() { + use logforth_core::Diagnostic; + use logforth_core::kv::Key; + use logforth_core::kv::Value; + use logforth_core::kv::Visitor; + use logforth_diagnostic_tokio_task_local::FutureExt; + use logforth_diagnostic_tokio_task_local::TaskLocalDiagnostic; + use tokio::runtime::Runtime; + + struct PrintVisitor; + + impl Visitor for PrintVisitor { + fn visit(&mut self, key: Key<'_>, value: Value) -> Result<(), logforth_core::Error> { + println!("{}: {}", key.as_str(), value); + Ok(()) + } + } + + let rt = Runtime::new().unwrap(); + + rt.block_on( + async { + let diag = TaskLocalDiagnostic::default(); + diag.visit(&mut PrintVisitor).unwrap(); + } + .with_task_local_context([("user_id".to_string(), "42".to_string())]) + .with_task_local_context([("request_id".to_string(), "abc123".to_string())]), + ); +} From a149d22630a06f85d057511d3d1d296381a86006 Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 10 Oct 2025 17:27:59 +0800 Subject: [PATCH 3/5] revert Signed-off-by: tison --- diagnostics/tokio-task-local/Cargo.toml | 42 ------------- diagnostics/tokio-task-local/src/lib.rs | 63 -------------------- diagnostics/tokio-task-local/tests/simple.rs | 46 -------------- 3 files changed, 151 deletions(-) delete mode 100644 diagnostics/tokio-task-local/Cargo.toml delete mode 100644 diagnostics/tokio-task-local/src/lib.rs delete mode 100644 diagnostics/tokio-task-local/tests/simple.rs diff --git a/diagnostics/tokio-task-local/Cargo.toml b/diagnostics/tokio-task-local/Cargo.toml deleted file mode 100644 index d2f1ce2..0000000 --- a/diagnostics/tokio-task-local/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2024 FastLabs Developers -# -# 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. - -[package] -name = "logforth-diagnostic-tokio-task-local" -version = "0.3.0" - -description = "Tokio task-local diagnostic for Logforth." -keywords = ["logging", "log", "tokio"] - -categories.workspace = true -edition.workspace = true -homepage.workspace = true -license.workspace = true -readme.workspace = true -repository.workspace = true -rust-version.workspace = true - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[dependencies] -logforth-core = { workspace = true } -tokio = { workspace = true, features = ["rt"] } - -[dev-dependencies] -tokio = { workspace = true, features = ["full"] } - -[lints] -workspace = true diff --git a/diagnostics/tokio-task-local/src/lib.rs b/diagnostics/tokio-task-local/src/lib.rs deleted file mode 100644 index be34475..0000000 --- a/diagnostics/tokio-task-local/src/lib.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2024 FastLabs Developers -// -// 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. - -//! A diagnostic that enriches log records with tokio task-local context. - -use logforth_core::Diagnostic; -use logforth_core::Error; -use logforth_core::kv::Key; -use logforth_core::kv::Value; -use logforth_core::kv::Visitor; -use tokio::task_local; - -task_local! { - static CONTEXT: Vec<(String, String)>; -} - -/// A diagnostic that enriches log records with tokio task-local context. -#[derive(Default, Debug, Clone, Copy)] -#[non_exhaustive] -pub struct TaskLocalDiagnostic {} - -impl Diagnostic for TaskLocalDiagnostic { - fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> { - CONTEXT.with(|map| { - for (k, v) in map { - let key = Key::new_ref(k.as_str()); - let value = Value::from(v); - visitor.visit(key, value)?; - } - Ok(()) - }) - } -} - -/// blablabla -pub trait FutureExt: Future { - /// Run a future with a task-local context. - fn with_task_local_context( - self, - kvs: impl IntoIterator, - ) -> impl Future - where - Self: Sized, - Self::Output: 'static, - { - let mut context = CONTEXT.try_with(|v| v.clone()).unwrap_or_default(); - context.extend(kvs); - CONTEXT.scope(context, self) - } -} - -impl FutureExt for F {} diff --git a/diagnostics/tokio-task-local/tests/simple.rs b/diagnostics/tokio-task-local/tests/simple.rs deleted file mode 100644 index 5924f2d..0000000 --- a/diagnostics/tokio-task-local/tests/simple.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2024 FastLabs Developers -// -// 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. - -//! sss - -#[test] -fn test() { - use logforth_core::Diagnostic; - use logforth_core::kv::Key; - use logforth_core::kv::Value; - use logforth_core::kv::Visitor; - use logforth_diagnostic_tokio_task_local::FutureExt; - use logforth_diagnostic_tokio_task_local::TaskLocalDiagnostic; - use tokio::runtime::Runtime; - - struct PrintVisitor; - - impl Visitor for PrintVisitor { - fn visit(&mut self, key: Key<'_>, value: Value) -> Result<(), logforth_core::Error> { - println!("{}: {}", key.as_str(), value); - Ok(()) - } - } - - let rt = Runtime::new().unwrap(); - - rt.block_on( - async { - let diag = TaskLocalDiagnostic::default(); - diag.visit(&mut PrintVisitor).unwrap(); - } - .with_task_local_context([("user_id".to_string(), "42".to_string())]) - .with_task_local_context([("request_id".to_string(), "abc123".to_string())]), - ); -} From c66b21ea794e4d6e289a71938d4030e5a573373c Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 10 Oct 2025 18:16:35 +0800 Subject: [PATCH 4/5] reimpl Signed-off-by: tison --- .github/workflows/ci.yml | 1 + Cargo.toml | 2 + core/src/diagnostic/thread_local.rs | 8 +- diagnostics/task-local/Cargo.toml | 42 +++++++++ diagnostics/task-local/src/lib.rs | 139 ++++++++++++++++++++++++++++ logforth/Cargo.toml | 9 ++ logforth/examples/task_local.rs | 46 +++++++++ logforth/src/lib.rs | 4 + 8 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 diagnostics/task-local/Cargo.toml create mode 100644 diagnostics/task-local/src/lib.rs create mode 100644 logforth/examples/task_local.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d19eeb8..c518e2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,6 +89,7 @@ jobs: cargo run --features="starter-log" --example json_stdout cargo run --features="starter-log" --example rolling_file cargo run --features="starter-log" --example single_file + cargo run --features="starter-log,diagnostic-task-local" --example task_local cargo run --features="starter-log,append-async" --example asynchronous cargo run --features="starter-log,diagnostic-fastrace,layout-google-cloud-logging" --example google_cloud_logging cargo run --features="starter-log,append-fastrace,diagnostic-fastrace" --example fastrace diff --git a/Cargo.toml b/Cargo.toml index 0c07da9..d1b7d7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ logforth-append-syslog = { version = "0.3.0", path = "appenders/syslog" } logforth-bridge-log = { version = "0.3.0", path = "bridges/log" } logforth-core = { version = "0.3.0", path = "core" } logforth-diagnostic-fastrace = { version = "0.3.0", path = "diagnostics/fastrace" } +logforth-diagnostic-task-local = { version = "0.3.0", path = "diagnostics/task-local" } logforth-layout-google-cloud-logging = { version = "0.3.0", path = "layouts/google-cloud-logging" } logforth-layout-json = { version = "0.3.0", path = "layouts/json" } logforth-layout-logfmt = { version = "0.3.0", path = "layouts/logfmt" } @@ -62,6 +63,7 @@ log = { version = "0.4.27", features = ["kv_std", "kv_sval"] } opentelemetry = { version = "0.31.0", default-features = false } opentelemetry-otlp = { version = "0.31.0", default-features = false } opentelemetry_sdk = { version = "0.31.0", default-features = false } +pin-project = { version = "1.1.10" } rand = { version = "0.9" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0" } diff --git a/core/src/diagnostic/thread_local.rs b/core/src/diagnostic/thread_local.rs index 6f84a55..6f1b1b8 100644 --- a/core/src/diagnostic/thread_local.rs +++ b/core/src/diagnostic/thread_local.rs @@ -22,7 +22,7 @@ use crate::kv::Value; use crate::kv::Visitor; thread_local! { - static CONTEXT: RefCell> = const { RefCell::new(BTreeMap::new()) }; + static THREAD_LOCAL_MAP: RefCell> = const { RefCell::new(BTreeMap::new()) }; } /// A diagnostic that stores key-value pairs in a thread-local map. @@ -45,14 +45,14 @@ impl ThreadLocalDiagnostic { K: Into, V: Into, { - CONTEXT.with(|map| { + THREAD_LOCAL_MAP.with(|map| { map.borrow_mut().insert(key.into(), value.into()); }); } /// Remove a key-value pair from the thread local diagnostic. pub fn remove(key: &str) { - CONTEXT.with(|map| { + THREAD_LOCAL_MAP.with(|map| { map.borrow_mut().remove(key); }); } @@ -60,7 +60,7 @@ impl ThreadLocalDiagnostic { impl Diagnostic for ThreadLocalDiagnostic { fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> { - CONTEXT.with(|map| { + THREAD_LOCAL_MAP.with(|map| { let map = map.borrow(); for (key, value) in map.iter() { let key = Key::new_ref(key.as_str()); diff --git a/diagnostics/task-local/Cargo.toml b/diagnostics/task-local/Cargo.toml new file mode 100644 index 0000000..d69cf8b --- /dev/null +++ b/diagnostics/task-local/Cargo.toml @@ -0,0 +1,42 @@ +# Copyright 2024 FastLabs Developers +# +# 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. + +[package] +name = "logforth-diagnostic-task-local" +version = "0.3.0" + +description = "Task-local diagnostic for Logforth." +keywords = ["logging", "log", "async", "task", "future"] + +categories.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +rust-version.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +logforth-core = { workspace = true } +pin-project = { workspace = true } + +[dev-dependencies] +log = { workspace = true } + +[lints] +workspace = true diff --git a/diagnostics/task-local/src/lib.rs b/diagnostics/task-local/src/lib.rs new file mode 100644 index 0000000..5fa1319 --- /dev/null +++ b/diagnostics/task-local/src/lib.rs @@ -0,0 +1,139 @@ +// Copyright 2024 FastLabs Developers +// +// 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. + +//! A diagnostic that stores key-value pairs in a task-local map. +//! +//! # Examples +//! +//! ``` +//! use logforth_core::Diagnostic; +//! use logforth_core::kv::Visitor; +//! use logforth_diagnostic_task_local::FutureExt; +//! +//! let fut = async { log::info!("Hello, world!") }; +//! fut.with_task_local_context([("key".into(), "value".into())]); +//! ``` + +use std::cell::RefCell; +use std::pin::Pin; +use std::task::Context; +use std::task::Poll; + +use logforth_core::Diagnostic; +use logforth_core::Error; +use logforth_core::kv::Key; +use logforth_core::kv::Value; +use logforth_core::kv::Visitor; + +thread_local! { + static TASK_LOCAL_MAP: RefCell> = const { RefCell::new(Vec::new()) }; +} + +/// A diagnostic that stores key-value pairs in a task-local context. +/// +/// See [module-level documentation](self) for usage examples. +#[derive(Default, Debug, Clone, Copy)] +#[non_exhaustive] +pub struct TaskLocalDiagnostic {} + +impl Diagnostic for TaskLocalDiagnostic { + fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> { + TASK_LOCAL_MAP.with(|map| { + let map = map.borrow(); + for (key, value) in map.iter() { + let key = Key::new_ref(key.as_str()); + let value = Value::from(value.as_str()); + visitor.visit(key, value)?; + } + Ok(()) + }) + } +} + +/// An extension trait for futures to run them with a task-local context. +/// +/// See [module-level documentation](self) for usage examples. +pub trait FutureExt: Future { + /// Run a future with a task-local context. + fn with_task_local_context( + self, + kvs: impl IntoIterator, + ) -> impl Future + where + Self: Sized, + { + TaskLocalFuture { + future: Some(self), + context: kvs.into_iter().collect(), + } + } +} + +impl FutureExt for F {} + +#[pin_project::pin_project] +struct TaskLocalFuture { + #[pin] + future: Option, + context: Vec<(String, String)>, +} + +impl Future for TaskLocalFuture { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + let mut fut = this.future; + if let Some(future) = fut.as_mut().as_pin_mut() { + struct Guard { + n: usize, + } + + impl Drop for Guard { + fn drop(&mut self) { + TASK_LOCAL_MAP.with(|map| { + let mut map = map.borrow_mut(); + for _ in 0..self.n { + map.pop(); + } + }); + } + } + + TASK_LOCAL_MAP.with(|map| { + let mut map = map.borrow_mut(); + for (key, value) in this.context.iter() { + map.push((key.clone(), value.clone())); + } + }); + + let n = this.context.len(); + let guard = Guard { n }; + + let result = match future.poll(cx) { + Poll::Ready(output) => { + fut.set(None); + Poll::Ready(output) + } + Poll::Pending => Poll::Pending, + }; + + drop(guard); + return result; + } + + unreachable!("TaskLocalFuture polled after completion"); + } +} diff --git a/logforth/Cargo.toml b/logforth/Cargo.toml index 2915167..89cd59b 100644 --- a/logforth/Cargo.toml +++ b/logforth/Cargo.toml @@ -56,6 +56,7 @@ layout-text = ["dep:logforth-layout-text"] # Diagnostics diagnostic-fastrace = ["dep:logforth-diagnostic-fastrace"] +diagnostic-task-local = ["dep:logforth-diagnostic-task-local"] # Standalone features native-tls = ["logforth-append-syslog?/native-tls"] @@ -73,6 +74,7 @@ logforth-append-opentelemetry = { workspace = true, optional = true } logforth-append-syslog = { workspace = true, optional = true } logforth-bridge-log = { workspace = true, optional = true } logforth-diagnostic-fastrace = { workspace = true, optional = true } +logforth-diagnostic-task-local = { workspace = true, optional = true } logforth-layout-google-cloud-logging = { workspace = true, optional = true } logforth-layout-json = { workspace = true, optional = true } logforth-layout-logfmt = { workspace = true, optional = true } @@ -83,6 +85,7 @@ fastrace = { workspace = true, features = ["enable"] } log = { workspace = true, features = ["kv_serde"] } logforth-append-file = { workspace = true } serde = { workspace = true } +tokio = { workspace = true, features = ["full"] } [lints] workspace = true @@ -140,6 +143,12 @@ name = "single_file" path = "examples/single_file.rs" required-features = ["starter-log"] +[[example]] +doc-scrape-examples = true +name = "task_local" +path = "examples/task_local.rs" +required-features = ["starter-log", "diagnostic-task-local"] + [[example]] doc-scrape-examples = true name = "custom_layout_filter" diff --git a/logforth/examples/task_local.rs b/logforth/examples/task_local.rs new file mode 100644 index 0000000..d7751a6 --- /dev/null +++ b/logforth/examples/task_local.rs @@ -0,0 +1,46 @@ +// Copyright 2024 FastLabs Developers +// +// 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. + +//! Example of using task-local storage with `logForth`. + +use logforth::diagnostic::TaskLocalDiagnostic; +use logforth::diagnostic::task_local::FutureExt; +use logforth::record::LevelFilter; +use logforth_layout_text::TextLayout; + +#[tokio::main] +async fn main() { + logforth::starter_log::builder() + .dispatch(|d| { + d.filter(LevelFilter::All) + .diagnostic(TaskLocalDiagnostic::default()) + .append(logforth::append::Stderr::default().with_layout(TextLayout::default())) + }) + .apply(); + + async { + async { + log::error!("Hello error!"); + log::warn!("Hello warn!"); + log::info!("Hello info!"); + } + .with_task_local_context([("k3".to_string(), "v3".to_string())]) + .await; + log::debug!("Hello debug!"); + log::trace!("Hello trace!"); + } + .with_task_local_context([("k1".to_string(), "v1".to_string())]) + .with_task_local_context([("k2".to_string(), "v2".to_string())]) + .await; +} diff --git a/logforth/src/lib.rs b/logforth/src/lib.rs index b7a0b7d..b5ec29e 100644 --- a/logforth/src/lib.rs +++ b/logforth/src/lib.rs @@ -124,6 +124,10 @@ pub mod diagnostic { pub use logforth_core::diagnostic::*; #[cfg(feature = "diagnostic-fastrace")] pub use logforth_diagnostic_fastrace::FastraceDiagnostic; + #[cfg(feature = "diagnostic-task-local")] + pub use logforth_diagnostic_task_local as task_local; + #[cfg(feature = "diagnostic-task-local")] + pub use logforth_diagnostic_task_local::TaskLocalDiagnostic; } /// Filters for log records. From 9b2db513268043786d5ce382fd25ecd134bc83f2 Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 10 Oct 2025 18:22:06 +0800 Subject: [PATCH 5/5] chaneglog Signed-off-by: tison --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57b436b..82ad510 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ All notable changes to this project will be documented in this file. * Add `Level::Critical` variant to represent critical level logs. * Redesign `LevelFilter`. +### New features + +* Add `logforth-diagnostic-task-local` and `TaskLocalDiagnostic` to support task-local key-value context. + ## [0.28.1] 2025-10-06 ### Documentation changes