-
Notifications
You must be signed in to change notification settings - Fork 106
/
batLogger.ml
212 lines (177 loc) · 7.18 KB
/
batLogger.ml
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
(* -*- Mode: Caml; indent-tabs-mode: nil -*- *)
(******************************************************************************)
(* Copyright (c) 2009, Metaweb Technologies, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY METAWEB TECHNOLOGIES ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL METAWEB TECHNOLOGIES BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************)
open Printf
type log = {
name : string;
mutable level : int;
}
type level = NONE | FATAL | ERROR | WARN | NOTICE | INFO | DEBUG
type event = string * (string * string) list
type formatter = log -> level -> event -> float -> unit
(******************************************************************************)
(** log utilities *)
let int_of_level = function
| NONE -> 0 | FATAL -> 1 | ERROR -> 2 | WARN -> 3
| NOTICE -> 4 | INFO -> 5 | DEBUG -> 6
let level_of_int = function
| 0 -> NONE | 1 -> FATAL | 2 -> ERROR | 3 -> WARN
| 4 -> NOTICE | 5 -> INFO | 6 -> DEBUG
| i -> failwith ("invalid level: " ^ string_of_int i)
let name_of_level = function
| NONE -> "NONE" | FATAL -> "FATAL" | ERROR -> "ERROR" | WARN -> "WARN"
| NOTICE -> "NOTICE" | INFO -> "INFO" | DEBUG -> "DEBUG"
let level_of_name = function
| "NONE" -> NONE | "FATAL" -> FATAL | "ERROR" -> ERROR | "WARN" -> WARN
| "NOTICE" -> NOTICE | "INFO" -> INFO | "DEBUG" -> DEBUG
| n -> failwith ("invalid level: " ^ n)
let format_timestamp out ts =
let tm = Unix.gmtime ts in
let ms, _ = modf ts in
fprintf out "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ"
(1900 + tm.Unix.tm_year)
(1 + tm.Unix.tm_mon)
(tm.Unix.tm_mday)
(tm.Unix.tm_hour)
(tm.Unix.tm_min)
(tm.Unix.tm_sec)
(int_of_float (100000. *. ms))
(******************************************************************************)
(** log modules *)
let logs = Hashtbl.create 16
let default_level = ref (int_of_level INFO)
let make_log name =
try Hashtbl.find logs name
with Not_found ->
let lm = { name = name; level = !default_level }
in Hashtbl.replace logs name lm;
lm
let log_enable lm lev = lm.level <- int_of_level lev
let log_enabled lm lev =
let lev_no = int_of_level lev in
lev_no <= lm.level
let log_name lm = lm.name
let log_level lm = level_of_int lm.level
(******************************************************************************)
(** log formatters *)
let depth = ref 0
let formatters : (string * formatter) list ref = ref []
let register_formatter name f = formatters := (name, f) :: !formatters
let unregister_formatter name =
formatters := List.remove_assoc name !formatters
let rec format_kvl oc = function
| [] -> ()
| (k, v)::rest ->
fprintf oc "\t%s:%s" k v;
format_kvl oc rest
let make_std_formatter oc lm lev (event_name, event_args) timestamp =
fprintf oc "D:%a\tE:%s.%s\tL:%s%a\n%!"
(*D:*) format_timestamp timestamp
(*E:*) lm.name event_name
(*L:*) (name_of_level lev)
format_kvl event_args
let stderr_formatter = make_std_formatter stderr
let null_formatter lm lev event timestamp = ()
let format_indent oc depth =
for i = 0 to depth do
fprintf oc "| "
done
let make_dbg_formatter oc lm lev (event_name, event_args) timestamp =
let indent = try int_of_string (List.assoc "I" event_args) with _ -> 0 in
let args = List.remove_assoc "I" event_args in
fprintf oc "### %a%s.%s %a [%s]\n%!" format_indent indent
(log_name lm) event_name
format_kvl args (name_of_level lev)
let dbg_formatter lm lev ep ts = make_dbg_formatter stderr lm lev ep ts
(******************************************************************************)
(** log events *)
let log lm lev event_fun =
if log_enabled lm lev then
let time = Unix.gettimeofday () in
let event_name, event_args = event_fun () in
let event = event_name, ("I", string_of_int !depth) :: event_args in
List.iter (fun (name, fmt) -> fmt lm lev event time) !formatters
let with_log lm lev event_fun ?result body =
if log_enabled lm lev then begin
try
log lm lev event_fun;
incr depth;
let rv = body () in
decr depth;
log lm lev (fun () ->
let event_name, event_args = event_fun () in
let result_str = match result with
| Some f -> f rv
| None -> "-"
in
event_name, ("RESULT", result_str) ::event_args);
rv
with exn ->
decr depth;
log lm lev (fun () ->
let event_name, event_args = event_fun () in
event_name, ("EXN", Printexc.to_string exn) :: event_args);
raise exn
end else body ()
(******************************************************************************)
(** logger initialization *)
let init name_level_list formatter =
List.iter (fun (name, level) -> let lm = make_log name in log_enable lm level)
name_level_list;
register_formatter "default" formatter
let init_from_string name_level_string formatter =
let init_key_value ss =
try
let name_ss, level_ss = BatSubstring.splitl (fun c -> c <> ':') ss in
let name = BatSubstring.to_string name_ss in
let level = level_of_name (BatSubstring.to_string level_ss) in
let lm = make_log name in
log_enable lm level
with Not_found -> try
let level = level_of_name (BatSubstring.to_string ss) in
default_level := int_of_level level;
Hashtbl.iter (fun name lm -> log_enable lm level) logs
with Failure _ ->
failwith ("invalid log initialization: " ^ BatSubstring.to_string ss)
in
List.iter init_key_value (BatSubstring.split_on_comma (BatSubstring.of_string name_level_string) );
register_formatter "default" formatter
(******************************************************************************)
let test () =
let lm = make_log "test" in
let direct () =
log lm NOTICE (fun () -> "hello", []);
log lm DEBUG (fun () -> "debug msg1", []);
log lm ERROR (fun () -> "error msg1", []);
log lm ERROR (fun () -> "ok", ["ARG1", string_of_int 234]);
in
let rec run () =
direct ();
Unix.sleep 3;
run ()
in run ()
(******************************************************************************)