1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! `slog` support for `tui-logger`

use super::TUI_LOGGER;
use log::{self, Log, Record};
use slog::{self, Drain, KV};
use std::{fmt, io};

/// Key-Separator-Value serializer
// Copied from `slog-stdlog`
struct KSV<W: io::Write> {
    io: W,
}

impl<W: io::Write> KSV<W> {
    fn new(io: W) -> Self {
        KSV { io }
    }

    fn into_inner(self) -> W {
        self.io
    }
}

impl<W: io::Write> slog::Serializer for KSV<W> {
    fn emit_arguments(&mut self, key: slog::Key, val: &fmt::Arguments) -> slog::Result {
        write!(self.io, ", {}: {}", key, val)?;
        Ok(())
    }
}

// Copied from `slog-stdlog`
struct LazyLogString<'a> {
    info: &'a slog::Record<'a>,
    logger_values: &'a slog::OwnedKVList,
}

impl<'a> LazyLogString<'a> {
    fn new(info: &'a slog::Record, logger_values: &'a slog::OwnedKVList) -> Self {
        LazyLogString {
            info,
            logger_values,
        }
    }
}

impl<'a> fmt::Display for LazyLogString<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.info.msg())?;

        let io = io::Cursor::new(Vec::new());
        let mut ser = KSV::new(io);

        self.logger_values
            .serialize(self.info, &mut ser)
            .map_err(|_| fmt::Error)?;
        self.info
            .kv()
            .serialize(self.info, &mut ser)
            .map_err(|_| fmt::Error)?;

        let values = ser.into_inner().into_inner();

        write!(f, "{}", String::from_utf8_lossy(&values))
    }
}

#[allow(clippy::needless_doctest_main)]
///  slog-compatible Drain that feeds messages to `tui-logger`.
///
///  ## Basic usage:
///  ```
///  use slog::{self, o, Drain, info};
///  //use tui_logger;
///  
///  fn main() {
///     let drain = tui_logger::slog_drain().fuse();
///     let log = slog::Logger::root(drain, o!());
///     info!(log, "Logging via slog works!");
///
///  }
pub struct TuiSlogDrain;

impl Drain for TuiSlogDrain {
    type Ok = ();
    type Err = io::Error;
    // Copied from `slog-stdlog`
    fn log(&self, info: &slog::Record, logger_values: &slog::OwnedKVList) -> io::Result<()> {
        let level = match info.level() {
            slog::Level::Critical | slog::Level::Error => log::Level::Error,
            slog::Level::Warning => log::Level::Warn,
            slog::Level::Info => log::Level::Info,
            slog::Level::Debug => log::Level::Debug,
            slog::Level::Trace => log::Level::Trace,
        };

        let mut target = info.tag();
        if target.is_empty() {
            target = info.module();
        }

        let lazy = LazyLogString::new(info, logger_values);
        TUI_LOGGER.log(
            &Record::builder()
                .args(format_args!("{}", lazy))
                .level(level)
                .target(target)
                .file(Some(info.file()))
                .line(Some(info.line()))
                .build(),
        );

        Ok(())
    }
}