All notable changes to this project will be documented in this file.
- Bump minimum supported Rust version (MSRV) to 1.89.0.
Appendhas no moreexitmethod. Users should composelogforth::core::default_logger().flush()with their own graceful shutdown logic.Asyncappender'sflushmethod is now blocking until all buffered logs are flushed by worker threads. Any errors during flushing will be propagated back to theflushcaller.Record::payloadis nowstd::fmt::Argumentsinstead ofCow<'static, str>.RecordOwned::as_recordhas been removed; useRecordOwned::withinstead. (This is a limitation of Rust as described here.)
Record::target_staticshould return&'static strinstead of&str.
- Rename
DefaultTraptoBestEffortTrapfor better clarity. - Rename
logforth_core::record::MetadatatoFilterCriteria. - Redesign
LevelFilterto allow different comparison methods. - Redesign
Levelas opentelemetry severity numbers.- Add
Level::Fatalvariant to represent fatal level logs.
- Add
LogStarterStdStreamBuilder's setter follows builder pattern.LogStarterStdStreamBuilder::with_layoutis nowlayout.LogStarterStdStreamBuilder::with_filteris nowfilter.
- Add
logforth-diagnostic-task-localandTaskLocalDiagnosticto support task-local key-value context.
- Fix
FastraceDiagnosticdoes not formattrace_idandspan_idas hex strings.
doc_auto_cfgis nowdoc_cfg.
- To work with the
logcrate, now it's recommended to add the "starter-log" feature flag and set both:fn main() { logforth::starter_log::builder().apply(); }
TextLayoutis now behindlayout-textfeature flag, and colored is always available when the feature is enabled.EnvFilteris now self-hosted. Some methods may be changed, but the general user experience should retain:EnvFilter's constructors (from_env, etc.) are moved toEnvFilterBuilder.
- There is no longer
NonBlockingrelated logics.
SingleFileappender is removed. You can replace it withappend::File.RollingFileis nowFileand is behindappend-fileflag.Fileappender now requiresfilenamewhen constructing.File'sfilename_prefixis now renamed to mandatoryfilename.File'smax_log_filesnow takesNonZeroUsize.File's rollover strategy methods has been changed:max_file_size->rollover_sizeand takesNonZeroUsizerotation->rollover_minutely,rollover_hourly,rollover_daily- By default, no rollover is performed.
- Rollover filename strategy has been changed:
given: filename = app filename_suffix = log max_log_files = 3 before rollover: app.log app.1.log app.2.log after rollover: app.log app.1.log - old app.log app.2.log - old app.1.log - old app.2.log deleted
- All interfaces that return
anyhow::Resultis now using a result overlogforth::Error. - Internal log structs are migrated from
logcrate to self-hosted types. This should not affect most users, but if you are customizing appender, layout, filter, and diagnostic, you should replacelog::Record,log::Metadata, orlog::Level, withlogforth::Record,logforth::Metadata, orlogforth::Level. - All components are factored out into their own crates.
JsonLayoutnow collects diagnostics context into a separate fielddiags.- Upgrade to opentelemetry 0.31.0.
- Timestamp format in
TextLayout,JsonLayout, andLogfmtLayoutis changed from RFC 9557 to RFC 3339 format.- That is, from "2025-01-10T15:22:37.868815+08:00[Asia/Shanghai]" to "2025-01-10T15:22:37.868815+08:00".
PlainTextLayoutis added to support plain text format without any extra dependency.Asyncappender is added to support async logging with configurable buffer size and worker threads.Traptrait and a defaultDefaultTrapis added to support handling internal errors.
- Bump the Rust edition to 2024.
- Claim stabilize targets for the crate.
RollingFileappender now correctly delete old files on systems wherecreate_timeis not available (#142)
- Implement
Testingappender that sends logs toeprintln!.
OpenTelemetryLogBuild::buildis now infallible.- Upgrade to opentelemetry 0.30.0.
- Feature flags are renamed with prefix like
append-rolling-fileandlayout-json. OpenTelemetryLogappender now acceptsMakeBodyoverLayout.Filter::matchesandFilter::enablednow take an extra&[Box<dyn Diagnostic>]argument to retrieve the mapped diagnostics context.
OpenTelemetryLogBuild::newnow acceptsopentelemetry_otlp::LogExporter.- Bump minimum supported Rust version (MSRV) to 1.85.0.
FastraceDiagnosticnow exportssampledattribute.
Diagnosticis now a trait.Visitor's method signature is simplified.Append::flushis now fallible.Diagnostic's andVisitor'svisitmethods are fallible.NonBlockingrelated types and the feature flag are now private.logforth::Builderis renamed tologforth::LoggerBuilder.LoggerBuilderhas no longer an option to configure the globalmax_level. Check its documentation for more details.- Constructing
RollingFileandSyslogappender is heavily simplified.
Before:
fn construct_rolling_file() {
let rolling_writer = RollingFileWriter::builder()
.rotation(Rotation::Daily)
.filename_prefix("app_log")
.build("logs")
.unwrap();
let (non_blocking, _guard) = rolling_file::non_blocking(rolling_writer).finish();
logforth::builder()
.dispatch(|d| {
d.filter(log::LevelFilter::Trace)
.append(RollingFile::new(non_blocking).with_layout(JsonLayout::default()))
})
.apply();
}
fn construct_syslog() {
let syslog_writer = SyslogWriter::tcp_well_known().unwrap();
let (non_blocking, _guard) = syslog::non_blocking(syslog_writer).finish();
logforth::builder()
.dispatch(|d| {
d.filter(log::LevelFilter::Trace)
.append(Syslog::new(non_blocking))
})
.apply();
}After:
fn construct_rolling_file() {
let (rolling_writer, _guard) = RollingFileBuilder::new("logs")
.layout(JsonLayout::default())
.rotation(Rotation::Daily)
.filename_prefix("app_log")
.build()
.unwrap();
logforth::builder()
.dispatch(|d| d.filter(log::LevelFilter::Trace).append(rolling_writer))
.apply();
}
fn construct_syslog() {
let (append, _guard) = SyslogBuilder::tcp_well_known().unwrap().build();
logforth::builder()
.dispatch(|d| d.filter(log::LevelFilter::Trace).append(append))
.apply();
}- Add
LogfmtLayoutto support logfmt format. - Add
GoogleStructuredLogLayoutto support Google structured log format. LoggerBuildernow has abuildmethod to construct theLoggerfor use.
- Upgrade to opentelemetry 0.29.0 to avoid transitive dependency to axum.
LayoutandFilteras traits- Default features are now empty; no more
coloredin default.
- Add
StaticDiagnosticfor globally configuring context.
- Revisit all
pub(crate)methods:- Expose
Layout::formatand all layout implsformatmethod. - Expose
Nonblockingmethods to make it usable outside this crate.
- Expose
- Upgrade
jiffto 0.2.0 andopentelemetryto 0.28.0.
- Re-export
coloredto decouple the dependency. In addition, replace theno-colorfeature flag with thecoloredfeature flag.
- Bump MSRV to 1.80 for upgrading
coloredto 3.0. - Layout and Appender now accept a new argument
diagnostics: &[Diagnostic]to retrieve the mapped diagnostics context. Users can uselogforth::diagnostic::Visitorto visit the diagnostics context.
- Add
logforth::diagnostic::FastraceDiagnosticto support attaching trace id to as key-value context. - Add
logforth::diagnostic::ThreadLocalDiagnosticto support attaching thread local key-value context.
- Fix minimum version required for env_filter should be 0.1.1 (#87).
- Migrate from
log'skv_unstablefeature tokvfeature (#85).
module_pathis replaced bytargetinJsonLayoutandTextLayout(#82).- Error perform logging now prints error in Debug format (#84)
- Re-export
broadcastandnative_tlsconstructors from fasyslog (#81).
- The mapping between syslog severity and log's level is changed.
log::Level::Erroris mapped tosyslog::Severity::Error(unchanged).log::Level::Warnis mapped tosyslog::Severity::Warning(unchanged).log::Level::Infois mapped tosyslog::Severity::Notice(changed).log::Level::Debugis mapped tosyslog::Severity::Info(changed).log::Level::Traceis mapped tosyslog::Severity::Debug(unchanged).
- Add
journaldfeature to support journald appenders (#80).
- Change the re-export of
syslogtologforth::syslog::fasyslog(#74)
- Add
syslogfeature to support syslog appenders (#72)
Two breaking changes in #72:
rolling_filefeature flag is renamed torolling-file.NonBlockingrelated structures and methods are relocated, now you'd construct a non-blocking like:
fn main() {
let rolling_writer = RollingFileWriter::builder()
.rotation(Rotation::Daily)
.filename_prefix("app_log")
.build("logs")
.unwrap();
let (non_blocking, _guard) = rolling_file::non_blocking(rolling_writer).finish();
logforth::builder()
.dispatch(|d| {
d.filter(log::LevelFilter::Trace)
.append(RollingFile::new(non_blocking).with_layout(JsonLayout::default()))
})
.apply();
}or:
fn main() {
let syslog_writer = SyslogWriter::tcp_well_known().unwrap();
let (non_blocking, _guard) = syslog::non_blocking(syslog_writer).finish();
logforth::builder()
.dispatch(|d| {
d.filter(log::LevelFilter::Trace)
.append(Syslog::new(non_blocking))
})
.apply();
}Note that each NonBlocking now has a type parameter to ensure that they match the corresponding appenders.
Two minor breaking changes in #71:
JsonLayout's fieldtzis now private. You can change it with the newtimezonemethod, likeJsonLayout::default().timezone(TimeZone::UTC).DispatchBuildernow always acceptsfilterfirst, and thenappend. Once anappendis configured, no morefiltercan be added. This is for a strict order on config so that the code is more consistent.
API is further improve in both #69 and #70.
Now the logger build logic is like:
use log::LevelFilter;
use logforth::append;
use logforth::layout::JsonLayout;
fn main() {
logforth::builder()
.dispatch(|b| b.filter(LevelFilter::Debug).append(append::Stderr::default().with_layout(JsonLayout::default())))
.dispatch(|b| b.filter(LevelFilter::Info).append(append::Stdout::default().with_layout(JsonLayout::default())))
.apply();
}And we provide a convenient way to build the logger with default setup (stderr or stdout, with RUST_LOG envvar respected):
fn main() {
logforth::stderr().apply();
// or logforth::stdout().apply(); // for logging to stdout
}-
refactor: layouts and encoders should be nested to appenders (#64)
Previous code:
fn main() { Logger::new() .dispatch( Dispatch::new() .filter(LevelFilter::Trace) .layout(JsonLayout::default()) .append(append::Stdout), ) .apply() .unwrap(); log::error!("Hello error!"); log::warn!("Hello warn!"); log::info!("Hello info!"); log::debug!("Hello debug!"); log::trace!("Hello trace!"); }
New code:
fn main() { Logger::new() .dispatch( Dispatch::new() .filter(LevelFilter::Trace) .append(append::Stdout::default().with_layout(JsonLayout::default())), ) .apply() .unwrap(); log::error!("Hello error!"); log::warn!("Hello warn!"); log::info!("Hello info!"); log::debug!("Hello debug!"); log::trace!("Hello trace!"); }
Besides, the default layout of Stdout, Stderr, and RollingFile is changed from
IdenticalLayouttoTextLayout. -
refactor: unify level/target filter to directive filter (#65)
Most
Fromconversions are kept so that typically you won't notice the change. But if you directly useLevelFilterandTargetFilter, they are now removed. The functionalities can be covered byEnvFilter.Also, the feature flag
env-filteris removed. TheEnvFilteris always available now.