Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
375 lines
12 KiB
Rust
375 lines
12 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use std::fmt::Display;
|
|
|
|
use mail_parser::DateTime;
|
|
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
|
|
|
use crate::{Error, Event, EventDetails, Key, Level, Value};
|
|
use base64::{Engine, engine::general_purpose::STANDARD};
|
|
|
|
pub struct FmtWriter<T: AsyncWrite + Unpin> {
|
|
writer: T,
|
|
ansi: bool,
|
|
multiline: bool,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
enum Color {
|
|
Black,
|
|
Red,
|
|
Green,
|
|
Yellow,
|
|
Blue,
|
|
Magenta,
|
|
Cyan,
|
|
White,
|
|
}
|
|
|
|
impl<T: AsyncWrite + Unpin> FmtWriter<T> {
|
|
pub fn new(writer: T) -> Self {
|
|
Self {
|
|
writer,
|
|
ansi: false,
|
|
multiline: false,
|
|
}
|
|
}
|
|
|
|
pub fn with_ansi(self, ansi: bool) -> Self {
|
|
Self { ansi, ..self }
|
|
}
|
|
|
|
pub fn with_multiline(self, multiline: bool) -> Self {
|
|
Self { multiline, ..self }
|
|
}
|
|
|
|
pub async fn write(&mut self, event: &Event<EventDetails>) -> std::io::Result<()> {
|
|
// Write timestamp
|
|
if self.ansi {
|
|
self.writer
|
|
.write_all(Color::White.as_code().as_bytes())
|
|
.await?;
|
|
}
|
|
self.writer
|
|
.write_all(
|
|
DateTime::from_timestamp(event.inner.timestamp as i64)
|
|
.to_rfc3339()
|
|
.as_bytes(),
|
|
)
|
|
.await?;
|
|
if self.ansi {
|
|
self.writer.write_all(Color::reset().as_bytes()).await?;
|
|
}
|
|
self.writer.write_all(" ".as_bytes()).await?;
|
|
|
|
// Write level
|
|
if self.ansi {
|
|
self.writer
|
|
.write_all(
|
|
match event.inner.level {
|
|
Level::Error => Color::Red,
|
|
Level::Warn => Color::Yellow,
|
|
Level::Info => Color::Green,
|
|
Level::Debug => Color::Blue,
|
|
Level::Trace => Color::Magenta,
|
|
Level::Disable => return Ok(()),
|
|
}
|
|
.as_code_bold()
|
|
.as_bytes(),
|
|
)
|
|
.await?;
|
|
}
|
|
self.writer
|
|
.write_all(event.inner.level.as_str().as_bytes())
|
|
.await?;
|
|
if self.ansi {
|
|
self.writer.write_all(Color::reset().as_bytes()).await?;
|
|
}
|
|
self.writer.write_all(" ".as_bytes()).await?;
|
|
|
|
// Write message
|
|
if self.ansi {
|
|
self.writer
|
|
.write_all(Color::White.as_code_bold().as_bytes())
|
|
.await?;
|
|
}
|
|
self.writer
|
|
.write_all(event.inner.typ.description().as_bytes())
|
|
.await?;
|
|
if self.ansi {
|
|
self.writer.write_all(Color::reset().as_bytes()).await?;
|
|
}
|
|
self.writer.write_all(" (".as_bytes()).await?;
|
|
self.writer
|
|
.write_all(event.inner.typ.as_str().as_bytes())
|
|
.await?;
|
|
|
|
self.writer
|
|
.write_all(if self.multiline { ")\n" } else { ") " }.as_bytes())
|
|
.await?;
|
|
|
|
// Write keys
|
|
if let Some(parent_event) = &event.inner.span {
|
|
self.write_keys(&parent_event.keys, &event.keys, 1).await?;
|
|
} else {
|
|
self.write_keys(&[], &event.keys, 1).await?;
|
|
}
|
|
|
|
if !self.multiline {
|
|
self.writer.write_all("\n".as_bytes()).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn write_keys(
|
|
&mut self,
|
|
span_keys: &[(Key, Value)],
|
|
keys: &[(Key, Value)],
|
|
indent: usize,
|
|
) -> std::io::Result<()> {
|
|
Box::pin(async move {
|
|
let mut is_first = true;
|
|
for (key, value) in span_keys.iter().chain(keys.iter()) {
|
|
if matches!(key, Key::SpanId) {
|
|
continue;
|
|
} else if is_first {
|
|
is_first = false;
|
|
} else if !self.multiline {
|
|
self.writer.write_all(", ".as_bytes()).await?;
|
|
}
|
|
|
|
// Write key
|
|
if self.multiline {
|
|
for _ in 0..indent {
|
|
self.writer.write_all("\t".as_bytes()).await?;
|
|
}
|
|
}
|
|
if self.ansi {
|
|
self.writer
|
|
.write_all(Color::Cyan.as_code().as_bytes())
|
|
.await?;
|
|
}
|
|
self.writer.write_all(key.as_str().as_bytes()).await?;
|
|
if self.ansi {
|
|
self.writer.write_all(Color::reset().as_bytes()).await?;
|
|
}
|
|
|
|
// Write value
|
|
self.writer.write_all(" = ".as_bytes()).await?;
|
|
self.write_value(value, indent).await?;
|
|
|
|
if self.multiline && !matches!(value, Value::Event(_)) {
|
|
self.writer.write_all("\n".as_bytes()).await?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
})
|
|
.await
|
|
}
|
|
|
|
async fn write_value(&mut self, value: &Value, indent: usize) -> std::io::Result<()> {
|
|
Box::pin(async move {
|
|
match value {
|
|
Value::String(v) => {
|
|
self.writer.write_all("\"".as_bytes()).await?;
|
|
for ch in v.as_bytes() {
|
|
match ch {
|
|
b'\r' => {
|
|
self.writer.write_all("\\r".as_bytes()).await?;
|
|
}
|
|
b'\n' => {
|
|
self.writer.write_all("\\n".as_bytes()).await?;
|
|
}
|
|
b'\t' => {
|
|
self.writer.write_all("\\t".as_bytes()).await?;
|
|
}
|
|
b'\\' => {
|
|
self.writer.write_all("\\\\".as_bytes()).await?;
|
|
}
|
|
_ => {
|
|
self.writer.write_all(&[*ch]).await?;
|
|
}
|
|
}
|
|
}
|
|
self.writer.write_all("\"".as_bytes()).await?;
|
|
}
|
|
Value::UInt(v) => {
|
|
self.writer.write_all(v.to_string().as_bytes()).await?;
|
|
}
|
|
Value::Int(v) => {
|
|
self.writer.write_all(v.to_string().as_bytes()).await?;
|
|
}
|
|
Value::Float(v) => {
|
|
self.writer.write_all(v.to_string().as_bytes()).await?;
|
|
}
|
|
Value::Timestamp(v) => {
|
|
self.writer
|
|
.write_all(DateTime::from_timestamp(*v as i64).to_rfc3339().as_bytes())
|
|
.await?;
|
|
}
|
|
Value::Duration(v) => {
|
|
self.writer.write_all(v.to_string().as_bytes()).await?;
|
|
self.writer.write_all("ms".as_bytes()).await?;
|
|
}
|
|
Value::Bytes(bytes) => {
|
|
self.writer.write_all("base64:".as_bytes()).await?;
|
|
self.writer
|
|
.write_all(STANDARD.encode(bytes).as_bytes())
|
|
.await?;
|
|
}
|
|
Value::Bool(true) => {
|
|
self.writer.write_all("true".as_bytes()).await?;
|
|
}
|
|
Value::Bool(false) => {
|
|
self.writer.write_all("false".as_bytes()).await?;
|
|
}
|
|
Value::Ipv4(v) => {
|
|
self.writer.write_all(v.to_string().as_bytes()).await?;
|
|
}
|
|
Value::Ipv6(v) => {
|
|
self.writer.write_all(v.to_string().as_bytes()).await?;
|
|
}
|
|
Value::Event(e) => {
|
|
self.writer
|
|
.write_all(e.0.inner.description().as_bytes())
|
|
.await?;
|
|
self.writer.write_all(" (".as_bytes()).await?;
|
|
self.writer.write_all(e.0.inner.as_str().as_bytes()).await?;
|
|
self.writer.write_all(")".as_bytes()).await?;
|
|
if !e.0.keys.is_empty() {
|
|
self.writer
|
|
.write_all(if self.multiline { "\n" } else { " { " }.as_bytes())
|
|
.await?;
|
|
|
|
self.write_keys(&e.0.keys, &[], indent + 1).await?;
|
|
|
|
if !self.multiline {
|
|
self.writer.write_all(" }".as_bytes()).await?;
|
|
}
|
|
} else if self.multiline {
|
|
self.writer.write_all("\n".as_bytes()).await?;
|
|
}
|
|
}
|
|
Value::Array(arr) => {
|
|
self.writer.write_all("[".as_bytes()).await?;
|
|
for (pos, value) in arr.iter().enumerate() {
|
|
if pos > 0 {
|
|
self.writer.write_all(", ".as_bytes()).await?;
|
|
}
|
|
self.write_value(value, indent).await?;
|
|
}
|
|
self.writer.write_all("]".as_bytes()).await?;
|
|
}
|
|
Value::None => {
|
|
self.writer.write_all("(null)".as_bytes()).await?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub async fn flush(&mut self) -> std::io::Result<()> {
|
|
self.writer.flush().await
|
|
}
|
|
|
|
pub fn update_writer(&mut self, writer: T) {
|
|
self.writer = writer;
|
|
}
|
|
}
|
|
|
|
impl Color {
|
|
pub fn as_code(&self) -> &'static str {
|
|
match self {
|
|
Color::Black => "\x1b[30m",
|
|
Color::Red => "\x1b[31m",
|
|
Color::Green => "\x1b[32m",
|
|
Color::Yellow => "\x1b[33m",
|
|
Color::Blue => "\x1b[34m",
|
|
Color::Magenta => "\x1b[35m",
|
|
Color::Cyan => "\x1b[36m",
|
|
Color::White => "\x1b[37m",
|
|
}
|
|
}
|
|
|
|
pub fn as_code_bold(&self) -> &'static str {
|
|
match self {
|
|
Color::Black => "\x1b[30;1m",
|
|
Color::Red => "\x1b[31;1m",
|
|
Color::Green => "\x1b[32;1m",
|
|
Color::Yellow => "\x1b[33;1m",
|
|
Color::Blue => "\x1b[34;1m",
|
|
Color::Magenta => "\x1b[35;1m",
|
|
Color::Cyan => "\x1b[36;1m",
|
|
Color::White => "\x1b[37;1m",
|
|
}
|
|
}
|
|
|
|
pub fn reset() -> &'static str {
|
|
"\x1b[0m"
|
|
}
|
|
}
|
|
|
|
impl Display for Value {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Value::String(value) => value.fmt(f),
|
|
Value::UInt(value) => value.fmt(f),
|
|
Value::Int(value) => value.fmt(f),
|
|
Value::Float(value) => value.fmt(f),
|
|
Value::Timestamp(value) => value.fmt(f),
|
|
Value::Duration(value) => value.fmt(f),
|
|
Value::Bytes(value) => STANDARD.encode(value).fmt(f),
|
|
Value::Bool(value) => value.fmt(f),
|
|
Value::Ipv4(value) => value.fmt(f),
|
|
Value::Ipv6(value) => value.fmt(f),
|
|
Value::Event(value) => {
|
|
"{".fmt(f)?;
|
|
value.fmt(f)?;
|
|
"}".fmt(f)
|
|
}
|
|
Value::Array(value) => {
|
|
f.write_str("[")?;
|
|
for (i, value) in value.iter().enumerate() {
|
|
if i > 0 {
|
|
f.write_str(", ")?;
|
|
}
|
|
value.fmt(f)?;
|
|
}
|
|
f.write_str("]")
|
|
}
|
|
Value::None => "(null)".fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for Error {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
self.0.inner.description().fmt(f)?;
|
|
" (".fmt(f)?;
|
|
self.0.inner.as_str().fmt(f)?;
|
|
")".fmt(f)?;
|
|
|
|
if !self.0.keys.is_empty() {
|
|
f.write_str(": ")?;
|
|
for (i, (key, value)) in self.0.keys.iter().enumerate() {
|
|
if i > 0 {
|
|
f.write_str(", ")?;
|
|
}
|
|
key.as_str().fmt(f)?;
|
|
f.write_str(" = ")?;
|
|
value.fmt(f)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|