问题
(extracted from ocaml: exposing a printf function in an object's method, so it can be answered independently)
I have the following (simplified) ocaml code, for a logger:
type log_level =
| Error
| Warn
| Info
let ord lvl =
match lvl with
| Error -> 50
| Warn -> 40
| Info -> 30
let current_level = ref (ord Warn)
let logf name lvl =
let do_log str =
if (ord lvl) >= !current_level then
print_endline str
in
Printf.ksprintf do_log
The logf function can be used with a printf format, as in:
logf "func" Warn "testing with string: %s and int: %d" "str" 42;
Is there any way to achieve the typical logging behaviour of only formatting arguments when they're actually needed? i.e something like:
let logf name lvl <args> =
if (ord lvl) >= !current_level then
Printf.printf <args>
I'm thinking it's because only the compiler knows how many arguments there will be in a format expression, and I guess there's no such thing as varargs in ocaml? So you can't ever define the full body of a printf function, you can only use currying and let the compiler magic figure it out. Is there any way to achieve what I want? Perhaps with mkprintf?
回答1:
The continuation functions such as Printf.kfprintf are provided specifically to allow format wrappers such as this. It looks something like this:
open Printf
type log_level = Error | Warn | Info
let ord = function Error -> 50 | Warn -> 40 | Info -> 30
let string_of_lvl = function
| Error -> "error"
| Warn -> "warn"
| Info -> "info"
let current_level = ref (ord Warn)
let printf_with_info name lvl =
kfprintf fprintf stdout "[<%s>] <%s>: " name (string_of_lvl lvl)
let logf name lvl =
if ord lvl >= !current_level then match lvl with
| Error | Warn -> printf
| Info -> printf_with_info name lvl
else
ifprintf stdout
回答2:
This seems to get me some of the way:
let logf name lvl =
if (ord lvl) >= !current_level then
Printf.fprintf stdout
else
Printf.ifprintf stdout
I'm surprised, as I thought this might mean !current_level would be evaluated too early (at module load time or something). But changing the current_level at runtime does seem to work correctly. I guess currying isn't pure like in haskell (which makes sense, given the impurity above ;)).
But it restricts me to using the fprintf family. I'd prefer to use ksprintf, because I also want a formatter, which (depending on log level) may add information like:
`[<name>] <level>: <message>`
Perhaps I could achieve this by concatenating formats (using ^^), but I don't see how.
回答3:
Whatever you do, calling any Printf.*printf funciton will parse the format string at runtime. This may change in 4.02. Or you can use some syntax extension for conditional printing.
来源:https://stackoverflow.com/questions/20413684/ocaml-printf-function-skip-formatting-entirely-if-some-condition-holds