LOG(INFO) vs LOG(WARNING) vs LOG(ERROR) vs CHECK in Abseil-C++: Key Differences Explained

LOG(INFO), LOG(WARNING), and LOG(ERROR) emit diagnostic messages at different severity levels without stopping execution, while CHECK macros verify conditions and abort the program immediately upon failure.

The Abseil C++ library (abseil/abseil-cpp) provides two distinct families of macros for observability and defense. While both LOG and CHECK write to the same underlying logging infrastructure, they serve fundamentally different purposes regarding program flow control and severity levels.

Understanding LOG Severity Levels

The LOG macros record messages at specific severities defined in absl/base/log_severity.h: kInfo, kWarning, kError, and kFatal.

LOG(INFO) for Routine Diagnostics

LOG(INFO) represents the lowest severity level and is used for routine diagnostics that are always safe to emit. It indicates normal program operation and provides tracing information for developers.

LOG(WARNING) for Potential Issues

LOG(WARNING) flags situations that might be problematic but do not prevent the program from continuing execution. Use this when the program encounters recoverable anomalies or unexpected but tolerated states.

LOG(ERROR) for Serious Conditions

LOG(ERROR) corresponds to kError severity and signals serious conditions that the program can tolerate but which require immediate developer attention. Execution continues normally after the message is logged.

How LOG Macros Work Under the Hood

All three macros expand to ABSL_LOG_INTERNAL_LOG_IMPL defined in absl/log/log.h. According to the Abseil source code, this implementation constructs a LogMessage object that streams the supplied expression(s). When the statement ends, the destructor forwards the built string to configured LogSinks. Execution always continues after the logging statement regardless of the severity level specified.

CHECK Macros Enforce Runtime Invariants

In contrast to observational logging, the CHECK family declared in absl/log/check.h performs defensive programming by enforcing runtime invariants.

Fatal Termination on Failure

A failing CHECK expands to ABSL_LOG_INTERNAL_CHECK_IMPL which evaluates the condition. If the expression evaluates to false, the macro creates a LogMessage with severity kFatal, prints a stack trace, runs registered error handlers, and calls abort(). The program terminates immediately; no subsequent code executes.

Comparison Variants (CHECK_EQ, CHECK_NE, etc.)

CHECK_EQ, CHECK_NE, CHECK_LT, CHECK_GT, CHECK_LE, and CHECK_GE provide syntactic sugar for binary comparisons. These variants automatically stringify both operands in the error message, providing more diagnostic context than a raw CHECK(a == b).

QCHECK for Quiet Fatal Failures

QCHECK uses QFATAL severity instead of FATAL, suppressing the stack trace output while still terminating the program immediately upon condition failure.

DCHECK for Debug Builds

DCHECK variants behave like CHECK only in debug builds (#ifndef NDEBUG). In release builds, they compile away completely, similar to how DLOG behaves compared to standard LOG macros.

Structured Comparison

Program Flow Control: LOG(severity) macros return normally and execution continues, while CHECK calls abort() and terminates the process.

Severity Assignment: LOG macros accept INFO, WARNING, ERROR, or FATAL as parameters. CHECK is fixed to FATAL severity (or QFATAL for QCHECK).

Compilation Behavior: LOG statements are always compiled unless stripped by ABSL_MIN_LOG_LEVEL. CHECK is always compiled in all builds, while DCHECK is debug-only.

Implementation Entry: LOG uses ABSL_LOG_INTERNAL_LOG_IMPL in absl/log/log.h, while CHECK uses ABSL_LOG_INTERNAL_CHECK_IMPL in absl/log/check.h.

Practical Code Examples

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

void InitializeServer(int port, const std::string& config) {
  // Observational logging - execution continues after each line
  LOG(INFO) << "Server starting on port " << port;
  LOG(WARNING) << "Using default configuration for port " << port;
  LOG(ERROR) << "Failed to load SSL certificates";
  
  // Defensive checks - program aborts if condition is false
  CHECK(!config.empty()) << "Configuration cannot be empty";
  CHECK_EQ(port, 8080) << "Port mismatch detected";
  
  // Debug-only check (removed in release builds)
  DCHECK(port > 0) << "Port must be positive";
}

Summary

  • LOG(INFO), LOG(WARNING), and LOG(ERROR) emit messages at increasing severity levels (kInfo, kWarning, kError) without altering program flow
  • CHECK macros enforce invariants by calling abort() when conditions evaluate to false, using kFatal severity internally
  • All LOG severities are defined in absl/base/log_severity.h and processed through ABSL_LOG_INTERNAL_LOG_IMPL in absl/log/log.h
  • DCHECK variants are stripped in release builds unlike CHECK which remains active in all configurations
  • QCHECK provides quiet fatal failures without stack traces, using QFATAL severity

Frequently Asked Questions

Does LOG(ERROR) stop program execution?

No. LOG(ERROR) records a message at kError severity and returns normally. Unlike CHECK or LOG(FATAL), it does not call abort() or terminate the process. The program continues executing the next statement immediately after the logging call.

What is the difference between CHECK and standard assert?

CHECK macros are always compiled into the binary (unless using DCHECK), whereas standard assert macros are typically disabled in release builds via NDEBUG. Additionally, CHECK integrates with the Abseil logging infrastructure (LogMessage in absl/log/internal/log_impl.h) and supports streaming operators for custom error messages, providing richer diagnostics than standard assert.

Can I disable LOG(INFO) at compile time?

Yes. You can define ABSL_MIN_LOG_LEVEL to exclude lower severity levels from the compiled binary. When set above kInfo, LOG(INFO) statements compile away completely, similar to how DCHECK behaves in release builds. This allows zero-cost omission of verbose logging in production binaries.

When should I use CHECK_EQ instead of CHECK?

Use CHECK_EQ, CHECK_NE, CHECK_LT, CHECK_GT, CHECK_LE, or CHECK_GE when comparing two values. These variants, defined in absl/log/check.h, automatically stringify both operands in the error message, providing more context than a raw CHECK(a == b) which would only show the expression text rather than the actual values.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →