1
+ use actix_web:: dev:: Service ;
1
2
use actix_web:: middleware:: from_fn;
2
3
use actix_web:: { App , HttpServer } ;
3
4
use actix_web_prom:: PrometheusMetricsBuilder ;
4
5
use clap:: Parser ;
6
+
5
7
use labrinth:: app_config;
6
8
use labrinth:: background_task:: BackgroundTask ;
7
9
use labrinth:: database:: redis:: RedisPool ;
@@ -13,9 +15,15 @@ use labrinth::util::env::parse_var;
13
15
use labrinth:: util:: ratelimit:: rate_limit_middleware;
14
16
use labrinth:: { check_env_vars, clickhouse, database, file_hosting, queue} ;
15
17
use std:: ffi:: CStr ;
18
+ use std:: str:: FromStr ;
16
19
use std:: sync:: Arc ;
17
- use tracing:: { error, info} ;
20
+ use tracing:: level_filters:: LevelFilter ;
21
+ use tracing:: { Instrument , error, info, info_span} ;
18
22
use tracing_actix_web:: TracingLogger ;
23
+ use tracing_ecs:: ECSLayerBuilder ;
24
+ use tracing_subscriber:: EnvFilter ;
25
+ use tracing_subscriber:: layer:: SubscriberExt ;
26
+ use tracing_subscriber:: util:: SubscriberInitExt ;
19
27
20
28
#[ cfg( target_os = "linux" ) ]
21
29
#[ global_allocator]
@@ -46,12 +54,59 @@ struct Args {
46
54
run_background_task : Option < BackgroundTask > ,
47
55
}
48
56
57
+ #[ derive( Debug , Clone , Default , PartialEq , Eq ) ]
58
+ enum OutputFormat {
59
+ #[ default]
60
+ Human ,
61
+ Json ,
62
+ }
63
+
64
+ impl FromStr for OutputFormat {
65
+ type Err = ( ) ;
66
+
67
+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
68
+ match s {
69
+ "human" => Ok ( Self :: Human ) ,
70
+ "json" => Ok ( Self :: Json ) ,
71
+ _ => Err ( ( ) ) ,
72
+ }
73
+ }
74
+ }
75
+
49
76
#[ actix_rt:: main]
50
77
async fn main ( ) -> std:: io:: Result < ( ) > {
51
78
let args = Args :: parse ( ) ;
52
79
80
+ color_eyre:: install ( ) . expect ( "failed to install `color-eyre`" ) ;
53
81
dotenvy:: dotenv ( ) . ok ( ) ;
54
- console_subscriber:: init ( ) ;
82
+ let console_layer = console_subscriber:: spawn ( ) ;
83
+ let env_filter = EnvFilter :: builder ( )
84
+ . with_default_directive ( LevelFilter :: INFO . into ( ) )
85
+ . from_env_lossy ( ) ;
86
+
87
+ let output_format =
88
+ dotenvy:: var ( "LABRINTH_FORMAT" ) . map_or ( OutputFormat :: Human , |format| {
89
+ format
90
+ . parse :: < OutputFormat > ( )
91
+ . unwrap_or_else ( |_| panic ! ( "invalid output format '{format}'" ) )
92
+ } ) ;
93
+
94
+ match output_format {
95
+ OutputFormat :: Human => {
96
+ tracing_subscriber:: registry ( )
97
+ . with ( console_layer)
98
+ . with ( env_filter)
99
+ . with ( tracing_subscriber:: fmt:: layer ( ) )
100
+ . init ( ) ;
101
+ }
102
+ OutputFormat :: Json => {
103
+ tracing_subscriber:: registry ( )
104
+ . with ( console_layer)
105
+ . with ( env_filter)
106
+ . with ( ECSLayerBuilder :: default ( ) . stdout ( ) )
107
+ . init ( ) ;
108
+ }
109
+ }
55
110
56
111
if check_env_vars ( ) {
57
112
error ! ( "Some environment variables are missing!" ) ;
@@ -199,6 +254,33 @@ async fn main() -> std::io::Result<()> {
199
254
HttpServer :: new ( move || {
200
255
App :: new ( )
201
256
. wrap ( TracingLogger :: default ( ) )
257
+ . wrap_fn ( |req, srv| {
258
+ // We capture the same fields as `tracing-actix-web`'s `RootSpanBuilder`.
259
+ // See `root_span!` macro.
260
+ let span = info_span ! (
261
+ "HTTP request" ,
262
+ http. method = %req. method( ) ,
263
+ http. client_ip = %req. connection_info( ) . realip_remote_addr( ) . unwrap_or( "" ) ,
264
+ http. user_agent = %req. headers( ) . get( "User-Agent" ) . map_or( "" , |h| h. to_str( ) . unwrap_or( "" ) ) ,
265
+ http. target = %req. uri( ) . path_and_query( ) . map_or( "" , |p| p. as_str( ) ) ,
266
+ http. authenticated = %req. headers( ) . get( "Authorization" ) . is_some( )
267
+ ) ;
268
+
269
+ let fut = srv. call ( req) ;
270
+ async move {
271
+ fut. await . inspect ( |resp| {
272
+ let _span = info_span ! (
273
+ "HTTP response" ,
274
+ http. status = %resp. response( ) . status( ) . as_u16( ) ,
275
+ ) . entered ( ) ;
276
+
277
+ resp. response ( )
278
+ . error ( )
279
+ . inspect ( |err| log_error ( err) ) ;
280
+ } )
281
+ }
282
+ . instrument ( span)
283
+ } )
202
284
. wrap ( prometheus. clone ( ) )
203
285
. wrap ( from_fn ( rate_limit_middleware) )
204
286
. wrap ( actix_web:: middleware:: Compress :: default ( ) )
@@ -209,3 +291,15 @@ async fn main() -> std::io::Result<()> {
209
291
. run ( )
210
292
. await
211
293
}
294
+
295
+ fn log_error ( err : & actix_web:: Error ) {
296
+ if err. as_response_error ( ) . status_code ( ) . is_client_error ( ) {
297
+ tracing:: debug!(
298
+ "Error encountered while processing the incoming HTTP request: {err}"
299
+ ) ;
300
+ } else {
301
+ tracing:: error!(
302
+ "Error encountered while processing the incoming HTTP request: {err}"
303
+ ) ;
304
+ }
305
+ }
0 commit comments