22% %% This allows for tracking the lifecycle of a request from HTTP receipt through processing and response.
33
44-module (hb_tracer ).
5+
56-export ([start_trace /0 , record_step /2 , get_trace /1 , format_error_trace /1 ]).
67
78-include (" include/hb.hrl" ).
89
10+ % %% @doc Start a new tracer acting as queue of events registered.
911start_trace () ->
10- Trace = #{ steps => queue :new () },
11- TracePID = spawn (fun () -> trace_loop (Trace ) end ),
12- ? event (trace , {trace_started , TracePID }),
13- TracePID .
12+ Trace = #{steps => queue :new ()},
13+ TracePID = spawn (fun () -> trace_loop (Trace ) end ),
14+ ? event (trace , {trace_started , TracePID }),
15+ TracePID .
1416
1517trace_loop (Trace ) ->
16- receive
17- {record_step , Step } ->
18- Steps = maps :get (steps , Trace ),
19- NewTrace = Trace #{steps => queue :in (Step , Steps )},
20- ? event (trace , {step_recorded , Step }),
21- trace_loop (NewTrace );
22- {get_trace , From } ->
23- % Convert queue to list for the response
24- TraceWithList = Trace #{steps => queue :to_list (maps :get (steps , Trace ))},
25- From ! {trace , TraceWithList },
26- trace_loop (Trace )
27- end .
28-
18+ receive
19+ {record_step , Step } ->
20+ Steps = maps :get (steps , Trace ),
21+ NewTrace = Trace #{steps => queue :in (Step , Steps )},
22+ ? event (trace , {step_recorded , Step }),
23+ trace_loop (NewTrace );
24+ {get_trace , From } ->
25+ % Convert queue to list for the response
26+ TraceWithList =
27+ Trace #{steps =>
28+ queue :to_list (
29+ maps :get (steps , Trace ))},
30+ From ! {trace , TraceWithList },
31+ trace_loop (Trace )
32+ end .
33+
34+ % %% @doc Register a new step into a tracer
2935record_step (TracePID , Step ) ->
30- TracePID ! {record_step , Step }.
36+ TracePID ! {record_step , Step }.
3137
38+ % %% @doc Exports the complete queue of events
3239get_trace (TracePID ) ->
33- TracePID ! {get_trace , self ()},
34- receive
35- {trace , Trace } ->
36- Trace
37- after 5000 ->
38- ? event (trace , {trace_timeout , TracePID }),
39- {trace , #{}}
40- end .
41-
40+ TracePID ! {get_trace , self ()},
41+ receive
42+ {trace , Trace } ->
43+ Trace
44+ after 5000 ->
45+ ? event (trace , {trace_timeout , TracePID }),
46+ {trace , #{}}
47+ end .
48+
49+ % %% @doc Format a trace for error in a user-friendly emoji oriented output
4250format_error_trace (Trace ) ->
43- Steps = maps :get (steps , Trace , []),
44-
45- TraceMap = lists :foldl (fun (TraceItem , Acc ) ->
46- case TraceItem of
47- {http , {parsed_singleton , _ReqSingleton , _ }} ->
48- maps :put (request_parsing , true , Acc );
49- {ao_core , {stage , Stage , _Task }} ->
50- maps :put (resolve_stage , Stage , Acc );
51- {ao_result , {load_device_failed , _ , _ , _ , _ , {exec_exception , Exception }, _ , _ }} ->
52- maps :put (error , Exception , Acc );
53- {ao_result , {exec_failed , _ , _ , _ , {func , Fun }, _ , {exec_exception , Error }, _ , _ }} ->
54- maps :put (error , {Fun , Error }, Acc );
55- _ -> Acc
56- end
57- end , #{}, Steps ),
58-
59- % Build the trace message
60- TraceStrings = [" Oops! Something went wrong. Here's the rundown:" ],
61-
62- % Add parsing status
63- ParsingTrace = case maps :get (request_parsing , TraceMap , false ) of
64- false ->
65- TraceStrings ++ [[failure_emoji (), " Parsing your request" ]];
66- true ->
67- TraceStrings ++ [[checkmark_emoji (), " Parsing your request" ]]
68- end ,
69-
70- % Add stage information
71- StageTrace = case maps :get (resolve_stage , TraceMap , undefined ) of
72- undefined ->
73- ParsingTrace ;
74- Stage ->
75- ParsingTrace ++ [[stage_to_emoji (Stage ), " Resolved steps of your execution" ]]
76- end ,
77-
78- % Add error information
79- ErrorTrace = case maps :get (error , TraceMap , undefined ) of
80- undefined ->
81- StageTrace ;
82- {Fun , Reason } ->
83- StageTrace ++ [[failure_emoji (), io_lib :format (" Error: ~p -> ~p " , [Fun , Reason ])]];
84- Error ->
85- StageTrace ++ [[failure_emoji (), io_lib :format (" Error: ~p " , [Error ])]]
86- end ,
87- string :join (ErrorTrace , " \n " ).
88-
51+ Steps = maps :get (steps , Trace , []),
52+ TraceMap =
53+ lists :foldl (fun (TraceItem , Acc ) ->
54+ case TraceItem of
55+ {http , {parsed_singleton , _ReqSingleton , _ }} ->
56+ maps :put (request_parsing , true , Acc );
57+ {ao_core , {stage , Stage , _Task }} -> maps :put (resolve_stage , Stage , Acc );
58+ {ao_result ,
59+ {load_device_failed , _ , _ , _ , _ , {exec_exception , Exception }, _ , _ }} ->
60+ maps :put (error , Exception , Acc );
61+ {ao_result ,
62+ {exec_failed ,
63+ _ ,
64+ _ ,
65+ _ ,
66+ {func , Fun },
67+ _ ,
68+ {exec_exception , Error },
69+ _ ,
70+ _ }} ->
71+ maps :put (error , {Fun , Error }, Acc );
72+ _ -> Acc
73+ end
74+ end ,
75+ #{},
76+ Steps ),
77+ % Build the trace message
78+ TraceStrings = <<" Oops! Something went wrong. Here's the rundown:" >>,
79+ % Add parsing status
80+ ParsingTrace =
81+ case maps :get (request_parsing , TraceMap , false ) of
82+ false ->
83+ Emoji = failure_emoji (),
84+ <<TraceStrings /binary , " \n " , Emoji /binary , " Parsing your request" >>;
85+ true ->
86+ Emoji = checkmark_emoji (),
87+ <<TraceStrings /binary , " \n " , Emoji /binary , " Parsing your request" >>
88+ end ,
89+ % Add stage information
90+ StageTrace =
91+ case maps :get (resolve_stage , TraceMap , undefined ) of
92+ undefined ->
93+ ParsingTrace ;
94+ Stage ->
95+ StageEmoji = stage_to_emoji (Stage ),
96+ <<ParsingTrace /binary ,
97+ " \n " ,
98+ StageEmoji /binary ,
99+ " Resolved steps of your execution" >>
100+ end ,
101+ % Add error information
102+ case maps :get (error , TraceMap , undefined ) of
103+ undefined ->
104+ StageTrace ;
105+ {Fun , Reason } ->
106+ FailureEmoji = failure_emoji (),
107+ ErrMsg = list_to_binary (io_lib :format (" ~p -> ~p " , [Fun , Reason ])),
108+ <<StageTrace /binary , " \n " , FailureEmoji /binary , " Error " , ErrMsg /binary >>;
109+ Error ->
110+ FailureEmoji = failure_emoji (),
111+ <<StageTrace /binary , " \n " , FailureEmoji /binary , " Error " , Error /binary >>
112+ end .
89113
90114checkmark_emoji () ->
91- % Unicode for checkmark
92- " \xE2\x9C\x85 " . % \xE2\x9C\x85 is the checkmark emoji in UTF-8
115+ % Unicode for checkmark
116+ << " \xE2\x9C\x85 " >> . % \xE2\x9C\x85 is the checkmark emoji in UTF-8
93117
94118failure_emoji () ->
95- % Unicode for failure emoji
96- " \xE2\x9D\x8C " . % \xE2\x9D\x8C is the failure emoji in UTF-8
119+ % Unicode for failure emoji
120+ << " \xE2\x9D\x8C " >> . % \xE2\x9D\x8C is the failure emoji in UTF-8
97121
98122% Helper function to convert stage number to emoji
99123stage_to_emoji (Stage ) when Stage >= 1 , Stage =< 9 ->
100- % Unicode for circled numbers 1-9
101- [Stage + 48 , 16#E2 , 16#83 , 16#A3 ];
124+ % Unicode for circled numbers 1-9
125+ StageEmoji = Stage + 48 ,
126+ <<StageEmoji , 16#E2 , 16#83 , 16#A3 >>;
102127stage_to_emoji (_ ) ->
103- " " .
128+ " " .
0 commit comments