/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qlogging.h" #include "qlist.h" #include "qbytearray.h" #include "qstring.h" #include "qvarlengtharray.h" #include QT_BEGIN_NAMESPACE /*! \class QMessageLogContext \relates \brief The QMessageLogContext class provides additional information about a log message. \since 5.0 The class provides information about the source code location a qDebug(), qWarning(), qCritical() or qFatal() message was generated. \sa QMessageLogger, QMessageHandler, qInstallMessageHandler() */ /*! \class QMessageLogger \relates \brief The QMessageLogger class generates log messages. \since 5.0 QMessageLogger is used to generate messages for the Qt logging framework. Most of the time is transparently used through the qDebug(), qWarning(), qCritical, or qFatal() functions, which are actually macros that expand to QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug() et al. One example of direct use is to forward errors that stem from a scripting language, e.g. QML: \snippet doc/src/snippets/code/qlogging/qlogging.cpp 1 \sa QMessageLogContext, qDebug(), qWarning(), qCritical(), qFatal() */ /*! \internal */ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) { // Strip the function info down to the base function name // note that this throws away the template definitions, // the parameter types (overloads) and any const/volatile qualifiers. if (info.isEmpty()) return info; int pos; // skip trailing [with XXX] for templates (gcc) pos = info.size() - 1; if (info.endsWith(']')) { while (--pos) { if (info.at(pos) == '[') info.truncate(pos); } } // operator names with '(', ')', '<', '>' in it static const char operator_call[] = "operator()"; static const char operator_lessThan[] = "operator<"; static const char operator_greaterThan[] = "operator>"; static const char operator_lessThanEqual[] = "operator<="; static const char operator_greaterThanEqual[] = "operator>="; // canonize operator names info.replace("operator ", "operator"); // remove argument list forever { int parencount = 0; pos = info.lastIndexOf(')'); if (pos == -1) { // Don't know how to parse this function name return info; } // find the beginning of the argument list --pos; ++parencount; while (pos && parencount) { if (info.at(pos) == ')') ++parencount; else if (info.at(pos) == '(') --parencount; --pos; } if (parencount != 0) return info; info.truncate(++pos); if (info.at(pos - 1) == ')') { if (info.indexOf(operator_call) == pos - (int)strlen(operator_call)) break; // this function returns a pointer to a function // and we matched the arguments of the return type's parameter list // try again info.remove(0, info.indexOf('(')); info.chop(1); continue; } else { break; } } // find the beginning of the function name int parencount = 0; int templatecount = 0; --pos; // make sure special characters in operator names are kept if (pos > -1) { switch (info.at(pos)) { case ')': if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1) pos -= 2; break; case '<': if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1) --pos; break; case '>': if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1) --pos; break; case '=': { int operatorLength = (int)strlen(operator_lessThanEqual); if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1) pos -= 2; else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1) pos -= 2; break; } default: break; } } while (pos > -1) { if (parencount < 0 || templatecount < 0) return info; char c = info.at(pos); if (c == ')') ++parencount; else if (c == '(') --parencount; else if (c == '>') ++templatecount; else if (c == '<') --templatecount; else if (c == ' ' && templatecount == 0 && parencount == 0) break; --pos; } info = info.mid(pos + 1); // remove trailing '*', '&' that are part of the return argument while ((info.at(0) == '*') || (info.at(0) == '&')) info = info.mid(1); // we have the full function name now. // clean up the templates while ((pos = info.lastIndexOf('>')) != -1) { if (!info.contains('<')) break; // find the matching close int end = pos; templatecount = 1; --pos; while (pos && templatecount) { register char c = info.at(pos); if (c == '>') ++templatecount; else if (c == '<') --templatecount; --pos; } ++pos; info.remove(pos, end - pos + 1); } return info; } // tokens as recognized in QT_MESSAGE_PATTERN static const char typeTokenC[] = "%{type}"; static const char messageTokenC[] = "%{message}"; static const char fileTokenC[] = "%{file}"; static const char lineTokenC[] = "%{line}"; static const char functionTokenC[] = "%{function}"; static const char emptyTokenC[] = ""; struct QMessagePattern { QMessagePattern(); ~QMessagePattern(); // 0 terminated arrays of literal tokens / literal or placeholder tokens const char **literals; const char **tokens; }; QMessagePattern::QMessagePattern() { QString pattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN")); if (pattern.isEmpty()) { pattern = QLatin1String("%{message}"); } // scanner QList lexemes; QString lexeme; bool inPlaceholder = false; for (int i = 0; i < pattern.size(); ++i) { const QChar c = pattern.at(i); if ((c == QLatin1Char('%')) && !inPlaceholder) { if ((i + 1 < pattern.size()) && pattern.at(i + 1) == QLatin1Char('{')) { // beginning of placeholder if (!lexeme.isEmpty()) { lexemes.append(lexeme); lexeme.clear(); } inPlaceholder = true; } } lexeme.append(c); if ((c == QLatin1Char('}') && inPlaceholder)) { // end of placeholder lexemes.append(lexeme); lexeme.clear(); inPlaceholder = false; } } if (!lexeme.isEmpty()) lexemes.append(lexeme); // tokenizer QVarLengthArray literalsVar; tokens = new const char*[lexemes.size() + 1]; tokens[lexemes.size()] = 0; for (int i = 0; i < lexemes.size(); ++i) { const QString lexeme = lexemes.at(i); if (lexeme.startsWith(QLatin1String("%{")) && lexeme.endsWith(QLatin1Char('}'))) { // placeholder if (lexeme == QLatin1String(typeTokenC)) { tokens[i] = typeTokenC; } else if (lexeme == QLatin1String(messageTokenC)) tokens[i] = messageTokenC; else if (lexeme == QLatin1String(fileTokenC)) tokens[i] = fileTokenC; else if (lexeme == QLatin1String(lineTokenC)) tokens[i] = lineTokenC; else if (lexeme == QLatin1String(functionTokenC)) tokens[i] = functionTokenC; else { fprintf(stderr, "%s\n", QString::fromLatin1("QT_MESSAGE_PATTERN: Unknown placeholder %1\n" ).arg(lexeme).toLocal8Bit().constData()); fflush(stderr); tokens[i] = emptyTokenC; } } else { char *literal = new char[lexeme.size() + 1]; strncpy(literal, lexeme.toLocal8Bit().constData(), lexeme.size()); literal[lexeme.size()] = '\0'; literalsVar.append(literal); tokens[i] = literal; } } literals = new const char*[literalsVar.size() + 1]; literals[literalsVar.size()] = 0; memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*)); } QMessagePattern::~QMessagePattern() { for (int i = 0; literals[i] != 0; ++i) delete [] literals[i]; delete [] literals; literals = 0; delete [] tokens; tokens = 0; } Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern) /*! \internal */ Q_CORE_EXPORT QByteArray qMessageFormatString(QtMsgType type, const QMessageLogContext &context, const char *str) { QByteArray message; QMessagePattern *pattern = qMessagePattern(); if (!pattern) { // after destruction of static QMessagePattern instance message.append(str); message.append('\n'); return message; } // we do not convert file, function, line literals to local encoding due to overhead for (int i = 0; pattern->tokens[i] != 0; ++i) { const char *token = pattern->tokens[i]; if (token == messageTokenC) { message.append(str); } else if (token == typeTokenC) { switch (type) { case QtDebugMsg: message.append("debug"); break; case QtWarningMsg: message.append("warning"); break; case QtCriticalMsg:message.append("critical"); break; case QtFatalMsg: message.append("fatal"); break; } } else if (token == fileTokenC) { if (context.file) message.append(context.file); else message.append("unknown"); } else if (token == lineTokenC) { message.append(QString::number(context.line).toLatin1().constData()); } else if (token == functionTokenC) { if (context.function) message.append(qCleanupFuncinfo(context.function)); else message.append("unknown"); } else { message.append(token); } } message.append('\n'); return message; } QT_END_NAMESPACE