log.cpp 6.82 KB
/* ****************************************************************************
 * Copyright 2019 Open Systems Development BV                                 *
 *                                                                            *
 * Permission is hereby granted, free of charge, to any person obtaining a    *
 * copy of this software and associated documentation files (the "Software"), *
 * to deal in the Software without restriction, including without limitation  *
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,   *
 * and/or sell copies of the Software, and to permit persons to whom the      *
 * Software is furnished to do so, subject to the following conditions:       *
 *                                                                            *
 * The above copyright notice and this permission notice shall be included in *
 * all copies or substantial portions of the Software.                        *
 *                                                                            *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    *
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        *
 * DEALINGS IN THE SOFTWARE.                                                  *
 * ***************************************************************************/

// std
#include <ios>
#include <iomanip>
#include <sstream>
#include <thread>

#include "timeutils.h"
#include "log.h"
#include "threadcontext.h"

#include <QBuffer>
#include <QByteArray>
#include <QDataStream>
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QIODevice>
#include <QObject>
#include <QMutexLocker>

namespace osdev {
namespace components {

namespace {

QString toString(LogLevel level)
{
    switch(level)
    {
        case LogLevel::Trace:
            return "T";
        case LogLevel::Debug:
            return "D";
        case LogLevel::Info:
            return "I";
        case LogLevel::Warning:
            return "W";
        case LogLevel::Error:
            return "E";
    }
    return "U";
}

} // anonymous

QString         Log::s_context = QString();
QString         Log::s_fileName = QString();
LogLevel        Log::s_logLevel = LogLevel::Debug;
QMutex          Log::m_mutex;

void Log::init(const QString& context, const QString& logFile, LogLevel logDepth)
{
    s_logLevel = logDepth;
    s_context = context;

    if ( !logFile.isEmpty() )
    {
        s_fileName = logFile;
    }
}

void Log::terminate()
{
    s_context.clear();
    s_fileName.clear();
    s_logLevel = LogLevel::Info;
}

void Log::log(const QString& category, const QString& message, LogLevel level)
{
    QMutexLocker lock( &m_mutex );

    QString logCategory = s_context + '|' + toString(level) + '|' + ThreadContext::instance().context() + '|' + category;
    QString logMessage = message;
    if(logMessage.isEmpty())
    {
        static const QString emptyMessage("--");
        logMessage = emptyMessage;
    }
    else
    {
        // Replace % signs.
        logMessage.replace('%', "%%");
    }

    writeLog( logCategory, logMessage, level );
}

QString Log::fileinfoMessage(const char* file, int line, const QString& message)
{
    static const QString templ("%1:%2| %3");
    QFileInfo fi(file);
    return templ.arg( fi.fileName() ).arg(line).arg(message);
}

void Log::trace(const QString &category, const QString &message)
{
    log(category, message, LogLevel::Trace);
}

void Log::debug(const QString& category, const QString& message)
{
    log( category, message, LogLevel::Debug );
}

void Log::info(const QString& category, const QString& message)
{
    log( category, message, LogLevel::Info );
}

void Log::warning(const QString& category, const QString& message)
{
    log(category, message, LogLevel::Warning );
}

void Log::error(const QString& category, const QString& message)
{
    log(category, message, LogLevel::Error );
}

void Log::logObject(const QString& category, QObject* object)
{
    QBuffer buffer;
    buffer.open(QIODevice::ReadWrite);

    QDataStream datastream(&buffer);
    QString data;

    datastream << object;
    datastream >> data;

    log( category, data, LogLevel::Debug );
}

void Log::trace(const char *file, int line, const QString &category, const QString &message)
{
    log(category, fileinfoMessage(file, line, message), LogLevel::Trace );
}

void Log::debug(const char* file, int line, const QString& category, const QString& message)
{
    log( category, fileinfoMessage(file, line, message), LogLevel::Debug );
}

void Log::info(const char* file, int line, const QString& category, const QString& message)
{
    log( category, fileinfoMessage(file, line, message), LogLevel::Info );
}

void Log::warning(const char* file, int line, const QString& category, const QString& message)
{
    log( category, fileinfoMessage(file, line, message), LogLevel::Warning);
}

void Log::error(const char* file, int line, const QString& category, const QString& message)
{
    log( category, fileinfoMessage(file, line, message), LogLevel::Error );
}

void Log::logObject(const char* file, int line, const QString& category, QObject* object)
{
    QBuffer buffer;
    buffer.open(QIODevice::ReadWrite);

    QDataStream datastream(&buffer);
    QString data;

    datastream << object;
    datastream >> data;

    log( " [LOGOBJECT] " + category, fileinfoMessage(file, line, data), LogLevel::Debug );
}

QString Log::getDateTime()
{
    return QDateTime::currentDateTime().toString( "dd-MM-yyyy HH:mm:ss.zzz" );
}

void Log::writeLog(const QString& category, const QString& message, LogLevel level)
{

    if ( level >= s_logLevel )
    {
        std::ostringstream threadIdStr;
        threadIdStr << std::right << std::setfill('0') << std::setw(12) << std::hex << std::this_thread::get_id();

        QString logLine;
        QTextStream(&logLine) << getDateTime() << '|' << threadIdStr.str().c_str() << '|' << category << '|' << message;
        if ( !s_fileName.isEmpty() )
        {
            QFile mFile( s_fileName );
            if ( !mFile.open( QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text ) )
            {
                qWarning() << "Error opening the logfile : " << s_fileName << " for writing.";
                qWarning() << logLine;
            }
            else
            {
                QTextStream out( &mFile );
                out << logLine << '\n';
                mFile.close();
            }
        }
        else
        {
            qDebug() << logLine;
        }
    }
}

}   /* End namespace components   */
}   /* End namespace osdev        */