abseil / The Abseil Logging Library (original) (raw)

The Abseil Logging library provides facilities for writing short text messages about the status of a program to stderr, disk files, or other sinks (via an extension API).

API Overview

The LOG() and CHECK() macro families are the core of the API. Each forms the beginning of a statement that additional data may optionally be streamed into just like std::cout.

All data streamed into a single macro will be concatenated and written to the logfiles as a single message with a prefix formed from metadata (time, file/line, etc.). In particular, and unlike std::cout, the library supplies a newline at the end of each message, so you shouldn’t generally end logging statements with \n or std::endl. Any newlines that are streamed in will show up in the logfiles.

For more detailed information, see the header files.

Header Files for LOG() and CHECK()

Import the logging and check macros with the following #includes. The path toabsl may differ depending on its installation location within your system.

#include "absl/log/log.h"
#include "absl/log/check.h"

LOG() Macro

LOG() takes a severity level as an argument, which defines the granularity and type of logging information to log. The four basic severity levels are INFO,WARNING, ERROR, and FATAL. FATAL is not named idly; it causes the logging library to terminate the process after recording the streamed message. See below for more about severity levels, including best practices for picking one.

LOG(INFO) << "Hello world!";

This will produce a message in the logs like:

I0926 09:00:00.000000   12345 foo.cc:10] Hello world!

The format of the metadata is documented below.

CHECK() Macro

CHECK() is an assertion. Its severity is always FATAL, so its only argument is a condition that should be true. If it isn’t, CHECK() writes to the log and terminates the process. It’s active in all build modes (unlike the Cassert() macro) and logs failures to the application logs in the same way asLOG() but with additional information about what failed and where.

Like FATAL, a CHECK() assertion should be used sparingly (especially in server code) and only in cases where it is preferable to actually terminate the process rather than attempt recovery: e.g. no recovery is possible, or memory corruption could damage user data. Note that you should also be aware of where your code may be running; a CHECK() in a commandline utility or batch processing job needs less caution than a CHECK() in a user-facing service. If you are unsure where your code will run (if you are writing a utility library, for example), be conservative and assume it will be used in production-facing services and should avoid CHECK() where possible.

CHECK(!filenames_sorted.empty()) << "no files matched";
ProcessFile(filenames_sorted.front());

This will produce a message in the logs like:

F0926 09:00:01.000000   12345 foo.cc:100] Check failed: !filenames_sorted.empty() no files matched
E0926 09:00:01.150000   12345 process_state.cc:1133] *** SIGABRT received by PID 12345 (TID 12345) on cpu 0 from PID 12345; stack trace: ***
E0926 09:00:01.250000   12345 process_state.cc:1136] PC: @     0xdeadbeef  (unknown)  raise
    @     0xdeadbeef       1920  FailureSignalHandler()
    @     0xdeadc0w5    2377680  (unknown)
(more stack frames follow)

Note that this log entry uses the prefix F for a severity level of FATAL. The text of the condition itself is logged before the streamed operands. Additionally, a stack trace is logged at severity ERROR (on lines prefixed with E) after the FATAL message but before the process terminates.

Special two-argument forms spelled CHECK_EQ(), CHECK_GT(), CHECK_STREQ()(for char* strings), etc. can be used to make assertions about comparisons between streamable, comparable types. In addition to the text of the arguments, these forms log the actual values of the arguments.

int x = 3, y = 5;
CHECK_EQ(2 * x, y) << "oops!";

This will produce a message in the logs like:

F0926 09:00:02.000000   12345 foo.cc:20] Check failed: 2 * x == y (6 vs. 5) oops!

Alternate Macro Names for Interoperability

Abseil provides alternative macro names prefixed with ABSL_ (e.g. ABSL_LOG) for the benefit of projects that need them. These macros are provided in separate absl_log.h and absl_check.h header files. Both names are identical and are equally supported.

We expect most libraries to avoid logging in headers, and most projects to use only one logging framework. In these cases, the shorter names tend to be more convenient and readable, which is especially valuable for macros used as heavily as these tend to be.

Severity Levels

The absl::LogSeverity type represents severity levels. LOG()’s parameter is actually not of this type, or of any type. Some macro tricks are used to make LOG(ERROR) work without using a macro or global symbol namedERROR. This is necessary because ERROR is defined by some popular third-party packages (e.g. Microsoft Windows) and cannot be redefined.

There are four proper severity levels:

There are also two pseudo-levels:

