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