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