1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
/** \file logging.h */ // -*-c++-*-
// Copyright (C) 2010 Daniel Burrows
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING. If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
#ifndef APTITUDE_UTIL_LOGGING_H
#define APTITUDE_UTIL_LOGGING_H
#include <boost/optional.hpp>
#include <sigc++/connection.h>
#include <sigc++/slot.h>
#include <memory>
#include <sstream>
#include <limits.h> // For INT_MIN
namespace aptitude
{
namespace util
{
/** \brief A lightweight, no-frills logging library.
*
* aptitude implements its own logging instead of using an
* external library for a couple reasons. We don't need the full
* power of a real logging library, since we only use this for
* debugging purposes, and real logging libraries have proven to
* be flaky and poorly maintained. In particular log4cxx was
* responsible for many crashes due to aptitude breaking
* undocumented assumptions that the library made.
*/
namespace logging
{
/** \brief The importance of a log message.
*/
enum log_level
{
TRACE_LEVEL = 0,
DEBUG_LEVEL = 1,
INFO_LEVEL = 2,
WARN_LEVEL = 3,
ERROR_LEVEL = 4,
FATAL_LEVEL = 5,
/** \brief The OFF level is special: loggers set to this
* level never display any messages.
*/
OFF_LEVEL = INT_MIN
};
/** \brief Get a string corresponding to a log level.
*
* The level must be a member of the enum log_level.
*/
const char *describe_log_level(log_level l);
class Logger;
typedef std::shared_ptr<Logger> LoggerPtr;
class LoggingSystem;
/** \brief A logger is used to log messages in a particular
* category in conjunction with the LOG_*() macros below.
*/
class Logger
{
// The "effective" level of this logger. Only messages at or
// above this level will be logged. Defaults to ERROR.
//
// This is provided as a concrete member in the base class so
// that "is logging enabled?" tests can be inlined.
log_level effectiveLevel;
class Impl;
Logger(log_level _effectiveLevel);
Logger(const Logger &);
// Logger is really just an interface to the hidden
// LoggingSystem singleton. For organizational reasons and to
// handle threading better, all the "real" code in this module
// lives over there. Note that this means there can't be any
// other implementations of Logger; the virtual methods here
// are just to hide the implementation, not to provide
// polymorphism.
friend class LoggingSystem;
public:
virtual ~Logger();
/** \brief Return \b true if messages logged at the given
* level will appear.
*
* Useful because this lets us suppress the (potentially
* expensive) generation of log messages if they're disabled.
*/
bool isEnabledFor(log_level l) const
{
return
effectiveLevel != OFF_LEVEL &&
l >= effectiveLevel;
}
/** \brief Retrieve the effective log level of this logger. */
log_level getEffectiveLevel() const { return effectiveLevel; }
/** \brief Unconditionally log a message to this logger.
*
* This logs even if the logger is not enabled.
*/
virtual void log(const char *sourceFilename,
int sourceLineNumber,
log_level logLevel,
const std::string &msg) = 0;
/** \brief Return the name of this logger's category. */
virtual const std::string &getCategory() const = 0;
/** \brief Modify the level that is set for this logger.
*
* Any descendants of this logger will be automatically
* updated if necessary.
*
* \warning This function is not thread-safe. This is a
* concession I made for the sake of a simple implementation.
* Normally, setLevel will be invoked during initialization,
* before any background threads are created.
*
* \param value The new level to configure, or an empty
* boost::optional value to clear the configuration of this
* logger.
*/
virtual void setLevel(const boost::optional<log_level> &value) = 0;
/** \brief Register a slot that is to be invoked when a
* message should be logged.
*
* This is one of the places where I compromise efficiency
* (maybe) to get a simple implementation and a simple
* interface.
*
* The parameters to the slot are:
*
* 1. The name of the source file in which the log message
* is located.
* 2. The line number on which the log message is located.
* 3. The log level of the log message.
* 4. The logger that generated the message.
* 5. The log message.
*
* Note that this is the *only* way that messages are logged,
* in contrast to many other logging frameworks.
*
* This function is thread-safe (as long as it's safe to copy
* the slot, anyway).
*/
virtual sigc::connection
connect_message_logged(const sigc::slot<
void,
const char *,
int,
log_level,
LoggerPtr,
std::string> &slot) = 0;
/** \brief Retrieve a logger for the given category, creating
* it if necessary.
*
* A category consists of one or more nonempty components
* separated by ".", such as "aptitude.util.logging", with
* the exception that the root logger is indicated by the
* empty string ("").
*
* This function is thread-safe.
*/
static LoggerPtr getLogger(const std::string &category);
};
#define LOG_LEVEL(level, logger, msg) \
do \
{ \
::aptitude::util::logging::log_level __aptitude_util_logging_level = (level); \
::aptitude::util::logging::LoggerPtr __aptitude_util_logging_logger = (logger); \
if(__aptitude_util_logging_logger->isEnabledFor(__aptitude_util_logging_level)) \
{ \
std::ostringstream __aptitude_util_logging_stream; \
__aptitude_util_logging_stream << msg; \
(__aptitude_util_logging_logger)->log(__FILE__, \
__LINE__, \
__aptitude_util_logging_level, \
__aptitude_util_logging_stream.str()); \
} \
} while(0) \
#define LOG_TRACE(logger, msg) LOG_LEVEL(::aptitude::util::logging::TRACE_LEVEL, logger, msg)
#define LOG_DEBUG(logger, msg) LOG_LEVEL(::aptitude::util::logging::DEBUG_LEVEL, logger, msg)
#define LOG_INFO(logger, msg) LOG_LEVEL(::aptitude::util::logging::INFO_LEVEL, logger, msg)
#define LOG_WARN(logger, msg) LOG_LEVEL(::aptitude::util::logging::WARN_LEVEL, logger, msg)
#define LOG_ERROR(logger, msg) LOG_LEVEL(::aptitude::util::logging::ERROR_LEVEL, logger, msg)
#define LOG_FATAL(logger, msg) LOG_LEVEL(::aptitude::util::logging::FATAL_LEVEL, logger, msg)
/** \brief Represents the entire logging system.
*
* This class is exposed primarily for use with tests: to test
* the logging system itself, you really need to be able to
* create a local one that no-one else will mess with.
*
* That said, this is a representation of an entire logging
* system. It keeps track of which loggers are available and
* provides a routine for creating them that's equivalent to
* Logger::getLogger.
*/
class LoggingSystem
{
class Impl;
friend class Logger;
LoggingSystem();
LoggingSystem(const LoggingSystem &);
friend std::shared_ptr<LoggingSystem> createLoggingSystem();
public:
virtual ~LoggingSystem();
/** \brief Retrieve a logger for the given category.
*
* The returned logger is the only logger for that category
* within this logging system, but will be distinct from
* loggers created in any other logging system (including the
* global one managed by Logger::getLogger).
*/
virtual LoggerPtr getLogger(const std::string &category) = 0;
};
/** \brief Create a new, local logging system. */
std::shared_ptr<LoggingSystem> createLoggingSystem();
}
}
}
#endif // APTITUDE_UTIL_LOGGING_H
|