I implemented the logger 2 months ago. I know I should review and summarize it. Here is place holder now, so I won’t forget about this post. Hope I will finish it by next week.

Why I need a logger?

This is a review for the logger implementation. At the beginning, I want to log my program to see see heartbeat in every 0.5 sec, and track its performance. It is the first logger at AirLogger. It is very straightforward: Open a file, start to write string, and until I close it. Honestly, I wrote a terrible logger class.

[x] Singleton Pattern. It’s barely a good singleton design.
[x] Log level. I defined 5 log levels to provide different depth message
[x] Macro function wrapper. Use macro to wrap logger function itself
[ ] Thread-safe
[ ] I/O control Just Open and leave it there, thank I remember to close it before it ends This bad File IO slow my program from 20fps camera capture reduce to 10fps.
[ ] precision timestamp. I used localtime() routine, but it is in second.
Appropriate message format.

After a few weeks, I need a real logger at my work. It is not just heart beat, but also write different level info into files. It should has a very high precision timestamp in millisecond, even nano second level. I want to extract data from each video frame and write into file with this utility. The video is about 30fps or 60 maybe, 1080p. I need to record the timestamp of each frame. If it is real-time camera. The timestamp should be the clock epoch, in microseconds or nanosecond precision. For future work, I want this tool can measure the time elapse for a code block. On my understanding, I will identify a block of code that highly hog the CPU and could processed at GPGPU or DSP. After modify this code block, use CUDA/OpenCL to make code in parallel programming. then we can compare what is different between them.

What I changed

So consider others implementation, here is something I improved on my logger class.

  1. Move getInstance to header, and keep header as clean as possible.
  2. disable copy constructor and assignment operators

    MetaLogger(MetaLogger const&) = delete;
    MetaLogger(MetaLogger&&) = delete;
    MetaLogger& operator=(MetaLogger const&) = delete;
    MetaLogger& operator=(MetaLogger &&) = delete;
  3. Use enum to define Logger Level instead of a type struct

  4. Instead of use a log() and switch condition for different level. Make each level log method as deprecated method. And try to use parameter format list to format message. Like this:

    std::string error(const char* fat, ...)
  5. Make default destructor as protected

    ~MetaLogger() = default
  6. each error() or info() only provide the ostream, and use another write(msg) to format the final message.

  7. Corresponding to last one, make logger I/O as another class. metaLogger only format log message, but call another fileLogger to handle Output stream.
  8. Use constant size buffer

    static const int MAX_BUFF 512
  9. Use a mutex lock to access the text buffer

  10. Use a better macro wrap to delegate my write function

    #define LOG_ERROR_(fmt, ...) \
    m_logger_error("0x%08x](%s:%s:%d)" fat, std::this_thread::get_id(), FILENAME, __FUNCTION__, __LINE__, ##__VA_ARGS__)
  11. use va_list, va_start to delegate the debugging message.

  12. Multi-threading. The original implementation REALLY slow everything down. However, even with multi-threading, I can’t write this message into file very time it calls. Another thread-safe queue.

Key Components

  • Singleton
  • Threading
  • Tread-safe queue

Thanks for the following reference:
Logging In C++
Logger in Java
Design Patterns: Elements of Reusable Object-Oriented Software