Skip to content

Commit

Permalink
Add printing of key-value pairs
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomasdezeeuw committed Nov 22, 2019
1 parent bff5ca1 commit d7c9e34
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ timestamp = ["chrono"]
[dependencies]
chrono = { version = "0.4", optional = true }
libc = { version = "0.2.58" }
log = { version = "0.4", features = ["std"] }
log = { version = "0.4.8", features = ["std", "kv_unstable"] }
log-panics = { version = "2", optional = true, features = ["with-backtrace"] }

[dev-dependencies]
Expand Down
81 changes: 81 additions & 0 deletions examples/key_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use log;
use std_logger::REQUEST_TARGET;

fn main() {
// Initialize the logger.
std_logger::init();

// Fake the handling of a request.
logger_middleware(Request {
url: "/".to_owned(),
method: "GET".to_owned(),
});
}

// Our fake HTTP request.
struct Request {
url: String,
method: String,
}

// Our fake HTTP response.
struct Response {
status_code: u16,
body: String,
}

fn logger_middleware(request: Request) -> Response {
// Clone the url and method. Note: don't actually do this in an HTTP this is
// rather wastefull to.
let url = request.url.clone();
let method = request.url.clone();

// Call our handler.
let response = http_handler(request);

log::info!("Hello world");

let kvs: Vec<Box<dyn log::kv::Source>> = vec![
Box::new(("url", &url)),
Box::new(("method", &method)),
Box::new(("status_code", response.status_code)),
Box::new(("body_size", response.body.len() as u64)),
];

let record = log::Record::builder()
.args(format_args!("got request"))
.level(log::Level::Info)
.target(REQUEST_TARGET)
.file(Some(file!()))
.line(Some(line!()))
.module_path(Some(module_path!()))
.key_values(&kvs)
.build();
log::logger().log(&record);

let record = log::Record::builder()
.args(format_args!("some message"))
.level(log::Level::Info)
.target("some_target")
.file(Some(file!()))
.line(Some(line!()))
.module_path(Some(module_path!()))
.key_values(&("single", "value"))
.build();
log::logger().log(&record);

response
}

fn http_handler(request: Request) -> Response {
match (request.method.as_str(), request.url.as_str()) {
("GET", "/") => Response {
status_code: 200,
body: "Home page".to_owned(),
},
_ => Response {
status_code: 404,
body: "Not found".to_owned(),
},
}
}
46 changes: 41 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,10 @@
)]

use std::cell::RefCell;
use std::env;
use std::io::{self, Write};
use std::{env, fmt};

use log::{LevelFilter, Log, Metadata, Record, SetLoggerError};
use log::{kv, LevelFilter, Log, Metadata, Record, SetLoggerError};

#[cfg(feature = "timestamp")]
use chrono::{Datelike, Timelike};
Expand Down Expand Up @@ -370,6 +370,35 @@ impl Log for Logger {
fn flush(&self) {}
}

/// Prints key values in ": key1=value1, key2=value2" format.
///
/// # Notes
///
/// Prints ": " itself, only when there is at least one key value pair.
struct KeyValuePrinter<'a>(&'a dyn kv::Source);

impl<'a> fmt::Display for KeyValuePrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0
.visit(&mut KeyValueVisitor(true, f))
.map_err(|_| fmt::Error)
}
}

struct KeyValueVisitor<'a, 'b>(bool, &'a mut fmt::Formatter<'b>);

impl<'a, 'b, 'kvs> kv::Visitor<'kvs> for KeyValueVisitor<'a, 'b> {
fn visit_pair(&mut self, key: kv::Key<'kvs>, value: kv::Value<'kvs>) -> Result<(), kv::Error> {
self.1
.write_str(if self.0 { ": " } else { ", " })
.and_then(|()| {
self.0 = false;
write!(self.1, "{}={}", key, value)
})
.map_err(Into::into)
}
}

/// The actual logging of a record.
fn log(record: &Record) {
// Thread local buffer for logging. This way we only lock standard out/error
Expand Down Expand Up @@ -401,17 +430,24 @@ fn log(record: &Record) {

match record.target() {
REQUEST_TARGET => {
writeln!(&mut buffer, "[REQUEST]: {}", record.args()).unwrap_or_else(log_failure);
writeln!(
&mut buffer,
"[REQUEST]: {}{}",
record.args(),
KeyValuePrinter(record.key_values())
)
.unwrap_or_else(log_failure);

write_once(stdout(), &buffer).unwrap_or_else(log_failure);
}
target => {
writeln!(
&mut buffer,
"[{}] {}: {}",
"[{}] {}: {}{}",
record.level(),
target,
record.args()
record.args(),
KeyValuePrinter(record.key_values())
)
.unwrap_or_else(log_failure);

Expand Down

0 comments on commit d7c9e34

Please sign in to comment.