# Abseil CHECK vs LOG_IF vs assert: When to Use Each in C++

> Learn the fundamental differences between Abseil CHECK LOG_IF and assert in C++. Discover when to use each for effective error handling and debugging in your projects.

- Repository: [Abseil/abseil-cpp](https://github.com/abseil/abseil-cpp)
- Tags: best-practices
- Published: 2026-07-12

---

**Abseil CHECK always aborts on failure regardless of build mode, LOG_IF conditionally logs based on severity (only aborting with FATAL), while standard assert is stripped in release builds when NDEBUG is defined.**

When writing defensive C++ code using the abseil-cpp repository, choosing between Abseil CHECK, LOG_IF, and the standard assert macro determines how your application handles runtime validation, error reporting, and termination. These three mechanisms serve distinct purposes—each with unique compilation behaviors, overhead characteristics, and failure guarantees that impact production reliability.

## What Each Macro Does

### CHECK – Runtime Invariants That Never Disappear

The **`CHECK(condition)`** macro guarantees that a boolean condition evaluates to true at runtime. If the condition fails, the process terminates immediately with a fatal log message that includes the failed expression, an optional streamed message, and a stack trace (when supported). According to the source code in [`absl/log/check.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/check.h), these macros are **not** controlled by `NDEBUG` (lines 24–27), meaning the check executes in both debug and release builds.

This makes CHECK ideal for defensive programming scenarios where violating an invariant would corrupt data or compromise security. The macro expands to `ABSL_LOG_INTERNAL_CHECK_IMPL`, which creates a `LogMessage` with severity `kFatal` and invokes registered error handlers before aborting.

### LOG_IF – Conditional Diagnostics

The **`LOG_IF(severity, condition)`** macro emits a log entry only when the condition evaluates to true. Defined in [`absl/log/log.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/log.h) (lines 64–66), it expands to `ABSL_LOG_INTERNAL_LOG_IF_IMPL`. Unlike CHECK, LOG_IF does not automatically terminate the program unless you explicitly specify `FATAL` as the severity level.

When the condition is false, the entire statement collapses to a no-op, preventing evaluation of the streamed arguments (lines 56–60). This short-circuit behavior makes LOG_IF suitable for optional diagnostics, such as logging warnings only when error thresholds exceed specific values, without affecting program flow in the common case.

### assert – Debug-Only Sanity Checks

The standard **`assert(expr)`** macro from `<cassert>` checks an expression only when `NDEBUG` is not defined. In release builds where `NDEBUG` is defined, the macro expands to `(void)0`, removing the check entirely. While assert prints the failed expression and source location, it does not provide stack traces or invoke user-defined abort handlers.

Use assert for inexpensive sanity checks that you are comfortable removing from production binaries, such as validating algorithm preconditions during development.

## Key Architectural Differences

### Evaluation Guarantees and NDEBUG Behavior

The fundamental distinction lies in when the condition gets evaluated:

- **CHECK** – The condition is always evaluated, independent of compilation flags. As noted in [`absl/log/check.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/check.h), the check executes regardless of whether you are building in debug or release mode.
- **LOG_IF** – The condition is evaluated at runtime, but when false, the statement becomes a no-op with zero overhead for the streamed arguments.
- **assert** – The expression is evaluated only when `NDEBUG` is undefined; otherwise, the code disappears completely.

### Severity Handling and Program Flow

**CHECK** implicitly uses fatal severity. Internally, `ABSL_LOG_INTERNAL_CHECK_IMPL` (implemented in [`absl/log/internal/check_impl.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/internal/check_impl.h)) constructs a `LogMessage` that triggers the abort handler chain and terminates the process.

**LOG_IF** provides explicit severity control. You can specify `INFO`, `WARNING`, `ERROR`, or `FATAL`. Only `LOG_IF(FATAL, condition)` produces the same termination behavior as CHECK, while other severities merely write log entries and allow execution to continue.

### Performance and Runtime Overhead

| Macro | Overhead in Release | Controlled By |
|-------|---------------------|---------------|
| **CHECK** | Minimal (single boolean check + possible log) | Always runs |
| **LOG_IF** | Zero when condition is false | Runtime condition |
| **assert** | Zero (code omitted) | Compile-time (`NDEBUG`) |

## Code Examples in Practice

```cpp
#include "absl/log/check.h"
#include "absl/log/log.h"
#include <cassert>
#include <string>

// Example 1: CHECK aborts unconditionally on failure
void ProcessData(absl::string_view data) {
  CHECK(!data.empty()) << "ProcessData received empty input";
  // ... critical processing ...
}

// Example 2: LOG_IF emits warnings conditionally
void MonitorErrors(int error_count) {
  LOG_IF(WARNING, error_count > 100)
      << "Error threshold exceeded: " << error_count;
  // Execution continues regardless of logging
}

// Example 3: LOG_IF with FATAL mimics CHECK
void ValidateConfig(bool is_valid) {
  LOG_IF(FATAL, !is_valid) << "Invalid configuration detected";
  // Unreachable if is_valid is false
}

// Example 4: assert disappears in release builds
void DebugAlgorithm(int iterations) {
  assert(iterations > 0 && "iterations must be positive");
  // Removed entirely when NDEBUG is defined
}

```

## Implementation Details in abseil-cpp

Understanding the source structure clarifies the behavioral differences:

- **[`absl/log/check.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/check.h)** – Declares `CHECK`, `DCHECK`, `QCHECK`, and `PCHECK` variants. The implementation resides in [`absl/log/internal/check_impl.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/internal/check_impl.h), which creates a fatal `LogMessage` and manages abort handlers.
- **[`absl/log/log.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/log.h)** – Declares `LOG_IF`, `LOG`, and conditional logging macros. The implementation uses `ABSL_LOG_INTERNAL_LOG_IF_IMPL` with short-circuit logic defined in [`absl/log/internal/conditions.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/internal/conditions.h).
- **[`absl/log/internal/log_message.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/internal/log_message.h)** – Core class that formats messages, attaches stack traces, and performs termination for fatal logs.

The `DCHECK` variant (debug-check) in Abseil behaves similarly to standard `assert`—it is stripped in release builds—while regular `CHECK` remains active, providing a clear migration path from debug-only assertions to permanent runtime checks.

## Summary

- **CHECK** validates invariants that must never be violated in production, aborting with full diagnostic output regardless of build configuration.
- **LOG_IF** provides flexible conditional logging with selectable severity, aborting only when explicitly configured with `FATAL` severity.
- **assert** offers lightweight debug-only validation that disappears in release builds, suitable for development-time sanity checks.

## Frequently Asked Questions

### When should I use CHECK instead of assert?

Use **CHECK** when the condition must be verified in production builds to prevent data corruption or security vulnerabilities. Use **assert** only for internal logic checks that are safe to remove in release binaries, such as algorithm invariants during development.

### Does LOG_IF(FATAL, condition) behave exactly like CHECK(condition)?

While both terminate the program when the condition fails, **CHECK** provides richer diagnostic output by default, including the failed expression string and automatic stack traces. **LOG_IF(FATAL, condition)** terminates with the message you stream but requires you to construct the log message explicitly. Both invoke the same fatal logging machinery in [`absl/log/internal/log_message.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/internal/log_message.h).

### Will CHECK statements impact performance in release builds?

CHECK introduces minimal overhead—a single boolean evaluation and branch prediction. In performance-critical hot paths, consider whether the invariant is truly unrecoverable, as the check executes unconditionally. Unlike assert, CHECK cannot be compiled away, so use it only for conditions that absolutely require runtime validation.

### Can I disable CHECK macros in production builds?

No. Unlike standard `assert`, **CHECK** macros are intentionally not controlled by `NDEBUG` or other build flags. If you need conditional checks that disappear in release builds, use `DCHECK` (declared alongside CHECK in [`absl/log/check.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/check.h)), which follows the same stripping rules as standard assert.