Skip to content

Commit cfb7c6a

Browse files
committed
Add log_request_and_response. Add Request::id field.
1 parent eedb69a commit cfb7c6a

File tree

10 files changed

+76
-31
lines changed

10 files changed

+76
-31
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ include_dir = { version = "0.7", optional = true }
2424
#libflate = "1"
2525
once_cell = "1"
2626
permit = "^0.2.1"
27+
rand = { version = "^0.8.5", features = ["small_rng"] }
2728
safe-regex = "^0.2.5"
2829
safina-executor = { version = "^0.3.3" }
2930
safina-sync = { version = "^0.2.4" }

examples/events-sse.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
//! ```
2424
#![forbid(unsafe_code)]
2525
use permit::Permit;
26-
use servlin::log::log_response;
26+
use servlin::log::log_request_and_response;
2727
use servlin::reexport::{safina_executor, safina_timer};
2828
use servlin::{
2929
socket_addr_127_0_0_1, Error, Event, EventSender, HttpServerBuilder, Request, Response,
@@ -62,14 +62,14 @@ fn event_sender_thread(state: Arc<State>, permit: Permit) {
6262
}
6363

6464
#[allow(clippy::unnecessary_wraps)]
65-
fn subscribe(state: &Arc<State>, _req: &Request) -> Result<Response, Error> {
65+
fn subscribe(state: Arc<State>, _req: Request) -> Result<Response, Error> {
6666
let (sender, response) = Response::event_stream();
6767
state.subscribers.lock().unwrap().push(sender);
6868
Ok(response)
6969
}
7070

7171
#[allow(clippy::unnecessary_wraps)]
72-
fn handle_req(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
72+
fn handle_req(state: Arc<State>, req: Request) -> Result<Response, Error> {
7373
match (req.method(), req.url().path()) {
7474
("GET", "/health") => Ok(Response::text(200, "ok")),
7575
("GET", "/subscribe") => subscribe(state, req),
@@ -85,7 +85,8 @@ pub fn main() {
8585
std::thread::spawn(move || event_sender_thread(state_clone, event_sender_thread_permit));
8686
safina_timer::start_timer_thread();
8787
let executor = safina_executor::Executor::default();
88-
let request_handler = move |req: Request| log_response(&req, handle_req(&state, &req)).unwrap();
88+
let request_handler =
89+
move |req: Request| log_request_and_response(req, |req| handle_req(state, req));
8990
executor
9091
.block_on(
9192
HttpServerBuilder::new()

examples/html_form.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
//! <http://127.0.0.1:8000/>
2323
#![forbid(unsafe_code)]
2424
use serde::Deserialize;
25-
use servlin::log::log_response;
25+
use servlin::log::log_request_and_response;
2626
use servlin::reexport::{safina_executor, safina_timer};
2727
use servlin::{socket_addr_127_0_0_1, Error, HttpServerBuilder, Request, Response};
2828
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -51,7 +51,7 @@ impl State {
5151
}
5252
}
5353

54-
fn index(state: &Arc<State>) -> Response {
54+
fn index(state: Arc<State>) -> Response {
5555
Response::html(
5656
200,
5757
format!(
@@ -74,12 +74,12 @@ fn index(state: &Arc<State>) -> Response {
7474
)
7575
}
7676

77-
fn increment(state: &Arc<State>) -> Response {
77+
fn increment(state: Arc<State>) -> Response {
7878
state.increment();
7979
Response::redirect_303("/")
8080
}
8181

82-
fn add(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
82+
fn add(state: Arc<State>, req: Request) -> Result<Response, Error> {
8383
#[derive(Deserialize)]
8484
struct Input {
8585
num: usize,
@@ -94,7 +94,7 @@ fn add(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
9494
Ok(Response::redirect_303("/"))
9595
}
9696

97-
fn handle_req(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
97+
fn handle_req(state: Arc<State>, req: Request) -> Result<Response, Error> {
9898
match (req.method(), req.url().path()) {
9999
("GET", "/health") => Ok(Response::text(200, "ok")),
100100
("GET", "/") => Ok(index(state)),
@@ -109,7 +109,8 @@ pub fn main() {
109109
safina_timer::start_timer_thread();
110110
let executor = safina_executor::Executor::default();
111111
let state = Arc::new(State::new());
112-
let request_handler = move |req: Request| log_response(&req, handle_req(&state, &req)).unwrap();
112+
let request_handler =
113+
move |req: Request| log_request_and_response(req, |req| handle_req(state, req));
113114
executor
114115
.block_on(
115116
HttpServerBuilder::new()

examples/http-put.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
//! Upload received, body_len=5, upload_count=2
2323
//! ```
2424
#![forbid(unsafe_code)]
25-
use servlin::log::{log_response, set_global_logger, LogFileWriter};
25+
use servlin::log::{log_request_and_response, set_global_logger, LogFileWriter};
2626
use servlin::reexport::{safina_executor, safina_timer};
2727
use servlin::{socket_addr_127_0_0_1, Error, HttpServerBuilder, Request, Response};
2828
use std::io::Read;
@@ -43,7 +43,7 @@ impl State {
4343
}
4444
}
4545

46-
fn put(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
46+
fn put(state: Arc<State>, req: Request) -> Result<Response, Error> {
4747
if req.body.is_pending() {
4848
return Ok(Response::get_body_and_reprocess(1024 * 1024));
4949
}
@@ -59,7 +59,7 @@ fn put(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
5959
))
6060
}
6161

62-
fn handle_req(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
62+
fn handle_req(state: Arc<State>, req: Request) -> Result<Response, Error> {
6363
match (req.method(), req.url().path()) {
6464
("GET", "/health") => Ok(Response::text(200, "ok")),
6565
("PUT", "/upload") => put(state, req),
@@ -80,7 +80,8 @@ pub fn main() {
8080
let executor = safina_executor::Executor::default();
8181
let cache_dir = TempDir::new().unwrap();
8282
let state = Arc::new(State::new());
83-
let request_handler = move |req: Request| log_response(&req, handle_req(&state, &req)).unwrap();
83+
let request_handler =
84+
move |req: Request| log_request_and_response(req, |req| handle_req(state, req));
8485
executor
8586
.block_on(
8687
HttpServerBuilder::new()

examples/json_api.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
#![forbid(unsafe_code)]
3838
use serde::Deserialize;
3939
use serde_json::json;
40-
use servlin::log::log_response;
40+
use servlin::log::log_request_and_response;
4141
use servlin::reexport::{safina_executor, safina_timer};
4242
use servlin::{socket_addr_127_0_0_1, Error, HttpServerBuilder, Request, Response};
4343
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -66,16 +66,16 @@ impl State {
6666
}
6767
}
6868

69-
fn get_count(state: &Arc<State>) -> Response {
69+
fn get_count(state: Arc<State>) -> Response {
7070
Response::json(200, json!({ "count": state.get() })).unwrap()
7171
}
7272

73-
fn increment(state: &Arc<State>) -> Response {
73+
fn increment(state: Arc<State>) -> Response {
7474
state.increment();
7575
Response::json(200, json!({ "count": state.get() })).unwrap()
7676
}
7777

78-
fn add(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
78+
fn add(state: Arc<State>, req: Request) -> Result<Response, Error> {
7979
#[derive(Deserialize)]
8080
struct Input {
8181
num: usize,
@@ -90,7 +90,7 @@ fn add(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
9090
Ok(Response::json(200, json!({ "count": state.get() })).unwrap())
9191
}
9292

93-
fn handle_req(state: &Arc<State>, req: &Request) -> Result<Response, Error> {
93+
fn handle_req(state: Arc<State>, req: Request) -> Result<Response, Error> {
9494
match (req.method(), req.url().path()) {
9595
("GET", "/health") => Ok(Response::text(200, "ok")),
9696
("GET", "/get") => Ok(get_count(state)),
@@ -105,7 +105,8 @@ pub fn main() {
105105
safina_timer::start_timer_thread();
106106
let executor = safina_executor::Executor::default();
107107
let state = Arc::new(State::new());
108-
let request_handler = move |req: Request| log_response(&req, handle_req(&state, &req)).unwrap();
108+
let request_handler =
109+
move |req: Request| log_request_and_response(req, |req| handle_req(state, req));
109110
executor
110111
.block_on(
111112
HttpServerBuilder::new()

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ mod headers;
138138
mod http_conn;
139139
mod http_error;
140140
pub mod log;
141+
mod rand;
141142
mod request;
142143
mod request_body;
143144
mod response;

src/log/logger.rs

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::log::tag::Tag;
33
use crate::log::tag_list::TagList;
44
use crate::log::tag_value::TagValue;
55
use crate::log::Level;
6+
use crate::Request;
67
use std::cell::RefCell;
78
use std::io::Write;
89
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
@@ -139,6 +140,12 @@ pub fn with_thread_local_log_tags<R, F: FnOnce(&[Tag]) -> R>(f: F) -> R {
139140
THREAD_LOCAL_TAGS.with(|cell| f(cell.borrow().as_slice()))
140141
}
141142

143+
pub fn add_thread_local_log_tags_from_request(req: &Request) {
144+
add_thread_local_log_tag("http_method", req.method());
145+
add_thread_local_log_tag("path", req.url().path());
146+
add_thread_local_log_tag("request_id", req.id);
147+
}
148+
142149
#[allow(clippy::module_name_repetitions)]
143150
#[derive(Debug)]
144151
pub struct LoggerStoppedError {}

src/log/mod.rs

+29-11
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ pub use log_file_writer::LogFileWriter;
1212
use logger::log;
1313
pub use logger::set_global_logger;
1414
pub use logger::{
15-
add_thread_local_log_tag, clear_thread_local_log_tags, with_thread_local_log_tags,
16-
LoggerStoppedError,
15+
add_thread_local_log_tag, add_thread_local_log_tags_from_request, clear_thread_local_log_tags,
16+
with_thread_local_log_tags, LoggerStoppedError,
1717
};
1818
use std::fmt::{Display, Formatter};
1919
use std::time::SystemTime;
@@ -83,17 +83,11 @@ pub fn debug(msg: impl Into<String>, tags: impl Into<TagList>) -> Result<(), Log
8383
///
8484
/// # Errors
8585
/// Returns `Err` when the global logger has stopped.
86-
#[allow(clippy::needless_pass_by_value)]
8786
#[allow(clippy::module_name_repetitions)]
88-
pub fn log_response(
89-
req: &Request,
90-
result: Result<Response, Error>,
91-
) -> Result<Response, LoggerStoppedError> {
87+
pub fn log_response(result: Result<Response, Error>) -> Result<Response, LoggerStoppedError> {
9288
match result {
9389
Ok(response) => {
9490
let mut tags = Vec::new();
95-
tags.push(Tag::new("http_method", req.method()));
96-
tags.push(Tag::new("path", req.url().path()));
9791
tags.push(Tag::new("code", response.code));
9892
if let Some(body_len) = response.body.len() {
9993
tags.push(Tag::new("body_len", body_len));
@@ -112,8 +106,6 @@ pub fn log_response(
112106
if let Some(backtrace) = e.backtrace {
113107
tags.push(Tag::new("msg", format!("{backtrace:?}")));
114108
}
115-
tags.push(Tag::new("http_method", req.method()));
116-
tags.push(Tag::new("path", req.url().path()));
117109
tags.push(Tag::new("code", response.code));
118110
if let Some(body_len) = response.body.len() {
119111
tags.push(Tag::new("body_len", body_len));
@@ -123,3 +115,29 @@ pub fn log_response(
123115
}
124116
}
125117
}
118+
119+
/// Adds thread-local log tags from the request and then calls the handler `f`.
120+
/// When `f` does logging, the log messages will include the request id, HTTP method, and path.
121+
/// When `f` returns, this function makes a new log event for the result
122+
/// and sends it to the global logger.
123+
///
124+
/// When the result of `f` is an [`Error`] without a response,
125+
/// this function uses [`Response::internal_server_errror_500`] to make one.
126+
///
127+
/// Returns the response.
128+
///
129+
/// Clears thread-local log tags.
130+
///
131+
/// # Errors
132+
/// Returns `Err` when the global logger has stopped.
133+
#[allow(clippy::module_name_repetitions)]
134+
pub fn log_request_and_response<F: FnOnce(Request) -> Result<Response, Error>>(
135+
req: Request,
136+
f: F,
137+
) -> Response {
138+
clear_thread_local_log_tags();
139+
add_thread_local_log_tags_from_request(&req);
140+
let response = log_response(f(req)).unwrap();
141+
clear_thread_local_log_tags();
142+
response
143+
}

src/rand.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use rand::rngs::SmallRng;
2+
use rand::{RngCore, SeedableRng};
3+
use std::cell::RefCell;
4+
5+
thread_local! {
6+
pub static THREAD_LOCAL_SMALL_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_entropy());
7+
}
8+
9+
pub fn next_insecure_rand_u64() -> u64 {
10+
THREAD_LOCAL_SMALL_RNG.with(|cell| cell.borrow_mut().next_u64())
11+
}

src/request.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::head::read_http_head;
22
use crate::http_error::HttpError;
3+
use crate::rand::next_insecure_rand_u64;
34
use crate::{AsciiString, ContentType, HeaderList, RequestBody, Response};
45
use fixed_buffer::FixedBuf;
56
use futures_io::AsyncRead;
@@ -10,6 +11,7 @@ use url::Url;
1011

1112
#[derive(Clone, Eq, PartialEq)]
1213
pub struct Request {
14+
pub id: u64,
1315
pub remote_addr: SocketAddr,
1416
pub method: String,
1517
pub url: Url,
@@ -240,6 +242,7 @@ pub async fn read_http_request<const BUF_SIZE: usize>(
240242
(false, None, _) => RequestBody::empty(),
241243
};
242244
Ok(Request {
245+
id: next_insecure_rand_u64(),
243246
remote_addr,
244247
method: head.method,
245248
url: head.url,

0 commit comments

Comments
 (0)