@@ -5,7 +5,6 @@ use axum_client_ip::InsecureClientIp;
5
5
use log:: * ;
6
6
use serde:: { Deserialize , Serialize } ;
7
7
use serde_json:: json;
8
- use std:: collections:: BTreeMap ;
9
8
use std:: time:: Duration ;
10
9
use tracing:: field:: Field ;
11
10
use tracing:: span:: { Attributes , Id , Record , Span } ;
@@ -27,15 +26,16 @@ use crate::AppState;
27
26
// * We have a period task to wipe logs past their retention.
28
27
//
29
28
#[ repr( i32 ) ]
30
- #[ derive( Debug , Clone , Serialize , Deserialize ) ]
29
+ #[ derive( Debug , Clone , Deserialize , Serialize ) ]
31
30
enum LogType {
32
31
Undefined = 0 ,
33
32
AdminRequest = 1 ,
34
33
HttpRequest = 2 ,
35
34
RecordApiRequest = 3 ,
36
35
}
37
36
38
- #[ derive( Debug , Clone , Serialize , Deserialize ) ]
37
+ /// DB schema representation.
38
+ #[ derive( Debug , Clone , Deserialize , Serialize ) ]
39
39
pub ( crate ) struct Log {
40
40
pub id : Option < [ u8 ; 16 ] > ,
41
41
pub created : Option < f64 > ,
@@ -82,14 +82,6 @@ pub(super) fn sqlite_logger_on_request(_req: &Request<Body>, _span: &Span) {
82
82
// request related information into the span
83
83
}
84
84
85
- #[ inline]
86
- fn as_millis_f64 ( d : & Duration ) -> f64 {
87
- const NANOS_PER_MILLI : f64 = 1_000_000.0 ;
88
- const MILLIS_PER_SEC : u64 = 1_000 ;
89
- return ( d. as_secs ( ) as f64 ) * ( MILLIS_PER_SEC as f64 )
90
- + ( d. subsec_nanos ( ) as f64 ) / ( NANOS_PER_MILLI ) ;
91
- }
92
-
93
85
pub ( super ) fn sqlite_logger_on_response (
94
86
response : & Response < Body > ,
95
87
latency : Duration ,
@@ -121,7 +113,7 @@ impl SqliteLogLayer {
121
113
//
122
114
// TODO: should we use a bounded receiver to create back-pressure?
123
115
// TODO: We could use recv_many() and batch insert.
124
- fn write_log ( & self , log : Log ) -> Result < ( ) , trailbase_sqlite:: Error > {
116
+ fn write_log ( & self , log : LogFieldStorage ) -> Result < ( ) , trailbase_sqlite:: Error > {
125
117
return self . conn . call_and_forget ( move |conn| {
126
118
let mut stmt = match conn. prepare_cached (
127
119
r#"
@@ -139,24 +131,23 @@ impl SqliteLogLayer {
139
131
} ;
140
132
141
133
if let Err ( err) = stmt. execute ( rusqlite:: params!(
142
- log. r#type,
134
+ // "type": FIXME: should be: admin, records, auth, other request
135
+ LogType :: HttpRequest as i32 ,
143
136
log. level,
144
137
log. status,
145
138
log. method,
146
- log. url ,
147
- log. latency ,
139
+ log. uri ,
140
+ log. latency_ms ,
148
141
log. client_ip,
149
142
log. referer,
150
- log. user_agent
143
+ log. user_agent // FIXME: we're not writing the JSON data.
151
144
) ) {
152
145
warn ! ( "logs writing failed: {err}" ) ;
153
146
}
154
147
} ) ;
155
148
}
156
149
}
157
150
158
- const BUG_TEXT : & str = "Span not found, this is a bug" ;
159
-
160
151
impl < S > Layer < S > for SqliteLogLayer
161
152
where
162
153
S : tracing:: Subscriber ,
@@ -176,60 +167,27 @@ where
176
167
let mut extensions = span. extensions_mut ( ) ;
177
168
if let Some ( storage) = extensions. get_mut :: < LogFieldStorage > ( ) {
178
169
values. record ( & mut LogJsonVisitor ( storage) ) ;
170
+ } else {
171
+ info ! ( "logs already consumed" ) ;
179
172
}
180
173
}
181
174
182
175
fn on_event ( & self , event : & tracing:: Event < ' _ > , ctx : Context < ' _ , S > ) {
183
- let Some ( span) = ctx. event_span ( event) else {
184
- return ;
185
- } ;
176
+ let span = ctx. event_span ( event) . expect ( BUG_TEXT ) ;
186
177
187
178
let mut extensions = span. extensions_mut ( ) ;
188
- if let Some ( s) = extensions. get_mut :: < LogFieldStorage > ( ) {
189
- let mut storage = std:: mem:: take ( s) ;
190
-
179
+ if let Some ( mut storage) = extensions. remove :: < LogFieldStorage > ( ) {
191
180
event. record ( & mut LogJsonVisitor ( & mut storage) ) ;
192
181
193
- let log = Log {
194
- id : None ,
195
- created : None ,
196
- // FIXME: Is it a admin/records/auth,plain http request...?
197
- // Or should this even be here? Couldn't we just infer client-side by prefix?
198
- r#type : LogType :: HttpRequest as i32 ,
199
- level : level_to_int ( event. metadata ( ) . level ( ) ) ,
200
- status : storage. status as u16 ,
201
- method : storage. method ,
202
- url : storage. uri ,
203
- latency : storage. latency_ms ,
204
- client_ip : storage. client_ip ,
205
- referer : storage. referer ,
206
- user_agent : storage. user_agent ,
207
- data : {
208
- if storage. fields . is_empty ( ) {
209
- None
210
- } else {
211
- Some ( json ! ( storage. fields) )
212
- }
213
- } ,
214
- } ;
182
+ storage. level = level_to_int ( event. metadata ( ) . level ( ) ) ;
215
183
216
- if let Err ( err) = self . write_log ( log ) {
184
+ if let Err ( err) = self . write_log ( storage ) {
217
185
warn ! ( "Failed to send to logs to writer: {err}" ) ;
218
186
}
219
187
}
220
188
}
221
189
}
222
190
223
- fn level_to_int ( level : & tracing:: Level ) -> i32 {
224
- match * level {
225
- tracing:: Level :: TRACE => 4 ,
226
- tracing:: Level :: DEBUG => 3 ,
227
- tracing:: Level :: INFO => 2 ,
228
- tracing:: Level :: WARN => 1 ,
229
- tracing:: Level :: ERROR => 0 ,
230
- }
231
- }
232
-
233
191
#[ derive( Debug , Default , Clone ) ]
234
192
struct LogFieldStorage {
235
193
// Request fields/properties.
@@ -241,13 +199,16 @@ struct LogFieldStorage {
241
199
user_agent : String ,
242
200
version : String ,
243
201
202
+ // Log level.
203
+ level : i64 ,
204
+
244
205
// Response fields/properties
245
206
status : u64 ,
246
207
latency_ms : f64 ,
247
208
length : i64 ,
248
209
249
210
// All other fields.
250
- fields : BTreeMap < & ' static str , serde_json:: Value > ,
211
+ fields : serde_json :: Map < String , serde_json:: Value > ,
251
212
}
252
213
253
214
struct LogJsonVisitor < ' a > ( & ' a mut LogFieldStorage ) ;
@@ -257,7 +218,7 @@ impl tracing::field::Visit for LogJsonVisitor<'_> {
257
218
match field. name ( ) {
258
219
"latency_ms" => self . 0 . latency_ms = double,
259
220
name => {
260
- self . 0 . fields . insert ( name, json ! ( double) ) ;
221
+ self . 0 . fields . insert ( name. into ( ) , double. into ( ) ) ;
261
222
}
262
223
} ;
263
224
}
@@ -266,7 +227,7 @@ impl tracing::field::Visit for LogJsonVisitor<'_> {
266
227
match field. name ( ) {
267
228
"length" => self . 0 . length = int,
268
229
name => {
269
- self . 0 . fields . insert ( name, json ! ( int) ) ;
230
+ self . 0 . fields . insert ( name. into ( ) , int. into ( ) ) ;
270
231
}
271
232
} ;
272
233
}
@@ -275,13 +236,13 @@ impl tracing::field::Visit for LogJsonVisitor<'_> {
275
236
match field. name ( ) {
276
237
"status" => self . 0 . status = uint,
277
238
name => {
278
- self . 0 . fields . insert ( name, json ! ( uint) ) ;
239
+ self . 0 . fields . insert ( name. into ( ) , uint. into ( ) ) ;
279
240
}
280
241
} ;
281
242
}
282
243
283
244
fn record_bool ( & mut self , field : & Field , b : bool ) {
284
- self . 0 . fields . insert ( field. name ( ) , json ! ( b ) ) ;
245
+ self . 0 . fields . insert ( field. name ( ) . into ( ) , b . into ( ) ) ;
285
246
}
286
247
287
248
fn record_str ( & mut self , field : & Field , s : & str ) {
@@ -291,7 +252,7 @@ impl tracing::field::Visit for LogJsonVisitor<'_> {
291
252
"referer" => self . 0 . referer = s. to_string ( ) ,
292
253
"user_agent" => self . 0 . user_agent = s. to_string ( ) ,
293
254
name => {
294
- self . 0 . fields . insert ( name, json ! ( s ) ) ;
255
+ self . 0 . fields . insert ( name. into ( ) , s . into ( ) ) ;
295
256
}
296
257
} ;
297
258
}
@@ -303,57 +264,44 @@ impl tracing::field::Visit for LogJsonVisitor<'_> {
303
264
"uri" => self . 0 . uri = v,
304
265
"version" => self . 0 . version = v,
305
266
name => {
306
- self . 0 . fields . insert ( name, json ! ( v ) ) ;
267
+ self . 0 . fields . insert ( name. into ( ) , v . into ( ) ) ;
307
268
}
308
269
} ;
309
270
}
310
271
311
272
fn record_error ( & mut self , field : & Field , err : & ( dyn std:: error:: Error + ' static ) ) {
312
- self . 0 . fields . insert ( field. name ( ) , json ! ( err. to_string( ) ) ) ;
273
+ self
274
+ . 0
275
+ . fields
276
+ . insert ( field. name ( ) . into ( ) , json ! ( err. to_string( ) ) ) ;
313
277
}
314
278
}
315
279
316
- // #[derive(Debug)]
317
- // struct CustomFieldStorage(BTreeMap<String, serde_json::Value>);
318
- //
319
- // struct JsonVisitor<'a>(&'a mut BTreeMap<String, serde_json::Value>);
320
- //
321
- // impl<'a> tracing::field::Visit for JsonVisitor<'a> {
322
- // fn record_f64(&mut self, field: &Field, double: f64) {
323
- // self.0.insert(field.name().to_string(), json!(double));
324
- // }
325
- //
326
- // fn record_i64(&mut self, field: &Field, int: i64) {
327
- // self.0.insert(field.name().to_string(), json!(int));
328
- // }
329
- //
330
- // fn record_u64(&mut self, field: &Field, uint: u64) {
331
- // self.0.insert(field.name().to_string(), json!(uint));
332
- // }
333
- //
334
- // fn record_bool(&mut self, field: &Field, b: bool) {
335
- // self.0.insert(field.name().to_string(), json!(b));
336
- // }
337
- //
338
- // fn record_str(&mut self, field: &Field, s: &str) {
339
- // self.0.insert(field.name().to_string(), json!(s));
340
- // }
341
- //
342
- // fn record_error(&mut self, field: &Field, err: &(dyn std::error::Error + 'static)) {
343
- // self
344
- // .0
345
- // .insert(field.name().to_string(), json!(err.to_string()));
346
- // }
347
- //
348
- // fn record_debug(&mut self, field: &Field, dbg: &dyn std::fmt::Debug) {
349
- // self
350
- // .0
351
- // .insert(field.name().to_string(), json!(format!("{:?}", dbg)));
352
- // }
353
- // }
280
+ #[ inline]
281
+ fn as_millis_f64 ( d : & Duration ) -> f64 {
282
+ const NANOS_PER_MILLI : f64 = 1_000_000.0 ;
283
+ const MILLIS_PER_SEC : u64 = 1_000 ;
284
+
285
+ return ( d. as_secs ( ) as f64 ) * ( MILLIS_PER_SEC as f64 )
286
+ + ( d. subsec_nanos ( ) as f64 ) / ( NANOS_PER_MILLI ) ;
287
+ }
354
288
289
+ #[ inline]
355
290
fn get_header < ' a > ( headers : & ' a HeaderMap , header_name : & ' static str ) -> Option < & ' a str > {
356
291
headers
357
292
. get ( header_name)
358
293
. and_then ( |header_value| header_value. to_str ( ) . ok ( ) )
359
294
}
295
+
296
+ #[ inline]
297
+ fn level_to_int ( level : & tracing:: Level ) -> i64 {
298
+ match * level {
299
+ tracing:: Level :: TRACE => 4 ,
300
+ tracing:: Level :: DEBUG => 3 ,
301
+ tracing:: Level :: INFO => 2 ,
302
+ tracing:: Level :: WARN => 1 ,
303
+ tracing:: Level :: ERROR => 0 ,
304
+ }
305
+ }
306
+
307
+ const BUG_TEXT : & str = "Span not found, this is a bug" ;
0 commit comments