///|
/// Logfmt-style formatter producing key=value pairs.
pub fn logfmt_formatter() -> (LogEntry) -> String {
fn(entry : LogEntry) -> String {
let sb = StringBuilder()
sb.write_string("level=")
sb.write_string(entry.level.to_string())
sb.write_string(" msg=")
sb.write_string(logfmt_esc(entry.message))
sb.write_string(" ts=")
sb.write_string(entry.timestamp.to_string())
if entry.mod_name.length() > 0 {
sb.write_string(" module=")
sb.write_string(logfmt_esc(entry.mod_name))
}
if entry.file.length() > 0 {
sb.write_string(" file=")
sb.write_string(logfmt_esc(entry.file))
sb.write_string(":")
sb.write_string(entry.line.to_string())
}
let n = entry.fields.length()
let mut i = 0
while i < n {
let (k, v) = entry.fields[i]
sb.write_string(" ")
sb.write_string(k)
sb.write_string("=")
sb.write_string(logfmt_esc(v))
i = i + 1
}
sb.to_string()
}
}
// Escape a string value for safe inclusion in logfmt output.
///|
fn logfmt_esc(s : String) -> String {
let needs_quoting = s.contains(" ") ||
s.contains("=") ||
s.contains("\"") ||
s.contains("\n")
if !needs_quoting {
return s
}
let sb = StringBuilder()
sb.write_string("\"")
for c in s.iter() {
if c == '\\' {
sb.write_string("\\\\")
} else if c == '"' {
sb.write_string("\\\"")
} else if c == '\n' {
sb.write_string("\\n")
} else {
sb.write_char(c)
}
}
sb.write_string("\"")
sb.to_string()
}
///|
/// XML formatter producing element with structured children.
pub fn xml_formatter() -> (LogEntry) -> String {
fn(entry : LogEntry) -> String {
let sb = StringBuilder()
sb.write_string("")
sb.write_string("")
sb.write_string(entry.timestamp.to_string())
sb.write_string("")
sb.write_string("")
sb.write_string(xml_esc(entry.level.to_string()))
sb.write_string("")
sb.write_string("")
sb.write_string(xml_esc(entry.message))
sb.write_string("")
sb.write_string("")
sb.write_string(xml_esc(entry.mod_name))
sb.write_string("")
sb.write_string("")
sb.write_string(xml_esc(entry.file))
sb.write_string("")
sb.write_string("")
sb.write_string(entry.line.to_string())
sb.write_string("")
sb.write_string("")
sb.write_string(entry.thread_id.to_string())
sb.write_string("")
sb.write_string("")
sb.write_string(entry.sequence.to_string())
sb.write_string("")
if entry.fields.length() > 0 {
sb.write_string("")
let n = entry.fields.length()
let mut i = 0
while i < n {
let (k, v) = entry.fields[i]
sb.write_string("")
i = i + 1
}
sb.write_string("")
}
sb.write_string("")
sb.to_string()
}
}
// Escape special XML characters (<, >, &, ", ').
///|
fn xml_esc(s : String) -> String {
let sb = StringBuilder()
for c in s.iter() {
if c == '<' {
sb.write_string("<")
} else if c == '>' {
sb.write_string(">")
} else if c == '&' {
sb.write_string("&")
} else if c == '"' {
sb.write_string(""")
} else if c == '\'' {
sb.write_string("'")
} else {
sb.write_char(c)
}
}
sb.to_string()
}
// Keep for future ANSI terminal color support
///|
fn _esc_ansi(s : String) -> String {
let sb = StringBuilder()
for c in s.iter() {
if c == '\\' {
sb.write_string("\\\\")
} else if c == 'e' || c == 'E' {
sb.write_string("\u001b")
} else {
sb.write_char(c)
}
}
sb.to_string()
}
///|
/// Colorized formatter with optional ANSI color codes by log level.
pub fn pretty_formatter(use_color : Bool) -> (LogEntry) -> String {
fn(entry : LogEntry) -> String {
let sb = StringBuilder()
if use_color {
sb.write_string(color_code(entry.level))
}
sb.write_string("[")
sb.write_string(entry.timestamp.to_string())
sb.write_string("] [")
sb.write_string(entry.level.to_string())
sb.write_string("]")
if entry.mod_name.length() > 0 {
sb.write_string(" (")
sb.write_string(entry.mod_name)
sb.write_string(")")
}
sb.write_string(" ")
sb.write_string(entry.message)
let n = entry.fields.length()
let mut i = 0
while i < n {
let (k, v) = entry.fields[i]
sb.write_string(" ")
sb.write_string(k)
sb.write_string("=")
sb.write_string(v)
i = i + 1
}
if use_color {
sb.write_string("\u001b[0m")
}
sb.to_string()
}
}
// Return ANSI color code for the given log level.
///|
fn color_code(l : Level) -> String {
match l {
Debug => "\u001b[90m"
Info => "\u001b[92m"
Warn => "\u001b[93m"
Error => "\u001b[91m"
Fatal => "\u001b[35m"
}
}
///|
/// ANSI red foreground color code.
pub fn ansi_red() -> String {
"\u001b[91m"
}
///|
/// ANSI green foreground color code.
pub fn ansi_green() -> String {
"\u001b[92m"
}
///|
/// ANSI yellow foreground color code.
pub fn ansi_yellow() -> String {
"\u001b[93m"
}
///|
/// ANSI blue foreground color code.
pub fn ansi_blue() -> String {
"\u001b[94m"
}
///|
/// ANSI magenta foreground color code.
pub fn ansi_magenta() -> String {
"\u001b[35m"
}
///|
/// ANSI cyan foreground color code.
pub fn ansi_cyan() -> String {
"\u001b[96m"
}
///|
/// ANSI gray foreground color code.
pub fn ansi_gray() -> String {
"\u001b[90m"
}
///|
/// ANSI reset code to clear all attributes.
pub fn ansi_reset() -> String {
"\u001b[0m"
}
///|
/// ANSI bold / increased intensity code.
pub fn ansi_bold() -> String {
"\u001b[1m"
}
///|
/// ANSI dim / decreased intensity code.
pub fn ansi_dim() -> String {
"\u001b[2m"
}