1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtCore module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
44 #include "qbytearray.h"
46 #include "qvarlengtharray.h"
53 \class QMessageLogContext
55 \brief The QMessageLogContext class provides additional information about a log message.
58 The class provides information about the source code location a qDebug(), qWarning(),
59 qCritical() or qFatal() message was generated.
61 \sa QMessageLogger, QMessageHandler, qInstallMessageHandler()
67 \brief The QMessageLogger class generates log messages.
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()
75 One example of direct use is to forward errors that stem from a scripting language, e.g. QML:
77 \snippet doc/src/snippets/code/qlogging/qlogging.cpp 1
79 \sa QMessageLogContext, qDebug(), qWarning(), qCritical(), qFatal()
85 Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info)
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.
96 // skip trailing [with XXX] for templates (gcc)
97 pos = info.size() - 1;
98 if (info.endsWith(']')) {
100 if (info.at(pos) == '[')
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>=";
112 // canonize operator names
113 info.replace("operator ", "operator");
115 // remove argument list
118 pos = info.lastIndexOf(')');
120 // Don't know how to parse this function name
124 // find the beginning of the argument list
127 while (pos && parencount) {
128 if (info.at(pos) == ')')
130 else if (info.at(pos) == '(')
137 info.truncate(++pos);
139 if (info.at(pos - 1) == ')') {
140 if (info.indexOf(operator_call) == pos - (int)strlen(operator_call))
143 // this function returns a pointer to a function
144 // and we matched the arguments of the return type's parameter list
146 info.remove(0, info.indexOf('('));
154 // find the beginning of the function name
156 int templatecount = 0;
159 // make sure special characters in operator names are kept
161 switch (info.at(pos)) {
163 if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1)
167 if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1)
171 if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1)
175 int operatorLength = (int)strlen(operator_lessThanEqual);
176 if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
178 else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
188 if (parencount < 0 || templatecount < 0)
191 char c = info.at(pos);
200 else if (c == ' ' && templatecount == 0 && parencount == 0)
205 info = info.mid(pos + 1);
207 // we have the full function name now.
208 // clean up the templates
209 while ((pos = info.lastIndexOf('>')) != -1) {
210 if (!info.contains('<'))
213 // find the matching close
217 while (pos && templatecount) {
218 register char c = info.at(pos);
226 info.remove(pos, end - pos + 1);
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[] = "";
240 struct QMessagePattern {
244 // 0 terminated arrays of literal tokens / literal or placeholder tokens
245 const char **literals;
249 QMessagePattern::QMessagePattern()
251 QString pattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
252 if (pattern.isEmpty()) {
253 pattern = QLatin1String("%{message}");
257 QList<QString> lexemes;
259 bool inPlaceholder = false;
260 for (int i = 0; i < pattern.size(); ++i) {
261 const QChar c = pattern.at(i);
262 if ((c == QLatin1Char('%'))
264 if ((i + 1 < pattern.size())
265 && pattern.at(i + 1) == QLatin1Char('{')) {
266 // beginning of placeholder
267 if (!lexeme.isEmpty()) {
268 lexemes.append(lexeme);
271 inPlaceholder = true;
277 if ((c == QLatin1Char('}') && inPlaceholder)) {
278 // end of placeholder
279 lexemes.append(lexeme);
281 inPlaceholder = false;
284 if (!lexeme.isEmpty())
285 lexemes.append(lexeme);
288 QVarLengthArray<const char*> literalsVar;
289 tokens = new const char*[lexemes.size() + 1];
290 tokens[lexemes.size()] = 0;
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('}'))) {
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;
308 fprintf(stderr, "%s\n",
309 QString::fromLatin1("QT_MESSAGE_PATTERN: Unknown placeholder %1\n"
310 ).arg(lexeme).toLocal8Bit().constData());
312 tokens[i] = emptyTokenC;
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);
322 literals = new const char*[literalsVar.size() + 1];
323 literals[literalsVar.size()] = 0;
324 memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*));
327 QMessagePattern::~QMessagePattern()
329 for (int i = 0; literals[i] != 0; ++i)
330 delete [] literals[i];
337 Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
342 Q_CORE_EXPORT QByteArray qMessageFormatString(QtMsgType type, const QMessageLogContext &context,
347 QMessagePattern *pattern = qMessagePattern();
349 // after destruction of static QMessagePattern instance
351 message.append('\n');
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) {
360 } else if (token == typeTokenC) {
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;
367 } else if (token == fileTokenC) {
369 message.append(context.file);
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));
378 message.append("unknown");
380 message.append(token);
383 message.append('\n');