Skip to content

Commit

Permalink
Replace chrono dependency by time (#578)
Browse files Browse the repository at this point in the history
[chrono](https://crates.io/crates/chrono) has not releases for very long time.
Also, it has security error: https://rustsec.org/advisories/RUSTSEC-2020-0159.html

Also, this would reduce dependencies because chrono uses very old time version so many binaries which use gotham ends with 2 versions of time linked.

Co-authored-by: msrd0 <msrd0@users.noreply.github.com>
  • Loading branch information
AngelicosPhosphoros and msrd0 committed Nov 10, 2021
1 parent 097822e commit ca0f906
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 32 deletions.
2 changes: 1 addition & 1 deletion gotham/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ anyhow = "1.0"
base64 = "0.13"
bincode = { version = "1.0", optional = true }
bytes = "1.0"
chrono = { version = "0.4", features = ["clock"], default-features = false }
cookie = "0.15"
futures-util = "0.3.14"
httpdate = "1.0"
Expand All @@ -50,6 +49,7 @@ rand_chacha = "0.3"
regex = "1.0"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
time = {version = "0.3.4", default-features = false, features = ["std", "formatting", "macros"]}
tokio = { version = "1.11.0", features = ["net", "rt-multi-thread", "time", "fs", "io-util"] }
tokio-rustls = { version = "0.23", optional = true }
uuid = { version = "0.8", features = ["v4"] }
Expand Down
82 changes: 52 additions & 30 deletions gotham/src/helpers/timing.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,85 @@
//! Defines types for timing requests and emitting timing information.
use chrono::prelude::*;
use std::fmt::{self, Display, Formatter};
use std::time::{Duration, Instant};

use time::OffsetDateTime;

/// Timer struct used to record execution times of requests.
///
/// The `elapsed` function returns the elapsed time in an easy to format way,
/// suitable for use with requset logging middlewares.
#[derive(Clone, Copy)]
pub(crate) struct Timer {
start: DateTime<Utc>,
// We use 2 start fields
// because we want formattable time to print start time
// but we cannot use it to calculate duration because it is not monotonic.
//
// It is possible that we spent a lot of time between initialization of fields,
// for example, if current thread unscheduled by OS but it should be very rare.
// On the other hand, adjusting system clock by NTP is much more possible.
start_monotonic: Instant,
start_formattable: OffsetDateTime,
}

impl Timer {
/// Begins measuring from the current time.
pub(crate) fn new() -> Timer {
Timer { start: Utc::now() }
Timer {
start_monotonic: Instant::now(),
start_formattable: OffsetDateTime::now_utc(),
}
}

/// Finishes measuring, and returns the elapsed time as a `Timing` value.
pub(crate) fn elapsed(&self) -> Timing {
let duration = Utc::now()
.signed_duration_since(self.start)
.num_microseconds();

match duration {
Some(dur) => Timing::Microseconds(dur),
None => Timing::Invalid,
}
let duration = self.start_monotonic.elapsed();
Timing(duration)
}

/// Retrieves the start time of this timer.
pub(crate) fn start_time(&self) -> &DateTime<Utc> {
&self.start
pub(crate) fn start_time(&self) -> &OffsetDateTime {
&self.start_formattable
}
}

/// Represents an elapsed time measured by `Timer`.
#[derive(Clone, Copy)]
pub(crate) enum Timing {
/// A number of microseconds measured by `Timer`.
Microseconds(i64),

/// An invalid state, where the amount of time elapsed was unable to be measured.
Invalid,
}
pub(crate) struct Timing(Duration);

impl Display for Timing {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
Timing::Microseconds(i) => {
if i < 1000 {
write!(f, "{}µs", i)
} else if i < 1_000_000 {
write!(f, "{:.2}ms", (i as f32) / 1000.0)
} else {
write!(f, "{:.2}s", (i as f32) / 1_000_000.0)
}
let duration = self.0;
match duration.as_micros() {
i if i < 1000 => {
write!(f, "{}µs", i)
}
i if i < 1_000_000 => {
write!(f, "{:.2}ms", (i as f64) / 1000.0)
}
_ => {
write!(f, "{:.2}s", duration.as_secs_f32())
}
Timing::Invalid => f.write_str("invalid"),
}
}
}

#[cfg(test)]
mod tests {
use std::time::Duration;

use super::Timing;

#[test]
fn test_durations() {
let microsecond = Duration::from_micros(1);

let t0 = Timing(microsecond * 555);
assert_eq!(t0.to_string(), "555µs");

let t1 = Timing(microsecond * 666_444);
assert_eq!(t1.to_string(), "666.44ms");

let t2 = Timing(microsecond * 777_444_333);
assert_eq!(t2.to_string(), "777.44s");
}
}
10 changes: 9 additions & 1 deletion gotham/src/middleware/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,16 @@ impl Middleware for RequestLogger {

// hook onto the end of the request to log the access
let f = chain(state).and_then(move |(state, response)| {

// format the start time to the CLF formats
let datetime = timer.start_time().format("%d/%b/%Y:%H:%M:%S %z");
let datetime = {
use time::format_description::FormatItem;
use time::macros::format_description;
const DT_FORMAT: &[FormatItem<'static>]
= format_description!("[day]/[month repr:short]/[year]:[hour repr:24]:[minute]:[second] [offset_hour][offset_minute]");

timer.start_time().format(&DT_FORMAT).expect("Failed to format time")
};

// grab the ip address from the state
let ip = client_addr(&state).unwrap().ip();
Expand Down

0 comments on commit ca0f906

Please sign in to comment.