Google Cloud C++ Client 2.10.1
C++ Client Library for Google Cloud Platform
Loading...
Searching...
No Matches
log.h
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// https://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#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_LOG_H
16#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_LOG_H
17
18#include "google/cloud/version.h"
19#include "absl/types/optional.h"
20#include <atomic>
21#include <chrono>
22#include <cstdlib>
23#include <functional>
24#include <iostream>
25#include <map>
26#include <memory>
27#include <mutex>
28#include <sstream>
29#include <string>
30#include <thread>
31
32namespace google {
33namespace cloud {
35/// Concatenate two pre-processor tokens.
36#define GOOGLE_CLOUD_CPP_PP_CONCAT(a, b) a##b
37
38/**
39 * Create a unique, or most likely unique identifier.
40 *
41 * In GCP_LOG() we need an identifier for the logger, we can easily create a C++
42 * scope to make it work with any name, say "foo". However the user may have a
43 * variable called "foo" that they want to use in the scope (for example, to log
44 * the value of "foo". We try to make it unlikely that there will be collision
45 * by using an identifier that has a long prefix and depends on the line number.
46 */
47#define GOOGLE_CLOUD_CPP_LOGGER_IDENTIFIER
48 GOOGLE_CLOUD_CPP_PP_CONCAT(google_cloud_cpp_log_, __LINE__)
49
50/**
51 * The main entry point for the Google Cloud Platform C++ Library loggers.
52 *
53 * Typically this used only in tests, applications should use GCP_LOG(). This
54 * macro introduces a new scope (via the for-loop) with a single new identifier:
55 * GOOGLE_CLOUD_CPP_LOG_RECORD_IDENTIFIER
56 * We use a for-loop (as opposed to an if-statement, or a do-while), because
57 * then we can say:
58 *
59 * @code
60 * GCP_LOG(WARNING) << a << b << c;
61 * @endcode
62 *
63 * and the variables are scoped correctly. The for-loop also affords us an
64 * opportunity to check that the log level is enabled before entering the body
65 * of the loop, and if disabled we can minimize the cost of the log. For
66 * example, disabled logs do not create an iostream at all.
67 *
68 * Finally, the type of the GOOGLE_CLOUD_CPP_LOG_RECORD_IDENTIFIER changes if
69 * the log level is disabled at compile-time. For compile-time disabled log
70 * levels the compiler should be able to determine that the loop will not
71 * execute at all, and completely eliminate the code (we verified this using
72 * godbolt.org with modern GCC and Clang versions).
73 *
74 * Note the use of fully-qualified class names, including the initial `::`, this
75 * is to prevent problems if anybody uses this macro in a context where `Logger`
76 * is defined by the enclosing namespaces.
77 */
78#define GOOGLE_CLOUD_CPP_LOG_I(level, sink)
79 for (::google::cloud::Logger<::google::cloud::LogSink::CompileTimeEnabled(
80 ::google::cloud::Severity::level)>
82 ::google::cloud::Severity::level, __func__, __FILE__, __LINE__,
83 sink);
87
88// Note that we do not use `GOOGLE_CLOUD_CPP_PP_CONCAT` here: we actually want
89// to concatenate `GCP_LS` with the literal `level`. On some platforms, the
90// level names are used as macros (e.g., on macOS, `DEBUG` is often a macro with
91// value 1 for debug builds). If we were to use `GOOGLE_CLOUD_CPP_PP_CONCAT` and
92// `level` is a a macro, then we would get `GCP_LS_<macro_value>`, i.e.,
93// `GCP_LS_1` (incorrect) instead of `GCP_LS_DEBUG` (correct).
94/**
95 * Log a message with the Google Cloud Platform C++ Libraries logging framework.
96 */
97#define GCP_LOG(level)
98 GOOGLE_CLOUD_CPP_LOG_I(GCP_LS_##level, ::google::cloud::LogSink::Instance())
99
100#ifndef GOOGLE_CLOUD_CPP_LOGGING_MIN_SEVERITY_ENABLED
101#define GOOGLE_CLOUD_CPP_LOGGING_MIN_SEVERITY_ENABLED GCP_LS_DEBUG
102#endif // GOOGLE_CLOUD_CPP_LOGGING_MIN_SEVERITY_ENABLED
103
104/**
105 * Define the severity levels for Google Cloud Platform C++ Libraries logging.
106 *
107 * These are modelled after the severity level in syslog(1) and many derived
108 * tools.
109 *
110 * We force the enum to be represented as an `int` because we will store the
111 * values in an `std::atomic<>` and the implementations usually optimize
112 * `std::atomic<int>` but not `std::atomic<Foo>`
113 */
114enum class Severity : int {
115 /// Use this level for messages that indicate the code is entering and leaving
116 /// functions.
117 GCP_LS_TRACE, // NOLINT(readability-identifier-naming)
118 /// Use this level for debug messages that should not be present in
119 /// production.
121 /// Informational messages, such as normal progress.
122 GCP_LS_INFO, // NOLINT(readability-identifier-naming)
123 /// Informational messages, such as unusual, but expected conditions.
124 GCP_LS_NOTICE, // NOLINT(readability-identifier-naming)
125 /// An indication of problems, users may need to take action.
126 GCP_LS_WARNING, // NOLINT(readability-identifier-naming)
127 /// An error has been detected. Do not use for normal conditions, such as
128 /// remote servers disconnecting.
129 GCP_LS_ERROR, // NOLINT(readability-identifier-naming)
130 /// The system is in a critical state, such as running out of local resources.
131 GCP_LS_CRITICAL, // NOLINT(readability-identifier-naming)
132 /// The system is at risk of immediate failure.
133 GCP_LS_ALERT, // NOLINT(readability-identifier-naming)
134 /// The system is unusable. GCP_LOG(FATAL) will call std::abort().
135 GCP_LS_FATAL, // NOLINT(readability-identifier-naming)
136 /// The highest possible severity level.
137 GCP_LS_HIGHEST = GCP_LS_FATAL, // NOLINT(readability-identifier-naming)
138 /// The lowest possible severity level.
139 GCP_LS_LOWEST = GCP_LS_TRACE, // NOLINT(readability-identifier-naming)
140 /// The lowest level that is enabled at compile-time.
141 // NOLINTNEXTLINE(readability-identifier-naming)
143};
144
145/// Convert a human-readable representation to a Severity.
146absl::optional<Severity> ParseSeverity(std::string const& name);
147
148/// Streaming operator, writes a human-readable representation.
149std::ostream& operator<<(std::ostream& os, Severity x);
150
151/**
152 * Represents a single log message.
153 */
154struct LogRecord {
156 std::string function;
157 std::string filename;
158 int lineno;
159 std::thread::id thread_id;
160 std::chrono::system_clock::time_point timestamp;
161 std::string message;
162};
163
164/// Default formatting of a LogRecord.
165std::ostream& operator<<(std::ostream& os, LogRecord const& rhs);
166
167/**
168 * The logging backend interface.
169 */
170class LogBackend {
171 public:
172 virtual ~LogBackend() = default;
173
174 virtual void Process(LogRecord const& log_record) = 0;
175 virtual void ProcessWithOwnership(LogRecord log_record) = 0;
176 virtual void Flush() {}
177};
178
179/**
180 * A sink to receive log records.
181 */
182class LogSink {
183 public:
184 LogSink();
185
186 /// Return true if the severity is enabled at compile time.
187 static bool constexpr CompileTimeEnabled(Severity level) {
188 return level >= Severity::GCP_LS_LOWEST_ENABLED;
189 }
190
191 /**
192 * Return the singleton instance for this application.
193 */
194 static LogSink& Instance();
195
196 /**
197 * Return true if this object has no backends.
198 *
199 * We want to avoid synchronization overhead when checking if a log message is
200 * enabled. Most of the time, most messages will be disabled, so incurring the
201 * locking overhead on each message would be too expensive and would
202 * discourage developers from creating logs. Furthermore, missing a few
203 * messages while the change of state "propagates" to other threads does not
204 * affect the correctness of the program.
205 *
206 * Note that `memory_order_relaxed` does not provide a compiler barrier
207 * either, so in theory stores into the atomic could be reordered by the
208 * optimizer. We have no reason to worry about that because all the writes
209 * are done inside a critical section protected by a mutex. The compiler
210 * cannot (or should not) reorder operations around those.
211 */
212 bool empty() const { return empty_.load(std::memory_order_relaxed); }
213
214 /**
215 * Return true if @p severity is enabled.
216 *
217 * We want to avoid synchronization overhead when checking if a log message is
218 * enabled. Most of the time, most messages will be disabled, so incurring the
219 * locking overhead on each message would be too expensive and would
220 * discourage developers from creating logs. Furthermore, missing a few
221 * messages while the change of state "propagates" to other threads does not
222 * affect the correctness of the program.
223 *
224 * Note that `memory_order_relaxed` does not provide a compiler barrier
225 * either, so in theory stores into the atomic could be reordered by the
226 * optimizer. We have no reason to worry about that because all the writes
227 * are done inside a critical section protected by a mutex. The compiler
228 * cannot (or should not) reorder operations around those.
229 */
230 bool is_enabled(Severity severity) const {
231 auto minimum = minimum_severity_.load(std::memory_order_relaxed);
232 return static_cast<int>(severity) >= minimum;
233 }
234
235 void set_minimum_severity(Severity minimum) {
236 minimum_severity_.store(static_cast<int>(minimum));
237 }
238 Severity minimum_severity() const {
239 return static_cast<Severity>(minimum_severity_.load());
240 }
241
242 // A `long` is perfectly fine here, it is guaranteed to be at least 32-bits
243 // wide, it is unlikely that an application would need more than 2 billion
244 // logging backends during its lifetime. The style guide recommends using
245 // `std::int32_t` for such a use-case, unfortunately I (coryan) picked `long`
246 // before we had tooling to enforce this style guide rule.
247 using BackendId = long; // NOLINT(google-runtime-int)
248
249 BackendId AddBackend(std::shared_ptr<LogBackend> backend);
250 void RemoveBackend(BackendId id);
251 void ClearBackends();
252 std::size_t BackendCount() const;
253
254 void Log(LogRecord log_record);
255
256 /// Flush all the current backends
257 void Flush();
258
259 /**
260 * Enable `std::clog` on `LogSink::Instance()`.
261 *
262 * This is also enabled if the "GOOGLE_CLOUD_CPP_ENABLE_CLOG" environment
263 * variable is set.
264 */
265 static void EnableStdClog(
267 Instance().EnableStdClogImpl(min_severity);
268 }
269
270 /**
271 * Disable `std::clog` on `LogSink::Instance()`.
272 *
273 * Note that this will remove the default logging backend.
274 */
275 static void DisableStdClog() { Instance().DisableStdClogImpl(); }
276
277 private:
278 void EnableStdClogImpl(Severity min_severity);
279 void DisableStdClogImpl();
280 void SetDefaultBackend(std::shared_ptr<LogBackend> backend);
281 BackendId AddBackendImpl(std::shared_ptr<LogBackend> backend);
282 void RemoveBackendImpl(BackendId id);
283
284 std::map<BackendId, std::shared_ptr<LogBackend>> CopyBackends();
285
286 std::atomic<bool> empty_;
287 std::atomic<int> minimum_severity_;
288 std::mutex mutable mu_;
289 BackendId next_id_ = 0;
290 BackendId default_backend_id_ = 0;
291 std::map<BackendId, std::shared_ptr<LogBackend>> backends_;
292};
293
294/**
295 * Implements operator<< for all types, without any effect.
296 *
297 * It is desirable to disable at compile-time tracing, debugging, and other low
298 * severity messages. The Google Cloud Platform C++ Libraries logging adaptors
299 * return an object of this class when the particular log-line is disabled at
300 * compile-time.
301 */
302struct NullStream {
303 /// Generic do-nothing streaming operator
304 template <typename T>
305 NullStream& operator<<(T) {
306 return *this;
307 }
308};
309
310/**
311 * Define the class to capture a log message.
312 *
313 * @tparam CompileTimeEnabled this represents whether the severity has been
314 * disabled at compile-time. The class is specialized for `false` to minimize
315 * the run-time impact of (and, in practice, completely elide) disabled logs.
316 */
317template <bool CompileTimeEnabled>
318class Logger {
319 public:
320 Logger(Severity severity, char const* function, char const* filename,
321 int lineno, LogSink& sink)
322 : enabled_(!sink.empty() && sink.is_enabled(severity)),
323 severity_(severity),
324 function_(function),
325 filename_(filename),
326 lineno_(lineno) {}
327
328 ~Logger() {
329 if (severity_ >= Severity::GCP_LS_FATAL) std::abort();
330 }
331
332 bool enabled() const { return enabled_; }
333
334 /// Send the log record captured by this object to @p sink.
335 void LogTo(LogSink& sink) {
336 if (!stream_ || !enabled_) {
337 return;
338 }
339 enabled_ = false;
340 LogRecord record;
341 record.severity = severity_;
342 record.function = function_;
343 record.filename = filename_;
344 record.lineno = lineno_;
345 record.thread_id = std::this_thread::get_id();
346 record.timestamp = std::chrono::system_clock::now();
347 record.message = stream_->str();
348 sink.Log(std::move(record));
349 }
350
351 /// Return the iostream that captures the log message.
352 std::ostream& Stream() {
353 if (!stream_) stream_ = std::make_unique<std::ostringstream>();
354 return *stream_;
355 }
356
357 private:
358 bool enabled_;
359 Severity severity_;
360 char const* function_;
361 char const* filename_;
362 int lineno_;
363 std::unique_ptr<std::ostringstream> stream_;
364};
365
366/**
367 * Define the logger for a disabled log level.
368 */
369template <>
370class Logger<false> {
371 public:
372 Logger(Severity severity, char const*, char const*, int, LogSink&)
373 : severity_(severity) {}
374
375 ~Logger() {
376 if (severity_ >= Severity::GCP_LS_FATAL) std::abort();
377 }
378
379 ///@{
380 /**
381 * @name Trivial implementations that meet the generic Logger<bool> interface.
382 */
383 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
384 bool enabled() const { return false; }
385 void LogTo(LogSink&) {}
386 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
387 NullStream Stream() { return NullStream(); }
388 ///@}
389
390 private:
391 Severity severity_;
392};
393
394namespace internal {
395std::shared_ptr<LogBackend> DefaultLogBackend();
396} // namespace internal
398} // namespace cloud
399} // namespace google
400
401#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_LOG_H
The logging backend interface.
Definition: log.h:170
virtual void ProcessWithOwnership(LogRecord log_record)=0
virtual void Process(LogRecord const &log_record)=0
virtual void Flush()
Definition: log.h:176
virtual ~LogBackend()=default
A sink to receive log records.
Definition: log.h:182
static LogSink & Instance()
Return the singleton instance for this application.
void Log(LogRecord log_record)
static void DisableStdClog()
Disable std::clog on LogSink::Instance().
Definition: log.h:275
bool empty() const
Return true if this object has no backends.
Definition: log.h:212
BackendId AddBackend(std::shared_ptr< LogBackend > backend)
Severity minimum_severity() const
Definition: log.h:238
void set_minimum_severity(Severity minimum)
Definition: log.h:235
void RemoveBackend(BackendId id)
bool is_enabled(Severity severity) const
Return true if severity is enabled.
Definition: log.h:230
void Flush()
Flush all the current backends.
std::size_t BackendCount() const
static void EnableStdClog(Severity min_severity=Severity::GCP_LS_LOWEST_ENABLED)
Enable std::clog on LogSink::Instance().
Definition: log.h:265
static bool constexpr CompileTimeEnabled(Severity level)
Return true if the severity is enabled at compile time.
Definition: log.h:187
~Logger()
Definition: log.h:375
bool enabled() const
Definition: log.h:384
Logger(Severity severity, char const *, char const *, int, LogSink &)
Definition: log.h:372
NullStream Stream()
Definition: log.h:387
void LogTo(LogSink &)
Definition: log.h:385
Define the class to capture a log message.
Definition: log.h:318
std::ostream & Stream()
Return the iostream that captures the log message.
Definition: log.h:352
Logger(Severity severity, char const *function, char const *filename, int lineno, LogSink &sink)
Definition: log.h:320
~Logger()
Definition: log.h:328
bool enabled() const
Definition: log.h:332
void LogTo(LogSink &sink)
Send the log record captured by this object to sink.
Definition: log.h:335
#define GOOGLE_CLOUD_CPP_PP_CONCAT(a, b)
Definition: log.h:36
#define GOOGLE_CLOUD_CPP_LOGGER_IDENTIFIER
Definition: log.h:47
#define GOOGLE_CLOUD_CPP_LOGGING_MIN_SEVERITY_ENABLED
Definition: log.h:101
#define GOOGLE_CLOUD_CPP_LOG_I(level, sink)
Definition: log.h:78
Contains all the Google Cloud C++ Library APIs.
Definition: async_operation.h:23
absl::optional< Severity > ParseSeverity(std::string const &name)
Convert a human-readable representation to a Severity.
Severity
Define the severity levels for Google Cloud Platform C++ Libraries logging.
Definition: log.h:114
@ GCP_LS_TRACE
Use this level for messages that indicate the code is entering and leaving functions.
@ GCP_LS_FATAL
The system is unusable. GCP_LOG(FATAL) will call std::abort().
@ GCP_LS_LOWEST
The lowest possible severity level.
@ GCP_LS_ERROR
An error has been detected.
@ GCP_LS_INFO
Informational messages, such as normal progress.
@ GCP_LS_LOWEST_ENABLED
The lowest level that is enabled at compile-time.
@ GCP_LS_HIGHEST
The highest possible severity level.
@ GCP_LS_NOTICE
Informational messages, such as unusual, but expected conditions.
@ GCP_LS_CRITICAL
The system is in a critical state, such as running out of local resources.
@ GCP_LS_DEBUG
Use this level for debug messages that should not be present in production.
@ GCP_LS_ALERT
The system is at risk of immediate failure.
@ GCP_LS_WARNING
An indication of problems, users may need to take action.
Definition: async_operation.h:22
Represents a single log message.
Definition: log.h:154
int lineno
Definition: log.h:158
std::string filename
Definition: log.h:157
Severity severity
Definition: log.h:155
std::string function
Definition: log.h:156
std::chrono::system_clock::time_point timestamp
Definition: log.h:160
std::string message
Definition: log.h:161
std::thread::id thread_id
Definition: log.h:159
Implements operator<< for all types, without any effect.
Definition: log.h:302
#define GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
Definition: version.h:45
#define GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
Definition: version.h:43