@alin, try using this program as a sample:
let () =
Lwt_log_core.default :=
Lwt_log.channel
~template:"$(date).$(milliseconds) [$(level)] $(message)"
~close_mode:`Keep
~channel:Lwt_io.stdout
();
Lwt_log_core.add_rule "*" Lwt_log_core.Info;
Lwt_main.run begin
Lwt_log_core.info "blah"
end
(* ocamlfind opt -linkpkg -package lwt.unix foo.ml && ./a.out *)
It produces output like this:
May 31 18:49:30.863 [info] blah
The Lwt_log docs are admittedly confusing, as they are split between two modules: Lwt_log_core and Lwt_log. This also got me once, because I last used Lwt_log heavily before it was split up like this.
In the program, I deliberately used Lwt_log_core for things that come from that module, so you can quickly find out what’s documented there, and what’s documented in Lwt_log. Lwt_log_core is actually included in Lwt_log, however, so normally one would just use Lwt_log to qualify all the identifiers.
EDIT: and thanks for asking. Another useful reminder to improve these docs 
EDIT 2: I also want to add that the template can be more elaborate when you are using a logger from Lwt_log, as you can see in the Lwt_log docs: