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:
- INFO{#severity-info} corresponds to
absl::LogSeverity::kInfo. It describes expected events that are important for understanding the state of the program but which are not indicative of a problem. Libraries, especially low-level common libraries, should use this level sparingly lest they spam the logs of every program that uses them.
The VLOG() macro usesINFOseverity with an additional verbosity parameter and can be switched on and off on a per-source-file basis. It is a good alternative for libraries. - WARNING{#severity-warning} corresponds to
absl::LogSeverity::kWarning. It describes unexpected events which _may_indicate a problem. - ERROR{#severity-error} corresponds to
absl::LogSeverity::kError. It describes unexpected problematic events that the program is able to recover from.ERRORmessages should be actionable, meaning that they should describe actual problems with the software or its configuration (and not e.g. with user input) and the combination of the message, the file name and line number, and surrounding messages should be sufficient to at least understand the event being reported. - FATAL{#severity-fatal} corresponds to
absl::LogSeverity::kFataland is the implicit severity level forCHECKfailures. It describes unrecoverable problems. Logging at this level terminates the process. TheFATALlogging level should be used sparingly and carefully in services, especially user-facing services, and in library code that may be included in such services. Each fatal log is a potential outage if a significant fraction of the serving jobs hit it at once.
Fatal logging is more often appropriate for developer tools, some batch jobs, and failures at job startup. That said, process termination and outages are always preferable to undefined behavior (which could include user data corruption and/or a security or privacy incident), soFATALis sometimes appropriate even in server and library code as a last resort in response to unexpected behavior that cannot be handled any other way.
There are also two pseudo-levels:
- DFATAL{#severity-dfatal} (“debug fatal”) corresponds to
absl::kLogDebugFatal. Its value isERRORin optimized builds (e.g. in production) andFATALin other builds (e.g. tests). It can be useful for ensuring that an unexpected event causes tests to fail (by terminating the process) but does not harm production. Since production jobs will continue past aDFATALfailure, make sure to recover gracefully. - QFATAL{#severity-qfatal} (“quiet fatal”) does not have a corresponding
absl::LogSeverityvalue. It behaves likeFATALexcept that no stack trace is logged andatexit()handlers are not run. It is usually the best choice for errors occurring at startup (e.g. flag validation) where the control flow is uninteresting and unnecessary to diagnosis. - DO_NOT_SUBMIT{#severity-do-not-submit} is an alias for
ERROR, and is the right level to use when you are usingLOGfor what’s often calledprintf() debugging. The name is easy to spot in review and is caught by common (but not on-by-default) presubmit checks. The contract is that it must not be checked in, so the Abseil team reserves the right to delete it, change what it does, and/or scrub any instances that do end up checked in, with or without notice.
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.
QCHECK()works likeCHECK()but with the same variations asQFATALvs.FATAL: it does not log a stack trace or runatexit()handlers on failure.
int main (int argc, char**argv) {
absl::ParseCommandLine(argc, argv);
QCHECK(!absl::GetFlag(FLAGS_path).empty()) << "--path is required";
... PLOG()andPCHECK()automatically append a string describingerrnoto the logged message. They are useful with system library calls that seterrnoon failure to indicate the nature of the failure. Their names are intended to be consistent with theperrorlibrary function.
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()(“debug log”) andDCHECK()disappear from the binary completely in optimized builds. Note thatDLOG(FATAL)andDCHECK()have very different semantics fromLOG(DFATAL).
Debug logging is helpful for information that’s useful when debugging tests but expensive to collect (e.g. acquiring a contended lock) in production:
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()adds a condition parameter and is equivalent to anifstatement. As withifand the ternary operator, the condition will be contextually converted tobool.PLOG_IF()andDLOG_IF()variants also exist.
LOG_IF(INFO, absl::GetFlag(FLAGS_dry_run))
<< "--dry_run set; no changes will be made"; LOG_EVERY_N(),LOG_FIRST_N(),LOG_EVERY_N_SEC(), andLOG_EVERY_POW_2()add more complicated conditions that can’t be easily replicated with a simpleifstatement. Each of these maintains a per-statement state object in static storage that’s used to determine whether it’s time to log again. They are thread-safe.
The tokenCOUNTERmay be streamed into these; it will be replaced by a monotonically increasing count of the number of times execution has passed through this statement, including both the times when logging happened and the times when it did not. Macro variants with an added condition (e.g.LOG_IF_EVERY_N()) also exist, as do many combinations withVLOG(),PLOG(), andDLOG().
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.
.AtLocation(absl::string_view file, int line)
Overrides the location inferred from the callsite. The string pointed to byfilemust be valid until the end of the statement..NoPrefix()
Omits the prefix from this line. The prefix includes metadata about the logged data such as source code location and timestamp..WithVerbosity(int verbose_level)
Sets the verbosity field of the logged message as if it was logged byVLOG(verbose_level). UnlikeVLOG(), this method does not affect whether statement is evaluated when the specifiedverbose_levelhas been disabled. The only effect is onLogSinkimplementations which make use of theabsl::LogSink::verbosity()value. The valueabsl::LogEntry::kNoVerbosityLevelcan be specified to mark the message not verbose..WithTimestamp(absl::Time timestamp)
Uses the specified timestamp instead of one collected at the time of execution..WithThreadID(absl::LogEntry::tid_t tid)
Uses the specified thread ID instead of one collected at the time of execution..WithMetadataFrom(const absl::LogEntry &entry)
Copies all metadata (but no data) from the specifiedabsl::LogEntry.
This can be used to change the severity of a message, but it has some limitations:ABSL_MIN_LOG_LEVELis evaluated against the severity passed intoLOG(or the implicitFATALlevel ofCHECK).LOG(FATAL)andCHECKterminate the process unconditionally, even if the severity is changed later.
.WithPerror()
Appends to the logged message a colon, a space, a textual description of the current value oferrno(as bystrerror(3)), and the numerical value oferrno. The result is comparable toPLOG()andPCHECK()..ToSinkAlso(absl::LogSink* sink)
Sends this message to*sinkin addition to whatever other sinks it would otherwise have been sent to.sinkmust not be null..ToSinkOnly(absl::LogSink* sink)
Sends this message to*sinkand no others.sinkmust not be null.
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:
CHECK()uses stringification to include the source code text of the failed condition in the failure message. There’s no way to do this in a function.CHECK(bar()) << foo();does not evaluatefoo()whenbar()returnstrue. LikewiseLOG(INFO) << foo();does not evaluatefoo()ifABSL_MIN_LOG_LEVELiskWarning. Functions can’t do this.
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.