Google Cloud C++ Client  1.32.1
C++ Client Library for Google Cloud Platform
log.cc
Go to the documentation of this file.
1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "google/cloud/log.h"
16 #include "google/cloud/internal/getenv.h"
17 #include "google/cloud/internal/log_impl.h"
18 #include "absl/strings/str_split.h"
19 #include "absl/time/time.h"
20 #include "absl/types/optional.h"
21 #include <array>
22 #include <thread>
23 
24 namespace google {
25 namespace cloud {
26 inline namespace GOOGLE_CLOUD_CPP_NS {
27 
28 static_assert(sizeof(Severity) == sizeof(int),
29  "Expected Severity to be represented as an int");
30 
31 static_assert(static_cast<int>(Severity::GCP_LS_LOWEST) <
32  static_cast<int>(Severity::GCP_LS_HIGHEST),
33  "Expect LOWEST severity to be smaller than HIGHEST severity");
34 
35 static_assert(static_cast<int>(Severity::GCP_LS_LOWEST_ENABLED) <=
36  static_cast<int>(Severity::GCP_LS_FATAL),
37  "Severity FATAL cannot be disable at compile time");
38 
39 namespace {
40 struct Timestamp {
41  explicit Timestamp(std::chrono::system_clock::time_point const& tp)
42  : t(absl::FromChrono(tp)) {}
43  absl::Time t;
44 };
45 
46 std::ostream& operator<<(std::ostream& os, Timestamp const& ts) {
47  auto constexpr kFormat = "%E4Y-%m-%dT%H:%M:%E9SZ";
48  return os << absl::FormatTime(kFormat, ts.t, absl::UTCTimeZone());
49 }
50 
51 auto constexpr kSeverityCount = static_cast<int>(Severity::GCP_LS_HIGHEST) + 1;
52 
53 std::array<char const*, kSeverityCount> constexpr kSeverityNames{
54  "TRACE", "DEBUG", "INFO", "NOTICE", "WARNING",
55  "ERROR", "CRITICAL", "ALERT", "FATAL",
56 };
57 
58 absl::optional<Severity> ParseSeverity(std::string const& name) {
59  int i = 0;
60  for (auto const* n : kSeverityNames) {
61  if (name == n) return static_cast<Severity>(i);
62  ++i;
63  }
64  return {};
65 }
66 
67 absl::optional<std::size_t> ParseSize(std::string const& str) {
68  std::size_t econv = -1;
69  auto const val = std::stol(str, &econv);
70  if (econv != str.size()) return {};
71  if (val <= 0) return {};
72  return static_cast<std::size_t>(val);
73 }
74 
75 } // namespace
76 
77 std::ostream& operator<<(std::ostream& os, Severity x) {
78  auto index = static_cast<int>(x);
79  return os << kSeverityNames[index];
80 }
81 
82 std::ostream& operator<<(std::ostream& os, LogRecord const& rhs) {
83  return os << Timestamp{rhs.timestamp} << " [" << rhs.severity << "]"
84  << " <" << rhs.thread_id << ">"
85  << " " << rhs.message << " (" << rhs.filename << ':' << rhs.lineno
86  << ')';
87 }
88 
90  : empty_(true),
91  minimum_severity_(static_cast<int>(Severity::GCP_LS_LOWEST_ENABLED)) {}
92 
94  static auto* const kInstance = [] {
95  auto* p = new LogSink;
96  p->SetDefaultBackend(internal::DefaultLogBackend());
97  return p;
98  }();
99  return *kInstance;
100 }
101 
102 // NOLINTNEXTLINE(google-runtime-int)
103 long LogSink::AddBackend(std::shared_ptr<LogBackend> backend) {
104  std::unique_lock<std::mutex> lk(mu_);
105  return AddBackendImpl(std::move(backend));
106 }
107 
108 // NOLINTNEXTLINE(google-runtime-int)
109 void LogSink::RemoveBackend(long id) {
110  std::unique_lock<std::mutex> lk(mu_);
111  RemoveBackendImpl(id);
112 }
113 
115  std::unique_lock<std::mutex> lk(mu_);
116  backends_.clear();
117  default_backend_id_ = 0;
118  empty_.store(backends_.empty());
119 }
120 
121 std::size_t LogSink::BackendCount() const {
122  std::unique_lock<std::mutex> lk(mu_);
123  return backends_.size();
124 }
125 
126 void LogSink::Log(LogRecord log_record) {
127  auto copy = CopyBackends();
128  if (copy.empty()) return;
129  // In general, we just give each backend a const-reference and the backends
130  // must make a copy if needed. But if there is only one backend we can give
131  // the backend an opportunity to optimize things by transferring ownership of
132  // the LogRecord to it.
133  if (copy.size() == 1) {
134  copy.begin()->second->ProcessWithOwnership(std::move(log_record));
135  return;
136  }
137  for (auto& kv : copy) {
138  kv.second->Process(log_record);
139  }
140 }
141 
142 void LogSink::Flush() {
143  auto copy = CopyBackends();
144  for (auto& kv : copy) kv.second->Flush();
145 }
146 
147 void LogSink::EnableStdClogImpl(Severity min_severity) {
148  std::unique_lock<std::mutex> lk(mu_);
149  if (default_backend_id_ != 0) return;
150  default_backend_id_ =
151  AddBackendImpl(std::make_shared<internal::StdClogBackend>(min_severity));
152 }
153 
154 void LogSink::DisableStdClogImpl() {
155  std::unique_lock<std::mutex> lk(mu_);
156  if (default_backend_id_ == 0) return;
157  // Note that the backend set by SetDefaultBackend() may be any LogBackend
158  // subclass, and so not necessarily a StdClogBackend. But, by default, it
159  // always is one, or a CircularBufferBackend that wraps a StdClogBackend.
160  RemoveBackendImpl(default_backend_id_);
161  default_backend_id_ = 0;
162 }
163 
164 void LogSink::SetDefaultBackend(std::shared_ptr<LogBackend> backend) {
165  std::unique_lock<std::mutex> lk(mu_);
166  if (default_backend_id_ != 0) return;
167  default_backend_id_ = AddBackendImpl(std::move(backend));
168 }
169 
170 LogSink::BackendId LogSink::AddBackendImpl(
171  std::shared_ptr<LogBackend> backend) {
172  auto const id = ++next_id_;
173  backends_.emplace(id, std::move(backend));
174  empty_.store(backends_.empty());
175  return id;
176 }
177 
178 void LogSink::RemoveBackendImpl(BackendId id) {
179  auto it = backends_.find(id);
180  if (backends_.end() == it) {
181  return;
182  }
183  backends_.erase(it);
184  empty_.store(backends_.empty());
185 }
186 
187 // Make a copy of the backends because calling user-defined functions while
188 // holding a lock is a bad idea: the application may change the backends while
189 // we are holding this lock, and soon deadlock occurs.
190 std::map<LogSink::BackendId, std::shared_ptr<LogBackend>>
191 LogSink::CopyBackends() {
192  std::lock_guard<std::mutex> lk(mu_);
193  return backends_;
194 }
195 
196 namespace internal {
197 
198 std::shared_ptr<LogBackend> DefaultLogBackend() {
199  auto constexpr kLogConfig = "GOOGLE_CLOUD_CPP_EXPERIMENTAL_LOG_CONFIG";
200  auto constexpr kEnableClog = "GOOGLE_CLOUD_CPP_ENABLE_CLOG";
201 
202  auto config = internal::GetEnv(kLogConfig).value_or("");
203  std::vector<std::string> fields = absl::StrSplit(config, ',');
204  if (!fields.empty()) {
205  auto min_severity = Severity::GCP_LS_LOWEST_ENABLED;
206  if (fields[0] == "lastN" && fields.size() == 3) {
207  auto size = ParseSize(fields[1]);
208  auto min_flush_severity = ParseSeverity(fields[2]);
209  if (size.has_value() && min_flush_severity.has_value()) {
210  return std::make_shared<CircularBufferBackend>(
211  *size, *min_flush_severity,
212  std::make_shared<StdClogBackend>(min_severity));
213  }
214  }
215  if (fields[0] == "clog" && fields.size() == 1) {
216  return std::make_shared<StdClogBackend>(min_severity);
217  }
218  }
219 
220  auto min_severity =
221  ParseSeverity(internal::GetEnv(kEnableClog).value_or("FATAL"));
222  return std::make_shared<StdClogBackend>(
223  min_severity.value_or(Severity::GCP_LS_LOWEST_ENABLED));
224 }
225 
226 } // namespace internal
227 } // namespace GOOGLE_CLOUD_CPP_NS
228 } // namespace cloud
229 } // namespace google