Skip to content

Commit

Permalink
Merge pull request #100 from tomprince/stop-allocating
Browse files Browse the repository at this point in the history
Stop allocating so much while formatting.
  • Loading branch information
untitaker authored Jan 15, 2017
2 parents e6ea475 + c9401eb commit 5a76b50
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 19 deletions.
4 changes: 2 additions & 2 deletions examples/formatstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extern crate env_logger;
use iron::prelude::*;

use logger::Logger;
use logger::format::Format;
use logger::Format;

static FORMAT: &'static str =
"Uri: {uri}, Method: {method}, Status: {status}, Duration: {response-time}, Time: {request-time}";
Expand All @@ -19,7 +19,7 @@ fn main() {
let mut chain = Chain::new(no_op_handler);
let format = Format::new(FORMAT);
chain.link(Logger::new(Some(format.unwrap())));

println!("Run `RUST_LOG=info cargo run --example formatstring` to see logs.");
match Iron::new(chain).http("127.0.0.1:3000") {
Result::Ok(listening) => println!("{:?}", listening),
Expand Down
42 changes: 41 additions & 1 deletion src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
use std::default::Default;
use std::str::Chars;
use std::iter::Peekable;
use std::fmt::Formatter;

use self::FormatText::{Method, URI, Status, ResponseTime, RemoteAddr, RequestTime};

/// A formatting style for the `Logger`, consisting of multiple
/// `FormatUnit`s concatenated into one line.
#[derive(Clone)]
pub struct Format(pub Vec<FormatUnit>);
pub struct Format(Vec<FormatUnit>);

impl Default for Format {
/// Return the default formatting style for the `Logger`:
Expand Down Expand Up @@ -46,6 +47,28 @@ impl Format {
}
}

pub trait ContextDisplay<'a> {
type Item;
type Display: fmt::Display;
fn display_with(&'a self,
render: &'a Fn(&mut Formatter, &Self::Item) -> Result<(), fmt::Error>)
-> Self::Display;
}


impl<'a> ContextDisplay<'a> for Format {
type Item = FormatText;
type Display = FormatDisplay<'a>;
fn display_with(&'a self,
render: &'a Fn(&mut Formatter, &FormatText) -> Result<(), fmt::Error>)
-> FormatDisplay<'a> {
FormatDisplay {
format: self,
render: render,
}
}
}

struct FormatParser<'a> {
// The characters of the format string.
chars: Peekable<Chars<'a>>,
Expand Down Expand Up @@ -164,3 +187,20 @@ pub enum FormatText {
pub struct FormatUnit {
pub text: FormatText,
}


pub struct FormatDisplay<'a> {
format: &'a Format,
render: &'a Fn(&mut Formatter, &FormatText) -> Result<(), fmt::Error>,
}

use std::fmt;
impl<'a> fmt::Display for FormatDisplay<'a> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
let Format(ref format) = *self.format;
for unit in format {
(self.render)(fmt, &unit.text)?;
}
Ok(())
}
}
41 changes: 25 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ use iron::{AfterMiddleware, BeforeMiddleware, IronResult, IronError, Request, Re
use iron::typemap::Key;

use format::FormatText::{Str, Method, URI, Status, ResponseTime, RemoteAddr, RequestTime};
use format::{Format, FormatText};
use format::{ContextDisplay, FormatText};

pub mod format;
use std::fmt::{Display, Formatter};

mod format;
pub use format::Format;

/// `Middleware` for logging request and response info to the terminal.
pub struct Logger {
format: Option<Format>
format: Format,
}

impl Logger {
Expand All @@ -37,6 +40,7 @@ impl Logger {
/// chain.link_after(logger_after);
/// ```
pub fn new(format: Option<Format>) -> (Logger, Logger) {
let format = format.unwrap_or_default();
(Logger { format: format.clone() }, Logger { format: format })
}
}
Expand All @@ -54,25 +58,30 @@ impl Logger {

let response_time = time::now() - entry_time;
let response_time_ms = (response_time.num_seconds() * 1000) as f64 + (response_time.num_nanoseconds().unwrap_or(0) as f64) / 1000000.0;
let Format(format) = self.format.clone().unwrap_or_default();

{
let render = |text: &FormatText| {
let render = |fmt: &mut Formatter, text: &FormatText| {
match *text {
Str(ref string) => string.clone(),
Method => format!("{}", req.method),
URI => format!("{}", req.url),
Status => res.status
.map(|status| format!("{}", status))
.unwrap_or("<missing status code>".to_owned()),
ResponseTime => format!("{} ms", response_time_ms),
RemoteAddr => format!("{}", req.remote_addr),
RequestTime => format!("{}", entry_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ%z").unwrap()),
Str(ref string) => fmt.write_str(string),
Method => req.method.fmt(fmt),
URI => req.url.fmt(fmt),
Status => {
match res.status {
Some(status) => status.fmt(fmt),
None => fmt.write_str("<missing status code>"),
}
}
ResponseTime => fmt.write_fmt(format_args!("{} ms", response_time_ms)),
RemoteAddr => req.remote_addr.fmt(fmt),
RequestTime => {
entry_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ%z")
.unwrap()
.fmt(fmt)
}
}
};

let lg = format.iter().map(|unit| render(&unit.text)).collect::<Vec<String>>().join("");
info!("{}", lg);
info!("{}", self.format.display_with(&render));
}

Ok(())
Expand Down

0 comments on commit 5a76b50

Please sign in to comment.