Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions absl/log/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ cc_library(
visibility = ["//visibility:public"],
deps = [
":globals",
"//absl/base",
"//absl/base:config",
"//absl/log/internal:globals",
"//absl/time",
Expand Down Expand Up @@ -430,6 +431,20 @@ cc_test(
],
)

cc_test(
name = "initialize_test",
size = "small",
srcs = ["initialize_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":initialize",
"//absl/log/internal:globals",
"@googletest//:gtest",
"@googletest//:gtest_main",
],
)

cc_test(
name = "log_basic_test",
size = "small",
Expand Down
16 changes: 16 additions & 0 deletions absl/log/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::base
absl::config
absl::log_globals
absl::log_internal_globals
Expand Down Expand Up @@ -1002,6 +1003,21 @@ absl_cc_test(
GTest::gmock_main
)

absl_cc_test(
NAME
log_initialize_test
SRCS
"initialize_test.cc"
COPTS
${ABSL_TEST_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::log_initialize
absl::log_internal_globals
GTest::gmock_main
)

absl_cc_test(
NAME
log_format_test
Expand Down
12 changes: 11 additions & 1 deletion absl/log/initialize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "absl/log/initialize.h"

#include "absl/base/call_once.h"
#include "absl/base/config.h"
#include "absl/log/internal/globals.h"
#include "absl/time/time.h"
Expand All @@ -22,6 +23,9 @@ namespace absl {
ABSL_NAMESPACE_BEGIN

namespace {

absl::once_flag init_log_once;

void InitializeLogImpl(absl::TimeZone time_zone) {
// This comes first since it is used by RAW_LOG.
absl::log_internal::SetTimeZone(time_zone);
Expand All @@ -32,7 +36,13 @@ void InitializeLogImpl(absl::TimeZone time_zone) {
}
} // namespace

void InitializeLog() { InitializeLogImpl(absl::LocalTimeZone()); }
void InitializeLog() {
// Repeated calls are intentionally safe: only the first call has effect, and
// subsequent calls are no-ops. This lets independent libraries that each
// want to bring up the logging library coexist without coordination.
absl::call_once(init_log_once,
[]() { InitializeLogImpl(absl::LocalTimeZone()); });
}

ABSL_NAMESPACE_END
} // namespace absl
7 changes: 6 additions & 1 deletion absl/log/initialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ ABSL_NAMESPACE_BEGIN
// After initialization is finished, log messages are directed to all registered
// `LogSink`s.
//
// It is an error to call this function twice.
// Calling this function more than once is safe: only the first call has effect
// and subsequent calls are no-ops. This is intended to support situations
// where multiple independent libraries each want to bring up the logging
// library and have no way to coordinate among themselves. Concurrent calls
// from multiple threads are likewise safe; later callers block until the
// first call has completed.
//
// There is no corresponding function to shut down the logging library.
void InitializeLog();
Expand Down
58 changes: 58 additions & 0 deletions absl/log/initialize_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// Copyright 2026 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 <thread> // NOLINT(build/c++11)
#include <vector>

#include "gtest/gtest.h"
#include "absl/log/internal/globals.h"

namespace {

TEST(InitializeLogTest, FirstCallInitializes) {
absl::InitializeLog();
EXPECT_TRUE(absl::log_internal::IsInitialized());
}

TEST(InitializeLogTest, SecondCallIsNoOp) {
// The first call may have happened in a previous test in the same binary;
// either way, calling again must not fail and must leave the library in the
// initialized state.
absl::InitializeLog();
absl::InitializeLog();
EXPECT_TRUE(absl::log_internal::IsInitialized());
}

TEST(InitializeLogTest, ConcurrentCallsAreSafe) {
// Spawn several threads that race to initialize the logging library. Without
// the call_once guard inside InitializeLog(), this would either crash via
// ABSL_RAW_LOG(FATAL) inside SetTimeZone() or expose a data race on the
// initialization globals. With the guard, exactly one underlying
// initialization runs and the rest become no-ops.
constexpr int kNumThreads = 8;
std::vector<std::thread> threads;
threads.reserve(kNumThreads);
for (int i = 0; i < kNumThreads; ++i) {
threads.emplace_back([] { absl::InitializeLog(); });
}
for (auto& t : threads) {
t.join();
}
EXPECT_TRUE(absl::log_internal::IsInitialized());
}

} // namespace