mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-10-02 15:24:47 +00:00

Currently there is no good way to get timestamped log entries with the `tracing-fmt` subscriber. Even with `full`, timestamps are not included. This is surprising to those coming from other logging crates. It is also inconvenient to work around, as you would either need to add a custom field to _all_ spans and events across all your crates, or write your own subscriber which sort of reduces the usefulness of `fmt`. This patch introduces a trait for event formatters (`FormatEvent`) so that it is easier to write more complex formatters (previously they were just `Fn(...) -> fmt::Result` which would require boxing a closure to produce). It then implements this new trait for `default::Format`, which is the new default formatter. It implements "compact"/full formatting exactly like before, except that is also generic over a timer. The timer must implement `default::FormatTime`, which has a single method that writes the current time out to a `fmt::Write`. This method is implemented for a two new unit structs `SystemTime` and `Uptime`. `SystemTime` is pretty-printed using `chrono` with the new default feature `chrono`, and `Debug` of `std::net::SystemTime` is used if the feature is turned off. `Uptime` prints the fractional number of seconds since an epoch. Users can also provide `()` as the time formatter to not produce timestamps in logged entries (accessible through `default::Format::without_time`). This patch also switches the default output to the verbose format, with `compact` offered to return to the compact output format. Closes #94.
157 lines
5.7 KiB
Rust
157 lines
5.7 KiB
Rust
extern crate futures;
|
|
extern crate hyper;
|
|
#[macro_use]
|
|
extern crate tracing;
|
|
extern crate tokio;
|
|
extern crate tracing_env_logger;
|
|
extern crate tracing_fmt;
|
|
extern crate tracing_futures;
|
|
|
|
use futures::future;
|
|
use hyper::rt::{Future, Stream};
|
|
use hyper::server::conn::Http;
|
|
use hyper::service::service_fn;
|
|
use hyper::{Body, Method, Request, Response, StatusCode};
|
|
|
|
use std::str;
|
|
|
|
use tracing::{field, Level};
|
|
use tracing_futures::{Instrument, Instrumented};
|
|
|
|
type BoxFut = Box<dyn Future<Item = Response<Body>, Error = hyper::Error> + Send>;
|
|
|
|
fn echo(req: Request<Body>) -> Instrumented<BoxFut> {
|
|
span!(
|
|
Level::TRACE,
|
|
"request",
|
|
method = &field::debug(req.method()),
|
|
uri = &field::debug(req.uri()),
|
|
headers = &field::debug(req.headers())
|
|
)
|
|
.enter(|| {
|
|
info!("received request");
|
|
let mut response = Response::new(Body::empty());
|
|
|
|
let (rsp_span, fut): (_, BoxFut) = match (req.method(), req.uri().path()) {
|
|
// Serve some instructions at /
|
|
(&Method::GET, "/") => {
|
|
const BODY: &'static str = "Try POSTing data to /echo";
|
|
*response.body_mut() = Body::from(BODY);
|
|
(
|
|
span!(Level::TRACE, "response", body = &field::display(&BODY)),
|
|
Box::new(future::ok(response)),
|
|
)
|
|
}
|
|
|
|
// Simply echo the body back to the client.
|
|
(&Method::POST, "/echo") => {
|
|
let body = req.into_body();
|
|
let span = span!(Level::TRACE, "response", response_kind = &"echo");
|
|
*response.body_mut() = body;
|
|
(span, Box::new(future::ok(response)))
|
|
}
|
|
|
|
// Convert to uppercase before sending back to client.
|
|
(&Method::POST, "/echo/uppercase") => {
|
|
let mapping = req.into_body().map(|chunk| {
|
|
let upper = chunk
|
|
.iter()
|
|
.map(|byte| byte.to_ascii_uppercase())
|
|
.collect::<Vec<u8>>();
|
|
debug!(
|
|
{
|
|
chunk = field::debug(str::from_utf8(&chunk[..])),
|
|
uppercased = field::debug(str::from_utf8(&upper[..]))
|
|
},
|
|
"uppercased request body"
|
|
);
|
|
upper
|
|
});
|
|
|
|
*response.body_mut() = Body::wrap_stream(mapping);
|
|
(
|
|
span!(Level::TRACE, "response", response_kind = "uppercase"),
|
|
Box::new(future::ok(response)),
|
|
)
|
|
}
|
|
|
|
// Reverse the entire body before sending back to the client.
|
|
//
|
|
// Since we don't know the end yet, we can't simply stream
|
|
// the chunks as they arrive. So, this returns a different
|
|
// future, waiting on concatenating the full body, so that
|
|
// it can be reversed. Only then can we return a `Response`.
|
|
(&Method::POST, "/echo/reversed") => {
|
|
let span = span!(Level::TRACE, "response", response_kind = "reversed");
|
|
let reversed = span.enter(|| {
|
|
req.into_body().concat2().map(move |chunk| {
|
|
let body = chunk.iter().rev().cloned().collect::<Vec<u8>>();
|
|
debug!(
|
|
{
|
|
chunk = field::debug(str::from_utf8(&chunk[..])),
|
|
body = field::debug(str::from_utf8(&body[..]))
|
|
},
|
|
"reversed request body");
|
|
*response.body_mut() = Body::from(body);
|
|
response
|
|
})
|
|
});
|
|
(span, Box::new(reversed))
|
|
}
|
|
|
|
// The 404 Not Found route...
|
|
_ => {
|
|
*response.status_mut() = StatusCode::NOT_FOUND;
|
|
(
|
|
span!(
|
|
Level::TRACE,
|
|
"response",
|
|
body = &field::debug(()),
|
|
status = &field::debug(&StatusCode::NOT_FOUND)
|
|
),
|
|
Box::new(future::ok(response)),
|
|
)
|
|
}
|
|
};
|
|
|
|
fut.instrument(rsp_span)
|
|
})
|
|
}
|
|
|
|
fn main() {
|
|
let subscriber = tracing_fmt::FmtSubscriber::builder().finish();
|
|
tracing_env_logger::try_init().expect("init log adapter");
|
|
|
|
tracing::subscriber::with_default(subscriber, || {
|
|
let addr: ::std::net::SocketAddr = ([127, 0, 0, 1], 3000).into();
|
|
let server_span = span!(Level::TRACE, "server", local = &field::debug(addr));
|
|
let server = tokio::net::TcpListener::bind(&addr)
|
|
.expect("bind")
|
|
.incoming()
|
|
.fold(Http::new(), move |http, sock| {
|
|
let span = span!(
|
|
Level::TRACE,
|
|
"connection",
|
|
remote = &field::debug(&sock.peer_addr().unwrap())
|
|
);
|
|
hyper::rt::spawn(
|
|
http.serve_connection(sock, service_fn(echo))
|
|
.map_err(|e| {
|
|
error!({ error = field::display(e) }, "serve error");
|
|
})
|
|
.instrument(span),
|
|
);
|
|
Ok::<_, ::std::io::Error>(http)
|
|
})
|
|
.map(|_| ())
|
|
.map_err(|e| {
|
|
error!({ error = field::display(e) }, "server error");
|
|
})
|
|
.instrument(server_span.clone());
|
|
server_span.enter(|| {
|
|
info!("listening...");
|
|
hyper::rt::run(server);
|
|
});
|
|
})
|
|
}
|