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

Log to file #5342

Closed
wants to merge 8 commits into from
Closed

Log to file #5342

wants to merge 8 commits into from

Conversation

IceSentry
Copy link
Contributor

@IceSentry IceSentry commented Jul 16, 2022

Objective

  • Add the ability to log to a file

Solution

  • Add a dependency on tracing-appender

Notes

For reasons I don't know, tracing-appender didn't support custom suffix. This means it's impossible to log to a file with a .log extension. I submitted a fix that was merged to add support for it, but there has been no release of tracing-appender in almost a year

Closes: #5233

@IceSentry IceSentry added A-Diagnostics Logging, crash handling, error reporting and performance analysis C-Dependencies A change to the crates that Bevy depends on C-Usability A simple quality-of-life change that makes Bevy easier to use labels Jul 16, 2022
@IceSentry
Copy link
Contributor Author

I opened tokio-rs/tracing#2225 to fix the lack of suffix customisation in tracing-appender

@johanhelsing
Copy link
Contributor

First of all, the design of tracing subscriber makes it really hard to conditionally add a layer without using cfg flags. This means that I need a cfg flag to conditionally add the file appender, but that also means the file appender settings need to be conditionally added to the LogSettings. An alternate option could be to simply have a separate setting resource for the file appender.

I found out it's possible to circumvent some of the type issues by adding an Option<Layer>.

See: IceSentry#3

@IceSentry IceSentry marked this pull request as ready for review February 23, 2023 07:41
@mockersf
Copy link
Member

this will not work in wasm, and it should either be gated or mentioned in the docs

@IceSentry IceSentry force-pushed the log_to_file branch 2 times, most recently from 2ba5958 to ae4782f Compare February 23, 2023 07:48
@IceSentry IceSentry changed the title add log_to_file feature Log to file Feb 23, 2023
@IceSentry
Copy link
Contributor Author

this will not work in wasm

The diff doesn't show it, but it's in the block that adds layers that don't work in wasm or android. I will add documentation explaining this, but do you also want the field on the struct to be gated?

@IceSentry
Copy link
Contributor Author

I'm not sure why I put it there actually. I don't see why it wouldn't work on android. I'll move it to a new gated section for not wasm

@mockersf
Copy link
Member

I will add documentation explaining this, but do you also want the field on the struct to be gated?

Documentation should be good enough, something like

// ## Platform-specific
//
// **`iOS`**, **`Android`**, and the **`Web`** do not have decorations.
pub decorations: bool,

crates/bevy_log/Cargo.toml Outdated Show resolved Hide resolved
crates/bevy_log/src/lib.rs Show resolved Hide resolved
Copy link
Contributor

@johanhelsing johanhelsing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like you accidentally committed the log file, but otherwise it seems good to me :)

@johanhelsing
Copy link
Contributor

Made a PR to this branch to remove the extra log file: IceSentry#4

@johanhelsing
Copy link
Contributor

One caveat with this, is that it won't catch panics. That is if you add a panic to the logs example, you get:

dev/bevy b69aec5 ~1 …1 cargo run --example logs
    Finished dev [unoptimized + debuginfo] target(s) in 0.34s
     Running `target\debug\examples\logs.exe`
2023-03-20T08:51:49.583296Z  INFO bevy_winit::system: Creating new window "Bevy App" (0v0)
2023-03-20T08:51:49.763815Z  INFO bevy_render::renderer: AdapterInfo { name: "NVIDIA GeForce GTX 970", vendor: 4318, device: 5058, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "522.25", backend: Vulkan }
2023-03-20T08:51:50.363948Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 11 Pro", kernel: "22621", cpu: "AMD Ryzen 9 3900X 12-Core Processor", core_count: "12", memory: "31.9 GiB" }
2023-03-20T08:51:50.443608Z  INFO logs: helpful information that is worth printing by default
2023-03-20T08:51:50.443754Z  WARN logs: something bad happened that isn't a failure, but thats worth calling out
2023-03-20T08:51:50.443842Z ERROR logs: something failed
thread 'Compute Task Pool (14)' panicked at 'something really horrible happened!', examples/app/logs.rs:28:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'Compute Task Pool (14)' panicked at 'A system has panicked so the executor cannot continue.: RecvError', crates\bevy_ecs\src\schedule\executor\multi_threaded.rs:190:60
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', C:\Users\Johan\dev\bevy\crates\bevy_tasks\src\task_pool.rs:371:49
error: process didn't exit successfully: `target\debug\examples\logs.exe` (exit code: 101)
dev/bevy b69aec5 ~1 …1 cat log
2023-03-20T08:51:33.556888Z  INFO bevy_winit::system: Creating new window "Bevy App" (0v0)
2023-03-20T08:51:33.939334Z  INFO bevy_render::renderer: AdapterInfo { name: "NVIDIA GeForce GTX 970", vendor: 4318, device: 5058, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "522.25", backend: Vulkan }
2023-03-20T08:51:34.961082Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 11 Pro", kernel: "22621", cpu: "AMD Ryzen 9 3900X 12-Core Processor", core_count: "12", memory: "31.9 GiB" }
2023-03-20T08:51:35.041160Z  INFO logs: helpful information that is worth printing by default
2023-03-20T08:51:35.041269Z  WARN logs: something bad happened that isn't a failure, but thats worth calling out
2023-03-20T08:51:35.041366Z ERROR logs: something failed
2023-03-20T08:51:49.583414Z  INFO bevy_winit::system: Creating new window "Bevy App" (0v0)
2023-03-20T08:51:49.763948Z  INFO bevy_render::renderer: AdapterInfo { name: "NVIDIA GeForce GTX 970", vendor: 4318, device: 5058, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "522.25", backend: Vulkan }
2023-03-20T08:51:50.364098Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 11 Pro", kernel: "22621", cpu: "AMD Ryzen 9 3900X 12-Core Processor", core_count: "12", memory: "31.9 GiB" }
2023-03-20T08:51:50.443724Z  INFO logs: helpful information that is worth printing by default
2023-03-20T08:51:50.443828Z  WARN logs: something bad happened that isn't a failure, but thats worth calling out
2023-03-20T08:51:50.443896Z ERROR logs: something failed

I'm not sure if it is out of scope for this new API, or whether we should try to create a more high-level API, less coupled to file appender?

@mockersf
Copy link
Member

mockersf commented Mar 20, 2023

I'm not sure why I put it there actually. I don't see why it wouldn't work on android. I'll move it to a new gated section for not wasm

Could you check how it works on Android? Aren't apps sandboxed, unless you require additional permissions? Is the log file in a reachable place?

@IceSentry
Copy link
Contributor Author

it won't catch panics

I think that's fine, if people want to log an error message before a panic they should do that. We could probably provide a panic_print!() or something like that I guess.

Could you check how it works on Android?

I'm not sure how to do that. I guess I should just keep it gated for now.

@johanhelsing
Copy link
Contributor

it won't catch panics

I think that's fine, if people want to log an error message before a panic they should do that. We could probably provide a panic_print!() or something like that I guess.

That would be quite some boilerplate on expect, unwrap, unreachable! etc. and wouldn't handle panics like those coming from incorrect use of Query::single, World::resource, or systems with missing resources, not to mention 3rd party crates.

Not really sure how this is commonly solved, maybe possible through a panic hook? Currently I'm wrapping my whole executable in a launcher process that pipes both stdout and stderr to files, mainly to catch panics, would be nice if I didn't have to.

@arsmilitaris
Copy link
Contributor

I would like to add that I'm using this PR in my project, and the solution I found to capture panics (which was the only thing the updated LogPlugin wasn't doing) to a file was indeed using a panic hook.

If one adds this to the beginning of the main() function:

std::panic::set_hook(Box::new(custom_panic_hook));

and then the custom_panic_hook can be just:

// Logging
fn custom_panic_hook(info: &std::panic::PanicInfo) {
    // Perform any necessary logging or error handling here
    error!("Panic occurred: {:?}", info);
}

