1 // Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #ifndef DB_LOG_H
8 #define DB_LOG_H
9 
10 #include <log/macros.h>
11 
12 #include <map>
13 #include <mutex>
14 #include <list>
15 
16 /// @file db_log.h
17 ///
18 /// We want to reuse the database backend connection and exchange code
19 /// for other uses, in particular for hook libraries. But this code
20 /// includes some calls to the system logger for debug and uncommon
21 /// cases and of course we do not want to get log messages from
22 /// a hook library to seem to come from DHCP server core.
23 ///
24 /// The solution is to use a database logger which calls the right
25 /// logger with mapped messages.
26 
27 namespace isc {
28 namespace db {
29 
30 ///@{
31 /// @brief Database logging levels
32 ///
33 /// Defines the levels used to output debug messages in the database
34 /// support.  Note that higher numbers equate to more verbose (and detailed)
35 /// output.
36 
37 /// @brief Additional information
38 ///
39 /// Record detailed tracing. This is generally reserved for tracing access to
40 /// the lease database.
41 extern const int DB_DBG_TRACE_DETAIL;
42 
43 ///@}
44 
45 /// @brief Common database library logger.
46 extern isc::log::Logger database_logger;
47 
48 ///@{
49 /// @brief Database messages
50 ///
51 enum DbMessageID {
52     DB_INVALID_ACCESS,
53 
54     PGSQL_DEALLOC_ERROR,
55     PGSQL_FATAL_ERROR,
56     PGSQL_START_TRANSACTION,
57     PGSQL_COMMIT,
58     PGSQL_ROLLBACK,
59 
60     MYSQL_FATAL_ERROR,
61     MYSQL_START_TRANSACTION,
62     MYSQL_COMMIT,
63     MYSQL_ROLLBACK,
64 
65     CQL_DEALLOC_ERROR,
66     CQL_CONNECTION_BEGIN_TRANSACTION,
67     CQL_CONNECTION_COMMIT,
68     CQL_CONNECTION_ROLLBACK
69 };
70 ///@}
71 
72 /// @brief Database logger class
73 ///
74 class DbLogger {
75 public:
76     /// @brief Translation map type
77     typedef std::map<DbMessageID, isc::log::MessageID> MessageMap;
78 
79     /// @brief Constructor
80     ///
81     /// @param logger logger which will be called
82     /// @param map message id translation map
DbLogger(isc::log::Logger & logger,const MessageMap & map)83     DbLogger(isc::log::Logger& logger, const MessageMap& map)
84         : logger_(logger), map_(map) {
85     }
86 
87     /// @brief Translate message
88     ///
89     /// @param id database message id
90     /// @return logger message
91     /// @throw Unexpected if the id is not in the message map
92     const isc::log::MessageID& translateMessage(const DbMessageID& id) const;
93 
94     /// @brief The logger
95     isc::log::Logger& logger_;
96 
97     /// @brief The translation map
98     const MessageMap& map_;
99 };
100 
101 /// @brief Database logger stack
102 typedef std::list<DbLogger> DbLoggerStack;
103 
104 /// @brief Global database logger stack (initialized to database logger)
105 extern DbLoggerStack db_logger_stack;
106 
107 /// @brief Global mutex to protect logger stack
108 extern std::mutex db_logger_mutex;
109 
110 /// @brief Check database logger stack
111 ///
112 /// @throw Unexpected if the stack is empty
113 void checkDbLoggerStack();
114 
115 /// @brief log type enumerations for use in DB_LOG specializations
116 enum log_type_t {
117     fatal,
118     error,
119     warn,
120     info,
121     debug,
122 };
123 
124 /// @brief DB_LOG_* logic
125 template <log_type_t log_type>
126 struct DB_LOG {
127     /// @brief To preserve the old way of logging, this constructor facilitates
128     /// initiating the DB_LOG_* chain call.
129     DB_LOG(DbMessageID const message_id, int const debug_level = 0) {
130         std::lock_guard<std::mutex> lock(isc::db::db_logger_mutex);
131         isc::db::checkDbLoggerStack();
132         if (isEnabled()) {
133             formatter_ = formatter(message_id, debug_level);
134         }
135     }
136 
137     /// @brief Pass parameters to replace logger placeholders.
138     ///
139     /// @param first the parameter to be processed now
140     /// @param args the parameters to be processes in recursive calls
141     ///
142     /// @return reference to this object so that these calls may be chained.
143     template <typename T, typename... Args>
argDB_LOG144     DB_LOG& arg(T first, Args... args) {
145         formatter_.arg(first);
146         return arg(args...);
147     }
148 
149     /// @brief The last invocation of the arg() which is without parameters.
150     ///
151     /// Required when using variadic arguments.
152     ///
153     /// @return reference to this object so that these calls may be chained.
argDB_LOG154     DB_LOG& arg() {
155         return *this;
156     }
157 
158 private:
159     /// @brief Initializes the logging formatter.
160     ///
161     /// @param message_id one of the DbMessageID enums
162     /// @param debug_level one of debug levels specified in log_dbglevels.h
163     ///
164     /// @return the formatter responsible for logging
165     isc::log::Logger::Formatter
166     formatter(DbMessageID const message_id, int const debug_level = 0);
167 
168     /// @brief Check if the logger is ready to log.
169     ///
170     /// @param debug_level required only for debug log type
171     ///
172     /// @return true if the logger is enabled, false otherwise
173     bool isEnabled(int const debug_level = 0) const;
174 
175     /// @brief the formatter responsible for logging
176     isc::log::Logger::Formatter formatter_;
177 };
178 
179 /// @brief all DB_LOG specializations
180 /// @{
181 struct DB_LOG_FATAL : DB_LOG<fatal> {
DB_LOG_FATALDB_LOG_FATAL182     DB_LOG_FATAL(DbMessageID const message_id) : DB_LOG(message_id) {
183     }
184 };
185 
186 struct DB_LOG_ERROR : DB_LOG<error> {
DB_LOG_ERRORDB_LOG_ERROR187     DB_LOG_ERROR(DbMessageID const message_id) : DB_LOG(message_id) {
188     }
189 };
190 
191 struct DB_LOG_WARN : DB_LOG<warn> {
DB_LOG_WARNDB_LOG_WARN192     DB_LOG_WARN(DbMessageID const message_id) : DB_LOG(message_id) {
193     }
194 };
195 
196 struct DB_LOG_INFO : DB_LOG<info> {
DB_LOG_INFODB_LOG_INFO197     DB_LOG_INFO(DbMessageID const message_id) : DB_LOG(message_id) {
198     }
199 };
200 
201 struct DB_LOG_DEBUG : DB_LOG<debug> {
DB_LOG_DEBUGDB_LOG_DEBUG202     DB_LOG_DEBUG(int const debug_level, DbMessageID const message_id)
203         : DB_LOG(message_id, debug_level) {
204     }
205 };
206 ///@}
207 
208 /// @brief DHCP server database message map
209 extern const db::DbLogger::MessageMap db_message_map;
210 
211 /// @brief Database logger translator.
212 extern db::DbLogger db_logger_translator;
213 
214 } // namespace db
215 } // namespace isc
216 
217 #endif // DB_LOG_H
218