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
(* SPDX-License-Identifier: AGPL-3.0-or-later *)
(* Copyright © 2021-2024 OCamlPro *)
(* Written by the Owi programmers *)
type logger =
{ channel : out_channel
; mutex : Mutex.t
}
let global_stats_logger : logger option ref = ref None
let init_logger_to_file f =
let logger =
{ channel = Out_channel.open_text (Fpath.to_string f)
; mutex = Mutex.create ()
}
in
Out_channel.output_char logger.channel '[';
global_stats_logger := Some logger
(* Be careful that f will be run in the critical section
so should be kept small *)
let on_logger f =
match !global_stats_logger with
| None -> ()
| Some logger -> begin
Mutex.protect logger.mutex (fun () -> f logger.channel)
end
(* assumes that v does not need escaping*)
let emit_key_val_s ch ?(end_comma = true) k v =
Out_channel.output_char ch '"';
Out_channel.output_string ch k;
Out_channel.output_char ch '"';
Out_channel.output_string ch {|:"|};
Out_channel.output_string ch v;
Out_channel.output_char ch '"';
if end_comma then Out_channel.output_char ch ','
(* assumes that v does not need escaping*)
let emit_key_val_i ch ?(end_comma = true) k v =
Out_channel.output_char ch '"';
Out_channel.output_string ch k;
Out_channel.output_char ch '"';
Out_channel.output_char ch ':';
Out_channel.output_string ch @@ Int.to_string v;
if end_comma then Out_channel.output_char ch ','
type scope =
| Global
| Process
| Thread
let event ?(scope = Thread) ?(arg_writter = None) name cat =
let pid = Unix.getpid () in
let tid = (Domain.self () :> int) in
let ts = Int.of_float (Unix.gettimeofday () *. 1e6) in
let scope =
match scope with Global -> "g" | Process -> "p" | Thread -> "t"
in
on_logger (fun ch ->
begin
Out_channel.output_string ch "{";
emit_key_val_s ch "name" name;
emit_key_val_s ch "cat" cat;
emit_key_val_s ch "ph" "i";
emit_key_val_i ch "ts" ts;
emit_key_val_i ch "pid" pid;
emit_key_val_i ch "tid" tid;
emit_key_val_s ch "s" scope ~end_comma:false;
begin
match arg_writter with
| None -> ()
| Some arg_writter -> begin
Out_channel.output_string ch {|,"args":{|};
arg_writter ch;
Out_channel.output_char ch '}'
end
end;
Out_channel.output_string ch "},"
end )
let start_span ?(arg_writter = None) name cat =
let pid = Unix.getpid () in
let tid = (Domain.self () :> int) in
let ts = Int.of_float (Unix.gettimeofday () *. 1e6) in
on_logger (fun ch ->
begin
Out_channel.output_string ch "{";
emit_key_val_s ch "name" name;
emit_key_val_s ch "cat" cat;
emit_key_val_s ch "ph" "B";
emit_key_val_i ch "ts" ts;
emit_key_val_i ch "pid" pid;
emit_key_val_i ch "tid" tid ~end_comma:false;
begin
match arg_writter with
| None -> ()
| Some arg_writter -> begin
Out_channel.output_string ch {|,"args":{|};
arg_writter ch;
Out_channel.output_char ch '}'
end
end;
Out_channel.output_string ch "},"
end )
let close_span () =
let pid = Unix.getpid () in
let tid = (Domain.self () :> int) in
let ts = Int.of_float (Unix.gettimeofday () *. 1e6) in
on_logger (fun ch ->
begin
Out_channel.output_string ch "{";
emit_key_val_s ch "ph" "E";
emit_key_val_i ch "ts" ts;
emit_key_val_i ch "pid" pid;
emit_key_val_i ch "tid" tid ~end_comma:false;
Out_channel.output_string ch "},"
end )
(* {
"name": "myName",
"cat": "category,list",
"ph": "B",
"ts": 12345,
"pid": 123,
"tid": 456,
"args": {
"someArg": 1,
"anotherArg": {
"value": "my value"
}
} *)
(* } *)