any logger in core?

135 views
Skip to first unread message

Francois Berenger

unread,
Jul 12, 2012, 9:30:06 PM7/12/12
to ocaml...@googlegroups.com
Hi,

Is there a logger in core or recommended to use with core?

I used one which is derived from batteries logger but I'm
not so happy with it (I need to preprocess files to expand
the log directives).

Thanks,
F.

Yaron Minsky

unread,
Jul 12, 2012, 11:10:52 PM7/12/12
to ocaml...@googlegroups.com
There's a logging module in Async that we use internally, that will be
added to the public release in the next release, which should be
within a month.

y

Anil Madhavapeddy

unread,
Jul 13, 2012, 5:11:44 AM7/13/12
to ocaml...@googlegroups.com
Does it have a camlp4 facility to remove some logging statements at
compile-time (e.g. expensive debug prints), out of interest? This is
a pretty neat feature of Pa_lwt_log.

-anil

Yaron Minsky

unread,
Jul 13, 2012, 6:44:44 AM7/13/12
to ocaml...@googlegroups.com
Not as a camlp4 extension, and I'm not sure it's worthwhile in most
cases. Usually, if we want the ability to debug in our program, we
want it as a runtime option in production, not as a compile-time
option. So when we have code that generates errors that we may not
use, we use a type called Error.t, which is a lazy datastructure.
Thus, we can call:

Error.create "Blorb wasn't found among the list of blonks" (blorb,blonks)
<:sexp_of<Blorb.t * Blonk.t list>>

