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
42namespace cb {
43namespace logger {
44
45struct 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 */
57LOGGER_PUBLIC_API
58boost::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 */
73LOGGER_PUBLIC_API
74void 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 */
84LOGGER_PUBLIC_API
85void 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 */
99LOGGER_PUBLIC_API
100spdlog::logger* get();
101
102/**
103 * Reset the underlying logger object
104 *
105 * See note about thread safety at the top of the file
106 */
107LOGGER_PUBLIC_API
108void 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 */
117LOGGER_PUBLIC_API
118void 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 */
127LOGGER_PUBLIC_API
128void 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 */
135LOGGER_PUBLIC_API
136bool checkLogLevels(spdlog::level::level_enum level);
137
138/**
139 * Set the log level of all registered spdLoggers
140 * @param log severity level
141 */
142LOGGER_PUBLIC_API
143void setLogLevels(spdlog::level::level_enum level);
144
145/**
146 * Tell the logger to flush its buffers
147 */
148LOGGER_PUBLIC_API
149void 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 */
155LOGGER_PUBLIC_API
156void shutdown();
157
158/**
159 * @return whether or not the logger has been initialized
160 */
161LOGGER_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