Question

My problem concerns logging of library classes (classes that are used inside libraries), We are currently using log4cxx but the log4j library implements the same concepts.

Say i have a process that have several entities, A,B and C. Each of them use many different classes and functions, clearly separated in the code.

A,B and C use many library classes, functions, objects, resources and sometimes even globals (legacy code, nothing i can do about it...) - let us call them all foo

Logging A,B and C turned out to be a performance issue, the log gets blasted when we set the log level to debug. After viewing our system we came to these conclusions:

  1. We want to be able to change debug level for only one of the classes at a time (or all of them, using root)
  2. When all kind of foo print to log, we need to see which entity called it, A, B or C.
  3. As there are many foo we want to be able to change the debug level separately for each foo
  4. A foo should be treated as a common library, it can not depend directly on A,B or C.
  5. A,B and C might use the same instance of foo (for example, the same instance of our resource handling class is used A,B and C), in the log we would like to see which class used foo.
  6. A could use B (or C), but we dont have to see it in the log...

This is what we came up with so far -

A, B and C will have separate loggers. A global variable(kept in a different library with all of our logging helpers and wrappers) will always keep the current log reporting. Every time an entity starts handling it's logic, it sets the global variable to the proper logger. When a foo wants to report to the log, it reports through the global variable and adding it's name (and context) to the log message.

Problem is, it feels like there must be something that does this already, the solution does not feel clean, holding a global variable like that...

Are we doing something wrong here? Is there a better solution to this?

Was it helpful?

Solution

I do not know of an existing solution. I would probably come up with a logger with an interface like the following (whether it is a standalone implementation or just a wrapper on your existing one):

class Logger {
public:
    enum Level {
        ...
    };
    static Logger* Instance();
    void Log(Level level, const char* module, const char* msg);
    void AddModuleFilter(const char* context, const char* module, Level level);
    void SetThreadLocalContext(const char* context);
    ...
};

The main deviation from common log libraries are the context-sensitive module filter. We may have setups like the following to set different levels according to who makes the call (the context):

// foo calls by entity A have the DEBUG log level
Logger::Instance()->AddModuleFilter("A", "foo", Logger::DEBUG);
// foo calls by entity B have the WARNING log level
Logger::Instance()->AddModuleFilter("B", "foo", Logger::WARNING);

Then, the call flow will be like:

// In entity A
Logger::Instance()->SetThreadLocalContext("A");
// Call function foo
Logger::Instance()->Log(log_level, "foo", log_msg);

Not sure whether such an interface meets your purpose. Anyway, I always regard interface first a good approach. Once you have a clear interface, implementing it should be an easy job.

OTHER TIPS

If the separate library is itself a class, you can use a class-level variable in the library to hold the logger instance reference. I don't often program in C++ or Java, but I think a class-level variable is 'static' in C++ and Java.

It is still a global variable underneath, but at least it's class-scoped global variable (like a class variable named debugLog.log where debugLog is the class name).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top