aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ansi.rs6
-rw-r--r--src/cli.rs13
-rw-r--r--src/config.rs87
-rw-r--r--src/display.rs45
-rw-r--r--src/event.rs2
-rw-r--r--src/grid/tests.rs12
-rw-r--r--src/input.rs4
-rw-r--r--src/lib.rs1
-rw-r--r--src/logging.rs203
-rw-r--r--src/macros.rs28
-rw-r--r--src/main.rs35
-rw-r--r--src/renderer/mod.rs14
-rw-r--r--src/term/mod.rs14
13 files changed, 338 insertions, 126 deletions
diff --git a/src/ansi.rs b/src/ansi.rs
index f1ca759a..2171f868 100644
--- a/src/ansi.rs
+++ b/src/ansi.rs
@@ -60,7 +60,7 @@ fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
let r = parse_hex!();
let val = next!();
- if val != Some('/') { println!("val={:?}", val); return None; }
+ if val != Some('/') { return None; }
let g = parse_hex!();
if next!() != Some('/') { return None; }
let b = parse_hex!();
@@ -901,7 +901,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
macro_rules! unhandled {
() => {{
- warn!("[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}",
+ debug!("[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}",
action, args, intermediates);
return;
}}
@@ -1137,7 +1137,7 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W>
) {
macro_rules! unhandled {
() => {{
- warn!("[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})",
+ debug!("[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})",
params, intermediates, byte as char, byte);
return;
}}
diff --git a/src/cli.rs b/src/cli.rs
index 0a36179a..99728780 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -31,6 +31,7 @@ pub struct Options {
pub command: Option<Shell<'static>>,
pub working_dir: Option<PathBuf>,
pub config: Option<PathBuf>,
+ pub persistent_logging: bool,
}
impl Default for Options {
@@ -46,6 +47,7 @@ impl Default for Options {
command: None,
working_dir: None,
config: None,
+ persistent_logging: false,
}
}
}
@@ -71,6 +73,9 @@ impl Options {
.conflicts_with("live-config-reload"))
.arg(Arg::with_name("print-events")
.long("print-events"))
+ .arg(Arg::with_name("persistent-logging")
+ .long("persistent-logging")
+ .help("Keep the log file after quitting Alacritty"))
.arg(Arg::with_name("dimensions")
.long("dimensions")
.short("d")
@@ -129,6 +134,10 @@ impl Options {
options.live_config_reload = Some(false);
}
+ if matches.is_present("persistent-logging") {
+ options.persistent_logging = true;
+ }
+
if let Some(mut dimensions) = matches.values_of("dimensions") {
let width = dimensions.next().map(|w| w.parse().map(Column));
let height = dimensions.next().map(|h| h.parse().map(Line));
@@ -147,8 +156,8 @@ impl Options {
}
match matches.occurrences_of("v") {
- 0 => {},
- 1 => options.log_level = log::LevelFilter::Info,
+ 0 if !options.print_events => {},
+ 0 | 1 => options.log_level = log::LevelFilter::Info,
2 => options.log_level = log::LevelFilter::Debug,
3 | _ => options.log_level = log::LevelFilter::Trace
}
diff --git a/src/config.rs b/src/config.rs
index ee30c085..ae1ec897 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -73,7 +73,7 @@ fn deserialize_duration_ms<'a, D>(deserializer: D) -> ::std::result::Result<Dura
match u64::deserialize(deserializer) {
Ok(threshold_ms) => Ok(Duration::from_millis(threshold_ms)),
Err(err) => {
- eprintln!("problem with config: {}; Using default value", err);
+ error!("problem with config: {}; Using default value", err);
Ok(default_threshold_ms())
},
}
@@ -172,7 +172,7 @@ fn deserialize_visual_bell_duration<'a, D>(deserializer: D) -> ::std::result::Re
match u16::deserialize(deserializer) {
Ok(duration) => Ok(duration),
Err(err) => {
- eprintln!("problem with config: {}; Using default value", err);
+ error!("problem with config: {}; Using default value", err);
Ok(default_visual_bell_duration())
},
}
@@ -310,19 +310,19 @@ impl<'de> Deserialize<'de> for Decorations {
"none" => Ok(Decorations::None),
"full" => Ok(Decorations::Full),
"true" => {
- eprintln!("deprecated decorations boolean value, \
+ error!("deprecated decorations boolean value, \
use one of transparent|buttonless|none|full instead; \
Falling back to \"full\"");
Ok(Decorations::Full)
},
"false" => {
- eprintln!("deprecated decorations boolean value, \
+ error!("deprecated decorations boolean value, \
use one of transparent|buttonless|none|full instead; \
Falling back to \"none\"");
Ok(Decorations::None)
},
_ => {
- eprintln!("invalid decorations value: {}; Using default value", value);
+ error!("invalid decorations value: {}; Using default value", value);
Ok(Decorations::Full)
}
}
@@ -336,23 +336,23 @@ impl<'de> Deserialize<'de> for Decorations {
"none" => Ok(Decorations::None),
"full" => Ok(Decorations::Full),
"true" => {
- eprintln!("deprecated decorations boolean value, \
+ error!("deprecated decorations boolean value, \
use one of none|full instead; \
Falling back to \"full\"");
Ok(Decorations::Full)
},
"false" => {
- eprintln!("deprecated decorations boolean value, \
+ error!("deprecated decorations boolean value, \
use one of none|full instead; \
Falling back to \"none\"");
Ok(Decorations::None)
},
"transparent" | "buttonless" => {
- eprintln!("macos-only decorations value: {}; Using default value", value);
+ error!("macos-only decorations value: {}; Using default value", value);
Ok(Decorations::Full)
},
_ => {
- eprintln!("invalid decorations value: {}; Using default value", value);
+ error!("invalid decorations value: {}; Using default value", value);
Ok(Decorations::Full)
}
}
@@ -392,7 +392,7 @@ fn deserialize_padding<'a, D>(deserializer: D) -> ::std::result::Result<Delta<u8
match Delta::deserialize(deserializer) {
Ok(delta) => Ok(delta),
Err(err) => {
- eprintln!("problem with config: {}; Using default value", err);
+ error!("problem with config: {}; Using default value", err);
Ok(default_padding())
},
}
@@ -503,6 +503,10 @@ pub struct Config {
#[serde(default, deserialize_with="failure_default")]
cursor: Cursor,
+ /// Keep the log file after quitting
+ #[serde(default, deserialize_with="failure_default")]
+ persistent_logging: bool,
+
// TODO: DEPRECATED
#[serde(default, deserialize_with = "failure_default")]
custom_cursor_colors: Option<bool>,
@@ -528,7 +532,7 @@ fn failure_default_vec<'a, D, T>(deserializer: D) -> ::std::result::Result<Vec<T
let vec = match Vec::<serde_yaml::Value>::deserialize(deserializer) {
Ok(vec) => vec,
Err(err) => {
- eprintln!("problem with config: {}; Using empty vector", err);
+ error!("problem with config: {}; Using empty vector", err);
return Ok(Vec::new());
},
};
@@ -539,7 +543,7 @@ fn failure_default_vec<'a, D, T>(deserializer: D) -> ::std::result::Result<Vec<T
match T::deserialize(value) {
Ok(binding) => bindings.push(binding),
Err(err) => {
- eprintln!("problem with config: {}; Skipping value", err);
+ error!("problem with config: {}; Skipping value", err);
},
}
}
@@ -557,7 +561,7 @@ fn deserialize_tabspaces<'a, D>(deserializer: D) -> ::std::result::Result<usize,
match usize::deserialize(deserializer) {
Ok(value) => Ok(value),
Err(err) => {
- eprintln!("problem with config: {}; Using `8`", err);
+ error!("problem with config: {}; Using `8`", err);
Ok(default_tabspaces())
},
}
@@ -569,7 +573,7 @@ fn default_true_bool<'a, D>(deserializer: D) -> ::std::result::Result<bool, D::E
match bool::deserialize(deserializer) {
Ok(value) => Ok(value),
Err(err) => {
- eprintln!("problem with config: {}; Using `true`", err);
+ error!("problem with config: {}; Using `true`", err);
Ok(true)
},
}
@@ -583,7 +587,7 @@ fn failure_default<'a, D, T>(deserializer: D)
match T::deserialize(deserializer) {
Ok(value) => Ok(value),
Err(err) => {
- eprintln!("problem with config: {}; Using default value", err);
+ error!("problem with config: {}; Using default value", err);
Ok(T::default())
},
}
@@ -645,7 +649,7 @@ fn deserialize_scrolling_history<'a, D>(deserializer: D) -> ::std::result::Resul
match u32::deserialize(deserializer) {
Ok(lines) => {
if lines > MAX_SCROLLBACK_LINES {
- eprintln!(
+ error!(
"problem with config: scrollback size is {}, but expected a maximum of {}; \
Using {1} instead",
lines, MAX_SCROLLBACK_LINES,
@@ -656,7 +660,7 @@ fn deserialize_scrolling_history<'a, D>(deserializer: D) -> ::std::result::Resul
}
},
Err(err) => {
- eprintln!("problem with config: {}; Using default value", err);
+ error!("problem with config: {}; Using default value", err);
Ok(default_scrolling_history())
},
}
@@ -668,7 +672,7 @@ fn deserialize_scrolling_multiplier<'a, D>(deserializer: D) -> ::std::result::Re
match u8::deserialize(deserializer) {
Ok(lines) => Ok(lines),
Err(err) => {
- eprintln!("problem with config: {}; Using default value", err);
+ error!("problem with config: {}; Using default value", err);
Ok(default_scrolling_multiplier())
},
}
@@ -710,7 +714,7 @@ impl<'a> de::Deserialize<'a> for ModsWrapper {
"Shift" => res.shift = true,
"Alt" | "Option" => res.alt = true,
"Control" => res.ctrl = true,
- _ => eprintln!("unknown modifier {:?}", modifier),
+ _ => error!("unknown modifier {:?}", modifier),
}
}
@@ -831,7 +835,7 @@ impl<'a> de::Deserialize<'a> for ModeWrapper {
"~AppCursor" => res.not_mode |= mode::TermMode::APP_CURSOR,
"AppKeypad" => res.mode |= mode::TermMode::APP_KEYPAD,
"~AppKeypad" => res.not_mode |= mode::TermMode::APP_KEYPAD,
- _ => eprintln!("unknown mode {:?}", modifier),
+ _ => error!("unknown mode {:?}", modifier),
}
}
@@ -1193,7 +1197,7 @@ fn deserialize_color_index<'a, D>(deserializer: D) -> ::std::result::Result<u8,
match u8::deserialize(deserializer) {
Ok(index) => {
if index < 16 {
- eprintln!(
+ error!(
"problem with config: indexed_color's index is '{}', \
but a value bigger than 15 was expected; \
Ignoring setting",
@@ -1207,7 +1211,7 @@ fn deserialize_color_index<'a, D>(deserializer: D) -> ::std::result::Result<u8,
}
},
Err(err) => {
- eprintln!("problem with config: {}; Ignoring setting", err);
+ error!("problem with config: {}; Ignoring setting", err);
// Return value out of range to ignore this color
Ok(0)
@@ -1262,7 +1266,7 @@ fn deserialize_optional_color<'a, D>(deserializer: D) -> ::std::result::Result<O
},
Ok(None) => Ok(None),
Err(err) => {
- eprintln!("problem with config: {}; Using standard foreground color", err);
+ error!("problem with config: {}; Using standard foreground color", err);
Ok(None)
},
}
@@ -1361,7 +1365,7 @@ fn rgb_from_hex<'a, D>(deserializer: D) -> ::std::result::Result<Rgb, D::Error>
match rgb {
Ok(rgb) => Ok(rgb),
Err(err) => {
- eprintln!("problem with config: {}; Using color #ff00ff", err);
+ error!("problem with config: {}; Using color #ff00ff", err);
Ok(Rgb { r: 255, g: 0, b: 255 })
},
}
@@ -1671,6 +1675,12 @@ impl Config {
self.scrolling.history = history;
}
+ /// Keep the log file after quitting Alacritty
+ #[inline]
+ pub fn persistent_logging(&self) -> bool {
+ self.persistent_logging
+ }
+
pub fn load_from<P: Into<PathBuf>>(path: P) -> Result<Config> {
let path = path.into();
let raw = Config::read_file(path.as_path())?;
@@ -1701,24 +1711,22 @@ impl Config {
}
fn print_deprecation_warnings(&mut self) {
- use ::util::fmt;
if self.dimensions.is_some() {
- eprintln!("{}", fmt::Yellow("Config `dimensions` is deprecated. \
- Please use `window.dimensions` instead."));
+ warn!("{}", "Config `dimensions` is deprecated. \
+ Please use `window.dimensions` instead.");
}
if self.padding.is_some() {
- eprintln!("{}", fmt::Yellow("Config `padding` is deprecated. \
- Please use `window.padding` instead."));
+ warn!("{}", "Config `padding` is deprecated. Please use `window.padding` instead.");
}
if self.mouse.faux_scrollback_lines.is_some() {
- println!("{}", fmt::Yellow("Config `mouse.faux_scrollback_lines` is deprecated. \
- Please use `mouse.faux_scrolling_lines` instead."));
+ warn!("{}", "Config `mouse.faux_scrollback_lines` is deprecated. \
+ Please use `mouse.faux_scrolling_lines` instead.");
}
if let Some(custom_cursor_colors) = self.custom_cursor_colors {
- eprintln!("{}", fmt::Yellow("Config `custom_cursor_colors` is deprecated."));
+ warn!("{}", "Config `custom_cursor_colors` is deprecated.");
if !custom_cursor_colors {
self.colors.cursor.cursor = None;
@@ -1727,18 +1735,17 @@ impl Config {
}
if self.cursor_style.is_some() {
- eprintln!("{}", fmt::Yellow("Config `cursor_style` is deprecated. \
- Please use `cursor.style` instead."));
+ warn!("{}", "Config `cursor_style` is deprecated. Please use `cursor.style` instead.");
}
if self.hide_cursor_when_typing.is_some() {
- eprintln!("{}", fmt::Yellow("Config `hide_cursor_when_typing` is deprecated. \
- Please use `mouse.hide_when_typing` instead."));
+ warn!("{}", "Config `hide_cursor_when_typing` is deprecated. \
+ Please use `mouse.hide_when_typing` instead.");
}
if self.unfocused_hollow_cursor.is_some() {
- eprintln!("{}", fmt::Yellow("Config `unfocused_hollow_cursor` is deprecated. \
- Please use `cursor.unfocused_hollow` instead."));
+ warn!("{}", "Config `unfocused_hollow_cursor` is deprecated. \
+ Please use `cursor.unfocused_hollow` instead.");
}
}
}
@@ -1839,7 +1846,7 @@ impl DeserializeSize for Size {
match size {
Ok(size) => Ok(size),
Err(err) => {
- eprintln!("problem with config: {}; Using size 12", err);
+ error!("problem with config: {}; Using size 12", err);
Ok(Size::new(12.))
},
}
@@ -2062,7 +2069,7 @@ impl Monitor {
let _ = config_tx.send(config);
handler.on_config_reload();
},
- Err(err) => eprintln!("Ignoring invalid config: {}", err),
+ Err(err) => error!("Ignoring invalid config: {}", err),
}
}
}
diff --git a/src/display.rs b/src/display.rs
index 1a145185..12842649 100644
--- a/src/display.rs
+++ b/src/display.rs
@@ -28,6 +28,7 @@ use renderer::{self, GlyphCache, QuadRenderer};
use term::{Term, SizeInfo, RenderableCell};
use sync::FairMutex;
use window::{self, Window};
+use logging::LoggerProxy;
use Rgb;
#[derive(Debug)]
@@ -99,6 +100,7 @@ pub struct Display {
meter: Meter,
font_size: font::Size,
size_info: SizeInfo,
+ logger_proxy: LoggerProxy,
}
/// Can wakeup the render loop from other threads
@@ -129,7 +131,11 @@ impl Display {
&self.size_info
}
- pub fn new(config: &Config, options: &cli::Options) -> Result<Display, Error> {
+ pub fn new(
+ config: &Config,
+ options: &cli::Options,
+ logger_proxy: LoggerProxy
+ ) -> Result<Display, Error> {
// Extract some properties from config
let render_timer = config.render_timer();
@@ -218,6 +224,7 @@ impl Display {
meter: Meter::new(),
font_size: font::Size::new(0.),
size_info,
+ logger_proxy,
})
}
@@ -420,10 +427,38 @@ impl Display {
g: 0x4e,
b: 0x53,
};
- self.renderer
- .with_api(config, &size_info, visual_bell_intensity, |mut api| {
- api.render_string(&timing[..], glyph_cache, color);
- });
+ self.renderer.with_api(config, &size_info, visual_bell_intensity, |mut api| {
+ api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, color);
+ });
+ }
+
+ // Display errors and warnings
+ if self.logger_proxy.errors() {
+ let msg = match self.logger_proxy.log_path() {
+ Some(path) => format!(" ERROR! See log at {} ", path),
+ None => " ERROR! See log in stderr ".into(),
+ };
+ let color = Rgb {
+ r: 0xff,
+ g: 0x00,
+ b: 0x00,
+ };
+ self.renderer.with_api(config, &size_info, visual_bell_intensity, |mut api| {
+ api.render_string(&msg, size_info.lines() - 1, glyph_cache, color);
+ });
+ } else if self.logger_proxy.warnings() {
+ let msg = match self.logger_proxy.log_path() {
+ Some(path) => format!(" WARNING! See log at {} ", path),
+ None => " WARNING! See log in stderr ".into(),
+ };
+ let color = Rgb {
+ r: 0xff,
+ g: 0xff,
+ b: 0x00,
+ };
+ self.renderer.with_api(config, &size_info, visual_bell_intensity, |mut api| {
+ api.render_string(&msg, size_info.lines() - 1, glyph_cache, color);
+ });
}
}
diff --git a/src/event.rs b/src/event.rs
index 7a1faa70..ece8ec6f 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -488,7 +488,7 @@ impl<N: Notify> Processor<N> {
let hide_mouse = &mut self.hide_mouse;
let mut process = |event| {
if print_events {
- println!("glutin event: {:?}", event);
+ info!("glutin event: {:?}", event);
}
Processor::handle_event(
&mut processor,
diff --git a/src/grid/tests.rs b/src/grid/tests.rs
index e136e3b3..9cb30a7f 100644
--- a/src/grid/tests.rs
+++ b/src/grid/tests.rs
@@ -20,19 +20,13 @@ use index::{Point, Line, Column};
// Scroll up moves lines upwards
#[test]
fn scroll_up() {
- println!();
-
let mut grid = Grid::new(Line(10), Column(1), 0, 0);
for i in 0..10 {
grid[Line(i)][Column(0)] = i;
}
- println!("grid: {:?}", grid);
-
grid.scroll_up(&(Line(0)..Line(10)), Line(2), &0);
- println!("grid: {:?}", grid);
-
assert_eq!(grid[Line(0)][Column(0)], 2);
assert_eq!(grid[Line(0)].occ, 1);
assert_eq!(grid[Line(1)][Column(0)], 3);
@@ -58,19 +52,13 @@ fn scroll_up() {
// Scroll down moves lines downwards
#[test]
fn scroll_down() {
- println!();
-
let mut grid = Grid::new(Line(10), Column(1), 0, 0);
for i in 0..10 {
grid[Line(i)][Column(0)] = i;
}
- println!("grid: {:?}", grid);
-
grid.scroll_down(&(Line(0)..Line(10)), Line(2), &0);
- println!("grid: {:?}", grid);
-
assert_eq!(grid[Line(0)][Column(0)], 0); // was 8
assert_eq!(grid[Line(0)].occ, 0);
assert_eq!(grid[Line(1)][Column(0)], 0); // was 9
diff --git a/src/input.rs b/src/input.rs
index 600a1abe..d2f8f6b5 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -218,7 +218,7 @@ impl Action {
.and_then(|clipboard| clipboard.load_primary() )
.map(|contents| { self.paste(ctx, &contents) })
.unwrap_or_else(|err| {
- eprintln!("Error loading data from clipboard. {}", Red(err));
+ error!("Error loading data from clipboard. {}", Red(err));
});
},
Action::PasteSelection => {
@@ -228,7 +228,7 @@ impl Action {
.and_then(|clipboard| clipboard.load_selection() )
.map(|contents| { self.paste(ctx, &contents) })
.unwrap_or_else(|err| {
- warn!("Error loading data from clipboard. {}", Red(err));
+ error!("Error loading data from clipboard. {}", Red(err));
});
}
},
diff --git a/src/lib.rs b/src/lib.rs
index 1d393d1a..7ba3538b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -64,6 +64,7 @@ extern crate xdg;
extern crate base64;
extern crate terminfo;
extern crate url;
+extern crate time;
#[macro_use]
pub mod macros;
diff --git a/src/logging.rs b/src/logging.rs
index 10929980..5ad1dcd5 100644
--- a/src/logging.rs
+++ b/src/logging.rs
@@ -17,37 +17,134 @@
//! The main executable is supposed to call `initialize()` exactly once during
//! startup. All logging messages are written to stdout, given that their
//! log-level is sufficient for the level configured in `cli::Options`.
-use log;
-use std::sync;
-use std::io;
use cli;
+use log::{self, Level};
+use time;
-pub struct Logger<T> {
+use std::env;
+use std::fs::{self, File, OpenOptions};
+use std::io::{self, LineWriter, Stdout, Write};
+use std::path::PathBuf;
+use std::process;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::{Arc, Mutex};
+
+pub fn initialize(options: &cli::Options) -> Result<LoggerProxy, log::SetLoggerError> {
+ // Use env_logger if RUST_LOG environment variable is defined. Otherwise,
+ // use the alacritty-only logger.
+ if ::std::env::var("RUST_LOG").is_ok() {
+ ::env_logger::try_init()?;
+ Ok(LoggerProxy::default())
+ } else {
+ let logger = Logger::new(options.log_level);
+ let proxy = logger.proxy();
+
+ log::set_boxed_logger(Box::new(logger))?;
+
+ Ok(proxy)
+ }
+}
+
+/// Proxy object for bidirectional communicating with the global logger.
+#[derive(Clone, Default)]
+pub struct LoggerProxy {
+ errors: Arc<AtomicBool>,
+ warnings: Arc<AtomicBool>,
+ logfile_proxy: OnDemandLogFileProxy,
+}
+
+impl LoggerProxy {
+ /// Check for new logged errors.
+ pub fn errors(&self) -> bool {
+ self.errors.load(Ordering::Relaxed)
+ }
+
+ /// Check for new logged warnings.
+ pub fn warnings(&self) -> bool {
+ self.warnings.load(Ordering::Relaxed)
+ }
+
+ /// Get the path of the log file if it has been created.
+ pub fn log_path(&self) -> Option<&str> {
+ if self.logfile_proxy.created.load(Ordering::Relaxed) {
+ Some(&self.logfile_proxy.path)
+ } else {
+ None
+ }
+ }
+
+ /// Clear log warnings/errors from the Alacritty UI.
+ pub fn clear(&mut self) {
+ self.errors.store(false, Ordering::Relaxed);
+ self.warnings.store(false, Ordering::Relaxed);
+ }
+
+ pub fn delete_log(&mut self) {
+ self.logfile_proxy.delete_log();
+ }
+}
+
+struct Logger {
level: log::LevelFilter,
- output: sync::Mutex<T>
+ logfile: Mutex<OnDemandLogFile>,
+ stdout: Mutex<LineWriter<Stdout>>,
+ errors: Arc<AtomicBool>,
+ warnings: Arc<AtomicBool>,
}
-impl<T: Send + io::Write> Logger<T> {
+impl Logger {
// False positive, see: https://github.com/rust-lang-nursery/rust-clippy/issues/734
#[cfg_attr(feature = "cargo-clippy", allow(new_ret_no_self))]
- pub fn new(output: T, level: log::LevelFilter) -> Logger<io::LineWriter<T>> {
+ fn new(level: log::LevelFilter) -> Self {
log::set_max_level(level);
+
+ let logfile = Mutex::new(OnDemandLogFile::new());
+ let stdout = Mutex::new(LineWriter::new(io::stdout()));
+
Logger {
level,
- output: sync::Mutex::new(io::LineWriter::new(output))
+ logfile,
+ stdout,
+ errors: Arc::new(AtomicBool::new(false)),
+ warnings: Arc::new(AtomicBool::new(false)),
+ }
+ }
+
+ fn proxy(&self) -> LoggerProxy {
+ LoggerProxy {
+ errors: self.errors.clone(),
+ warnings: self.warnings.clone(),
+ logfile_proxy: self.logfile.lock().expect("").proxy(),
}
}
}
-impl<T: Send + io::Write> log::Log for Logger<T> {
+impl log::Log for Logger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() <= self.level
}
fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) && record.target().starts_with("alacritty") {
- if let Ok(ref mut writer) = self.output.lock() {
- let _ = writer.write_all(format!("{}\n", record.args()).as_ref());
+ let msg = format!(
+ "[{}] [{}] {}\n",
+ time::now().strftime("%F %R").unwrap(),
+ record.level(),
+ record.args()
+ );
+
+ if let Ok(ref mut logfile) = self.logfile.lock() {
+ let _ = logfile.write_all(msg.as_ref());
+ }
+
+ if let Ok(ref mut stdout) = self.stdout.lock() {
+ let _ = stdout.write_all(msg.as_ref());
+ }
+
+ match record.level() {
+ Level::Error => self.errors.store(true, Ordering::Relaxed),
+ Level::Warn => self.warnings.store(true, Ordering::Relaxed),
+ _ => (),
}
}
}
@@ -55,12 +152,82 @@ impl<T: Send + io::Write> log::Log for Logger<T> {
fn flush(&self) {}
}
-pub fn initialize(options: &cli::Options) -> Result<(), log::SetLoggerError> {
- // Use env_logger if RUST_LOG environment variable is defined. Otherwise,
- // use the alacritty-only logger.
- if ::std::env::var("RUST_LOG").is_ok() {
- ::env_logger::try_init()
- } else {
- log::set_boxed_logger(Box::new(Logger::new(io::stdout(), options.log_level)))
+#[derive(Clone, Default)]
+struct OnDemandLogFileProxy {
+ created: Arc<AtomicBool>,
+ path: String,
+}
+
+impl OnDemandLogFileProxy {
+ fn delete_log(&mut self) {
+ if self.created.load(Ordering::Relaxed) && fs::remove_file(&self.path).is_ok() {
+ let _ = writeln!(io::stdout(), "Deleted log file at {:?}", self.path);
+ self.created.store(false, Ordering::Relaxed);
+ }
+ }
+}
+
+struct OnDemandLogFile {
+ file: Option<LineWriter<File>>,
+ created: Arc<AtomicBool>,
+ path: PathBuf,
+}
+
+impl OnDemandLogFile {
+ fn new() -> Self {
+ let mut path = env::temp_dir();
+ path.push(format!("Alacritty-{}.log", process::id()));
+
+ OnDemandLogFile {
+ path,
+ file: None,
+ created: Arc::new(AtomicBool::new(false)),
+ }
+ }
+
+ fn file(&mut self) -> Result<&mut LineWriter<File>, io::Error> {
+ // Allow to recreate the file if it has been deleted at runtime
+ if self.file.is_some() && !self.path.as_path().exists() {
+ self.file = None;
+ }
+
+ // Create the file if it doesn't exist yet
+ if self.file.is_none() {
+ let file = OpenOptions::new()
+ .append(true)
+ .create(true)
+ .open(&self.path);
+
+ match file {
+ Ok(file) => {
+ self.file = Some(io::LineWriter::new(file));
+ self.created.store(true, Ordering::Relaxed);
+ let _ = writeln!(io::stdout(), "Created log file at {:?}", self.path);
+ }
+ Err(e) => {
+ let _ = writeln!(io::stdout(), "Unable to create log file: {}", e);
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(self.file.as_mut().unwrap())
+ }
+
+ fn proxy(&self) -> OnDemandLogFileProxy {
+ OnDemandLogFileProxy {
+ created: self.created.clone(),
+ path: self.path.to_string_lossy().to_string(),
+ }
+ }
+}
+
+impl Write for OnDemandLogFile {
+ fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
+ self.file()?.write(buf)
+ }
+
+ fn flush(&mut self) -> Result<(), io::Error> {
+ self.file()?.flush()
}
}
diff --git a/src/macros.rs b/src/macros.rs
index 464110e6..519f8b6a 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -15,33 +15,7 @@
#[macro_export]
macro_rules! die {
($($arg:tt)*) => {{
- eprintln!($($arg)*);
+ error!($($arg)*);
::std::process::exit(1);
}}
}
-
-#[macro_export]
-macro_rules! maybe {
- ($option:expr) => {
- match $option {
- Some(value) => value,
- None => return None,
- }
- }
-}
-
-#[macro_export]
-macro_rules! print {
- ($($arg:tt)*) => {{
- use std::io::Write;
- let _ = write!(::std::io::stdout(), $($arg)*);
- }};
-}
-
-#[macro_export]
-macro_rules! eprint {
- ($($arg:tt)*) => {{
- use std::io::Write;
- let _ = write!(::std::io::stderr(), $($arg)*);
- }};
-}
diff --git a/src/main.rs b/src/main.rs
index 11adb396..a44c8eb3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -52,7 +52,7 @@ use alacritty::event;
use alacritty::event_loop::{self, EventLoop, Msg};
#[cfg(target_os = "macos")]
use alacritty::locale;
-use alacritty::logging;
+use alacritty::logging::{self, LoggerProxy};
use alacritty::sync::FairMutex;
use alacritty::term::Term;
use alacritty::tty::{self, process_should_exit};
@@ -65,8 +65,13 @@ fn main() {
#[cfg(windows)]
unsafe { AttachConsole(ATTACH_PARENT_PROCESS); }
- // Load command line options and config
+ // Load command line options
let options = cli::Options::load();
+
+ // Initialize the logger as soon as possible as to capture output from other subsystems
+ let logger_proxy = logging::initialize(&options).expect("Unable to initialize logger");
+
+ // Load configuration file
let config = load_config(&options).update_dynamic_title(&options);
// Switch to home directory
@@ -77,11 +82,9 @@ fn main() {
locale::set_locale_environment();
// Run alacritty
- if let Err(err) = run(config, &options) {
+ if let Err(err) = run(config, &options, logger_proxy) {
die!("Alacritty encountered an unrecoverable error:\n\n\t{}\n", Red(err));
}
-
- info!("Goodbye.");
}
/// Load configuration
@@ -98,7 +101,7 @@ fn load_config(options: &cli::Options) -> Config {
});
Config::load_from(&*config_path).unwrap_or_else(|err| {
- eprintln!("Error: {}; Loading default config", err);
+ error!("Error: {}; Loading default config", err);
Config::default()
})
}
@@ -107,10 +110,11 @@ fn load_config(options: &cli::Options) -> Config {
///
/// Creates a window, the terminal state, pty, I/O event loop, input processor,
/// config change monitor, and runs the main display loop.
-fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
- // Initialize the logger first as to capture output from other subsystems
- logging::initialize(options)?;
-
+fn run(
+ mut config: Config,
+ options: &cli::Options,
+ mut logger_proxy: LoggerProxy,
+) -> Result<(), Box<Error>> {
info!("Welcome to Alacritty.");
if let Some(config_path) = config.path() {
info!("Configuration loaded from {}", config_path.display());
@@ -122,7 +126,7 @@ fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
// Create a display.
//
// The display manages a window and can draw the terminal
- let mut display = Display::new(&config, options)?;
+ let mut display = Display::new(&config, options, logger_proxy.clone())?;
info!(
"PTY Dimensions: {:?} x {:?}",
@@ -135,7 +139,8 @@ fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
// This object contains all of the state about what's being displayed. It's
// wrapped in a clonable mutex since both the I/O loop and display need to
// access it.
- let terminal = Term::new(&config, display.size().to_owned());
+ let mut terminal = Term::new(&config, display.size().to_owned());
+ terminal.set_logger_proxy(logger_proxy.clone());
let terminal = Arc::new(FairMutex::new(terminal));
// Find the window ID for setting $WINDOWID
@@ -258,5 +263,11 @@ fn run(mut config: Config, options: &cli::Options) -> Result<(), Box<Error>> {
#[cfg(windows)]
unsafe { FreeConsole(); }
+ info!("Goodbye.");
+
+ if !options.persistent_logging && !config.persistent_logging() {
+ logger_proxy.delete_log();
+ }
+
Ok(())
}
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
index 1a193caf..ef5a1e76 100644
--- a/src/renderer/mod.rs
+++ b/src/renderer/mod.rs
@@ -817,10 +817,16 @@ impl<'a> RenderApi<'a> {
self.batch.clear();
}
- /// Render a string in a predefined location. Used for printing render time for profiling and
- /// optimization.
- pub fn render_string(&mut self, string: &str, glyph_cache: &mut GlyphCache, color: Rgb) {
- let line = Line(23);
+
+ /// Render a string in a variable location. Used for printing the render timer, warnings and
+ /// errors.
+ pub fn render_string(
+ &mut self,
+ string: &str,
+ line: Line,
+ glyph_cache: &mut GlyphCache,
+ color: Rgb
+ ) {
let col = Column(0);
let cells = string
diff --git a/src/term/mod.rs b/src/term/mod.rs
index 39213ee2..0f228f86 100644
--- a/src/term/mod.rs
+++ b/src/term/mod.rs
@@ -31,6 +31,7 @@ use config::{Config, VisualBellAnimation};
use {MouseCursor, Rgb};
use copypasta::{Clipboard, Load, Store};
use input::FONT_SIZE_STEP;
+use logging::LoggerProxy;
pub mod cell;
pub mod color;
@@ -810,6 +811,9 @@ pub struct Term {
/// Automatically scroll to bottom when new lines are added
auto_scroll: bool,
+
+ /// Proxy object for clearing displayed errors and warnings
+ logger_proxy: Option<LoggerProxy>,
}
/// Terminal size info
@@ -936,9 +940,14 @@ impl Term {
dynamic_title: config.dynamic_title(),
tabspaces,
auto_scroll: config.scrolling().auto_scroll,
+ logger_proxy: None,
}
}
+ pub fn set_logger_proxy(&mut self, logger_proxy: LoggerProxy) {
+ self.logger_proxy = Some(logger_proxy);
+ }
+
pub fn change_font_size(&mut self, delta: f32) {
// Saturating addition with minimum font size FONT_SIZE_STEP
let new_size = self.font_size + Size::new(delta);
@@ -1822,6 +1831,11 @@ impl ansi::Handler for Term {
}
},
ansi::ClearMode::All => {
+ // Clear displayed errors and warnings
+ if let Some(ref mut logger_proxy) = self.logger_proxy {
+ logger_proxy.clear();
+ }
+
self.grid.region_mut(..).each(|c| c.reset(&template));
},
ansi::ClearMode::Above => {