PiperOrigin-RevId: 470080638 Change-Id: I8d9ddfabc7704c383ed5a73abf0411f4c58a4bf7pull/1267/head
parent
54022b0deb
commit
92fdbfb301
62 changed files with 12367 additions and 0 deletions
@ -0,0 +1,476 @@ |
||||
# |
||||
# Copyright 2022 The Abseil Authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# https://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
# |
||||
|
||||
load( |
||||
"//absl:copts/configure_copts.bzl", |
||||
"ABSL_DEFAULT_COPTS", |
||||
"ABSL_DEFAULT_LINKOPTS", |
||||
"ABSL_TEST_COPTS", |
||||
) |
||||
|
||||
package(default_visibility = ["//visibility:public"]) |
||||
|
||||
licenses(["notice"]) |
||||
|
||||
# Public targets |
||||
cc_library( |
||||
name = "check", |
||||
hdrs = ["check.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
"//absl/base:core_headers", |
||||
"//absl/log/internal:check_op", |
||||
"//absl/log/internal:conditions", |
||||
"//absl/log/internal:log_message", |
||||
"//absl/log/internal:strip", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "die_if_null", |
||||
srcs = ["die_if_null.cc"], |
||||
hdrs = ["die_if_null.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/strings", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "flags", |
||||
srcs = ["flags.cc"], |
||||
hdrs = ["flags.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
visibility = ["//visibility:public"], |
||||
deps = [ |
||||
":globals", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/flags:flag", |
||||
"//absl/flags:marshalling", |
||||
"//absl/log/internal:config", |
||||
"//absl/log/internal:flags", |
||||
"//absl/strings", |
||||
], |
||||
# Binaries which do not access these flags from C++ still want this library linked in. |
||||
alwayslink = True, |
||||
) |
||||
|
||||
cc_library( |
||||
name = "globals", |
||||
srcs = ["globals.cc"], |
||||
hdrs = ["globals.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
visibility = ["//visibility:public"], |
||||
deps = [ |
||||
"//absl/base:atomic_hook", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/hash", |
||||
"//absl/strings", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "initialize", |
||||
srcs = ["initialize.cc"], |
||||
hdrs = ["initialize.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
visibility = ["//visibility:public"], |
||||
deps = [ |
||||
":globals", |
||||
"//absl/base:config", |
||||
"//absl/log/internal:globals", |
||||
"//absl/time", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "log", |
||||
hdrs = ["log.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
"//absl/log/internal:conditions", |
||||
"//absl/log/internal:log_message", |
||||
"//absl/log/internal:strip", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "log_entry", |
||||
srcs = ["log_entry.cc"], |
||||
hdrs = ["log_entry.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
"//absl/base:config", |
||||
"//absl/base:log_severity", |
||||
"//absl/log/internal:config", |
||||
"//absl/strings", |
||||
"//absl/time", |
||||
"//absl/types:span", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "log_sink", |
||||
srcs = ["log_sink.cc"], |
||||
hdrs = ["log_sink.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log_entry", |
||||
"//absl/base:config", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "log_sink_registry", |
||||
hdrs = ["log_sink_registry.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log_sink", |
||||
"//absl/base:config", |
||||
"//absl/log/internal:log_sink_set", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "log_streamer", |
||||
hdrs = ["log_streamer.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log", |
||||
"//absl/base:config", |
||||
"//absl/base:log_severity", |
||||
"//absl/memory", |
||||
"//absl/strings", |
||||
"//absl/strings:internal", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "scoped_mock_log", |
||||
testonly = True, |
||||
srcs = ["scoped_mock_log.cc"], |
||||
hdrs = ["scoped_mock_log.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log_entry", |
||||
":log_sink", |
||||
":log_sink_registry", |
||||
"//absl/base:config", |
||||
"//absl/base:log_severity", |
||||
"//absl/base:raw_logging_internal", |
||||
"//absl/strings", |
||||
"@com_google_googletest//:gtest", |
||||
], |
||||
) |
||||
|
||||
# Test targets |
||||
cc_test( |
||||
name = "basic_log_test", |
||||
size = "small", |
||||
srcs = ["basic_log_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":globals", |
||||
":log", |
||||
":log_entry", |
||||
":scoped_mock_log", |
||||
"//absl/base", |
||||
"//absl/base:log_severity", |
||||
"//absl/log/internal:test_actions", |
||||
"//absl/log/internal:test_helpers", |
||||
"//absl/log/internal:test_matchers", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "check_test", |
||||
size = "small", |
||||
srcs = ["check_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
tags = [ |
||||
"no_test_ios", |
||||
"no_test_wasm", |
||||
], |
||||
deps = [ |
||||
":check", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/log/internal:test_helpers", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "die_if_null_test", |
||||
size = "small", |
||||
srcs = ["die_if_null_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":die_if_null", |
||||
"//absl/base:core_headers", |
||||
"//absl/log/internal:test_helpers", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "flags_test", |
||||
size = "small", |
||||
srcs = ["flags_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":flags", |
||||
":globals", |
||||
":log", |
||||
":scoped_mock_log", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/flags:flag", |
||||
"//absl/flags:reflection", |
||||
"//absl/log/internal:flags", |
||||
"//absl/log/internal:test_helpers", |
||||
"//absl/log/internal:test_matchers", |
||||
"//absl/strings", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "globals_test", |
||||
size = "small", |
||||
srcs = ["globals_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":globals", |
||||
":log", |
||||
":scoped_mock_log", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/log/internal:globals", |
||||
"//absl/log/internal:test_helpers", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "log_entry_test", |
||||
size = "small", |
||||
srcs = ["log_entry_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log_entry", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/log/internal:format", |
||||
"//absl/log/internal:test_helpers", |
||||
"//absl/strings", |
||||
"//absl/time", |
||||
"//absl/types:span", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "log_format_test", |
||||
size = "small", |
||||
srcs = ["log_format_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log", |
||||
":scoped_mock_log", |
||||
"//absl/log/internal:config", |
||||
"//absl/log/internal:test_matchers", |
||||
"//absl/strings", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "log_macro_hygiene_test", |
||||
size = "small", |
||||
srcs = ["log_macro_hygiene_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log", |
||||
":scoped_mock_log", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "log_sink_test", |
||||
size = "medium", |
||||
srcs = ["log_sink_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
tags = [ |
||||
"no_test_ios", |
||||
"no_test_wasm", |
||||
], |
||||
deps = [ |
||||
":log", |
||||
":log_sink", |
||||
":log_sink_registry", |
||||
":scoped_mock_log", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:raw_logging_internal", |
||||
"//absl/log/internal:test_actions", |
||||
"//absl/log/internal:test_helpers", |
||||
"//absl/log/internal:test_matchers", |
||||
"//absl/strings", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "log_streamer_test", |
||||
size = "medium", |
||||
srcs = ["log_streamer_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log", |
||||
":log_streamer", |
||||
":scoped_mock_log", |
||||
"//absl/base", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/log/internal:test_actions", |
||||
"//absl/log/internal:test_helpers", |
||||
"//absl/log/internal:test_matchers", |
||||
"//absl/strings", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "log_modifier_methods_test", |
||||
size = "small", |
||||
srcs = ["log_modifier_methods_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log", |
||||
":log_sink", |
||||
":scoped_mock_log", |
||||
"//absl/log/internal:test_actions", |
||||
"//absl/log/internal:test_helpers", |
||||
"//absl/log/internal:test_matchers", |
||||
"//absl/strings", |
||||
"//absl/time", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "scoped_mock_log_test", |
||||
size = "small", |
||||
srcs = ["scoped_mock_log_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
linkstatic = 1, |
||||
tags = [ |
||||
"no_test_ios", |
||||
"no_test_wasm", |
||||
], |
||||
deps = [ |
||||
":globals", |
||||
":log", |
||||
":scoped_mock_log", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/log/internal:test_helpers", |
||||
"//absl/log/internal:test_matchers", |
||||
"//absl/memory", |
||||
"//absl/strings", |
||||
"//absl/synchronization", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "stripping_test", |
||||
size = "small", |
||||
srcs = ["stripping_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
# This test requires all code live in the binary (instead of shared libraries) |
||||
# because we test for the existence of specific literals in the binary. |
||||
linkstatic = 1, |
||||
deps = [ |
||||
":check", |
||||
":log", |
||||
"//absl/base:strerror", |
||||
"//absl/flags:program_name", |
||||
"//absl/log/internal:test_helpers", |
||||
"//absl/strings", |
||||
"//absl/strings:str_format", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "log_benchmark", |
||||
testonly = 1, |
||||
srcs = ["log_benchmark.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
tags = ["benchmark"], |
||||
deps = [ |
||||
":check", |
||||
":flags", |
||||
":globals", |
||||
":log", |
||||
":log_entry", |
||||
":log_sink", |
||||
":log_sink_registry", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/flags:flag", |
||||
"//absl/log/internal:flags", |
||||
"@com_github_google_benchmark//:benchmark_main", |
||||
], |
||||
) |
@ -0,0 +1,836 @@ |
||||
# |
||||
# Copyright 2022 The Abseil Authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# https://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
# |
||||
|
||||
# Internal targets |
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_check_op |
||||
SRCS |
||||
"internal/check_op.cc" |
||||
HDRS |
||||
"internal/check_op.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log_internal_nullguard |
||||
absl::log_internal_nullstream |
||||
absl::log_internal_strip |
||||
absl::strings |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_conditions |
||||
SRCS |
||||
"internal/conditions.cc" |
||||
HDRS |
||||
"internal/conditions.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::base |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log_internal_voidify |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_config |
||||
SRCS |
||||
HDRS |
||||
"internal/config.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::core_headers |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_flags |
||||
SRCS |
||||
HDRS |
||||
"internal/flags.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::flags |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_format |
||||
SRCS |
||||
"internal/log_format.cc" |
||||
HDRS |
||||
"internal/log_format.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log_internal_config |
||||
absl::log_internal_globals |
||||
absl::log_severity |
||||
absl::strings |
||||
absl::str_format |
||||
absl::time |
||||
absl::span |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_globals |
||||
SRCS |
||||
"internal/globals.cc" |
||||
HDRS |
||||
"internal/globals.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log_severity |
||||
absl::raw_logging_internal |
||||
absl::strings |
||||
absl::time |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_message |
||||
SRCS |
||||
"internal/log_message.cc" |
||||
HDRS |
||||
"internal/log_message.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::base |
||||
absl::config |
||||
absl::core_headers |
||||
absl::errno_saver |
||||
absl::inlined_vector |
||||
absl::examine_stack |
||||
absl::log_internal_config |
||||
absl::log_internal_format |
||||
absl::log_internal_globals |
||||
absl::log_internal_log_sink_set |
||||
absl::log_internal_nullguard |
||||
absl::log_globals |
||||
absl::log_entry |
||||
absl::log_severity |
||||
absl::log_sink |
||||
absl::log_sink_registry |
||||
absl::memory |
||||
absl::raw_logging_internal |
||||
absl::strings |
||||
absl::strerror |
||||
absl::str_format |
||||
absl::time |
||||
absl::span |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_log_sink_set |
||||
SRCS |
||||
"internal/log_sink_set.cc" |
||||
HDRS |
||||
"internal/log_sink_set.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::base |
||||
absl::cleanup |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log_internal_config |
||||
absl::log_internal_globals |
||||
absl::log_globals |
||||
absl::log_entry |
||||
absl::log_severity |
||||
absl::log_sink |
||||
absl::raw_logging_internal |
||||
absl::synchronization |
||||
absl::span |
||||
absl::strings |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_nullguard |
||||
SRCS |
||||
HDRS |
||||
"internal/nullguard.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_nullstream |
||||
SRCS |
||||
HDRS |
||||
"internal/nullstream.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log_severity |
||||
absl::strings |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_strip |
||||
SRCS |
||||
HDRS |
||||
"internal/strip.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::log_internal_message |
||||
absl::log_internal_nullstream |
||||
absl::log_severity |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_test_actions |
||||
SRCS |
||||
"internal/test_actions.cc" |
||||
HDRS |
||||
"internal/test_actions.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::log_entry |
||||
absl::log_internal_config |
||||
absl::log_severity |
||||
absl::strings |
||||
absl::time |
||||
TESTONLY |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_test_helpers |
||||
SRCS |
||||
"internal/test_helpers.cc" |
||||
HDRS |
||||
"internal/test_helpers.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::log_globals |
||||
absl::log_initialize |
||||
absl::log_internal_globals |
||||
absl::log_severity |
||||
GTest::gtest |
||||
TESTONLY |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_test_matchers |
||||
SRCS |
||||
"internal/test_matchers.cc" |
||||
HDRS |
||||
"internal/test_matchers.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::log_entry |
||||
absl::log_internal_config |
||||
absl::log_internal_test_helpers |
||||
absl::log_severity |
||||
absl::strings |
||||
absl::time |
||||
GTest::gtest |
||||
GTest::gmock |
||||
TESTONLY |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_internal_voidify |
||||
SRCS |
||||
HDRS |
||||
"internal/voidify.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
) |
||||
|
||||
# Public targets |
||||
absl_cc_library( |
||||
NAME |
||||
check |
||||
SRCS |
||||
HDRS |
||||
"check.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::core_headers |
||||
absl::log_internal_check_op |
||||
absl::log_internal_conditions |
||||
absl::log_internal_message |
||||
absl::log_internal_strip |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
die_if_null |
||||
SRCS |
||||
"die_if_null.cc" |
||||
HDRS |
||||
"die_if_null.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log |
||||
absl::strings |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_flags |
||||
SRCS |
||||
"flags.cc" |
||||
HDRS |
||||
"flags.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log_globals |
||||
absl::log_severity |
||||
absl::log_internal_config |
||||
absl::log_internal_flags |
||||
absl::flags |
||||
absl::flags_marshalling |
||||
absl::strings |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_globals |
||||
SRCS |
||||
"globals.cc" |
||||
HDRS |
||||
"globals.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::atomic_hook |
||||
absl::config |
||||
absl::core_headers |
||||
absl::hash |
||||
absl::log_severity |
||||
absl::strings |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_initialize |
||||
SRCS |
||||
"initialize.cc" |
||||
HDRS |
||||
"initialize.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::log_globals |
||||
absl::log_internal_globals |
||||
absl::time |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log |
||||
SRCS |
||||
HDRS |
||||
"log.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::log_internal_conditions |
||||
absl::log_internal_message |
||||
absl::log_internal_strip |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_entry |
||||
SRCS |
||||
"log_entry.cc" |
||||
HDRS |
||||
"log_entry.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::log_internal_config |
||||
absl::log_severity |
||||
absl::span |
||||
absl::strings |
||||
absl::time |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_sink |
||||
SRCS |
||||
"log_sink.cc" |
||||
HDRS |
||||
"log_sink.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::log_entry |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_sink_registry |
||||
SRCS |
||||
HDRS |
||||
"log_sink_registry.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::log_sink |
||||
absl::log_internal_log_sink_set |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
log_streamer |
||||
SRCS |
||||
HDRS |
||||
"log_streamer.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::log |
||||
absl::log_severity |
||||
absl::memory |
||||
absl::strings |
||||
absl::strings_internal |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_library( |
||||
NAME |
||||
scoped_mock_log |
||||
SRCS |
||||
"scoped_mock_log.cc" |
||||
HDRS |
||||
"scoped_mock_log.h" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::log_entry |
||||
absl::log_severity |
||||
absl::log_sink |
||||
absl::log_sink_registry |
||||
absl::raw_logging_internal |
||||
absl::strings |
||||
GTest::gmock |
||||
GTest::gtest |
||||
PUBLIC |
||||
TESTONLY |
||||
) |
||||
|
||||
# Test targets |
||||
absl_cc_test( |
||||
NAME |
||||
basic_log_test |
||||
SRCS |
||||
"basic_log_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::base |
||||
absl::log |
||||
absl::log_entry |
||||
absl::log_globals |
||||
absl::log_severity |
||||
absl::log_internal_test_actions |
||||
absl::log_internal_test_helpers |
||||
absl::log_internal_test_matchers |
||||
absl::scoped_mock_log |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
check_test |
||||
SRCS |
||||
"check_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::check |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log_internal_test_helpers |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
die_if_null_test |
||||
SRCS |
||||
"die_if_null_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::core_headers |
||||
absl::die_if_null |
||||
absl::log_internal_test_helpers |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_flags_test |
||||
SRCS |
||||
"flags_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::core_headers |
||||
absl::log |
||||
absl::log_flags |
||||
absl::log_globals |
||||
absl::log_internal_flags |
||||
absl::log_internal_test_helpers |
||||
absl::log_internal_test_matchers |
||||
absl::log_severity |
||||
absl::flags |
||||
absl::flags_reflection |
||||
absl::scoped_mock_log |
||||
absl::strings |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_entry_test |
||||
SRCS |
||||
"log_entry_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::core_headers |
||||
absl::log_entry |
||||
absl::log_internal_format |
||||
absl::log_internal_globals |
||||
absl::log_internal_test_helpers |
||||
absl::log_severity |
||||
absl::span |
||||
absl::strings |
||||
absl::time |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_globals_test |
||||
SRCS |
||||
"globals_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::core_headers |
||||
absl::log |
||||
absl::log_globals |
||||
absl::log_internal_globals |
||||
absl::log_internal_test_helpers |
||||
absl::log_severity |
||||
absl::scoped_mock_log |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_format_test |
||||
SRCS |
||||
"log_format_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::log |
||||
absl::log_internal_config |
||||
absl::log_internal_test_matchers |
||||
absl::scoped_mock_log |
||||
absl::strings |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_macro_hygiene_test |
||||
SRCS |
||||
"log_macro_hygiene_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::core_headers |
||||
absl::log |
||||
absl::log_severity |
||||
absl::scoped_mock_log |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_sink_test |
||||
SRCS |
||||
"log_sink_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::core_headers |
||||
absl::log |
||||
absl::log_internal_test_actions |
||||
absl::log_internal_test_helpers |
||||
absl::log_internal_test_matchers |
||||
absl::log_sink |
||||
absl::log_sink_registry |
||||
absl::log_severity |
||||
absl::raw_logging_internal |
||||
absl::scoped_mock_log |
||||
absl::strings |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_streamer_test |
||||
SRCS |
||||
"log_streamer_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::base |
||||
absl::core_headers |
||||
absl::log |
||||
absl::log_internal_test_actions |
||||
absl::log_internal_test_helpers |
||||
absl::log_internal_test_matchers |
||||
absl::log_streamer |
||||
absl::log_severity |
||||
absl::scoped_mock_log |
||||
absl::strings |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_modifier_methods_test |
||||
SRCS |
||||
"log_modifier_methods_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::log |
||||
absl::log_internal_test_actions |
||||
absl::log_internal_test_helpers |
||||
absl::log_internal_test_matchers |
||||
absl::log_sink |
||||
absl::scoped_mock_log |
||||
absl::strings |
||||
absl::time |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
scoped_mock_log_test |
||||
SRCS |
||||
"scoped_mock_log_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::core_headers |
||||
absl::log |
||||
absl::log_globals |
||||
absl::log_internal_globals |
||||
absl::log_internal_test_helpers |
||||
absl::log_internal_test_matchers |
||||
absl::log_severity |
||||
absl::memory |
||||
absl::scoped_mock_log |
||||
absl::strings |
||||
absl::synchronization |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_internal_stderr_log_sink_test |
||||
SRCS |
||||
"internal/stderr_log_sink_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::core_headers |
||||
absl::log |
||||
absl::log_globals |
||||
absl::log_internal_test_helpers |
||||
absl::log_severity |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
log_stripping_test |
||||
SRCS |
||||
"stripping_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
LINKOPTS |
||||
${ABSL_DEFAULT_LINKOPTS} |
||||
DEPS |
||||
absl::check |
||||
absl::flags_program_name |
||||
absl::log |
||||
absl::log_internal_test_helpers |
||||
absl::strerror |
||||
absl::strings |
||||
absl::str_format |
||||
GTest::gmock |
||||
GTest::gtest_main |
||||
) |
@ -0,0 +1,440 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The testcases in this file are expected to pass or be skipped with any value
|
||||
// of ABSL_MIN_LOG_LEVEL
|
||||
|
||||
#include <cerrno> |
||||
#include <sstream> |
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/internal/sysinfo.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/globals.h" |
||||
#include "absl/log/internal/test_actions.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/internal/test_matchers.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/log/scoped_mock_log.h" |
||||
|
||||
namespace { |
||||
#if GTEST_HAS_DEATH_TEST |
||||
using ::absl::log_internal::DeathTestExpectedLogging; |
||||
using ::absl::log_internal::DeathTestUnexpectedLogging; |
||||
using ::absl::log_internal::DeathTestValidateExpectations; |
||||
using ::absl::log_internal::DiedOfFatal; |
||||
using ::absl::log_internal::DiedOfQFatal; |
||||
#endif |
||||
using ::absl::log_internal::LoggingEnabledAt; |
||||
using ::absl::log_internal::LogSeverity; |
||||
using ::absl::log_internal::Prefix; |
||||
using ::absl::log_internal::SourceBasename; |
||||
using ::absl::log_internal::SourceFilename; |
||||
using ::absl::log_internal::SourceLine; |
||||
using ::absl::log_internal::Stacktrace; |
||||
using ::absl::log_internal::TextMessage; |
||||
using ::absl::log_internal::ThreadID; |
||||
using ::absl::log_internal::TimestampInMatchWindow; |
||||
using ::absl::log_internal::Verbosity; |
||||
using ::testing::AnyNumber; |
||||
using ::testing::Eq; |
||||
using ::testing::IsEmpty; |
||||
using ::testing::IsTrue; |
||||
|
||||
class BasicLogTest : public testing::TestWithParam<absl::LogSeverityAtLeast> {}; |
||||
|
||||
std::string ThresholdName( |
||||
testing::TestParamInfo<absl::LogSeverityAtLeast> severity) { |
||||
std::stringstream ostr; |
||||
ostr << severity.param; |
||||
return ostr.str().substr( |
||||
severity.param == absl::LogSeverityAtLeast::kInfinity ? 0 : 2); |
||||
} |
||||
|
||||
INSTANTIATE_TEST_SUITE_P(WithParam, BasicLogTest, |
||||
testing::Values(absl::LogSeverityAtLeast::kInfo, |
||||
absl::LogSeverityAtLeast::kWarning, |
||||
absl::LogSeverityAtLeast::kError, |
||||
absl::LogSeverityAtLeast::kFatal, |
||||
absl::LogSeverityAtLeast::kInfinity), |
||||
ThresholdName); |
||||
|
||||
TEST_P(BasicLogTest, Info) { |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam()); |
||||
|
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(INFO) << "hello world"; }; |
||||
|
||||
if (LoggingEnabledAt(absl::LogSeverity::kInfo)) { |
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq(__FILE__)), |
||||
SourceBasename(Eq("basic_log_test.cc")), |
||||
SourceLine(Eq(log_line)), Prefix(IsTrue()), |
||||
LogSeverity(Eq(absl::LogSeverity::kInfo)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("hello world")), |
||||
Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
literal: "hello world" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
} |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
} |
||||
|
||||
TEST_P(BasicLogTest, Warning) { |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam()); |
||||
|
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(WARNING) << "hello world"; }; |
||||
|
||||
if (LoggingEnabledAt(absl::LogSeverity::kWarning)) { |
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq(__FILE__)), |
||||
SourceBasename(Eq("basic_log_test.cc")), |
||||
SourceLine(Eq(log_line)), Prefix(IsTrue()), |
||||
LogSeverity(Eq(absl::LogSeverity::kWarning)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("hello world")), |
||||
Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
literal: "hello world" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
} |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
} |
||||
|
||||
TEST_P(BasicLogTest, Error) { |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam()); |
||||
|
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(ERROR) << "hello world"; }; |
||||
|
||||
if (LoggingEnabledAt(absl::LogSeverity::kError)) { |
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq(__FILE__)), |
||||
SourceBasename(Eq("basic_log_test.cc")), |
||||
SourceLine(Eq(log_line)), Prefix(IsTrue()), |
||||
LogSeverity(Eq(absl::LogSeverity::kError)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("hello world")), |
||||
Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
literal: "hello world" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
} |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
using BasicLogDeathTest = BasicLogTest; |
||||
|
||||
INSTANTIATE_TEST_SUITE_P(WithParam, BasicLogDeathTest, |
||||
testing::Values(absl::LogSeverityAtLeast::kInfo, |
||||
absl::LogSeverityAtLeast::kFatal, |
||||
absl::LogSeverityAtLeast::kInfinity), |
||||
ThresholdName); |
||||
|
||||
TEST_P(BasicLogDeathTest, Fatal) { |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam()); |
||||
|
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(FATAL) << "hello world"; }; |
||||
|
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink( |
||||
absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
|
||||
::testing::InSequence s; |
||||
|
||||
// Note the logic in DeathTestValidateExpectations() caters for the case
|
||||
// of logging being disabled at FATAL level.
|
||||
|
||||
if (LoggingEnabledAt(absl::LogSeverity::kFatal)) { |
||||
// The first call without the stack trace.
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq(__FILE__)), |
||||
SourceBasename(Eq("basic_log_test.cc")), |
||||
SourceLine(Eq(log_line)), Prefix(IsTrue()), |
||||
LogSeverity(Eq(absl::LogSeverity::kFatal)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("hello world")), |
||||
Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)), |
||||
ENCODED_MESSAGE(EqualsProto( |
||||
R"pb(value { literal: "hello world" })pb")), |
||||
Stacktrace(IsEmpty())))) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
// The second call with the stack trace.
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq(__FILE__)), |
||||
SourceBasename(Eq("basic_log_test.cc")), |
||||
SourceLine(Eq(log_line)), Prefix(IsTrue()), |
||||
LogSeverity(Eq(absl::LogSeverity::kFatal)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("hello world")), |
||||
Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)), |
||||
ENCODED_MESSAGE(EqualsProto( |
||||
R"pb(value { literal: "hello world" })pb")), |
||||
Stacktrace(Not(IsEmpty()))))) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
} |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
|
||||
TEST_P(BasicLogDeathTest, QFatal) { |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam()); |
||||
|
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(QFATAL) << "hello world"; }; |
||||
|
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink( |
||||
absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
|
||||
if (LoggingEnabledAt(absl::LogSeverity::kFatal)) { |
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq(__FILE__)), |
||||
SourceBasename(Eq("basic_log_test.cc")), |
||||
SourceLine(Eq(log_line)), Prefix(IsTrue()), |
||||
LogSeverity(Eq(absl::LogSeverity::kFatal)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("hello world")), |
||||
Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)), |
||||
ENCODED_MESSAGE(EqualsProto( |
||||
R"pb(value { literal: "hello world" })pb")), |
||||
Stacktrace(IsEmpty())))) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
} |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
}, |
||||
DiedOfQFatal, DeathTestValidateExpectations()); |
||||
} |
||||
#endif |
||||
|
||||
TEST_P(BasicLogTest, Level) { |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam()); |
||||
|
||||
for (auto severity : {absl::LogSeverity::kInfo, absl::LogSeverity::kWarning, |
||||
absl::LogSeverity::kError}) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [severity] { LOG(LEVEL(severity)) << "hello world"; }; |
||||
|
||||
if (LoggingEnabledAt(severity)) { |
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq(__FILE__)), |
||||
SourceBasename(Eq("basic_log_test.cc")), |
||||
SourceLine(Eq(log_line)), Prefix(IsTrue()), |
||||
LogSeverity(Eq(severity)), TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("hello world")), |
||||
Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
literal: "hello world" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
} |
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
} |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
TEST_P(BasicLogDeathTest, Level) { |
||||
// TODO(b/242568884): re-enable once bug is fixed.
|
||||
// absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam());
|
||||
|
||||
// Ensure that `severity` is not a compile-time constant to prove that
|
||||
// `LOG(LEVEL(severity))` works regardless:
|
||||
auto volatile severity = absl::LogSeverity::kFatal; |
||||
|
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [severity] { LOG(LEVEL(severity)) << "hello world"; }; |
||||
|
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink( |
||||
absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
|
||||
::testing::InSequence s; |
||||
|
||||
if (LoggingEnabledAt(absl::LogSeverity::kFatal)) { |
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq(__FILE__)), |
||||
SourceBasename(Eq("basic_log_test.cc")), |
||||
SourceLine(Eq(log_line)), Prefix(IsTrue()), |
||||
LogSeverity(Eq(absl::LogSeverity::kFatal)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("hello world")), |
||||
Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)), |
||||
ENCODED_MESSAGE(EqualsProto( |
||||
R"pb(value { literal: "hello world" })pb")), |
||||
Stacktrace(IsEmpty())))) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq(__FILE__)), |
||||
SourceBasename(Eq("basic_log_test.cc")), |
||||
SourceLine(Eq(log_line)), Prefix(IsTrue()), |
||||
LogSeverity(Eq(absl::LogSeverity::kFatal)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("hello world")), |
||||
Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)), |
||||
ENCODED_MESSAGE(EqualsProto( |
||||
R"pb(value { literal: "hello world" })pb")), |
||||
Stacktrace(Not(IsEmpty()))))) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
} |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
#endif |
||||
|
||||
TEST_P(BasicLogTest, LevelClampsNegativeValues) { |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam()); |
||||
|
||||
if (!LoggingEnabledAt(absl::LogSeverity::kInfo)) { |
||||
GTEST_SKIP() << "This test cases required INFO log to be enabled"; |
||||
return; |
||||
} |
||||
|
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(LogSeverity(Eq(absl::LogSeverity::kInfo)))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(LEVEL(-1)) << "hello world"; |
||||
} |
||||
|
||||
TEST_P(BasicLogTest, LevelClampsLargeValues) { |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam()); |
||||
|
||||
if (!LoggingEnabledAt(absl::LogSeverity::kError)) { |
||||
GTEST_SKIP() << "This test cases required ERROR log to be enabled"; |
||||
return; |
||||
} |
||||
|
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(LogSeverity(Eq(absl::LogSeverity::kError)))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(LEVEL(static_cast<int>(absl::LogSeverity::kFatal) + 1)) << "hello world"; |
||||
} |
||||
|
||||
TEST(ErrnoPreservationTest, InSeverityExpression) { |
||||
errno = 77; |
||||
int saved_errno; |
||||
LOG(LEVEL((saved_errno = errno, absl::LogSeverity::kInfo))); |
||||
EXPECT_THAT(saved_errno, Eq(77)); |
||||
} |
||||
|
||||
TEST(ErrnoPreservationTest, InStreamedExpression) { |
||||
if (!LoggingEnabledAt(absl::LogSeverity::kInfo)) { |
||||
GTEST_SKIP() << "This test cases required INFO log to be enabled"; |
||||
return; |
||||
} |
||||
|
||||
errno = 77; |
||||
int saved_errno = 0; |
||||
LOG(INFO) << (saved_errno = errno, "hello world"); |
||||
EXPECT_THAT(saved_errno, Eq(77)); |
||||
} |
||||
|
||||
TEST(ErrnoPreservationTest, AfterStatement) { |
||||
errno = 77; |
||||
LOG(INFO); |
||||
const int saved_errno = errno; |
||||
EXPECT_THAT(saved_errno, Eq(77)); |
||||
} |
||||
|
||||
// Tests that using a variable/parameter in a logging statement suppresses
|
||||
// unused-variable/parameter warnings.
|
||||
// -----------------------------------------------------------------------
|
||||
class UnusedVariableWarningCompileTest { |
||||
// These four don't prove anything unless `ABSL_MIN_LOG_LEVEL` is greater than
|
||||
// `kInfo`.
|
||||
static void LoggedVariable() { |
||||
const int x = 0; |
||||
LOG(INFO) << x; |
||||
} |
||||
static void LoggedParameter(const int x) { LOG(INFO) << x; } |
||||
static void SeverityVariable() { |
||||
const int x = 0; |
||||
LOG(LEVEL(x)) << "hello world"; |
||||
} |
||||
static void SeverityParameter(const int x) { LOG(LEVEL(x)) << "hello world"; } |
||||
}; |
||||
|
||||
} // namespace
|
@ -0,0 +1,227 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/check.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares a family of `CHECK` macros.
|
||||
//
|
||||
// `CHECK` macros terminate the program with a fatal error if the specified
|
||||
// condition is not true.
|
||||
//
|
||||
// Except for those whose names begin with `DCHECK`, these macros are not
|
||||
// controlled by `NDEBUG` (cf. `assert`), so the check will be executed
|
||||
// regardless of compilation mode. `CHECK` and friends are thus useful for
|
||||
// confirming invariants in situations where continuing to run would be worse
|
||||
// than terminating, e.g., due to risk of data corruption or security
|
||||
// compromise. It is also more robust and portable to deliberately terminate
|
||||
// at a particular place with a useful message and backtrace than to assume some
|
||||
// ultimately unspecified and unreliable crashing behavior (such as a
|
||||
// "segmentation fault").
|
||||
|
||||
#ifndef ABSL_LOG_CHECK_H_ |
||||
#define ABSL_LOG_CHECK_H_ |
||||
|
||||
#include "absl/base/optimization.h" |
||||
#include "absl/log/internal/check_op.h" // IWYU pragma: export |
||||
#include "absl/log/internal/conditions.h" // IWYU pragma: export |
||||
#include "absl/log/internal/log_message.h" // IWYU pragma: export |
||||
#include "absl/log/internal/strip.h" // IWYU pragma: export |
||||
|
||||
// CHECK()
|
||||
//
|
||||
// `CHECK` terminates the program with a fatal error if `condition` is not true.
|
||||
//
|
||||
// The message may include additional information such as stack traces, when
|
||||
// available.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// CHECK(!cheese.empty()) << "Out of Cheese";
|
||||
//
|
||||
// Might produce a message like:
|
||||
//
|
||||
// Check failed: !cheese.empty() Out of Cheese
|
||||
#define CHECK(condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, \
|
||||
ABSL_PREDICT_FALSE(!(condition))) \
|
||||
ABSL_LOG_INTERNAL_CHECK(#condition).InternalStream() |
||||
|
||||
// QCHECK()
|
||||
//
|
||||
// `QCHECK` behaves like `CHECK` but does not print a full stack trace and does
|
||||
// not run registered error handlers (as `QFATAL`). It is useful when the
|
||||
// problem is definitely unrelated to program flow, e.g. when validating user
|
||||
// input.
|
||||
#define QCHECK(condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, \
|
||||
ABSL_PREDICT_FALSE(!(condition))) \
|
||||
ABSL_LOG_INTERNAL_QCHECK(#condition).InternalStream() |
||||
|
||||
// PCHECK()
|
||||
//
|
||||
// `PCHECK` behaves like `CHECK` but appends a description of the current state
|
||||
// of `errno` to the failure message.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// int fd = open("/var/empty/missing", O_RDONLY);
|
||||
// PCHECK(fd != -1) << "posix is difficult";
|
||||
//
|
||||
// Might produce a message like:
|
||||
//
|
||||
// Check failed: fd != -1 posix is difficult: No such file or directory [2]
|
||||
#define PCHECK(condition) CHECK(condition).WithPerror() |
||||
|
||||
// DCHECK()
|
||||
//
|
||||
// `DCHECK` behaves like `CHECK` in debug mode and does nothing otherwise (as
|
||||
// `DLOG`). Unlike with `CHECK` (but as with `assert`), it is not safe to rely
|
||||
// on evaluation of `condition`: when `NDEBUG` is enabled, DCHECK does not
|
||||
// evaluate the condition.
|
||||
#ifndef NDEBUG |
||||
#define DCHECK(condition) CHECK(condition) |
||||
#else |
||||
#define DCHECK(condition) CHECK(true || (condition)) |
||||
#endif |
||||
|
||||
// `CHECK_EQ` and friends are syntactic sugar for `CHECK(x == y)` that
|
||||
// automatically output the expression being tested and the evaluated values on
|
||||
// either side.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// int x = 3, y = 5;
|
||||
// CHECK_EQ(2 * x, y) << "oops!";
|
||||
//
|
||||
// Might produce a message like:
|
||||
//
|
||||
// Check failed: 2 * x == y (6 vs. 5) oops!
|
||||
//
|
||||
// The values must implement the appropriate comparison operator as well as
|
||||
// `operator<<(std::ostream&, ...)`. Care is taken to ensure that each
|
||||
// argument is evaluated exactly once, and that anything which is legal to pass
|
||||
// as a function argument is legal here. In particular, the arguments may be
|
||||
// temporary expressions which will end up being destroyed at the end of the
|
||||
// statement,
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// CHECK_EQ(std::string("abc")[1], 'b');
|
||||
//
|
||||
// WARNING: Passing `NULL` as an argument to `CHECK_EQ` and similar macros does
|
||||
// not compile. Use `nullptr` instead.
|
||||
#define CHECK_EQ(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_CHECK_OP(Check_EQ, ==, val1, val2) |
||||
#define CHECK_NE(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_CHECK_OP(Check_NE, !=, val1, val2) |
||||
#define CHECK_LE(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_CHECK_OP(Check_LE, <=, val1, val2) |
||||
#define CHECK_LT(val1, val2) ABSL_LOG_INTERNAL_CHECK_OP(Check_LT, <, val1, val2) |
||||
#define CHECK_GE(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_CHECK_OP(Check_GE, >=, val1, val2) |
||||
#define CHECK_GT(val1, val2) ABSL_LOG_INTERNAL_CHECK_OP(Check_GT, >, val1, val2) |
||||
#define QCHECK_EQ(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_OP(Check_EQ, ==, val1, val2) |
||||
#define QCHECK_NE(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_OP(Check_NE, !=, val1, val2) |
||||
#define QCHECK_LE(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_OP(Check_LE, <=, val1, val2) |
||||
#define QCHECK_LT(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_OP(Check_LT, <, val1, val2) |
||||
#define QCHECK_GE(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_OP(Check_GE, >=, val1, val2) |
||||
#define QCHECK_GT(val1, val2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_OP(Check_GT, >, val1, val2) |
||||
#ifndef NDEBUG |
||||
#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2) |
||||
#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2) |
||||
#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2) |
||||
#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2) |
||||
#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2) |
||||
#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2) |
||||
#else // ndef NDEBUG
|
||||
#define DCHECK_EQ(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2) |
||||
#define DCHECK_NE(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2) |
||||
#define DCHECK_LE(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2) |
||||
#define DCHECK_LT(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2) |
||||
#define DCHECK_GE(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2) |
||||
#define DCHECK_GT(val1, val2) ABSL_LOG_INTERNAL_DCHECK_NOP(val1, val2) |
||||
#endif // def NDEBUG
|
||||
|
||||
// `CHECK_OK` and friends validate that the provided `absl::Status` or
|
||||
// `absl::StatusOr<T>` is OK. If it isn't, they print a failure message that
|
||||
// includes the actual status.
|
||||
//
|
||||
// As with all `DCHECK` variants, `DCHECK_OK` has no effect (not even
|
||||
// evaluating its argument) if `NDEBUG` is enabled.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// CHECK_OK(FunctionReturnsStatus(x, y, z)) << "oops!";
|
||||
//
|
||||
// Might produce a message like:
|
||||
//
|
||||
// Check failed: FunctionReturnsStatus(x, y, z) is OK (ABORTED: timeout) oops!
|
||||
#define CHECK_OK(status) ABSL_LOG_INTERNAL_CHECK_OK(status) |
||||
#define QCHECK_OK(status) ABSL_LOG_INTERNAL_QCHECK_OK(status) |
||||
#ifndef NDEBUG |
||||
#define DCHECK_OK(status) ABSL_LOG_INTERNAL_CHECK_OK(status) |
||||
#else |
||||
#define DCHECK_OK(status) ABSL_LOG_INTERNAL_DCHECK_NOP(status, nullptr) |
||||
#endif |
||||
|
||||
// `CHECK_STREQ` and friends provide `CHECK_EQ` functionality for C strings,
|
||||
// i.e., nul-terminated char arrays. The `CASE` versions are case-insensitive.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// CHECK_STREQ(argv[0], "./skynet");
|
||||
//
|
||||
// Note that both arguments may be temporary strings which are destroyed by the
|
||||
// compiler at the end of the current full expression.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// CHECK_STREQ(Foo().c_str(), Bar().c_str());
|
||||
#define CHECK_STREQ(s1, s2) \ |
||||
ABSL_LOG_INTERNAL_CHECK_STROP(strcmp, ==, true, s1, s2) |
||||
#define CHECK_STRNE(s1, s2) \ |
||||
ABSL_LOG_INTERNAL_CHECK_STROP(strcmp, !=, false, s1, s2) |
||||
#define CHECK_STRCASEEQ(s1, s2) \ |
||||
ABSL_LOG_INTERNAL_CHECK_STROP(strcasecmp, ==, true, s1, s2) |
||||
#define CHECK_STRCASENE(s1, s2) \ |
||||
ABSL_LOG_INTERNAL_CHECK_STROP(strcasecmp, !=, false, s1, s2) |
||||
#define QCHECK_STREQ(s1, s2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_STROP(strcmp, ==, true, s1, s2) |
||||
#define QCHECK_STRNE(s1, s2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_STROP(strcmp, !=, false, s1, s2) |
||||
#define QCHECK_STRCASEEQ(s1, s2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_STROP(strcasecmp, ==, true, s1, s2) |
||||
#define QCHECK_STRCASENE(s1, s2) \ |
||||
ABSL_LOG_INTERNAL_QCHECK_STROP(strcasecmp, !=, false, s1, s2) |
||||
#ifndef NDEBUG |
||||
#define DCHECK_STREQ(s1, s2) CHECK_STREQ(s1, s2) |
||||
#define DCHECK_STRCASEEQ(s1, s2) CHECK_STRCASEEQ(s1, s2) |
||||
#define DCHECK_STRNE(s1, s2) CHECK_STRNE(s1, s2) |
||||
#define DCHECK_STRCASENE(s1, s2) CHECK_STRCASENE(s1, s2) |
||||
#else // ndef NDEBUG
|
||||
#define DCHECK_STREQ(s1, s2) ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2) |
||||
#define DCHECK_STRCASEEQ(s1, s2) ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2) |
||||
#define DCHECK_STRNE(s1, s2) ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2) |
||||
#define DCHECK_STRCASENE(s1, s2) ABSL_LOG_INTERNAL_DCHECK_NOP(s1, s2) |
||||
#endif // def NDEBUG
|
||||
|
||||
#endif // ABSL_LOG_CHECK_H_
|
@ -0,0 +1,433 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/check.h" |
||||
|
||||
#include <ostream> |
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
|
||||
namespace { |
||||
using ::testing::AllOf; |
||||
using ::testing::HasSubstr; |
||||
using ::testing::Not; |
||||
|
||||
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( |
||||
new absl::log_internal::LogTestEnvironment); |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
|
||||
TEST(CHECKDeathTest, TestBasicValues) { |
||||
CHECK(true); |
||||
|
||||
EXPECT_DEATH(CHECK(false), "Check failed: false"); |
||||
|
||||
int i = 2; |
||||
CHECK(i != 3); // NOLINT
|
||||
} |
||||
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TEST(CHECKTest, TestLogicExpressions) { |
||||
int i = 5; |
||||
CHECK(i > 0 && i < 10); |
||||
CHECK(i < 0 || i > 3); |
||||
} |
||||
|
||||
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L |
||||
ABSL_CONST_INIT const auto global_var_check = [](int i) { |
||||
CHECK(i > 0); // NOLINT
|
||||
return i + 1; |
||||
}(3); |
||||
|
||||
ABSL_CONST_INIT const auto global_var = [](int i) { |
||||
CHECK_GE(i, 0); // NOLINT
|
||||
return i + 1; |
||||
}(global_var_check); |
||||
#endif // ABSL_INTERNAL_CPLUSPLUS_LANG
|
||||
|
||||
TEST(CHECKTest, TestPlacementsInCompoundStatements) { |
||||
// check placement inside if/else clauses
|
||||
if (true) CHECK(true); |
||||
|
||||
if (false) |
||||
; // NOLINT
|
||||
else |
||||
CHECK(true); |
||||
|
||||
switch (0) |
||||
case 0: |
||||
CHECK(true); // NOLINT
|
||||
|
||||
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L |
||||
constexpr auto var = [](int i) { |
||||
CHECK(i > 0); // NOLINT
|
||||
return i + 1; |
||||
}(global_var); |
||||
(void)var; |
||||
#endif // ABSL_INTERNAL_CPLUSPLUS_LANG
|
||||
} |
||||
|
||||
TEST(CHECKTest, TestBoolConvertible) { |
||||
struct Tester { |
||||
} tester; |
||||
CHECK([&]() { return &tester; }()); |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
|
||||
TEST(CHECKDeathTest, TestChecksWithSideeffects) { |
||||
int var = 0; |
||||
CHECK([&var]() { |
||||
++var; |
||||
return true; |
||||
}()); |
||||
EXPECT_EQ(var, 1); |
||||
|
||||
EXPECT_DEATH(CHECK([&var]() { |
||||
++var; |
||||
return false; |
||||
}()) << var, |
||||
"Check failed: .* 2"); |
||||
} |
||||
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
|
||||
TEST(CHECKDeachTest, TestOrderOfInvocationsBetweenCheckAndMessage) { |
||||
int counter = 0; |
||||
|
||||
auto GetStr = [&counter]() -> std::string { |
||||
return counter++ == 0 ? "" : "non-empty"; |
||||
}; |
||||
|
||||
EXPECT_DEATH(CHECK(!GetStr().empty()) << GetStr(), HasSubstr("non-empty")); |
||||
} |
||||
|
||||
TEST(CHECKTest, TestSecondaryFailure) { |
||||
auto FailingRoutine = []() { |
||||
CHECK(false) << "Secondary"; |
||||
return false; |
||||
}; |
||||
EXPECT_DEATH(CHECK(FailingRoutine()) << "Primary", |
||||
AllOf(HasSubstr("Secondary"), Not(HasSubstr("Primary")))); |
||||
} |
||||
|
||||
TEST(CHECKTest, TestSecondaryFailureInMessage) { |
||||
auto MessageGen = []() { |
||||
CHECK(false) << "Secondary"; |
||||
return "Primary"; |
||||
}; |
||||
EXPECT_DEATH(CHECK(false) << MessageGen(), |
||||
AllOf(HasSubstr("Secondary"), Not(HasSubstr("Primary")))); |
||||
} |
||||
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TEST(CHECKTest, TestBinaryChecksWithPrimitives) { |
||||
CHECK_EQ(1, 1); |
||||
CHECK_NE(1, 2); |
||||
CHECK_GE(1, 1); |
||||
CHECK_GE(2, 1); |
||||
CHECK_LE(1, 1); |
||||
CHECK_LE(1, 2); |
||||
CHECK_GT(2, 1); |
||||
CHECK_LT(1, 2); |
||||
} |
||||
|
||||
// For testing using CHECK*() on anonymous enums.
|
||||
enum { CASE_A, CASE_B }; |
||||
|
||||
TEST(CHECKTest, TestBinaryChecksWithEnumValues) { |
||||
// Tests using CHECK*() on anonymous enums.
|
||||
CHECK_EQ(CASE_A, CASE_A); |
||||
CHECK_NE(CASE_A, CASE_B); |
||||
CHECK_GE(CASE_A, CASE_A); |
||||
CHECK_GE(CASE_B, CASE_A); |
||||
CHECK_LE(CASE_A, CASE_A); |
||||
CHECK_LE(CASE_A, CASE_B); |
||||
CHECK_GT(CASE_B, CASE_A); |
||||
CHECK_LT(CASE_A, CASE_B); |
||||
} |
||||
|
||||
TEST(CHECKTest, TestBinaryChecksWithNullptr) { |
||||
const void* p_null = nullptr; |
||||
const void* p_not_null = &p_null; |
||||
CHECK_EQ(p_null, nullptr); |
||||
CHECK_EQ(nullptr, p_null); |
||||
CHECK_NE(p_not_null, nullptr); |
||||
CHECK_NE(nullptr, p_not_null); |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
|
||||
// Test logging of various char-typed values by failing CHECK*().
|
||||
TEST(CHECKDeathTest, TestComparingCharsValues) { |
||||
{ |
||||
char a = ';'; |
||||
char b = 'b'; |
||||
EXPECT_DEATH(CHECK_EQ(a, b), "Check failed: a == b \\(';' vs. 'b'\\)"); |
||||
b = 1; |
||||
EXPECT_DEATH(CHECK_EQ(a, b), |
||||
"Check failed: a == b \\(';' vs. char value 1\\)"); |
||||
} |
||||
{ |
||||
signed char a = ';'; |
||||
signed char b = 'b'; |
||||
EXPECT_DEATH(CHECK_EQ(a, b), "Check failed: a == b \\(';' vs. 'b'\\)"); |
||||
b = -128; |
||||
EXPECT_DEATH(CHECK_EQ(a, b), |
||||
"Check failed: a == b \\(';' vs. signed char value -128\\)"); |
||||
} |
||||
{ |
||||
unsigned char a = ';'; |
||||
unsigned char b = 'b'; |
||||
EXPECT_DEATH(CHECK_EQ(a, b), "Check failed: a == b \\(';' vs. 'b'\\)"); |
||||
b = 128; |
||||
EXPECT_DEATH(CHECK_EQ(a, b), |
||||
"Check failed: a == b \\(';' vs. unsigned char value 128\\)"); |
||||
} |
||||
} |
||||
|
||||
TEST(CHECKDeathTest, TestNullValuesAreReportedCleanly) { |
||||
const char* a = nullptr; |
||||
const char* b = nullptr; |
||||
EXPECT_DEATH(CHECK_NE(a, b), |
||||
"Check failed: a != b \\(\\(null\\) vs. \\(null\\)\\)"); |
||||
|
||||
a = "xx"; |
||||
EXPECT_DEATH(CHECK_EQ(a, b), "Check failed: a == b \\(xx vs. \\(null\\)\\)"); |
||||
EXPECT_DEATH(CHECK_EQ(b, a), "Check failed: b == a \\(\\(null\\) vs. xx\\)"); |
||||
|
||||
std::nullptr_t n{}; |
||||
EXPECT_DEATH(CHECK_NE(n, nullptr), |
||||
"Check failed: n != nullptr \\(\\(null\\) vs. \\(null\\)\\)"); |
||||
} |
||||
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TEST(CHECKTest, TestSTREQ) { |
||||
CHECK_STREQ("this", "this"); |
||||
CHECK_STREQ(nullptr, nullptr); |
||||
CHECK_STRCASEEQ("this", "tHiS"); |
||||
CHECK_STRCASEEQ(nullptr, nullptr); |
||||
CHECK_STRNE("this", "tHiS"); |
||||
CHECK_STRNE("this", nullptr); |
||||
CHECK_STRCASENE("this", "that"); |
||||
CHECK_STRCASENE(nullptr, "that"); |
||||
CHECK_STREQ((std::string("a") + "b").c_str(), "ab"); |
||||
CHECK_STREQ(std::string("test").c_str(), |
||||
(std::string("te") + std::string("st")).c_str()); |
||||
} |
||||
|
||||
TEST(CHECKTest, TestComparisonPlacementsInCompoundStatements) { |
||||
// check placement inside if/else clauses
|
||||
if (true) CHECK_EQ(1, 1); |
||||
if (true) CHECK_STREQ("c", "c"); |
||||
|
||||
if (false) |
||||
; // NOLINT
|
||||
else |
||||
CHECK_LE(0, 1); |
||||
|
||||
if (false) |
||||
; // NOLINT
|
||||
else |
||||
CHECK_STRNE("a", "b"); |
||||
|
||||
switch (0) |
||||
case 0: |
||||
CHECK_NE(1, 0); |
||||
|
||||
switch (0) |
||||
case 0: |
||||
CHECK_STRCASEEQ("A", "a"); |
||||
|
||||
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L |
||||
constexpr auto var = [](int i) { |
||||
CHECK_GT(i, 0); |
||||
return i + 1; |
||||
}(global_var); |
||||
(void)var; |
||||
|
||||
// CHECK_STR... checks are not supported in constexpr routines.
|
||||
// constexpr auto var2 = [](int i) {
|
||||
// CHECK_STRNE("c", "d");
|
||||
// return i + 1;
|
||||
// }(global_var);
|
||||
|
||||
#if defined(__GNUC__) |
||||
int var3 = (({ CHECK_LE(1, 2); }), global_var < 10) ? 1 : 0; |
||||
(void)var3; |
||||
|
||||
int var4 = (({ CHECK_STREQ("a", "a"); }), global_var < 10) ? 1 : 0; |
||||
(void)var4; |
||||
#endif // __GNUC__
|
||||
#endif // ABSL_INTERNAL_CPLUSPLUS_LANG
|
||||
} |
||||
|
||||
TEST(CHECKTest, TestDCHECK) { |
||||
#ifdef NDEBUG |
||||
DCHECK(1 == 2) << " DCHECK's shouldn't be compiled in normal mode"; |
||||
#endif |
||||
DCHECK(1 == 1); // NOLINT(readability/check)
|
||||
DCHECK_EQ(1, 1); |
||||
DCHECK_NE(1, 2); |
||||
DCHECK_GE(1, 1); |
||||
DCHECK_GE(2, 1); |
||||
DCHECK_LE(1, 1); |
||||
DCHECK_LE(1, 2); |
||||
DCHECK_GT(2, 1); |
||||
DCHECK_LT(1, 2); |
||||
|
||||
// Test DCHECK on std::nullptr_t
|
||||
const void* p_null = nullptr; |
||||
const void* p_not_null = &p_null; |
||||
DCHECK_EQ(p_null, nullptr); |
||||
DCHECK_EQ(nullptr, p_null); |
||||
DCHECK_NE(p_not_null, nullptr); |
||||
DCHECK_NE(nullptr, p_not_null); |
||||
} |
||||
|
||||
TEST(CHECKTest, TestQCHECK) { |
||||
// The tests that QCHECK does the same as CHECK
|
||||
QCHECK(1 == 1); // NOLINT(readability/check)
|
||||
QCHECK_EQ(1, 1); |
||||
QCHECK_NE(1, 2); |
||||
QCHECK_GE(1, 1); |
||||
QCHECK_GE(2, 1); |
||||
QCHECK_LE(1, 1); |
||||
QCHECK_LE(1, 2); |
||||
QCHECK_GT(2, 1); |
||||
QCHECK_LT(1, 2); |
||||
|
||||
// Tests using QCHECK*() on anonymous enums.
|
||||
QCHECK_EQ(CASE_A, CASE_A); |
||||
QCHECK_NE(CASE_A, CASE_B); |
||||
QCHECK_GE(CASE_A, CASE_A); |
||||
QCHECK_GE(CASE_B, CASE_A); |
||||
QCHECK_LE(CASE_A, CASE_A); |
||||
QCHECK_LE(CASE_A, CASE_B); |
||||
QCHECK_GT(CASE_B, CASE_A); |
||||
QCHECK_LT(CASE_A, CASE_B); |
||||
} |
||||
|
||||
TEST(CHECKTest, TestQCHECKPlacementsInCompoundStatements) { |
||||
// check placement inside if/else clauses
|
||||
if (true) QCHECK(true); |
||||
|
||||
if (false) |
||||
; // NOLINT
|
||||
else |
||||
QCHECK(true); |
||||
|
||||
if (false) |
||||
; // NOLINT
|
||||
else |
||||
QCHECK(true); |
||||
|
||||
switch (0) |
||||
case 0: |
||||
QCHECK(true); |
||||
|
||||
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L |
||||
constexpr auto var = [](int i) { |
||||
QCHECK(i > 0); // NOLINT
|
||||
return i + 1; |
||||
}(global_var); |
||||
(void)var; |
||||
|
||||
#if defined(__GNUC__) |
||||
int var2 = (({ CHECK_LE(1, 2); }), global_var < 10) ? 1 : 0; |
||||
(void)var2; |
||||
#endif // __GNUC__
|
||||
#endif // ABSL_INTERNAL_CPLUSPLUS_LANG
|
||||
} |
||||
|
||||
class ComparableType { |
||||
public: |
||||
explicit ComparableType(int v) : v_(v) {} |
||||
|
||||
void MethodWithCheck(int i) { |
||||
CHECK_EQ(*this, i); |
||||
CHECK_EQ(i, *this); |
||||
} |
||||
|
||||
int Get() const { return v_; } |
||||
|
||||
private: |
||||
friend bool operator==(const ComparableType& lhs, const ComparableType& rhs) { |
||||
return lhs.v_ == rhs.v_; |
||||
} |
||||
friend bool operator!=(const ComparableType& lhs, const ComparableType& rhs) { |
||||
return lhs.v_ != rhs.v_; |
||||
} |
||||
friend bool operator<(const ComparableType& lhs, const ComparableType& rhs) { |
||||
return lhs.v_ < rhs.v_; |
||||
} |
||||
friend bool operator<=(const ComparableType& lhs, const ComparableType& rhs) { |
||||
return lhs.v_ <= rhs.v_; |
||||
} |
||||
friend bool operator>(const ComparableType& lhs, const ComparableType& rhs) { |
||||
return lhs.v_ > rhs.v_; |
||||
} |
||||
friend bool operator>=(const ComparableType& lhs, const ComparableType& rhs) { |
||||
return lhs.v_ >= rhs.v_; |
||||
} |
||||
friend bool operator==(const ComparableType& lhs, int rhs) { |
||||
return lhs.v_ == rhs; |
||||
} |
||||
friend bool operator==(int lhs, const ComparableType& rhs) { |
||||
return lhs == rhs.v_; |
||||
} |
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, const ComparableType& v) { |
||||
return out << "ComparableType{" << v.Get() << "}"; |
||||
} |
||||
|
||||
int v_; |
||||
}; |
||||
|
||||
TEST(CHECKTest, TestUserDefinedCompOp) { |
||||
CHECK_EQ(ComparableType{0}, ComparableType{0}); |
||||
CHECK_NE(ComparableType{1}, ComparableType{2}); |
||||
CHECK_LT(ComparableType{1}, ComparableType{2}); |
||||
CHECK_LE(ComparableType{1}, ComparableType{2}); |
||||
CHECK_GT(ComparableType{2}, ComparableType{1}); |
||||
CHECK_GE(ComparableType{2}, ComparableType{2}); |
||||
} |
||||
|
||||
TEST(CHECKTest, TestCheckInMethod) { |
||||
ComparableType v{1}; |
||||
v.MethodWithCheck(1); |
||||
} |
||||
|
||||
TEST(CHECKDeathTest, TestUserDefinedStreaming) { |
||||
ComparableType v1{1}; |
||||
ComparableType v2{2}; |
||||
|
||||
EXPECT_DEATH( |
||||
CHECK_EQ(v1, v2), |
||||
HasSubstr( |
||||
"Check failed: v1 == v2 (ComparableType{1} vs. ComparableType{2})")); |
||||
} |
||||
|
||||
} // namespace
|
@ -0,0 +1,32 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/die_if_null.h" |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
void DieBecauseNull(const char* file, int line, const char* exprtext) { |
||||
LOG(FATAL).AtLocation(file, line) |
||||
<< absl::StrCat("Check failed: '", exprtext, "' Must be non-null"); |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,76 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/die_if_null.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares macro `ABSL_DIE_IF_NULL`.
|
||||
|
||||
#ifndef ABSL_LOG_DIE_IF_NULL_H_ |
||||
#define ABSL_LOG_DIE_IF_NULL_H_ |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#include <utility> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/optimization.h" |
||||
|
||||
// ABSL_DIE_IF_NULL()
|
||||
//
|
||||
// `ABSL_DIE_IF_NULL` behaves as `CHECK_NE` against `nullptr` but *also*
|
||||
// "returns" its argument. It is useful in initializers where statements (like
|
||||
// `CHECK_NE`) can't be used. Outside initializers, prefer `CHECK` or
|
||||
// `CHECK_NE`. `ABSL_DIE_IF_NULL` works for both raw pointers and (compatible)
|
||||
// smart pointers including `std::unique_ptr` and `std::shared_ptr`; more
|
||||
// generally, it works for any type that can be compared to nullptr_t. For
|
||||
// types that aren't raw pointers, `ABSL_DIE_IF_NULL` returns a reference to
|
||||
// its argument, preserving the value category. Example:
|
||||
//
|
||||
// Foo() : bar_(ABSL_DIE_IF_NULL(MethodReturningUniquePtr())) {}
|
||||
//
|
||||
// Use `CHECK(ptr)` or `CHECK(ptr != nullptr)` if the returned pointer is
|
||||
// unused.
|
||||
#define ABSL_DIE_IF_NULL(val) \ |
||||
::absl::log_internal::DieIfNull(__FILE__, __LINE__, #val, (val)) |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// Crashes the process after logging `exprtext` annotated at the `file` and
|
||||
// `line` location. Called when `ABSL_DIE_IF_NULL` fails. Calling this function
|
||||
// generates less code than its implementation would if inlined, for a slight
|
||||
// code size reduction each time `ABSL_DIE_IF_NULL` is called.
|
||||
ABSL_ATTRIBUTE_NORETURN ABSL_ATTRIBUTE_NOINLINE void DieBecauseNull( |
||||
const char* file, int line, const char* exprtext); |
||||
|
||||
// Helper for `ABSL_DIE_IF_NULL`.
|
||||
template <typename T> |
||||
ABSL_MUST_USE_RESULT T DieIfNull(const char* file, int line, |
||||
const char* exprtext, T&& t) { |
||||
if (ABSL_PREDICT_FALSE(t == nullptr)) { |
||||
// Call a non-inline helper function for a small code size improvement.
|
||||
DieBecauseNull(file, line, exprtext); |
||||
} |
||||
return std::forward<T>(t); |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_DIE_IF_NULL_H_
|
@ -0,0 +1,107 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/die_if_null.h" |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
|
||||
namespace { |
||||
|
||||
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( |
||||
new absl::log_internal::LogTestEnvironment); |
||||
|
||||
// TODO(b/69907837): Revisit these tests with the goal of making them less
|
||||
// convoluted.
|
||||
TEST(AbslDieIfNull, Simple) { |
||||
int64_t t; |
||||
void* ptr = static_cast<void*>(&t); |
||||
void* ref = ABSL_DIE_IF_NULL(ptr); |
||||
ASSERT_EQ(ptr, ref); |
||||
|
||||
char* t_as_char; |
||||
t_as_char = ABSL_DIE_IF_NULL(reinterpret_cast<char*>(&t)); |
||||
(void)t_as_char; |
||||
|
||||
unsigned char* t_as_uchar; |
||||
t_as_uchar = ABSL_DIE_IF_NULL(reinterpret_cast<unsigned char*>(&t)); |
||||
(void)t_as_uchar; |
||||
|
||||
int* t_as_int; |
||||
t_as_int = ABSL_DIE_IF_NULL(reinterpret_cast<int*>(&t)); |
||||
(void)t_as_int; |
||||
|
||||
int64_t* t_as_int64_t; |
||||
t_as_int64_t = ABSL_DIE_IF_NULL(reinterpret_cast<int64_t*>(&t)); |
||||
(void)t_as_int64_t; |
||||
|
||||
std::unique_ptr<int64_t> sptr(new int64_t); |
||||
EXPECT_EQ(sptr.get(), ABSL_DIE_IF_NULL(sptr).get()); |
||||
ABSL_DIE_IF_NULL(sptr).reset(); |
||||
|
||||
int64_t* int_ptr = new int64_t(); |
||||
EXPECT_EQ(int_ptr, ABSL_DIE_IF_NULL(std::unique_ptr<int64_t>(int_ptr)).get()); |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
TEST(DeathCheckAbslDieIfNull, Simple) { |
||||
void* ptr; |
||||
ASSERT_DEATH({ ptr = ABSL_DIE_IF_NULL(nullptr); }, ""); |
||||
(void)ptr; |
||||
|
||||
std::unique_ptr<int64_t> sptr; |
||||
ASSERT_DEATH(ptr = ABSL_DIE_IF_NULL(sptr).get(), ""); |
||||
} |
||||
#endif |
||||
|
||||
// Ensures that ABSL_DIE_IF_NULL works with C++11's std::unique_ptr and
|
||||
// std::shared_ptr.
|
||||
TEST(AbslDieIfNull, DoesNotCompareSmartPointerToNULL) { |
||||
std::unique_ptr<int> up(new int); |
||||
EXPECT_EQ(&up, &ABSL_DIE_IF_NULL(up)); |
||||
ABSL_DIE_IF_NULL(up).reset(); |
||||
|
||||
std::shared_ptr<int> sp(new int); |
||||
EXPECT_EQ(&sp, &ABSL_DIE_IF_NULL(sp)); |
||||
ABSL_DIE_IF_NULL(sp).reset(); |
||||
} |
||||
|
||||
// Verifies that ABSL_DIE_IF_NULL returns an rvalue reference if its argument is
|
||||
// an rvalue reference.
|
||||
TEST(AbslDieIfNull, PreservesRValues) { |
||||
int64_t* ptr = new int64_t(); |
||||
auto uptr = ABSL_DIE_IF_NULL(std::unique_ptr<int64_t>(ptr)); |
||||
EXPECT_EQ(ptr, uptr.get()); |
||||
} |
||||
|
||||
// Verifies that ABSL_DIE_IF_NULL returns an lvalue if its argument is an
|
||||
// lvalue.
|
||||
TEST(AbslDieIfNull, PreservesLValues) { |
||||
int64_t array[2] = {0}; |
||||
int64_t* a = array + 0; |
||||
int64_t* b = array + 1; |
||||
using std::swap; |
||||
swap(ABSL_DIE_IF_NULL(a), ABSL_DIE_IF_NULL(b)); |
||||
EXPECT_EQ(array + 1, a); |
||||
EXPECT_EQ(array + 0, b); |
||||
} |
||||
|
||||
} // namespace
|
@ -0,0 +1,112 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/flags.h" |
||||
|
||||
#include <stddef.h> |
||||
|
||||
#include <algorithm> |
||||
#include <cstdlib> |
||||
#include <string> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/flags/flag.h" |
||||
#include "absl/flags/marshalling.h" |
||||
#include "absl/log/globals.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/strings/numbers.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
namespace { |
||||
|
||||
void SyncLoggingFlags() { |
||||
absl::SetFlag(&FLAGS_minloglevel, static_cast<int>(absl::MinLogLevel())); |
||||
absl::SetFlag(&FLAGS_log_prefix, absl::ShouldPrependLogPrefix()); |
||||
} |
||||
|
||||
bool RegisterSyncLoggingFlags() { |
||||
log_internal::SetLoggingGlobalsListener(&SyncLoggingFlags); |
||||
return true; |
||||
} |
||||
|
||||
ABSL_ATTRIBUTE_UNUSED const bool unused = RegisterSyncLoggingFlags(); |
||||
|
||||
template <typename T> |
||||
T GetFromEnv(const char* varname, T dflt) { |
||||
const char* val = ::getenv(varname); |
||||
if (val != nullptr) { |
||||
std::string err; |
||||
ABSL_INTERNAL_CHECK(absl::ParseFlag(val, &dflt, &err), err.c_str()); |
||||
} |
||||
return dflt; |
||||
} |
||||
|
||||
constexpr absl::LogSeverityAtLeast StderrThresholdDefault() { |
||||
return absl::LogSeverityAtLeast::kError; |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
ABSL_FLAG(int, stderrthreshold, |
||||
static_cast<int>(absl::log_internal::StderrThresholdDefault()), |
||||
"Log messages at or above this threshold level are copied to stderr.") |
||||
.OnUpdate([] { |
||||
absl::log_internal::RawSetStderrThreshold( |
||||
static_cast<absl::LogSeverityAtLeast>( |
||||
absl::GetFlag(FLAGS_stderrthreshold))); |
||||
}); |
||||
|
||||
ABSL_FLAG(int, minloglevel, static_cast<int>(absl::LogSeverityAtLeast::kInfo), |
||||
"Messages logged at a lower level than this don't actually " |
||||
"get logged anywhere") |
||||
.OnUpdate([] { |
||||
absl::log_internal::RawSetMinLogLevel( |
||||
static_cast<absl::LogSeverityAtLeast>( |
||||
absl::GetFlag(FLAGS_minloglevel))); |
||||
}); |
||||
|
||||
ABSL_FLAG(std::string, log_backtrace_at, "", |
||||
"Emit a backtrace when logging at file:linenum.") |
||||
.OnUpdate([] { |
||||
const std::string log_backtrace_at = |
||||
absl::GetFlag(FLAGS_log_backtrace_at); |
||||
if (log_backtrace_at.empty()) return; |
||||
|
||||
const size_t last_colon = log_backtrace_at.rfind(':'); |
||||
if (last_colon == log_backtrace_at.npos) return; |
||||
|
||||
const absl::string_view file = |
||||
absl::string_view(log_backtrace_at).substr(0, last_colon); |
||||
int line; |
||||
if (absl::SimpleAtoi( |
||||
absl::string_view(log_backtrace_at).substr(last_colon + 1), |
||||
&line)) { |
||||
absl::SetLogBacktraceLocation(file, line); |
||||
} |
||||
}); |
||||
|
||||
ABSL_FLAG(bool, log_prefix, true, |
||||
"Prepend the log prefix to the start of each log line") |
||||
.OnUpdate([] { |
||||
absl::log_internal::RawEnableLogPrefix(absl::GetFlag(FLAGS_log_prefix)); |
||||
}); |
@ -0,0 +1,43 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/flags.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef ABSL_LOG_FLAGS_H_ |
||||
#define ABSL_LOG_FLAGS_H_ |
||||
|
||||
// The Abseil Logging library supports the following command line flags to
|
||||
// configure logging behavior at runtime:
|
||||
//
|
||||
// --stderrthreshold=<value>
|
||||
// Log messages at or above this threshold level are copied to stderr.
|
||||
//
|
||||
// --minloglevel=<value>
|
||||
// Messages logged at a lower level than this are discarded and don't actually
|
||||
// get logged anywhere.
|
||||
//
|
||||
// --log_backtrace_at=<file:linenum>
|
||||
// Emit a backtrace (stack trace) when logging at file:linenum.
|
||||
//
|
||||
// To use these commandline flags, the //absl/log:flags library must be
|
||||
// explicitly linked, and absl::ParseCommandLine() must be called before the
|
||||
// call to absl::InitializeLog().
|
||||
//
|
||||
// To configure the Log library programmatically, use the interfaces defined in
|
||||
// absl/log/globals.h.
|
||||
|
||||
#endif // ABSL_LOG_FLAGS_H_
|
@ -0,0 +1,181 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/flags.h" |
||||
|
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/flags/flag.h" |
||||
#include "absl/flags/reflection.h" |
||||
#include "absl/log/globals.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/internal/test_matchers.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/log/scoped_mock_log.h" |
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
namespace { |
||||
using ::absl::log_internal::TextMessage; |
||||
|
||||
using ::testing::HasSubstr; |
||||
using ::testing::Not; |
||||
|
||||
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( |
||||
new absl::log_internal::LogTestEnvironment); |
||||
|
||||
constexpr static absl::LogSeverityAtLeast DefaultStderrThreshold() { |
||||
return absl::LogSeverityAtLeast::kError; |
||||
} |
||||
|
||||
class LogFlagsTest : public ::testing::Test { |
||||
protected: |
||||
absl::FlagSaver flag_saver_; |
||||
}; |
||||
|
||||
TEST_F(LogFlagsTest, StderrKnobsDefault) { |
||||
EXPECT_EQ(absl::StderrThreshold(), DefaultStderrThreshold()); |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, SetStderrThreshold) { |
||||
absl::SetFlag(&FLAGS_stderrthreshold, |
||||
static_cast<int>(absl::LogSeverityAtLeast::kInfo)); |
||||
|
||||
EXPECT_EQ(absl::StderrThreshold(), absl::LogSeverityAtLeast::kInfo); |
||||
|
||||
absl::SetFlag(&FLAGS_stderrthreshold, |
||||
static_cast<int>(absl::LogSeverityAtLeast::kError)); |
||||
|
||||
EXPECT_EQ(absl::StderrThreshold(), absl::LogSeverityAtLeast::kError); |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, SetMinLogLevel) { |
||||
absl::SetFlag(&FLAGS_minloglevel, |
||||
static_cast<int>(absl::LogSeverityAtLeast::kError)); |
||||
|
||||
EXPECT_EQ(absl::MinLogLevel(), absl::LogSeverityAtLeast::kError); |
||||
|
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level( |
||||
absl::LogSeverityAtLeast::kWarning); |
||||
|
||||
EXPECT_EQ(absl::GetFlag(FLAGS_minloglevel), |
||||
static_cast<int>(absl::LogSeverityAtLeast::kWarning)); |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, PrependLogPrefix) { |
||||
absl::SetFlag(&FLAGS_log_prefix, false); |
||||
|
||||
EXPECT_EQ(absl::ShouldPrependLogPrefix(), false); |
||||
|
||||
absl::EnableLogPrefix(true); |
||||
|
||||
EXPECT_EQ(absl::GetFlag(FLAGS_log_prefix), true); |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, EmptyBacktraceAtFlag) { |
||||
absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo); |
||||
absl::SetFlag(&FLAGS_log_backtrace_at, ""); |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:"))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO) << "hello world"; |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, BacktraceAtNonsense) { |
||||
absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo); |
||||
absl::SetFlag(&FLAGS_log_backtrace_at, "gibberish"); |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:"))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO) << "hello world"; |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, BacktraceAtWrongFile) { |
||||
absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo); |
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(INFO) << "hello world"; }; |
||||
absl::SetFlag(&FLAGS_log_backtrace_at, |
||||
absl::StrCat("some_other_file.cc:", log_line)); |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:"))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, BacktraceAtWrongLine) { |
||||
absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo); |
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(INFO) << "hello world"; }; |
||||
absl::SetFlag(&FLAGS_log_backtrace_at, |
||||
absl::StrCat("flags_test.cc:", log_line + 1)); |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:"))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, BacktraceAtWholeFilename) { |
||||
absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo); |
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(INFO) << "hello world"; }; |
||||
absl::SetFlag(&FLAGS_log_backtrace_at, absl::StrCat(__FILE__, ":", log_line)); |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:"))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, BacktraceAtNonmatchingSuffix) { |
||||
absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo); |
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(INFO) << "hello world"; }; |
||||
absl::SetFlag(&FLAGS_log_backtrace_at, |
||||
absl::StrCat("flags_test.cc:", log_line, "gibberish")); |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(TextMessage(Not(HasSubstr("(stacktrace:"))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
} |
||||
|
||||
TEST_F(LogFlagsTest, LogsBacktrace) { |
||||
absl::SetMinLogLevel(absl::LogSeverityAtLeast::kInfo); |
||||
const int log_line = __LINE__ + 1; |
||||
auto do_log = [] { LOG(INFO) << "hello world"; }; |
||||
absl::SetFlag(&FLAGS_log_backtrace_at, |
||||
absl::StrCat("flags_test.cc:", log_line)); |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(TextMessage(HasSubstr("(stacktrace:")))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
} |
||||
|
||||
} // namespace
|
@ -0,0 +1,148 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/globals.h" |
||||
|
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
|
||||
#include <atomic> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/internal/atomic_hook.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/hash/hash.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace { |
||||
|
||||
// These atomics represent logging library configuration.
|
||||
// Integer types are used instead of absl::LogSeverity to ensure that a
|
||||
// lock-free std::atomic is used when possible.
|
||||
ABSL_CONST_INIT std::atomic<int> min_log_level{ |
||||
static_cast<int>(absl::LogSeverityAtLeast::kInfo)}; |
||||
ABSL_CONST_INIT std::atomic<int> stderrthreshold{ |
||||
static_cast<int>(absl::LogSeverityAtLeast::kError)}; |
||||
// We evaluate this value as a hash comparison to avoid having to
|
||||
// hold a mutex or make a copy (to access the value of a string-typed flag) in
|
||||
// very hot codepath.
|
||||
ABSL_CONST_INIT std::atomic<size_t> log_backtrace_at_hash{0}; |
||||
ABSL_CONST_INIT std::atomic<bool> prepend_log_prefix{true}; |
||||
|
||||
ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES |
||||
absl::base_internal::AtomicHook<log_internal::LoggingGlobalsListener> |
||||
logging_globals_listener; |
||||
|
||||
size_t HashSiteForLogBacktraceAt(absl::string_view file, int line) { |
||||
return absl::HashOf(file, line); |
||||
} |
||||
|
||||
void TriggerLoggingGlobalsListener() { |
||||
auto* listener = logging_globals_listener.Load(); |
||||
if (listener != nullptr) listener(); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
namespace log_internal { |
||||
|
||||
void RawSetMinLogLevel(absl::LogSeverityAtLeast severity) { |
||||
min_log_level.store(static_cast<int>(severity), std::memory_order_release); |
||||
} |
||||
|
||||
void RawSetStderrThreshold(absl::LogSeverityAtLeast severity) { |
||||
stderrthreshold.store(static_cast<int>(severity), std::memory_order_release); |
||||
} |
||||
|
||||
void RawEnableLogPrefix(bool on_off) { |
||||
prepend_log_prefix.store(on_off, std::memory_order_release); |
||||
} |
||||
|
||||
void SetLoggingGlobalsListener(LoggingGlobalsListener l) { |
||||
logging_globals_listener.Store(l); |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
|
||||
absl::LogSeverityAtLeast MinLogLevel() { |
||||
return static_cast<absl::LogSeverityAtLeast>( |
||||
min_log_level.load(std::memory_order_acquire)); |
||||
} |
||||
|
||||
void SetMinLogLevel(absl::LogSeverityAtLeast severity) { |
||||
log_internal::RawSetMinLogLevel(severity); |
||||
TriggerLoggingGlobalsListener(); |
||||
} |
||||
|
||||
namespace log_internal { |
||||
|
||||
ScopedMinLogLevel::ScopedMinLogLevel(absl::LogSeverityAtLeast severity) |
||||
: saved_severity_(absl::MinLogLevel()) { |
||||
absl::SetMinLogLevel(severity); |
||||
} |
||||
ScopedMinLogLevel::~ScopedMinLogLevel() { |
||||
absl::SetMinLogLevel(saved_severity_); |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
|
||||
absl::LogSeverityAtLeast StderrThreshold() { |
||||
return static_cast<absl::LogSeverityAtLeast>( |
||||
stderrthreshold.load(std::memory_order_acquire)); |
||||
} |
||||
|
||||
void SetStderrThreshold(absl::LogSeverityAtLeast severity) { |
||||
log_internal::RawSetStderrThreshold(severity); |
||||
TriggerLoggingGlobalsListener(); |
||||
} |
||||
|
||||
ScopedStderrThreshold::ScopedStderrThreshold(absl::LogSeverityAtLeast severity) |
||||
: saved_severity_(absl::StderrThreshold()) { |
||||
absl::SetStderrThreshold(severity); |
||||
} |
||||
|
||||
ScopedStderrThreshold::~ScopedStderrThreshold() { |
||||
absl::SetStderrThreshold(saved_severity_); |
||||
} |
||||
|
||||
namespace log_internal { |
||||
|
||||
bool ShouldLogBacktraceAt(absl::string_view file, int line) { |
||||
const size_t flag_hash = |
||||
log_backtrace_at_hash.load(std::memory_order_acquire); |
||||
|
||||
return flag_hash != 0 && flag_hash == HashSiteForLogBacktraceAt(file, line); |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
|
||||
void SetLogBacktraceLocation(absl::string_view file, int line) { |
||||
log_backtrace_at_hash.store(HashSiteForLogBacktraceAt(file, line), |
||||
std::memory_order_release); |
||||
} |
||||
|
||||
bool ShouldPrependLogPrefix() { |
||||
return prepend_log_prefix.load(std::memory_order_acquire); |
||||
} |
||||
|
||||
void EnableLogPrefix(bool on_off) { |
||||
log_internal::RawEnableLogPrefix(on_off); |
||||
TriggerLoggingGlobalsListener(); |
||||
} |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,165 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/globals.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares global logging library configuration knobs.
|
||||
|
||||
#ifndef ABSL_LOG_GLOBALS_H_ |
||||
#define ABSL_LOG_GLOBALS_H_ |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Minimum Log Level
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Messages logged at or above this severity are directed to all registered log
|
||||
// sinks or skipped otherwise. This parameter can also be modified using
|
||||
// command line flag --minloglevel.
|
||||
// See absl/base/log_severity.h for descriptions of severity levels.
|
||||
|
||||
// MinLogLevel()
|
||||
//
|
||||
// Returns the value of the Minimum Log Level parameter.
|
||||
// This function is async-signal-safe.
|
||||
ABSL_MUST_USE_RESULT absl::LogSeverityAtLeast MinLogLevel(); |
||||
|
||||
// SetMinLogLevel()
|
||||
//
|
||||
// Updates the value of Minimum Log Level parameter.
|
||||
// This function is async-signal-safe.
|
||||
void SetMinLogLevel(absl::LogSeverityAtLeast severity); |
||||
|
||||
namespace log_internal { |
||||
|
||||
// ScopedMinLogLevel
|
||||
//
|
||||
// RAII type used to temporarily update the Min Log Level parameter.
|
||||
class ScopedMinLogLevel final { |
||||
public: |
||||
explicit ScopedMinLogLevel(absl::LogSeverityAtLeast severity); |
||||
ScopedMinLogLevel(const ScopedMinLogLevel&) = delete; |
||||
ScopedMinLogLevel& operator=(const ScopedMinLogLevel&) = delete; |
||||
~ScopedMinLogLevel(); |
||||
|
||||
private: |
||||
absl::LogSeverityAtLeast saved_severity_; |
||||
}; |
||||
|
||||
} // namespace log_internal
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Stderr Threshold
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Messages logged at or above this level are directed to stderr in
|
||||
// addition to other registered log sinks. This parameter can also be modified
|
||||
// using command line flag --stderrthreshold.
|
||||
// See absl/base/log_severity.h for descriptions of severity levels.
|
||||
|
||||
// StderrThreshold()
|
||||
//
|
||||
// Returns the value of the Stderr Threshold parameter.
|
||||
// This function is async-signal-safe.
|
||||
ABSL_MUST_USE_RESULT absl::LogSeverityAtLeast StderrThreshold(); |
||||
|
||||
// SetStderrThreshold()
|
||||
//
|
||||
// Updates the Stderr Threshold parameter.
|
||||
// This function is async-signal-safe.
|
||||
void SetStderrThreshold(absl::LogSeverityAtLeast severity); |
||||
inline void SetStderrThreshold(absl::LogSeverity severity) { |
||||
absl::SetStderrThreshold(static_cast<absl::LogSeverityAtLeast>(severity)); |
||||
} |
||||
|
||||
// ScopedStderrThreshold
|
||||
//
|
||||
// RAII type used to temporarily update the Stderr Threshold parameter.
|
||||
class ScopedStderrThreshold final { |
||||
public: |
||||
explicit ScopedStderrThreshold(absl::LogSeverityAtLeast severity); |
||||
ScopedStderrThreshold(const ScopedStderrThreshold&) = delete; |
||||
ScopedStderrThreshold& operator=(const ScopedStderrThreshold&) = delete; |
||||
~ScopedStderrThreshold(); |
||||
|
||||
private: |
||||
absl::LogSeverityAtLeast saved_severity_; |
||||
}; |
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Log Backtrace At
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Users can request backtrace to be logged at specific locations, specified
|
||||
// by file and line number.
|
||||
|
||||
// ShouldLogBacktraceAt()
|
||||
//
|
||||
// Returns true if we should log a backtrace at the specified location.
|
||||
namespace log_internal { |
||||
ABSL_MUST_USE_RESULT bool ShouldLogBacktraceAt(absl::string_view file, |
||||
int line); |
||||
} // namespace log_internal
|
||||
|
||||
// SetLogBacktraceLocation()
|
||||
//
|
||||
// Sets the location the backtrace should be logged at.
|
||||
void SetLogBacktraceLocation(absl::string_view file, int line); |
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Prepend Log Prefix
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// This option tells the logging library that every logged message
|
||||
// should include the prefix (severity, date, time, PID, etc.)
|
||||
|
||||
// ShouldPrependLogPrefix()
|
||||
//
|
||||
// Returns the value of the Prepend Log Prefix option.
|
||||
// This function is async-signal-safe.
|
||||
ABSL_MUST_USE_RESULT bool ShouldPrependLogPrefix(); |
||||
|
||||
// EnableLogPrefix()
|
||||
//
|
||||
// Updates the value of the Prepend Log Prefix option.
|
||||
// This function is async-signal-safe.
|
||||
void EnableLogPrefix(bool on_off); |
||||
|
||||
namespace log_internal { |
||||
|
||||
using LoggingGlobalsListener = void (*)(); |
||||
void SetLoggingGlobalsListener(LoggingGlobalsListener l); |
||||
|
||||
// Internal implementation for the setter routines. These are used
|
||||
// to break circular dependencies between flags and globals. Each "Raw"
|
||||
// routine corresponds to the non-"Raw" counterpart and used to set the
|
||||
// configuration parameter directly without calling back to the listener.
|
||||
void RawSetMinLogLevel(absl::LogSeverityAtLeast severity); |
||||
void RawSetStderrThreshold(absl::LogSeverityAtLeast severity); |
||||
void RawEnableLogPrefix(bool on_off); |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_GLOBALS_H_
|
@ -0,0 +1,91 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/globals.h" |
||||
|
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/internal/globals.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/log/scoped_mock_log.h" |
||||
|
||||
namespace { |
||||
|
||||
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( |
||||
new absl::log_internal::LogTestEnvironment); |
||||
|
||||
constexpr static absl::LogSeverityAtLeast DefaultMinLogLevel() { |
||||
return absl::LogSeverityAtLeast::kInfo; |
||||
} |
||||
constexpr static absl::LogSeverityAtLeast DefaultStderrThreshold() { |
||||
return absl::LogSeverityAtLeast::kError; |
||||
} |
||||
|
||||
TEST(TestGlobals, MinLogLevel) { |
||||
EXPECT_EQ(absl::MinLogLevel(), DefaultMinLogLevel()); |
||||
absl::SetMinLogLevel(absl::LogSeverityAtLeast::kError); |
||||
EXPECT_EQ(absl::MinLogLevel(), absl::LogSeverityAtLeast::kError); |
||||
absl::SetMinLogLevel(DefaultMinLogLevel()); |
||||
} |
||||
|
||||
TEST(TestGlobals, ScopedMinLogLevel) { |
||||
EXPECT_EQ(absl::MinLogLevel(), DefaultMinLogLevel()); |
||||
{ |
||||
absl::log_internal::ScopedMinLogLevel scoped_stderr_threshold( |
||||
absl::LogSeverityAtLeast::kError); |
||||
EXPECT_EQ(absl::MinLogLevel(), absl::LogSeverityAtLeast::kError); |
||||
} |
||||
EXPECT_EQ(absl::MinLogLevel(), DefaultMinLogLevel()); |
||||
} |
||||
|
||||
TEST(TestGlobals, StderrThreshold) { |
||||
EXPECT_EQ(absl::StderrThreshold(), DefaultStderrThreshold()); |
||||
absl::SetStderrThreshold(absl::LogSeverityAtLeast::kError); |
||||
EXPECT_EQ(absl::StderrThreshold(), absl::LogSeverityAtLeast::kError); |
||||
absl::SetStderrThreshold(DefaultStderrThreshold()); |
||||
} |
||||
|
||||
TEST(TestGlobals, ScopedStderrThreshold) { |
||||
EXPECT_EQ(absl::StderrThreshold(), DefaultStderrThreshold()); |
||||
{ |
||||
absl::ScopedStderrThreshold scoped_stderr_threshold( |
||||
absl::LogSeverityAtLeast::kError); |
||||
EXPECT_EQ(absl::StderrThreshold(), absl::LogSeverityAtLeast::kError); |
||||
} |
||||
EXPECT_EQ(absl::StderrThreshold(), DefaultStderrThreshold()); |
||||
} |
||||
|
||||
TEST(TestGlobals, LogBacktraceAt) { |
||||
EXPECT_FALSE(absl::log_internal::ShouldLogBacktraceAt("some_file.cc", 111)); |
||||
absl::SetLogBacktraceLocation("some_file.cc", 111); |
||||
EXPECT_TRUE(absl::log_internal::ShouldLogBacktraceAt("some_file.cc", 111)); |
||||
EXPECT_FALSE( |
||||
absl::log_internal::ShouldLogBacktraceAt("another_file.cc", 222)); |
||||
} |
||||
|
||||
TEST(TestGlobals, LogPrefix) { |
||||
EXPECT_TRUE(absl::ShouldPrependLogPrefix()); |
||||
absl::EnableLogPrefix(false); |
||||
EXPECT_FALSE(absl::ShouldPrependLogPrefix()); |
||||
absl::EnableLogPrefix(true); |
||||
EXPECT_TRUE(absl::ShouldPrependLogPrefix()); |
||||
} |
||||
|
||||
} // namespace
|
@ -0,0 +1,34 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/initialize.h" |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/log/internal/globals.h" |
||||
#include "absl/time/time.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
void InitializeLog() { |
||||
// This comes first since it is used by RAW_LOG.
|
||||
absl::log_internal::SetTimeZone(absl::LocalTimeZone()); |
||||
|
||||
// Note that initialization is complete, so logs can now be sent to their
|
||||
// proper destinations rather than stderr.
|
||||
log_internal::SetInitialized(); |
||||
} |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,45 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/initialize.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares the Abseil Log initialization routine InitializeLog().
|
||||
|
||||
#ifndef ABSL_LOG_INITIALIZE_H_ |
||||
#define ABSL_LOG_INITIALIZE_H_ |
||||
|
||||
#include "absl/base/config.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
// InitializeLog()
|
||||
//
|
||||
// Initializes the Abseil logging library.
|
||||
//
|
||||
// Before this function is called, all log messages are directed only to stderr.
|
||||
// After initialization is finished, log messages are directed to all registered
|
||||
// `LogSink`s.
|
||||
//
|
||||
// It is an error to call this function twice.
|
||||
//
|
||||
// There is no corresponding function to shut down the logging library.
|
||||
void InitializeLog(); |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INITIALIZE_H_
|
@ -0,0 +1,302 @@ |
||||
# |
||||
# Copyright 2022 The Abseil Authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# https://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
# |
||||
|
||||
load( |
||||
"//absl:copts/configure_copts.bzl", |
||||
"ABSL_DEFAULT_COPTS", |
||||
"ABSL_DEFAULT_LINKOPTS", |
||||
"ABSL_TEST_COPTS", |
||||
) |
||||
|
||||
package(default_visibility = [ |
||||
"//absl/log:__pkg__", |
||||
]) |
||||
|
||||
licenses(["notice"]) |
||||
|
||||
cc_library( |
||||
name = "check_op", |
||||
srcs = ["check_op.cc"], |
||||
hdrs = ["check_op.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
visibility = [ |
||||
"//absl/log:__pkg__", |
||||
], |
||||
deps = [ |
||||
":nullguard", |
||||
":nullstream", |
||||
":strip", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/strings", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "conditions", |
||||
srcs = ["conditions.cc"], |
||||
hdrs = ["conditions.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":voidify", |
||||
"//absl/base", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "config", |
||||
hdrs = ["config.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
visibility = [ |
||||
"//absl/log:__pkg__", |
||||
], |
||||
deps = [ |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "flags", |
||||
hdrs = ["flags.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
"//absl/flags:flag", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "format", |
||||
srcs = ["log_format.cc"], |
||||
hdrs = ["log_format.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":config", |
||||
":globals", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/strings", |
||||
"//absl/strings:str_format", |
||||
"//absl/time", |
||||
"//absl/types:span", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "globals", |
||||
srcs = ["globals.cc"], |
||||
hdrs = ["globals.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
visibility = [ |
||||
"//absl/log:__pkg__", |
||||
], |
||||
deps = [ |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/base:raw_logging_internal", |
||||
"//absl/strings", |
||||
"//absl/time", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "log_message", |
||||
srcs = ["log_message.cc"], |
||||
hdrs = ["log_message.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
visibility = [ |
||||
"//absl/log:__pkg__", |
||||
], |
||||
deps = [ |
||||
":config", |
||||
":format", |
||||
":globals", |
||||
":log_sink_set", |
||||
":nullguard", |
||||
"//absl/base", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:errno_saver", |
||||
"//absl/base:log_severity", |
||||
"//absl/base:raw_logging_internal", |
||||
"//absl/base:strerror", |
||||
"//absl/container:inlined_vector", |
||||
"//absl/debugging:examine_stack", |
||||
"//absl/log:globals", |
||||
"//absl/log:log_entry", |
||||
"//absl/log:log_sink", |
||||
"//absl/log:log_sink_registry", |
||||
"//absl/memory", |
||||
"//absl/strings", |
||||
"//absl/strings:str_format", |
||||
"//absl/time", |
||||
"//absl/types:span", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "log_sink_set", |
||||
srcs = ["log_sink_set.cc"], |
||||
hdrs = ["log_sink_set.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":config", |
||||
":globals", |
||||
"//absl/base", |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/base:raw_logging_internal", |
||||
"//absl/cleanup", |
||||
"//absl/log:globals", |
||||
"//absl/log:log_entry", |
||||
"//absl/log:log_sink", |
||||
"//absl/strings", |
||||
"//absl/synchronization", |
||||
"//absl/types:span", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "nullguard", |
||||
hdrs = ["nullguard.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
"//absl/base:config", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "nullstream", |
||||
hdrs = ["nullstream.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/strings", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "strip", |
||||
hdrs = ["strip.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":log_message", |
||||
":nullstream", |
||||
"//absl/base:log_severity", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "test_actions", |
||||
testonly = True, |
||||
srcs = ["test_actions.cc"], |
||||
hdrs = ["test_actions.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":config", |
||||
"//absl/base:config", |
||||
"//absl/base:log_severity", |
||||
"//absl/log:log_entry", |
||||
"//absl/strings", |
||||
"//absl/time", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "test_helpers", |
||||
testonly = True, |
||||
srcs = ["test_helpers.cc"], |
||||
hdrs = ["test_helpers.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":globals", |
||||
"//absl/base:config", |
||||
"//absl/base:log_severity", |
||||
"//absl/log:globals", |
||||
"//absl/log:initialize", |
||||
"@com_google_googletest//:gtest", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "test_matchers", |
||||
testonly = True, |
||||
srcs = ["test_matchers.cc"], |
||||
hdrs = ["test_matchers.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = [ |
||||
":config", |
||||
":test_helpers", |
||||
"//absl/base:config", |
||||
"//absl/base:log_severity", |
||||
"//absl/log:log_entry", |
||||
"//absl/strings", |
||||
"//absl/time", |
||||
"@com_google_googletest//:gtest", |
||||
], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "voidify", |
||||
hdrs = ["voidify.h"], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
deps = ["//absl/base:config"], |
||||
) |
||||
|
||||
# Test targets |
||||
cc_test( |
||||
name = "stderr_log_sink_test", |
||||
size = "small", |
||||
srcs = ["stderr_log_sink_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
linkopts = ABSL_DEFAULT_LINKOPTS, |
||||
tags = [ |
||||
"no_test_android", |
||||
"no_test_darwin_x86_64", |
||||
"no_test_ios", |
||||
"no_test_wasm", |
||||
], |
||||
deps = [ |
||||
":test_helpers", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:log_severity", |
||||
"//absl/log", |
||||
"//absl/log:globals", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
@ -0,0 +1,118 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/check_op.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#ifdef _MSC_VER |
||||
#define strcasecmp _stricmp |
||||
#else |
||||
#include <strings.h> // for strcasecmp, but msvc does not have this header |
||||
#endif |
||||
|
||||
#include <sstream> |
||||
#include <string> |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
#define ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(x) \ |
||||
template std::string* MakeCheckOpString(x, x, const char*) |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(bool); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(int64_t); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(uint64_t); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(float); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(double); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(char); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(unsigned char); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const std::string&); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const absl::string_view&); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const char*); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const signed char*); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const unsigned char*); |
||||
ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const void*); |
||||
#undef ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING |
||||
|
||||
CheckOpMessageBuilder::CheckOpMessageBuilder(const char* exprtext) { |
||||
stream_ << exprtext << " ("; |
||||
} |
||||
|
||||
std::ostream& CheckOpMessageBuilder::ForVar2() { |
||||
stream_ << " vs. "; |
||||
return stream_; |
||||
} |
||||
|
||||
std::string* CheckOpMessageBuilder::NewString() { |
||||
stream_ << ")"; |
||||
return new std::string(stream_.str()); |
||||
} |
||||
|
||||
void MakeCheckOpValueString(std::ostream& os, const char v) { |
||||
if (v >= 32 && v <= 126) { |
||||
os << "'" << v << "'"; |
||||
} else { |
||||
os << "char value " << int{v}; |
||||
} |
||||
} |
||||
|
||||
void MakeCheckOpValueString(std::ostream& os, const signed char v) { |
||||
if (v >= 32 && v <= 126) { |
||||
os << "'" << v << "'"; |
||||
} else { |
||||
os << "signed char value " << int{v}; |
||||
} |
||||
} |
||||
|
||||
void MakeCheckOpValueString(std::ostream& os, const unsigned char v) { |
||||
if (v >= 32 && v <= 126) { |
||||
os << "'" << v << "'"; |
||||
} else { |
||||
os << "unsigned char value " << int{v}; |
||||
} |
||||
} |
||||
|
||||
void MakeCheckOpValueString(std::ostream& os, const void* p) { |
||||
if (p == nullptr) { |
||||
os << "(null)"; |
||||
} else { |
||||
os << p; |
||||
} |
||||
} |
||||
|
||||
// Helper functions for string comparisons.
|
||||
#define DEFINE_CHECK_STROP_IMPL(name, func, expected) \ |
||||
std::string* Check##func##expected##Impl(const char* s1, const char* s2, \
|
||||
const char* exprtext) { \
|
||||
bool equal = s1 == s2 || (s1 && s2 && !func(s1, s2)); \
|
||||
if (equal == expected) { \
|
||||
return nullptr; \
|
||||
} else { \
|
||||
return new std::string( \
|
||||
absl::StrCat(exprtext, " (", s1, " vs. ", s2, ")")); \
|
||||
} \
|
||||
} |
||||
DEFINE_CHECK_STROP_IMPL(CHECK_STREQ, strcmp, true) |
||||
DEFINE_CHECK_STROP_IMPL(CHECK_STRNE, strcmp, false) |
||||
DEFINE_CHECK_STROP_IMPL(CHECK_STRCASEEQ, strcasecmp, true) |
||||
DEFINE_CHECK_STROP_IMPL(CHECK_STRCASENE, strcasecmp, false) |
||||
#undef DEFINE_CHECK_STROP_IMPL |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,385 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/check_op.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This file declares helpers routines and macros used to implement `CHECK`
|
||||
// macros.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_CHECK_OP_H_ |
||||
#define ABSL_LOG_INTERNAL_CHECK_OP_H_ |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#include <ostream> |
||||
#include <sstream> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/optimization.h" |
||||
#include "absl/log/internal/nullguard.h" |
||||
#include "absl/log/internal/nullstream.h" |
||||
#include "absl/log/internal/strip.h" |
||||
|
||||
// `ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL` wraps string literals that
|
||||
// should be stripped when `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`.
|
||||
#ifdef ABSL_MIN_LOG_LEVEL |
||||
#define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal) \ |
||||
(::absl::LogSeverity::kFatal >= \
|
||||
static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \
|
||||
? (literal) \
|
||||
: "") |
||||
#else |
||||
#define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal) (literal) |
||||
#endif |
||||
|
||||
#ifdef NDEBUG |
||||
// `NDEBUG` is defined, so `DCHECK_EQ(x, y)` and so on do nothing. However, we
|
||||
// still want the compiler to parse `x` and `y`, because we don't want to lose
|
||||
// potentially useful errors and warnings.
|
||||
#define ABSL_LOG_INTERNAL_DCHECK_NOP(x, y) \ |
||||
while (false && ((void)(x), (void)(y), 0)) \
|
||||
::absl::log_internal::NullStream().InternalStream() |
||||
#endif |
||||
|
||||
#define ABSL_LOG_INTERNAL_CHECK_OP(name, op, val1, val2) \ |
||||
while ( \
|
||||
::std::string* absl_log_internal_check_op_result ABSL_ATTRIBUTE_UNUSED = \
|
||||
::absl::log_internal::name##Impl( \
|
||||
::absl::log_internal::GetReferenceableValue(val1), \
|
||||
::absl::log_internal::GetReferenceableValue(val2), \
|
||||
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val1 " " #op \
|
||||
" " #val2))) \
|
||||
ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_op_result).InternalStream() |
||||
#define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val2) \ |
||||
while (::std::string* absl_log_internal_qcheck_op_result = \
|
||||
::absl::log_internal::name##Impl( \
|
||||
::absl::log_internal::GetReferenceableValue(val1), \
|
||||
::absl::log_internal::GetReferenceableValue(val2), \
|
||||
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val1 " " #op \
|
||||
" " #val2))) \
|
||||
ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_op_result).InternalStream() |
||||
#define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s2) \ |
||||
while (::std::string* absl_log_internal_check_strop_result = \
|
||||
::absl::log_internal::Check##func##expected##Impl( \
|
||||
(s1), (s2), \
|
||||
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#s1 " " #op " " #s2))) \
|
||||
ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_strop_result) \
|
||||
.InternalStream() |
||||
#define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s2) \ |
||||
while (::std::string* absl_log_internal_qcheck_strop_result = \
|
||||
::absl::log_internal::Check##func##expected##Impl( \
|
||||
(s1), (s2), \
|
||||
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#s1 " " #op " " #s2))) \
|
||||
ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_strop_result) \
|
||||
.InternalStream() |
||||
// This one is tricky:
|
||||
// * We must evaluate `val` exactly once, yet we need to do two things with it:
|
||||
// evaluate `.ok()` and (sometimes) `.ToString()`.
|
||||
// * `val` might be an `absl::Status` or some `absl::StatusOr<T>`.
|
||||
// * `val` might be e.g. `ATemporary().GetStatus()`, which may return a
|
||||
// reference to a member of `ATemporary` that is only valid until the end of
|
||||
// the full expression.
|
||||
// * We don't want this file to depend on `absl::Status` `#include`s or linkage,
|
||||
// nor do we want to move the definition to status and introduce a dependency
|
||||
// in the other direction. We can be assured that callers must already have a
|
||||
// `Status` and the necessary `#include`s and linkage.
|
||||
// * Callsites should be small and fast (at least when `val.ok()`): one branch,
|
||||
// minimal stack footprint.
|
||||
// * In particular, the string concat stuff should be out-of-line and emitted
|
||||
// in only one TU to save linker input size
|
||||
// * We want the `val.ok()` check inline so static analyzers and optimizers can
|
||||
// see it.
|
||||
// * As usual, no braces so we can stream into the expansion with `operator<<`.
|
||||
// * Also as usual, it must expand to a single (partial) statement with no
|
||||
// ambiguous-else problems.
|
||||
#define ABSL_LOG_INTERNAL_CHECK_OK(val) \ |
||||
for (::std::pair<const ::absl::Status*, ::std::string*> \
|
||||
absl_log_internal_check_ok_goo; \
|
||||
absl_log_internal_check_ok_goo.first = \
|
||||
::absl::log_internal::AsStatus(val), \
|
||||
absl_log_internal_check_ok_goo.second = \
|
||||
ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \
|
||||
? nullptr \
|
||||
: ::absl::status_internal::MakeCheckFailString( \
|
||||
absl_log_internal_check_ok_goo.first, \
|
||||
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val " is OK")), \
|
||||
!ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \
|
||||
ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second) \
|
||||
.InternalStream() |
||||
#define ABSL_LOG_INTERNAL_QCHECK_OK(val) \ |
||||
for (::std::pair<const ::absl::Status*, ::std::string*> \
|
||||
absl_log_internal_check_ok_goo; \
|
||||
absl_log_internal_check_ok_goo.first = \
|
||||
::absl::log_internal::AsStatus(val), \
|
||||
absl_log_internal_check_ok_goo.second = \
|
||||
ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \
|
||||
? nullptr \
|
||||
: ::absl::status_internal::MakeCheckFailString( \
|
||||
absl_log_internal_check_ok_goo.first, \
|
||||
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val " is OK")), \
|
||||
!ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \
|
||||
ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_check_ok_goo.second) \
|
||||
.InternalStream() |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
class Status; |
||||
template <typename T> |
||||
class StatusOr; |
||||
|
||||
namespace status_internal { |
||||
std::string* MakeCheckFailString(const absl::Status* status, |
||||
const char* prefix); |
||||
} // namespace status_internal
|
||||
|
||||
namespace log_internal { |
||||
|
||||
// Convert a Status or a StatusOr to its underlying status value.
|
||||
//
|
||||
// (This implementation does not require a dep on absl::Status to work.)
|
||||
inline const absl::Status* AsStatus(const absl::Status& s) { return &s; } |
||||
template <typename T> |
||||
const absl::Status* AsStatus(const absl::StatusOr<T>& s) { |
||||
return &s.status(); |
||||
} |
||||
|
||||
// A helper class for formatting `expr (V1 vs. V2)` in a `CHECK_XX` statement.
|
||||
// See `MakeCheckOpString` for sample usage.
|
||||
class CheckOpMessageBuilder final { |
||||
public: |
||||
// Inserts `exprtext` and ` (` to the stream.
|
||||
explicit CheckOpMessageBuilder(const char* exprtext); |
||||
~CheckOpMessageBuilder() = default; |
||||
// For inserting the first variable.
|
||||
std::ostream& ForVar1() { return stream_; } |
||||
// For inserting the second variable (adds an intermediate ` vs. `).
|
||||
std::ostream& ForVar2(); |
||||
// Get the result (inserts the closing `)`).
|
||||
std::string* NewString(); |
||||
|
||||
private: |
||||
std::ostringstream stream_; |
||||
}; |
||||
|
||||
// This formats a value for a failing `CHECK_XX` statement. Ordinarily, it uses
|
||||
// the definition for `operator<<`, with a few special cases below.
|
||||
template <typename T> |
||||
inline void MakeCheckOpValueString(std::ostream& os, const T& v) { |
||||
os << log_internal::NullGuard<T>::Guard(v); |
||||
} |
||||
|
||||
// Overloads for char types provide readable values for unprintable characters.
|
||||
void MakeCheckOpValueString(std::ostream& os, char v); |
||||
void MakeCheckOpValueString(std::ostream& os, signed char v); |
||||
void MakeCheckOpValueString(std::ostream& os, unsigned char v); |
||||
void MakeCheckOpValueString(std::ostream& os, const void* p); |
||||
|
||||
namespace detect_specialization { |
||||
|
||||
// MakeCheckOpString is being specialized for every T and U pair that is being
|
||||
// passed to the CHECK_op macros. However, there is a lot of redundancy in these
|
||||
// specializations that creates unnecessary library and binary bloat.
|
||||
// The number of instantiations tends to be O(n^2) because we have two
|
||||
// independent inputs. This technique works by reducing `n`.
|
||||
//
|
||||
// Most user-defined types being passed to CHECK_op end up being printed as a
|
||||
// builtin type. For example, enums tend to be implicitly converted to its
|
||||
// underlying type when calling operator<<, and pointers are printed with the
|
||||
// `const void*` overload.
|
||||
// To reduce the number of instantiations we coerce these values before calling
|
||||
// MakeCheckOpString instead of inside it.
|
||||
//
|
||||
// To detect if this coercion is needed, we duplicate all the relevant
|
||||
// operator<< overloads as specified in the standard, just in a different
|
||||
// namespace. If the call to `stream << value` becomes ambiguous, it means that
|
||||
// one of these overloads is the one selected by overload resolution. We then
|
||||
// do overload resolution again just with our overload set to see which one gets
|
||||
// selected. That tells us which type to coerce to.
|
||||
// If the augmented call was not ambiguous, it means that none of these were
|
||||
// selected and we can't coerce the input.
|
||||
//
|
||||
// As a secondary step to reduce code duplication, we promote integral types to
|
||||
// their 64-bit variant. This does not change the printed value, but reduces the
|
||||
// number of instantiations even further. Promoting an integer is very cheap at
|
||||
// the call site.
|
||||
int64_t operator<<(std::ostream&, short value); // NOLINT
|
||||
int64_t operator<<(std::ostream&, unsigned short value); // NOLINT
|
||||
int64_t operator<<(std::ostream&, int value); |
||||
int64_t operator<<(std::ostream&, unsigned int value); |
||||
int64_t operator<<(std::ostream&, long value); // NOLINT
|
||||
uint64_t operator<<(std::ostream&, unsigned long value); // NOLINT
|
||||
int64_t operator<<(std::ostream&, long long value); // NOLINT
|
||||
uint64_t operator<<(std::ostream&, unsigned long long value); // NOLINT
|
||||
float operator<<(std::ostream&, float value); |
||||
double operator<<(std::ostream&, double value); |
||||
long double operator<<(std::ostream&, long double value); |
||||
bool operator<<(std::ostream&, bool value); |
||||
const void* operator<<(std::ostream&, const void* value); |
||||
const void* operator<<(std::ostream&, std::nullptr_t); |
||||
|
||||
// These `char` overloads are specified like this in the standard, so we have to
|
||||
// write them exactly the same to ensure the call is ambiguous.
|
||||
// If we wrote it in a different way (eg taking std::ostream instead of the
|
||||
// template) then one call might have a higher rank than the other and it would
|
||||
// not be ambiguous.
|
||||
template <typename Traits> |
||||
char operator<<(std::basic_ostream<char, Traits>&, char); |
||||
template <typename Traits> |
||||
signed char operator<<(std::basic_ostream<char, Traits>&, signed char); |
||||
template <typename Traits> |
||||
unsigned char operator<<(std::basic_ostream<char, Traits>&, unsigned char); |
||||
template <typename Traits> |
||||
const char* operator<<(std::basic_ostream<char, Traits>&, const char*); |
||||
template <typename Traits> |
||||
const signed char* operator<<(std::basic_ostream<char, Traits>&, |
||||
const signed char*); |
||||
template <typename Traits> |
||||
const unsigned char* operator<<(std::basic_ostream<char, Traits>&, |
||||
const unsigned char*); |
||||
|
||||
// This overload triggers when the call is not ambiguous.
|
||||
// It means that T is being printed with some overload not on this list.
|
||||
// We keep the value as `const T&`.
|
||||
template <typename T, typename = decltype(std::declval<std::ostream&>() |
||||
<< std::declval<const T&>())> |
||||
const T& Detect(int); |
||||
|
||||
// This overload triggers when the call is ambiguous.
|
||||
// It means that T is either one from this list or printed as one from this
|
||||
// list. Eg an enum that decays to `int` for printing.
|
||||
// We ask the overload set to give us the type we want to convert it to.
|
||||
template <typename T> |
||||
decltype(detect_specialization::operator<<(std::declval<std::ostream&>(), |
||||
std::declval<const T&>())) |
||||
Detect(char); |
||||
|
||||
} // namespace detect_specialization
|
||||
|
||||
template <typename T> |
||||
using CheckOpStreamType = decltype(detect_specialization::Detect<T>(0)); |
||||
|
||||
// Build the error message string. Specify no inlining for code size.
|
||||
template <typename T1, typename T2> |
||||
ABSL_ATTRIBUTE_RETURNS_NONNULL std::string* MakeCheckOpString( |
||||
T1 v1, T2 v2, const char* exprtext) ABSL_ATTRIBUTE_NOINLINE; |
||||
|
||||
template <typename T1, typename T2> |
||||
std::string* MakeCheckOpString(T1 v1, T2 v2, const char* exprtext) { |
||||
CheckOpMessageBuilder comb(exprtext); |
||||
MakeCheckOpValueString(comb.ForVar1(), v1); |
||||
MakeCheckOpValueString(comb.ForVar2(), v2); |
||||
return comb.NewString(); |
||||
} |
||||
|
||||
// Add a few commonly used instantiations as extern to reduce size of objects
|
||||
// files.
|
||||
#define ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(x) \ |
||||
extern template std::string* MakeCheckOpString(x, x, const char*) |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(bool); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(int64_t); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(uint64_t); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(float); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(double); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(char); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(unsigned char); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const std::string&); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const absl::string_view&); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const char*); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const signed char*); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const unsigned char*); |
||||
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void*); |
||||
#undef ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN |
||||
|
||||
// Helper functions for `ABSL_LOG_INTERNAL_CHECK_OP` macro family. The
|
||||
// `(int, int)` override works around the issue that the compiler will not
|
||||
// instantiate the template version of the function on values of unnamed enum
|
||||
// type.
|
||||
#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL(name, op) \ |
||||
template <typename T1, typename T2> \
|
||||
inline constexpr ::std::string* name##Impl(const T1& v1, const T2& v2, \
|
||||
const char* exprtext) { \
|
||||
using U1 = CheckOpStreamType<T1>; \
|
||||
using U2 = CheckOpStreamType<T2>; \
|
||||
return ABSL_PREDICT_TRUE(v1 op v2) \
|
||||
? nullptr \
|
||||
: MakeCheckOpString<U1, U2>(v1, v2, exprtext); \
|
||||
} \
|
||||
inline constexpr ::std::string* name##Impl(int v1, int v2, \
|
||||
const char* exprtext) { \
|
||||
return name##Impl<int, int>(v1, v2, exprtext); \
|
||||
} |
||||
|
||||
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_EQ, ==) |
||||
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_NE, !=) |
||||
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LE, <=) |
||||
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LT, <) |
||||
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GE, >=) |
||||
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GT, >) |
||||
#undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL |
||||
|
||||
std::string* CheckstrcmptrueImpl(const char* s1, const char* s2, |
||||
const char* exprtext); |
||||
std::string* CheckstrcmpfalseImpl(const char* s1, const char* s2, |
||||
const char* exprtext); |
||||
std::string* CheckstrcasecmptrueImpl(const char* s1, const char* s2, |
||||
const char* exprtext); |
||||
std::string* CheckstrcasecmpfalseImpl(const char* s1, const char* s2, |
||||
const char* exprtext); |
||||
|
||||
// `CHECK_EQ` and friends want to pass their arguments by reference, however
|
||||
// this winds up exposing lots of cases where people have defined and
|
||||
// initialized static const data members but never declared them (i.e. in a .cc
|
||||
// file), meaning they are not referenceable. This function avoids that problem
|
||||
// for integers (the most common cases) by overloading for every primitive
|
||||
// integer type, even the ones we discourage, and returning them by value.
|
||||
template <typename T> |
||||
inline constexpr const T& GetReferenceableValue(const T& t) { |
||||
return t; |
||||
} |
||||
inline constexpr char GetReferenceableValue(char t) { return t; } |
||||
inline constexpr unsigned char GetReferenceableValue(unsigned char t) { |
||||
return t; |
||||
} |
||||
inline constexpr signed char GetReferenceableValue(signed char t) { return t; } |
||||
inline constexpr short GetReferenceableValue(short t) { return t; } // NOLINT
|
||||
inline constexpr unsigned short GetReferenceableValue( // NOLINT
|
||||
unsigned short t) { // NOLINT
|
||||
return t; |
||||
} |
||||
inline constexpr int GetReferenceableValue(int t) { return t; } |
||||
inline unsigned int GetReferenceableValue(unsigned int t) { return t; } |
||||
inline constexpr long GetReferenceableValue(long t) { return t; } // NOLINT
|
||||
inline constexpr unsigned long GetReferenceableValue( // NOLINT
|
||||
unsigned long t) { // NOLINT
|
||||
return t; |
||||
} |
||||
inline constexpr long long GetReferenceableValue(long long t) { // NOLINT
|
||||
return t; |
||||
} |
||||
inline constexpr unsigned long long GetReferenceableValue( // NOLINT
|
||||
unsigned long long t) { // NOLINT
|
||||
return t; |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_CHECK_OP_H_
|
@ -0,0 +1,83 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/conditions.h" |
||||
|
||||
#include <atomic> |
||||
#include <cstdint> |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/base/internal/cycleclock.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
namespace { |
||||
|
||||
// The following code behaves like AtomicStatsCounter::LossyAdd() for
|
||||
// speed since it is fine to lose occasional updates.
|
||||
// Returns old value of *counter.
|
||||
uint32_t LossyIncrement(std::atomic<uint32_t>* counter) { |
||||
const uint32_t value = counter->load(std::memory_order_relaxed); |
||||
counter->store(value + 1, std::memory_order_relaxed); |
||||
return value; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
bool LogEveryNState::ShouldLog(int n) { |
||||
return n != 0 && (LossyIncrement(&counter_) % n) == 0; |
||||
} |
||||
|
||||
bool LogFirstNState::ShouldLog(int n) { |
||||
const uint32_t counter_value = counter_.load(std::memory_order_relaxed); |
||||
if (static_cast<int64_t>(counter_value) < n) { |
||||
counter_.store(counter_value + 1, std::memory_order_relaxed); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool LogEveryPow2State::ShouldLog() { |
||||
const uint32_t new_value = LossyIncrement(&counter_) + 1; |
||||
return (new_value & (new_value - 1)) == 0; |
||||
} |
||||
|
||||
bool LogEveryNSecState::ShouldLog(double seconds) { |
||||
using absl::base_internal::CycleClock; |
||||
LossyIncrement(&counter_); |
||||
const int64_t now_cycles = CycleClock::Now(); |
||||
int64_t next_cycles = next_log_time_cycles_.load(std::memory_order_relaxed); |
||||
#if defined(__myriad2__) |
||||
// myriad2 does not have 8-byte compare and exchange. Use a racy version that
|
||||
// is "good enough" but will over-log in the face of concurrent logging.
|
||||
if (now_cycles > next_cycles) { |
||||
next_log_time_cycles_.store(now_cycles + seconds * CycleClock::Frequency(), |
||||
std::memory_order_relaxed); |
||||
return true; |
||||
} |
||||
return false; |
||||
#else |
||||
do { |
||||
if (now_cycles <= next_cycles) return false; |
||||
} while (!next_log_time_cycles_.compare_exchange_weak( |
||||
next_cycles, now_cycles + seconds * CycleClock::Frequency(), |
||||
std::memory_order_relaxed, std::memory_order_relaxed)); |
||||
return true; |
||||
#endif |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,222 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/conditions.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This file contains implementation of conditional log statements, like LOG_IF
|
||||
// including all the ABSL_LOG_INTERNAL_..._CONDITION_... macros and
|
||||
// various condition classes like LogEveryNState.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_CONDITIONS_H_ |
||||
#define ABSL_LOG_INTERNAL_CONDITIONS_H_ |
||||
|
||||
#ifdef _WIN32 |
||||
#include <cstdlib> |
||||
#else |
||||
#include <unistd.h> |
||||
#endif |
||||
#include <stdlib.h> |
||||
|
||||
#include <atomic> |
||||
#include <cstdint> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/log/internal/voidify.h" |
||||
|
||||
// `ABSL_LOG_INTERNAL_CONDITION` prefixes another macro that expands to a
|
||||
// temporary `LogMessage` instantiation followed by zero or more streamed
|
||||
// expressions. This definition is tricky to read correctly. It evaluates to
|
||||
// either
|
||||
//
|
||||
// (void)0;
|
||||
//
|
||||
// or
|
||||
//
|
||||
// ::absl::log_internal::Voidify() &&
|
||||
// ::absl::log_internal::LogMessage(...) << "the user's message";
|
||||
//
|
||||
// If the condition is evaluable at compile time, as is often the case, it
|
||||
// compiles away to just one side or the other.
|
||||
//
|
||||
// Although this is not used anywhere a statement (e.g. `if`) could not go,
|
||||
// the ternary expression does a better job avoiding spurious diagnostics
|
||||
// (dangling else, missing switch case) and preserving noreturn semantics (e.g.
|
||||
// on `LOG(FATAL)`) without requiring braces.
|
||||
#define ABSL_LOG_INTERNAL_STATELESS_CONDITION(condition) \ |
||||
switch (0) \
|
||||
case 0: \
|
||||
!(condition) ? (void)0 : ::absl::log_internal::Voidify()&& |
||||
|
||||
// `ABSL_LOG_INTERNAL_STATEFUL_CONDITION` applies a condition like
|
||||
// `ABSL_LOG_INTERNAL_CONDITION` but adds to that a series of variable
|
||||
// declarations, including a local static object which stores the state needed
|
||||
// to implement the stateful macros like `LOG_EVERY_N`.
|
||||
//
|
||||
// `for`-loops are used to declare scoped variables without braces (to permit
|
||||
// streaming into the macro's expansion) and without the dangling-`else`
|
||||
// problems/diagnostics that come with `if`.
|
||||
//
|
||||
// Two more variables are declared in separate `for`-loops:
|
||||
//
|
||||
// * `COUNTER` implements a streamable token whose value when streamed is the
|
||||
// number of times execution has passed through the macro.
|
||||
// * A boolean flag is used to prevent any of the `for`-loops from ever actually
|
||||
// looping.
|
||||
#define ABSL_LOG_INTERNAL_STATEFUL_CONDITION(condition) \ |
||||
for (bool absl_log_internal_stateful_condition_do_log(condition); \
|
||||
absl_log_internal_stateful_condition_do_log; \
|
||||
absl_log_internal_stateful_condition_do_log = false) \
|
||||
ABSL_LOG_INTERNAL_STATEFUL_CONDITION_IMPL |
||||
#define ABSL_LOG_INTERNAL_STATEFUL_CONDITION_IMPL(kind, ...) \ |
||||
for (static ::absl::log_internal::Log##kind##State \
|
||||
absl_log_internal_stateful_condition_state; \
|
||||
absl_log_internal_stateful_condition_do_log && \
|
||||
absl_log_internal_stateful_condition_state.ShouldLog(__VA_ARGS__); \
|
||||
absl_log_internal_stateful_condition_do_log = false) \
|
||||
for (const uint32_t COUNTER ABSL_ATTRIBUTE_UNUSED = \
|
||||
absl_log_internal_stateful_condition_state.counter(); \
|
||||
absl_log_internal_stateful_condition_do_log; \
|
||||
absl_log_internal_stateful_condition_do_log = false) |
||||
|
||||
// `ABSL_LOG_INTERNAL_CONDITION_*` serve to combine any conditions from the
|
||||
// macro (e.g. `LOG_IF` or `VLOG`) with inherent conditions (e.g.
|
||||
// `ABSL_MIN_LOG_LEVEL`) into a single boolean expression. We could chain
|
||||
// ternary operators instead, however some versions of Clang sometimes issue
|
||||
// spurious diagnostics after such expressions due to a control flow analysis
|
||||
// bug.
|
||||
#ifdef ABSL_MIN_LOG_LEVEL |
||||
#define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION( \
|
||||
(condition) && ::absl::LogSeverity::kInfo >= \
|
||||
static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL)) |
||||
#define ABSL_LOG_INTERNAL_CONDITION_WARNING(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION( \
|
||||
(condition) && ::absl::LogSeverity::kWarning >= \
|
||||
static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL)) |
||||
#define ABSL_LOG_INTERNAL_CONDITION_ERROR(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION( \
|
||||
(condition) && ::absl::LogSeverity::kError >= \
|
||||
static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL)) |
||||
// NOTE: Use ternary operators instead of short-circuiting to mitigate
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=51928.
|
||||
#define ABSL_LOG_INTERNAL_CONDITION_FATAL(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION( \
|
||||
((condition) \
|
||||
? (::absl::LogSeverity::kFatal >= \
|
||||
static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \
|
||||
? true \
|
||||
: (::absl::log_internal::AbortQuietly(), false)) \
|
||||
: false)) |
||||
// NOTE: Use ternary operators instead of short-circuiting to mitigate
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=51928.
|
||||
#define ABSL_LOG_INTERNAL_CONDITION_QFATAL(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION( \
|
||||
((condition) \
|
||||
? (::absl::LogSeverity::kFatal >= \
|
||||
static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \
|
||||
? true \
|
||||
: (::absl::log_internal::ExitQuietly(), false)) \
|
||||
: false)) |
||||
|
||||
#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \ |
||||
for (int log_internal_severity_loop = 1; log_internal_severity_loop; \
|
||||
log_internal_severity_loop = 0) \
|
||||
for (const absl::LogSeverity log_internal_severity = \
|
||||
::absl::NormalizeLogSeverity(severity); \
|
||||
log_internal_severity_loop; log_internal_severity_loop = 0) \
|
||||
ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL |
||||
#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION( \
|
||||
(condition) && \
|
||||
(log_internal_severity >= \
|
||||
static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \
|
||||
(log_internal_severity == ::absl::LogSeverity::kFatal && \
|
||||
(::absl::log_internal::AbortQuietly(), false)))) |
||||
#else // ndef ABSL_MIN_LOG_LEVEL
|
||||
#define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION(condition) |
||||
#define ABSL_LOG_INTERNAL_CONDITION_WARNING(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION(condition) |
||||
#define ABSL_LOG_INTERNAL_CONDITION_ERROR(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION(condition) |
||||
#define ABSL_LOG_INTERNAL_CONDITION_FATAL(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION(condition) |
||||
#define ABSL_LOG_INTERNAL_CONDITION_QFATAL(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION(condition) |
||||
#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \ |
||||
for (int log_internal_severity_loop = 1; log_internal_severity_loop; \
|
||||
log_internal_severity_loop = 0) \
|
||||
for (const absl::LogSeverity log_internal_severity = \
|
||||
::absl::NormalizeLogSeverity(severity); \
|
||||
log_internal_severity_loop; log_internal_severity_loop = 0) \
|
||||
ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL |
||||
#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \ |
||||
ABSL_LOG_INTERNAL_##type##_CONDITION(condition) |
||||
#endif // ndef ABSL_MIN_LOG_LEVEL
|
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// Stateful condition class name should be "Log" + name + "State".
|
||||
class LogEveryNState final { |
||||
public: |
||||
bool ShouldLog(int n); |
||||
uint32_t counter() { return counter_.load(std::memory_order_relaxed); } |
||||
|
||||
private: |
||||
std::atomic<uint32_t> counter_{0}; |
||||
}; |
||||
|
||||
class LogFirstNState final { |
||||
public: |
||||
bool ShouldLog(int n); |
||||
uint32_t counter() { return counter_.load(std::memory_order_relaxed); } |
||||
|
||||
private: |
||||
std::atomic<uint32_t> counter_{0}; |
||||
}; |
||||
|
||||
class LogEveryPow2State final { |
||||
public: |
||||
bool ShouldLog(); |
||||
uint32_t counter() { return counter_.load(std::memory_order_relaxed); } |
||||
|
||||
private: |
||||
std::atomic<uint32_t> counter_{0}; |
||||
}; |
||||
|
||||
class LogEveryNSecState final { |
||||
public: |
||||
bool ShouldLog(double seconds); |
||||
uint32_t counter() { return counter_.load(std::memory_order_relaxed); } |
||||
|
||||
private: |
||||
std::atomic<uint32_t> counter_{0}; |
||||
// Cycle count according to CycleClock that we should next log at.
|
||||
std::atomic<int64_t> next_log_time_cycles_{0}; |
||||
}; |
||||
|
||||
// Helper routines to abort the application quietly
|
||||
|
||||
ABSL_ATTRIBUTE_NORETURN inline void AbortQuietly() { abort(); } |
||||
ABSL_ATTRIBUTE_NORETURN inline void ExitQuietly() { _exit(1); } |
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_CONDITIONS_H_
|
@ -0,0 +1,45 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/config.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_CONFIG_H_ |
||||
#define ABSL_LOG_INTERNAL_CONFIG_H_ |
||||
|
||||
#include "absl/base/config.h" |
||||
|
||||
#ifdef _WIN32 |
||||
#include <cstdint> |
||||
#else |
||||
#include <sys/types.h> |
||||
#endif |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
#ifdef _WIN32 |
||||
using Tid = uint32_t; |
||||
#else |
||||
using Tid = pid_t; |
||||
#endif |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_CONFIG_H_
|
@ -0,0 +1,53 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/log_flags.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares set of flags which can be used to configure Abseil
|
||||
// Logging library behaviour at runtime.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_FLAGS_H_ |
||||
#define ABSL_LOG_INTERNAL_FLAGS_H_ |
||||
|
||||
#include <string> |
||||
|
||||
#include "absl/flags/declare.h" |
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// These flags should not be used in C++ code to access logging library
|
||||
// configuration knobs. Use interfaces defined in absl/log/globals.h
|
||||
// instead. It is still ok to use these flags on a command line.
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// Log messages at this severity or above are sent to stderr in *addition* to
|
||||
// logfiles. Defaults to `ERROR`. See log_severity.h for numeric values of
|
||||
// severity levels.
|
||||
ABSL_DECLARE_FLAG(int, stderrthreshold); |
||||
|
||||
// Log messages at this severity or above are logged; others are discarded.
|
||||
// Defaults to `INFO`, i.e. log all severities. See log_severity.h for numeric
|
||||
// values of severity levels.
|
||||
ABSL_DECLARE_FLAG(int, minloglevel); |
||||
|
||||
// If specified in the form file:linenum, any messages logged from a matching
|
||||
// location will also include a backtrace.
|
||||
ABSL_DECLARE_FLAG(std::string, log_backtrace_at); |
||||
|
||||
// If true, the log prefix (severity, date, time, PID, etc.) is prepended to
|
||||
// each message logged. Defaults to true.
|
||||
ABSL_DECLARE_FLAG(bool, log_prefix); |
||||
|
||||
#endif // ABSL_LOG_INTERNAL_FLAGS_H_
|
@ -0,0 +1,125 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/globals.h" |
||||
|
||||
#include <atomic> |
||||
#include <cstdio> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/internal/raw_logging.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/time.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
namespace { |
||||
// Keeps track of whether Logging initialization is finalized.
|
||||
// Log messages generated before that will go to stderr.
|
||||
ABSL_CONST_INIT std::atomic<bool> logging_initialized(false); |
||||
|
||||
// The TimeZone used for logging. This may only be set once.
|
||||
ABSL_CONST_INIT std::atomic<absl::TimeZone*> timezone_ptr{nullptr}; |
||||
|
||||
// If true, the logging library will symbolize stack in fatal messages
|
||||
ABSL_CONST_INIT std::atomic<bool> symbolize_stack_trace(true); |
||||
|
||||
// Specifies maximum number of stack frames to report in fatal messages.
|
||||
ABSL_CONST_INIT std::atomic<int> max_frames_in_stack_trace(64); |
||||
|
||||
ABSL_CONST_INIT std::atomic<bool> exit_on_dfatal(true); |
||||
ABSL_CONST_INIT std::atomic<bool> suppress_sigabort_trace(false); |
||||
} // namespace
|
||||
|
||||
bool IsInitialized() { |
||||
return logging_initialized.load(std::memory_order_acquire); |
||||
} |
||||
|
||||
void SetInitialized() { |
||||
logging_initialized.store(true, std::memory_order_release); |
||||
} |
||||
|
||||
void WriteToStderr(absl::string_view message, absl::LogSeverity severity) { |
||||
// Avoid using std::cerr from this module since we may get called during
|
||||
// exit code, and cerr may be partially or fully destroyed by then.
|
||||
std::fwrite(message.data(), message.size(), 1, stderr); |
||||
|
||||
#if defined(_WIN64) || defined(_WIN32) || defined(_WIN16) |
||||
// C99 requires stderr to not be fully-buffered by default (7.19.3.7), but
|
||||
// MS CRT buffers it anyway, so we must `fflush` to ensure the string hits
|
||||
// the console/file before the program dies (and takes the libc buffers
|
||||
// with it).
|
||||
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/stream-i-o
|
||||
if (severity >= absl::LogSeverity::kWarning) { |
||||
std::fflush(stderr); |
||||
} |
||||
#else |
||||
// Avoid unused parameter warning in this branch.
|
||||
(void)severity; |
||||
#endif |
||||
} |
||||
|
||||
void SetTimeZone(absl::TimeZone tz) { |
||||
absl::TimeZone* expected = nullptr; |
||||
absl::TimeZone* new_tz = new absl::TimeZone(tz); |
||||
// timezone_ptr can only be set once, otherwise new_tz is leaked.
|
||||
if (!timezone_ptr.compare_exchange_strong(expected, new_tz, |
||||
std::memory_order_release, |
||||
std::memory_order_relaxed)) { |
||||
ABSL_RAW_LOG(FATAL, |
||||
"absl::log_internal::SetTimeZone() has already been called"); |
||||
} |
||||
} |
||||
|
||||
const absl::TimeZone* TimeZone() { |
||||
return timezone_ptr.load(std::memory_order_acquire); |
||||
} |
||||
|
||||
bool ShouldSymbolizeLogStackTrace() { |
||||
return symbolize_stack_trace.load(std::memory_order_acquire); |
||||
} |
||||
|
||||
void EnableSymbolizeLogStackTrace(bool on_off) { |
||||
symbolize_stack_trace.store(on_off, std::memory_order_release); |
||||
} |
||||
|
||||
int MaxFramesInLogStackTrace() { |
||||
return max_frames_in_stack_trace.load(std::memory_order_acquire); |
||||
} |
||||
|
||||
void SetMaxFramesInLogStackTrace(int max_num_frames) { |
||||
max_frames_in_stack_trace.store(max_num_frames, std::memory_order_release); |
||||
} |
||||
|
||||
bool ExitOnDFatal() { return exit_on_dfatal.load(std::memory_order_acquire); } |
||||
|
||||
void SetExitOnDFatal(bool on_off) { |
||||
exit_on_dfatal.store(on_off, std::memory_order_release); |
||||
} |
||||
|
||||
bool SuppressSigabortTrace() { |
||||
return suppress_sigabort_trace.load(std::memory_order_acquire); |
||||
} |
||||
|
||||
bool SetSuppressSigabortTrace(bool on_off) { |
||||
return suppress_sigabort_trace.exchange(on_off); |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,101 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/globals.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header file contains various global objects and static helper routines
|
||||
// use in logging implementation.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_GLOBALS_H_ |
||||
#define ABSL_LOG_INTERNAL_GLOBALS_H_ |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/time.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// IsInitialized returns true if the logging library is initialized.
|
||||
// This function is async-signal-safe
|
||||
bool IsInitialized(); |
||||
|
||||
// SetLoggingInitialized is called once after logging initialization is done.
|
||||
void SetInitialized(); |
||||
|
||||
// Unconditionally write a `message` to stderr. If `severity` exceeds kInfo
|
||||
// we also flush the stderr stream.
|
||||
void WriteToStderr(absl::string_view message, absl::LogSeverity severity); |
||||
|
||||
// Set the TimeZone used for human-friendly times (for example, the log message
|
||||
// prefix) printed by the logging library. This may only be called once.
|
||||
void SetTimeZone(absl::TimeZone tz); |
||||
|
||||
// Returns the TimeZone used for human-friendly times (for example, the log
|
||||
// message prefix) printed by the logging library Returns nullptr prior to
|
||||
// initialization.
|
||||
const absl::TimeZone* TimeZone(); |
||||
|
||||
// Returns true if stack traces emitted by the logging library should be
|
||||
// symbolized. This function is async-signal-safe.
|
||||
bool ShouldSymbolizeLogStackTrace(); |
||||
|
||||
// Enables or disables symbolization of stack traces emitted by the
|
||||
// logging library. This function is async-signal-safe.
|
||||
void EnableSymbolizeLogStackTrace(bool on_off); |
||||
|
||||
// Returns the maximum number of frames that appear in stack traces
|
||||
// emitted by the logging library. This function is async-signal-safe.
|
||||
int MaxFramesInLogStackTrace(); |
||||
|
||||
// Sets the maximum number of frames that appear in stack traces emitted by
|
||||
// the logging library. This function is async-signal-safe.
|
||||
void SetMaxFramesInLogStackTrace(int max_num_frames); |
||||
|
||||
// Determines whether we exit the program for a LOG(DFATAL) message in
|
||||
// debug mode. It does this by skipping the call to Fail/FailQuietly.
|
||||
// This is intended for testing only.
|
||||
//
|
||||
// This can have some effects on LOG(FATAL) as well. Failure messages
|
||||
// are always allocated (rather than sharing a buffer), the crash
|
||||
// reason is not recorded, the "gwq" status message is not updated,
|
||||
// and the stack trace is not recorded. The LOG(FATAL) *will* still
|
||||
// exit the program. Since this function is used only in testing,
|
||||
// these differences are acceptable.
|
||||
//
|
||||
// Additionally, LOG(LEVEL(FATAL)) is indistinguishable from LOG(DFATAL) and
|
||||
// will not terminate the program if SetExitOnDFatal(false) has been called.
|
||||
bool ExitOnDFatal(); |
||||
|
||||
// SetExitOnDFatal() sets the ExitOnDFatal() status
|
||||
void SetExitOnDFatal(bool on_off); |
||||
|
||||
// Determines if the logging library should suppress logging of stacktraces in
|
||||
// the `SIGABRT` handler, typically because we just logged a stacktrace as part
|
||||
// of `LOG(FATAL)` and are about to send ourselves a `SIGABRT` to end the
|
||||
// program.
|
||||
bool SuppressSigabortTrace(); |
||||
|
||||
// Sets the SuppressSigabortTrace() status and returns the previous state.
|
||||
bool SetSuppressSigabortTrace(bool on_off); |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_GLOBALS_H_
|
@ -0,0 +1,189 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/log_format.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#ifdef _MSC_VER |
||||
#include <winsock2.h> // For timeval |
||||
#else |
||||
#include <sys/time.h> |
||||
#endif |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <limits> |
||||
#include <string> |
||||
#include <type_traits> |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/base/optimization.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/log/internal/globals.h" |
||||
#include "absl/strings/numbers.h" |
||||
#include "absl/strings/str_format.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/civil_time.h" |
||||
#include "absl/time/time.h" |
||||
#include "absl/types/span.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
namespace { |
||||
|
||||
// The fields before the filename are all fixed-width except for the thread ID,
|
||||
// which is of bounded width.
|
||||
size_t FormatBoundedFields(absl::LogSeverity severity, absl::Time timestamp, |
||||
log_internal::Tid tid, absl::Span<char>& buf) { |
||||
constexpr size_t kBoundedFieldsMaxLen = |
||||
sizeof("SMMDD HH:MM:SS.NNNNNN ") + |
||||
(1 + std::numeric_limits<log_internal::Tid>::digits10 + 1) - sizeof(""); |
||||
if (ABSL_PREDICT_FALSE(buf.size() < kBoundedFieldsMaxLen)) { |
||||
// We don't bother trying to truncate these fields if the buffer is too
|
||||
// short (or almost too short) because it would require doing a lot more
|
||||
// length checking (slow) and it should never happen. A 15kB buffer should
|
||||
// be enough for anyone. Instead we mark `buf` full without writing
|
||||
// anything.
|
||||
buf.remove_suffix(buf.size()); |
||||
return 0; |
||||
} |
||||
|
||||
// We can't call absl::LocalTime(), localtime_r(), or anything else here that
|
||||
// isn't async-signal-safe. We can only use the time zone if it has already
|
||||
// been loaded.
|
||||
const absl::TimeZone* tz = absl::log_internal::TimeZone(); |
||||
if (ABSL_PREDICT_FALSE(tz == nullptr)) { |
||||
// If a time zone hasn't been set yet because we are logging before the
|
||||
// logging library has been initialized, we fallback to a simpler, slower
|
||||
// method. Just report the raw Unix time in seconds. We cram this into the
|
||||
// normal time format for the benefit of parsers.
|
||||
auto tv = absl::ToTimeval(timestamp); |
||||
int snprintf_result = absl::SNPrintF( |
||||
buf.data(), buf.size(), "%c0000 00:00:%02d.%06d %7d ", |
||||
absl::LogSeverityName(severity)[0], static_cast<int>(tv.tv_sec), |
||||
static_cast<int>(tv.tv_usec), static_cast<int>(tid)); |
||||
if (snprintf_result >= 0) { |
||||
buf.remove_prefix(snprintf_result); |
||||
return static_cast<size_t>(snprintf_result); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
char* p = buf.data(); |
||||
*p++ = absl::LogSeverityName(severity)[0]; |
||||
const absl::TimeZone::CivilInfo ci = tz->At(timestamp); |
||||
absl::numbers_internal::PutTwoDigits(ci.cs.month(), p); |
||||
p += 2; |
||||
absl::numbers_internal::PutTwoDigits(ci.cs.day(), p); |
||||
p += 2; |
||||
*p++ = ' '; |
||||
absl::numbers_internal::PutTwoDigits(ci.cs.hour(), p); |
||||
p += 2; |
||||
*p++ = ':'; |
||||
absl::numbers_internal::PutTwoDigits(ci.cs.minute(), p); |
||||
p += 2; |
||||
*p++ = ':'; |
||||
absl::numbers_internal::PutTwoDigits(ci.cs.second(), p); |
||||
p += 2; |
||||
*p++ = '.'; |
||||
const int64_t usecs = absl::ToInt64Microseconds(ci.subsecond); |
||||
absl::numbers_internal::PutTwoDigits(usecs / 10000, p); |
||||
p += 2; |
||||
absl::numbers_internal::PutTwoDigits(usecs / 100 % 100, p); |
||||
p += 2; |
||||
absl::numbers_internal::PutTwoDigits(usecs % 100, p); |
||||
p += 2; |
||||
*p++ = ' '; |
||||
constexpr bool unsigned_tid_t = !std::is_signed<log_internal::Tid>::value; |
||||
if ((unsigned_tid_t || tid >= 0) && tid < 10) *p++ = ' '; |
||||
if ((unsigned_tid_t || tid > -10) && tid < 100) *p++ = ' '; |
||||
if ((unsigned_tid_t || tid > -100) && tid < 1000) *p++ = ' '; |
||||
if ((unsigned_tid_t || tid > -1000) && tid < 10000) *p++ = ' '; |
||||
if ((unsigned_tid_t || tid > -10000) && tid < 100000) *p++ = ' '; |
||||
if ((unsigned_tid_t || tid > -100000) && tid < 1000000) *p++ = ' '; |
||||
p = absl::numbers_internal::FastIntToBuffer(tid, p); |
||||
*p++ = ' '; |
||||
const size_t bytes_formatted = p - buf.data(); |
||||
buf.remove_prefix(bytes_formatted); |
||||
return bytes_formatted; |
||||
} |
||||
|
||||
// Copies into `dst` as many bytes of `src` as will fit, then advances `dst`
|
||||
// past the copied bytes and returns the number of bytes written.
|
||||
size_t AppendTruncated(absl::string_view src, absl::Span<char>& dst) { |
||||
if (src.size() > dst.size()) src = src.substr(0, dst.size()); |
||||
memcpy(dst.data(), src.data(), src.size()); |
||||
dst.remove_prefix(src.size()); |
||||
return src.size(); |
||||
} |
||||
|
||||
size_t FormatLineNumber(int line, absl::Span<char>& buf) { |
||||
constexpr size_t kLineFieldMaxLen = |
||||
sizeof(":] ") + (1 + std::numeric_limits<int>::digits10 + 1) - sizeof(""); |
||||
if (ABSL_PREDICT_FALSE(buf.size() < kLineFieldMaxLen)) { |
||||
// As above, we don't bother trying to truncate this if the buffer is too
|
||||
// short and it should never happen.
|
||||
buf.remove_suffix(buf.size()); |
||||
return 0; |
||||
} |
||||
char* p = buf.data(); |
||||
*p++ = ':'; |
||||
p = absl::numbers_internal::FastIntToBuffer(line, p); |
||||
*p++ = ']'; |
||||
*p++ = ' '; |
||||
const size_t bytes_formatted = p - buf.data(); |
||||
buf.remove_prefix(bytes_formatted); |
||||
return bytes_formatted; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
std::string FormatLogMessage(absl::LogSeverity severity, |
||||
absl::CivilSecond civil_second, |
||||
absl::Duration subsecond, log_internal::Tid tid, |
||||
absl::string_view basename, int line, |
||||
absl::string_view message) { |
||||
return absl::StrFormat( |
||||
"%c%02d%02d %02d:%02d:%02d.%06d %7d %s:%d] %s", |
||||
absl::LogSeverityName(severity)[0], civil_second.month(), |
||||
civil_second.day(), civil_second.hour(), civil_second.minute(), |
||||
civil_second.second(), absl::ToInt64Microseconds(subsecond), tid, |
||||
basename, line, message); |
||||
} |
||||
|
||||
// This method is fairly hot, and the library always passes a huge `buf`, so we
|
||||
// save some bounds-checking cycles by not trying to do precise truncation.
|
||||
// Truncating at a field boundary is probably a better UX anyway.
|
||||
//
|
||||
// The prefix is written in three parts, each of which does a single
|
||||
// bounds-check and truncation:
|
||||
// 1. severity, timestamp, and thread ID
|
||||
// 2. filename
|
||||
// 3. line number and bracket
|
||||
size_t FormatLogPrefix(absl::LogSeverity severity, absl::Time timestamp, |
||||
log_internal::Tid tid, absl::string_view basename, |
||||
int line, absl::Span<char>& buf) { |
||||
auto prefix_size = FormatBoundedFields(severity, timestamp, tid, buf); |
||||
prefix_size += AppendTruncated(basename, buf); |
||||
prefix_size += FormatLineNumber(line, buf); |
||||
return prefix_size; |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,73 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/log_format.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This file declares routines implementing formatting of log message and log
|
||||
// prefix.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_LOG_FORMAT_H_ |
||||
#define ABSL_LOG_INTERNAL_LOG_FORMAT_H_ |
||||
|
||||
#include <stddef.h> |
||||
|
||||
#include <string> |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/civil_time.h" |
||||
#include "absl/time/time.h" |
||||
#include "absl/types/span.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// Formats log message based on provided data.
|
||||
std::string FormatLogMessage(absl::LogSeverity severity, |
||||
absl::CivilSecond civil_second, |
||||
absl::Duration subsecond, log_internal::Tid tid, |
||||
absl::string_view basename, int line, |
||||
absl::string_view message); |
||||
|
||||
// Formats various entry metadata into a text string meant for use as a
|
||||
// prefix on a log message string. Writes into `buf`, advances `buf` to point
|
||||
// at the remainder of the buffer (i.e. past any written bytes), and returns the
|
||||
// number of bytes written.
|
||||
//
|
||||
// In addition to calling `buf->remove_prefix()` (or the equivalent), this
|
||||
// function may also do `buf->remove_suffix(buf->size())` in cases where no more
|
||||
// bytes (i.e. no message data) should be written into the buffer. For example,
|
||||
// if the prefix ought to be:
|
||||
// I0926 09:00:00.000000 1234567 foo.cc:123]
|
||||
// `buf` is too small, the function might fill the whole buffer:
|
||||
// I0926 09:00:00.000000 1234
|
||||
// (note the apparrently incorrect thread ID), or it might write less:
|
||||
// I0926 09:00:00.000000
|
||||
// In this case, it might also empty `buf` prior to returning to prevent
|
||||
// message data from being written into the space where a reader would expect to
|
||||
// see a thread ID.
|
||||
size_t FormatLogPrefix(absl::LogSeverity severity, absl::Time timestamp, |
||||
log_internal::Tid tid, absl::string_view basename, |
||||
int line, absl::Span<char>& buf); |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_LOG_FORMAT_H_
|
@ -0,0 +1,508 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/log_message.h" |
||||
|
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#ifndef _WIN32 |
||||
#include <unistd.h> |
||||
#endif |
||||
|
||||
#include <algorithm> |
||||
#include <array> |
||||
#include <atomic> |
||||
#include <memory> |
||||
#include <ostream> |
||||
#include <string> |
||||
#include <tuple> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/internal/raw_logging.h" |
||||
#include "absl/base/internal/strerror.h" |
||||
#include "absl/base/internal/sysinfo.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/container/inlined_vector.h" |
||||
#include "absl/debugging/internal/examine_stack.h" |
||||
#include "absl/log/globals.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/log/internal/globals.h" |
||||
#include "absl/log/internal/log_format.h" |
||||
#include "absl/log/internal/log_sink_set.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/log/log_sink.h" |
||||
#include "absl/log/log_sink_registry.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/str_format.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/clock.h" |
||||
#include "absl/time/time.h" |
||||
#include "absl/types/span.h" |
||||
|
||||
extern "C" ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL( |
||||
AbslInternalOnFatalLogMessage)(const absl::LogEntry&) { |
||||
// Default - Do nothing
|
||||
} |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
namespace { |
||||
// Copies into `dst` as many bytes of `src` as will fit, then truncates the
|
||||
// copied bytes from the front of `dst` and returns the number of bytes written.
|
||||
size_t AppendTruncated(absl::string_view src, absl::Span<char>* dst) { |
||||
if (src.size() > dst->size()) src = src.substr(0, dst->size()); |
||||
memcpy(dst->data(), src.data(), src.size()); |
||||
dst->remove_prefix(src.size()); |
||||
return src.size(); |
||||
} |
||||
|
||||
absl::string_view Basename(absl::string_view filepath) { |
||||
#ifdef _WIN32 |
||||
size_t path = filepath.find_last_of("/\\"); |
||||
#else |
||||
size_t path = filepath.find_last_of('/'); |
||||
#endif |
||||
if (path != filepath.npos) filepath.remove_prefix(path + 1); |
||||
return filepath; |
||||
} |
||||
|
||||
void WriteToString(const char* data, void* str) { |
||||
reinterpret_cast<std::string*>(str)->append(data); |
||||
} |
||||
void WriteToStream(const char* data, void* os) { |
||||
auto* cast_os = static_cast<std::ostream*>(os); |
||||
*cast_os << data; |
||||
} |
||||
} // namespace
|
||||
|
||||
// A write-only `std::streambuf` that writes into an `absl::Span<char>`.
|
||||
//
|
||||
// This class is responsible for writing a metadata prefix just before the first
|
||||
// data are streamed in. The metadata are subject to change (cf.
|
||||
// `LogMessage::AtLocation`) until then, so we wait as long as possible.
|
||||
//
|
||||
// This class is also responsible for reserving space for a trailing newline
|
||||
// so that one can be added later by `Finalize` no matter how many data are
|
||||
// streamed in.
|
||||
class LogEntryStreambuf final : public std::streambuf { |
||||
public: |
||||
explicit LogEntryStreambuf(absl::Span<char> buf, const absl::LogEntry& entry) |
||||
: buf_(buf), entry_(entry), prefix_len_(0), finalized_(false) { |
||||
// To detect when data are first written, we leave the put area null,
|
||||
// override `overflow`, and check ourselves in `xsputn`.
|
||||
} |
||||
|
||||
LogEntryStreambuf(LogEntryStreambuf&&) = delete; |
||||
LogEntryStreambuf& operator=(LogEntryStreambuf&&) = delete; |
||||
|
||||
absl::Span<const char> Finalize() { |
||||
assert(!finalized_); |
||||
// If no data were ever streamed in, this is where we must write the prefix.
|
||||
if (pbase() == nullptr) Initialize(); |
||||
// Here we reclaim the two bytes we reserved.
|
||||
size_t idx = pptr() - pbase(); |
||||
setp(buf_.data(), buf_.data() + buf_.size()); |
||||
pbump(idx); |
||||
sputc('\n'); |
||||
sputc('\0'); |
||||
finalized_ = true; |
||||
return absl::Span<const char>(pbase(), pptr() - pbase()); |
||||
} |
||||
size_t prefix_len() const { return prefix_len_; } |
||||
|
||||
protected: |
||||
std::streamsize xsputn(const char* s, std::streamsize n) override { |
||||
if (pbase() == nullptr) Initialize(); |
||||
return Append(absl::string_view(s, n)); |
||||
} |
||||
|
||||
int overflow(int ch = EOF) override { |
||||
if (pbase() == nullptr) Initialize(); |
||||
if (ch == EOF) return 0; |
||||
if (pptr() == epptr()) return EOF; |
||||
*pptr() = static_cast<char>(ch); |
||||
pbump(1); |
||||
return 1; |
||||
} |
||||
|
||||
private: |
||||
void Initialize() { |
||||
// Here we reserve two bytes in our buffer to guarantee `Finalize` space to
|
||||
// add a trailing "\n\0".
|
||||
assert(buf_.size() >= 2); |
||||
setp(buf_.data(), buf_.data() + buf_.size() - 2); |
||||
if (entry_.prefix()) { |
||||
absl::Span<char> remaining = buf_; |
||||
prefix_len_ = log_internal::FormatLogPrefix( |
||||
entry_.log_severity(), entry_.timestamp(), entry_.tid(), |
||||
entry_.source_basename(), entry_.source_line(), remaining); |
||||
pbump(prefix_len_); |
||||
} |
||||
} |
||||
|
||||
size_t Append(absl::string_view data) { |
||||
absl::Span<char> remaining(pptr(), epptr() - pptr()); |
||||
const size_t written = AppendTruncated(data, &remaining); |
||||
pbump(written); |
||||
return written; |
||||
} |
||||
|
||||
const absl::Span<char> buf_; |
||||
const absl::LogEntry& entry_; |
||||
size_t prefix_len_; |
||||
bool finalized_; |
||||
}; |
||||
|
||||
struct LogMessage::LogMessageData final { |
||||
LogMessageData(const char* file, int line, absl::LogSeverity severity, |
||||
absl::Time timestamp); |
||||
LogMessageData(const LogMessageData&) = delete; |
||||
LogMessageData& operator=(const LogMessageData&) = delete; |
||||
|
||||
// `LogEntry` sent to `LogSink`s; contains metadata.
|
||||
absl::LogEntry entry; |
||||
|
||||
// true => this was first fatal msg
|
||||
bool first_fatal; |
||||
// true => all failures should be quiet
|
||||
bool fail_quietly; |
||||
// true => PLOG was requested
|
||||
bool is_perror; |
||||
|
||||
// Extra `LogSink`s to log to, in addition to `global_sinks`.
|
||||
absl::InlinedVector<absl::LogSink*, 16> extra_sinks; |
||||
// If true, log to `extra_sinks` but not to `global_sinks` or hardcoded
|
||||
// non-sink targets (e.g. stderr, log files).
|
||||
bool extra_sinks_only; |
||||
|
||||
// A formatted string message is built in `string_buf`.
|
||||
std::array<char, kLogMessageBufferSize> string_buf; |
||||
|
||||
// A `std::streambuf` that stores into `string_buf`.
|
||||
LogEntryStreambuf streambuf_; |
||||
}; |
||||
|
||||
LogMessage::LogMessageData::LogMessageData(const char* file, int line, |
||||
absl::LogSeverity severity, |
||||
absl::Time timestamp) |
||||
: extra_sinks_only(false), |
||||
streambuf_(absl::MakeSpan(string_buf), entry) { |
||||
entry.full_filename_ = file; |
||||
entry.base_filename_ = Basename(file); |
||||
entry.line_ = line; |
||||
entry.prefix_ = absl::ShouldPrependLogPrefix(); |
||||
entry.severity_ = absl::NormalizeLogSeverity(severity); |
||||
entry.verbose_level_ = absl::LogEntry::kNoVerbosityLevel; |
||||
entry.timestamp_ = timestamp; |
||||
entry.tid_ = absl::base_internal::GetCachedTID(); |
||||
} |
||||
|
||||
LogMessage::LogMessage(const char* file, int line, absl::LogSeverity severity) |
||||
: data_( |
||||
absl::make_unique<LogMessageData>(file, line, severity, absl::Now())) |
||||
, |
||||
stream_(&data_->streambuf_) |
||||
{ |
||||
data_->first_fatal = false; |
||||
data_->is_perror = false; |
||||
data_->fail_quietly = false; |
||||
|
||||
// Legacy defaults for LOG's ostream:
|
||||
stream_.setf(std::ios_base::showbase | std::ios_base::boolalpha); |
||||
// `fill('0')` is omitted here because its effects are very different without
|
||||
// structured logging. Resolution is tracked in b/111310488.
|
||||
|
||||
// This logs a backtrace even if the location is subsequently changed using
|
||||
// AtLocation. This quirk, and the behavior when AtLocation is called twice,
|
||||
// are fixable but probably not worth fixing.
|
||||
LogBacktraceIfNeeded(); |
||||
} |
||||
|
||||
LogMessage::~LogMessage() { |
||||
#ifdef ABSL_MIN_LOG_LEVEL |
||||
if (data_->entry.log_severity() < |
||||
static_cast<absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) && |
||||
data_->entry.log_severity() < absl::LogSeverity::kFatal) { |
||||
return; |
||||
} |
||||
#endif |
||||
Flush(); |
||||
} |
||||
|
||||
LogMessage& LogMessage::AtLocation(absl::string_view file, int line) { |
||||
data_->entry.full_filename_ = file; |
||||
data_->entry.base_filename_ = Basename(file); |
||||
data_->entry.line_ = line; |
||||
LogBacktraceIfNeeded(); |
||||
return *this; |
||||
} |
||||
|
||||
LogMessage& LogMessage::NoPrefix() { |
||||
data_->entry.prefix_ = false; |
||||
return *this; |
||||
} |
||||
|
||||
LogMessage& LogMessage::WithVerbosity(int verbose_level) { |
||||
if (verbose_level == absl::LogEntry::kNoVerbosityLevel) { |
||||
data_->entry.verbose_level_ = absl::LogEntry::kNoVerbosityLevel; |
||||
} else { |
||||
data_->entry.verbose_level_ = std::max(0, verbose_level); |
||||
} |
||||
return *this; |
||||
} |
||||
|
||||
LogMessage& LogMessage::WithTimestamp(absl::Time timestamp) { |
||||
data_->entry.timestamp_ = timestamp; |
||||
return *this; |
||||
} |
||||
|
||||
LogMessage& LogMessage::WithThreadID(absl::LogEntry::tid_t tid) { |
||||
data_->entry.tid_ = tid; |
||||
return *this; |
||||
} |
||||
|
||||
LogMessage& LogMessage::WithMetadataFrom(const absl::LogEntry& entry) { |
||||
data_->entry.full_filename_ = entry.full_filename_; |
||||
data_->entry.base_filename_ = entry.base_filename_; |
||||
data_->entry.line_ = entry.line_; |
||||
data_->entry.prefix_ = entry.prefix_; |
||||
data_->entry.severity_ = entry.severity_; |
||||
data_->entry.verbose_level_ = entry.verbose_level_; |
||||
data_->entry.timestamp_ = entry.timestamp_; |
||||
data_->entry.tid_ = entry.tid_; |
||||
return *this; |
||||
} |
||||
|
||||
LogMessage& LogMessage::WithPerror() { |
||||
data_->is_perror = true; |
||||
return *this; |
||||
} |
||||
|
||||
LogMessage& LogMessage::ToSinkAlso(absl::LogSink* sink) { |
||||
ABSL_INTERNAL_CHECK(sink, "null LogSink*"); |
||||
data_->extra_sinks.push_back(sink); |
||||
return *this; |
||||
} |
||||
|
||||
LogMessage& LogMessage::ToSinkOnly(absl::LogSink* sink) { |
||||
ABSL_INTERNAL_CHECK(sink, "null LogSink*"); |
||||
data_->extra_sinks.clear(); |
||||
data_->extra_sinks.push_back(sink); |
||||
data_->extra_sinks_only = true; |
||||
return *this; |
||||
} |
||||
|
||||
#ifdef __ELF__ |
||||
extern "C" void __gcov_dump() ABSL_ATTRIBUTE_WEAK; |
||||
extern "C" void __gcov_flush() ABSL_ATTRIBUTE_WEAK; |
||||
#endif |
||||
|
||||
void LogMessage::FailWithoutStackTrace() { |
||||
// Now suppress repeated trace logging:
|
||||
log_internal::SetSuppressSigabortTrace(true); |
||||
#if defined _DEBUG && defined COMPILER_MSVC |
||||
// When debugging on windows, avoid the obnoxious dialog.
|
||||
__debugbreak(); |
||||
#endif |
||||
|
||||
#ifdef __ELF__ |
||||
// For b/8737634, flush coverage if we are in coverage mode.
|
||||
if (&__gcov_dump != nullptr) { |
||||
__gcov_dump(); |
||||
} else if (&__gcov_flush != nullptr) { |
||||
__gcov_flush(); |
||||
} |
||||
#endif |
||||
|
||||
abort(); |
||||
} |
||||
|
||||
void LogMessage::FailQuietly() { |
||||
// _exit. Calling abort() would trigger all sorts of death signal handlers
|
||||
// and a detailed stack trace. Calling exit() would trigger the onexit
|
||||
// handlers, including the heap-leak checker, which is guaranteed to fail in
|
||||
// this case: we probably just new'ed the std::string that we logged.
|
||||
// Anyway, if you're calling Fail or FailQuietly, you're trying to bail out
|
||||
// of the program quickly, and it doesn't make much sense for FailQuietly to
|
||||
// offer different guarantees about exit behavior than Fail does. (And as a
|
||||
// consequence for QCHECK and CHECK to offer different exit behaviors)
|
||||
_exit(1); |
||||
} |
||||
|
||||
template LogMessage& LogMessage::operator<<(const char& v); |
||||
template LogMessage& LogMessage::operator<<(const signed char& v); |
||||
template LogMessage& LogMessage::operator<<(const unsigned char& v); |
||||
template LogMessage& LogMessage::operator<<(const short& v); // NOLINT
|
||||
template LogMessage& LogMessage::operator<<(const unsigned short& v); // NOLINT
|
||||
template LogMessage& LogMessage::operator<<(const int& v); |
||||
template LogMessage& LogMessage::operator<<(const unsigned int& v); |
||||
template LogMessage& LogMessage::operator<<(const long& v); // NOLINT
|
||||
template LogMessage& LogMessage::operator<<(const unsigned long& v); // NOLINT
|
||||
template LogMessage& LogMessage::operator<<(const long long& v); // NOLINT
|
||||
template LogMessage& LogMessage::operator<<( |
||||
const unsigned long long& v); // NOLINT
|
||||
template LogMessage& LogMessage::operator<<(void* const& v); |
||||
template LogMessage& LogMessage::operator<<(const void* const& v); |
||||
template LogMessage& LogMessage::operator<<(const float& v); |
||||
template LogMessage& LogMessage::operator<<(const double& v); |
||||
template LogMessage& LogMessage::operator<<(const bool& v); |
||||
template LogMessage& LogMessage::operator<<(const std::string& v); |
||||
template LogMessage& LogMessage::operator<<(const absl::string_view& v); |
||||
|
||||
void LogMessage::Flush() { |
||||
if (data_->entry.log_severity() < absl::MinLogLevel()) |
||||
return; |
||||
|
||||
if (data_->is_perror) { |
||||
InternalStream() << ": " << absl::base_internal::StrError(errno_saver_()) |
||||
<< " [" << errno_saver_() << "]"; |
||||
} |
||||
|
||||
// Have we already seen a fatal message?
|
||||
ABSL_CONST_INIT static std::atomic_flag seen_fatal = ATOMIC_FLAG_INIT; |
||||
if (data_->entry.log_severity() == absl::LogSeverity::kFatal && |
||||
absl::log_internal::ExitOnDFatal()) { |
||||
// Exactly one LOG(FATAL) message is responsible for aborting the process,
|
||||
// even if multiple threads LOG(FATAL) concurrently.
|
||||
data_->first_fatal = !seen_fatal.test_and_set(std::memory_order_relaxed); |
||||
} |
||||
|
||||
data_->entry.text_message_with_prefix_and_newline_and_nul_ = |
||||
data_->streambuf_.Finalize(); |
||||
data_->entry.prefix_len_ = data_->streambuf_.prefix_len(); |
||||
SendToLog(); |
||||
} |
||||
|
||||
void LogMessage::SetFailQuietly() { data_->fail_quietly = true; } |
||||
|
||||
bool LogMessage::IsFatal() const { |
||||
return data_->entry.log_severity() == absl::LogSeverity::kFatal && |
||||
absl::log_internal::ExitOnDFatal(); |
||||
} |
||||
|
||||
void LogMessage::PrepareToDie() { |
||||
// If we log a FATAL message, flush all the log destinations, then toss
|
||||
// a signal for others to catch. We leave the logs in a state that
|
||||
// someone else can use them (as long as they flush afterwards)
|
||||
if (data_->first_fatal) { |
||||
// Notify observers about the upcoming fatal error.
|
||||
ABSL_INTERNAL_C_SYMBOL(AbslInternalOnFatalLogMessage)(data_->entry); |
||||
} |
||||
|
||||
if (!data_->fail_quietly) { |
||||
// Log the message first before we start collecting stack trace.
|
||||
log_internal::LogToSinks(data_->entry, absl::MakeSpan(data_->extra_sinks), |
||||
data_->extra_sinks_only); |
||||
|
||||
// `DumpStackTrace` generates an empty string under MSVC.
|
||||
// Adding the constant prefix here simplifies testing.
|
||||
data_->entry.stacktrace_ = "*** Check failure stack trace: ***\n"; |
||||
debugging_internal::DumpStackTrace( |
||||
0, log_internal::MaxFramesInLogStackTrace(), |
||||
log_internal::ShouldSymbolizeLogStackTrace(), WriteToString, |
||||
&data_->entry.stacktrace_); |
||||
} |
||||
} |
||||
|
||||
void LogMessage::Die() { |
||||
absl::FlushLogSinks(); |
||||
|
||||
if (data_->fail_quietly) { |
||||
FailQuietly(); |
||||
} else { |
||||
FailWithoutStackTrace(); |
||||
} |
||||
} |
||||
|
||||
void LogMessage::SendToLog() { |
||||
if (IsFatal()) PrepareToDie(); |
||||
// Also log to all registered sinks, even if OnlyLogToStderr() is set.
|
||||
log_internal::LogToSinks(data_->entry, absl::MakeSpan(data_->extra_sinks), |
||||
data_->extra_sinks_only); |
||||
if (IsFatal()) Die(); |
||||
} |
||||
|
||||
void LogMessage::LogBacktraceIfNeeded() { |
||||
if (!absl::log_internal::IsInitialized()) return; |
||||
|
||||
if (!absl::log_internal::ShouldLogBacktraceAt(data_->entry.source_basename(), |
||||
data_->entry.source_line())) |
||||
return; |
||||
stream_ << " (stacktrace:\n"; |
||||
debugging_internal::DumpStackTrace( |
||||
1, log_internal::MaxFramesInLogStackTrace(), |
||||
log_internal::ShouldSymbolizeLogStackTrace(), WriteToStream, &stream_); |
||||
stream_ << ") "; |
||||
} |
||||
|
||||
LogMessageFatal::LogMessageFatal(const char* file, int line) |
||||
: LogMessage(file, line, absl::LogSeverity::kFatal) {} |
||||
|
||||
LogMessageFatal::LogMessageFatal(const char* file, int line, |
||||
absl::string_view failure_msg) |
||||
: LogMessage(file, line, absl::LogSeverity::kFatal) { |
||||
*this << "Check failed: " << failure_msg << " "; |
||||
} |
||||
|
||||
// ABSL_ATTRIBUTE_NORETURN doesn't seem to work on destructors with msvc, so
|
||||
// disable msvc's warning about the d'tor never returning.
|
||||
#ifdef _MSC_VER |
||||
#pragma warning(push) |
||||
#pragma warning(disable : 4722) |
||||
#endif |
||||
LogMessageFatal::~LogMessageFatal() { |
||||
Flush(); |
||||
FailWithoutStackTrace(); |
||||
} |
||||
#ifdef _MSC_VER |
||||
#pragma warning(pop) |
||||
#endif |
||||
|
||||
LogMessageQuietlyFatal::LogMessageQuietlyFatal(const char* file, int line) |
||||
: LogMessage(file, line, absl::LogSeverity::kFatal) { |
||||
SetFailQuietly(); |
||||
} |
||||
|
||||
LogMessageQuietlyFatal::LogMessageQuietlyFatal(const char* file, int line, |
||||
absl::string_view failure_msg) |
||||
: LogMessage(file, line, absl::LogSeverity::kFatal) { |
||||
SetFailQuietly(); |
||||
*this << "Check failed: " << failure_msg << " "; |
||||
} |
||||
|
||||
// ABSL_ATTRIBUTE_NORETURN doesn't seem to work on destructors with msvc, so
|
||||
// disable msvc's warning about the d'tor never returning.
|
||||
#ifdef _MSC_VER |
||||
#pragma warning(push) |
||||
#pragma warning(disable : 4722) |
||||
#endif |
||||
LogMessageQuietlyFatal::~LogMessageQuietlyFatal() { |
||||
Flush(); |
||||
FailQuietly(); |
||||
} |
||||
#ifdef _MSC_VER |
||||
#pragma warning(pop) |
||||
#endif |
||||
|
||||
} // namespace log_internal
|
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,287 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/log_message.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This file declares `class absl::log_internal::LogMessage`. This class more or
|
||||
// less represents a particular log message. LOG/CHECK macros create a
|
||||
// temporary instance of `LogMessage` and then stream values to it. At the end
|
||||
// of the LOG/CHECK statement, LogMessage instance goes out of scope and
|
||||
// `~LogMessage` directs the message to the registered log sinks.
|
||||
// Heap-allocation of `LogMessage` is unsupported. Construction outside of a
|
||||
// `LOG` macro is unsupported.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_LOG_MESSAGE_H_ |
||||
#define ABSL_LOG_INTERNAL_LOG_MESSAGE_H_ |
||||
|
||||
#include <ios> |
||||
#include <memory> |
||||
#include <ostream> |
||||
#include <streambuf> |
||||
#include <string> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/internal/errno_saver.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/log/internal/nullguard.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/log/log_sink.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/time.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
constexpr int kLogMessageBufferSize = 15000; |
||||
|
||||
class LogMessage { |
||||
public: |
||||
// Used for `LOG`.
|
||||
LogMessage(const char* file, int line, |
||||
absl::LogSeverity severity) ABSL_ATTRIBUTE_COLD; |
||||
LogMessage(const LogMessage&) = delete; |
||||
LogMessage& operator=(const LogMessage&) = delete; |
||||
~LogMessage() ABSL_ATTRIBUTE_COLD; |
||||
|
||||
// Overrides the location inferred from the callsite. The string pointed to
|
||||
// by `file` must be valid until the end of the statement.
|
||||
LogMessage& AtLocation(absl::string_view file, int line); |
||||
// Omits the prefix from this line. The prefix includes metadata about the
|
||||
// logged data such as source code location and timestamp.
|
||||
LogMessage& NoPrefix(); |
||||
// Sets the verbosity field of the logged message as if it was logged by
|
||||
// `VLOG(verbose_level)`. Unlike `VLOG`, this method does not affect
|
||||
// evaluation of the statement when the specified `verbose_level` has been
|
||||
// disabled. The only effect is on `absl::LogSink` implementations which
|
||||
// make use of the `absl::LogSink::verbosity()` value. The value
|
||||
// `absl::LogEntry::kNoVerbosityLevel` can be specified to mark the message
|
||||
// not verbose.
|
||||
LogMessage& WithVerbosity(int verbose_level); |
||||
// Uses the specified timestamp instead of one collected in the constructor.
|
||||
LogMessage& WithTimestamp(absl::Time timestamp); |
||||
// Uses the specified thread ID instead of one collected in the constructor.
|
||||
LogMessage& WithThreadID(absl::LogEntry::tid_t tid); |
||||
// Copies all metadata (but no data) from the specified `absl::LogEntry`.
|
||||
LogMessage& WithMetadataFrom(const absl::LogEntry& entry); |
||||
// Appends to the logged message a colon, a space, a textual description of
|
||||
// the current value of `errno` (as by strerror(3)), and the numerical value
|
||||
// of `errno`.
|
||||
LogMessage& WithPerror(); |
||||
// Sends this message to `*sink` in addition to whatever other sinks it would
|
||||
// otherwise have been sent to. `sink` must not be null.
|
||||
LogMessage& ToSinkAlso(absl::LogSink* sink); |
||||
// Sends this message to `*sink` and no others. `sink` must not be null.
|
||||
LogMessage& ToSinkOnly(absl::LogSink* sink); |
||||
|
||||
// Don't call this method from outside this library.
|
||||
LogMessage& InternalStream() { return *this; } |
||||
|
||||
// By-value overloads for small, common types let us overlook common failures
|
||||
// to define globals and static data members (i.e. in a .cc file).
|
||||
// clang-format off
|
||||
// The CUDA toolchain cannot handle these <<<'s:
|
||||
LogMessage& operator<<(char v) { return operator<< <char>(v); } |
||||
LogMessage& operator<<(signed char v) { return operator<< <signed char>(v); } |
||||
LogMessage& operator<<(unsigned char v) { |
||||
return operator<< <unsigned char>(v); |
||||
} |
||||
LogMessage& operator<<(signed short v) { // NOLINT
|
||||
return operator<< <signed short>(v); // NOLINT
|
||||
} |
||||
LogMessage& operator<<(signed int v) { return operator<< <signed int>(v); } |
||||
LogMessage& operator<<(signed long v) { // NOLINT
|
||||
return operator<< <signed long>(v); // NOLINT
|
||||
} |
||||
LogMessage& operator<<(signed long long v) { // NOLINT
|
||||
return operator<< <signed long long>(v); // NOLINT
|
||||
} |
||||
LogMessage& operator<<(unsigned short v) { // NOLINT
|
||||
return operator<< <unsigned short>(v); // NOLINT
|
||||
} |
||||
LogMessage& operator<<(unsigned int v) { |
||||
return operator<< <unsigned int>(v); |
||||
} |
||||
LogMessage& operator<<(unsigned long v) { // NOLINT
|
||||
return operator<< <unsigned long>(v); // NOLINT
|
||||
} |
||||
LogMessage& operator<<(unsigned long long v) { // NOLINT
|
||||
return operator<< <unsigned long long>(v); // NOLINT
|
||||
} |
||||
LogMessage& operator<<(void* v) { return operator<< <void*>(v); } |
||||
LogMessage& operator<<(const void* v) { return operator<< <const void*>(v); } |
||||
LogMessage& operator<<(float v) { return operator<< <float>(v); } |
||||
LogMessage& operator<<(double v) { return operator<< <double>(v); } |
||||
LogMessage& operator<<(bool v) { return operator<< <bool>(v); } |
||||
// clang-format on
|
||||
|
||||
// Handle stream manipulators e.g. std::endl.
|
||||
LogMessage& operator<<(std::ostream& (*m)(std::ostream& os)); |
||||
LogMessage& operator<<(std::ios_base& (*m)(std::ios_base& os)); |
||||
|
||||
// Literal strings. This allows us to record C string literals as literals in
|
||||
// the logging.proto.Value.
|
||||
//
|
||||
// Allow this overload to be inlined to prevent generating instantiations of
|
||||
// this template for every value of `SIZE` encountered in each source code
|
||||
// file. That significantly increases linker input sizes. Inlining is cheap
|
||||
// because the argument to this overload is almost always a string literal so
|
||||
// the call to `strlen` can be replaced at compile time. The overload for
|
||||
// `char[]` below should not be inlined. The compiler typically does not have
|
||||
// the string at compile time and cannot replace the call to `strlen` so
|
||||
// inlining it increases the binary size. See the discussion on
|
||||
// cl/107527369.
|
||||
template <int SIZE> |
||||
LogMessage& operator<<(const char (&buf)[SIZE]); |
||||
|
||||
// This prevents non-const `char[]` arrays from looking like literals.
|
||||
template <int SIZE> |
||||
LogMessage& operator<<(char (&buf)[SIZE]) ABSL_ATTRIBUTE_NOINLINE; |
||||
|
||||
// Default: uses `ostream` logging to convert `v` to a string.
|
||||
template <typename T> |
||||
LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE; |
||||
|
||||
// Note: We explicitly do not support `operator<<` for non-const references
|
||||
// because it breaks logging of non-integer bitfield types (i.e., enums).
|
||||
|
||||
protected: |
||||
// Call `abort()` or similar to perform `LOG(FATAL)` crash. It is assumed
|
||||
// that the caller has already generated and written the trace as appropriate.
|
||||
ABSL_ATTRIBUTE_NORETURN static void FailWithoutStackTrace(); |
||||
|
||||
// Similar to `FailWithoutStackTrace()`, but without `abort()`. Terminates
|
||||
// the process with an error exit code.
|
||||
ABSL_ATTRIBUTE_NORETURN static void FailQuietly(); |
||||
|
||||
// Dispatches the completed `absl::LogEntry` to applicable `absl::LogSink`s.
|
||||
// This might as well be inlined into `~LogMessage` except that
|
||||
// `~LogMessageFatal` needs to call it early.
|
||||
void Flush(); |
||||
|
||||
// After this is called, failures are done as quiet as possible for this log
|
||||
// message.
|
||||
void SetFailQuietly(); |
||||
|
||||
private: |
||||
struct LogMessageData; // Opaque type containing message state
|
||||
|
||||
// Returns `true` if the message is fatal or enabled debug-fatal.
|
||||
bool IsFatal() const; |
||||
|
||||
// Records some tombstone-type data in anticipation of `Die`.
|
||||
void PrepareToDie(); |
||||
void Die(); |
||||
|
||||
void SendToLog(); |
||||
|
||||
// Checks `FLAGS_log_backtrace_at` and appends a backtrace if appropriate.
|
||||
void LogBacktraceIfNeeded(); |
||||
|
||||
// This should be the first data member so that its initializer captures errno
|
||||
// before any other initializers alter it (e.g. with calls to new) and so that
|
||||
// no other destructors run afterward an alter it (e.g. with calls to delete).
|
||||
absl::base_internal::ErrnoSaver errno_saver_; |
||||
|
||||
// We keep the data in a separate struct so that each instance of `LogMessage`
|
||||
// uses less stack space.
|
||||
std::unique_ptr<LogMessageData> data_; |
||||
|
||||
std::ostream stream_; |
||||
}; |
||||
|
||||
// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
|
||||
template <typename T> |
||||
LogMessage& LogMessage::operator<<(const T& v) { |
||||
stream_ << log_internal::NullGuard<T>().Guard(v); |
||||
return *this; |
||||
} |
||||
inline LogMessage& LogMessage::operator<<( |
||||
std::ostream& (*m)(std::ostream& os)) { |
||||
stream_ << m; |
||||
return *this; |
||||
} |
||||
inline LogMessage& LogMessage::operator<<( |
||||
std::ios_base& (*m)(std::ios_base& os)) { |
||||
stream_ << m; |
||||
return *this; |
||||
} |
||||
template <int SIZE> |
||||
LogMessage& LogMessage::operator<<(const char (&buf)[SIZE]) { |
||||
stream_ << buf; |
||||
return *this; |
||||
} |
||||
// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
|
||||
template <int SIZE> |
||||
LogMessage& LogMessage::operator<<(char (&buf)[SIZE]) { |
||||
stream_ << buf; |
||||
return *this; |
||||
} |
||||
// We instantiate these specializations in the library's TU to save space in
|
||||
// other TUs. Since the template is marked `ABSL_ATTRIBUTE_NOINLINE` we will be
|
||||
// emitting a function call either way.
|
||||
extern template LogMessage& LogMessage::operator<<(const char& v); |
||||
extern template LogMessage& LogMessage::operator<<(const signed char& v); |
||||
extern template LogMessage& LogMessage::operator<<(const unsigned char& v); |
||||
extern template LogMessage& LogMessage::operator<<(const short& v); // NOLINT
|
||||
extern template LogMessage& LogMessage::operator<<( |
||||
const unsigned short& v); // NOLINT
|
||||
extern template LogMessage& LogMessage::operator<<(const int& v); |
||||
extern template LogMessage& LogMessage::operator<<( |
||||
const unsigned int& v); // NOLINT
|
||||
extern template LogMessage& LogMessage::operator<<(const long& v); // NOLINT
|
||||
extern template LogMessage& LogMessage::operator<<( |
||||
const unsigned long& v); // NOLINT
|
||||
extern template LogMessage& LogMessage::operator<<( |
||||
const long long& v); // NOLINT
|
||||
extern template LogMessage& LogMessage::operator<<( |
||||
const unsigned long long& v); // NOLINT
|
||||
extern template LogMessage& LogMessage::operator<<(void* const& v); |
||||
extern template LogMessage& LogMessage::operator<<(const void* const& v); |
||||
extern template LogMessage& LogMessage::operator<<(const float& v); |
||||
extern template LogMessage& LogMessage::operator<<(const double& v); |
||||
extern template LogMessage& LogMessage::operator<<(const bool& v); |
||||
extern template LogMessage& LogMessage::operator<<(const std::string& v); |
||||
extern template LogMessage& LogMessage::operator<<(const absl::string_view& v); |
||||
|
||||
// `LogMessageFatal` ensures the process will exit in failure after logging this
|
||||
// message.
|
||||
class LogMessageFatal final : public LogMessage { |
||||
public: |
||||
LogMessageFatal(const char* file, int line) ABSL_ATTRIBUTE_COLD; |
||||
LogMessageFatal(const char* file, int line, |
||||
absl::string_view failure_msg) ABSL_ATTRIBUTE_COLD; |
||||
ABSL_ATTRIBUTE_NORETURN ~LogMessageFatal(); |
||||
}; |
||||
|
||||
class LogMessageQuietlyFatal final : public LogMessage { |
||||
public: |
||||
LogMessageQuietlyFatal(const char* file, int line) ABSL_ATTRIBUTE_COLD; |
||||
LogMessageQuietlyFatal(const char* file, int line, |
||||
absl::string_view failure_msg) ABSL_ATTRIBUTE_COLD; |
||||
ABSL_ATTRIBUTE_NORETURN ~LogMessageQuietlyFatal(); |
||||
}; |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
extern "C" ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL( |
||||
AbslInternalOnFatalLogMessage)(const absl::LogEntry&); |
||||
|
||||
#endif // ABSL_LOG_INTERNAL_LOG_MESSAGE_H_
|
@ -0,0 +1,295 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/log_sink_set.h" |
||||
|
||||
#ifndef ABSL_HAVE_THREAD_LOCAL |
||||
#include <pthread.h> |
||||
#endif |
||||
|
||||
#ifdef __ANDROID__ |
||||
#include <android/log.h> |
||||
#endif |
||||
|
||||
#ifdef _WIN32 |
||||
#include <windows.h> |
||||
#endif |
||||
|
||||
#include <algorithm> |
||||
#include <vector> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/call_once.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/internal/raw_logging.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/base/thread_annotations.h" |
||||
#include "absl/cleanup/cleanup.h" |
||||
#include "absl/log/globals.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/log/internal/globals.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/log/log_sink.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/synchronization/mutex.h" |
||||
#include "absl/types/span.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
namespace { |
||||
|
||||
// Returns a mutable reference to a thread-local variable that should be true if
|
||||
// a globally-registered `LogSink`'s `Send()` is currently being invoked on this
|
||||
// thread.
|
||||
bool& ThreadIsLoggingStatus() { |
||||
#ifdef ABSL_HAVE_THREAD_LOCAL |
||||
ABSL_CONST_INIT thread_local bool thread_is_logging = false; |
||||
return thread_is_logging; |
||||
#else |
||||
ABSL_CONST_INIT static pthread_key_t thread_is_logging_key; |
||||
static const bool unused = [] { |
||||
if (pthread_key_create(&thread_is_logging_key, [](void* data) { |
||||
delete reinterpret_cast<bool*>(data); |
||||
})) { |
||||
perror("pthread_key_create failed!"); |
||||
abort(); |
||||
} |
||||
return true; |
||||
}(); |
||||
bool* thread_is_logging_ptr = |
||||
reinterpret_cast<bool*>(pthread_getspecific(thread_is_logging_key)); |
||||
|
||||
if (ABSL_PREDICT_FALSE(!thread_is_logging_ptr)) { |
||||
thread_is_logging_ptr = new bool{false}; |
||||
if (pthread_setspecific(thread_is_logging_key, thread_is_logging_ptr)) { |
||||
perror("pthread_setspecific failed"); |
||||
abort(); |
||||
} |
||||
} |
||||
return *thread_is_logging_ptr; |
||||
#endif |
||||
} |
||||
|
||||
class StderrLogSink final : public LogSink { |
||||
public: |
||||
~StderrLogSink() override = default; |
||||
|
||||
void Send(const absl::LogEntry& entry) override { |
||||
if (entry.log_severity() < absl::StderrThreshold() && |
||||
absl::log_internal::IsInitialized()) { |
||||
return; |
||||
} |
||||
|
||||
ABSL_CONST_INIT static absl::once_flag warn_if_not_initialized; |
||||
absl::call_once(warn_if_not_initialized, []() { |
||||
if (absl::log_internal::IsInitialized()) return; |
||||
const char w[] = |
||||
"WARNING: All log messages before absl::InitializeLog() is called" |
||||
" are written to STDERR\n"; |
||||
absl::log_internal::WriteToStderr(w, absl::LogSeverity::kWarning); |
||||
}); |
||||
|
||||
if (!entry.stacktrace().empty()) { |
||||
absl::log_internal::WriteToStderr(entry.stacktrace(), |
||||
entry.log_severity()); |
||||
} else { |
||||
// TODO(b/226937039): do this outside else condition once we avoid
|
||||
// ReprintFatalMessage
|
||||
absl::log_internal::WriteToStderr( |
||||
entry.text_message_with_prefix_and_newline(), entry.log_severity()); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
#if defined(__ANDROID__) |
||||
class AndroidLogSink final : public LogSink { |
||||
public: |
||||
~AndroidLogSink() override = default; |
||||
|
||||
void Send(const absl::LogEntry& entry) override { |
||||
const int level = AndroidLogLevel(entry); |
||||
// TODO(b/37587197): make the tag ("native") configurable.
|
||||
__android_log_write(level, "native", |
||||
entry.text_message_with_prefix_and_newline_c_str()); |
||||
if (entry.log_severity() == absl::LogSeverity::kFatal) |
||||
__android_log_write(ANDROID_LOG_FATAL, "native", "terminating.\n"); |
||||
} |
||||
|
||||
private: |
||||
static int AndroidLogLevel(const absl::LogEntry& entry) { |
||||
switch (entry.log_severity()) { |
||||
case absl::LogSeverity::kFatal: |
||||
return ANDROID_LOG_FATAL; |
||||
case absl::LogSeverity::kError: |
||||
return ANDROID_LOG_ERROR; |
||||
case absl::LogSeverity::kWarning: |
||||
return ANDROID_LOG_WARN; |
||||
default: |
||||
if (entry.verbosity() >= 2) return ANDROID_LOG_VERBOSE; |
||||
if (entry.verbosity() == 1) return ANDROID_LOG_DEBUG; |
||||
return ANDROID_LOG_INFO; |
||||
} |
||||
} |
||||
}; |
||||
#endif // !defined(__ANDROID__)
|
||||
|
||||
#if defined(_WIN32) |
||||
class WindowsDebuggerLogSink final : public LogSink { |
||||
public: |
||||
~WindowsDebuggerLogSink() override = default; |
||||
|
||||
void Send(const absl::LogEntry& entry) override { |
||||
if (entry.log_severity() < absl::StderrThreshold() && |
||||
absl::log_internal::IsInitialized()) { |
||||
return; |
||||
} |
||||
::OutputDebugStringA(entry.text_message_with_prefix_and_newline_c_str()); |
||||
} |
||||
}; |
||||
#endif // !defined(_WIN32)
|
||||
|
||||
class GlobalLogSinkSet final { |
||||
public: |
||||
GlobalLogSinkSet() { |
||||
#if defined(__myriad2__) || defined(__Fuchsia__) |
||||
// myriad2 and Fuchsia do not log to stderr by default.
|
||||
#else |
||||
static StderrLogSink* stderr_log_sink = new StderrLogSink; |
||||
AddLogSink(stderr_log_sink); |
||||
#endif |
||||
#ifdef __ANDROID__ |
||||
static AndroidLogSink* android_log_sink = new AndroidLogSink; |
||||
AddLogSink(android_log_sink); |
||||
#endif |
||||
#if defined(_WIN32) |
||||
static WindowsDebuggerLogSink* debugger_log_sink = |
||||
new WindowsDebuggerLogSink; |
||||
AddLogSink(debugger_log_sink); |
||||
#endif // !defined(_WIN32)
|
||||
} |
||||
|
||||
void LogToSinks(const absl::LogEntry& entry, |
||||
absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only) |
||||
ABSL_LOCKS_EXCLUDED(guard_) { |
||||
SendToSinks(entry, extra_sinks); |
||||
|
||||
if (!extra_sinks_only) { |
||||
if (ThreadIsLoggingToLogSink()) { |
||||
absl::log_internal::WriteToStderr( |
||||
entry.text_message_with_prefix_and_newline(), entry.log_severity()); |
||||
} else { |
||||
absl::ReaderMutexLock global_sinks_lock(&guard_); |
||||
ThreadIsLoggingStatus() = true; |
||||
// Ensure the "thread is logging" status is reverted upon leaving the
|
||||
// scope even in case of exceptions.
|
||||
auto status_cleanup = |
||||
absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; }); |
||||
SendToSinks(entry, absl::MakeSpan(sinks_)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void AddLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) { |
||||
{ |
||||
absl::WriterMutexLock global_sinks_lock(&guard_); |
||||
auto pos = std::find(sinks_.begin(), sinks_.end(), sink); |
||||
if (pos == sinks_.end()) { |
||||
sinks_.push_back(sink); |
||||
return; |
||||
} |
||||
} |
||||
ABSL_INTERNAL_LOG(FATAL, "Duplicate log sinks are not supported"); |
||||
} |
||||
|
||||
void RemoveLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) { |
||||
{ |
||||
absl::WriterMutexLock global_sinks_lock(&guard_); |
||||
auto pos = std::find(sinks_.begin(), sinks_.end(), sink); |
||||
if (pos != sinks_.end()) { |
||||
sinks_.erase(pos); |
||||
return; |
||||
} |
||||
} |
||||
ABSL_INTERNAL_LOG(FATAL, "Mismatched log sink being removed"); |
||||
} |
||||
|
||||
void FlushLogSinks() ABSL_LOCKS_EXCLUDED(guard_) { |
||||
if (ThreadIsLoggingToLogSink()) { |
||||
// The thread_local condition demonstrates that we're already holding the
|
||||
// lock in order to iterate over `sinks_` for dispatch. The thread-safety
|
||||
// annotations don't know this, so we use `ABSL_NO_THREAD_SAFETY_ANALYSIS`
|
||||
guard_.AssertReaderHeld(); |
||||
FlushLogSinksLocked(); |
||||
} else { |
||||
absl::ReaderMutexLock global_sinks_lock(&guard_); |
||||
// In case if LogSink::Flush overload decides to log
|
||||
ThreadIsLoggingStatus() = true; |
||||
// Ensure the "thread is logging" status is reverted upon leaving the
|
||||
// scope even in case of exceptions.
|
||||
auto status_cleanup = |
||||
absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; }); |
||||
FlushLogSinksLocked(); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
void FlushLogSinksLocked() ABSL_SHARED_LOCKS_REQUIRED(guard_) { |
||||
for (absl::LogSink* sink : sinks_) { |
||||
sink->Flush(); |
||||
} |
||||
} |
||||
|
||||
// Helper routine for LogToSinks.
|
||||
static void SendToSinks(const absl::LogEntry& entry, |
||||
absl::Span<absl::LogSink*> sinks) { |
||||
for (absl::LogSink* sink : sinks) { |
||||
sink->Send(entry); |
||||
} |
||||
} |
||||
|
||||
using LogSinksSet = std::vector<absl::LogSink*>; |
||||
absl::Mutex guard_; |
||||
LogSinksSet sinks_ ABSL_GUARDED_BY(guard_); |
||||
}; |
||||
|
||||
// Returns reference to the global LogSinks set.
|
||||
GlobalLogSinkSet& GlobalSinks() { |
||||
static GlobalLogSinkSet* global_sinks = new GlobalLogSinkSet; |
||||
return *global_sinks; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
bool ThreadIsLoggingToLogSink() { return ThreadIsLoggingStatus(); } |
||||
|
||||
void LogToSinks(const absl::LogEntry& entry, |
||||
absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only) { |
||||
log_internal::GlobalSinks().LogToSinks(entry, extra_sinks, extra_sinks_only); |
||||
} |
||||
|
||||
void AddLogSink(absl::LogSink* sink) { |
||||
log_internal::GlobalSinks().AddLogSink(sink); |
||||
} |
||||
|
||||
void RemoveLogSink(absl::LogSink* sink) { |
||||
log_internal::GlobalSinks().RemoveLogSink(sink); |
||||
} |
||||
|
||||
void FlushLogSinks() { log_internal::GlobalSinks().FlushLogSinks(); } |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,54 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/log_sink_set.h
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_LOG_SINK_SET_H_ |
||||
#define ABSL_LOG_INTERNAL_LOG_SINK_SET_H_ |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/log/log_sink.h" |
||||
#include "absl/types/span.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// Returns true if a globally-registered `LogSink`'s `Send()` is currently
|
||||
// being invoked on this thread.
|
||||
bool ThreadIsLoggingToLogSink(); |
||||
|
||||
// This function may log to two sets of sinks:
|
||||
//
|
||||
// * If `extra_sinks_only` is true, it will dispatch only to `extra_sinks`.
|
||||
// `LogMessage::ToSinkAlso` and `LogMessage::ToSinkOnly` are used to attach
|
||||
// extra sinks to the entry.
|
||||
// * Otherwise it will also log to the global sinks set. This set is managed
|
||||
// by `absl::AddLogSink` and `absl::RemoveLogSink`.
|
||||
void LogToSinks(const absl::LogEntry& entry, |
||||
absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only); |
||||
|
||||
// Implementation for operations with log sink set.
|
||||
void AddLogSink(absl::LogSink* sink); |
||||
void RemoveLogSink(absl::LogSink* sink); |
||||
void FlushLogSinks(); |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_LOG_SINK_SET_H_
|
@ -0,0 +1,56 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/nullguard.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// NullGuard exists such that NullGuard<T>::Guard(v) returns v, unless passed a
|
||||
// nullptr_t, or a null char* or const char*, in which case it returns "(null)".
|
||||
// This allows streaming NullGuard<T>::Guard(v) to an output stream without
|
||||
// hitting undefined behavior for null values.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_NULLGUARD_H_ |
||||
#define ABSL_LOG_INTERNAL_NULLGUARD_H_ |
||||
|
||||
#include <cstddef> |
||||
|
||||
#include "absl/base/config.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
template <typename T> |
||||
struct NullGuard final { |
||||
static const T& Guard(const T& v) { return v; } |
||||
}; |
||||
template <> |
||||
struct NullGuard<char*> final { |
||||
static const char* Guard(const char* v) { return v ? v : "(null)"; } |
||||
}; |
||||
template <> |
||||
struct NullGuard<const char*> final { |
||||
static const char* Guard(const char* v) { return v ? v : "(null)"; } |
||||
}; |
||||
template <> |
||||
struct NullGuard<std::nullptr_t> final { |
||||
static const char* Guard(const std::nullptr_t&) { return "(null)"; } |
||||
}; |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_NULLGUARD_H_
|
@ -0,0 +1,134 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/nullstream.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Classes `NullStream`, `NullStreamMaybeFatal ` and `NullStreamFatal`
|
||||
// implement a subset of the `LogMessage` API and are used instead when logging
|
||||
// of messages has been disabled.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_NULLSTREAM_H_ |
||||
#define ABSL_LOG_INTERNAL_NULLSTREAM_H_ |
||||
|
||||
#ifdef _WIN32 |
||||
#include <cstdlib> |
||||
#else |
||||
#include <unistd.h> |
||||
#endif |
||||
#include <ios> |
||||
#include <ostream> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// A `NullStream` implements the API of `LogMessage` (a few methods and
|
||||
// `operator<<`) but does nothing. All methods are defined inline so the
|
||||
// compiler can eliminate the whole instance and discard anything that's
|
||||
// streamed in.
|
||||
class NullStream { |
||||
public: |
||||
NullStream& AtLocation(absl::string_view, int) { return *this; } |
||||
template <typename SourceLocationType> |
||||
NullStream& AtLocation(SourceLocationType) { |
||||
return *this; |
||||
} |
||||
NullStream& NoPrefix() { return *this; } |
||||
NullStream& WithVerbosity(int) { return *this; } |
||||
template <typename TimeType> |
||||
NullStream& WithTimestamp(TimeType) { |
||||
return *this; |
||||
} |
||||
template <typename Tid> |
||||
NullStream& WithThreadID(Tid) { |
||||
return *this; |
||||
} |
||||
template <typename LogEntryType> |
||||
NullStream& WithMetadataFrom(const LogEntryType&) { |
||||
return *this; |
||||
} |
||||
NullStream& WithPerror() { return *this; } |
||||
template <typename LogSinkType> |
||||
NullStream& ToSinkAlso(LogSinkType*) { |
||||
return *this; |
||||
} |
||||
template <typename LogSinkType> |
||||
NullStream& ToSinkOnly(LogSinkType*) { |
||||
return *this; |
||||
} |
||||
template <typename LogSinkType> |
||||
NullStream& OutputToSink(LogSinkType*, bool) { |
||||
return *this; |
||||
} |
||||
NullStream& InternalStream() { return *this; } |
||||
}; |
||||
template <typename T> |
||||
inline NullStream& operator<<(NullStream& str, const T&) { |
||||
return str; |
||||
} |
||||
inline NullStream& operator<<(NullStream& str, |
||||
std::ostream& (*)(std::ostream& os)) { |
||||
return str; |
||||
} |
||||
inline NullStream& operator<<(NullStream& str, |
||||
std::ios_base& (*)(std::ios_base& os)) { |
||||
return str; |
||||
} |
||||
|
||||
// `NullStreamMaybeFatal` implements the process termination semantics of
|
||||
// `LogMessage`, which is used for `DFATAL` severity and expression-defined
|
||||
// severity e.g. `LOG(LEVEL(HowBadIsIt()))`. Like `LogMessage`, it terminates
|
||||
// the process when destroyed if the passed-in severity equals `FATAL`.
|
||||
class NullStreamMaybeFatal final : public NullStream { |
||||
public: |
||||
explicit NullStreamMaybeFatal(absl::LogSeverity severity) |
||||
: fatal_(severity == absl::LogSeverity::kFatal) {} |
||||
~NullStreamMaybeFatal() { |
||||
if (fatal_) _exit(1); |
||||
} |
||||
|
||||
private: |
||||
bool fatal_; |
||||
}; |
||||
|
||||
// `NullStreamFatal` implements the process termination semantics of
|
||||
// `LogMessageFatal`, which means it always terminates the process. `DFATAL`
|
||||
// and expression-defined severity use `NullStreamMaybeFatal` above.
|
||||
class NullStreamFatal final : public NullStream { |
||||
public: |
||||
NullStreamFatal() {} |
||||
// ABSL_ATTRIBUTE_NORETURN doesn't seem to work on destructors with msvc, so
|
||||
// disable msvc's warning about the d'tor never returning.
|
||||
#ifdef _MSC_VER |
||||
#pragma warning(push) |
||||
#pragma warning(disable : 4722) |
||||
#endif |
||||
ABSL_ATTRIBUTE_NORETURN ~NullStreamFatal() { _exit(1); } |
||||
#ifdef _MSC_VER |
||||
#pragma warning(pop) |
||||
#endif |
||||
}; |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_GLOBALS_H_
|
@ -0,0 +1,105 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdlib.h> |
||||
|
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/globals.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/log.h" |
||||
|
||||
namespace { |
||||
using ::testing::AllOf; |
||||
using ::testing::HasSubstr; |
||||
|
||||
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( |
||||
new absl::log_internal::LogTestEnvironment); |
||||
|
||||
MATCHER_P2(HasSubstrTimes, substr, expected_count, "") { |
||||
int count = 0; |
||||
std::string::size_type pos = 0; |
||||
std::string needle(substr); |
||||
while ((pos = arg.find(needle, pos)) != std::string::npos) { |
||||
++count; |
||||
pos += needle.size(); |
||||
} |
||||
|
||||
return count == expected_count; |
||||
} |
||||
|
||||
TEST(StderrLogSinkDeathTest, InfoMessagesInStderr) { |
||||
EXPECT_DEATH_IF_SUPPORTED( |
||||
{ |
||||
absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); |
||||
LOG(INFO) << "INFO message"; |
||||
exit(1); |
||||
}, |
||||
"INFO message"); |
||||
} |
||||
|
||||
TEST(StderrLogSinkDeathTest, WarningMessagesInStderr) { |
||||
EXPECT_DEATH_IF_SUPPORTED( |
||||
{ |
||||
absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); |
||||
LOG(WARNING) << "WARNING message"; |
||||
exit(1); |
||||
}, |
||||
"WARNING message"); |
||||
} |
||||
|
||||
TEST(StderrLogSinkDeathTest, ErrorMessagesInStderr) { |
||||
EXPECT_DEATH_IF_SUPPORTED( |
||||
{ |
||||
absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); |
||||
LOG(ERROR) << "ERROR message"; |
||||
exit(1); |
||||
}, |
||||
"ERROR message"); |
||||
} |
||||
|
||||
TEST(StderrLogSinkDeathTest, FatalMessagesInStderr) { |
||||
char message[] = "FATAL message"; |
||||
char stacktrace[] = "*** Check failure stack trace: ***"; |
||||
|
||||
int expected_count = 1; |
||||
|
||||
EXPECT_DEATH_IF_SUPPORTED( |
||||
{ |
||||
absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); |
||||
LOG(FATAL) << message; |
||||
}, |
||||
AllOf(HasSubstrTimes(message, expected_count), HasSubstr(stacktrace))); |
||||
} |
||||
|
||||
TEST(StderrLogSinkDeathTest, SecondaryFatalMessagesInStderr) { |
||||
auto MessageGen = []() -> std::string { |
||||
LOG(FATAL) << "Internal failure"; |
||||
return "External failure"; |
||||
}; |
||||
|
||||
EXPECT_DEATH_IF_SUPPORTED( |
||||
{ |
||||
absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); |
||||
LOG(FATAL) << MessageGen(); |
||||
}, |
||||
"Internal failure"); |
||||
} |
||||
|
||||
} // namespace
|
@ -0,0 +1,71 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/strip.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_STRIP_H_ |
||||
#define ABSL_LOG_INTERNAL_STRIP_H_ |
||||
|
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/internal/log_message.h" |
||||
#include "absl/log/internal/nullstream.h" |
||||
|
||||
// `ABSL_LOGGING_INTERNAL_LOG_*` evaluates to a temporary `LogMessage` object or
|
||||
// to a related object with a compatible API but different behavior. This set
|
||||
// of defines comes in three flavors: vanilla, plus two variants that strip some
|
||||
// logging in subtly different ways for subtly different reasons (see below).
|
||||
#if defined(STRIP_LOG) && STRIP_LOG |
||||
#define ABSL_LOGGING_INTERNAL_LOG_INFO ::absl::log_internal::NullStream() |
||||
#define ABSL_LOGGING_INTERNAL_LOG_WARNING ::absl::log_internal::NullStream() |
||||
#define ABSL_LOGGING_INTERNAL_LOG_ERROR ::absl::log_internal::NullStream() |
||||
#define ABSL_LOGGING_INTERNAL_LOG_FATAL ::absl::log_internal::NullStreamFatal() |
||||
#define ABSL_LOGGING_INTERNAL_LOG_QFATAL ::absl::log_internal::NullStreamFatal() |
||||
#define ABSL_LOGGING_INTERNAL_LOG_DFATAL \ |
||||
::absl::log_internal::NullStreamMaybeFatal(::absl::kLogDebugFatal) |
||||
#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \ |
||||
::absl::log_internal::NullStreamMaybeFatal(log_internal_severity) |
||||
#define ABSL_LOG_INTERNAL_CHECK(failure_message) ABSL_LOGGING_INTERNAL_LOG_FATAL |
||||
#define ABSL_LOG_INTERNAL_QCHECK(failure_message) \ |
||||
ABSL_LOGGING_INTERNAL_LOG_QFATAL |
||||
#else // !defined(STRIP_LOG) || !STRIP_LOG
|
||||
#define ABSL_LOGGING_INTERNAL_LOG_INFO \ |
||||
::absl::log_internal::LogMessage(__FILE__, __LINE__, \
|
||||
::absl::LogSeverity::kInfo) |
||||
#define ABSL_LOGGING_INTERNAL_LOG_WARNING \ |
||||
::absl::log_internal::LogMessage(__FILE__, __LINE__, \
|
||||
::absl::LogSeverity::kWarning) |
||||
#define ABSL_LOGGING_INTERNAL_LOG_ERROR \ |
||||
::absl::log_internal::LogMessage(__FILE__, __LINE__, \
|
||||
::absl::LogSeverity::kError) |
||||
#define ABSL_LOGGING_INTERNAL_LOG_FATAL \ |
||||
::absl::log_internal::LogMessageFatal(__FILE__, __LINE__) |
||||
#define ABSL_LOGGING_INTERNAL_LOG_QFATAL \ |
||||
::absl::log_internal::LogMessageQuietlyFatal(__FILE__, __LINE__) |
||||
#define ABSL_LOGGING_INTERNAL_LOG_DFATAL \ |
||||
::absl::log_internal::LogMessage(__FILE__, __LINE__, ::absl::kLogDebugFatal) |
||||
#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \ |
||||
::absl::log_internal::LogMessage(__FILE__, __LINE__, log_internal_severity) |
||||
// These special cases dispatch to special-case constructors that allow us to
|
||||
// avoid an extra function call and shrink non-LTO binaries by a percent or so.
|
||||
#define ABSL_LOG_INTERNAL_CHECK(failure_message) \ |
||||
::absl::log_internal::LogMessageFatal(__FILE__, __LINE__, failure_message) |
||||
#define ABSL_LOG_INTERNAL_QCHECK(failure_message) \ |
||||
::absl::log_internal::LogMessageQuietlyFatal(__FILE__, __LINE__, \
|
||||
failure_message) |
||||
#endif // !defined(STRIP_LOG) || !STRIP_LOG
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_STRIP_H_
|
@ -0,0 +1,68 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/test_actions.h" |
||||
|
||||
#include <cassert> |
||||
#include <iostream> |
||||
#include <string> |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/strings/escaping.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/time.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
void WriteToStderrWithFilename::operator()(const absl::LogEntry& entry) const { |
||||
std::cerr << message << " (file: " << entry.source_filename() << ")" |
||||
<< std::endl; |
||||
} |
||||
|
||||
void WriteEntryToStderr::operator()(const absl::LogEntry& entry) const { |
||||
if (!message.empty()) std::cerr << message << std::endl; |
||||
|
||||
std::cerr << "LogEntry{\n" |
||||
<< " source_filename: \"" |
||||
<< absl::CHexEscape(entry.source_filename()) << "\"\n" |
||||
<< " source_basename: \"" |
||||
<< absl::CHexEscape(entry.source_basename()) << "\"\n" |
||||
<< " source_line: " << entry.source_line() << "\n" |
||||
<< " prefix: " << (entry.prefix() ? "true\n" : "false\n") |
||||
<< " log_severity: " << entry.log_severity() << "\n" |
||||
<< " timestamp: " << entry.timestamp() << "\n" |
||||
<< " text_message: \"" << absl::CHexEscape(entry.text_message()) |
||||
<< "\"\n verbosity: " << entry.verbosity() << "\n" |
||||
<< "}" << std::endl; |
||||
} |
||||
|
||||
void WriteEntryToStderr::operator()(absl::LogSeverity severity, |
||||
absl::string_view filename, |
||||
absl::string_view log_message) const { |
||||
if (!message.empty()) std::cerr << message << std::endl; |
||||
|
||||
std::cerr << "LogEntry{\n" |
||||
<< " source_filename: \"" << absl::CHexEscape(filename) << "\"\n" |
||||
<< " log_severity: " << severity << "\n" |
||||
<< " text_message: \"" << absl::CHexEscape(log_message) << "}" |
||||
<< std::endl; |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,90 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/test_actions.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This file declares Googletest's actions used in the Abseil Logging library
|
||||
// unit tests.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_TEST_ACTIONS_H_ |
||||
#define ABSL_LOG_INTERNAL_TEST_ACTIONS_H_ |
||||
|
||||
#include <iostream> |
||||
#include <ostream> |
||||
#include <string> |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// These actions are used by the child process in a death test.
|
||||
//
|
||||
// Expectations set in the child cannot cause test failure in the parent
|
||||
// directly. Instead, the child can use these actions with
|
||||
// `EXPECT_CALL`/`WillOnce` and `ON_CALL`/`WillByDefault` (for unexpected calls)
|
||||
// to write messages to stderr that the parent can match against.
|
||||
struct WriteToStderr final { |
||||
explicit WriteToStderr(absl::string_view m) : message(m) {} |
||||
std::string message; |
||||
|
||||
template <typename... Args> |
||||
void operator()(const Args&...) const { |
||||
std::cerr << message << std::endl; |
||||
} |
||||
}; |
||||
|
||||
struct WriteToStderrWithFilename final { |
||||
explicit WriteToStderrWithFilename(absl::string_view m) : message(m) {} |
||||
|
||||
std::string message; |
||||
|
||||
void operator()(const absl::LogEntry& entry) const; |
||||
}; |
||||
|
||||
struct WriteEntryToStderr final { |
||||
explicit WriteEntryToStderr(absl::string_view m) : message(m) {} |
||||
|
||||
std::string message = ""; |
||||
|
||||
void operator()(const absl::LogEntry& entry) const; |
||||
void operator()(absl::LogSeverity, absl::string_view, |
||||
absl::string_view) const; |
||||
}; |
||||
|
||||
// See the documentation for `DeathTestValidateExpectations` above.
|
||||
// `DeathTestExpectedLogging` should be used once in a given death test, and the
|
||||
// applicable severity level is the one that should be passed to
|
||||
// `DeathTestValidateExpectations`.
|
||||
inline WriteEntryToStderr DeathTestExpectedLogging() { |
||||
return WriteEntryToStderr{"Mock received expected entry:"}; |
||||
} |
||||
|
||||
// `DeathTestUnexpectedLogging` should be used zero or more times to mark
|
||||
// messages that should not hit the logs as the process dies.
|
||||
inline WriteEntryToStderr DeathTestUnexpectedLogging() { |
||||
return WriteEntryToStderr{"Mock received unexpected entry:"}; |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_TEST_ACTIONS_H_
|
@ -0,0 +1,82 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
#include "absl/log/internal/test_helpers.h" |
||||
|
||||
#ifdef __Fuchsia__ |
||||
#include <zircon/syscalls.h> |
||||
#endif |
||||
|
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/globals.h" |
||||
#include "absl/log/initialize.h" |
||||
#include "absl/log/internal/globals.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// Returns false if the specified severity level is disabled by
|
||||
// `ABSL_MIN_LOG_LEVEL` or `absl::MinLogLevel()`.
|
||||
bool LoggingEnabledAt(absl::LogSeverity severity) { |
||||
return severity >= kAbslMinLogLevel && severity >= absl::MinLogLevel(); |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Googletest Death Test Predicates
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
|
||||
bool DiedOfFatal(int exit_status) { |
||||
#if defined(_WIN32) |
||||
// Depending on NDEBUG and (configuration?) MSVC's abort either results
|
||||
// in error code 3 (SIGABRT) or error code 0x80000003 (breakpoint
|
||||
// triggered).
|
||||
return ::testing::ExitedWithCode(3)(exit_status & ~0x80000000); |
||||
#elif defined(__Fuchsia__) |
||||
// The Fuchsia death test implementation kill()'s the process when it detects
|
||||
// an exception, so it should exit with the corresponding code. See
|
||||
// FuchsiaDeathTest::Wait().
|
||||
return ::testing::ExitedWithCode(ZX_TASK_RETCODE_SYSCALL_KILL)(exit_status); |
||||
#elif defined(__ANDROID__) && defined(__aarch64__) |
||||
// These are all run under a qemu config that eats died-due-to-signal exit
|
||||
// statuses.
|
||||
return true; |
||||
#else |
||||
return ::testing::KilledBySignal(SIGABRT)(exit_status); |
||||
#endif |
||||
} |
||||
|
||||
bool DiedOfQFatal(int exit_status) { |
||||
return ::testing::ExitedWithCode(1)(exit_status); |
||||
} |
||||
|
||||
#endif |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Helper for Log inititalization in test
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void LogTestEnvironment::SetUp() { |
||||
if (!absl::log_internal::IsInitialized()) { |
||||
absl::InitializeLog(); |
||||
} |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,71 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/test_helpers.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This file declares testing helpers for the logging library.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_TEST_HELPERS_H_ |
||||
#define ABSL_LOG_INTERNAL_TEST_HELPERS_H_ |
||||
|
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/globals.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// `ABSL_MIN_LOG_LEVEL` can't be used directly since it is not always defined.
|
||||
constexpr auto kAbslMinLogLevel = |
||||
#ifdef ABSL_MIN_LOG_LEVEL |
||||
static_cast<absl::LogSeverityAtLeast>(ABSL_MIN_LOG_LEVEL); |
||||
#else |
||||
absl::LogSeverityAtLeast::kInfo; |
||||
#endif |
||||
|
||||
// Returns false if the specified severity level is disabled by
|
||||
// `ABSL_MIN_LOG_LEVEL` or `absl::MinLogLevel()`.
|
||||
bool LoggingEnabledAt(absl::LogSeverity severity); |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Googletest Death Test Predicates
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
|
||||
bool DiedOfFatal(int exit_status); |
||||
bool DiedOfQFatal(int exit_status); |
||||
|
||||
#endif |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Helper for Log inititalization in test
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class LogTestEnvironment : public ::testing::Environment { |
||||
public: |
||||
~LogTestEnvironment() override = default; |
||||
|
||||
void SetUp() override; |
||||
}; |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_TEST_HELPERS_H_
|
@ -0,0 +1,168 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/internal/test_matchers.h" |
||||
|
||||
#include <sstream> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/clock.h" |
||||
#include "absl/time/time.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> SourceFilename( |
||||
const ::testing::Matcher<absl::string_view>& source_filename) { |
||||
return Property("source_filename", &absl::LogEntry::source_filename, |
||||
source_filename); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> SourceBasename( |
||||
const ::testing::Matcher<absl::string_view>& source_basename) { |
||||
return Property("source_basename", &absl::LogEntry::source_basename, |
||||
source_basename); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> SourceLine( |
||||
const ::testing::Matcher<int>& source_line) { |
||||
return Property("source_line", &absl::LogEntry::source_line, source_line); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> Prefix( |
||||
const ::testing::Matcher<bool>& prefix) { |
||||
return Property("prefix", &absl::LogEntry::prefix, prefix); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> LogSeverity( |
||||
const ::testing::Matcher<absl::LogSeverity>& log_severity) { |
||||
return Property("log_severity", &absl::LogEntry::log_severity, log_severity); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> Timestamp( |
||||
const ::testing::Matcher<absl::Time>& timestamp) { |
||||
return Property("timestamp", &absl::LogEntry::timestamp, timestamp); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> TimestampInMatchWindow() { |
||||
return Property("timestamp", &absl::LogEntry::timestamp, |
||||
::testing::AllOf(::testing::Ge(absl::Now()), |
||||
::testing::Truly([](absl::Time arg) { |
||||
return arg <= absl::Now(); |
||||
}))); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> ThreadID( |
||||
const ::testing::Matcher<absl::LogEntry::tid_t>& tid) { |
||||
return Property("tid", &absl::LogEntry::tid, tid); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefixAndNewline( |
||||
const ::testing::Matcher<absl::string_view>& |
||||
text_message_with_prefix_and_newline) { |
||||
return Property("text_message_with_prefix_and_newline", |
||||
&absl::LogEntry::text_message_with_prefix_and_newline, |
||||
text_message_with_prefix_and_newline); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefix( |
||||
const ::testing::Matcher<absl::string_view>& text_message_with_prefix) { |
||||
return Property("text_message_with_prefix", |
||||
&absl::LogEntry::text_message_with_prefix, |
||||
text_message_with_prefix); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> TextMessage( |
||||
const ::testing::Matcher<absl::string_view>& text_message) { |
||||
return Property("text_message", &absl::LogEntry::text_message, text_message); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> TextPrefix( |
||||
const ::testing::Matcher<absl::string_view>& text_prefix) { |
||||
return ResultOf( |
||||
[](const absl::LogEntry& entry) { |
||||
absl::string_view msg = entry.text_message_with_prefix(); |
||||
msg.remove_suffix(entry.text_message().size()); |
||||
return msg; |
||||
}, |
||||
text_prefix); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> Verbosity( |
||||
const ::testing::Matcher<int>& verbosity) { |
||||
return Property("verbosity", &absl::LogEntry::verbosity, verbosity); |
||||
} |
||||
|
||||
::testing::Matcher<const absl::LogEntry&> Stacktrace( |
||||
const ::testing::Matcher<absl::string_view>& stacktrace) { |
||||
return Property("stacktrace", &absl::LogEntry::stacktrace, stacktrace); |
||||
} |
||||
|
||||
class MatchesOstreamImpl final |
||||
: public ::testing::MatcherInterface<absl::string_view> { |
||||
public: |
||||
explicit MatchesOstreamImpl(std::string expected) |
||||
: expected_(std::move(expected)) {} |
||||
bool MatchAndExplain(absl::string_view actual, |
||||
::testing::MatchResultListener*) const override { |
||||
return actual == expected_; |
||||
} |
||||
void DescribeTo(std::ostream* os) const override { |
||||
*os << "matches the contents of the ostringstream, which are \"" |
||||
<< expected_ << "\""; |
||||
} |
||||
|
||||
void DescribeNegationTo(std::ostream* os) const override { |
||||
*os << "does not match the contents of the ostringstream, which are \"" |
||||
<< expected_ << "\""; |
||||
} |
||||
|
||||
private: |
||||
const std::string expected_; |
||||
}; |
||||
::testing::Matcher<absl::string_view> MatchesOstream( |
||||
const std::ostringstream& stream) { |
||||
return ::testing::MakeMatcher(new MatchesOstreamImpl(stream.str())); |
||||
} |
||||
|
||||
// We need to validate what is and isn't logged as the process dies due to
|
||||
// `FATAL`, `QFATAL`, `CHECK`, etc., but assertions inside a death test
|
||||
// subprocess don't directly affect the pass/fail status of the parent process.
|
||||
// Instead, we use the mock actions `DeathTestExpectedLogging` and
|
||||
// `DeathTestUnexpectedLogging` to write specific phrases to `stderr` that we
|
||||
// can validate in the parent process using this matcher.
|
||||
::testing::Matcher<const std::string&> DeathTestValidateExpectations() { |
||||
if (log_internal::LoggingEnabledAt(absl::LogSeverity::kFatal)) { |
||||
return ::testing::Matcher<const std::string&>(::testing::AllOf( |
||||
::testing::HasSubstr("Mock received expected entry"), |
||||
Not(::testing::HasSubstr("Mock received unexpected entry")))); |
||||
} |
||||
// If `FATAL` logging is disabled, neither message should have been written.
|
||||
return ::testing::Matcher<const std::string&>(::testing::AllOf( |
||||
Not(::testing::HasSubstr("Mock received expected entry")), |
||||
Not(::testing::HasSubstr("Mock received unexpected entry")))); |
||||
} |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,90 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/test_matchers.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This file declares Googletest's matchers used in the Abseil Logging library
|
||||
// unit tests.
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_TEST_MATCHERS_H_ |
||||
#define ABSL_LOG_INTERNAL_TEST_MATCHERS_H_ |
||||
|
||||
#include <iosfwd> |
||||
#include <sstream> |
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/time.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
// These matchers correspond to the components of `absl::LogEntry`.
|
||||
::testing::Matcher<const absl::LogEntry&> SourceFilename( |
||||
const ::testing::Matcher<absl::string_view>& source_filename); |
||||
::testing::Matcher<const absl::LogEntry&> SourceBasename( |
||||
const ::testing::Matcher<absl::string_view>& source_basename); |
||||
// Be careful with this one; multi-line statements using `__LINE__` evaluate
|
||||
// differently on different platforms. In particular, the MSVC implementation
|
||||
// of `EXPECT_DEATH` returns the line number of the macro expansion to all lines
|
||||
// within the code block that's expected to die.
|
||||
::testing::Matcher<const absl::LogEntry&> SourceLine( |
||||
const ::testing::Matcher<int>& source_line); |
||||
::testing::Matcher<const absl::LogEntry&> Prefix( |
||||
const ::testing::Matcher<bool>& prefix); |
||||
::testing::Matcher<const absl::LogEntry&> LogSeverity( |
||||
const ::testing::Matcher<absl::LogSeverity>& log_severity); |
||||
::testing::Matcher<const absl::LogEntry&> Timestamp( |
||||
const ::testing::Matcher<absl::Time>& timestamp); |
||||
// Matches if the `LogEntry`'s timestamp falls after the instantiation of this
|
||||
// matcher and before its execution, as is normal when used with EXPECT_CALL.
|
||||
::testing::Matcher<const absl::LogEntry&> TimestampInMatchWindow(); |
||||
::testing::Matcher<const absl::LogEntry&> ThreadID( |
||||
const ::testing::Matcher<absl::LogEntry::tid_t>&); |
||||
::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefixAndNewline( |
||||
const ::testing::Matcher<absl::string_view>& |
||||
text_message_with_prefix_and_newline); |
||||
::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefix( |
||||
const ::testing::Matcher<absl::string_view>& text_message_with_prefix); |
||||
::testing::Matcher<const absl::LogEntry&> TextMessage( |
||||
const ::testing::Matcher<absl::string_view>& text_message); |
||||
::testing::Matcher<const absl::LogEntry&> TextPrefix( |
||||
const ::testing::Matcher<absl::string_view>& text_prefix); |
||||
::testing::Matcher<const absl::LogEntry&> Verbosity( |
||||
const ::testing::Matcher<int>& verbosity); |
||||
::testing::Matcher<const absl::LogEntry&> Stacktrace( |
||||
const ::testing::Matcher<absl::string_view>& stacktrace); |
||||
// Behaves as `Eq(stream.str())`, but produces better failure messages.
|
||||
::testing::Matcher<absl::string_view> MatchesOstream( |
||||
const std::ostringstream& stream); |
||||
::testing::Matcher<const std::string&> DeathTestValidateExpectations(); |
||||
|
||||
// This feature coming soon =).
|
||||
#define ENCODED_MESSAGE(message_matcher) ::testing::_ |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_TEST_MATCHERS_H_
|
@ -0,0 +1,44 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/internal/voidify.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This class is used to explicitly ignore values in the conditional logging
|
||||
// macros. This avoids compiler warnings like "value computed is not used" and
|
||||
// "statement has no effect".
|
||||
|
||||
#ifndef ABSL_LOG_INTERNAL_VOIDIFY_H_ |
||||
#define ABSL_LOG_INTERNAL_VOIDIFY_H_ |
||||
|
||||
#include "absl/base/config.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
class Voidify final { |
||||
public: |
||||
// This has to be an operator with a precedence lower than << but higher than
|
||||
// ?:
|
||||
template <typename T> |
||||
void operator&&(const T&) const&& {} |
||||
}; |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_INTERNAL_VOIDIFY_H_
|
@ -0,0 +1,439 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/log.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares a family of LOG macros.
|
||||
//
|
||||
// Basic invocation looks like this:
|
||||
//
|
||||
// LOG(INFO) << "Found " << num_cookies << " cookies";
|
||||
//
|
||||
// Most `LOG` macros take a severity level argument. The severity levels are
|
||||
// `INFO`, `WARNING`, `ERROR`, and `FATAL`. They are defined
|
||||
// in absl/base/log_severity.h.
|
||||
// * The `FATAL` severity level terminates the program with a stack trace after
|
||||
// logging its message. Error handlers registered with `RunOnFailure`
|
||||
// (process_state.h) are run, but exit handlers registered with `atexit(3)`
|
||||
// are not.
|
||||
// * The `QFATAL` pseudo-severity level is equivalent to `FATAL` but triggers
|
||||
// quieter termination messages, e.g. without a full stack trace, and skips
|
||||
// running registered error handlers.
|
||||
// Some preprocessor shenanigans are used to ensure that e.g. `LOG(INFO)` has
|
||||
// the same meaning even if a local symbol or preprocessor macro named `INFO` is
|
||||
// defined. To specify a severity level using an expression instead of a
|
||||
// literal, use `LEVEL(expr)`.
|
||||
// Example:
|
||||
//
|
||||
// LOG(LEVEL(stale ? absl::LogSeverity::kWarning : absl::LogSeverity::kInfo))
|
||||
// << "Cookies are " << days << " days old";
|
||||
|
||||
// `LOG` macros evaluate to an unterminated statement. The value at the end of
|
||||
// the statement supports some chainable methods:
|
||||
//
|
||||
// * .AtLocation(absl::string_view file, int line)
|
||||
// .AtLocation(absl::SourceLocation loc)
|
||||
// Overrides the location inferred from the callsite. The string pointed to
|
||||
// by `file` must 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.
|
||||
// * .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 specified `absl::LogEntry`.
|
||||
// This can be used to change the severity of a message, but it has some
|
||||
// limitations:
|
||||
// * `ABSL_MIN_LOG_LEVEL` is evaluated against the severity passed into
|
||||
// `LOG` (or the implicit `FATAL` level of `CHECK`).
|
||||
// * `LOG(FATAL)` and `CHECK` terminate the process unconditionally, even if
|
||||
// the severity is changed later.
|
||||
// `.WithMetadataFrom(entry)` should almost always be used in combination
|
||||
// with `LOG(LEVEL(entry.log_severity()))`.
|
||||
// * .WithPerror()
|
||||
// Appends to the logged message a colon, a space, a textual description of
|
||||
// the current value of `errno` (as by `strerror(3)`), and the numerical
|
||||
// value of `errno`.
|
||||
// * .ToSinkAlso(absl::LogSink* sink)
|
||||
// Sends this message to `*sink` in addition to whatever other sinks it
|
||||
// would otherwise have been sent to. `sink` must not be null.
|
||||
// * .ToSinkOnly(absl::LogSink* sink)
|
||||
// Sends this message to `*sink` and no others. `sink` must not be null.
|
||||
//
|
||||
// No interfaces in this header are async-signal-safe; their use in signal
|
||||
// handlers is unsupported and may deadlock your program or eat your lunch.
|
||||
//
|
||||
// Many logging statements are inherently conditional. For example,
|
||||
// `LOG_IF(INFO, !foo)` does nothing if `foo` is true. Even seemingly
|
||||
// unconditional statements like `LOG(INFO)` might be disabled at
|
||||
// compile-time to minimize binary size or for security reasons.
|
||||
//
|
||||
// * Except for the condition in a `CHECK` or `QCHECK` statement, programs must
|
||||
// not rely on evaluation of expressions anywhere in logging statements for
|
||||
// correctness. For example, this is ok:
|
||||
//
|
||||
// CHECK((fp = fopen("config.ini", "r")) != nullptr);
|
||||
//
|
||||
// But this is probably not ok:
|
||||
//
|
||||
// LOG(INFO) << "Server status: " << StartServerAndReturnStatusString();
|
||||
//
|
||||
// The example below is bad too; the `i++` in the `LOG_IF` condition might
|
||||
// not be evaluated, resulting in an infinite loop:
|
||||
//
|
||||
// for (int i = 0; i < 1000000;)
|
||||
// LOG_IF(INFO, i++ % 1000 == 0) << "Still working...";
|
||||
//
|
||||
// * Except where otherwise noted, conditions which cause a statement not to log
|
||||
// also cause expressions not to be evaluated. Programs may rely on this for
|
||||
// performance reasons, e.g. by streaming the result of an expensive function
|
||||
// call into a `DLOG` or `LOG_EVERY_N` statement.
|
||||
// * Care has been taken to ensure that expressions are parsed by the compiler
|
||||
// even if they are never evaluated. This means that syntax errors will be
|
||||
// caught and variables will be considered used for the purposes of
|
||||
// unused-variable diagnostics. For example, this statement won't compile
|
||||
// even if `INFO`-level logging has been compiled out:
|
||||
//
|
||||
// int number_of_cakes = 40;
|
||||
// LOG(INFO) << "Number of cakes: " << number_of_cake; // Note the typo!
|
||||
//
|
||||
// Similarly, this won't produce unused-variable compiler diagnostics even
|
||||
// if `INFO`-level logging is compiled out:
|
||||
//
|
||||
// {
|
||||
// char fox_line1[] = "Hatee-hatee-hatee-ho!";
|
||||
// LOG_IF(ERROR, false) << "The fox says " << fox_line1;
|
||||
// char fox_line2[] = "A-oo-oo-oo-ooo!";
|
||||
// LOG(INFO) << "The fox also says " << fox_line2;
|
||||
// }
|
||||
//
|
||||
// This error-checking is not perfect; for example, symbols that have been
|
||||
// declared but not defined may not produce link errors if used in logging
|
||||
// statements that compile away.
|
||||
//
|
||||
// Expressions streamed into these macros are formatted using `operator<<` just
|
||||
// as they would be if streamed into a `std::ostream`, however it should be
|
||||
// noted that their actual type is unspecified.
|
||||
//
|
||||
// To implement a custom formatting operator for a type you own, define
|
||||
// `std::ostream& operator<<(std::ostream&, ...)` in your type's namespace (for
|
||||
// ADL) just as you would to stream it to `std::cout`.
|
||||
//
|
||||
// Those macros that support streaming honor output manipulators and `fmtflag`
|
||||
// changes that output data (e.g. `std::ends`) or control formatting of data
|
||||
// (e.g. `std::hex` and `std::fixed`), however flushing such a stream is
|
||||
// ignored. The message produced by a log statement is sent to registered
|
||||
// `absl::LogSink` instances at the end of the statement; those sinks are
|
||||
// responsible for their own flushing (e.g. to disk) semantics.
|
||||
//
|
||||
// Flag settings are not carried over from one `LOG` statement to the next; this
|
||||
// is a bit different than e.g. `std::cout`:
|
||||
//
|
||||
// LOG(INFO) << std::hex << 0xdeadbeef; // logs "0xdeadbeef"
|
||||
// LOG(INFO) << 0xdeadbeef; // logs "3735928559"
|
||||
|
||||
#ifndef ABSL_LOG_LOG_H_ |
||||
#define ABSL_LOG_LOG_H_ |
||||
|
||||
#include "absl/log/internal/conditions.h" |
||||
#include "absl/log/internal/log_message.h" |
||||
#include "absl/log/internal/strip.h" |
||||
|
||||
// LOG()
|
||||
//
|
||||
// `LOG` takes a single argument which is a severity level. Data streamed in
|
||||
// comprise the logged message.
|
||||
// Example:
|
||||
//
|
||||
// LOG(INFO) << "Found " << num_cookies << " cookies";
|
||||
#define LOG(severity) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, true) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
// PLOG()
|
||||
//
|
||||
// `PLOG` behaves like `LOG` except that a description of the current state of
|
||||
// `errno` is appended to the streamed message.
|
||||
#define PLOG(severity) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, true) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
// DLOG()
|
||||
//
|
||||
// `DLOG` behaves like `LOG` in debug mode (i.e. `#ifndef NDEBUG`). Otherwise
|
||||
// it compiles away and does nothing. Note that `DLOG(FATAL)` does not
|
||||
// terminate the program if `NDEBUG` is defined.
|
||||
#ifndef NDEBUG |
||||
#define DLOG(severity) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, true) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
#else |
||||
#define DLOG(severity) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, false) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
#endif |
||||
|
||||
// `LOG_IF` and friends add a second argument which specifies a condition. If
|
||||
// the condition is false, nothing is logged.
|
||||
// Example:
|
||||
//
|
||||
// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
|
||||
//
|
||||
// There is no `VLOG_IF` because the order of evaluation of the arguments is
|
||||
// ambiguous and the alternate spelling with an `if`-statement is trivial.
|
||||
#define LOG_IF(severity, condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, condition) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
#define PLOG_IF(severity, condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, condition) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
#ifndef NDEBUG |
||||
#define DLOG_IF(severity, condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, condition) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
#else |
||||
#define DLOG_IF(severity, condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATELESS, false && (condition)) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
#endif |
||||
|
||||
// LOG_EVERY_N
|
||||
//
|
||||
// An instance of `LOG_EVERY_N` increments a hidden zero-initialized counter
|
||||
// every time execution passes through it and logs the specified message when
|
||||
// the counter's value is a multiple of `n`, doing nothing otherwise. Each
|
||||
// instance has its own counter. The counter's value can be logged by streaming
|
||||
// the symbol `COUNTER`. `LOG_EVERY_N` is thread-safe.
|
||||
// Example:
|
||||
//
|
||||
// LOG_EVERY_N(WARNING, 1000) << "Got a packet with a bad CRC (" << COUNTER
|
||||
// << " total)";
|
||||
#define LOG_EVERY_N(severity, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
// LOG_FIRST_N
|
||||
//
|
||||
// `LOG_FIRST_N` behaves like `LOG_EVERY_N` except that the specified message is
|
||||
// logged when the counter's value is less than `n`. `LOG_FIRST_N` is
|
||||
// thread-safe.
|
||||
#define LOG_FIRST_N(severity, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(FirstN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
// LOG_EVERY_POW_2
|
||||
//
|
||||
// `LOG_EVERY_POW_2` behaves like `LOG_EVERY_N` except that the specified
|
||||
// message is logged when the counter's value is a power of 2.
|
||||
// `LOG_EVERY_POW_2` is thread-safe.
|
||||
#define LOG_EVERY_POW_2(severity) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryPow2) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
// LOG_EVERY_N_SEC
|
||||
//
|
||||
// An instance of `LOG_EVERY_N_SEC` uses a hidden state variable to log the
|
||||
// specified message at most once every `n_seconds`. A hidden counter of
|
||||
// executions (whether a message is logged or not) is also maintained and can be
|
||||
// logged by streaming the symbol `COUNTER`. `LOG_EVERY_N_SEC` is thread-safe.
|
||||
// Example:
|
||||
//
|
||||
// LOG_EVERY_N_SEC(INFO, 2.5) << "Got " << COUNTER << " cookies so far";
|
||||
#define LOG_EVERY_N_SEC(severity, n_seconds) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryNSec, n_seconds) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define PLOG_EVERY_N(severity, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
#define PLOG_FIRST_N(severity, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(FirstN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
#define PLOG_EVERY_POW_2(severity) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryPow2) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
#define PLOG_EVERY_N_SEC(severity, n_seconds) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, true)(EveryNSec, n_seconds) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
#ifndef NDEBUG |
||||
#define DLOG_EVERY_N(severity, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
|
||||
(EveryN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_FIRST_N(severity, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
|
||||
(FirstN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_EVERY_POW_2(severity) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
|
||||
(EveryPow2) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_EVERY_N_SEC(severity, n_seconds) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, true) \
|
||||
(EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#else // def NDEBUG
|
||||
#define DLOG_EVERY_N(severity, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
|
||||
(EveryN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_FIRST_N(severity, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
|
||||
(FirstN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_EVERY_POW_2(severity) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
|
||||
(EveryPow2) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_EVERY_N_SEC(severity, n_seconds) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_INFO(STATEFUL, false) \
|
||||
(EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
#endif // def NDEBUG
|
||||
|
||||
#define VLOG_EVERY_N(verbose_level, n) \ |
||||
for (int absl_logging_internal_verbose_level = (verbose_level), \
|
||||
absl_logging_internal_log_loop = 1; \
|
||||
absl_logging_internal_log_loop; absl_logging_internal_log_loop = 0) \
|
||||
ABSL_LOG_INTERNAL_CONDITION_INFO( \
|
||||
STATEFUL, VLOG_IS_ON(absl_logging_internal_verbose_level)) \
|
||||
(EveryN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
|
||||
absl_logging_internal_verbose_level) |
||||
|
||||
#define VLOG_FIRST_N(verbose_level, n) \ |
||||
for (int absl_logging_internal_verbose_level = (verbose_level), \
|
||||
absl_logging_internal_log_loop = 1; \
|
||||
absl_logging_internal_log_loop; absl_logging_internal_log_loop = 0) \
|
||||
ABSL_LOG_INTERNAL_CONDITION_INFO( \
|
||||
STATEFUL, VLOG_IS_ON(absl_logging_internal_verbose_level)) \
|
||||
(FirstN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
|
||||
absl_logging_internal_verbose_level) |
||||
|
||||
#define VLOG_EVERY_POW_2(verbose_level) \ |
||||
for (int absl_logging_internal_verbose_level = (verbose_level), \
|
||||
absl_logging_internal_log_loop = 1; \
|
||||
absl_logging_internal_log_loop; absl_logging_internal_log_loop = 0) \
|
||||
ABSL_LOG_INTERNAL_CONDITION_INFO( \
|
||||
STATEFUL, VLOG_IS_ON(absl_logging_internal_verbose_level)) \
|
||||
(EveryPow2) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
|
||||
absl_logging_internal_verbose_level) |
||||
|
||||
#define VLOG_EVERY_N_SEC(verbose_level, n_seconds) \ |
||||
for (int absl_logging_internal_verbose_level = (verbose_level), \
|
||||
absl_logging_internal_log_loop = 1; \
|
||||
absl_logging_internal_log_loop; absl_logging_internal_log_loop = 0) \
|
||||
ABSL_LOG_INTERNAL_CONDITION_INFO( \
|
||||
STATEFUL, VLOG_IS_ON(absl_logging_internal_verbose_level)) \
|
||||
(EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream() \
|
||||
.WithVerbosity(absl_logging_internal_verbose_level) |
||||
|
||||
// `LOG_IF_EVERY_N` and friends behave as the corresponding `LOG_EVERY_N`
|
||||
// but neither increment a counter nor log a message if condition is false (as
|
||||
// `LOG_IF`).
|
||||
// Example:
|
||||
//
|
||||
// LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << COUNTER
|
||||
// << "th big cookie";
|
||||
#define LOG_IF_EVERY_N(severity, condition, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define LOG_IF_FIRST_N(severity, condition, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(FirstN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define LOG_IF_EVERY_POW_2(severity, condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryPow2) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define LOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryNSec, \
|
||||
n_seconds) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define PLOG_IF_EVERY_N(severity, condition, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
#define PLOG_IF_FIRST_N(severity, condition, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(FirstN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
#define PLOG_IF_EVERY_POW_2(severity, condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryPow2) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
#define PLOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryNSec, \
|
||||
n_seconds) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() \
|
||||
.WithPerror() |
||||
|
||||
#ifndef NDEBUG |
||||
#define DLOG_IF_EVERY_N(severity, condition, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_IF_FIRST_N(severity, condition, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(FirstN, n) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_IF_EVERY_POW_2(severity, condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryPow2) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, condition)(EveryNSec, \
|
||||
n_seconds) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#else // def NDEBUG
|
||||
#define DLOG_IF_EVERY_N(severity, condition, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, false && (condition))( \
|
||||
EveryN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_IF_FIRST_N(severity, condition, n) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, false && (condition))( \
|
||||
FirstN, n) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_IF_EVERY_POW_2(severity, condition) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, false && (condition))( \
|
||||
EveryPow2) ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
|
||||
#define DLOG_IF_EVERY_N_SEC(severity, condition, n_seconds) \ |
||||
ABSL_LOG_INTERNAL_CONDITION_##severity(STATEFUL, false && (condition))( \
|
||||
EveryNSec, n_seconds) \
|
||||
ABSL_LOGGING_INTERNAL_LOG_##severity.InternalStream() |
||||
#endif // def NDEBUG
|
||||
|
||||
#endif // ABSL_LOG_LOG_H_
|
@ -0,0 +1,97 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/flags/flag.h" |
||||
#include "absl/log/check.h" |
||||
#include "absl/log/globals.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/log/log_sink.h" |
||||
#include "absl/log/log_sink_registry.h" |
||||
#include "benchmark/benchmark.h" |
||||
|
||||
namespace { |
||||
|
||||
class NullLogSink : public absl::LogSink { |
||||
public: |
||||
NullLogSink() { absl::AddLogSink(this); } |
||||
|
||||
~NullLogSink() override { absl::RemoveLogSink(this); } |
||||
|
||||
void Send(const absl::LogEntry&) override {} |
||||
}; |
||||
|
||||
constexpr int x = -1; |
||||
|
||||
void BM_SuccessfulBinaryCheck(benchmark::State& state) { |
||||
int n = 0; |
||||
while (state.KeepRunningBatch(8)) { |
||||
CHECK_GE(n, x); |
||||
CHECK_GE(n, x); |
||||
CHECK_GE(n, x); |
||||
CHECK_GE(n, x); |
||||
CHECK_GE(n, x); |
||||
CHECK_GE(n, x); |
||||
CHECK_GE(n, x); |
||||
CHECK_GE(n, x); |
||||
++n; |
||||
} |
||||
benchmark::DoNotOptimize(n); |
||||
} |
||||
BENCHMARK(BM_SuccessfulBinaryCheck); |
||||
|
||||
static void BM_SuccessfulUnaryCheck(benchmark::State& state) { |
||||
int n = 0; |
||||
while (state.KeepRunningBatch(8)) { |
||||
CHECK(n >= x); |
||||
CHECK(n >= x); |
||||
CHECK(n >= x); |
||||
CHECK(n >= x); |
||||
CHECK(n >= x); |
||||
CHECK(n >= x); |
||||
CHECK(n >= x); |
||||
CHECK(n >= x); |
||||
++n; |
||||
} |
||||
benchmark::DoNotOptimize(n); |
||||
} |
||||
BENCHMARK(BM_SuccessfulUnaryCheck); |
||||
|
||||
static void BM_DisabledLogOverhead(benchmark::State& state) { |
||||
absl::ScopedStderrThreshold disable_stderr_logging( |
||||
absl::LogSeverityAtLeast::kInfinity); |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level( |
||||
absl::LogSeverityAtLeast::kInfinity); |
||||
for (auto _ : state) { |
||||
LOG(INFO); |
||||
} |
||||
} |
||||
BENCHMARK(BM_DisabledLogOverhead); |
||||
|
||||
static void BM_EnabledLogOverhead(benchmark::State& state) { |
||||
absl::ScopedStderrThreshold stderr_logging( |
||||
absl::LogSeverityAtLeast::kInfinity); |
||||
absl::log_internal::ScopedMinLogLevel scoped_min_log_level( |
||||
absl::LogSeverityAtLeast::kInfo); |
||||
ABSL_ATTRIBUTE_UNUSED NullLogSink null_sink; |
||||
for (auto _ : state) { |
||||
LOG(INFO); |
||||
} |
||||
} |
||||
BENCHMARK(BM_EnabledLogOverhead); |
||||
|
||||
} // namespace
|
||||
|
@ -0,0 +1,29 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/log_entry.h" |
||||
|
||||
#include "absl/base/config.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL |
||||
constexpr int LogEntry::kNoVerbosityLevel; |
||||
constexpr int LogEntry::kNoVerboseLevel; |
||||
#endif |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,163 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/log_entry.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares `class absl::LogEntry`, which represents a log record as
|
||||
// passed to `LogSink::Send`. Data returned by pointer or by reference or by
|
||||
// `absl::string_view` must be copied if they are needed after the lifetime of
|
||||
// the `absl::LogEntry`.
|
||||
|
||||
#ifndef ABSL_LOG_LOG_ENTRY_H_ |
||||
#define ABSL_LOG_LOG_ENTRY_H_ |
||||
|
||||
#include <cstddef> |
||||
#include <string> |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/internal/config.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/time.h" |
||||
#include "absl/types/span.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
namespace log_internal { |
||||
// Test only friend.
|
||||
class LogEntryTestPeer; |
||||
class LogMessage; |
||||
} // namespace log_internal
|
||||
|
||||
// LogEntry
|
||||
//
|
||||
// Represents a single entry in a log, i.e., one log message.
|
||||
//
|
||||
// LogEntry is copyable and thread-compatible.
|
||||
class LogEntry final { |
||||
public: |
||||
using tid_t = log_internal::Tid; |
||||
|
||||
// For non-verbose log entries, `verbosity()` returns `kNoVerbosityLevel`.
|
||||
static constexpr int kNoVerbosityLevel = -1; |
||||
static constexpr int kNoVerboseLevel = -1; // TO BE removed
|
||||
|
||||
LogEntry(const LogEntry&) = default; |
||||
LogEntry& operator=(const LogEntry&) = default; |
||||
|
||||
// Source file and line where the log message occurred.
|
||||
// Take special care not to dereference the pointers returned by
|
||||
// source_filename() and source_basename() after the lifetime of the
|
||||
// `LogEntry`. This will usually work, because these are usually backed by a
|
||||
// statically allocated char array obtained from the `__FILE__` macro, but
|
||||
// it is nevertheless incorrect and will be broken by statements like
|
||||
// `LOG(INFO).AtLocation(...)` (see above). If you need the data later, you
|
||||
// must copy it.
|
||||
absl::string_view source_filename() const { return full_filename_; } |
||||
absl::string_view source_basename() const { return base_filename_; } |
||||
int source_line() const { return line_; } |
||||
|
||||
// LogEntry::prefix()
|
||||
//
|
||||
// True unless cleared by LOG(...).NoPrefix(), which indicates suppression of
|
||||
// the line prefix containing metadata like file, line, timestamp, etc.
|
||||
bool prefix() const { return prefix_; } |
||||
|
||||
// LogEntry::log_severity()
|
||||
//
|
||||
// Returns this LogEntry's severity.
|
||||
absl::LogSeverity log_severity() const { return severity_; } |
||||
|
||||
// LogEntry::verbosity()
|
||||
//
|
||||
// Returns this LogEntry's verbosity, or kNoVerbosityLevel for a non-verbose
|
||||
// LogEntry.
|
||||
int verbosity() const { return verbose_level_; } |
||||
|
||||
// LogEntry::timestamp()
|
||||
//
|
||||
// Returns the time at which this LogEntry was written.
|
||||
absl::Time timestamp() const { return timestamp_; } |
||||
|
||||
// LogEntry::tid()
|
||||
//
|
||||
// Returns the id of the thread that wrote this LogEntry.
|
||||
tid_t tid() const { return tid_; } |
||||
|
||||
// Text-formatted version of the log message. An underlying buffer holds:
|
||||
//
|
||||
// * A prefix formed by formatting metadata (timestamp, filename, line number,
|
||||
// etc.)
|
||||
// * The streamed data
|
||||
// * A newline
|
||||
// * A nul terminator
|
||||
//
|
||||
// These methods give access to the most commonly-used substrings of the
|
||||
// buffer's contents. Other combinations can be obtained with substring
|
||||
// arithmetic.
|
||||
absl::string_view text_message_with_prefix_and_newline() const { |
||||
return absl::string_view( |
||||
text_message_with_prefix_and_newline_and_nul_.data(), |
||||
text_message_with_prefix_and_newline_and_nul_.size() - 1); |
||||
} |
||||
absl::string_view text_message_with_prefix() const { |
||||
return absl::string_view( |
||||
text_message_with_prefix_and_newline_and_nul_.data(), |
||||
text_message_with_prefix_and_newline_and_nul_.size() - 2); |
||||
} |
||||
absl::string_view text_message_with_newline() const { |
||||
return absl::string_view( |
||||
text_message_with_prefix_and_newline_and_nul_.data() + prefix_len_, |
||||
text_message_with_prefix_and_newline_and_nul_.size() - prefix_len_ - 1); |
||||
} |
||||
absl::string_view text_message() const { |
||||
return absl::string_view( |
||||
text_message_with_prefix_and_newline_and_nul_.data() + prefix_len_, |
||||
text_message_with_prefix_and_newline_and_nul_.size() - prefix_len_ - 2); |
||||
} |
||||
const char* text_message_with_prefix_and_newline_c_str() const { |
||||
return text_message_with_prefix_and_newline_and_nul_.data(); |
||||
} |
||||
|
||||
// LogEntry::stacktrace()
|
||||
//
|
||||
// Optional stacktrace, e.g., for `FATAL` logs.
|
||||
absl::string_view stacktrace() const { return stacktrace_; } |
||||
|
||||
private: |
||||
LogEntry() = default; |
||||
|
||||
absl::string_view full_filename_; |
||||
absl::string_view base_filename_; |
||||
int line_; |
||||
bool prefix_; |
||||
absl::LogSeverity severity_; |
||||
int verbose_level_; // >=0 for `VLOG`, etc.; otherwise `kNoVerbosityLevel`.
|
||||
absl::Time timestamp_; |
||||
tid_t tid_; |
||||
absl::Span<const char> text_message_with_prefix_and_newline_and_nul_; |
||||
size_t prefix_len_; |
||||
std::string stacktrace_; |
||||
|
||||
friend class log_internal::LogEntryTestPeer; |
||||
friend class log_internal::LogMessage; |
||||
}; |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_LOG_ENTRY_H_
|
@ -0,0 +1,432 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/log_entry.h" |
||||
|
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
|
||||
#include <cstring> |
||||
#include <limits> |
||||
#include <string> |
||||
#include <type_traits> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/internal/log_format.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/strings/numbers.h" |
||||
#include "absl/strings/str_split.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/civil_time.h" |
||||
#include "absl/time/time.h" |
||||
#include "absl/types/span.h" |
||||
|
||||
namespace { |
||||
|
||||
using ::absl::log_internal::LogEntryTestPeer; |
||||
using ::testing::Eq; |
||||
using ::testing::IsTrue; |
||||
using ::testing::StartsWith; |
||||
using ::testing::StrEq; |
||||
|
||||
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( |
||||
new absl::log_internal::LogTestEnvironment); |
||||
|
||||
// Copies into `dst` as many bytes of `src` as will fit, then truncates the
|
||||
// copied bytes from the front of `dst` and returns the number of bytes written.
|
||||
size_t AppendTruncated(absl::string_view src, absl::Span<char>& dst) { |
||||
if (src.size() > dst.size()) src = src.substr(0, dst.size()); |
||||
memcpy(dst.data(), src.data(), src.size()); |
||||
dst.remove_prefix(src.size()); |
||||
return src.size(); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace log_internal { |
||||
|
||||
class LogEntryTestPeer { |
||||
public: |
||||
LogEntryTestPeer(absl::string_view base_filename, int line, bool prefix, |
||||
absl::LogSeverity severity, absl::string_view timestamp, |
||||
absl::LogEntry::tid_t tid, absl::string_view text_message) |
||||
: buf_(15000, '\0') { |
||||
entry_.base_filename_ = base_filename; |
||||
entry_.line_ = line; |
||||
entry_.prefix_ = prefix; |
||||
entry_.severity_ = severity; |
||||
std::string time_err; |
||||
EXPECT_THAT( |
||||
absl::ParseTime("%Y-%m-%d%ET%H:%M:%E*S", timestamp, |
||||
absl::LocalTimeZone(), &entry_.timestamp_, &time_err), |
||||
IsTrue()) |
||||
<< "Failed to parse time " << timestamp << ": " << time_err; |
||||
entry_.tid_ = tid; |
||||
std::pair<absl::string_view, std::string> timestamp_bits = |
||||
absl::StrSplit(timestamp, absl::ByChar('.')); |
||||
EXPECT_THAT(absl::ParseCivilTime(timestamp_bits.first, &ci_.cs), IsTrue()) |
||||
<< "Failed to parse time " << timestamp_bits.first; |
||||
timestamp_bits.second.resize(9, '0'); |
||||
int64_t nanos = 0; |
||||
EXPECT_THAT(absl::SimpleAtoi(timestamp_bits.second, &nanos), IsTrue()) |
||||
<< "Failed to parse time " << timestamp_bits.first; |
||||
ci_.subsecond = absl::Nanoseconds(nanos); |
||||
|
||||
absl::Span<char> view = absl::MakeSpan(buf_); |
||||
view.remove_suffix(2); |
||||
entry_.prefix_len_ = |
||||
entry_.prefix_ |
||||
? log_internal::FormatLogPrefix( |
||||
entry_.log_severity(), entry_.timestamp(), entry_.tid(), |
||||
entry_.source_basename(), entry_.source_line(), view) |
||||
: 0; |
||||
|
||||
EXPECT_THAT(entry_.prefix_len_, Eq(view.data() - buf_.data())); |
||||
AppendTruncated(text_message, view); |
||||
view = absl::Span<char>(view.data(), view.size() + 2); |
||||
view[0] = '\n'; |
||||
view[1] = '\0'; |
||||
view.remove_prefix(2); |
||||
buf_.resize(view.data() - buf_.data()); |
||||
entry_.text_message_with_prefix_and_newline_and_nul_ = absl::MakeSpan(buf_); |
||||
} |
||||
LogEntryTestPeer(const LogEntryTestPeer&) = delete; |
||||
LogEntryTestPeer& operator=(const LogEntryTestPeer&) = delete; |
||||
|
||||
std::string FormatLogMessage() const { |
||||
return log_internal::FormatLogMessage( |
||||
entry_.log_severity(), ci_.cs, ci_.subsecond, entry_.tid(), |
||||
entry_.source_basename(), entry_.source_line(), entry_.text_message()); |
||||
} |
||||
std::string FormatPrefixIntoSizedBuffer(size_t sz) { |
||||
std::string str(sz, '\0'); |
||||
absl::Span<char> buf(&str[0], str.size()); |
||||
const size_t prefix_size = log_internal::FormatLogPrefix( |
||||
entry_.log_severity(), entry_.timestamp(), entry_.tid(), |
||||
entry_.source_basename(), entry_.source_line(), buf); |
||||
EXPECT_THAT(prefix_size, Eq(buf.data() - str.data())); |
||||
str.resize(prefix_size); |
||||
return str; |
||||
} |
||||
const absl::LogEntry& entry() const { return entry_; } |
||||
|
||||
private: |
||||
absl::LogEntry entry_; |
||||
absl::TimeZone::CivilInfo ci_; |
||||
std::vector<char> buf_; |
||||
}; |
||||
|
||||
} // namespace log_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
namespace { |
||||
constexpr bool kUsePrefix = true, kNoPrefix = false; |
||||
|
||||
TEST(LogEntryTest, Baseline) { |
||||
LogEntryTestPeer entry("foo.cc", 1234, kUsePrefix, absl::LogSeverity::kInfo, |
||||
"2020-01-02T03:04:05.6789", 451, "hello world"); |
||||
EXPECT_THAT(entry.FormatLogMessage(), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:1234] hello world")); |
||||
EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:1234] ")); |
||||
for (size_t sz = strlen("I0102 03:04:05.678900 451 foo.cc:1234] ") + 20; |
||||
sz != std::numeric_limits<size_t>::max(); sz--) |
||||
EXPECT_THAT("I0102 03:04:05.678900 451 foo.cc:1234] ", |
||||
StartsWith(entry.FormatPrefixIntoSizedBuffer(sz))); |
||||
|
||||
EXPECT_THAT(entry.entry().text_message_with_prefix_and_newline(), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:1234] hello world\n")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline_c_str(), |
||||
StrEq("I0102 03:04:05.678900 451 foo.cc:1234] hello world\n")); |
||||
EXPECT_THAT(entry.entry().text_message_with_prefix(), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:1234] hello world")); |
||||
EXPECT_THAT(entry.entry().text_message(), Eq("hello world")); |
||||
} |
||||
|
||||
TEST(LogEntryTest, NoPrefix) { |
||||
LogEntryTestPeer entry("foo.cc", 1234, kNoPrefix, absl::LogSeverity::kInfo, |
||||
"2020-01-02T03:04:05.6789", 451, "hello world"); |
||||
EXPECT_THAT(entry.FormatLogMessage(), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:1234] hello world")); |
||||
// These methods are not responsible for honoring `prefix()`.
|
||||
EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:1234] ")); |
||||
for (size_t sz = strlen("I0102 03:04:05.678900 451 foo.cc:1234] ") + 20; |
||||
sz != std::numeric_limits<size_t>::max(); sz--) |
||||
EXPECT_THAT("I0102 03:04:05.678900 451 foo.cc:1234] ", |
||||
StartsWith(entry.FormatPrefixIntoSizedBuffer(sz))); |
||||
|
||||
EXPECT_THAT(entry.entry().text_message_with_prefix_and_newline(), |
||||
Eq("hello world\n")); |
||||
EXPECT_THAT(entry.entry().text_message_with_prefix_and_newline_c_str(), |
||||
StrEq("hello world\n")); |
||||
EXPECT_THAT(entry.entry().text_message_with_prefix(), Eq("hello world")); |
||||
EXPECT_THAT(entry.entry().text_message(), Eq("hello world")); |
||||
} |
||||
|
||||
TEST(LogEntryTest, EmptyFields) { |
||||
LogEntryTestPeer entry("", 0, kUsePrefix, absl::LogSeverity::kInfo, |
||||
"2020-01-02T03:04:05", 0, ""); |
||||
const std::string format_message = entry.FormatLogMessage(); |
||||
EXPECT_THAT(format_message, Eq("I0102 03:04:05.000000 0 :0] ")); |
||||
EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000), Eq(format_message)); |
||||
for (size_t sz = format_message.size() + 20; |
||||
sz != std::numeric_limits<size_t>::max(); sz--) |
||||
EXPECT_THAT(format_message, |
||||
StartsWith(entry.FormatPrefixIntoSizedBuffer(sz))); |
||||
|
||||
EXPECT_THAT(entry.entry().text_message_with_prefix_and_newline(), |
||||
Eq("I0102 03:04:05.000000 0 :0] \n")); |
||||
EXPECT_THAT(entry.entry().text_message_with_prefix_and_newline_c_str(), |
||||
StrEq("I0102 03:04:05.000000 0 :0] \n")); |
||||
EXPECT_THAT(entry.entry().text_message_with_prefix(), |
||||
Eq("I0102 03:04:05.000000 0 :0] ")); |
||||
EXPECT_THAT(entry.entry().text_message(), Eq("")); |
||||
} |
||||
|
||||
TEST(LogEntryTest, NegativeFields) { |
||||
if (std::is_signed<absl::LogEntry::tid_t>::value) { |
||||
LogEntryTestPeer entry("foo.cc", -1234, kUsePrefix, |
||||
absl::LogSeverity::kInfo, "2020-01-02T03:04:05.6789", |
||||
-451, "hello world"); |
||||
EXPECT_THAT(entry.FormatLogMessage(), |
||||
Eq("I0102 03:04:05.678900 -451 foo.cc:-1234] hello world")); |
||||
EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000), |
||||
Eq("I0102 03:04:05.678900 -451 foo.cc:-1234] ")); |
||||
for (size_t sz = |
||||
strlen("I0102 03:04:05.678900 -451 foo.cc:-1234] ") + 20; |
||||
sz != std::numeric_limits<size_t>::max(); sz--) |
||||
EXPECT_THAT("I0102 03:04:05.678900 -451 foo.cc:-1234] ", |
||||
StartsWith(entry.FormatPrefixIntoSizedBuffer(sz))); |
||||
|
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline(), |
||||
Eq("I0102 03:04:05.678900 -451 foo.cc:-1234] hello world\n")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline_c_str(), |
||||
StrEq("I0102 03:04:05.678900 -451 foo.cc:-1234] hello world\n")); |
||||
EXPECT_THAT(entry.entry().text_message_with_prefix(), |
||||
Eq("I0102 03:04:05.678900 -451 foo.cc:-1234] hello world")); |
||||
EXPECT_THAT(entry.entry().text_message(), Eq("hello world")); |
||||
} else { |
||||
LogEntryTestPeer entry("foo.cc", -1234, kUsePrefix, |
||||
absl::LogSeverity::kInfo, "2020-01-02T03:04:05.6789", |
||||
451, "hello world"); |
||||
EXPECT_THAT(entry.FormatLogMessage(), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:-1234] hello world")); |
||||
EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:-1234] ")); |
||||
for (size_t sz = |
||||
strlen("I0102 03:04:05.678900 451 foo.cc:-1234] ") + 20; |
||||
sz != std::numeric_limits<size_t>::max(); sz--) |
||||
EXPECT_THAT("I0102 03:04:05.678900 451 foo.cc:-1234] ", |
||||
StartsWith(entry.FormatPrefixIntoSizedBuffer(sz))); |
||||
|
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline(), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:-1234] hello world\n")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline_c_str(), |
||||
StrEq("I0102 03:04:05.678900 451 foo.cc:-1234] hello world\n")); |
||||
EXPECT_THAT(entry.entry().text_message_with_prefix(), |
||||
Eq("I0102 03:04:05.678900 451 foo.cc:-1234] hello world")); |
||||
EXPECT_THAT(entry.entry().text_message(), Eq("hello world")); |
||||
} |
||||
} |
||||
|
||||
TEST(LogEntryTest, LongFields) { |
||||
LogEntryTestPeer entry( |
||||
"I am the very model of a modern Major-General / " |
||||
"I've information vegetable, animal, and mineral.", |
||||
2147483647, kUsePrefix, absl::LogSeverity::kInfo, |
||||
"2020-01-02T03:04:05.678967896789", 2147483647, |
||||
"I know the kings of England, and I quote the fights historical / " |
||||
"From Marathon to Waterloo, in order categorical."); |
||||
EXPECT_THAT(entry.FormatLogMessage(), |
||||
Eq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.")); |
||||
EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000), |
||||
Eq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:2147483647] ")); |
||||
for (size_t sz = |
||||
strlen("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:2147483647] ") + |
||||
20; |
||||
sz != std::numeric_limits<size_t>::max(); sz--) |
||||
EXPECT_THAT( |
||||
"I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:2147483647] ", |
||||
StartsWith(entry.FormatPrefixIntoSizedBuffer(sz))); |
||||
|
||||
EXPECT_THAT(entry.entry().text_message_with_prefix_and_newline(), |
||||
Eq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.\n")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline_c_str(), |
||||
StrEq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.\n")); |
||||
EXPECT_THAT(entry.entry().text_message_with_prefix(), |
||||
Eq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message(), |
||||
Eq("I know the kings of England, and I quote the fights historical / " |
||||
"From Marathon to Waterloo, in order categorical.")); |
||||
} |
||||
|
||||
TEST(LogEntryTest, LongNegativeFields) { |
||||
if (std::is_signed<absl::LogEntry::tid_t>::value) { |
||||
LogEntryTestPeer entry( |
||||
"I am the very model of a modern Major-General / " |
||||
"I've information vegetable, animal, and mineral.", |
||||
-2147483647, kUsePrefix, absl::LogSeverity::kInfo, |
||||
"2020-01-02T03:04:05.678967896789", -2147483647, |
||||
"I know the kings of England, and I quote the fights historical / " |
||||
"From Marathon to Waterloo, in order categorical."); |
||||
EXPECT_THAT( |
||||
entry.FormatLogMessage(), |
||||
Eq("I0102 03:04:05.678967 -2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.")); |
||||
EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000), |
||||
Eq("I0102 03:04:05.678967 -2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] ")); |
||||
for (size_t sz = |
||||
strlen( |
||||
"I0102 03:04:05.678967 -2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] ") + |
||||
20; |
||||
sz != std::numeric_limits<size_t>::max(); sz--) |
||||
EXPECT_THAT( |
||||
"I0102 03:04:05.678967 -2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] ", |
||||
StartsWith(entry.FormatPrefixIntoSizedBuffer(sz))); |
||||
|
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline(), |
||||
Eq("I0102 03:04:05.678967 -2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.\n")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline_c_str(), |
||||
StrEq("I0102 03:04:05.678967 -2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.\n")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix(), |
||||
Eq("I0102 03:04:05.678967 -2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message(), |
||||
Eq("I know the kings of England, and I quote the fights historical / " |
||||
"From Marathon to Waterloo, in order categorical.")); |
||||
} else { |
||||
LogEntryTestPeer entry( |
||||
"I am the very model of a modern Major-General / " |
||||
"I've information vegetable, animal, and mineral.", |
||||
-2147483647, kUsePrefix, absl::LogSeverity::kInfo, |
||||
"2020-01-02T03:04:05.678967896789", 2147483647, |
||||
"I know the kings of England, and I quote the fights historical / " |
||||
"From Marathon to Waterloo, in order categorical."); |
||||
EXPECT_THAT( |
||||
entry.FormatLogMessage(), |
||||
Eq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.")); |
||||
EXPECT_THAT(entry.FormatPrefixIntoSizedBuffer(1000), |
||||
Eq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] ")); |
||||
for (size_t sz = |
||||
strlen( |
||||
"I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] ") + |
||||
20; |
||||
sz != std::numeric_limits<size_t>::max(); sz--) |
||||
EXPECT_THAT( |
||||
"I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] ", |
||||
StartsWith(entry.FormatPrefixIntoSizedBuffer(sz))); |
||||
|
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline(), |
||||
Eq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.\n")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix_and_newline_c_str(), |
||||
StrEq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.\n")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message_with_prefix(), |
||||
Eq("I0102 03:04:05.678967 2147483647 I am the very model of a " |
||||
"modern Major-General / I've information vegetable, animal, " |
||||
"and mineral.:-2147483647] I know the kings of England, and I " |
||||
"quote the fights historical / From Marathon to Waterloo, in " |
||||
"order categorical.")); |
||||
EXPECT_THAT( |
||||
entry.entry().text_message(), |
||||
Eq("I know the kings of England, and I quote the fights historical / " |
||||
"From Marathon to Waterloo, in order categorical.")); |
||||
} |
||||
} |
||||
|
||||
} // namespace
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,171 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/log/scoped_mock_log.h" |
||||
|
||||
namespace { |
||||
using ::testing::_; |
||||
using ::testing::Eq; |
||||
|
||||
namespace not_absl { |
||||
|
||||
class Dummy { |
||||
public: |
||||
Dummy() {} |
||||
|
||||
private: |
||||
Dummy(const Dummy&) = delete; |
||||
Dummy& operator=(const Dummy&) = delete; |
||||
}; |
||||
|
||||
// This line tests that local definitions of INFO, WARNING, ERROR, and
|
||||
// etc don't shadow the global ones used by the logging macros. If
|
||||
// they do, the LOG() calls in the tests won't compile, catching the
|
||||
// bug.
|
||||
const Dummy INFO, WARNING, ERROR, FATAL, NUM_SEVERITIES; |
||||
|
||||
// These makes sure that the uses of same-named types in the
|
||||
// implementation of the logging macros are fully qualified.
|
||||
class string {}; |
||||
class vector {}; |
||||
class LogMessage {}; |
||||
class LogMessageFatal {}; |
||||
class LogMessageQuietlyFatal {}; |
||||
class LogMessageVoidify {}; |
||||
class LogSink {}; |
||||
class NullStream {}; |
||||
class NullStreamFatal {}; |
||||
|
||||
} // namespace not_absl
|
||||
|
||||
using namespace not_absl; // NOLINT
|
||||
|
||||
// Tests for LOG(LEVEL(()).
|
||||
|
||||
TEST(LogHygieneTest, WorksForQualifiedSeverity) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
::testing::InSequence seq; |
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "To INFO")); |
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kWarning, _, "To WARNING")); |
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kError, _, "To ERROR")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
// Note that LOG(LEVEL()) expects the severity as a run-time
|
||||
// expression (as opposed to a compile-time constant). Hence we
|
||||
// test that :: is allowed before INFO, etc.
|
||||
LOG(LEVEL(absl::LogSeverity::kInfo)) << "To INFO"; |
||||
LOG(LEVEL(absl::LogSeverity::kWarning)) << "To WARNING"; |
||||
LOG(LEVEL(absl::LogSeverity::kError)) << "To ERROR"; |
||||
} |
||||
|
||||
TEST(LogHygieneTest, WorksWithAlternativeINFOSymbol) { |
||||
const double INFO ABSL_ATTRIBUTE_UNUSED = 7.77; |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "Hello world")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO) << "Hello world"; |
||||
} |
||||
|
||||
TEST(LogHygieneTest, WorksWithAlternativeWARNINGSymbol) { |
||||
const double WARNING ABSL_ATTRIBUTE_UNUSED = 7.77; |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kWarning, _, "Hello world")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(WARNING) << "Hello world"; |
||||
} |
||||
|
||||
TEST(LogHygieneTest, WorksWithAlternativeERRORSymbol) { |
||||
const double ERROR ABSL_ATTRIBUTE_UNUSED = 7.77; |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kError, _, "Hello world")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(ERROR) << "Hello world"; |
||||
} |
||||
|
||||
TEST(LogHygieneTest, WorksWithAlternativeLEVELSymbol) { |
||||
const double LEVEL ABSL_ATTRIBUTE_UNUSED = 7.77; |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kError, _, "Hello world")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(LEVEL(absl::LogSeverity::kError)) << "Hello world"; |
||||
} |
||||
|
||||
#define INFO Bogus |
||||
#ifdef NDEBUG |
||||
constexpr bool IsOptimized = false; |
||||
#else |
||||
constexpr bool IsOptimized = true; |
||||
#endif |
||||
|
||||
TEST(LogHygieneTest, WorksWithINFODefined) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "Hello world")) |
||||
.Times(2 + (IsOptimized ? 2 : 0)); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO) << "Hello world"; |
||||
LOG_IF(INFO, true) << "Hello world"; |
||||
|
||||
DLOG(INFO) << "Hello world"; |
||||
DLOG_IF(INFO, true) << "Hello world"; |
||||
} |
||||
|
||||
#undef INFO |
||||
|
||||
TEST(LogHygieneTest, ExpressionEvaluationInLEVELSeverity) { |
||||
auto i = static_cast<int>(absl::LogSeverity::kInfo); |
||||
LOG(LEVEL(++i)) << "hello world"; // NOLINT
|
||||
EXPECT_THAT(i, Eq(static_cast<int>(absl::LogSeverity::kInfo) + 1)); |
||||
} |
||||
|
||||
TEST(LogHygieneTest, ExpressionEvaluationInStreamedMessage) { |
||||
int i = 0; |
||||
LOG(INFO) << ++i; |
||||
EXPECT_THAT(i, 1); |
||||
LOG_IF(INFO, false) << ++i; |
||||
EXPECT_THAT(i, 1); |
||||
} |
||||
|
||||
// Tests that macros are usable in unbraced switch statements.
|
||||
// -----------------------------------------------------------
|
||||
|
||||
class UnbracedSwitchCompileTest { |
||||
static void Log() { |
||||
switch (0) { |
||||
case 0: |
||||
LOG(INFO); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
} // namespace
|
@ -0,0 +1,231 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <errno.h> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/log/internal/test_actions.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/internal/test_matchers.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/log/log_sink.h" |
||||
#include "absl/log/scoped_mock_log.h" |
||||
#include "absl/strings/match.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/time/time.h" |
||||
|
||||
namespace { |
||||
#if GTEST_HAS_DEATH_TEST |
||||
using ::absl::log_internal::DeathTestExpectedLogging; |
||||
using ::absl::log_internal::DeathTestUnexpectedLogging; |
||||
using ::absl::log_internal::DeathTestValidateExpectations; |
||||
using ::absl::log_internal::DiedOfQFatal; |
||||
#endif |
||||
using ::absl::log_internal::LogSeverity; |
||||
using ::absl::log_internal::Prefix; |
||||
using ::absl::log_internal::SourceBasename; |
||||
using ::absl::log_internal::SourceFilename; |
||||
using ::absl::log_internal::SourceLine; |
||||
using ::absl::log_internal::Stacktrace; |
||||
using ::absl::log_internal::TextMessage; |
||||
using ::absl::log_internal::TextMessageWithPrefix; |
||||
using ::absl::log_internal::TextMessageWithPrefixAndNewline; |
||||
using ::absl::log_internal::TextPrefix; |
||||
using ::absl::log_internal::ThreadID; |
||||
using ::absl::log_internal::Timestamp; |
||||
using ::absl::log_internal::Verbosity; |
||||
|
||||
using ::testing::AllOf; |
||||
using ::testing::AnyNumber; |
||||
using ::testing::AnyOf; |
||||
using ::testing::Eq; |
||||
using ::testing::IsEmpty; |
||||
using ::testing::IsFalse; |
||||
using ::testing::Truly; |
||||
|
||||
TEST(TailCallsModifiesTest, AtLocationFileLine) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf( |
||||
// The metadata should change:
|
||||
SourceFilename(Eq("/my/very/very/very_long_source_file.cc")), |
||||
SourceBasename(Eq("very_long_source_file.cc")), SourceLine(Eq(777)), |
||||
// The logged line should change too, even though the prefix must
|
||||
// grow to fit the new metadata.
|
||||
TextMessageWithPrefix(Truly([](absl::string_view msg) { |
||||
return absl::EndsWith(msg, |
||||
" very_long_source_file.cc:777] hello world"); |
||||
}))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).AtLocation("/my/very/very/very_long_source_file.cc", 777) |
||||
<< "hello world"; |
||||
} |
||||
|
||||
TEST(TailCallsModifiesTest, NoPrefix) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(AllOf(Prefix(IsFalse()), TextPrefix(IsEmpty()), |
||||
TextMessageWithPrefix(Eq("hello world"))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).NoPrefix() << "hello world"; |
||||
} |
||||
|
||||
TEST(TailCallsModifiesTest, NoPrefixNoMessageNoShirtNoShoesNoService) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, |
||||
Send(AllOf(Prefix(IsFalse()), TextPrefix(IsEmpty()), |
||||
TextMessageWithPrefix(IsEmpty()), |
||||
TextMessageWithPrefixAndNewline(Eq("\n"))))); |
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).NoPrefix(); |
||||
} |
||||
|
||||
TEST(TailCallsModifiesTest, WithVerbosity) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(Verbosity(Eq(2)))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).WithVerbosity(2) << "hello world"; |
||||
} |
||||
|
||||
TEST(TailCallsModifiesTest, WithVerbosityNoVerbosity) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, |
||||
Send(Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).WithVerbosity(2).WithVerbosity(absl::LogEntry::kNoVerbosityLevel) |
||||
<< "hello world"; |
||||
} |
||||
|
||||
TEST(TailCallsModifiesTest, WithTimestamp) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(Timestamp(Eq(absl::UnixEpoch())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).WithTimestamp(absl::UnixEpoch()) << "hello world"; |
||||
} |
||||
|
||||
TEST(TailCallsModifiesTest, WithThreadID) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(AllOf(ThreadID(Eq(1234))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).WithThreadID(1234) << "hello world"; |
||||
} |
||||
|
||||
TEST(TailCallsModifiesTest, WithMetadataFrom) { |
||||
class ForwardingLogSink : public absl::LogSink { |
||||
public: |
||||
void Send(const absl::LogEntry &entry) override { |
||||
LOG(LEVEL(entry.log_severity())).WithMetadataFrom(entry) |
||||
<< "forwarded: " << entry.text_message(); |
||||
} |
||||
} forwarding_sink; |
||||
|
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq("fake/file")), SourceBasename(Eq("file")), |
||||
SourceLine(Eq(123)), Prefix(IsFalse()), |
||||
LogSeverity(Eq(absl::LogSeverity::kWarning)), |
||||
Timestamp(Eq(absl::UnixEpoch())), ThreadID(Eq(456)), |
||||
TextMessage(Eq("forwarded: hello world")), Verbosity(Eq(7)), |
||||
ENCODED_MESSAGE( |
||||
EqualsProto(R"pb(value { literal: "forwarded: " } |
||||
value { str: "hello world" })pb"))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(WARNING) |
||||
.AtLocation("fake/file", 123) |
||||
.NoPrefix() |
||||
.WithTimestamp(absl::UnixEpoch()) |
||||
.WithThreadID(456) |
||||
.WithVerbosity(7) |
||||
.ToSinkOnly(&forwarding_sink) |
||||
<< "hello world"; |
||||
} |
||||
|
||||
TEST(TailCallsModifiesTest, WithPerror) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(TextMessage(AnyOf(Eq("hello world: Bad file number [9]"), |
||||
Eq("hello world: Bad file descriptor [9]"), |
||||
Eq("hello world: Bad file descriptor [8]"))), |
||||
ENCODED_MESSAGE( |
||||
AnyOf(EqualsProto(R"pb(value { literal: "hello world" } |
||||
value { literal: ": " } |
||||
value { str: "Bad file number" } |
||||
value { literal: " [" } |
||||
value { str: "9" } |
||||
value { literal: "]" })pb"), |
||||
EqualsProto(R"pb(value { literal: "hello world" } |
||||
value { literal: ": " } |
||||
value { str: "Bad file descriptor" } |
||||
value { literal: " [" } |
||||
value { str: "9" } |
||||
value { literal: "]" })pb"), |
||||
EqualsProto(R"pb(value { literal: "hello world" } |
||||
value { literal: ": " } |
||||
value { str: "Bad file descriptor" } |
||||
value { literal: " [" } |
||||
value { str: "8" } |
||||
value { literal: "]" })pb")))))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
errno = EBADF; |
||||
LOG(INFO).WithPerror() << "hello world"; |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
TEST(ModifierMethodDeathTest, ToSinkOnlyQFatal) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink( |
||||
absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
auto do_log = [&test_sink] { |
||||
LOG(QFATAL).ToSinkOnly(&test_sink.UseAsLocalSink()) << "hello world"; |
||||
}; |
||||
|
||||
EXPECT_CALL(test_sink, Send) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
|
||||
EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq("hello world")), |
||||
Stacktrace(IsEmpty())))) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
do_log(); |
||||
}, |
||||
DiedOfQFatal, DeathTestValidateExpectations()); |
||||
} |
||||
#endif |
||||
|
||||
} // namespace
|
@ -0,0 +1,23 @@ |
||||
// Copyright 2022 The Abseil Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/log_sink.h" |
||||
|
||||
#include "absl/base/config.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
void LogSink::KeyFunction() const {} |
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,64 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/log_sink.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares the interface class `absl::LogSink`.
|
||||
|
||||
#ifndef ABSL_LOG_LOG_SINK_H_ |
||||
#define ABSL_LOG_LOG_SINK_H_ |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/log/log_entry.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
// absl::LogSink
|
||||
//
|
||||
// `absl::LogSink` is an interface which can be extended to intercept and
|
||||
// process particular messages (with `LOG.ToSinkOnly()` or
|
||||
// `LOG.ToSinkAlso()`) or all messages (if registered with
|
||||
// `absl::AddLogSink`). Implementations must be thread-safe, and should take
|
||||
// care not to take any locks that might be held by the `LOG` caller.
|
||||
class LogSink { |
||||
public: |
||||
virtual ~LogSink() = default; |
||||
|
||||
// LogSink::Send()
|
||||
//
|
||||
// `Send` is called synchronously during the log statement.
|
||||
//
|
||||
// It is safe to use `LOG` within an implementation of `Send`. `ToSinkOnly`
|
||||
// and `ToSinkAlso` are safe in general but can be used to create an infinite
|
||||
// loop if you try.
|
||||
virtual void Send(const absl::LogEntry& entry) = 0; |
||||
|
||||
// LogSink::Flush()
|
||||
//
|
||||
// Sinks that buffer messages should override this method to flush the buffer
|
||||
// and return.
|
||||
virtual void Flush() {} |
||||
|
||||
private: |
||||
// https://lld.llvm.org/missingkeyfunction.html#missing-key-function
|
||||
virtual void KeyFunction() const final; // NOLINT(readability/inheritance)
|
||||
}; |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_LOG_SINK_H_
|
@ -0,0 +1,61 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/log_sink_registry.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares APIs to operate on global set of registered log sinks.
|
||||
|
||||
#ifndef ABSL_LOG_LOG_SINK_REGISTRY_H_ |
||||
#define ABSL_LOG_LOG_SINK_REGISTRY_H_ |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/log/internal/log_sink_set.h" |
||||
#include "absl/log/log_sink.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
// AddLogSink(), RemoveLogSink()
|
||||
//
|
||||
// Adds or removes a `absl::LogSink` as a consumer of logging data.
|
||||
//
|
||||
// These functions are thread-safe.
|
||||
//
|
||||
// It is an error to attempt to add a sink that's already registered or to
|
||||
// attempt to remove one that isn't.
|
||||
//
|
||||
// To avoid unbounded recursion, dispatch to registered `absl::LogSink`s is
|
||||
// disabled per-thread while running the `Send()` method of registered
|
||||
// `absl::LogSink`s. Affected messages are dispatched to a special internal
|
||||
// sink instead which writes them to `stderr`.
|
||||
//
|
||||
// Do not call these inside `absl::LogSink::Send`.
|
||||
inline void AddLogSink(absl::LogSink* sink) { log_internal::AddLogSink(sink); } |
||||
inline void RemoveLogSink(absl::LogSink* sink) { |
||||
log_internal::RemoveLogSink(sink); |
||||
} |
||||
|
||||
// FlushLogSinks()
|
||||
//
|
||||
// Calls `absl::LogSink::Flush` on all registered sinks.
|
||||
//
|
||||
// Do not call this inside `absl::LogSink::Send`.
|
||||
inline void FlushLogSinks() { log_internal::FlushLogSinks(); } |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_LOG_SINK_REGISTRY_H_
|
@ -0,0 +1,419 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/log_sink.h" |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/internal/raw_logging.h" |
||||
#include "absl/log/internal/test_actions.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/internal/test_matchers.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/log/log_sink_registry.h" |
||||
#include "absl/log/scoped_mock_log.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace { |
||||
|
||||
using ::absl::log_internal::DeathTestExpectedLogging; |
||||
using ::absl::log_internal::DeathTestUnexpectedLogging; |
||||
using ::absl::log_internal::DeathTestValidateExpectations; |
||||
using ::absl::log_internal::DiedOfFatal; |
||||
using ::testing::_; |
||||
using ::testing::AnyNumber; |
||||
using ::testing::HasSubstr; |
||||
using ::testing::InSequence; |
||||
|
||||
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( |
||||
new absl::log_internal::LogTestEnvironment); |
||||
|
||||
// Tests for global log sink registration.
|
||||
// ---------------------------------------
|
||||
|
||||
TEST(LogSinkRegistryTest, AddLogSink) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
InSequence s; |
||||
EXPECT_CALL(test_sink, Log(_, _, "hello world")).Times(0); |
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, __FILE__, "Test : 42")); |
||||
EXPECT_CALL(test_sink, |
||||
Log(absl::LogSeverity::kWarning, __FILE__, "Danger ahead")); |
||||
EXPECT_CALL(test_sink, |
||||
Log(absl::LogSeverity::kError, __FILE__, "This is an error")); |
||||
|
||||
LOG(INFO) << "hello world"; |
||||
test_sink.StartCapturingLogs(); |
||||
|
||||
LOG(INFO) << "Test : " << 42; |
||||
LOG(WARNING) << "Danger" << ' ' << "ahead"; |
||||
LOG(ERROR) << "This is an error"; |
||||
|
||||
test_sink.StopCapturingLogs(); |
||||
LOG(INFO) << "Goodby world"; |
||||
} |
||||
|
||||
TEST(LogSinkRegistryTest, MultipleLogSinks) { |
||||
absl::ScopedMockLog test_sink1(absl::MockLogDefault::kDisallowUnexpected); |
||||
absl::ScopedMockLog test_sink2(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
::testing::InSequence seq; |
||||
EXPECT_CALL(test_sink1, Log(absl::LogSeverity::kInfo, _, "First")).Times(1); |
||||
EXPECT_CALL(test_sink2, Log(absl::LogSeverity::kInfo, _, "First")).Times(0); |
||||
|
||||
EXPECT_CALL(test_sink1, Log(absl::LogSeverity::kInfo, _, "Second")).Times(1); |
||||
EXPECT_CALL(test_sink2, Log(absl::LogSeverity::kInfo, _, "Second")).Times(1); |
||||
|
||||
EXPECT_CALL(test_sink1, Log(absl::LogSeverity::kInfo, _, "Third")).Times(0); |
||||
EXPECT_CALL(test_sink2, Log(absl::LogSeverity::kInfo, _, "Third")).Times(1); |
||||
|
||||
LOG(INFO) << "Before first"; |
||||
|
||||
test_sink1.StartCapturingLogs(); |
||||
LOG(INFO) << "First"; |
||||
|
||||
test_sink2.StartCapturingLogs(); |
||||
LOG(INFO) << "Second"; |
||||
|
||||
test_sink1.StopCapturingLogs(); |
||||
LOG(INFO) << "Third"; |
||||
|
||||
test_sink2.StopCapturingLogs(); |
||||
LOG(INFO) << "Fourth"; |
||||
} |
||||
|
||||
TEST(LogSinkRegistrationDeathTest, DuplicateSinkRegistration) { |
||||
ASSERT_DEATH_IF_SUPPORTED( |
||||
{ |
||||
absl::ScopedMockLog sink; |
||||
sink.StartCapturingLogs(); |
||||
absl::AddLogSink(&sink.UseAsLocalSink()); |
||||
}, |
||||
HasSubstr("Duplicate log sinks")); |
||||
} |
||||
|
||||
TEST(LogSinkRegistrationDeathTest, MismatchSinkRemoval) { |
||||
ASSERT_DEATH_IF_SUPPORTED( |
||||
{ |
||||
absl::ScopedMockLog sink; |
||||
absl::RemoveLogSink(&sink.UseAsLocalSink()); |
||||
}, |
||||
HasSubstr("Mismatched log sink")); |
||||
} |
||||
|
||||
// Tests for log sink semantic.
|
||||
// ---------------------------------------
|
||||
|
||||
TEST(LogSinkTest, FlushSinks) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Flush()).Times(2); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
|
||||
absl::FlushLogSinks(); |
||||
absl::FlushLogSinks(); |
||||
} |
||||
|
||||
TEST(LogSinkDeathTest, DeathInSend) { |
||||
class FatalSendSink : public absl::LogSink { |
||||
public: |
||||
void Send(const absl::LogEntry&) override { LOG(FATAL) << "goodbye world"; } |
||||
}; |
||||
|
||||
FatalSendSink sink; |
||||
EXPECT_EXIT({ LOG(INFO).ToSinkAlso(&sink) << "hello world"; }, DiedOfFatal, |
||||
_); |
||||
} |
||||
|
||||
// Tests for explicit log sink redirection.
|
||||
// ---------------------------------------
|
||||
|
||||
TEST(LogSinkTest, ToSinkAlso) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
absl::ScopedMockLog another_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
EXPECT_CALL(test_sink, Log(_, _, "hello world")); |
||||
EXPECT_CALL(another_sink, Log(_, _, "hello world")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).ToSinkAlso(&another_sink.UseAsLocalSink()) << "hello world"; |
||||
} |
||||
|
||||
TEST(LogSinkTest, ToSinkOnly) { |
||||
absl::ScopedMockLog another_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
EXPECT_CALL(another_sink, Log(_, _, "hello world")); |
||||
LOG(INFO).ToSinkOnly(&another_sink.UseAsLocalSink()) << "hello world"; |
||||
} |
||||
|
||||
TEST(LogSinkTest, ToManySinks) { |
||||
absl::ScopedMockLog sink1(absl::MockLogDefault::kDisallowUnexpected); |
||||
absl::ScopedMockLog sink2(absl::MockLogDefault::kDisallowUnexpected); |
||||
absl::ScopedMockLog sink3(absl::MockLogDefault::kDisallowUnexpected); |
||||
absl::ScopedMockLog sink4(absl::MockLogDefault::kDisallowUnexpected); |
||||
absl::ScopedMockLog sink5(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(sink3, Log(_, _, "hello world")); |
||||
EXPECT_CALL(sink4, Log(_, _, "hello world")); |
||||
EXPECT_CALL(sink5, Log(_, _, "hello world")); |
||||
|
||||
LOG(INFO) |
||||
.ToSinkAlso(&sink1.UseAsLocalSink()) |
||||
.ToSinkAlso(&sink2.UseAsLocalSink()) |
||||
.ToSinkOnly(&sink3.UseAsLocalSink()) |
||||
.ToSinkAlso(&sink4.UseAsLocalSink()) |
||||
.ToSinkAlso(&sink5.UseAsLocalSink()) |
||||
<< "hello world"; |
||||
} |
||||
|
||||
class ReentrancyTest : public ::testing::Test { |
||||
protected: |
||||
ReentrancyTest() = default; |
||||
enum class LogMode : int { kNormal, kToSinkAlso, kToSinkOnly }; |
||||
|
||||
class ReentrantSendLogSink : public absl::LogSink { |
||||
public: |
||||
explicit ReentrantSendLogSink(absl::LogSeverity severity, |
||||
absl::LogSink* sink, LogMode mode) |
||||
: severity_(severity), sink_(sink), mode_(mode) {} |
||||
explicit ReentrantSendLogSink(absl::LogSeverity severity) |
||||
: ReentrantSendLogSink(severity, nullptr, LogMode::kNormal) {} |
||||
|
||||
void Send(const absl::LogEntry&) override { |
||||
switch (mode_) { |
||||
case LogMode::kNormal: |
||||
LOG(LEVEL(severity_)) << "The log is coming from *inside the sink*."; |
||||
break; |
||||
case LogMode::kToSinkAlso: |
||||
LOG(LEVEL(severity_)).ToSinkAlso(sink_) |
||||
<< "The log is coming from *inside the sink*."; |
||||
break; |
||||
case LogMode::kToSinkOnly: |
||||
LOG(LEVEL(severity_)).ToSinkOnly(sink_) |
||||
<< "The log is coming from *inside the sink*."; |
||||
break; |
||||
default: |
||||
ABSL_RAW_LOG(FATAL, "Invalid mode %d.\n", static_cast<int>(mode_)); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
absl::LogSeverity severity_; |
||||
absl::LogSink* sink_; |
||||
LogMode mode_; |
||||
}; |
||||
|
||||
static absl::string_view LogAndReturn(absl::LogSeverity severity, |
||||
absl::string_view to_log, |
||||
absl::string_view to_return) { |
||||
LOG(LEVEL(severity)) << to_log; |
||||
return to_return; |
||||
} |
||||
}; |
||||
|
||||
TEST_F(ReentrancyTest, LogFunctionThatLogs) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
InSequence seq; |
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "hello")); |
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "world")); |
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kWarning, _, "danger")); |
||||
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "here")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO) << LogAndReturn(absl::LogSeverity::kInfo, "hello", "world"); |
||||
LOG(INFO) << LogAndReturn(absl::LogSeverity::kWarning, "danger", "here"); |
||||
} |
||||
|
||||
TEST_F(ReentrancyTest, RegisteredLogSinkThatLogsInSend) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
ReentrantSendLogSink renentrant_sink(absl::LogSeverity::kInfo); |
||||
EXPECT_CALL(test_sink, Log(_, _, "hello world")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
absl::AddLogSink(&renentrant_sink); |
||||
LOG(INFO) << "hello world"; |
||||
absl::RemoveLogSink(&renentrant_sink); |
||||
} |
||||
|
||||
TEST_F(ReentrancyTest, AlsoLogSinkThatLogsInSend) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kInfo); |
||||
EXPECT_CALL(test_sink, Log(_, _, "hello world")); |
||||
EXPECT_CALL(test_sink, |
||||
Log(_, _, "The log is coming from *inside the sink*.")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).ToSinkAlso(&reentrant_sink) << "hello world"; |
||||
} |
||||
|
||||
TEST_F(ReentrancyTest, RegisteredAlsoLogSinkThatLogsInSend) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kInfo); |
||||
EXPECT_CALL(test_sink, Log(_, _, "hello world")); |
||||
// We only call into the test_log sink once with this message, since the
|
||||
// second time log statement is run we are in "ThreadIsLogging" mode and all
|
||||
// the log statements are redirected into stderr.
|
||||
EXPECT_CALL(test_sink, |
||||
Log(_, _, "The log is coming from *inside the sink*.")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
absl::AddLogSink(&reentrant_sink); |
||||
LOG(INFO).ToSinkAlso(&reentrant_sink) << "hello world"; |
||||
absl::RemoveLogSink(&reentrant_sink); |
||||
} |
||||
|
||||
TEST_F(ReentrancyTest, OnlyLogSinkThatLogsInSend) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kInfo); |
||||
EXPECT_CALL(test_sink, |
||||
Log(_, _, "The log is coming from *inside the sink*.")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).ToSinkOnly(&reentrant_sink) << "hello world"; |
||||
} |
||||
|
||||
TEST_F(ReentrancyTest, RegisteredOnlyLogSinkThatLogsInSend) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kInfo); |
||||
EXPECT_CALL(test_sink, |
||||
Log(_, _, "The log is coming from *inside the sink*.")); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
absl::AddLogSink(&reentrant_sink); |
||||
LOG(INFO).ToSinkOnly(&reentrant_sink) << "hello world"; |
||||
absl::RemoveLogSink(&reentrant_sink); |
||||
} |
||||
|
||||
using ReentrancyDeathTest = ReentrancyTest; |
||||
|
||||
TEST_F(ReentrancyDeathTest, LogFunctionThatLogsFatal) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink; |
||||
|
||||
EXPECT_CALL(test_sink, Log) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
EXPECT_CALL(test_sink, Log(_, _, "hello")) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO) << LogAndReturn(absl::LogSeverity::kFatal, "hello", "world"); |
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
|
||||
TEST_F(ReentrancyDeathTest, RegisteredLogSinkThatLogsFatalInSend) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink; |
||||
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal); |
||||
EXPECT_CALL(test_sink, Log) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
EXPECT_CALL(test_sink, Log(_, _, "hello world")) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
absl::AddLogSink(&reentrant_sink); |
||||
LOG(INFO) << "hello world"; |
||||
// No need to call RemoveLogSink - process is dead at this point.
|
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
|
||||
TEST_F(ReentrancyDeathTest, AlsoLogSinkThatLogsFatalInSend) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink; |
||||
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal); |
||||
|
||||
EXPECT_CALL(test_sink, Log) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
EXPECT_CALL(test_sink, Log(_, _, "hello world")) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
EXPECT_CALL(test_sink, |
||||
Log(_, _, "The log is coming from *inside the sink*.")) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).ToSinkAlso(&reentrant_sink) << "hello world"; |
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
|
||||
TEST_F(ReentrancyDeathTest, RegisteredAlsoLogSinkThatLogsFatalInSend) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink; |
||||
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal); |
||||
EXPECT_CALL(test_sink, Log) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
EXPECT_CALL(test_sink, Log(_, _, "hello world")) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
EXPECT_CALL(test_sink, |
||||
Log(_, _, "The log is coming from *inside the sink*.")) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
absl::AddLogSink(&reentrant_sink); |
||||
LOG(INFO).ToSinkAlso(&reentrant_sink) << "hello world"; |
||||
// No need to call RemoveLogSink - process is dead at this point.
|
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
|
||||
TEST_F(ReentrancyDeathTest, OnlyLogSinkThatLogsFatalInSend) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink; |
||||
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal); |
||||
EXPECT_CALL(test_sink, Log) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
EXPECT_CALL(test_sink, |
||||
Log(_, _, "The log is coming from *inside the sink*.")) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
LOG(INFO).ToSinkOnly(&reentrant_sink) << "hello world"; |
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
|
||||
TEST_F(ReentrancyDeathTest, RegisteredOnlyLogSinkThatLogsFatalInSend) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink; |
||||
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal); |
||||
EXPECT_CALL(test_sink, Log) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
EXPECT_CALL(test_sink, |
||||
Log(_, _, "The log is coming from *inside the sink*.")) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
absl::AddLogSink(&reentrant_sink); |
||||
LOG(INFO).ToSinkOnly(&reentrant_sink) << "hello world"; |
||||
// No need to call RemoveLogSink - process is dead at this point.
|
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
|
||||
} // namespace
|
@ -0,0 +1,176 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/log_streamer.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares the class `LogStreamer` and convenience functions to
|
||||
// construct LogStreamer objects with different associated log severity levels.
|
||||
|
||||
#ifndef ABSL_LOG_LOG_STREAMER_H_ |
||||
#define ABSL_LOG_LOG_STREAMER_H_ |
||||
|
||||
#include <ios> |
||||
#include <memory> |
||||
#include <ostream> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/internal/ostringstream.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
// LogStreamer
|
||||
//
|
||||
// Although you can stream into `LOG(INFO)`, you can't pass it into a function
|
||||
// that takes a `std::ostream` parameter. `LogStreamer::stream()` provides a
|
||||
// `std::ostream` that buffers everything that's streamed in. The buffer's
|
||||
// contents are logged as if by `LOG` when the `LogStreamer` is destroyed.
|
||||
// If nothing is streamed in, an empty message is logged. If the specified
|
||||
// severity is `absl::LogSeverity::kFatal`, the program will be terminated when
|
||||
// the `LogStreamer` is destroyed regardless of whether any data were streamed
|
||||
// in.
|
||||
//
|
||||
// Factory functions corresponding to the `absl::LogSeverity` enumerators
|
||||
// are provided for convenience; if the desired severity is variable, invoke the
|
||||
// constructor directly.
|
||||
//
|
||||
// LogStreamer is movable, but not copyable.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// ShaveYakAndWriteToStream(
|
||||
// yak, absl::LogInfoStreamer(__FILE__, __LINE__).stream());
|
||||
//
|
||||
// {
|
||||
// // This logs a single line containing data streamed by all three function
|
||||
// // calls.
|
||||
// absl::LogStreamer streamer(absl::LogSeverity::kInfo, __FILE__, __LINE__);
|
||||
// ShaveYakAndWriteToStream(yak1, streamer.stream());
|
||||
// streamer.stream() << " ";
|
||||
// ShaveYakAndWriteToStream(yak2, streamer.stream());
|
||||
// streamer.stream() << " ";
|
||||
// ShaveYakAndWriteToStreamPointer(yak3, &streamer.stream());
|
||||
// }
|
||||
class LogStreamer final { |
||||
public: |
||||
// LogStreamer::LogStreamer()
|
||||
//
|
||||
// Creates a LogStreamer with a given `severity` that will log a message
|
||||
// attributed to the given `file` and `line`.
|
||||
explicit LogStreamer(absl::LogSeverity severity, absl::string_view file, |
||||
int line) |
||||
: severity_(severity), |
||||
line_(line), |
||||
file_(file), |
||||
stream_( |
||||
absl::make_unique<absl::strings_internal::OStringStream>(&buf_)) { |
||||
// To match `LOG`'s defaults:
|
||||
stream_->setf(std::ios_base::showbase | std::ios_base::boolalpha); |
||||
} |
||||
|
||||
// A moved-from `absl::LogStreamer` does not `LOG` when destroyed,
|
||||
// and a program that streams into one has undefined behavior.
|
||||
LogStreamer(LogStreamer&& that) noexcept |
||||
: severity_(that.severity_), |
||||
line_(that.line_), |
||||
file_(std::move(that.file_)), |
||||
buf_(std::move(that.buf_)), |
||||
stream_(that.stream_ |
||||
? absl::make_unique<absl::strings_internal::OStringStream>( |
||||
&buf_) |
||||
: nullptr) { |
||||
that.stream_.reset(); |
||||
} |
||||
LogStreamer& operator=(LogStreamer&& that) { |
||||
LOG_IF(LEVEL(severity_), stream_).AtLocation(file_, line_) << buf_; |
||||
severity_ = that.severity_; |
||||
file_ = std::move(that.file_); |
||||
line_ = that.line_; |
||||
buf_ = std::move(that.buf_); |
||||
stream_ = |
||||
that.stream_ |
||||
? absl::make_unique<absl::strings_internal::OStringStream>(&buf_) |
||||
: nullptr; |
||||
that.stream_.reset(); |
||||
return *this; |
||||
} |
||||
|
||||
// LogStreamer::~LogStreamer()
|
||||
//
|
||||
// Logs this LogStreamer's buffered content as if by LOG.
|
||||
~LogStreamer() { |
||||
LOG_IF(LEVEL(severity_), stream_).AtLocation(file_, line_) << buf_; |
||||
} |
||||
|
||||
// LogStreamer::stream()
|
||||
//
|
||||
// Returns the `std::ostream` to use to write into this LogStreamer' internal
|
||||
// buffer.
|
||||
std::ostream& stream() { return *stream_; } |
||||
|
||||
private: |
||||
absl::LogSeverity severity_; |
||||
int line_; |
||||
std::string file_; |
||||
std::string buf_; |
||||
// TODO(durandal): de-pointerize this once we are off of our old mostly-C++11
|
||||
// libstdc++ (which lacks move constructors for std streams).
|
||||
// `stream_` is null in a moved-from `LogStreamer`; this is in fact how we
|
||||
// recognize one to avoid logging when it is destroyed or reassigned.
|
||||
std::unique_ptr<absl::strings_internal::OStringStream> stream_; |
||||
}; |
||||
|
||||
// LogInfoStreamer()
|
||||
//
|
||||
// Returns a LogStreamer that writes at level LogSeverity::kInfo.
|
||||
inline LogStreamer LogInfoStreamer(absl::string_view file, int line) { |
||||
return absl::LogStreamer(absl::LogSeverity::kInfo, file, line); |
||||
} |
||||
|
||||
// LogWarningStreamer()
|
||||
//
|
||||
// Returns a LogStreamer that writes at level LogSeverity::kWarning.
|
||||
inline LogStreamer LogWarningStreamer(absl::string_view file, int line) { |
||||
return absl::LogStreamer(absl::LogSeverity::kWarning, file, line); |
||||
} |
||||
|
||||
// LogErrorStreamer()
|
||||
//
|
||||
// Returns a LogStreamer that writes at level LogSeverity::kError.
|
||||
inline LogStreamer LogErrorStreamer(absl::string_view file, int line) { |
||||
return absl::LogStreamer(absl::LogSeverity::kError, file, line); |
||||
} |
||||
|
||||
// LogFatalStreamer()
|
||||
//
|
||||
// Returns a LogStreamer that writes at level LogSeverity::kFatal.
|
||||
//
|
||||
// The program will be terminated when this `LogStreamer` is destroyed,
|
||||
// regardless of whether any data were streamed in.
|
||||
inline LogStreamer LogFatalStreamer(absl::string_view file, int line) { |
||||
return absl::LogStreamer(absl::LogSeverity::kFatal, file, line); |
||||
} |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_LOG_STREAMER_H_
|
@ -0,0 +1,361 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/log_streamer.h" |
||||
|
||||
#include <iostream> |
||||
#include <utility> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/internal/sysinfo.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/internal/test_actions.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/internal/test_matchers.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/log/scoped_mock_log.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace { |
||||
using ::absl::log_internal::DeathTestExpectedLogging; |
||||
using ::absl::log_internal::DeathTestUnexpectedLogging; |
||||
using ::absl::log_internal::DeathTestValidateExpectations; |
||||
#if GTEST_HAS_DEATH_TEST |
||||
using ::absl::log_internal::DiedOfFatal; |
||||
#endif |
||||
using ::absl::log_internal::LogSeverity; |
||||
using ::absl::log_internal::Prefix; |
||||
using ::absl::log_internal::SourceFilename; |
||||
using ::absl::log_internal::SourceLine; |
||||
using ::absl::log_internal::Stacktrace; |
||||
using ::absl::log_internal::TextMessage; |
||||
using ::absl::log_internal::ThreadID; |
||||
using ::absl::log_internal::TimestampInMatchWindow; |
||||
using ::testing::AnyNumber; |
||||
using ::testing::Eq; |
||||
using ::testing::HasSubstr; |
||||
using ::testing::IsEmpty; |
||||
using ::testing::IsTrue; |
||||
|
||||
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( |
||||
new absl::log_internal::LogTestEnvironment); |
||||
|
||||
void WriteToStream(absl::string_view data, std::ostream* os) { |
||||
*os << "WriteToStream: " << data; |
||||
} |
||||
void WriteToStreamRef(absl::string_view data, std::ostream& os) { |
||||
os << "WriteToStreamRef: " << data; |
||||
} |
||||
|
||||
TEST(LogStreamerTest, LogInfoStreamer) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kInfo)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("WriteToStream: foo")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
str: "WriteToStream: foo" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
WriteToStream("foo", &absl::LogInfoStreamer("path/file.cc", 1234).stream()); |
||||
} |
||||
|
||||
TEST(LogStreamerTest, LogWarningStreamer) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kWarning)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("WriteToStream: foo")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
str: "WriteToStream: foo" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
WriteToStream("foo", |
||||
&absl::LogWarningStreamer("path/file.cc", 1234).stream()); |
||||
} |
||||
|
||||
TEST(LogStreamerTest, LogErrorStreamer) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kError)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("WriteToStream: foo")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
str: "WriteToStream: foo" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
WriteToStream("foo", &absl::LogErrorStreamer("path/file.cc", 1234).stream()); |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
TEST(LogStreamerDeathTest, LogFatalStreamer) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink; |
||||
|
||||
EXPECT_CALL(test_sink, Send) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf( |
||||
SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kFatal)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("WriteToStream: foo")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
str: "WriteToStream: foo" |
||||
})pb"))))) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
WriteToStream("foo", |
||||
&absl::LogFatalStreamer("path/file.cc", 1234).stream()); |
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
#endif |
||||
|
||||
TEST(LogStreamerTest, LogStreamer) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kError)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("WriteToStream: foo")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
str: "WriteToStream: foo" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
WriteToStream( |
||||
"foo", &absl::LogStreamer(absl::LogSeverity::kError, "path/file.cc", 1234) |
||||
.stream()); |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
TEST(LogStreamerDeathTest, LogStreamer) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink; |
||||
|
||||
EXPECT_CALL(test_sink, Send) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf( |
||||
SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kFatal)), |
||||
TimestampInMatchWindow(), |
||||
ThreadID(Eq(absl::base_internal::GetTID())), |
||||
TextMessage(Eq("WriteToStream: foo")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
str: "WriteToStream: foo" |
||||
})pb"))))) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
WriteToStream("foo", &absl::LogStreamer(absl::LogSeverity::kFatal, |
||||
"path/file.cc", 1234) |
||||
.stream()); |
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
#endif |
||||
|
||||
TEST(LogStreamerTest, PassedByReference) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
TextMessage(Eq("WriteToStreamRef: foo")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
str: "WriteToStreamRef: foo" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
WriteToStreamRef("foo", absl::LogInfoStreamer("path/file.cc", 1234).stream()); |
||||
} |
||||
|
||||
TEST(LogStreamerTest, StoredAsLocal) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
auto streamer = absl::LogInfoStreamer("path/file.cc", 1234); |
||||
WriteToStream("foo", &streamer.stream()); |
||||
streamer.stream() << " "; |
||||
WriteToStreamRef("bar", streamer.stream()); |
||||
|
||||
// The call should happen when `streamer` goes out of scope; if it
|
||||
// happened before this `EXPECT_CALL` the call would be unexpected and the
|
||||
// test would fail.
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
TextMessage(Eq("WriteToStream: foo WriteToStreamRef: bar")), |
||||
ENCODED_MESSAGE(EqualsProto( |
||||
R"pb(value { |
||||
str: "WriteToStream: foo WriteToStreamRef: bar" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
TEST(LogStreamerDeathTest, StoredAsLocal) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
// This is fatal when it goes out of scope, but not until then:
|
||||
auto streamer = absl::LogFatalStreamer("path/file.cc", 1234); |
||||
std::cerr << "I'm still alive" << std::endl; |
||||
WriteToStream("foo", &streamer.stream()); |
||||
}, |
||||
DiedOfFatal, HasSubstr("I'm still alive")); |
||||
} |
||||
#endif |
||||
|
||||
TEST(LogStreamerTest, LogsEmptyLine) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(test_sink, Send(AllOf(SourceFilename(Eq("path/file.cc")), |
||||
SourceLine(Eq(1234)), TextMessage(Eq("")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
str: "" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
absl::LogInfoStreamer("path/file.cc", 1234); |
||||
} |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
TEST(LogStreamerDeathTest, LogsEmptyLine) { |
||||
EXPECT_EXIT( |
||||
{ |
||||
absl::ScopedMockLog test_sink; |
||||
|
||||
EXPECT_CALL(test_sink, Log) |
||||
.Times(AnyNumber()) |
||||
.WillRepeatedly(DeathTestUnexpectedLogging()); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf( |
||||
SourceFilename(Eq("path/file.cc")), TextMessage(Eq("")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "" })pb"))))) |
||||
.WillOnce(DeathTestExpectedLogging()); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
// This is fatal even though it's never used:
|
||||
auto streamer = absl::LogFatalStreamer("path/file.cc", 1234); |
||||
}, |
||||
DiedOfFatal, DeathTestValidateExpectations()); |
||||
} |
||||
#endif |
||||
|
||||
TEST(LogStreamerTest, MoveConstruction) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf( |
||||
SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
LogSeverity(Eq(absl::LogSeverity::kInfo)), |
||||
TextMessage(Eq("hello world")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "hello world" })pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
auto streamer1 = absl::LogInfoStreamer("path/file.cc", 1234); |
||||
streamer1.stream() << "hello"; |
||||
absl::LogStreamer streamer2(std::move(streamer1)); |
||||
streamer2.stream() << " world"; |
||||
} |
||||
|
||||
TEST(LogStreamerTest, MoveAssignment) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)), |
||||
LogSeverity(Eq(absl::LogSeverity::kInfo)), |
||||
TextMessage(Eq("hello")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "hello" })pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
EXPECT_CALL( |
||||
test_sink, |
||||
Send(AllOf(SourceFilename(Eq("elsewhere/name.cc")), SourceLine(Eq(5678)), |
||||
LogSeverity(Eq(absl::LogSeverity::kWarning)), |
||||
TextMessage(Eq("world; goodbye")), |
||||
ENCODED_MESSAGE(EqualsProto(R"pb(value { |
||||
str: "world; goodbye" |
||||
})pb")), |
||||
Stacktrace(IsEmpty())))); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
auto streamer1 = absl::LogInfoStreamer("path/file.cc", 1234); |
||||
streamer1.stream() << "hello"; |
||||
auto streamer2 = absl::LogWarningStreamer("elsewhere/name.cc", 5678); |
||||
streamer2.stream() << "world"; |
||||
streamer1 = std::move(streamer2); |
||||
streamer1.stream() << "; goodbye"; |
||||
} |
||||
|
||||
TEST(LogStreamerTest, CorrectDefaultFlags) { |
||||
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
// The `boolalpha` and `showbase` flags should be set by default, to match
|
||||
// `LOG`.
|
||||
EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq("false0xdeadbeef"))))) |
||||
.Times(2); |
||||
|
||||
test_sink.StartCapturingLogs(); |
||||
absl::LogInfoStreamer("path/file.cc", 1234).stream() |
||||
<< false << std::hex << 0xdeadbeef; |
||||
LOG(INFO) << false << std::hex << 0xdeadbeef; |
||||
} |
||||
|
||||
} // namespace
|
@ -0,0 +1,86 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/scoped_mock_log.h" |
||||
|
||||
#include <atomic> |
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/internal/raw_logging.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/log/log_sink.h" |
||||
#include "absl/log/log_sink_registry.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
ScopedMockLog::ScopedMockLog(MockLogDefault default_exp) |
||||
: sink_(this), is_capturing_logs_(false) { |
||||
if (default_exp == MockLogDefault::kIgnoreUnexpected) { |
||||
// Ignore all calls to Log we did not set expectations for.
|
||||
EXPECT_CALL(*this, Log).Times(::testing::AnyNumber()); |
||||
} else { |
||||
// Disallow all calls to Log we did not set expectations for.
|
||||
EXPECT_CALL(*this, Log).Times(0); |
||||
} |
||||
// By default Send mock forwards to Log mock.
|
||||
EXPECT_CALL(*this, Send) |
||||
.Times(::testing::AnyNumber()) |
||||
.WillRepeatedly([this](const absl::LogEntry& entry) { |
||||
is_triggered_.store(true, std::memory_order_relaxed); |
||||
Log(entry.log_severity(), std::string(entry.source_filename()), |
||||
std::string(entry.text_message())); |
||||
}); |
||||
|
||||
// By default We ignore all Flush calls.
|
||||
EXPECT_CALL(*this, Flush).Times(::testing::AnyNumber()); |
||||
} |
||||
|
||||
ScopedMockLog::~ScopedMockLog() { |
||||
ABSL_RAW_CHECK(is_triggered_.load(std::memory_order_relaxed), |
||||
"Did you forget to call StartCapturingLogs()?"); |
||||
|
||||
if (is_capturing_logs_) StopCapturingLogs(); |
||||
} |
||||
|
||||
void ScopedMockLog::StartCapturingLogs() { |
||||
ABSL_RAW_CHECK(!is_capturing_logs_, |
||||
"StartCapturingLogs() can be called only when the " |
||||
"absl::ScopedMockLog object is not capturing logs."); |
||||
|
||||
is_capturing_logs_ = true; |
||||
is_triggered_.store(true, std::memory_order_relaxed); |
||||
absl::AddLogSink(&sink_); |
||||
} |
||||
|
||||
void ScopedMockLog::StopCapturingLogs() { |
||||
ABSL_RAW_CHECK(is_capturing_logs_, |
||||
"StopCapturingLogs() can be called only when the " |
||||
"absl::ScopedMockLog object is capturing logs."); |
||||
|
||||
is_capturing_logs_ = false; |
||||
absl::RemoveLogSink(&sink_); |
||||
} |
||||
|
||||
absl::LogSink& ScopedMockLog::UseAsLocalSink() { |
||||
is_triggered_.store(true, std::memory_order_relaxed); |
||||
return sink_; |
||||
} |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,194 @@ |
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: log/scoped_mock_log.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header declares `class absl::ScopedMockLog`, for use in testing.
|
||||
|
||||
#ifndef ABSL_LOG_SCOPED_MOCK_LOG_H_ |
||||
#define ABSL_LOG_SCOPED_MOCK_LOG_H_ |
||||
|
||||
#include <atomic> |
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "absl/base/config.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/log_entry.h" |
||||
#include "absl/log/log_sink.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
// MockLogDefault
|
||||
//
|
||||
// Controls how ScopedMockLog responds to unexpected calls by default.
|
||||
enum class MockLogDefault { kIgnoreUnexpected, kDisallowUnexpected }; |
||||
|
||||
// ScopedMockLog
|
||||
//
|
||||
// ScopedMockLog is a LogSink that intercepts LOG() messages issued during its
|
||||
// lifespan.
|
||||
//
|
||||
// Using this together with GoogleTest, it's easy to test how a piece of code
|
||||
// calls LOG(). The typical usage, noting the distinction between
|
||||
// "uninteresting" and "unexpected", looks like this:
|
||||
//
|
||||
// using ::testing::_;
|
||||
// using ::testing::AnyNumber;
|
||||
// using ::testing::EndsWith;
|
||||
// using ::testing::kDoNotCaptureLogsYet;
|
||||
// using ::testing::Lt;
|
||||
//
|
||||
// TEST(FooTest, LogsCorrectly) {
|
||||
// // Simple robust setup, ignores unexpected logs.
|
||||
// absl::ScopedMockLog log;
|
||||
//
|
||||
// // We expect the WARNING "Something bad!" exactly twice.
|
||||
// EXPECT_CALL(log, Log(absl::LogSeverity::kWarning, _, "Something bad!"))
|
||||
// .Times(2);
|
||||
//
|
||||
// // But we want no messages from foo.cc.
|
||||
// EXPECT_CALL(log, Log(_, EndsWith("/foo.cc"), _)).Times(0);
|
||||
//
|
||||
// log.StartCapturingLogs(); // Call this after done setting expectations.
|
||||
// Foo(); // Exercises the code under test.
|
||||
// }
|
||||
//
|
||||
// TEST(BarTest, LogsExactlyCorrectly) {
|
||||
// // Strict checking, fails for unexpected logs.
|
||||
// absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
|
||||
//
|
||||
// // ... but ignore low severity messages
|
||||
// EXPECT_CALL(log, Log(Lt(absl::LogSeverity::kWarning), _, _))
|
||||
// .Times(AnyNumber());
|
||||
//
|
||||
// // We expect the ERROR "Something bad!" exactly once.
|
||||
// EXPECT_CALL(log, Log(absl::LogSeverity::kError, EndsWith("/foo.cc"),
|
||||
// "Something bad!"))
|
||||
// .Times(1);
|
||||
//
|
||||
// log.StartCapturingLogs(); // Call this after done setting expectations.
|
||||
// Bar(); // Exercises the code under test.
|
||||
// }
|
||||
//
|
||||
// Note that in a multi-threaded environment, all LOG() messages from a single
|
||||
// thread will be handled in sequence, but that cannot be guaranteed for
|
||||
// messages from different threads. In fact, if the same or multiple
|
||||
// expectations are matched on two threads concurrently, their actions will be
|
||||
// executed concurrently as well and may interleave.
|
||||
class ScopedMockLog final { |
||||
public: |
||||
// ScopedMockLog::ScopedMockLog()
|
||||
//
|
||||
// Sets up the log and adds default expectations.
|
||||
explicit ScopedMockLog( |
||||
MockLogDefault default_exp = MockLogDefault::kIgnoreUnexpected); |
||||
ScopedMockLog(const ScopedMockLog&) = delete; |
||||
ScopedMockLog& operator=(const ScopedMockLog&) = delete; |
||||
|
||||
// ScopedMockLog::~ScopedMockLog()
|
||||
//
|
||||
// Stops intercepting logs and destroys this ScopedMockLog.
|
||||
~ScopedMockLog(); |
||||
|
||||
// ScopedMockLog::StartCapturingLogs()
|
||||
//
|
||||
// Starts log capturing if the object isn't already doing so. Otherwise
|
||||
// crashes.
|
||||
//
|
||||
// Usually this method is called in the same thread that created this
|
||||
// ScopedMockLog. It is the user's responsibility to not call this method if
|
||||
// another thread may be calling it or StopCapturingLogs() at the same time.
|
||||
// It is undefined behavior to add expectations while capturing logs is
|
||||
// enabled.
|
||||
void StartCapturingLogs(); |
||||
|
||||
// ScopedMockLog::StopCapturingLogs()
|
||||
//
|
||||
// Stops log capturing if the object is capturing logs. Otherwise crashes.
|
||||
//
|
||||
// Usually this method is called in the same thread that created this object.
|
||||
// It is the user's responsibility to not call this method if another thread
|
||||
// may be calling it or StartCapturingLogs() at the same time.
|
||||
//
|
||||
// It is UB to add expectations, while capturing logs is enabled.
|
||||
void StopCapturingLogs(); |
||||
|
||||
// ScopedMockLog::UseAsLocalSink()
|
||||
//
|
||||
// Each `ScopedMockLog` is implemented with an `absl::LogSink`; this method
|
||||
// returns a reference to that sink (e.g. for use with
|
||||
// `LOG(...).ToSinkOnly()`) and marks the `ScopedMockLog` as having been used
|
||||
// even if `StartCapturingLogs` is never called.
|
||||
absl::LogSink& UseAsLocalSink(); |
||||
|
||||
// Implements the mock method:
|
||||
//
|
||||
// void Log(LogSeverity severity, absl::string_view file_path,
|
||||
// absl::string_view message);
|
||||
//
|
||||
// The second argument to Log() is the full path of the source file in
|
||||
// which the LOG() was issued.
|
||||
//
|
||||
// This is a shorthand form, which should be used by most users. Use the
|
||||
// `Send` mock only if you want to add expectations for other log message
|
||||
// attributes.
|
||||
MOCK_METHOD(void, Log, |
||||
(absl::LogSeverity severity, const std::string& file_path, |
||||
const std::string& message)); |
||||
|
||||
// Implements the mock method:
|
||||
//
|
||||
// void Send(const absl::LogEntry& entry);
|
||||
//
|
||||
// This is the most generic form of mock that can be specified. Use this mock
|
||||
// only if you want to add expectations for log message attributes different
|
||||
// from the log message text, log message path and log message severity.
|
||||
//
|
||||
// If no expectations are specified for this mock, the default action is to
|
||||
// forward the call to the `Log` mock.
|
||||
MOCK_METHOD(void, Send, (const absl::LogEntry&)); |
||||
|
||||
// Implements the mock method:
|
||||
//
|
||||
// void Flush();
|
||||
//
|
||||
// Use this mock only if you want to add expectations for log flush calls.
|
||||
MOCK_METHOD(void, Flush, ()); |
||||
|
||||
private: |
||||
class ForwardingSink final : public absl::LogSink { |
||||
public: |
||||
explicit ForwardingSink(ScopedMockLog* sml) : sml_(sml) {} |
||||
ForwardingSink(const ForwardingSink&) = delete; |
||||
ForwardingSink& operator=(const ForwardingSink&) = delete; |
||||
void Send(const absl::LogEntry& entry) override { sml_->Send(entry); } |
||||
void Flush() override { sml_->Flush(); } |
||||
|
||||
private: |
||||
ScopedMockLog* sml_; |
||||
}; |
||||
|
||||
ForwardingSink sink_; |
||||
bool is_capturing_logs_; |
||||
std::atomic<bool> is_triggered_; |
||||
}; |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_LOG_SCOPED_MOCK_LOG_H_
|
@ -0,0 +1,290 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/log/scoped_mock_log.h" |
||||
|
||||
#include <memory> |
||||
#include <thread> // NOLINT(build/c++11) |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest-spi.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/log_severity.h" |
||||
#include "absl/log/globals.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/internal/test_matchers.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/memory/memory.h" |
||||
#include "absl/strings/match.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/synchronization/barrier.h" |
||||
#include "absl/synchronization/notification.h" |
||||
|
||||
namespace { |
||||
|
||||
using ::testing::_; |
||||
using ::testing::AnyNumber; |
||||
using ::testing::Eq; |
||||
using ::testing::HasSubstr; |
||||
using ::testing::InSequence; |
||||
using ::testing::Lt; |
||||
using ::testing::Truly; |
||||
using absl::log_internal::SourceBasename; |
||||
using absl::log_internal::SourceFilename; |
||||
using absl::log_internal::SourceLine; |
||||
using absl::log_internal::TextMessageWithPrefix; |
||||
using absl::log_internal::ThreadID; |
||||
|
||||
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( |
||||
new absl::log_internal::LogTestEnvironment); |
||||
|
||||
#if GTEST_HAS_DEATH_TEST |
||||
TEST(ScopedMockLogDeathTest, |
||||
StartCapturingLogsCannotBeCalledWhenAlreadyCapturing) { |
||||
EXPECT_DEATH( |
||||
{ |
||||
absl::ScopedMockLog log; |
||||
log.StartCapturingLogs(); |
||||
log.StartCapturingLogs(); |
||||
}, |
||||
"StartCapturingLogs"); |
||||
} |
||||
|
||||
TEST(ScopedMockLogDeathTest, StopCapturingLogsCannotBeCalledWhenNotCapturing) { |
||||
EXPECT_DEATH( |
||||
{ |
||||
absl::ScopedMockLog log; |
||||
log.StopCapturingLogs(); |
||||
}, |
||||
"StopCapturingLogs"); |
||||
} |
||||
#endif |
||||
|
||||
// Tests that ScopedMockLog intercepts LOG()s when it's alive.
|
||||
TEST(ScopedMockLogTest, LogMockCatchAndMatchStrictExpectations) { |
||||
absl::ScopedMockLog log; |
||||
|
||||
// The following expectations must match in the order they appear.
|
||||
InSequence s; |
||||
EXPECT_CALL(log, |
||||
Log(absl::LogSeverity::kWarning, HasSubstr(__FILE__), "Danger.")); |
||||
EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "Working...")).Times(2); |
||||
EXPECT_CALL(log, Log(absl::LogSeverity::kError, _, "Bad!!")); |
||||
|
||||
log.StartCapturingLogs(); |
||||
LOG(WARNING) << "Danger."; |
||||
LOG(INFO) << "Working..."; |
||||
LOG(INFO) << "Working..."; |
||||
LOG(ERROR) << "Bad!!"; |
||||
} |
||||
|
||||
TEST(ScopedMockLogTest, LogMockCatchAndMatchSendExpectations) { |
||||
absl::ScopedMockLog log; |
||||
|
||||
EXPECT_CALL( |
||||
log, |
||||
Send(AllOf(SourceFilename(Eq("/my/very/very/very_long_source_file.cc")), |
||||
SourceBasename(Eq("very_long_source_file.cc")), |
||||
SourceLine(Eq(777)), ThreadID(Eq(1234)), |
||||
TextMessageWithPrefix(Truly([](absl::string_view msg) { |
||||
return absl::EndsWith( |
||||
msg, " very_long_source_file.cc:777] Info message"); |
||||
}))))); |
||||
|
||||
log.StartCapturingLogs(); |
||||
LOG(INFO) |
||||
.AtLocation("/my/very/very/very_long_source_file.cc", 777) |
||||
.WithThreadID(1234) |
||||
<< "Info message"; |
||||
} |
||||
|
||||
TEST(ScopedMockLogTest, ScopedMockLogCanBeNice) { |
||||
absl::ScopedMockLog log; |
||||
|
||||
InSequence s; |
||||
EXPECT_CALL(log, |
||||
Log(absl::LogSeverity::kWarning, HasSubstr(__FILE__), "Danger.")); |
||||
EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "Working...")).Times(2); |
||||
EXPECT_CALL(log, Log(absl::LogSeverity::kError, _, "Bad!!")); |
||||
|
||||
log.StartCapturingLogs(); |
||||
|
||||
// Any number of these are OK.
|
||||
LOG(INFO) << "Info message."; |
||||
// Any number of these are OK.
|
||||
LOG(WARNING).AtLocation("SomeOtherFile.cc", 100) << "Danger "; |
||||
|
||||
LOG(WARNING) << "Danger."; |
||||
|
||||
// Any number of these are OK.
|
||||
LOG(INFO) << "Info message."; |
||||
// Any number of these are OK.
|
||||
LOG(WARNING).AtLocation("SomeOtherFile.cc", 100) << "Danger "; |
||||
|
||||
LOG(INFO) << "Working..."; |
||||
|
||||
// Any number of these are OK.
|
||||
LOG(INFO) << "Info message."; |
||||
// Any number of these are OK.
|
||||
LOG(WARNING).AtLocation("SomeOtherFile.cc", 100) << "Danger "; |
||||
|
||||
LOG(INFO) << "Working..."; |
||||
|
||||
// Any number of these are OK.
|
||||
LOG(INFO) << "Info message."; |
||||
// Any number of these are OK.
|
||||
LOG(WARNING).AtLocation("SomeOtherFile.cc", 100) << "Danger "; |
||||
|
||||
LOG(ERROR) << "Bad!!"; |
||||
|
||||
// Any number of these are OK.
|
||||
LOG(INFO) << "Info message."; |
||||
// Any number of these are OK.
|
||||
LOG(WARNING).AtLocation("SomeOtherFile.cc", 100) << "Danger "; |
||||
} |
||||
|
||||
// Tests that ScopedMockLog generates a test failure if a message is logged
|
||||
// that is not expected (here, that means ERROR or FATAL).
|
||||
TEST(ScopedMockLogTest, RejectsUnexpectedLogs) { |
||||
EXPECT_NONFATAL_FAILURE( |
||||
{ |
||||
absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); |
||||
// Any INFO and WARNING messages are permitted.
|
||||
EXPECT_CALL(log, Log(Lt(absl::LogSeverity::kError), _, _)) |
||||
.Times(AnyNumber()); |
||||
log.StartCapturingLogs(); |
||||
LOG(INFO) << "Ignored"; |
||||
LOG(WARNING) << "Ignored"; |
||||
LOG(ERROR) << "Should not be ignored"; |
||||
}, |
||||
"Should not be ignored"); |
||||
} |
||||
|
||||
TEST(ScopedMockLogTest, CapturesLogsAfterStartCapturingLogs) { |
||||
absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfinity); |
||||
absl::ScopedMockLog log; |
||||
|
||||
// The ScopedMockLog object shouldn't see these LOGs, as it hasn't
|
||||
// started capturing LOGs yet.
|
||||
LOG(INFO) << "Ignored info"; |
||||
LOG(WARNING) << "Ignored warning"; |
||||
LOG(ERROR) << "Ignored error"; |
||||
|
||||
EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "Expected info")); |
||||
log.StartCapturingLogs(); |
||||
|
||||
// Only this LOG will be seen by the ScopedMockLog.
|
||||
LOG(INFO) << "Expected info"; |
||||
} |
||||
|
||||
TEST(ScopedMockLogTest, DoesNotCaptureLogsAfterStopCapturingLogs) { |
||||
absl::ScopedMockLog log; |
||||
EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "Expected info")); |
||||
|
||||
log.StartCapturingLogs(); |
||||
|
||||
// This LOG should be seen by the ScopedMockLog.
|
||||
LOG(INFO) << "Expected info"; |
||||
|
||||
log.StopCapturingLogs(); |
||||
|
||||
// The ScopedMockLog object shouldn't see these LOGs, as it has
|
||||
// stopped capturing LOGs.
|
||||
LOG(INFO) << "Ignored info"; |
||||
LOG(WARNING) << "Ignored warning"; |
||||
LOG(ERROR) << "Ignored error"; |
||||
} |
||||
|
||||
// Tests that all messages are intercepted regardless of issuing thread. The
|
||||
// purpose of this test is NOT to exercise thread-safety.
|
||||
TEST(ScopedMockLogTest, LogFromMultipleThreads) { |
||||
absl::ScopedMockLog log; |
||||
|
||||
// We don't establish an order to expectations here, since the threads may
|
||||
// execute their log statements in different order.
|
||||
EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, __FILE__, "Thread 1")); |
||||
EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, __FILE__, "Thread 2")); |
||||
|
||||
log.StartCapturingLogs(); |
||||
|
||||
absl::Barrier barrier(2); |
||||
std::thread thread1([&barrier]() { |
||||
barrier.Block(); |
||||
LOG(INFO) << "Thread 1"; |
||||
}); |
||||
std::thread thread2([&barrier]() { |
||||
barrier.Block(); |
||||
LOG(INFO) << "Thread 2"; |
||||
}); |
||||
|
||||
thread1.join(); |
||||
thread2.join(); |
||||
} |
||||
|
||||
// Tests that no sequence will be imposed on two LOG message expectations from
|
||||
// different threads. This test would actually deadlock if replaced to two LOG
|
||||
// statements from the same thread.
|
||||
TEST(ScopedMockLogTest, NoSequenceWithMultipleThreads) { |
||||
absl::ScopedMockLog log; |
||||
|
||||
absl::Barrier barrier(2); |
||||
EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, _)) |
||||
.Times(2) |
||||
.WillRepeatedly([&barrier]() { barrier.Block(); }); |
||||
|
||||
log.StartCapturingLogs(); |
||||
|
||||
std::thread thread1([]() { LOG(INFO) << "Thread 1"; }); |
||||
std::thread thread2([]() { LOG(INFO) << "Thread 2"; }); |
||||
|
||||
thread1.join(); |
||||
thread2.join(); |
||||
} |
||||
|
||||
TEST(ScopedMockLogTsanTest, |
||||
ScopedMockLogCanBeDeletedWhenAnotherThreadIsLogging) { |
||||
auto log = absl::make_unique<absl::ScopedMockLog>(); |
||||
EXPECT_CALL(*log, Log(absl::LogSeverity::kInfo, __FILE__, "Thread log")) |
||||
.Times(AnyNumber()); |
||||
|
||||
log->StartCapturingLogs(); |
||||
|
||||
absl::Notification logging_started; |
||||
|
||||
std::thread thread([&logging_started]() { |
||||
for (int i = 0; i < 100; ++i) { |
||||
if (i == 50) logging_started.Notify(); |
||||
LOG(INFO) << "Thread log"; |
||||
} |
||||
}); |
||||
|
||||
logging_started.WaitForNotification(); |
||||
log.reset(); |
||||
thread.join(); |
||||
} |
||||
|
||||
TEST(ScopedMockLogTest, AsLocalSink) { |
||||
absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); |
||||
|
||||
EXPECT_CALL(log, Log(_, _, "two")); |
||||
EXPECT_CALL(log, Log(_, _, "three")); |
||||
|
||||
LOG(INFO) << "one"; |
||||
LOG(INFO).ToSinkOnly(&log.UseAsLocalSink()) << "two"; |
||||
LOG(INFO).ToSinkAlso(&log.UseAsLocalSink()) << "three"; |
||||
} |
||||
|
||||
} // namespace
|
@ -0,0 +1,339 @@ |
||||
//
|
||||
// Copyright 2022 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Tests for stripping of literal strings.
|
||||
// ---------------------------------------
|
||||
//
|
||||
// When a `LOG` statement can be trivially proved at compile time to never fire,
|
||||
// e.g. due to `ABSL_MIN_LOG_LEVEL`, `NDEBUG`, or some explicit condition, data
|
||||
// streamed in can be dropped from the compiled program completely if they are
|
||||
// not used elsewhere. This most commonly affects string literals, which users
|
||||
// often want to strip to reduce binary size and/or redact information about
|
||||
// their program's internals (e.g. in a release build).
|
||||
//
|
||||
// These tests log strings and then validate whether they appear in the compiled
|
||||
// binary. This is done by opening the file corresponding to the running test
|
||||
// and running a simple string search on its contents. The strings to be logged
|
||||
// and searched for must be unique, and we must take care not to emit them into
|
||||
// the binary in any other place, e.g. when searching for them. The latter is
|
||||
// accomplished by computing them using base64; the source string appears in the
|
||||
// binary but the target string is computed at runtime.
|
||||
|
||||
#include <stdio.h> |
||||
|
||||
#if defined(__MACH__) |
||||
#include <mach-o/dyld.h> |
||||
#elif defined(_WIN32) |
||||
#include <Windows.h> |
||||
#include <tchar.h> |
||||
#endif |
||||
|
||||
#include <algorithm> |
||||
#include <functional> |
||||
#include <memory> |
||||
#include <ostream> |
||||
#include <string> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/internal/strerror.h" |
||||
#include "absl/flags/internal/program_name.h" |
||||
#include "absl/log/check.h" |
||||
#include "absl/log/internal/test_helpers.h" |
||||
#include "absl/log/log.h" |
||||
#include "absl/strings/escaping.h" |
||||
#include "absl/strings/str_format.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace { |
||||
using ::testing::_; |
||||
using ::testing::Eq; |
||||
using ::testing::NotNull; |
||||
|
||||
using absl::log_internal::kAbslMinLogLevel; |
||||
|
||||
std::string Base64UnescapeOrDie(absl::string_view data) { |
||||
std::string decoded; |
||||
CHECK(absl::Base64Unescape(data, &decoded)); |
||||
return decoded; |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A Googletest matcher which searches the running binary for a given string
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// This matcher is used to validate that literal strings streamed into
|
||||
// `LOG` statements that ought to be compiled out (e.g. `LOG_IF(INFO, false)`)
|
||||
// do not appear in the binary.
|
||||
//
|
||||
// Note that passing the string to be sought directly to `FileHasSubstr()` all
|
||||
// but forces its inclusion in the binary regardless of the logging library's
|
||||
// behavior. For example:
|
||||
//
|
||||
// LOG_IF(INFO, false) << "you're the man now dog";
|
||||
// // This will always pass:
|
||||
// // EXPECT_THAT(fp, FileHasSubstr("you're the man now dog"));
|
||||
// // So use this instead:
|
||||
// EXPECT_THAT(fp, FileHasSubstr(
|
||||
// Base64UnescapeOrDie("eW91J3JlIHRoZSBtYW4gbm93IGRvZw==")));
|
||||
|
||||
class FileHasSubstrMatcher final : public ::testing::MatcherInterface<FILE*> { |
||||
public: |
||||
explicit FileHasSubstrMatcher(absl::string_view needle) : needle_(needle) {} |
||||
|
||||
bool MatchAndExplain( |
||||
FILE* fp, ::testing::MatchResultListener* listener) const override { |
||||
std::string buf( |
||||
std::max<std::string::size_type>(needle_.size() * 2, 163840000), '\0'); |
||||
size_t buf_start_offset = 0; // The file offset of the byte at `buf[0]`.
|
||||
size_t buf_data_size = 0; // The number of bytes of `buf` which contain
|
||||
// data.
|
||||
|
||||
::fseek(fp, 0, SEEK_SET); |
||||
while (true) { |
||||
// Fill the buffer to capacity or EOF:
|
||||
while (buf_data_size < buf.size()) { |
||||
const size_t ret = fread(&buf[buf_data_size], sizeof(char), |
||||
buf.size() - buf_data_size, fp); |
||||
if (ret == 0) break; |
||||
buf_data_size += ret; |
||||
} |
||||
if (ferror(fp)) { |
||||
*listener << "error reading file"; |
||||
return false; |
||||
} |
||||
const absl::string_view haystack(&buf[0], buf_data_size); |
||||
const auto off = haystack.find(needle_); |
||||
if (off != haystack.npos) { |
||||
*listener << "string found at offset " << buf_start_offset + off; |
||||
return true; |
||||
} |
||||
if (feof(fp)) { |
||||
*listener << "string not found"; |
||||
return false; |
||||
} |
||||
// Copy the end of `buf` to the beginning so we catch matches that span
|
||||
// buffer boundaries. `buf` and `buf_data_size` are always large enough
|
||||
// that these ranges don't overlap.
|
||||
memcpy(&buf[0], &buf[buf_data_size - needle_.size()], needle_.size()); |
||||
buf_start_offset += buf_data_size - needle_.size(); |
||||
buf_data_size = needle_.size(); |
||||
} |
||||
} |
||||
void DescribeTo(std::ostream* os) const override { |
||||
*os << "contains the string \"" << needle_ << "\" (base64(\"" |
||||
<< Base64UnescapeOrDie(needle_) << "\"))"; |
||||
} |
||||
|
||||
void DescribeNegationTo(std::ostream* os) const override { |
||||
*os << "does not "; |
||||
DescribeTo(os); |
||||
} |
||||
|
||||
private: |
||||
std::string needle_; |
||||
}; |
||||
|
||||
class StrippingTest : public ::testing::Test { |
||||
protected: |
||||
void SetUp() override { |
||||
#ifndef NDEBUG |
||||
// Non-optimized builds don't necessarily eliminate dead code at all, so we
|
||||
// don't attempt to validate stripping against such builds.
|
||||
GTEST_SKIP() << "StrippingTests skipped since this build is not optimized"; |
||||
#elif defined(__EMSCRIPTEN__) |
||||
// These tests require a way to examine the running binary and look for
|
||||
// strings; there's no portable way to do that.
|
||||
GTEST_SKIP() |
||||
<< "StrippingTests skipped since this platform is not optimized"; |
||||
#endif |
||||
} |
||||
|
||||
// Opens this program's executable file. Returns `nullptr` and writes to
|
||||
// `stderr` on failure.
|
||||
std::unique_ptr<FILE, std::function<void(FILE*)>> OpenTestExecutable() { |
||||
#if defined(__linux__) |
||||
std::unique_ptr<FILE, std::function<void(FILE*)>> fp( |
||||
fopen("/proc/self/exe", "rb"), [](FILE* fp) { fclose(fp); }); |
||||
if (!fp) { |
||||
const std::string err = absl::base_internal::StrError(errno); |
||||
absl::FPrintF(stderr, "Failed to open /proc/self/exe: %s\n", err); |
||||
} |
||||
return fp; |
||||
#elif defined(__Fuchsia__) |
||||
// TODO(b/242579714): We need to restore the test coverage on this platform.
|
||||
std::unique_ptr<FILE, std::function<void(FILE*)>> fp( |
||||
fopen(absl::StrCat("/pkg/bin/", |
||||
absl::flags_internal::ShortProgramInvocationName()) |
||||
.c_str(), |
||||
"rb"), |
||||
[](FILE* fp) { fclose(fp); }); |
||||
if (!fp) { |
||||
const std::string err = absl::base_internal::StrError(errno); |
||||
absl::FPrintF(stderr, "Failed to open /pkg/bin/<binary name>: %s\n", err); |
||||
} |
||||
return fp; |
||||
#elif defined(__MACH__) |
||||
uint32_t size = 0; |
||||
int ret = _NSGetExecutablePath(nullptr, &size); |
||||
if (ret != -1) { |
||||
absl::FPrintF(stderr, |
||||
"Failed to get executable path: " |
||||
"_NSGetExecutablePath(nullptr) returned %d\n", |
||||
ret); |
||||
return nullptr; |
||||
} |
||||
std::string path(size, '\0'); |
||||
ret = _NSGetExecutablePath(&path[0], &size); |
||||
if (ret != 0) { |
||||
absl::FPrintF( |
||||
stderr, |
||||
"Failed to get executable path: _NSGetExecutablePath(buffer) " |
||||
"returned %d\n", |
||||
ret); |
||||
return nullptr; |
||||
} |
||||
std::unique_ptr<FILE, std::function<void(FILE*)>> fp( |
||||
fopen(path.c_str(), "rb"), [](FILE* fp) { fclose(fp); }); |
||||
if (!fp) { |
||||
const std::string err = absl::base_internal::StrError(errno); |
||||
absl::FPrintF(stderr, "Failed to open executable at %s: %s\n", path, err); |
||||
} |
||||
return fp; |
||||
#elif defined(_WIN32) |
||||
std::basic_string<TCHAR> path(4096, _T('\0')); |
||||
while (true) { |
||||
const uint32_t ret = ::GetModuleFileName(nullptr, &path[0], path.size()); |
||||
if (ret == 0) { |
||||
absl::FPrintF( |
||||
stderr, |
||||
"Failed to get executable path: GetModuleFileName(buffer) " |
||||
"returned 0\n"); |
||||
return nullptr; |
||||
} |
||||
if (ret < path.size()) break; |
||||
path.resize(path.size() * 2, _T('\0')); |
||||
} |
||||
std::unique_ptr<FILE, std::function<void(FILE*)>> fp( |
||||
_tfopen(path.c_str(), "rb"), [](FILE* fp) { fclose(fp); }); |
||||
if (!fp) absl::FPrintF(stderr, "Failed to open executable\n"); |
||||
return fp; |
||||
#else |
||||
absl::FPrintF(stderr, |
||||
"OpenTestExecutable() unimplemented on this platform\n"); |
||||
return nullptr; |
||||
#endif |
||||
} |
||||
|
||||
::testing::Matcher<FILE*> FileHasSubstr(absl::string_view needle) { |
||||
return MakeMatcher(new FileHasSubstrMatcher(needle)); |
||||
} |
||||
}; |
||||
|
||||
// This tests whether out methodology for testing stripping works on this
|
||||
// platform by looking for one string that definitely ought to be there and one
|
||||
// that definitely ought not to. If this fails, none of the `StrippingTest`s
|
||||
// are going to produce meaningful results.
|
||||
TEST_F(StrippingTest, Control) { |
||||
constexpr char kEncodedPositiveControl[] = |
||||
"U3RyaXBwaW5nVGVzdC5Qb3NpdGl2ZUNvbnRyb2w="; |
||||
const std::string encoded_negative_control = |
||||
absl::Base64Escape("StrippingTest.NegativeControl"); |
||||
|
||||
// Verify this mainly so we can encode other strings and know definitely they
|
||||
// won't encode to `kEncodedPositiveControl`.
|
||||
EXPECT_THAT(Base64UnescapeOrDie("U3RyaXBwaW5nVGVzdC5Qb3NpdGl2ZUNvbnRyb2w="), |
||||
Eq("StrippingTest.PositiveControl")); |
||||
|
||||
auto exe = OpenTestExecutable(); |
||||
ASSERT_THAT(exe, NotNull()); |
||||
EXPECT_THAT(exe.get(), FileHasSubstr(kEncodedPositiveControl)); |
||||
EXPECT_THAT(exe.get(), Not(FileHasSubstr(encoded_negative_control))); |
||||
} |
||||
|
||||
TEST_F(StrippingTest, Literal) { |
||||
// We need to load a copy of the needle string into memory (so we can search
|
||||
// for it) without leaving it lying around in plaintext in the executable file
|
||||
// as would happen if we used a literal. We might (or might not) leave it
|
||||
// lying around later; that's what the tests are for!
|
||||
const std::string needle = absl::Base64Escape("StrippingTest.Literal"); |
||||
LOG(INFO) << "U3RyaXBwaW5nVGVzdC5MaXRlcmFs"; |
||||
auto exe = OpenTestExecutable(); |
||||
ASSERT_THAT(exe, NotNull()); |
||||
if (absl::LogSeverity::kInfo >= kAbslMinLogLevel) { |
||||
EXPECT_THAT(exe.get(), FileHasSubstr(needle)); |
||||
} else { |
||||
EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle))); |
||||
} |
||||
} |
||||
|
||||
TEST_F(StrippingTest, LiteralInExpression) { |
||||
// We need to load a copy of the needle string into memory (so we can search
|
||||
// for it) without leaving it lying around in plaintext in the executable file
|
||||
// as would happen if we used a literal. We might (or might not) leave it
|
||||
// lying around later; that's what the tests are for!
|
||||
const std::string needle = |
||||
absl::Base64Escape("StrippingTest.LiteralInExpression"); |
||||
LOG(INFO) << absl::StrCat("secret: ", |
||||
"U3RyaXBwaW5nVGVzdC5MaXRlcmFsSW5FeHByZXNzaW9u"); |
||||
std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable(); |
||||
ASSERT_THAT(exe, NotNull()); |
||||
if (absl::LogSeverity::kInfo >= kAbslMinLogLevel) { |
||||
EXPECT_THAT(exe.get(), FileHasSubstr(needle)); |
||||
} else { |
||||
EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle))); |
||||
} |
||||
} |
||||
|
||||
TEST_F(StrippingTest, Fatal) { |
||||
// We need to load a copy of the needle string into memory (so we can search
|
||||
// for it) without leaving it lying around in plaintext in the executable file
|
||||
// as would happen if we used a literal. We might (or might not) leave it
|
||||
// lying around later; that's what the tests are for!
|
||||
const std::string needle = absl::Base64Escape("StrippingTest.Fatal"); |
||||
EXPECT_DEATH_IF_SUPPORTED(LOG(FATAL) << "U3RyaXBwaW5nVGVzdC5GYXRhbA==", ""); |
||||
std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable(); |
||||
ASSERT_THAT(exe, NotNull()); |
||||
if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) { |
||||
EXPECT_THAT(exe.get(), FileHasSubstr(needle)); |
||||
} else { |
||||
EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle))); |
||||
} |
||||
} |
||||
|
||||
TEST_F(StrippingTest, Level) { |
||||
const std::string needle = absl::Base64Escape("StrippingTest.Level"); |
||||
volatile auto severity = absl::LogSeverity::kWarning; |
||||
// Ensure that `severity` is not a compile-time constant to prove that
|
||||
// stripping works regardless:
|
||||
LOG(LEVEL(severity)) << "U3RyaXBwaW5nVGVzdC5MZXZlbA=="; |
||||
std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable(); |
||||
ASSERT_THAT(exe, NotNull()); |
||||
if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) { |
||||
// This can't be stripped at compile-time because it might evaluate to a
|
||||
// level that shouldn't be stripped.
|
||||
EXPECT_THAT(exe.get(), FileHasSubstr(needle)); |
||||
} else { |
||||
#if defined(_MSC_VER) || defined(__APPLE__) |
||||
// Dead code elimination misses this case.
|
||||
#else |
||||
// All levels should be stripped, so it doesn't matter what the severity
|
||||
// winds up being.
|
||||
EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle))); |
||||
#endif |
||||
} |
||||
} |
||||
|
||||
} // namespace
|
Loading…
Reference in new issue