1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
include Common;;
let log_binary_trace = ref false
let print_backtraces = ref true
let set_log_binary_trace bool = log_binary_trace := bool
type 'a printf = ('a, Format.formatter, unit) format -> 'a;;
type 'a log = 'a printf -> unit;;
type location = ..
module type S = sig
val error : 'a log -> unit
val warning : 'a log -> unit
val notice : 'a log -> unit
val info : 'a log -> unit
val debug : 'a log -> unit
val fatal : 'a log -> 'b
val not_yet_implemented : 'a log -> unit
val trace: 'a log -> ?loc:location -> ?bintrace:_ Syntax_tree.Location_identifier.t -> ?pp_ret:(Format.formatter -> 'b -> unit) -> (unit -> 'b) -> 'b
val fatal_handler: ('a -> 'b) -> ('a -> 'b)
end;;
module Dummy: S = struct
let error _ = ()
let warning _ = ()
let notice _ = ()
let info _ = ()
let debug _ = ()
let fatal _ =
raise (Fatal "dummy")
let not_yet_implemented = fatal
let trace _ ?loc:_ ?bintrace:_ ?pp_ret:_ f = f ()
let fatal_handler f x =
try f x with
| Fatal _ -> exit 1
| _ -> exit 2
end
module Level = struct
let critical = 2
let error = 3
let warning = 4
let notice = 5
let info = 6
let trace = info
let debug = 7
end;;
let current_level = ref Level.warning;;
let set_verbosity_level x =
let open Level in
let vb = match x with
| `Critical -> critical
| `Error -> error
| `Warning -> warning
| `Info -> info
| `Notice -> notice
| `Debug -> debug
in current_level := vb
let get_verbosity_level() =
let open Level in
let current_level = !current_level in
if current_level == error then `Error
else if current_level == warning then `Warning
else if current_level == info then `Info
else if current_level == notice then `Notice
else if current_level == debug then `Debug
else assert false
;;
let location_stack = ref [];;
let reset_location_stack () = location_stack := []
let current_location() = match !location_stack with
| [] -> None
| hd::_ -> Some hd
let current_location_stack() = !location_stack
let r_pp_location_stack = ref (fun fmt locstack ->
match locstack with
| [] -> ()
| _ -> Format.fprintf fmt "<cannot print location>"
)
;;
let set_pp_location_stack f = r_pp_location_stack := f;;
let pp_location_stack fmt loc =
if !current_level >= Level.trace
then ()
else !r_pp_location_stack fmt loc
;;
module MakeGeneric
(V:sig val get_verbosity_level: unit -> int end)
(Category:sig val category:string end) = struct
let maybe_log_bintrace level log =
if !log_binary_trace then begin
(Binarytrace.add_trace_to_file (Category.category)
(Single {category=Category.category;
severity = level;
content= Format.asprintf "%t" (fun fmt -> log (Format.fprintf fmt))}))
end
let print ?(last=false) f = f (Terminal.printf Category.category ~last)
let string_of_log: type a. a log -> string = fun log ->
let buf = Buffer.create 150 in
let ppf = Format.formatter_of_buffer buf in
log (Format.fprintf ppf);
Format.pp_print_flush ppf ();
Buffer.contents buf
;;
let log_of_string str : 'a log = fun m -> m (format_of_string "%s") str
let log_concat (log1:'a log) (log2:'b log) : 'c log =
fun (m:'c printf) -> m "%t%t" (fun fmt -> log1 (Format.fprintf fmt)) (fun fmt -> log2 (Format.fprintf fmt))
;;
let print_at_level level f =
if V.get_verbosity_level() >= level
then (maybe_log_bintrace level f;
print f)
;;
let notice f = print_at_level Level.notice f
let info f = print_at_level Level.info f
let generic_error level underlined_title log =
if V.get_verbosity_level() >= Level.error
then begin
maybe_log_bintrace level log;
let colored_log fmt =
Format.pp_open_stag fmt (Weight Bold);
Format.pp_open_stag fmt (Color Red);
pp_location_stack fmt !location_stack;
Format.pp_open_stag fmt (Underline true);
Format.fprintf fmt underlined_title ;
Format.pp_close_stag fmt ();
Format.fprintf fmt ": %t" (fun fmt -> log (Format.fprintf fmt));
Format.pp_close_stag fmt ();
Format.pp_close_stag fmt ()
in
print (fun p -> p "%t" colored_log)
end
;;
let error log = generic_error Level.error "Error" log;;
let warning log =
if V.get_verbosity_level() >= Level.error
then begin
maybe_log_bintrace Level.warning log;
let colored_log fmt =
Format.pp_open_stag fmt (Color Red);
pp_location_stack fmt !location_stack;
Format.pp_open_stag fmt (Underline true);
Format.fprintf fmt "Warning";
Format.pp_close_stag fmt ();
Format.fprintf fmt ": %t" (fun fmt -> log (Format.fprintf fmt));
Format.pp_close_stag fmt ();
in
print (fun p -> p "%t" colored_log)
end
;;
let debug log =
if V.get_verbosity_level() >= Level.debug
then begin
maybe_log_bintrace Level.debug log;
let colored_log fmt =
Format.pp_open_stag fmt (Weight Faint);
Format.pp_open_stag fmt (Color Cyan);
Format.pp_open_stag fmt (Italic true);
log (Format.fprintf fmt);
Format.pp_close_stag fmt ();
Format.pp_close_stag fmt ();
Format.pp_close_stag fmt ()
in
print (fun p -> p "%t" colored_log)
end
;;
(** Fail, but reuse the message in the failwith argument. *)
let fatal: type a. a log -> 'b = fun log ->
generic_error Level.critical "Fatal Error" log;
raise (Fatal (Format.asprintf "%s:%t" (Category.category) (fun fmt -> log (Format.fprintf fmt))))
let fatal_handler f x =
try f x
with
| Fatal s ->
let backtrace = (Printexc.get_backtrace()) in
print (fun p -> p "%s" backtrace);
exit 1
| e ->
let exc_string = Printexc.to_string e in
let backtrace = Printexc.get_backtrace() in
generic_error Level.critical "Uncaught exception" (fun p -> p "%s" exc_string);
print (fun p -> p "The full backtrace is:\n%s" backtrace);
exit 2
;;
let not_yet_implemented log =
fatal (log_concat (log_of_string "Not yet implemented: ") log)
;;
(** - If [loc] is not None, push the loc on the stack of locations.
- If printed: increase the nesting level.
-
- TODO: Add a log level; default would be info (or trace).
*)
let trace log ?(loc:location option) ?(bintrace: _ Syntax_tree.Location_identifier.t option) ?pp_ret f =
let open Binarytrace in
if !log_binary_trace then begin
(Binarytrace.add_trace_to_file (Category.category)
(Node {loc=bintrace;
category=Category.category;
content= string_of_log log}))
end;
let pop_loc =
match loc with
| None -> fun () -> ()
| Some loc ->
location_stack := loc::!location_stack;
fun () -> location_stack := List.tl !location_stack
in
let display_trace = V.get_verbosity_level() >= Level.trace in
begin try
let with_color log = fun fmt ->
Format.pp_open_stag fmt (Color Green);
log (Format.fprintf fmt);
Format.pp_close_stag fmt ()
in
if display_trace then begin
print ~last:false (fun p -> p "%t" @@ with_color log);
Terminal.open_nesting ();
end;
let res = f() in
if display_trace then begin
(match pp_ret with
| Some pp_ret ->
print ~last:true (fun p -> p "%t" @@ with_color (fun p -> p "Result: %a" pp_ret res));
| None ->
print ~last:true (fun p -> p "%t" @@ with_color (fun p -> p "Result: <not printed>")););
Terminal.close_nesting ();
end;
if !log_binary_trace then begin
let result = match pp_ret with
| Some pp_ret ->
let resultstr = string_of_log (fun p -> p "%a" pp_ret res) in
Result (Some resultstr)
| None ->
Result None
in
Binarytrace.add_trace_to_file (Category.category) result;
end;
pop_loc();
res
with e ->
let bt = Printexc.get_raw_backtrace () in
if display_trace then begin
print ~last:(not !print_backtraces) (fun p -> p "No result (exception %s)" (Printexc.to_string e));
if !print_backtraces then begin
print ~last:true (fun p -> p "%s" (Printexc.raw_backtrace_to_string bt));
print_backtraces := false
end;
Terminal.close_nesting ();
end;
pop_loc();
if !log_binary_trace then
Binarytrace.add_trace_to_file (Category.category) (Result None);
Printexc.raise_with_backtrace e bt
end
;;
end;;
module Make = MakeGeneric[@inlined hint](struct let get_verbosity_level() = !current_level end)
module MakeDebug = MakeGeneric(struct let get_verbosity_level() = Level.debug end)
module MakeSilent = MakeGeneric(struct let get_verbosity_level() = 1 end)
module Test() = struct
module Logs=Make(struct let category = "test" end);;
set_verbosity_level `Debug;;
Logs.notice (fun m -> m "Test 0 message %s %a ads" "Hu" Format.pp_print_string "Ha");;
Logs.notice (fun m -> m "Test simple message\n");;
Logs.warning (fun m -> m "Test 1 warning with newline in middle %s %a@\nads" "Hu" Format.pp_print_string "Ha");;
Logs.error (fun m -> m "Test 2 error with newline at the end %s %a@\n" "Hu" Format.pp_print_string "Ha");;
Logs.error (fun m -> m "Test 3 warning with newline in middle %s %a@\naaend" "Hu" Format.pp_print_string "Ha");;
Logs.debug (fun m -> m "Test 4 debug output %s %a@\naaend" "Hu" Format.pp_print_string "Ha");;
try Logs.not_yet_implemented (fun p -> p "FEATURE") with _ -> ();;
try Logs.fatal (fun m -> m "Test 5 fatal %s %a@\nad" "Hu" Format.pp_print_string "Ha") with _ -> ();;
let my_add a b =
Logs.trace (fun p -> p "Evaluating %d + %d" a b) ~pp_ret:Format.pp_print_int (fun () -> a + b)
;;
my_add (my_add 2 3) (my_add 4 5);;
type expr =
| Add of expr * expr
| Cst of int
let rec pp_expr fmt = function
| Cst i -> Format.fprintf fmt "%d" i
| Add(e1,e2) -> Format.fprintf fmt "(%a + %a)" pp_expr e1 pp_expr e2
let rec eval' = function
| Cst i -> i
| Add(e1,e2) ->
Logs.debug (fun p -> p "Before\n e1");
let r1 = eval e1 in
Logs.debug (fun p -> p "Middle");
let r2 = eval e2 in
Logs.debug (fun p -> p "End");
r1 + r2
and eval e =
Logs.trace (fun p -> p "Evaluating @\nexpression %a" pp_expr e) ~pp_ret:Format.pp_print_int (fun () -> eval' e);;
eval (Add (Add(Cst 2,Cst 3),Add(Cst 4,Cst 5)));;
exception Custom;;
let rec eval' = function
| Cst i when i == 5 -> raise Custom
| Cst i -> i
| Add(e1,e2) ->
Logs.debug (fun p -> p "Before\n e1");
let r1 = eval e1 in
Logs.debug (fun p -> p "Middle");
let r2 = eval e2 in
Logs.debug (fun p -> p "End");
r1 + r2
and eval e =
Logs.trace (fun p -> p "Evaluating @\nexpression %a" pp_expr e) ~pp_ret:Format.pp_print_int (fun () -> eval' e);;
try eval (Add (Add(Cst 2,Cst 3),Add(Cst 4,Cst 5)))
with Custom -> 3;;
end;;