Skip to content

Commit

Permalink
Log to file as JSON-format
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen committed Dec 1, 2024
1 parent 3f2baf8 commit c6be48c
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 14 deletions.
6 changes: 2 additions & 4 deletions crates/bevy_log/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ bevy_app = { path = "../bevy_app", version = "0.15.0" }
bevy_utils = { path = "../bevy_utils", version = "0.15.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0" }

tracing-subscriber = { version = "0.3.1", features = [
"registry",
"env-filter",
] }
tracing-subscriber = { version = "0.3.1", features = ["registry", "env-filter", "json"] }
tracing-chrome = { version = "0.7.0", optional = true }
tracing-log = "0.2.0"
tracing-error = { version = "0.2.0", optional = true }
tracing-appender = "0.2"

# Tracy dependency compatibility table:
# https://github.com/nagisa/rust_tracy_client
Expand Down
127 changes: 118 additions & 9 deletions crates/bevy_log/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
extern crate alloc;

use core::error::Error;
use std::path::PathBuf;

#[cfg(target_os = "android")]
mod android_tracing;
Expand Down Expand Up @@ -51,20 +52,20 @@ pub use bevy_utils::{
};
pub use tracing_subscriber;

use bevy_ecs::system::Resource;
#[cfg(feature = "tracing-chrome")]
use bevy_utils::synccell::SyncCell;

use bevy_app::{App, Plugin};
use tracing_log::LogTracer;
#[cfg(feature = "tracing-chrome")]
use tracing_subscriber::fmt::{format::DefaultFields, FormattedFields};
use tracing_subscriber::{
filter::{FromEnvError, ParseError},
prelude::*,
registry::Registry,
EnvFilter, Layer,
};
#[cfg(feature = "tracing-chrome")]
use {
bevy_ecs::system::Resource,
bevy_utils::synccell::SyncCell,
tracing_subscriber::fmt::{format::DefaultFields, FormattedFields},
};

/// Wrapper resource for `tracing-chrome`'s flush guard.
/// When the guard is dropped the chrome log is written to file.
Expand Down Expand Up @@ -96,6 +97,7 @@ pub(crate) struct FlushGuard(SyncCell<tracing_chrome::FlushGuard>);
/// level: Level::DEBUG,
/// filter: "wgpu=error,bevy_render=info,bevy_ecs=trace".to_string(),
/// custom_layer: |_| None,
/// file_appender_settings: None,
/// }))
/// .run();
/// }
Expand Down Expand Up @@ -173,6 +175,14 @@ pub struct LogPlugin {
///
/// Please see the `examples/log_layers.rs` for a complete example.
pub custom_layer: fn(app: &mut App) -> Option<BoxedLayer>,

/// Configure file logging
///
/// ## Platform-specific
///
/// **`WASM`** does not support logging to a file.
#[cfg(not(target_arch = "wasm32"))]
pub file_appender_settings: Option<FileAppenderSettings>,
}

/// A boxed [`Layer`] that can be used with [`LogPlugin`].
Expand All @@ -187,6 +197,69 @@ impl Default for LogPlugin {
filter: DEFAULT_FILTER.to_string(),
level: Level::INFO,
custom_layer: |_| None,
file_appender_settings: None,
}
}
}

/// Enum to control how often a new log file will be created
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Rolling {
/// Creates a new file every minute and appends the date to the file name
/// Date format: YYYY-MM-DD-HH-mm
Minutely,
/// Creates a new file every hour and appends the date to the file name
/// Date format: YYYY-MM-DD-HH
Hourly,
/// Creates a new file every day and appends the date to the file name
/// Date format: YYYY-MM-DD
Daily,
/// Never creates a new file
Never,
}

impl From<Rolling> for tracing_appender::rolling::Rotation {
fn from(val: Rolling) -> Self {
match val {
Rolling::Minutely => tracing_appender::rolling::Rotation::MINUTELY,
Rolling::Hourly => tracing_appender::rolling::Rotation::HOURLY,
Rolling::Daily => tracing_appender::rolling::Rotation::DAILY,
Rolling::Never => tracing_appender::rolling::Rotation::NEVER,
}
}
}

