fern 0.7.1

Simple, efficient logging
Documentation
//! This provides testing of the 'meta-logging' feature, which allows for
//! deadlock-free logging within logging formatters.
//!
//! These tests *will* deadlock if the feature is not enabled, so they're
//! disabled by default.
#![cfg(feature = "meta-logging-in-format")]
use std::{fmt, fs, io, io::prelude::*};

use log::{Level::*, Log};

mod support;

use support::manual_log;

// in order to actually trigger the situation that deadlocks, we need a custom
// Display implementation which performs logging:
struct VerboseDisplayThing<'a> {
    log_copy: &'a dyn Log,
    msg: &'a str,
}

impl<'a> fmt::Display for VerboseDisplayThing<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        manual_log(
            self.log_copy,
            Debug,
            format_args!(
                "VerboseDisplayThing is being displayed! [contents: {}]",
                self.msg
            ),
        );
        f.write_str(self.msg)
    }
}

#[test]
fn file_deadlock() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempfile::tempdir().expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test.log");

    {
        let (_max_level, logger) = fern::Dispatch::new()
            .format(|out, msg, record| out.finish(format_args!("[{}] {}", record.level(), msg)))
            .chain(io::stdout())
            .chain(fern::log_file(log_file).expect("Failed to open log file"))
            .into_log();

        let l = &*logger;

        manual_log(
            l,
            Info,
            format_args!(
                "Hello, world! {}",
                VerboseDisplayThing {
                    log_copy: l,
                    msg: "it's verbose!",
                }
            ),
        );

        // ensure all File objects are dropped and OS buffers are flushed.
        log::logger().flush();

        {
            let contents = {
                let mut log_read = fs::File::open(temp_log_dir.path().join("test.log")).unwrap();
                let mut buf = String::new();
                log_read.read_to_string(&mut buf).unwrap();
                buf
            };
            assert_eq!(
                contents,
                // double logs because we're logging to stdout & the file
                "[DEBUG] VerboseDisplayThing is being displayed! [contents: it's verbose!]\
                 \n[DEBUG] VerboseDisplayThing is being displayed! [contents: it's verbose!]\
                 \n[INFO] Hello, world! it's verbose!\n"
            );
        }
    } // ensure logger is dropped before temp dir

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}