If you want to specify a severity level using a C++ expression, e.g. so that the level used varies at runtime, you can do that too:

LOG(LEVEL(MoonPhase() == kFullMoon ? absl::LogSeverity::kFatal
                                   : absl::LogSeverity::kError))
      << "Spooky error!";

VLOG() Macro

VLOG() (“verbose log”) is used for runtime-configurable debug logging. The macro takes a non-negative integer verbosity level as an argument - INFOseverity is implied. Verbosity level values are arbitrary, however lower values correspond to more readily-visible messages. Non-zero verbosity levels are disabled by default, and disabled VLOG()s have a very small performance cost, so liberal use of VLOG() is acceptable in most cases without risk of serious performance degradation or unacceptable log spam.

Foo::Foo(int num_bars) {
  VLOG(4) << "Constructing a new Foo with " << num_bars << " Bars";
  for (int i = 0; i < num_bars; i++) bars_.push_back(MakeBar(this));
}

Setting the --v flag will turn on all VLOG() messages at or below the specified level. This is likely to make your logs hard to read and/or fill up your disk. The --vmodule flag allows different levels to be set for different source files; it takes a comma-delimited list of key=value pairs where each key is a glob matched against filenames and each value is the verbosity level that should be effective in matching files. The verbose logging level can also be changed at runtime with absl::SetVLogLevel and absl::SetGlobalVLogLevel:

class FooTest : public testing::Test {
 protected:
  FooTest() {
    // Crank up the `VLOG()` level for `Foo` since it does not log much otherwise:
    absl::SetVLogLevel("foo_impl", 4);
  }
};

Other Macro Variants

The logging API contains a number of additional macros for special cases.

int main (int argc, char**argv) {  
  absl::ParseCommandLine(argc, argv);  
  QCHECK(!absl::GetFlag(FLAGS_path).empty()) << "--path is required";  
  ...  
const int fd = open(path.c_str(), O_RDONLY);  
PCHECK(fd != -1) << "Failed to open " << path;  
const ssize_t bytes_read = read(fd, buf, sizeof(buf));  
PCHECK(bytes_read != -1) << "Failed to read from " << path;  
const int close_ret = close(fd);  
if (close_ret == -1) PLOG(WARNING) << "Failed to close " << path;  
DLOG(INFO) << server.State();  

Be careful with DCHECK(); if it’s worth checking in tests it’s probably worth checking in production too:

DCHECK(ptr != nullptr);  
ptr->Method();  

DCHECK can sometimes be useful for checking invariants in very hot codepaths, where checks in tests must be assumed to validate behavior in production.
Just like assert(), be sure not to rely on evaluation of side-effects inside DCHECK and DLOG statements:
c++ {.bad} DCHECK(server.Start()); // In an optimized build, no attempt will have been made to start the // server!

LOG_IF(INFO, absl::GetFlag(FLAGS_dry_run))  
    << "--dry_run set; no changes will be made";  
LOG_EVERY_N(WARNING, 1000) << "Got a packet with a bad CRC (" << COUNTER  
                           << " total)";  

Mutator Methods

The LOG() and CHECK() macros support some chainable methods that alter their behavior.

Logged Message Output

Log Line Format

Each message is logged with metadata of the following form:

I0926 09:00:00.000000   12345 foo.cc:10] Hello world!

The prefix starts with an I, representing the INFO severity level, combined with a date, 0926. The time follows, with microseconds, in the machine’s local timezone. 12345 is a thread ID number. foo.cc:10 is the source code location where the LOG() statement appears, and the bracket and space are a fixed delimiter before the message itself.

The prefix can be suppressed globally with the --nolog_prefix flag or for a single message the .NoPrefix() mutator method.

absl::LogSink

absl::LogSink is an extension point for processing logged messages, such as by writing them to a disk file. A single message can be directed to it with the.ToSinkOnly() or .ToSinkAlso() mutator methods, or a sink can be registered to observe all logged messages (except those logged with.ToSinkOnly()) with absl::AddLogSink() and unregistered withabsl::RemoveLogSink. For example:

class LinePrinterLogSink : public absl::LogSink {
 public:
  LinePrinterLogSink() : fp_(fopen("/dev/lp0", "a")) {
    PCHECK(fp_ != nullptr) << "Failed to open /dev/lp0";
  }
  ~LinePrinterLogSink() {
    fputc('\f', fp_);
    PCHECK(fclose(fp_) == 0) << "Failed to close /dev/lp0";
  }
  void Send(const absl::LogEntry& entry) override {
    for (absl::string_view line :
         absl::StrSplit(entry.text_message_with_prefix(), absl::ByChar('\n'))) {
      // Overprint severe entries for emphasis:
      for (int i = static_cast<int>(absl::LogSeverity::kInfo);
           i <= static_cast<int>(entry.log_severity()); i++) {
        absl::FPrintF(fp_, "%s\r", line);
      }
      fputc('\n', fp_);
    }
  }

