Projekt

Obecné

Profil

Stáhnout (7.01 KB) Statistiky
| Větev: | Tag: | Revize:
1 c137512e Oto Šťáva
// Copyright (c) 2013, Razvan Petru
2
// All rights reserved.
3
4
// Redistribution and use in source and binary forms, with or without modification,
5
// are permitted provided that the following conditions are met:
6
7
// * Redistributions of source code must retain the above copyright notice, this
8
//   list of conditions and the following disclaimer.
9
// * Redistributions in binary form must reproduce the above copyright notice, this
10
//   list of conditions and the following disclaimer in the documentation and/or other
11
//   materials provided with the distribution.
12
// * The name of the contributors may not be used to endorse or promote products
13
//   derived from this software without specific prior written permission.
14
15
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
19
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
24
// OF THE POSSIBILITY OF SUCH DAMAGE.
25
26
#include "QsLog.h"
27
#include "QsLogDest.h"
28
#ifdef QS_LOG_SEPARATE_THREAD
29
#include <QThreadPool>
30
#include <QRunnable>
31
#endif
32
#include <QMutex>
33
#include <QVector>
34
#include <QDateTime>
35
#include <QtGlobal>
36
#include <cstdlib>
37
#include <stdexcept>
38
39
namespace QsLogging
40
{
41
typedef QVector<DestinationPtr> DestinationList;
42
43
static const char TraceString[] = "TRACE";
44
static const char DebugString[] = "DEBUG";
45
static const char InfoString[]  = "INFO ";
46
static const char WarnString[]  = "WARN ";
47
static const char ErrorString[] = "ERROR";
48
static const char FatalString[] = "FATAL";
49
50
// not using Qt::ISODate because we need the milliseconds too
51
static const QString fmtDateTime("yyyy-MM-ddThh:mm:ss.zzz");
52
53
static Logger* sInstance = 0;
54
55
static const char* LevelToText(Level theLevel)
56
{
57
    switch (theLevel) {
58
        case TraceLevel:
59
            return TraceString;
60
        case DebugLevel:
61
            return DebugString;
62
        case InfoLevel:
63
            return InfoString;
64
        case WarnLevel:
65
            return WarnString;
66
        case ErrorLevel:
67
            return ErrorString;
68
        case FatalLevel:
69
            return FatalString;
70
        case OffLevel:
71
            return "";
72
        default: {
73
            Q_ASSERT(!"bad log level");
74
            return InfoString;
75
        }
76
    }
77
}
78
79
#ifdef QS_LOG_SEPARATE_THREAD
80
class LogWriterRunnable : public QRunnable
81
{
82
public:
83
    LogWriterRunnable(QString message, Level level);
84
    virtual void run();
85
86
private:
87
    QString mMessage;
88
    Level mLevel;
89
};
90
#endif
91
92
class LoggerImpl
93
{
94
public:
95
    LoggerImpl();
96
97
#ifdef QS_LOG_SEPARATE_THREAD
98
    QThreadPool threadPool;
99
#endif
100
    QMutex logMutex;
101
    Level level;
102
    DestinationList destList;
103
    bool includeTimeStamp;
104
    bool includeLogLevel;
105
};
106
107
#ifdef QS_LOG_SEPARATE_THREAD
108
LogWriterRunnable::LogWriterRunnable(QString message, Level level)
109
    : QRunnable()
110
    , mMessage(message)
111
    , mLevel(level)
112
{
113
}
114
115
void LogWriterRunnable::run()
116
{
117
    Logger::instance().write(mMessage, mLevel);
118
}
119
#endif
120
121
122
LoggerImpl::LoggerImpl()
123
    : level(InfoLevel)
124
    , includeTimeStamp(true)
125
    , includeLogLevel(true)
126
{
127
    // assume at least file + console
128
    destList.reserve(2);
129
#ifdef QS_LOG_SEPARATE_THREAD
130
    threadPool.setMaxThreadCount(1);
131
    threadPool.setExpiryTimeout(-1);
132
#endif
133
}
134
135
136
Logger::Logger()
137
    : d(new LoggerImpl)
138
{
139
}
140
141
Logger& Logger::instance()
142
{
143
    if (!sInstance)
144
        sInstance = new Logger;
145
146
    return *sInstance;
147
}
148
149
void Logger::destroyInstance()
150
{
151
    delete sInstance;
152
    sInstance = 0;
153
}
154
155
// tries to extract the level from a string log message. If available, conversionSucceeded will
156
// contain the conversion result.
157
Level Logger::levelFromLogMessage(const QString& logMessage, bool* conversionSucceeded)
158
{
159
    if (conversionSucceeded)
160
        *conversionSucceeded = true;
161
162
    if (logMessage.startsWith(QLatin1String(TraceString)))
163
        return TraceLevel;
164
    if (logMessage.startsWith(QLatin1String(DebugString)))
165
        return DebugLevel;
166
    if (logMessage.startsWith(QLatin1String(InfoString)))
167
        return InfoLevel;
168
    if (logMessage.startsWith(QLatin1String(WarnString)))
169
        return WarnLevel;
170
    if (logMessage.startsWith(QLatin1String(ErrorString)))
171
        return ErrorLevel;
172
    if (logMessage.startsWith(QLatin1String(FatalString)))
173
        return FatalLevel;
174
175
    if (conversionSucceeded)
176
        *conversionSucceeded = false;
177
    return OffLevel;
178
}
179
180
Logger::~Logger()
181
{
182
#ifdef QS_LOG_SEPARATE_THREAD
183
    d->threadPool.waitForDone();
184
#endif
185
    delete d;
186
    d = 0;
187
}
188
189
void Logger::addDestination(DestinationPtr destination)
190
{
191
    Q_ASSERT(destination.data());
192
    d->destList.push_back(destination);
193
}
194
195
void Logger::setLoggingLevel(Level newLevel)
196
{
197
    d->level = newLevel;
198
}
199
200
Level Logger::loggingLevel() const
201
{
202
    return d->level;
203
}
204
205
void Logger::setIncludeTimestamp(bool e)
206
{
207
    d->includeTimeStamp = e;
208
}
209
210
bool Logger::includeTimestamp() const
211
{
212
    return d->includeTimeStamp;
213
}
214
215
void Logger::setIncludeLogLevel(bool l)
216
{
217
    d->includeLogLevel = l;
218
}
219
220
bool Logger::includeLogLevel() const
221
{
222
    return d->includeLogLevel;
223
}
224
225
//! creates the complete log message and passes it to the logger
226
void Logger::Helper::writeToLog()
227
{
228
    const char* const levelName = LevelToText(level);
229
    QString completeMessage;
230
    Logger &logger = Logger::instance();
231
    if (logger.includeLogLevel()) {
232
        completeMessage.
233
                append(levelName).
234
                append(' ');
235
    }
236
    if (logger.includeTimestamp()) {
237
        completeMessage.
238
                append(QDateTime::currentDateTime().toString(fmtDateTime)).
239
                append(' ');
240
    }
241
    completeMessage.append(buffer);
242
    Logger::instance().enqueueWrite(completeMessage, level);
243
}
244
245
Logger::Helper::~Helper()
246
{
247
    try {
248
        writeToLog();
249
    }
250
    catch(std::exception&) {
251
        // you shouldn't throw exceptions from a sink
252
        Q_ASSERT(!"exception in logger helper destructor");
253
        throw;
254
    }
255
}
256
257
//! directs the message to the task queue or writes it directly
258
void Logger::enqueueWrite(const QString& message, Level level)
259
{
260
#ifdef QS_LOG_SEPARATE_THREAD
261
    LogWriterRunnable *r = new LogWriterRunnable(message, level);
262
    d->threadPool.start(r);
263
#else
264
    write(message, level);
265
#endif
266
}
267
268
//! Sends the message to all the destinations. The level for this message is passed in case
269
//! it's useful for processing in the destination.
270
void Logger::write(const QString& message, Level level)
271
{
272
    QMutexLocker lock(&d->logMutex);
273
    for (DestinationList::iterator it = d->destList.begin(),
274
        endIt = d->destList.end();it != endIt;++it) {
275
        (*it)->write(message, level);
276
    }
277
}
278
279
} // end namespace