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