1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2018 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 /*
19  *   A note on the thread safety of the logger API:
20  *
21  *   The API is thread safe unless the underlying logger object is changed
22  * during runtime. This means some methods can only be safely called if the
23  * caller guarantees no other threads exist and/or are calling the logging
24  * functions.
25  *
26  *   The caveat being we should not change the underlying logger object during
27  * run-time, the exception to this is during the initial memcached startup,
28  * where we are running in a single thread at the point we switch from console
29  * logging to file logging.
30  */
31 
32 #pragma once
33 
34 #include <logger/visibility.h>
35 #include <spdlog/fmt/ostr.h>
36 #include <spdlog/logger.h>
37 
38 #include <boost/optional/optional_fwd.hpp>
39 
40 #include <string>
41 
42 namespace cb {
43 namespace logger {
44 
45 struct Config;
46 
47 /**
48  * Initialize the logger.
49  *
50  * The default level for the created logger is set to INFO
51  *
52  * See note about thread safety at the top of the file
53  *
54  * @param logger_settings the configuration for the logger
55  * @return optional error message if something goes wrong
56  */
57 LOGGER_PUBLIC_API
58 boost::optional<std::string> initialize(const Config& logger_settings);
59 
60 /**
61  * Initialize the logger with the blackhole logger object
62  *
63  * This method is intended to be used by unit tests which
64  * don't need any output (but may call methods who tries
65  * to fetch the logger)
66  *
67  * See note about thread safety at the top of the file
68  *
69  * @throws std::bad_alloc
70  * @throws spdlog::spdlog_ex if an error occurs creating the logger
71  *                           (if it already exists for instance)
72  */
73 LOGGER_PUBLIC_API
74 void createBlackholeLogger();
75 
76 /**
77  * Initialize the logger with the logger which logs to the console
78  *
79  * See note about thread safety at the top of the file
80  *
81  * @throws std::bad_alloc
82  * @throws spdlog::spdlog_ex if an error occurs creating the logger
83  */
84 LOGGER_PUBLIC_API
85 void createConsoleLogger();
86 
87 /**
88  * Get the underlying logger object
89  *
90  * See note about thread safety at the top of the file.
91  *
92  * This will return null if a logger has not been
93  * initialized through one of the following:
94  *
95  * - initialize()
96  * - createBlackholeLogger()
97  * - createConsoleLogger()
98  */
99 LOGGER_PUBLIC_API
100 spdlog::logger* get();
101 
102 /**
103  * Reset the underlying logger object
104  *
105  * See note about thread safety at the top of the file
106  */
107 LOGGER_PUBLIC_API
108 void reset();
109 
110 /**
111  * Engines that create their own instances of an spdlogger should register
112  * the logger here to ensure that the verbosity of the logger is updated when
113  * memcached receives a request to update verbosity
114  *
115  * @param l spdlogger instance
116  */
117 LOGGER_PUBLIC_API
118 void registerSpdLogger(std::shared_ptr<spdlog::logger> l);
119 
120 /**
121  * Engines that create their own instances of an spdlogger should unregister
122  * the logger here to ensure that resources can be freed when their loggers
123  * go out of scope, or unsubscribe from runtime verbosity changes
124  *
125  * @param n The name of the spdlogger
126  */
127 LOGGER_PUBLIC_API
128 void unregisterSpdLogger(const std::string& n);
129 
130 /**
131  * Check the log level of all spdLoggers is equal to the given level
132  * @param log severity level
133  * @return true if all registered loggers have the specified severity level
134  */
135 LOGGER_PUBLIC_API
136 bool checkLogLevels(spdlog::level::level_enum level);
137 
138 /**
139  * Set the log level of all registered spdLoggers
140  * @param log severity level
141  */
142 LOGGER_PUBLIC_API
143 void setLogLevels(spdlog::level::level_enum level);
144 
145 /**
146  * Tell the logger to flush its buffers
147  */
148 LOGGER_PUBLIC_API
149 void flush();
150 
151 /**
152  * Tell the logger to shut down (flush buffers) and release _ALL_
153  * loggers (you'd need to create new loggers after this method)
154  */
155 LOGGER_PUBLIC_API
156 void shutdown();
157 
158 /**
159  * @return whether or not the logger has been initialized
160  */
161 LOGGER_PUBLIC_API const bool isInitialized();
162 
163 } // namespace logger
164 } // namespace cb
165 
166 #define CB_LOG_ENTRY(severity, ...)                       \
167     do {                                                  \
168         auto _logger_ = cb::logger::get();                \
169         if (_logger_ && _logger_->should_log(severity)) { \
170             _logger_->log(severity, __VA_ARGS__);         \
171         }                                                 \
172     } while (false)
173 
174 #define LOG_TRACE(...) \
175     CB_LOG_ENTRY(spdlog::level::level_enum::trace, __VA_ARGS__)
176 #define LOG_DEBUG(...) \
177     CB_LOG_ENTRY(spdlog::level::level_enum::debug, __VA_ARGS__)
178 #define LOG_INFO(...) CB_LOG_ENTRY(spdlog::level::level_enum::info, __VA_ARGS__)
179 #define LOG_WARNING(...) \
180     CB_LOG_ENTRY(spdlog::level::level_enum::warn, __VA_ARGS__)
181 #define LOG_ERROR(...) CB_LOG_ENTRY(spdlog::level::level_enum::err, __VA_ARGS__)
182 #define LOG_CRITICAL(...) \
183     CB_LOG_ENTRY(spdlog::level::level_enum::critical, __VA_ARGS__)
184