Skip to content
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

Make log4cxx optional (?) #30

Open
thomas-moulard opened this issue Oct 2, 2013 · 18 comments
Open

Make log4cxx optional (?) #30

thomas-moulard opened this issue Oct 2, 2013 · 18 comments

Comments

@thomas-moulard
Copy link
Member

No description provided.

@bchretien
Copy link
Member

Another possibility would be to use Boost's logger, since Boost is already a dependency. But then we can also make the Boost logger support optional.

@thomas-moulard
Copy link
Member Author

Yeah but the thing is that Boost Logger is still too new to be considered :(
(not available on Ubuntu 12.04)

@bchretien
Copy link
Member

I think that Boost >= 1.48 is required to install the former library, so it would just be another dependency. I don't know if there's some APT packages for it for pre-Boost 1.54 systems though.

@bchretien
Copy link
Member

Some Windows users (poke @aescande) would also like to see the dependency made optional (or removed), to make Windows compilation easier. I guess we could wrap the log4cxx calls in some generic logging macros (LOGGER_INIT, LOGGER_INFO, LOGGER_ERROR etc.). Thus, disabling the logger or even changing the logger library would be fairly straightforward.

@bchretien
Copy link
Member

Ok, this is more or less the case in core/debug.hh, so this should be even easier than expected.

@thomas-moulard
Copy link
Member Author

After thinking about it, I think that having one handler stored as a static pointer to function is fine. If you want to enable logging then you got to define your handler. We can provide one printing on stdout by default. This is good enough I think :)

@bchretien
Copy link
Member

I guess we need at least debug, info and error. With static pointers to functions, users could log info to a file and error to stderr for instance.

@thomas-moulard
Copy link
Member Author

Yeah but this can just be an enum to keep a simple interface.
Then the dispatch can be done into the class somehow...

@bchretien
Copy link
Member

We can consider something like:

// Register logging functions
registerLogger (INFO, ...);
registerLogger (ERROR, ...);
...

// Log some information
log (INFO, "something amazing happened");

@thomas-moulard
Copy link
Member Author

Having this API is fine, my recommendation is to only have one underlying static variable to minimize issues with initialization/destruction as well as potential concurrency issues with multi-threading.

It is reasonable to make the assumption that you don't change the logger while solving a problem but the log method must be thread safe...

@bchretien
Copy link
Member

We can also have multiple logger instances defined by some identifier (e.g. string such as "roboptim.core").

getLogger ("roboptim.core").log (INFO, "log message");

This seems to be what log4cxx is doing. This can be done with a unique global map, and getLogger takes care of logger creation when necessary.

Thread-safety is indeed my main concern (and the usual thorn in the side of developers when dealing with loggers).

@thomas-moulard
Copy link
Member Author

This is why separating the issues is important. A logger is two things:

  1. A singleton (difficult to make it thread safe)
  2. And a class (which can be thread safe, efficiency is another, separate, concern)

My suggestion is keeping 1. minimum, one static object, one function to set it.
Once this is done, then 2. can be implemented.

For 1. I feel that omniorb had it right:

http://omniorb.sourceforge.net/omni40/omniORB/omniORB004.html

namespace omniORB {
typedef void (logFunction)(const char);
void setLogFunction(logFunction f);
};

You may want to make it slightly more complicated with severities but not too much more.

The for 2. lockless structures in boost can be the solution. Then you got that then you can write on the disk whatever you want later.

@bchretien
Copy link
Member

Another thing to consider: a simple const char-based logger usually requires an extra step on the user's side to handle stringstream expressions (e.g. "matrix: " << mat). I guess log4cxx handles that in their macros that prefix the logged expression with a stringstream and returns the resulting string.

@thomas-moulard
Copy link
Member Author

Converting a matrix to a string is too slow to be done synchronously so ultimately, it is better either to copy (for small objects) or to keep a shared pointer (for bigger ones, if possible) so that you can do that separately.

One solution is to add directly into the logger the possibility to log basic types such as double, matrices and vectors to defer the transformation.

Ultimately if the data volume is too important, relying on binary logging may be needed.

@bchretien
Copy link
Member

This was just a dummy example, plus logging should be kept to a minimum in most cases (except in debugging scenarios where performance does not really matter).

Being able to store expressions simply containing references/pointers to large data would indeed be an interesting solution, but this is probably overkill in this case (and probably requires a lot of work). This could probably be part of a standalone logging library that could then be used in the user-defined logging functions considered here, but this would imply using a more complex log function signature...

I think that for now, we should keep things simple (thread-safe, handling strings and stringstream expressions), and leave the heavy-lifting to the user-defined log functions (possibly calling Boost.Log, log4cxx etc.).

@thomas-moulard
Copy link
Member Author

Yeah, of course. My only point is that if you make an interface at some point, it is better to have it take directly the object you want to log (matrices...) than strings as it will allow you to process them on the logger side.

@bchretien
Copy link
Member

For this, defining a boost::variant with supported types and expecting a user-defined visitor that describes how to deal with different types may be a solution. This just makes the user's job more difficult, since one needs to define how to deal with strings, matrices, etc.

@thomas-moulard
Copy link
Member Author

Yeah something like that. I would be more in favor to just use an abstract class to avoid the maintenance cost of the template. A more complex design could allow the user to register its own type but again this seems a bit too much knowing that we mainly log vector and matrices.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants