본문으로 건너뛰기
Abseil Code Review · 59/79

Abseil LOG·VLOG·CHECK 분석

· Hawk · 3분 읽기

#absl::log의 위치

Google은 오랜 기간 glog 를 사용해 왔다. Abseil의 absl/log/는 glog의 후속이며 인터페이스가 거의 동일하다.

#include "absl/log/log.h"
#include "absl/log/check.h"
LOG(INFO) << "user " << user_id << " logged in";
LOG(WARNING) << "slow query: " << elapsed;
LOG(ERROR) << "failed to send: " << status;
CHECK(ptr != nullptr) << "ptr must be set";
CHECK_EQ(actual, expected) << "mismatch";

스트림 인터페이스(operator<<)가 표준이다. 인자가 absl::AlphaNum 호환이면(거의 모든 primitive·string·absl::Time 등) 자동 변환.

#Severity 레벨

매크로의미보통 동작
LOG(INFO)정보stderr/파일
LOG(WARNING)경고stderr/파일
LOG(ERROR)에러stderr/파일 + alert
LOG(FATAL)치명로그 후 abort
LOG(QFATAL)조용한 fatalstack trace 없이 종료
LOG(DFATAL)debug=FATAL, release=ERROR개발 중에만 abort

FATAL반드시 종료. 절대 호출 후 코드가 실행되지 않는다고 가정한다(noreturn 표시).

#초기화 — InitializeLog

main에서 한 번 호출.

#include "absl/log/initialize.h"
int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
absl::InitializeLog();
// ... 이후 LOG 사용 가능
}

호출 전에는 LOG가 안전히 raw fallback으로 동작(stderr에 단순 출력)한다. 그러나 sink·level 설정이 적용되지 않으므로 production 코드는 초기화를 잊지 말 것.

#출력 포맷

기본 출력은 다음과 같은 한 줄이다.

I20260525 13:00:00.123456 12345 file.cc:42] user 123 logged in
  • I — severity 첫 글자 (I/W/E/F)
  • 타임스탬프 — UTC, ms 정밀도
  • thread id
  • 소스 파일 + 라인
  • 본문

--log_format/--stderrthreshold 같은 flag로 조절 가능.

#VLOG — verbose level

INFO보다 자세한 정보를 조건부 로 남긴다.

VLOG(1) << "step 1 details";
VLOG(2) << "step 1 inner detail";
VLOG(3) << "raw payload: " << payload;

--v=2 같은 flag로 활성화. 2 이하 레벨만 출력. 비활성 상태에서는 인자 평가도 안 일어남 (if (VLOG_IS_ON(2)) 등가).

VLOG(2) << "expensive: " << ComputeDump(); // ComputeDump 호출 안 됨 — --v < 2

이 lazy evaluation 덕에 production에서 자유롭게 VLOG를 뿌려도 비용 없음.

#CHECK — runtime assertion

assert보다 강한 invariant. release 빌드에서도 활성화.

CHECK(ptr != nullptr) << "ptr required";
CHECK_EQ(a, b) << "expected a == b, got " << a << " vs " << b;
CHECK_NE(x, 0);
CHECK_LT(idx, size);
CHECK_GE(count, 0);

실패 시 LOG(FATAL) → stack trace + abort. 메시지는 실패 직전에만 평가되어 lazy.

CHECK(x.IsValid()) << "x=" << x.DebugDump(); // DebugDump는 실패 시에만 호출

#DCHECK — debug 한정

CHECK의 debug-only 버전. release 빌드에서는 완전히 사라진다.

DCHECK(invariant) << "..."; // -DNDEBUG에서 no-op
DCHECK_EQ(size, expected);

성능에 민감한 hot path에서 비싼 invariant 검증에 적합.

#QCHECK — quiet check

QFATAL의 짝. 실패 시 stack trace 없이 종료.

QCHECK(absl::ParseFlag(input, &v, &err)) << "bad input: " << err;

flag·configuration 검증처럼 사용자 입력 에러는 stack trace가 노이즈가 되므로 QCHECK가 깔끔하다.

#LOG_EVERY_N / LOG_FIRST_N

쏟아지는 로그를 제한.

LOG_EVERY_N(WARNING, 100) << "rare event"; // 100번에 한 번
LOG_FIRST_N(INFO, 5) << "first 5 only"; // 처음 5번만
LOG_EVERY_N_SEC(INFO, 10) << "every 10s"; // 10초에 한 번

내부적으로 thread-local counter. 멀티스레드에서는 정확한 N마다가 아니라 근사. 디버그 dump 용도면 충분.

#매크로의 효율

LOG(INFO)if (Enabled(INFO)) 검사를 인라인으로 한다. 비활성 severity면 분기 한 번 이면 끝나고 메시지 인자 평가도 일어나지 않는다.

LOG(VERBOSE) << ExpensiveDump(); // VERBOSE 비활성이면 ExpensiveDump 호출 안 됨

#작은 예시 — 요청 핸들러

absl::Status HandleRequest(const Request& req) {
VLOG(1) << "request: " << req.DebugString();
CHECK(!req.user_id().empty()) << "user_id required";
auto user = LookupUser(req.user_id());
if (!user.ok()) {
LOG(WARNING) << "lookup failed for " << req.user_id() << ": " << user.status();
return user.status();
}
auto resp = Process(*user, req);
if (!resp.ok()) {
LOG(ERROR) << "process failed: " << resp.status();
return resp.status();
}
LOG_EVERY_N(INFO, 1000) << "processed " << req.user_id();
return absl::OkStatus();
}

#회피 패턴

// 회피 — std::cerr / printf
std::cerr << "error: " << status << "\n"; // ❌ thread-unsafe interleaving
printf("%s\n", msg.c_str()); // ❌ format 안 맞을 수 있음
// Good
LOG(ERROR) << "error: " << status;
// 회피 — LOG가 비싼 경우에도 항상 평가
LOG(INFO) << "dump: " << ExpensiveDump(); // INFO 비활성이면 낭비
// Good — VLOG로 lazy
VLOG(1) << "dump: " << ExpensiveDump();
// 회피 — CHECK 대신 if + return
if (!ptr) return absl::InternalError("null"); // ⚠️ invariant 위반인데 복구?
// Good — invariant이면 CHECK
CHECK(ptr != nullptr) << "internal invariant";

CHECK는 불변식 위반은 곧 버그 라는 의미. 사용자 입력 검증은 Status로.

#정리

  • LOG(severity)로 스트림 인터페이스. severity별 destination.
  • VLOG(N) + --v=N — 조건부 verbose. 비활성 시 인자 평가 안 됨.
  • CHECK/CHECK_EQ/CHECK_NE 등 — release 빌드에서도 활성. invariant 위반 시 abort.
  • DCHECK — debug 한정. release에서 사라짐.
  • LOG_EVERY_N 계열로 로그 스팸 억제.

#다음 장 예고

Part 11-02: LogSink — 출력 대상 커스터마이징.

#관련 항목

Abseil Code Review · 60 of 79

  1. 1 Abseil Code Review — Google production-grade C++ 라이브러리 분석
  2. 2 Abseil 개요 — Google이 std를 보완한 이유
  3. 3 Abseil 설계 철학 — std 호환과 추가 기능의 균형
  4. 4 Abseil 빌드와 의존성 — Bazel vs CMake
  5. 5 Abseil LTS vs HEAD 릴리스 모델 분석
  6. 6 Abseil Versioning과 ABI 호환성 정책
  7. 7 Abseil 매크로 — ABSL_HAVE_*·ABSL_ATTRIBUTE_*
  8. 8 Abseil ABSL_PREDICT_TRUE/FALSE — branch hint
  9. 9 absl::LogSeverity — 로그 레벨 타입
  10. 10 Abseil type_traits — negation·conjunction·void_t
  11. 11 Abseil Conformance·Policy 분석
  12. 12 Abseil Memory utilities 분석
  13. 13 Abseil raw_logging — heap-free 로깅
  14. 14 Abseil thread_annotations — clang TSA 통합
  15. 15 absl::Status — exception-free error handling
  16. 16 absl::StatusOr<T> — 값 또는 에러
  17. 17 absl status_macros — ASSIGN_OR_RETURN·RETURN_IF_ERROR
  18. 18 absl::Status payload — 구조화된 에러 컨텍스트
  19. 19 absl::Status ↔ exception 변환 패턴
  20. 20 absl::string_view — non-owning 문자열 참조
  21. 21 absl::string_view 함정 — dangling·c_str·임시 객체
  22. 22 absl::StrCat — 가변 인자 문자열 연결과 AlphaNum
  23. 23 absl::StrSplit — Delimiter·Predicate·컨테이너 변환
  24. 24 absl::StrJoin — 컨테이너 결합과 Formatter
  25. 25 absl::StrFormat — type-safe printf·FormatSpec
  26. 26 Abseil ASCII 함수 — locale-free 분류·대소문자 변환
  27. 27 Abseil Escape — CEscape·HexEscape·Base64
  28. 28 absl::flat_hash_map — Swiss Table 기반 hash map
  29. 29 absl::flat_hash_set — set 버전 Swiss Table
  30. 30 absl::node_hash_map — stable pointer가 필요할 때
  31. 31 absl::btree_map — sorted·cache-friendly B-tree
  32. 32 absl::FixedArray — 런타임 크기 stack 배열
  33. 33 absl::InlinedVector — small buffer optimization
  34. 34 Abseil Swiss Table internals — control byte·SIMD probing
  35. 35 absl::Mutex — reader-writer·fairness·deadlock 검출
  36. 36 absl::Mutex Conditional Critical Section — Await로 cv 없애기
  37. 37 absl::Notification — once-only signal
  38. 38 absl::BlockingCounter·Barrier — 다중 thread 조율
  39. 39 absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에
  40. 40 absl::Time·Duration 분석 — 단단한 type
  41. 41 absl::Time Format·Parse
  42. 42 absl::CivilTime 분석
  43. 43 absl::time_zone 분석
  44. 44 absl::Time mocking — 테스트 친화 시간
  45. 45 absl::BitGen — 모던 난수 생성기
  46. 46 Abseil Random Distributions — Uniform·Exponential
  47. 47 Abseil Mocking Random — 테스트 결정성
  48. 48 Abseil Random Seeding·Entropy
  49. 49 absl::int128·uint128 분석
  50. 50 absl::bits — popcount·countl_zero
  51. 51 absl::optional vs std::optional
  52. 52 absl::variant 분석
  53. 53 absl::span 분석
  54. 54 absl::any 분석
  55. 55 absl::compare — three-way 비교
  56. 56 Abseil utility — apply·in_place
  57. 57 Abseil AbslHashValue 분석
  58. 58 Abseil HashState chaining
  59. 59 Abseil Custom hashable 구현
  60. 60 Abseil LOG·VLOG·CHECK 분석
  61. 61 Abseil LogSink 분석
  62. 62 Abseil LogEntry·structured logging
  63. 63 Abseil Stack trace·failure_signal_handler
  64. 64 ABSL_FLAG 정의 분석
  65. 65 Abseil ParseCommandLine 동작
  66. 66 Abseil Flag introspection·validation
  67. 67 Google 스타일의 Abseil 사용 패턴
  68. 68 Abseil 자주 보는 anti-pattern
  69. 69 std → absl 마이그레이션 전략
  70. 70 absl::Cleanup — 함수 종료 시 실행 보장
  71. 71 Abseil algorithm container 확장 — c_sort·c_find_if·c_count_if
  72. 72 absl::function_ref와 any_invocable — 함수 객체 전달의 두 축
  73. 73 absl::bind_front와 Overload — 함수 객체 보조 도구
  74. 74 absl::Cord — 분산 시스템용 대용량 문자열
  75. 75 absl::from_chars·SimpleAtoi — 빠른 숫자 변환
  76. 76 absl::Cord vs std::string — 선택 기준과 메모리 프로파일
  77. 77 absl::GetStackTrace와 Symbolize — crash 시 readable stack
  78. 78 absl::ComputeCrc32c — 하드웨어 가속 체크섬
  79. 79 absl::PeriodicSampler — 적응형 샘플링·jitter 회피