 private:
  FILE* const fp_;
};

A LogSink receives two copies of each FATAL message: one without a stacktrace, and then one with. This quirk allows some diagnostic data to be observed even if stacktrace collection fails or takes too long. The process will terminate once the LogSink returns, i.e., there’s no need for the sink to callabort().

Any logging that takes place in a registered LogSink or in a function called by a registered LogSink is sent only to stderr and not to any registeredLogSinks so as to avoid infinite recursion.

stderr Output

A LogSink that writes to stderr is included and registered by default. globals.h declares some knobs that control which severity levels this sink writes to stderr and which it discards.

Configuration and Flags

There are a small number of runtime configuration knobs with accessors in globals.h. An optional :flags target is provided which defines Abseil flags that control these knobs from the command-line. If this target is not linked in, logging does not depend on Abseil flags (nor vice-versa).

If ABSL_MIN_LOG_LEVEL is defined as a preprocessor macro at build-time, logging at any severity less than its value (converted to absl::LogSeverity) is removed and fatal statements (e.g. CHECK, LOG(FATAL)) terminate silently. Expressions in logged messages are not evaluated, and string literals streamed to such statements are typically stripped as dead code. If this stripping is to be relied upon, the unit tests in stripping_test.cc should be run regularly with the toolchain and flags as the build you want stripped. This behavior is beyond the scope of what’s guaranteed by the C++ standard, so it cannot be guaranteed by Abseil.

If ABSL_MAX_VLOG_VERBOSITY is defined (as an integer) at build-time, VLOGs with greater verbosity are similarly removed.

FAQ

How do I make my type streamable into LOG()?

For a class type, define an AbslStringify() overload as a friend function template for your type. The logging library will check for such an overload when formatting user-defined types.

namespace foo {

class Point {
  ...
  template <typename Sink>
  friend void AbslStringify(Sink& sink, const Point& point) {
    absl::Format(&sink, "(%d, %d)", point.x, point.y);
  }

  int x;
  int y;
};

// If you can't declare the function in the class it's important that the
// AbslStringify overload is defined in the SAME namespace that defines Point.
// C++'s lookup rules rely on that.
enum class EnumWithStringify { kMany = 0, kChoices = 1 };

template <typename Sink>
void AbslStringify(Sink& sink, EnumWithStringify e) {
  absl::Format(&sink, "%s", e == EnumWithStringify::kMany ? "Many" : "Choices");
}

}  // namespace foo

AbslStringify() can also use absl::StrFormat’s catch-all %v type specifier within its own format strings to perform type deduction. Point above could be formatted as "(%v, %v)" for example, and deduce the int values as %d.

For more details regarding AbslStringify() and its integration with other libraries, see https://abseil.io/docs/cpp/guides/abslstringify.

Note: Types that implement operator<<(std::ostream &, T) can also be streamed into LOG() but it is recommended that users implement AbslStringify() as it has greater compatibility with other string formatting libraries.

Why does logging use macros and not functions?

There are several reasons the logging system uses macros:

Okay, but how does LOG(ERROR) not use the name ERROR? It’s right there!

LOG(severity) is a preprocessor macro whose definition looks likeLONG_INTERNAL_MACRO_NAME_##severity. The ## preprocessor operator concatenates tokens, so writing LOG(ERROR) is just like writingLONG_INTERNAL_MACRO_NAME_ERROR. The preprocessor works from left to right, so it expands LOG() before it inspects ERROR, and after expanding LOG() there is no longer an ERROR token to expand.

LONG_INTERNAL_MACRO_NAME_ERROR in turn expands to something like::internal_namespace::LogMessage(__FILE__, __LINE__, ::absl::LogSeverity::kError), which does not use the name ERROReither.

LONG_INTERNAL_MACRO_NAME_LEVEL(x) is a function-like macro of one argument; this is how LOG(LEVEL(x)) works.

This is also why misspelling a severity level, e.g. LOG(WARN) (this should beWARNING), produces a diagnostic about LONG_INTERNAL_MACRO_NAME_WARN not being defined rather than about WARN not being defined.