log.cpp 7.56 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        */