1+ use std:: fmt:: Write ;
2+
13use chrono:: { DateTime , Utc } ;
24#[ cfg( feature = "display" ) ]
5+ use crossterm:: style:: StyledContent ;
6+ #[ cfg( feature = "display" ) ]
37use crossterm:: style:: Stylize ;
48use serde:: { Deserialize , Serialize } ;
59use strum:: EnumString ;
6- use tracing:: { field:: Visit , span, warn, Metadata , Subscriber } ;
10+ use tracing:: { field:: Visit , span, warn, Event , Level , Metadata , Subscriber } ;
711use tracing_subscriber:: Layer ;
812#[ cfg( feature = "openapi" ) ]
913use utoipa:: ToSchema ;
1014use uuid:: Uuid ;
1115
16+ use crate :: tracing:: JsonVisitor ;
17+
1218/// Used to determine settings based on which backend crate does what
1319#[ derive( Clone , Debug , EnumString , Eq , PartialEq , Deserialize , Serialize ) ]
1420#[ cfg_attr( feature = "display" , derive( strum:: Display ) ) ]
@@ -52,21 +58,85 @@ pub struct LogItem {
5258 pub line : String ,
5359}
5460
61+ impl LogItem {
62+ pub fn new ( id : Uuid , internal_origin : Backend , line : String ) -> Self {
63+ Self {
64+ id,
65+ internal_origin,
66+ timestamp : Utc :: now ( ) ,
67+ line,
68+ }
69+ }
70+ }
71+
5572#[ cfg( feature = "display" ) ]
5673impl std:: fmt:: Display for LogItem {
5774 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
5875 let datetime: chrono:: DateTime < chrono:: Local > = DateTime :: from ( self . timestamp ) ;
5976
6077 write ! (
6178 f,
62- "{} [{}] {}" ,
79+ "{} {} {}" ,
6380 datetime. to_rfc3339( ) . dim( ) ,
64- self . internal_origin,
81+ format! ( "[{}]" , self . internal_origin) . grey ( ) ,
6582 self . line,
6683 )
6784 }
6885}
6986
87+ #[ cfg( feature = "display" ) ]
88+ pub trait ColoredLevel {
89+ fn colored ( & self ) -> StyledContent < & str > ;
90+ }
91+
92+ #[ cfg( feature = "display" ) ]
93+ impl ColoredLevel for tracing:: Level {
94+ fn colored ( & self ) -> StyledContent < & str > {
95+ match * self {
96+ Level :: TRACE => "TRACE" . magenta ( ) ,
97+ Level :: DEBUG => "DEBUG" . blue ( ) ,
98+ Level :: INFO => " INFO" . green ( ) ,
99+ Level :: WARN => " WARN" . yellow ( ) ,
100+ Level :: ERROR => "ERROR" . red ( ) ,
101+ }
102+ }
103+ }
104+
105+ #[ cfg( feature = "display" ) ]
106+ pub fn format_event ( event : & Event < ' _ > ) -> String {
107+ let metadata = event. metadata ( ) ;
108+ let mut visitor = JsonVisitor :: default ( ) ;
109+ event. record ( & mut visitor) ;
110+
111+ let mut message = String :: new ( ) ;
112+
113+ let target = visitor
114+ . target
115+ . unwrap_or_else ( || metadata. target ( ) . to_string ( ) ) ;
116+
117+ if !target. is_empty ( ) {
118+ let t = format ! ( "{target}: " ) . dim ( ) ;
119+ write ! ( message, "{t}" ) . unwrap ( ) ;
120+ }
121+
122+ let mut simple = None ;
123+ let mut extra = vec ! [ ] ;
124+ for ( key, value) in visitor. fields . iter ( ) {
125+ match key. as_str ( ) {
126+ "message" => simple = value. as_str ( ) ,
127+ _ => extra. push ( format ! ( "{key}={value}" ) ) ,
128+ }
129+ }
130+ if !extra. is_empty ( ) {
131+ write ! ( message, "{{{}}} " , extra. join( " " ) ) . unwrap ( ) ;
132+ }
133+ if let Some ( msg) = simple {
134+ write ! ( message, "{msg}" ) . unwrap ( ) ;
135+ }
136+
137+ format ! ( "{} {}" , metadata. level( ) . colored( ) , message)
138+ }
139+
70140/// Records logs for the deployment progress
71141pub trait LogRecorder : Clone + Send + ' static {
72142 fn record ( & self , log : LogItem ) ;
@@ -95,17 +165,16 @@ where
95165 return ;
96166 } ;
97167
98- // Find the outermost scope with the scope details containing the current state
168+ // Find the outermost scope with the scope details containing the current deployment id
99169 for span in scope. from_root ( ) {
100170 let extensions = span. extensions ( ) ;
101171
102172 if let Some ( details) = extensions. get :: < ScopeDetails > ( ) {
103- self . recorder . record ( LogItem {
104- id : details. id ,
105- internal_origin : self . internal_service . clone ( ) ,
106- timestamp : Utc :: now ( ) ,
107- line : "Test" . into ( ) ,
108- } ) ;
173+ self . recorder . record ( LogItem :: new (
174+ details. deployment_id ,
175+ self . internal_service . clone ( ) ,
176+ format_event ( event) ,
177+ ) ) ;
109178 break ;
110179 }
111180 }
@@ -124,39 +193,46 @@ where
124193 attrs. record ( & mut visitor) ;
125194 let details = visitor. details ;
126195
127- if details. id . is_nil ( ) {
128- warn ! ( "scope details does not have a valid id " ) ;
196+ if details. deployment_id . is_nil ( ) {
197+ warn ! ( "scope details does not have a valid deployment_id " ) ;
129198 return ;
130199 }
131200
132201 // Safe to unwrap since this is the `on_new_span` method
133202 let span = ctx. span ( id) . unwrap ( ) ;
134203 let mut extensions = span. extensions_mut ( ) ;
135204
136- self . recorder . record ( LogItem {
137- id : details. id ,
138- internal_origin : self . internal_service . clone ( ) ,
139- timestamp : Utc :: now ( ) ,
140- line : "Test" . into ( ) ,
141- } ) ;
205+ let metadata = attrs. metadata ( ) ;
206+
207+ let message = format ! (
208+ "{} Entering span {}" ,
209+ metadata. level( ) . colored( ) ,
210+ metadata. name( ) . blue( ) ,
211+ ) ;
212+
213+ self . recorder . record ( LogItem :: new (
214+ details. deployment_id ,
215+ self . internal_service . clone ( ) ,
216+ message,
217+ ) ) ;
142218
143219 extensions. insert :: < ScopeDetails > ( details) ;
144220 }
145221}
146222
147223#[ derive( Debug , Default ) ]
148224struct ScopeDetails {
149- id : Uuid ,
225+ deployment_id : Uuid ,
150226}
151- /// To extract `id ` field for scopes that have it
227+ /// To extract `deployment_id ` field for scopes that have it
152228#[ derive( Default ) ]
153229struct DeploymentIdVisitor {
154230 details : ScopeDetails ,
155231}
156232
157233impl DeploymentIdVisitor {
158234 /// Field containing the deployment identifier
159- const ID_IDENT : & ' static str = "id " ;
235+ const ID_IDENT : & ' static str = "deployment_id " ;
160236
161237 fn is_valid ( metadata : & Metadata ) -> bool {
162238 metadata. is_span ( ) && metadata. fields ( ) . field ( Self :: ID_IDENT ) . is_some ( )
@@ -166,7 +242,7 @@ impl DeploymentIdVisitor {
166242impl Visit for DeploymentIdVisitor {
167243 fn record_debug ( & mut self , field : & tracing:: field:: Field , value : & dyn std:: fmt:: Debug ) {
168244 if field. name ( ) == Self :: ID_IDENT {
169- self . details . id = Uuid :: try_parse ( & format ! ( "{value:?}" ) ) . unwrap_or_default ( ) ;
245+ self . details . deployment_id = Uuid :: try_parse ( & format ! ( "{value:?}" ) ) . unwrap_or_default ( ) ;
170246 }
171247 }
172248}
@@ -186,12 +262,11 @@ mod tests {
186262
187263 #[ test]
188264 fn test_timezone_formatting ( ) {
189- let item = LogItem {
190- id : Uuid :: new_v4 ( ) ,
191- internal_origin : Backend :: Deployer ,
192- timestamp : Utc :: now ( ) ,
193- line : r#"{"message": "Building"}"# . to_owned ( ) ,
194- } ;
265+ let item = LogItem :: new (
266+ Uuid :: new_v4 ( ) ,
267+ Backend :: Deployer ,
268+ r#"{"message": "Building"}"# . to_owned ( ) ,
269+ ) ;
195270
196271 with_tz ( "CEST" , || {
197272 let cest_dt = item. timestamp . with_timezone ( & chrono:: Local ) . to_rfc3339 ( ) ;
0 commit comments