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 
36 #include <boost/optional/optional.hpp>
37 #include <cJSON.h>
38 #include <memcached/extension.h>
39 #include <memcached/server_api.h>
40 #include <spdlog/logger.h>
41 
42 #include <string>
43 
44 namespace cb {
45 namespace logger {
46 
47 struct LOGGER_PUBLIC_API Config {
48     Config() = default;
49     explicit Config(const cJSON& json);
50 
51     bool operator==(const Config& other) const;
52     bool operator!=(const Config& other) const;
53 
54     /// The base name of the log files (we'll append: .000000.txt where
55     /// the numbers is a sequence counter. The higher the newer ;)
56     std::string filename;
57     /// 2 MB for the logging queue
58     size_t buffersize = 2048 * 1024;
59     /// 100 MB per cycled file
60     size_t cyclesize = 100 * 1024 * 1024;
61     /// if running in a unit test or not
62     bool unit_test = false;
63     /// Should messages be passed on to the console via stderr
64     bool console = true;
65     /// The default log level to initialize the logger to
66     spdlog::level::level_enum log_level = spdlog::level::level_enum::info;
67 };
68 
69 /**
70  * Initialize the logger.
71  *
72  * The default level for the created logger is set to INFO
73  *
74  * See note about thread safety at the top of the file
75  *
76  * @param logger_settings the configuration for the logger
77  * @return optional error message if something goes wrong
78  */
79 LOGGER_PUBLIC_API
80 boost::optional<std::string> initialize(const Config& logger_settings);
81 
82 /**
83  * Initialize the logger with the blackhole logger object
84  *
85  * This method is intended to be used by unit tests which
86  * don't need any output (but may call methods who tries
87  * to fetch the logger)
88  *
89  * See note about thread safety at the top of the file
90  *
91  * @throws std::bad_alloc
92  * @throws spdlog::spdlog_ex if an error occurs creating the logger
93  *                           (if it already exists for instance)
94  */
95 LOGGER_PUBLIC_API
96 void createBlackholeLogger();
97 
98 /**
99  * Initialize the logger with the logger which logs to the console
100  *
101  * See note about thread safety at the top of the file
102  *
103  * @throws std::bad_alloc
104  * @throws spdlog::spdlog_ex if an error occurs creating the logger
105  */
106 LOGGER_PUBLIC_API
107 void createConsoleLogger();
108 
109 /**
110  * Get the underlying logger object
111  *
112  * See note about thread safety at the top of the file.
113  *
114  * This will return null if a logger has not been
115  * initialized through one of the following:
116  *
117  * - initialize()
118  * - createBlackholeLogger()
119  * - createConsoleLogger()
120  */
121 LOGGER_PUBLIC_API
122 spdlog::logger* get();
123 
124 /**
125  * Reset the underlying logger object
126  *
127  * See note about thread safety at the top of the file
128  */
129 LOGGER_PUBLIC_API
130 void reset();
131 
132 LOGGER_PUBLIC_API
133 EXTENSION_LOGGER_DESCRIPTOR& getLoggerDescriptor();
134 
135 /**
136  * Convert a log level as being used by the memcached logger
137  * to spdlog's log levels
138  *
139  * @param sev The memcached severity level
140  * @return The corresponding value in spdlog
141  */
142 LOGGER_PUBLIC_API
143 spdlog::level::level_enum convertToSpdSeverity(EXTENSION_LOG_LEVEL sev);
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 (severity >= _logger_->level()) {      \
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