In this way, panics are correctly logged to the log file.

The launcher process solution is also interesting, and can be useful.

@IceSentry IceSentry force-pushed the log_to_file branch 2 times, most recently from bf553e3 to b6de76a Compare September 1, 2023 03:05
@IceSentry
Copy link
Contributor Author

IceSentry commented Sep 1, 2023

I added a panic hook that is only enabled when using the file appender. After some quick testing it does seem to catch panics from systems correctly.

crates/bevy_log/src/lib.rs Outdated Show resolved Hide resolved
crates/bevy_log/src/lib.rs Outdated Show resolved Hide resolved
@alice-i-cecile alice-i-cecile added this to the 0.13 milestone Nov 3, 2023
Copy link
Contributor

@spectria-limina spectria-limina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I was asked to jump in and review this PR. It's my first Bevy review, so hopefully all is good.

The idea looks good, but I have left some suggestions on potential improvements. Hope they are helpful!

/// ## Platform-specific
///
/// **`WASM`** does not support logging to a file.
pub file_appender_settings: Option<FileAppenderSettings>,

This comment was marked as resolved.

let subscriber = subscriber.with(fmt_layer);
let subscriber = subscriber.with(fmt_layer);

#[cfg(feature = "tracing-chrome")]

This comment was marked as resolved.

}

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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add a comma before "which"

.with_ansi(false)
.with_writer(non_blocking);
Some(file_fmt_layer)
} else {

This comment was marked as resolved.

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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't fully robust as it will mean that logs still get lost after teardown of the current app. This would apply to logs generated later in the teardown, or perhaps in a multi-App process if the App with the logging configuration were to be destroyed but others were to continue.

Given that only one logger can ever be configured globally, perhaps it's okay to simply mem::forget the guard? This applies equally to the existing guards elsewhere in the code.

} else {
None
};
let subscriber = subscriber.with(file_appender_layer);

This comment was marked as resolved.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iirc the subscriber changes type with each call, and is relatively unergonomic to use, which is why much of this code seems arbitrarily strange.

@@ -8,6 +8,8 @@ fn main() {
// Uncomment this to override the default log settings:
// level: bevy::log::Level::TRACE,
// filter: "wgpu=warn,bevy_ecs=info".to_string(),
// This will let you configure file logging

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will generate artifacts which is not ideal and examples shouldn't have to clean up after themselves.

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

/// Settings to control how to log to a file

This comment was marked as resolved.

crates/bevy_log/src/lib.rs Show resolved Hide resolved
#[cfg(feature = "tracing-chrome")]
let chrome_layer = {
let mut layer = tracing_chrome::ChromeLayerBuilder::new();
if let Ok(path) = std::env::var("TRACE_CHROME") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this PR will leave things in a situation where the Chrome tracing is controlled purely by environment variable, and the file logging purely by code. It might be good to unify both of these paths and allow control of both variations by either method, with environment variables taking priority.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather leave this to be fixed in a future PR but I agree that support for ENV controlled file logging would be good.

@alice-i-cecile alice-i-cecile modified the milestones: 0.13, 0.14 Jan 26, 2024
@alice-i-cecile alice-i-cecile added the C-Needs-Release-Note Work that should be called out in the blog due to impact label Feb 22, 2024
@IceSentry
Copy link
Contributor Author

I have way too much going on right now and don't have the time to update this PR. If anyone is interested in picking up this work feel free to pick this up.

@IceSentry IceSentry closed this May 6, 2024
@IceSentry IceSentry added the S-Adopt-Me The original PR author has no intent to complete this work. Pick me up! label May 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Diagnostics Logging, crash handling, error reporting and performance analysis C-Dependencies A change to the crates that Bevy depends on C-Needs-Release-Note Work that should be called out in the blog due to impact C-Usability A simple quality-of-life change that makes Bevy easier to use S-Adopt-Me The original PR author has no intent to complete this work. Pick me up!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bevy Log Output to Files
6 participants