package b0

  1. Overview
  2. Docs
Software construction and deployment kit

Install

dune-project
 Dependency

Authors

Maintainers

Sources

b0-0.0.6.tbz
sha512=e9aa779e66c08fc763019f16d4706f465d16c05d6400b58fbd0313317ef33ddea51952e2b058db28e65f7ddb7012f328c8bf02d8f1da17bb543348541a2587f0

doc/src/b0.std/b0__log.ml.html

Source file b0__log.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
(*---------------------------------------------------------------------------
   Copyright (c) 2025 The more programmers. All rights reserved.
   SPDX-License-Identifier: ISC
  ---------------------------------------------------------------------------*)

(* Reporting levels *)

type level = Quiet | Stdout | Stderr | Error | Warning | Info | Debug
let current_level = Atomic.make Warning
let level () = Atomic.get current_level
let set_level l = Atomic.set current_level l
let level_to_string = function
| Quiet -> "quiet" | Stdout -> "stdout" | Stderr -> "stderr"
| Error -> "error" | Warning -> "warning" | Info -> "info" | Debug -> "debug"

let level_of_string s = match String.trim s with
| "quiet" -> Ok Quiet | "stdout" -> Ok Stdout | "stderr" -> Ok Stderr
| "error" -> Ok Error | "warning" -> Ok Warning | "info" ->  Ok Info
| "debug" ->  Ok Debug
| e ->
    let pp_level = B0__fmt.code in
    let kind = B0__fmt.any "log level" in
    let dom =
      ["quiet"; "stdout"; "stderr"; "error"; "warning"; "info"; "debug"]
    in
    let unknown = B0__fmt.(unknown' ~kind pp_level ~hint:must_be) in
    B0__fmt.error "%a" unknown (e, dom)

(* Default reporter *)

let header_stdout_style = [`Fg `Cyan]
let header_stderr_style = [`Fg `Cyan]
let header_err_style = [`Fg `Red]
let header_warn_style = [`Fg `Yellow]
let header_info_style = [`Fg `Blue]
let header_debug_style = [`Faint; `Fg `Magenta]
let error_style = `Bold :: header_err_style
let warning_style = `Bold :: header_warn_style
let info_style = `Bold :: header_info_style
let debug_style = `Bold :: header_debug_style

let pp_level_header level ppf header = match level with
| Stdout -> B0__fmt.st header_stdout_style ppf header
| Stderr -> B0__fmt.st header_stderr_style ppf header
| Error -> B0__fmt.st header_err_style ppf header
| Warning -> B0__fmt.st header_warn_style ppf header
| Info -> B0__fmt.st header_info_style ppf header
| Debug -> B0__fmt.st header_debug_style ppf header
| Quiet -> ()

let pp_level ppf level = match level with
| Stdout | Stderr -> ()
| Error -> B0__fmt.st error_style ppf "Error"
| Warning -> B0__fmt.st warning_style ppf "Warning"
| Info -> B0__fmt.st info_style ppf "Info"
| Debug -> B0__fmt.st debug_style ppf "Debug"
| Quiet -> ()

let exec =
  (* We use the name as given to execv because sometimes executables
     masquerade as others by execv'ing. We want to use the name of the
     program not of the masquerade (which may be meaningless). *)
  (* assert (Array.length Sys.argv > 0) *)
  Filename.basename Sys.argv.(0)

let default_kmsg k level msgf =
  msgf @@ fun ?header fmt ->
  let ppf = if level = Stdout then B0__fmt.stdout else B0__fmt.stderr in
  let finish ppf =
    Format.pp_close_box ppf (); Format.pp_close_box ppf ();
    B0__fmt.flush_nl ppf ();
    k ()
  in
  Format.pp_open_box ppf 0;
  begin match header with
  | None ->
      begin match level with
      | Stderr | Stdout -> Format.pp_open_box ppf 0
      | level ->
          (* "%s: @[%a: " *)
          B0__fmt.string ppf exec;
          B0__fmt.string ppf ": ";
          Format.pp_open_box ppf 0;
          pp_level ppf level;
          B0__fmt.string ppf ": ";
      end
  | Some header ->
      if header = "" then Format.pp_open_box ppf 0 else begin
        (* "%s: @[[%s] " *)
        B0__fmt.string ppf exec;
        B0__fmt.string ppf ": ";
        Format.pp_open_box ppf 0;
        B0__fmt.char ppf '[';
        pp_level_header level ppf header;
        B0__fmt.string ppf "] ";
      end
  end;
  B0__fmt.kpf finish ppf fmt

(* Log monitoring *)

let err_count' = Atomic.make 0
let err_count () = Atomic.get err_count'
let warn_count' = Atomic.make 0
let warn_count () = Atomic.get warn_count'

(* Log functions *)

type ('a, 'b) msgf =
  (?header:string -> ('a, Format.formatter, unit, 'b) format4 -> 'a) -> 'b

type 'a log = ('a, unit) msgf -> unit
type reporter = { kmsg : 'a 'b. (unit -> 'b) -> level -> ('a, 'b) msgf -> 'b }

let reporter_default = { kmsg = default_kmsg }
let reporter = Atomic.make reporter_default
let kmsg k level msgf = match level with
| Error ->
    Atomic.incr err_count';
    if Atomic.get current_level < Error then k () else
    (Atomic.get reporter).kmsg k level msgf
| Warning ->
    Atomic.incr warn_count';
    if Atomic.get current_level < Warning then k () else
    (Atomic.get reporter).kmsg k level msgf
| _ ->
    let current_level = Atomic.get current_level in
    if current_level = Quiet || current_level < level then k () else
    (Atomic.get reporter).kmsg k level msgf

let kunit _ = ()
let msg level msgf = kmsg kunit level msgf
let quiet msgf = kmsg kunit Quiet msgf
let stdout msgf = kmsg kunit Stdout msgf
let stderr msgf = kmsg kunit Stderr msgf
let err msgf = kmsg kunit Error msgf
let warn msgf = kmsg kunit Warning msgf
let info msgf = kmsg kunit Info msgf
let debug msgf = kmsg kunit Debug msgf

(* Logging result errors *)

let pp_err = B0__fmt.lines

let if_error ?(level = Error) ?header ~use = function
| Ok v -> v
| Error msg -> kmsg (fun _ -> use) level (fun m -> m ?header "%a" pp_err msg)

let if_error' ?(level = Error) ?header ~use = function
| Ok _ as v -> v
| Error msg -> kmsg (fun _ -> Ok use) level (fun m -> m ?header "%a" pp_err msg)

let if_error_pp pp ?(level = Error) ?header ~use = function
| Ok v -> v
| Error e -> kmsg (fun _ -> use) level (fun m -> m ?header "%a" pp e)

let if_error_pp' pp ?(level = Error) ?header ~use = function
| Ok _ as v -> v
| Error e -> kmsg (fun _ -> Ok use) level (fun m -> m ?header "%a" pp e)

(* Logging timings *)

(* The churn here is because we have a recursive dep on Os.Mtime *)

type time_func =
  { time :
      'a 'b. ?level:level ->
      ('a -> (('b, Format.formatter, unit, 'a) format4 -> 'b) -> 'a) ->
      (unit -> 'a) -> 'a }

let time_func_init = { time = fun ?(level = Info) m f -> assert false }
let time_func = Atomic.make time_func_init
let set_time_func time = Atomic.set time_func time
let time ?level fmt f = (Atomic.get time_func).time ?level fmt f

(* Values *)

let value ?(level = Stderr) ?id pp v = match id with
| None -> kmsg (fun _ -> v) level (fun m -> m "%a" pp v)
| Some id -> kmsg (fun _ -> v) level (fun m -> m "id: @[%a@]" pp v)

(* Module reporter *)

module Reporter = struct
  type t = reporter =
    { kmsg : 'a 'b. (unit -> 'b) -> level -> ('a, 'b) msgf -> 'b }

  let nop = let kmsg k level msgf = k () in { kmsg }
  let default = reporter_default
  let get () = Atomic.get reporter
  let set r = Atomic.set reporter r
end