Allow customization of qDebug output at runtime
[profile/ivi/qtbase.git] / src / corelib / global / qlogging.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qlogging.h"
43 #include "qlist.h"
44 #include "qbytearray.h"
45 #include "qstring.h"
46 #include "qvarlengtharray.h"
47
48 #include <stdio.h>
49
50 QT_BEGIN_NAMESPACE
51
52 /*!
53     \class QMessageLogContext
54     \relates <QtGlobal>
55     \brief The QMessageLogContext class provides additional information about a log message.
56     \since 5.0
57
58     The class provides information about the source code location a qDebug(), qWarning(),
59     qCritical() or qFatal() message was generated.
60
61     \sa QMessageLogger, QMessageHandler, qInstallMessageHandler()
62 */
63
64 /*!
65     \class QMessageLogger
66     \relates <QtGlobal>
67     \brief The QMessageLogger class generates log messages.
68     \since 5.0
69
70     QMessageLogger is used to generate messages for the Qt logging framework. Most of the time
71     is transparently used through the qDebug(), qWarning(), qCritical, or qFatal() functions,
72     which are actually macros that expand to QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug()
73     et al.
74
75     One example of direct use is to forward errors that stem from a scripting language, e.g. QML:
76
77     \snippet doc/src/snippets/code/qlogging/qlogging.cpp 1
78
79     \sa QMessageLogContext, qDebug(), qWarning(), qCritical(), qFatal()
80 */
81
82 /*!
83     \internal
84 */
85 Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info)
86 {
87     // Strip the function info down to the base function name
88     // note that this throws away the template definitions,
89     // the parameter types (overloads) and any const/volatile qualifiers.
90
91     if (info.isEmpty())
92         return info;
93
94     int pos;
95
96     // skip trailing [with XXX] for templates (gcc)
97     pos = info.size() - 1;
98     if (info.endsWith(']')) {
99         while (--pos) {
100             if (info.at(pos) == '[')
101                 info.truncate(pos);
102         }
103     }
104
105     // operator names with '(', ')', '<', '>' in it
106     static const char operator_call[] = "operator()";
107     static const char operator_lessThan[] = "operator<";
108     static const char operator_greaterThan[] = "operator>";
109     static const char operator_lessThanEqual[] = "operator<=";
110     static const char operator_greaterThanEqual[] = "operator>=";
111
112     // canonize operator names
113     info.replace("operator ", "operator");
114
115     // remove argument list
116     forever {
117         int parencount = 0;
118         pos = info.lastIndexOf(')');
119         if (pos == -1) {
120             // Don't know how to parse this function name
121             return info;
122         }
123
124         // find the beginning of the argument list
125         --pos;
126         ++parencount;
127         while (pos && parencount) {
128             if (info.at(pos) == ')')
129                 ++parencount;
130             else if (info.at(pos) == '(')
131                 --parencount;
132             --pos;
133         }
134         if (parencount != 0)
135             return info;
136
137         info.truncate(++pos);
138
139         if (info.at(pos - 1) == ')') {
140             if (info.indexOf(operator_call) == pos - (int)strlen(operator_call))
141                 break;
142
143             // this function returns a pointer to a function
144             // and we matched the arguments of the return type's parameter list
145             // try again
146             info.remove(0, info.indexOf('('));
147             info.chop(1);
148             continue;
149         } else {
150             break;
151         }
152     }
153
154     // find the beginning of the function name
155     int parencount = 0;
156     int templatecount = 0;
157     --pos;
158
159     // make sure special characters in operator names are kept
160     if (pos > -1) {
161         switch (info.at(pos)) {
162         case ')':
163             if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1)
164                 pos -= 2;
165             break;
166         case '<':
167             if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1)
168                 --pos;
169             break;
170         case '>':
171             if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1)
172                 --pos;
173             break;
174         case '=': {
175             int operatorLength = (int)strlen(operator_lessThanEqual);
176             if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
177                 pos -= 2;
178             else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
179                 pos -= 2;
180             break;
181         }
182         default:
183             break;
184         }
185     }
186
187     while (pos > -1) {
188         if (parencount < 0 || templatecount < 0)
189             return info;
190
191         char c = info.at(pos);
192         if (c == ')')
193             ++parencount;
194         else if (c == '(')
195             --parencount;
196         else if (c == '>')
197             ++templatecount;
198         else if (c == '<')
199             --templatecount;
200         else if (c == ' ' && templatecount == 0 && parencount == 0)
201             break;
202
203         --pos;
204     }
205     info = info.mid(pos + 1);
206
207     // we have the full function name now.
208     // clean up the templates
209     while ((pos = info.lastIndexOf('>')) != -1) {
210         if (!info.contains('<'))
211             break;
212
213         // find the matching close
214         int end = pos;
215         templatecount = 1;
216         --pos;
217         while (pos && templatecount) {
218             register char c = info.at(pos);
219             if (c == '>')
220                 ++templatecount;
221             else if (c == '<')
222                 --templatecount;
223             --pos;
224         }
225         ++pos;
226         info.remove(pos, end - pos + 1);
227     }
228
229     return info;
230 }
231
232 // tokens as recognized in QT_MESSAGE_PATTERN
233 static const char typeTokenC[] = "%{type}";
234 static const char messageTokenC[] = "%{message}";
235 static const char fileTokenC[] = "%{file}";
236 static const char lineTokenC[] = "%{line}";
237 static const char functionTokenC[] = "%{function}";
238 static const char emptyTokenC[] = "";
239
240 struct QMessagePattern {
241     QMessagePattern();
242     ~QMessagePattern();
243
244     // 0 terminated arrays of literal tokens / literal or placeholder tokens
245     const char **literals;
246     const char **tokens;
247 };
248
249 QMessagePattern::QMessagePattern()
250 {
251     QString pattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
252     if (pattern.isEmpty()) {
253         pattern = QLatin1String("%{message}");
254     }
255
256     // scanner
257     QList<QString> lexemes;
258     QString lexeme;
259     bool inPlaceholder = false;
260     for (int i = 0; i < pattern.size(); ++i) {
261         const QChar c = pattern.at(i);
262         if ((c == QLatin1Char('%'))
263                 && !inPlaceholder) {
264             if ((i + 1 < pattern.size())
265                     && pattern.at(i + 1) == QLatin1Char('{')) {
266                 // beginning of placeholder
267                 if (!lexeme.isEmpty()) {
268                     lexemes.append(lexeme);
269                     lexeme.clear();
270                 }
271                 inPlaceholder = true;
272             }
273         }
274
275         lexeme.append(c);
276
277         if ((c == QLatin1Char('}') && inPlaceholder)) {
278             // end of placeholder
279             lexemes.append(lexeme);
280             lexeme.clear();
281             inPlaceholder = false;
282         }
283     }
284     if (!lexeme.isEmpty())
285         lexemes.append(lexeme);
286
287     // tokenizer
288     QVarLengthArray<const char*> literalsVar;
289     tokens = new const char*[lexemes.size() + 1];
290     tokens[lexemes.size()] = 0;
291
292     for (int i = 0; i < lexemes.size(); ++i) {
293         const QString lexeme = lexemes.at(i);
294         if (lexeme.startsWith(QLatin1String("%{"))
295                 && lexeme.endsWith(QLatin1Char('}'))) {
296             // placeholder
297             if (lexeme == QLatin1String(typeTokenC)) {
298                 tokens[i] = typeTokenC;
299             } else if (lexeme == QLatin1String(messageTokenC))
300                 tokens[i] = messageTokenC;
301             else if (lexeme == QLatin1String(fileTokenC))
302                 tokens[i] = fileTokenC;
303             else if (lexeme == QLatin1String(lineTokenC))
304                 tokens[i] = lineTokenC;
305             else if (lexeme == QLatin1String(functionTokenC))
306                 tokens[i] = functionTokenC;
307             else {
308                 fprintf(stderr, "%s\n",
309                         QString::fromLatin1("QT_MESSAGE_PATTERN: Unknown placeholder %1\n"
310                                             ).arg(lexeme).toLocal8Bit().constData());
311                 fflush(stderr);
312                 tokens[i] = emptyTokenC;
313             }
314         } else {
315             char *literal = new char[lexeme.size() + 1];
316             strncpy(literal, lexeme.toLocal8Bit().constData(), lexeme.size());
317             literal[lexeme.size()] = '\0';
318             literalsVar.append(literal);
319             tokens[i] = literal;
320         }
321     }
322     literals = new const char*[literalsVar.size() + 1];
323     literals[literalsVar.size()] = 0;
324     memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*));
325 }
326
327 QMessagePattern::~QMessagePattern()
328 {
329     for (int i = 0; literals[i] != 0; ++i)
330         delete [] literals[i];
331     delete [] literals;
332     literals = 0;
333     delete [] tokens;
334     tokens = 0;
335 }
336
337 Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
338
339 /*!
340     \internal
341 */
342 Q_CORE_EXPORT QByteArray qMessageFormatString(QtMsgType type, const QMessageLogContext &context,
343                                                const char *str)
344 {
345     QByteArray message;
346
347     QMessagePattern *pattern = qMessagePattern();
348     if (!pattern) {
349         // after destruction of static QMessagePattern instance
350         message.append(str);
351         message.append('\n');
352         return message;
353     }
354
355     // we do not convert file, function, line literals to local encoding due to overhead
356     for (int i = 0; pattern->tokens[i] != 0; ++i) {
357         const char *token = pattern->tokens[i];
358         if (token == messageTokenC) {
359             message.append(str);
360         } else if (token == typeTokenC) {
361             switch (type) {
362             case QtDebugMsg:   message.append("debug"); break;
363             case QtWarningMsg: message.append("warning"); break;
364             case QtCriticalMsg:message.append("critical"); break;
365             case QtFatalMsg:   message.append("fatal"); break;
366             }
367         } else if (token == fileTokenC) {
368             if (context.file)
369                 message.append(context.file);
370             else
371                 message.append("unknown");
372         } else if (token == lineTokenC) {
373             message.append(QString::number(context.line).toLatin1().constData());
374         } else if (token == functionTokenC) {
375             if (context.function)
376                 message.append(qCleanupFuncinfo(context.function));
377             else
378                 message.append("unknown");
379         } else {
380             message.append(token);
381         }
382     }
383     message.append('\n');
384     return message;
385 }
386
387 QT_END_NAMESPACE