#[derive(Resource)]
struct FileAppenderWorkerGuard(tracing_appender::non_blocking::WorkerGuard);

/// Settings to control how to log to a file
#[derive(Debug, Clone)]
pub struct FileAppenderSettings {
/// Controls how often a new file will be created
///
/// Defaults to [`Rolling::Never`]
pub rolling: Rolling,
/// The path of the directory where the log files will be added
///
/// Defaults to the local directory
pub path: PathBuf,
/// The prefix added when creating a file
///
/// Defaults to "log"
pub prefix: String,
/// When this is enabled, a panic hook will be used and any panic will be logged as an error
///
/// Defaults to true
pub use_panic_hook: bool,
}

impl Default for FileAppenderSettings {
fn default() -> Self {
Self {
rolling: Rolling::Never,
path: PathBuf::from("."),
prefix: String::from("log"),
use_panic_hook: true,
}
}
}
Expand Down Expand Up @@ -253,7 +326,7 @@ impl Plugin for LogPlugin {
}
}))
.build();
app.insert_resource(FlushGuard(SyncCell::new(guard)));
app.world_mut().insert_non_send_resource(guard);
chrome_layer
};

Expand All @@ -273,12 +346,48 @@ impl Plugin for LogPlugin {
meta.fields().field("tracy.frame_mark").is_none()
}));

let subscriber = subscriber.with(fmt_layer);

#[cfg(feature = "tracing-chrome")]
let subscriber = subscriber.with(chrome_layer);
#[cfg(feature = "tracing-tracy")]
let subscriber = subscriber.with(tracy_layer);

let subscriber = subscriber.with(fmt_layer);

let file_appender_layer = self.file_appender_settings.as_ref().map(|settings| {
if settings.use_panic_hook {
let old_handler = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
error!("panic occurred: {s:?}");
} else {
error!("panic occurred");
}
old_handler(panic_info);
}));
}

if settings.rolling == Rolling::Never && settings.prefix.is_empty() {
panic!("Using the Rolling::Never variant with no prefix will result in an empty filename, which is invalid");
}
let file_appender = tracing_appender::rolling::RollingFileAppender::new(
settings.rolling.into(),
&settings.path,
&settings.prefix,
);

let (non_blocking, worker_guard) = tracing_appender::non_blocking(file_appender);
// WARN We need to keep this somewhere so it doesn't get dropped.
// If it gets dropped then it will silently stop writing to the file
app.insert_resource(FileAppenderWorkerGuard(worker_guard));

// InnoCustomed: log to file as JSON-format
tracing_subscriber::fmt::layer()
.json()
.with_ansi(false)
.with_writer(non_blocking)
});
let subscriber = subscriber.with(file_appender_layer);

finished_subscriber = subscriber;
}

Expand Down
2 changes: 2 additions & 0 deletions examples/app/log_layers_ecs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ fn main() {
level: Level::TRACE,
filter: "warn,log_layers_ecs=trace".to_string(),
custom_layer,
// file_appender_settings: Some(FileAppenderSettings::default()),
file_appender_settings: None,
}))
.add_systems(Startup, (log_system, setup))
.add_systems(Update, print_logs)
Expand Down
9 changes: 8 additions & 1 deletion examples/app/logs.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
//! This example illustrates how to use logs in bevy.
use bevy::{log::once, prelude::*};
use bevy::{
log::{
once,
// FileAppenderSettings
},
prelude::*,
};

fn main() {
App::new()
.add_plugins(DefaultPlugins.set(bevy::log::LogPlugin {
// Uncomment this to override the default log settings:
// level: bevy::log::Level::TRACE,
// filter: "wgpu=warn,bevy_ecs=info".to_string(),
// file_appender_settings: Some(FileAppenderSettings::default()),
..default()
}))
.add_systems(Startup, setup)
Expand Down

0 comments on commit c6be48c

Please sign in to comment.