which runs in just a handful of nanos, and creates the Error. Only
when you actually serialize to a string do you pay the full conversion
costs. In our world, many errors are generated that aren't serialized
(they're serialized on demand when someone queries the system to look
at what's gone wrong), and this covers most of the horrible logging
cost.

y

Anil Madhavapeddy

unread,
Jul 14, 2012, 12:15:18 PM7/14/12
to ocaml...@googlegroups.com, dave....@eu.citrix.com
This is really nice, as the caller could render the Error in a more
structured way (e.g. in a js_of_ocaml-driven page). The Pa_lwt_log is a
simple extension that just converts a "log" entry into a "if level then
log" equivalent.

-anil

Francois Berenger

unread,
Nov 1, 2012, 9:19:39 PM11/1/12
to ocaml...@googlegroups.com, dave....@eu.citrix.com
Hello,

Is there a logger in the current release of core?

Thanks,
F.

Yaron Minsky

unread,
Nov 1, 2012, 9:33:40 PM11/1/12
to ocaml...@googlegroups.com, dave....@eu.citrix.com
Yes: if you open Async.Std, it's called "Log".

y

Francois Berenger

unread,
Nov 1, 2012, 9:40:40 PM11/1/12
to ocaml...@googlegroups.com, dave....@eu.citrix.com
OK, I'm hooked now.

I plus core in my new project.

Thanks!
F.

Francois Berenger

unread,
Nov 1, 2012, 9:59:45 PM11/1/12
to ocaml...@googlegroups.com, dave....@eu.citrix.com
Is there some example program using this logger?

Yaron Minsky

unread,
Nov 1, 2012, 10:14:35 PM11/1/12
to ocaml...@googlegroups.com, dave....@eu.citrix.com, Sean McLaughlin
Sean, does your omake-server use Async.Log? That might be a good example.

y

Sean McLaughlin

unread,
Nov 3, 2012, 6:43:03 PM11/3/12
to Yaron Minsky, ocaml...@googlegroups.com, dave....@eu.citrix.com
No, sorry. I didn't know about Async.Log, and I wrote my own logger.
Switching to this one is probably a good idea in the long run for
omake-server.

Yaron Minsky

unread,
Nov 4, 2012, 7:14:34 AM11/4/12
to Sean McLaughlin, ocaml...@googlegroups.com, dave....@eu.citrix.com
Don't feel too bad about knowing about it.  It's quite new.

But yeah, moving to it eventually sounds right.

Francois Berenger

unread,
Nov 5, 2012, 1:08:31 AM11/5/12
to ocaml...@googlegroups.com, Sean McLaughlin, dave....@eu.citrix.com

Is there some ocamldoc for it?

I think I managed to compile the ocamldoc for core and async, but did
not find anything about a Log module into it.

David House

unread,
Nov 5, 2012, 6:15:22 AM11/5/12
to ocaml...@googlegroups.com, Sean McLaughlin, dave....@eu.citrix.com
Judging from the mli, there should be some ocamldocs, yes.

However, I recommend just looking at the mli.

On Mon, Nov 5, 2012 at 6:08 AM, Francois Berenger

Yaron Minsky

unread,
Nov 5, 2012, 10:27:18 AM11/5/12
to ocaml...@googlegroups.com, Sean McLaughlin, dave....@eu.citrix.com

Francois Berenger

unread,
Nov 5, 2012, 9:51:25 PM11/5/12
to ocaml...@googlegroups.com
Argh! I'm forced to read an mli file. ;)

Yaron Minsky

unread,
Nov 5, 2012, 10:12:00 PM11/5/12
to ocaml...@googlegroups.com
No, did you see my link? You can read the ocamldocs:

https://ocaml.janestreet.com/ocaml-core/108.07.01/doc/async_extra/Log.html

On Mon, Nov 5, 2012 at 9:51 PM, Francois Berenger

Francois Berenger

unread,
Nov 6, 2012, 8:59:53 PM11/6/12
to ocaml...@googlegroups.com
Yes I saw it, thanks.

I'll also take a look at the .mli file, as I was advised to do so by
David House.
I'll try to make an example program to check I can use it correctly.

Francois Berenger

unread,
Nov 7, 2012, 2:18:21 AM11/7/12
to ocaml...@googlegroups.com
Hello,

How can I construct a value of type Async_extra.Log.Level
and an Async_extra.Log.Output?

Sorry for the stupid question but I have never used
polymorphic variants.

If you can point me to some simple explanation
on them, I will go and read it too
(I looked at http://caml.inria.fr/pub/docs/manual-ocaml/manual006.html
but it does not tell me why I should use them, just how).

Thanks a lot,
F.

Francois Berenger

unread,
Nov 7, 2012, 3:34:07 AM11/7/12
to ocaml...@googlegroups.com
I could create a test program.

It compiles, at least, but has the funny property of not logging anything out.

---
module Log = Async_extra.Log

let main () =

let logger = Log.create (Log.Level.of_string "Debug") [Log.Output.screen] in

Log.raw logger "%s\n" "raw log";
Log.info logger "%s\n" "info log";
Log.error logger "%s\n" "error log";
Log.debug logger "%s\n" "debug log";

Log.close logger
;;

main()
---

Here is the oasis file for it:
---
OASISFormat: 0.3
Name: Toto
Version: 0.1
Synopsis: Titi
Authors: Me
License: LGPL-3
Plugins: META (0.3), DevFiles (0.3)

Executable logger_test
Path: .
BuildDepends: async_extra
BuildTools: ocamlbuild
CompiledObject: native
MainIs: logger_test.ml
---

I was thinking all logs would print right away.

Regards,
F.

David House

unread,
Nov 7, 2012, 4:55:59 AM11/7/12
to ocaml...@googlegroups.com
You need to start the scheduler. You should do this in all async
programs, or else nothing will get run. Creating a log creates an
async Pipe.t, and doing Log.foo puts things onto that Pipe.t. There is
an async job to read things off of the pipe and to write them out. But
if you don't start the scheduler, this job won't get run. So add the
following at bottom of your code:

let () =
main ();
never_returns (Scheduler.go ())

The reason this is done, rather than just writing immediately, is in
case the write blocks, which would halt the whole process. If you
decide you don't care about this risk (e.g. if you're writing to a
terminal then it's not very likely), you can use Log.Blocking.

Also, a smaller point, it's more idiomatic to write "open Async.Std"
rather than referring directly to one of the three async
sub-libraries.

On Wed, Nov 7, 2012 at 8:34 AM, Francois Berenger

Yaron Minsky

unread,
Nov 7, 2012, 8:37:56 AM11/7/12
to ocaml...@googlegroups.com
You need to call Scheduler.go () to get the async scheduler running.

On Wed, Nov 7, 2012 at 3:34 AM, Francois Berenger

Francois

unread,
Nov 7, 2012, 10:48:14 PM11/7/12
to ocaml...@googlegroups.com


On Wednesday, November 7, 2012 6:57:04 PM UTC+9, David House wrote:
You need to start the scheduler. You should do this in all async
programs, or else nothing will get run. Creating a log creates an
async Pipe.t, and doing Log.foo puts things onto that Pipe.t. There is
an async job to read things off of the pipe and to write them out. But
if you don't start the scheduler, this job won't get run. So add the
following at bottom of your code:

let () =
  main ();
  never_returns (Scheduler.go ())

I don't know where to find never_returns.

But the following did work (and never stop):

---
module Log   = Async_extra.Log
module Sched = Async.Std.Scheduler


let main () =

  let logger = Log.create (Log.Level.of_string "Debug") [Log.Output.screen] in

  Log.raw   logger "%s\n" "raw log";
  Log.info  logger "%s\n" "info log";
  Log.error logger "%s\n" "error log";
  Log.debug logger "%s\n" "debug log";

  Log.close logger
;;

main();
Sched.go ()
---
 

The reason this is done, rather than just writing immediately, is in
case the write blocks,

I see.

 
which would halt the whole process. If you
decide you don't care about this risk (e.g. if you're writing to a
terminal then it's not very likely), you can use Log.Blocking.

I'll try to use that.


Also, a smaller point, it's more idiomatic to write "open Async.Std"
rather than referring directly to one of the three async
sub-libraries.

I'm affraid of open directives, I try to keep my code _very_ explicit
about what it is doing and which function from which module is used
(maybe because of past overexposure to some C++ code).

For the moment, I use the stdlib + batteries
+ (async and async_extra for just the logger).

In some other future thread, I might ask for some help on the
best way to remove batteries, then remove the stdlib,
hopefully to just depend on core and async in the future.

Thanks for all the help,
Francois.
 

David House

unread,
Nov 8, 2012, 5:12:21 AM11/8/12
to ocaml...@googlegroups.com
On Thu, Nov 8, 2012 at 3:48 AM, Francois
<francois.b...@gmail.com> wrote:
> I don't know where to find never_returns.

It's there if you open Core.Std.

> But the following did work (and never stop):

You need to explicitly shut down async using the shutdown function in Async.Std.

> I'm affraid of open directives, I try to keep my code _very_ explicit
> about what it is doing and which function from which module is used
> (maybe because of past overexposure to some C++ code).

I think that's exactly the right approach -- I often find myself
making similar comments when doing code review at work. Things are
much easier to follow if opens are reduced, or made more local, and
more explicit.

That being said, I do allow myself the luxury of opening Core.Std and
Async.Std in most of my modules that use core / async. I find this to
strike a good balance between concision and explicitness.

One of the reasons is that there are very few *values* brought into
scope by opening Core.Std and Async.Std. This conversation has contain
disproportionally many: never_returns, shutdown, etc. -- an unlucky
coincidence! But nearly everything is squirreled away inside a module,
which helps a lot. (In other words, our "Pervasives" is much smaller
than the ocaml standard library's.)

Francois Berenger

unread,
Nov 13, 2012, 3:02:43 AM11/13/12
to ocaml...@googlegroups.com
The choice in log levels is a little scarce.

Currently:
raw (I don't know it's level, I guess it's always printed but I may be wrong)
then, ordered by my intuitive notion of log priority:
debug < info < error

I'm used to:
debug < info < warn < error < fatal

So, I miss the warning and fatal log levels.
But, that's just based on my experience.

Regards,
F.

David House

unread,
Nov 13, 2012, 3:41:56 AM11/13/12
to ocaml...@googlegroups.com
Hmm, I am surprised you want so many!

I claim there is a cost in allowing tons and tons of different log
levels. Firstly, it complicates the interface. Secondly, it leads to
different applications choosing different logging levels for
essentially the same errors. E.g. how do you choose whether some
particular failure is an error, or a fatal? Isn't it likely that
someone else will make a different choice? There are lines that you
can draw, but it's a big grey area. Having fewer choices means that
everyone's programs are more consistent with respect to each other.

Putting it another way: three logging levels should be enough for anyone! :)

Malcolm Matalka

unread,
Nov 13, 2012, 4:27:40 AM11/13/12
to ocaml...@googlegroups.com
I agree with David. The current logger I am using at work as 5 or 6 log
levels and I have found little value in it. I think 'fatal' is
especially devilish since, in such a situation, your monitoring tools
should be the ones determining that.

/M

David House

unread,
Nov 13, 2012, 4:34:03 AM11/13/12
to ocaml...@googlegroups.com
Also, there is a function for logging fatal errors. It's called failwith :)

Francois Berenger

unread,
Nov 13, 2012, 8:40:42 PM11/13/12
to ocaml...@googlegroups.com
On Tuesday, November 13, 2012 6:34:05 PM UTC+9, David House wrote:
Also, there is a function for logging fatal errors. It's called failwith :)

failwith messages do not come with a timestamp.

Francois Berenger

unread,
Nov 13, 2012, 8:44:35 PM11/13/12
to ocaml...@googlegroups.com


On Tuesday, November 13, 2012 5:43:01 PM UTC+9, David House wrote:
Hmm, I am surprised you want so many!

I claim there is a cost in allowing tons and tons of different log
levels. Firstly, it complicates the interface. Secondly, it leads to
different applications choosing different logging levels for
essentially the same errors. E.g. how do you choose whether some
particular failure is an error, or a fatal?

After a fatal error, the program cannot continue anymore.
Think about a "fatal injury".
I see a grey zone between warn and error, but I think
programmers are usually smart and can be trusted.

Isn't it likely that
someone else will make a different choice? There are lines that you
can draw, but it's a big grey area. Having fewer choices means that
everyone's programs are more consistent with respect to each other.

I agree it's a grey zone.
But the current choice is drastic.
 
Reply all
Reply to author
Forward
0 new messages