Skip to content

Custom Sinks

Under certain circumstances, it is useful to send the log output to a destination other than a file, stderr and/or stdout. In case, the library provides the google::LogSink interface whose implementations can be used to write the log output to arbitrary locations.

Basic Interface

The sink interface is defined as follows:

class LogSink {
 public:
  virtual void send(LogSeverity severity, const char* full_filename,
                    const char* base_filename, int line,
                    const LogMessageTime& time, const char* message,
                    size_t message_len);
};

The user must implement google::LogSink::send, which is called by the library every time a message is logged.

Possible deadlock due to nested logging

This method can't use LOG() or CHECK() as logging system mutex(s) are held during this call.

Registering Log Sinks

To use the custom sink and instance of the above interface implementation must be registered using google::AddLogSink which expects a pointer to the google::LogSink instance. To unregister use google::RemoveLogSink. Both functions are thread-safe.

LogSink ownership

The google::LogSink instance must not be destroyed until the referencing pointer is unregistered.

Direct Logging

Instead of registering the sink, we can directly use to log messages. While LOG_TO_SINK(sink, severity) allows to log both to the sink and to a global log registry, e.g., a file, LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity) will avoid the latter.

Using a custom sink

custom_sink.cc
#include <glog/logging.h>

#include <algorithm>
#include <cstddef>
#include <iostream>
#include <iterator>

namespace {

struct MyLogSink : google::LogSink {  // (1)!
  void send(google::LogSeverity severity, const char* /*full_filename*/,
            const char* base_filename, int line,
            const google::LogMessageTime& /*time*/, const char* message,
            std::size_t message_len) override {
    std::cout << google::GetLogSeverityName(severity) << ' ' << base_filename
              << ':' << line << ' ';
    std::copy_n(message, message_len,
                std::ostreambuf_iterator<char>{std::cout});
    std::cout << '\n';
  }
};

}  // namespace

int main(int /*argc*/, char** argv) {
  google::InitGoogleLogging(argv[0]);

  MyLogSink sink;
  google::AddLogSink(&sink);  // (2)!

  LOG(INFO) << "logging to MySink";

  google::RemoveLogSink(&sink);  // (3)!

  // We can directly log to a sink without registering it
  LOG_TO_SINK(&sink, INFO) << "direct logging";  // (4)!
  LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, INFO)
      << "direct logging but not to file";
}
  1. MySink implements a custom sink that sends log messages to std::cout.
  2. The custom sink must be registered to for use with existing logging macros.
  3. Once the custom sink is no longer needed we remove it from the registry.
  4. A sink does not need to be registered globally. However, then, messages must be logged using dedicated macros.

Running the above example as GLOG_log_dir=. ./custom_sink_example will produce

Custom sink output
INFO custom_sink.cc:63 logging to MySink
INFO custom_sink.cc:68 direct logging
INFO custom_sink.cc:69 direct logging but not to file (1)
  1. This line is not present in the log file because we used LOG_TO_SINK_BUT_NOT_TO_LOGFILE to log the message.

and the corresponding log file will contain

Log file generated with the custom sink
Log file created at: 2024/06/11 13:24:27
Running on machine: pc
Running duration (h:mm:ss): 0:00:00
Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg
I20240611 13:24:27.476620 126237946035776 custom_sink.cc:63] logging to MySink
I20240611 13:24:27.476796 126237946035776 custom_sink.cc:68] direct logging