-
Notifications
You must be signed in to change notification settings - Fork 14
structured logging #59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
565a6b7
d03366f
b690a38
5f31b43
bf3f875
32f1238
54fef64
6ce2f43
c87470a
0a08364
499d16f
30c5a5f
0ecebfe
554c1c8
6ecc1d6
6a546df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,6 @@ open Prelude | |
|
|
||
| (** Global logger state *) | ||
| module State = struct | ||
|
|
||
| let all = Hashtbl.create 10 | ||
| let default_level = ref (`Info : Logger.level) | ||
|
|
||
|
|
@@ -77,30 +76,66 @@ module State = struct | |
| let output_ch ch = | ||
| fun str -> try output_string ch str; flush ch with _ -> () (* logging never fails, most probably ENOSPC *) | ||
|
|
||
| let format_simple level facil msg = | ||
| let format_simple_full level facil ts pairs msg = | ||
| let pid = Unix.getpid () in | ||
| let tid = U.gettid () in | ||
| let pinfo = if pid = tid then sprintf "%5u:" pid else sprintf "%5u:%u" pid tid in | ||
| sprintf "[%s] %s [%s:%s] %s\n" | ||
| (Time.to_string ~gmt:!utc_timezone ~ms:true (Unix.gettimeofday ())) | ||
| let pairs_str = match pairs with [] -> "" | _ -> " " ^ Logfmt.to_string pairs in | ||
| sprintf "[%s] %s [%s:%s] %s%s\n" | ||
| (Time.to_string ~gmt:!utc_timezone ~ms:true ts) | ||
| pinfo | ||
| facil.Logger.name | ||
| (Logger.string_level level) | ||
| msg | ||
| pairs_str | ||
|
|
||
| let format_logfmt level facil ts pairs msg = | ||
| let pairs = ("msg", msg) :: pairs in | ||
| let pid = Unix.getpid () in | ||
| let tid = U.gettid () in | ||
| let pairs = | ||
| if pid = tid then ("pid", string_of_int pid) :: pairs | ||
| else ("pid", string_of_int pid) :: ("tid", string_of_int tid) :: pairs | ||
| in | ||
| let pairs = | ||
| ("time", Time.to_string ~gmt:!utc_timezone ~ms:true ts) :: | ||
| ("level", Logger.string_level level) :: | ||
| ("facil", facil.Logger.name) :: | ||
| pairs | ||
| in | ||
| let buf = Buffer.create 32 in | ||
| Logfmt.add_to_buffer buf pairs; | ||
| Buffer.add_char buf '\n'; | ||
| Buffer.contents buf | ||
|
|
||
| open struct | ||
| let cur_format: ([`Plain|`Logfmt]*_) Atomic.t = Atomic.make (`Plain, format_simple_full) | ||
| let set_cur_format f = Atomic.set cur_format f | ||
| end | ||
| let get_cur_format () = Atomic.get cur_format | ||
| let is_structured_format () = match get_cur_format () with `Plain, _ -> false | `Logfmt, _ -> true | ||
| let set_plaintext () = set_cur_format (`Plain, format_simple_full) | ||
| let set_logfmt () = set_cur_format (`Logfmt, format_logfmt) | ||
|
|
||
| let format level facil ts pairs msg = | ||
| (snd (Atomic.get cur_format)) level facil ts pairs msg | ||
|
|
||
| let format_simple level facil msg = | ||
| format level facil (Unix.gettimeofday()) [] msg | ||
|
|
||
| let log_ch = stderr | ||
| let () = assert (Unix.descr_of_out_channel stderr = Unix.stderr) | ||
| let base_name = ref "" | ||
|
|
||
| let hook = ref (fun _ _ _ -> ()) | ||
| let output_simple level facil s = !hook level facil s; output_ch log_ch s | ||
|
|
||
| module Put = Logger.PutSimple( | ||
| struct | ||
| let format = format_simple | ||
| let output = fun level facil s -> let () = !hook level facil s in output_ch log_ch s | ||
| end) | ||
| let put = Logger.put_simple { | ||
| format; | ||
| output = output_simple; | ||
| } | ||
|
|
||
| module M = Logger.Make(Put) | ||
| (** Main logger, writes into [put] *) | ||
| let logger = Logger.make put | ||
|
|
||
| let self = "lib" | ||
|
|
||
|
|
@@ -117,11 +152,23 @@ module State = struct | |
| (fun () -> Unix.dup2 (Unix.descr_of_out_channel ch) Unix.stderr) | ||
| () | ||
| with | ||
| e -> M.warn (facility self) "reopen_log_ch(%s) failed : %s" file (Printexc.to_string e) | ||
| e -> | ||
| let now = (Unix.gettimeofday ()) in | ||
| logger.warn (facility self) now [] "reopen_log_ch(%s) failed : %s" file (Printexc.to_string e) | ||
|
|
||
| end | ||
|
|
||
| include State.M | ||
| let debug_s = State.logger.debug_s | ||
| let info_s = State.logger.info_s | ||
| let warn_s = State.logger.warn_s | ||
| let error_s = State.logger.error_s | ||
| let critical_s = State.logger.critical_s | ||
| let put_s = State.logger.put_s | ||
| let debug = State.logger.debug | ||
| let info = State.logger.info | ||
| let warn = State.logger.warn | ||
| let error = State.logger.error | ||
| let critical = State.logger.critical | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to export these here, they are overridden below with make_s
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the ones below are only visible within the class, they're not at toplevel. But I could remove |
||
|
|
||
| let facility = State.facility | ||
| let set_filter = State.set_filter | ||
|
|
@@ -142,43 +189,48 @@ let read_env_config = State.read_env_config | |
| (** | ||
| param [lines]: whether to split multiline message as separate log lines (default [true]) | ||
|
|
||
| param [backtrace]: whether to show backtrace if [exn] is given (default is [false]) | ||
| param [backtrace]: whethgter to show backtrace if [exn] is given (default is [false]) | ||
|
rr0gi marked this conversation as resolved.
Outdated
|
||
|
|
||
| param [saved_backtrace]: supply backtrace to show instead of using [Printexc.get_backtrace] | ||
|
|
||
| param [pairs] key/value pairs to add to the line, unconditionally | ||
|
|
||
| param [structured_pairs] key/value pairs to use for structured log formats only. Plain logging will discard. | ||
| *) | ||
| type 'a pr = ?exn:exn -> ?lines:bool -> ?backtrace:bool -> ?saved_backtrace:string list -> ('a, unit, string, unit) format4 -> 'a | ||
| type 'a pr = ?exn:exn -> ?lines:bool -> ?backtrace:bool -> ?saved_backtrace:string list -> ?ts:Time.t -> ?structured_pairs:Logger.Pairs.t -> ?pairs:Logger.Pairs.t -> ('a, unit, string, unit) format4 -> 'a | ||
|
|
||
| class logger facil = | ||
| let make_s output_line = | ||
| let make_s (output_line:Logger.facil -> Time.t -> Logger.Pairs.t -> string -> unit) = | ||
| let output = function | ||
| | true -> | ||
| fun facil s -> | ||
| fun facil ts pairs s -> | ||
| if String.contains s '\n' then | ||
| List.iter (output_line facil) @@ String.nsplit s "\n" | ||
| List.iter (output_line facil ts pairs) @@ String.nsplit s "\n" | ||
| else | ||
| output_line facil s | ||
| output_line facil ts pairs s | ||
| | false -> output_line | ||
| in | ||
| let print_bt lines exn bt s = | ||
| output lines facil (s ^ " : exn " ^ Exn.str exn ^ (if bt = [] then " (no backtrace)" else "")); | ||
| List.iter (fun line -> output_line facil (" " ^ line)) bt | ||
| let print_bt lines exn bt ts pairs s = | ||
| output lines facil ts pairs (s ^ " : exn " ^ Exn.str exn ^ (if bt = [] then " (no backtrace)" else "")); | ||
| List.iter (fun line -> output_line facil ts pairs (" " ^ line)) bt | ||
| in | ||
| fun ?exn ?(lines=true) ?(backtrace=false) ?saved_backtrace s -> | ||
| fun ?exn ?(lines=true) ?(backtrace=false) ?saved_backtrace ?(ts=Unix.gettimeofday()) ?(structured_pairs=[]) ?(pairs=[]) s -> | ||
| let pairs = if structured_pairs!=[] && State.is_structured_format () then List.rev_append structured_pairs pairs else pairs in | ||
|
rr0gi marked this conversation as resolved.
Outdated
rr0gi marked this conversation as resolved.
Outdated
|
||
| try | ||
| match exn with | ||
| | None -> output lines facil s | ||
| | None -> output lines facil ts pairs s | ||
| | Some exn -> | ||
| match saved_backtrace with | ||
| | Some bt -> print_bt lines exn bt s | ||
| | Some bt -> print_bt lines exn bt ts pairs s | ||
| | None -> | ||
| match backtrace with | ||
| | true -> print_bt lines exn (Exn.get_backtrace ()) s | ||
| | false -> output lines facil (s ^ " : exn " ^ Exn.str exn) | ||
| | true -> print_bt lines exn (Exn.get_backtrace ()) ts pairs s | ||
| | false -> output lines facil ts pairs (s ^ " : exn " ^ Exn.str exn) | ||
| with exn -> | ||
| output_line facil (sprintf "LOG FAILED : %S with message %S" (Exn.str exn) s) | ||
| output_line facil ts pairs (sprintf "LOG FAILED : %S with message %S" (Exn.str exn) s) | ||
| in | ||
| let make output ?exn ?lines ?backtrace ?saved_backtrace fmt = | ||
| ksprintf (fun s -> output ?exn ?lines ?backtrace ?saved_backtrace s) fmt | ||
| let make : _ -> _ pr = fun output ?exn ?lines ?backtrace ?saved_backtrace ?ts ?structured_pairs ?pairs fmt -> | ||
| ksprintf (fun s -> output ?exn ?lines ?backtrace ?saved_backtrace ?ts ?structured_pairs ?pairs s) fmt | ||
| in | ||
| let debug_s = make_s debug_s in | ||
| let warn_s = make_s warn_s in | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
|
|
||
| let[@inline] needs_escape c = | ||
| Char.code c < 0x20 || c = '"' || c = '\\' | ||
|
|
||
| let[@inline] needs_quotes c = | ||
| c = ' ' || Char.code c >= 0x80 | ||
|
|
||
| type cat = Safe | Has_space | Needs_escape | ||
|
|
||
| let categorize s : cat = | ||
| let quote = ref false in | ||
|
|
||
| try | ||
| for i=0 to String.length s-1 do | ||
| let c = String.unsafe_get s i in | ||
| if needs_escape c then raise_notrace Exit; | ||
| if needs_quotes c then quote := true | ||
| done; | ||
| if !quote then Has_space else Safe | ||
| with Exit -> Needs_escape | ||
|
|
||
| let add_pair buf k v = | ||
| Buffer.add_string buf k; | ||
| Buffer.add_char buf '='; | ||
| match categorize v with | ||
| | Safe -> Buffer.add_string buf v | ||
| | Has_space -> Printf.bprintf buf {|"%s"|} v | ||
| | Needs_escape -> Printf.bprintf buf "%S" v | ||
|
|
||
| let rec add_to_buffer buf (pairs:Logger.Pairs.t) : unit = | ||
| match pairs with | ||
| | [] -> () | ||
| | [k,v] -> add_pair buf k v | ||
| | (k,v) :: pairs -> add_pair buf k v; Buffer.add_char buf ' '; add_to_buffer buf pairs | ||
|
|
||
| let to_string pairs = match pairs with | ||
| | [] -> "" | ||
| | _ -> | ||
| let buf = Buffer.create 32 in | ||
| add_to_buffer buf pairs; | ||
| Buffer.contents buf |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
|
|
||
| val add_to_buffer : Buffer.t -> Logger.Pairs.t -> unit | ||
| val to_string : Logger.Pairs.t -> string |
Uh oh!
There was an error while loading. Please reload this page.