Source file log_config.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
open! Core
type t =
{ log_dir : string
; append : bool
; split : bool
}
[@@deriving sexp_of]
let log_paths t id =
let path = t.log_dir ^/ Command_id.to_string id in
if t.split
then `Split (~stdout:(path ^ ".stdout"), ~stderr:(path ^ ".stderr"))
else `Combined path
;;
let env_var_dir = "PROCTOPUS_LOG_DIR"
let env_var_append = "PROCTOPUS_LOG_APPEND"
let env_var_split = "PROCTOPUS_LOG_SPLIT"
let bool_to_env b = if b then "1" else "0"
let bool_of_env var =
Option.value_map (Sys.getenv var) ~default:false ~f:(fun v -> String.equal v "1")
;;
let to_env_vars t =
[ env_var_dir, t.log_dir
; env_var_append, bool_to_env t.append
; env_var_split, bool_to_env t.split
]
;;
let of_env () =
match Sys.getenv env_var_dir with
| None -> None
| Some log_dir ->
let append = bool_of_env env_var_append in
let split = bool_of_env env_var_split in
Some { log_dir; append; split }
;;
let open_log_writer ~path ~append =
Core_unix.mkdir_p (Filename.dirname path);
Async.Writer.open_file path ~append
;;
(** Forward a pipe into [output_write], optionally also writing each line to [log_writer].
Returns a deferred that resolves when the pipe is exhausted. *)
let forward_reader pipe ~output_write ~log_writer =
Async.Pipe.iter_without_pushback pipe ~f:(fun line ->
Async.Pipe.write_without_pushback_if_open output_write line;
Option.iter log_writer ~f:(fun writer -> Async.Writer.write_line writer line))
;;
let forward_output log_config ~id ~stdout ~stderr =
let open Async in
let output, output_write = Pipe.create () in
match log_config with
| None ->
don't_wait_for
(let%bind () =
Deferred.all_unit
[ forward_reader stdout ~output_write ~log_writer:None
; forward_reader stderr ~output_write ~log_writer:None
]
in
Pipe.close output_write;
return ());
output, fun () -> return ()
| Some log_config ->
let append = log_config.append in
(match log_paths log_config id with
| `Combined path ->
let forwarding_finished = Ivar.create () in
don't_wait_for
(let%bind writer = open_log_writer ~path ~append in
let log_writer = Some writer in
let%bind () =
Deferred.all_unit
[ forward_reader stdout ~output_write ~log_writer
; forward_reader stderr ~output_write ~log_writer
]
in
Pipe.close output_write;
let%bind () = Writer.close writer in
Ivar.fill_exn forwarding_finished ();
return ());
output, fun () -> Ivar.read forwarding_finished
| `Split (~stdout:stdout_path, ~stderr:stderr_path) ->
let forwarding_finished = Ivar.create () in
don't_wait_for
(let%bind stdout_writer = open_log_writer ~path:stdout_path ~append
and stderr_writer = open_log_writer ~path:stderr_path ~append in
let%bind () =
Deferred.all_unit
[ forward_reader stdout ~output_write ~log_writer:(Some stdout_writer)
; forward_reader stderr ~output_write ~log_writer:(Some stderr_writer)
]
in
Pipe.close output_write;
let%bind () = Writer.close stdout_writer in
let%bind () = Writer.close stderr_writer in
Ivar.fill_exn forwarding_finished ();
return ());
output, fun () -> Ivar.read forwarding_finished)
;;