1 use std::borrow::Cow; 2 use std::cell::RefCell; 3 use std::fmt; 4 use std::io::{self, Write}; 5 use std::rc::Rc; 6 use std::sync::Mutex; 7 8 use log::Level; 9 use termcolor::{self, ColorChoice, ColorSpec, WriteColor}; 10 11 use crate::fmt::{Formatter, WritableTarget, WriteStyle}; 12 13 pub(in crate::fmt::writer) mod glob { 14 pub use super::*; 15 } 16 17 impl Formatter { 18 /// Begin a new [`Style`]. 19 /// 20 /// # Examples 21 /// 22 /// Create a bold, red colored style and use it to print the log level: 23 /// 24 /// ``` 25 /// use std::io::Write; 26 /// use env_logger::fmt::Color; 27 /// 28 /// let mut builder = env_logger::Builder::new(); 29 /// 30 /// builder.format(|buf, record| { 31 /// let mut level_style = buf.style(); 32 /// 33 /// level_style.set_color(Color::Red).set_bold(true); 34 /// 35 /// writeln!(buf, "{}: {}", 36 /// level_style.value(record.level()), 37 /// record.args()) 38 /// }); 39 /// ``` 40 /// 41 /// [`Style`]: struct.Style.html style(&self) -> Style42 pub fn style(&self) -> Style { 43 Style { 44 buf: self.buf.clone(), 45 spec: ColorSpec::new(), 46 } 47 } 48 49 /// Get the default [`Style`] for the given level. 50 /// 51 /// The style can be used to print other values besides the level. default_level_style(&self, level: Level) -> Style52 pub fn default_level_style(&self, level: Level) -> Style { 53 let mut level_style = self.style(); 54 match level { 55 Level::Trace => level_style.set_color(Color::Cyan), 56 Level::Debug => level_style.set_color(Color::Blue), 57 Level::Info => level_style.set_color(Color::Green), 58 Level::Warn => level_style.set_color(Color::Yellow), 59 Level::Error => level_style.set_color(Color::Red).set_bold(true), 60 }; 61 level_style 62 } 63 64 /// Get a printable [`Style`] for the given level. 65 /// 66 /// The style can only be used to print the level. default_styled_level(&self, level: Level) -> StyledValue<'static, Level>67 pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> { 68 self.default_level_style(level).into_value(level) 69 } 70 } 71 72 pub(in crate::fmt::writer) struct BufferWriter { 73 inner: termcolor::BufferWriter, 74 test_target: Option<WritableTarget>, 75 } 76 77 pub(in crate::fmt) struct Buffer { 78 inner: termcolor::Buffer, 79 has_test_target: bool, 80 } 81 82 impl BufferWriter { stderr(is_test: bool, write_style: WriteStyle) -> Self83 pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self { 84 BufferWriter { 85 inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), 86 test_target: if is_test { 87 Some(WritableTarget::Stderr) 88 } else { 89 None 90 }, 91 } 92 } 93 stdout(is_test: bool, write_style: WriteStyle) -> Self94 pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self { 95 BufferWriter { 96 inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()), 97 test_target: if is_test { 98 Some(WritableTarget::Stdout) 99 } else { 100 None 101 }, 102 } 103 } 104 pipe( is_test: bool, write_style: WriteStyle, pipe: Box<Mutex<dyn io::Write + Send + 'static>>, ) -> Self105 pub(in crate::fmt::writer) fn pipe( 106 is_test: bool, 107 write_style: WriteStyle, 108 pipe: Box<Mutex<dyn io::Write + Send + 'static>>, 109 ) -> Self { 110 BufferWriter { 111 // The inner Buffer is never printed from, but it is still needed to handle coloring and other formating 112 inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()), 113 test_target: if is_test { 114 Some(WritableTarget::Pipe(pipe)) 115 } else { 116 None 117 }, 118 } 119 } 120 buffer(&self) -> Buffer121 pub(in crate::fmt::writer) fn buffer(&self) -> Buffer { 122 Buffer { 123 inner: self.inner.buffer(), 124 has_test_target: self.test_target.is_some(), 125 } 126 } 127 print(&self, buf: &Buffer) -> io::Result<()>128 pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> { 129 if let Some(target) = &self.test_target { 130 // This impl uses the `eprint` and `print` macros 131 // instead of `termcolor`'s buffer. 132 // This is so their output can be captured by `cargo test` 133 let log = String::from_utf8_lossy(buf.bytes()); 134 135 match target { 136 WritableTarget::Stderr => eprint!("{}", log), 137 WritableTarget::Stdout => print!("{}", log), 138 WritableTarget::Pipe(pipe) => write!(pipe.lock().unwrap(), "{}", log)?, 139 } 140 141 Ok(()) 142 } else { 143 self.inner.print(&buf.inner) 144 } 145 } 146 } 147 148 impl Buffer { clear(&mut self)149 pub(in crate::fmt) fn clear(&mut self) { 150 self.inner.clear() 151 } 152 write(&mut self, buf: &[u8]) -> io::Result<usize>153 pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 154 self.inner.write(buf) 155 } 156 flush(&mut self) -> io::Result<()>157 pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> { 158 self.inner.flush() 159 } 160 bytes(&self) -> &[u8]161 pub(in crate::fmt) fn bytes(&self) -> &[u8] { 162 self.inner.as_slice() 163 } 164 set_color(&mut self, spec: &ColorSpec) -> io::Result<()>165 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { 166 // Ignore styles for test captured logs because they can't be printed 167 if !self.has_test_target { 168 self.inner.set_color(spec) 169 } else { 170 Ok(()) 171 } 172 } 173 reset(&mut self) -> io::Result<()>174 fn reset(&mut self) -> io::Result<()> { 175 // Ignore styles for test captured logs because they can't be printed 176 if !self.has_test_target { 177 self.inner.reset() 178 } else { 179 Ok(()) 180 } 181 } 182 } 183 184 impl WriteStyle { into_color_choice(self) -> ColorChoice185 fn into_color_choice(self) -> ColorChoice { 186 match self { 187 WriteStyle::Always => ColorChoice::Always, 188 WriteStyle::Auto => ColorChoice::Auto, 189 WriteStyle::Never => ColorChoice::Never, 190 } 191 } 192 } 193 194 /// A set of styles to apply to the terminal output. 195 /// 196 /// Call [`Formatter::style`] to get a `Style` and use the builder methods to 197 /// set styling properties, like [color] and [weight]. 198 /// To print a value using the style, wrap it in a call to [`value`] when the log 199 /// record is formatted. 200 /// 201 /// # Examples 202 /// 203 /// Create a bold, red colored style and use it to print the log level: 204 /// 205 /// ``` 206 /// use std::io::Write; 207 /// use env_logger::fmt::Color; 208 /// 209 /// let mut builder = env_logger::Builder::new(); 210 /// 211 /// builder.format(|buf, record| { 212 /// let mut level_style = buf.style(); 213 /// 214 /// level_style.set_color(Color::Red).set_bold(true); 215 /// 216 /// writeln!(buf, "{}: {}", 217 /// level_style.value(record.level()), 218 /// record.args()) 219 /// }); 220 /// ``` 221 /// 222 /// Styles can be re-used to output multiple values: 223 /// 224 /// ``` 225 /// use std::io::Write; 226 /// use env_logger::fmt::Color; 227 /// 228 /// let mut builder = env_logger::Builder::new(); 229 /// 230 /// builder.format(|buf, record| { 231 /// let mut bold = buf.style(); 232 /// 233 /// bold.set_bold(true); 234 /// 235 /// writeln!(buf, "{}: {} {}", 236 /// bold.value(record.level()), 237 /// bold.value("some bold text"), 238 /// record.args()) 239 /// }); 240 /// ``` 241 /// 242 /// [`Formatter::style`]: struct.Formatter.html#method.style 243 /// [color]: #method.set_color 244 /// [weight]: #method.set_bold 245 /// [`value`]: #method.value 246 #[derive(Clone)] 247 pub struct Style { 248 buf: Rc<RefCell<Buffer>>, 249 spec: ColorSpec, 250 } 251 252 /// A value that can be printed using the given styles. 253 /// 254 /// It is the result of calling [`Style::value`]. 255 /// 256 /// [`Style::value`]: struct.Style.html#method.value 257 pub struct StyledValue<'a, T> { 258 style: Cow<'a, Style>, 259 value: T, 260 } 261 262 impl Style { 263 /// Set the text color. 264 /// 265 /// # Examples 266 /// 267 /// Create a style with red text: 268 /// 269 /// ``` 270 /// use std::io::Write; 271 /// use env_logger::fmt::Color; 272 /// 273 /// let mut builder = env_logger::Builder::new(); 274 /// 275 /// builder.format(|buf, record| { 276 /// let mut style = buf.style(); 277 /// 278 /// style.set_color(Color::Red); 279 /// 280 /// writeln!(buf, "{}", style.value(record.args())) 281 /// }); 282 /// ``` set_color(&mut self, color: Color) -> &mut Style283 pub fn set_color(&mut self, color: Color) -> &mut Style { 284 self.spec.set_fg(Some(color.into_termcolor())); 285 self 286 } 287 288 /// Set the text weight. 289 /// 290 /// If `yes` is true then text will be written in bold. 291 /// If `yes` is false then text will be written in the default weight. 292 /// 293 /// # Examples 294 /// 295 /// Create a style with bold text: 296 /// 297 /// ``` 298 /// use std::io::Write; 299 /// 300 /// let mut builder = env_logger::Builder::new(); 301 /// 302 /// builder.format(|buf, record| { 303 /// let mut style = buf.style(); 304 /// 305 /// style.set_bold(true); 306 /// 307 /// writeln!(buf, "{}", style.value(record.args())) 308 /// }); 309 /// ``` set_bold(&mut self, yes: bool) -> &mut Style310 pub fn set_bold(&mut self, yes: bool) -> &mut Style { 311 self.spec.set_bold(yes); 312 self 313 } 314 315 /// Set the text intensity. 316 /// 317 /// If `yes` is true then text will be written in a brighter color. 318 /// If `yes` is false then text will be written in the default color. 319 /// 320 /// # Examples 321 /// 322 /// Create a style with intense text: 323 /// 324 /// ``` 325 /// use std::io::Write; 326 /// 327 /// let mut builder = env_logger::Builder::new(); 328 /// 329 /// builder.format(|buf, record| { 330 /// let mut style = buf.style(); 331 /// 332 /// style.set_intense(true); 333 /// 334 /// writeln!(buf, "{}", style.value(record.args())) 335 /// }); 336 /// ``` set_intense(&mut self, yes: bool) -> &mut Style337 pub fn set_intense(&mut self, yes: bool) -> &mut Style { 338 self.spec.set_intense(yes); 339 self 340 } 341 342 /// Set the background color. 343 /// 344 /// # Examples 345 /// 346 /// Create a style with a yellow background: 347 /// 348 /// ``` 349 /// use std::io::Write; 350 /// use env_logger::fmt::Color; 351 /// 352 /// let mut builder = env_logger::Builder::new(); 353 /// 354 /// builder.format(|buf, record| { 355 /// let mut style = buf.style(); 356 /// 357 /// style.set_bg(Color::Yellow); 358 /// 359 /// writeln!(buf, "{}", style.value(record.args())) 360 /// }); 361 /// ``` set_bg(&mut self, color: Color) -> &mut Style362 pub fn set_bg(&mut self, color: Color) -> &mut Style { 363 self.spec.set_bg(Some(color.into_termcolor())); 364 self 365 } 366 367 /// Wrap a value in the style. 368 /// 369 /// The same `Style` can be used to print multiple different values. 370 /// 371 /// # Examples 372 /// 373 /// Create a bold, red colored style and use it to print the log level: 374 /// 375 /// ``` 376 /// use std::io::Write; 377 /// use env_logger::fmt::Color; 378 /// 379 /// let mut builder = env_logger::Builder::new(); 380 /// 381 /// builder.format(|buf, record| { 382 /// let mut style = buf.style(); 383 /// 384 /// style.set_color(Color::Red).set_bold(true); 385 /// 386 /// writeln!(buf, "{}: {}", 387 /// style.value(record.level()), 388 /// record.args()) 389 /// }); 390 /// ``` value<T>(&self, value: T) -> StyledValue<T>391 pub fn value<T>(&self, value: T) -> StyledValue<T> { 392 StyledValue { 393 style: Cow::Borrowed(self), 394 value, 395 } 396 } 397 398 /// Wrap a value in the style by taking ownership of it. into_value<T>(self, value: T) -> StyledValue<'static, T>399 pub(crate) fn into_value<T>(self, value: T) -> StyledValue<'static, T> { 400 StyledValue { 401 style: Cow::Owned(self), 402 value, 403 } 404 } 405 } 406 407 impl<'a, T> StyledValue<'a, T> { write_fmt<F>(&self, f: F) -> fmt::Result where F: FnOnce() -> fmt::Result,408 fn write_fmt<F>(&self, f: F) -> fmt::Result 409 where 410 F: FnOnce() -> fmt::Result, 411 { 412 self.style 413 .buf 414 .borrow_mut() 415 .set_color(&self.style.spec) 416 .map_err(|_| fmt::Error)?; 417 418 // Always try to reset the terminal style, even if writing failed 419 let write = f(); 420 let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error); 421 422 write.and(reset) 423 } 424 } 425 426 impl fmt::Debug for Style { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result427 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 428 f.debug_struct("Style").field("spec", &self.spec).finish() 429 } 430 } 431 432 macro_rules! impl_styled_value_fmt { 433 ($($fmt_trait:path),*) => { 434 $( 435 impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> { 436 fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { 437 self.write_fmt(|| T::fmt(&self.value, f)) 438 } 439 } 440 )* 441 }; 442 } 443 444 impl_styled_value_fmt!( 445 fmt::Debug, 446 fmt::Display, 447 fmt::Pointer, 448 fmt::Octal, 449 fmt::Binary, 450 fmt::UpperHex, 451 fmt::LowerHex, 452 fmt::UpperExp, 453 fmt::LowerExp 454 ); 455 456 // The `Color` type is copied from https://github.com/BurntSushi/ripgrep/tree/master/termcolor 457 458 /// The set of available colors for the terminal foreground/background. 459 /// 460 /// The `Ansi256` and `Rgb` colors will only output the correct codes when 461 /// paired with the `Ansi` `WriteColor` implementation. 462 /// 463 /// The `Ansi256` and `Rgb` color types are not supported when writing colors 464 /// on Windows using the console. If they are used on Windows, then they are 465 /// silently ignored and no colors will be emitted. 466 /// 467 /// This set may expand over time. 468 /// 469 /// This type has a `FromStr` impl that can parse colors from their human 470 /// readable form. The format is as follows: 471 /// 472 /// 1. Any of the explicitly listed colors in English. They are matched 473 /// case insensitively. 474 /// 2. A single 8-bit integer, in either decimal or hexadecimal format. 475 /// 3. A triple of 8-bit integers separated by a comma, where each integer is 476 /// in decimal or hexadecimal format. 477 /// 478 /// Hexadecimal numbers are written with a `0x` prefix. 479 #[allow(missing_docs)] 480 #[non_exhaustive] 481 #[derive(Clone, Debug, Eq, PartialEq)] 482 pub enum Color { 483 Black, 484 Blue, 485 Green, 486 Red, 487 Cyan, 488 Magenta, 489 Yellow, 490 White, 491 Ansi256(u8), 492 Rgb(u8, u8, u8), 493 } 494 495 impl Color { into_termcolor(self) -> termcolor::Color496 fn into_termcolor(self) -> termcolor::Color { 497 match self { 498 Color::Black => termcolor::Color::Black, 499 Color::Blue => termcolor::Color::Blue, 500 Color::Green => termcolor::Color::Green, 501 Color::Red => termcolor::Color::Red, 502 Color::Cyan => termcolor::Color::Cyan, 503 Color::Magenta => termcolor::Color::Magenta, 504 Color::Yellow => termcolor::Color::Yellow, 505 Color::White => termcolor::Color::White, 506 Color::Ansi256(value) => termcolor::Color::Ansi256(value), 507 Color::Rgb(r, g, b) => termcolor::Color::Rgb(r, g, b), 508 } 509 } 510 } 511