Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Logging.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
(* Yoann Padioleau * * Copyright (C) 2020 r2c * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation, with the * special exception on linking described in file license.txt. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file * license.txt for more details. *) (*****************************************************************************) (* Prelude *) (*****************************************************************************) (* Small wrapper around the easy_logging library. * * My requirements for a logging library are: * (1) different log levels (Debug, Info, Warning, Error) * (2) easy logging calls with sprintf style formatting by default * (3) ability to log on stdout/stderr or in a file * (4) easy one-line logger creation * (5) hierarchical loggers where you can enable/disable a whole set * of loggers * (6) configurable with an external log config file * * There are a few OCaml logging libraries, but they usually miss one or more * of the requirements above: * - Logs: popular library according to OPAM metrics. However, Logs is heavily * functorized (no (4)), and it requires to encapsulate your logging calls * in a closure 'Log.info (fun m -> m "xxx")' (no (2)). It also lacks * (5) and (6). * - dolog: very basic logging library, very easy to use with (1), (2), * but lacks especially (5) and (6) * - Bolt: not updated since 2013 * - merlin/logging.ml: not available directly under OPAM and seems * more limited than easy_logging * - easy_logging: not really popular according to OPAM (no package depends * on it), use OCaml objects, some documentation but no good examples, * some unintuitive interfaces, some issues to compile without -42 * due to ambiguous constructors, 0.8 still not in OPAM, etc. * But, it supports all the requirements if you know how to use it! * => I use easy_logging (actually I use easy_logging_yojson for (6)) * *) open Easy_logging_yojson type level = Easy_logging__.Logging_types.level module Handlers = Easy_logging_yojson.Handlers class type logger = object (** {3 Classic logging Methods} Each of these methods takes an optional [string list] of tags, then a set of parameters the way a printf function does. If the log level of the instance is low enough, a log item will be created theb passed to the handlers. Example : {[logger#warning "Unexpected value: %s" (to_string my_value)]} *) method flash : 'a. ?tags:string list -> ('a, unit, string, unit) format4 -> 'a method error : 'a. ?tags:string list -> ('a, unit, string, unit) format4 -> 'a method warning : 'a. ?tags:string list -> ('a, unit, string, unit) format4 -> 'a method info : 'a. ?tags:string list -> ('a, unit, string, unit) format4 -> 'a method trace : 'a. ?tags:string list -> ('a, unit, string, unit) format4 -> 'a method debug : 'a. ?tags:string list -> ('a, unit, string, unit) format4 -> 'a (** {3 Lazy logging methods} Each of these methods takes a [string lazy_t] as an input (as well as the optional tags. If the log level of the instance is low enough, the lazy value will forced into a [string], a log item will be created then passed to the handlers. Example: {[logger#ldebug (lazy (heavy_calculation () ))]} *) method ldebug : ?tags:string list -> string lazy_t -> unit method ltrace : ?tags:string list -> string lazy_t -> unit method linfo : ?tags:string list -> string lazy_t -> unit method lwarning : ?tags:string list -> string lazy_t -> unit method lerror : ?tags:string list -> string lazy_t -> unit method lflash : ?tags:string list -> string lazy_t -> unit (** {3 String logging methods} Each of these methods takes a [string] as an input (as well as the optional tags). Example: {[logger#sdebug string_variable]} *) method sdebug : ?tags:string list -> string -> unit method strace : ?tags:string list -> string -> unit method sinfo : ?tags:string list -> string -> unit method swarning : ?tags:string list -> string -> unit method serror : ?tags:string list -> string -> unit method sflash : ?tags:string list -> string -> unit (** {3 Other methods} *) method name : string method internal_level : level method set_level : level -> unit (** Sets the log level of the logger instance. *) method add_handler : Handlers.t -> unit (** Adds a handler to the logger instance. *) method get_handlers : Handlers.t list method set_handlers : Handlers.t list -> unit method add_tag_generator : (unit -> string) -> unit (** Will add a tag to each log message, resulting from the call of the supplied fonction (called each time a message is logged)*) method set_propagate : bool -> unit (** Sets the propagate attribute, which decides whether messages passed to this logger are propagated to its ancestors' handlers. *) (** {4 Internal methods} *) method get_handlers_propagate : Handlers.t list (** Returns the list of handlers of the logger, recursing with parents handlers if propagate is true*) method effective_level : level (** Returns this logger level if it is not [None], else searches amongst ancestors for the first defined level; returns [NoLevel] if no level can be found. *) end (*****************************************************************************) (* Entry points *) (*****************************************************************************) let all_loggers = ref ([] : logger list) let apply_to_all_loggers f = List.iter (fun logger -> f logger) !all_loggers let get_loggers () = !all_loggers let set_global_level level = apply_to_all_loggers (fun logger -> logger#set_level level) let add_PID_tag () = let pid_string = Unix.getpid () |> string_of_int in apply_to_all_loggers (fun logger -> logger#add_tag_generator (fun () -> pid_string)) let get_logger xs : logger = let final_name = "Main" :: xs |> String.concat "." in let logger = Logging.get_logger final_name in all_loggers := logger :: !all_loggers; logger let load_config_file file = Logging.load_global_config_file file