view Utils/Log.cpp @ 1:0b3630a29ad8

Initial version based on previous repository. Project was ported to QT6 and in now cmake based.
author Ideenmodellierer <tiefenrauscher@web.de>
date Thu, 27 Nov 2025 18:40:28 +0100
parents
children
line wrap: on
line source

//////////////////////////////////////////////////////////////////////////////
/// \file   Log.cpp
/// \brief  Basic logging tool
/// \author JD Gascuel.
/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved.
/// $Id$
//////////////////////////////////////////////////////////////////////////////
//
// BSD 2-Clause License:
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////

#include "Utils/Log.h"

#include "Utils/Exception.h"
#include "Utils/LogConsole.h"
#include "Utils/LogFile.h"

#include "Utils/LogAppender.h"

#include <QDir>
#include <QStringList>
#include <QRegularExpression>

// 2013-08-30 jDG: Because of Linux compile,
// we need to keep some minimal Qt4.8 compatibility...
#if !defined(NO_WIDGETS) && QT_VERSION > 0x050100
#   include <QWindow>
#endif

// Will be automaticallt set more verbose by File or Console appender,
// if needed.
Log::Level Log::minLevel = Log::LEVEL_WARNING;
QString Log::_applicationPath;
QString Log::_applicationName;

//////////////////////////////////////////////////////////////////////////////

static QtMessageHandler forwarder = 0;
static void Qt5Error(QtMsgType type, const QMessageLogContext &ctx, const QString &msg)
{
    // Spurious error from Qt 5.4.1, when we DO NOT USE any SSL socket...
    if( ctx.category && strcmp(ctx.category, "qt.network.ssl") == 0 )
        return;

    switch(type) {
    case QtDebugMsg:    LogAction(Log::LEVEL_DEBUG,   ctx.file, ctx.line, ctx.function) << msg; break;
    case QtInfoMsg:     LogAction(Log::LEVEL_TRACE,   ctx.file, ctx.line, ctx.function) << msg; break;
    case QtWarningMsg:  LogAction(Log::LEVEL_WARNING, ctx.file, ctx.line, ctx.function) << msg; break;
    case QtCriticalMsg: LogAction(Log::LEVEL_ERROR,   ctx.file, ctx.line, ctx.function) << msg; break;
    case QtFatalMsg:    LogAction(Log::LEVEL_ERROR,   ctx.file, ctx.line, ctx.function) << msg; break;
    }
}

//////////////////////////////////////////////////////////////////////////////

void Log::init(int& argc, char* argv[])
{
    minLevel = Log::LEVEL_WARNING;

    //---- Get the current application name ----------------------------------
    if( argc > 0 && _applicationPath.isEmpty() )
    {
        _applicationPath = QDir::current().absoluteFilePath(
                    QString::fromLatin1(argv[0])
                .replace('\\', '/'));
        _applicationName = _applicationPath.section('/', -1);
        _applicationPath = _applicationPath.section('/', 0, -2);

#ifdef Q_OS_WIN32
        if( _applicationName.endsWith(".exe") )
            _applicationName = _applicationName.section('.', 0, -2);
#elif defined(Q_OS_MACX)
        if( _applicationPath.endsWith("/MacOS") )
            _applicationPath = _applicationPath.section('/', 0, -2);
#elif defined(Q_OS_LINUX)
        // Nothing special, yet.
#else
#       error Unknown OS not yet implemented
#endif

        // Pop "/build/kitName" tail added by the QtCreator build process:
        if( _applicationPath.section('/', -2, -2) == "build" )
            _applicationPath = _applicationPath.section('/', 0, -3);
        if( _applicationPath.section('/', -1, -1) == "build" )
            _applicationPath = _applicationPath.section('/', 0, -2);

        // Pop /debug or /release tail too (Unix, Visual):
     //   QRegExp tail("[.]?(debug|release)", Qt::CaseInsensitive);
        QRegularExpression tail(
            R"([.]?(debug|release))",
            QRegularExpression::CaseInsensitiveOption
            );

    //    if( tail.exactMatch(_applicationPath.section('/', -1, -1)) )
        QRegularExpressionMatch match =
            tail.match(_applicationPath.section('/', -1, -1));
        if (match.hasMatch())
            _applicationPath = _applicationPath.section('/', 0, -2);
        if( _applicationName.endsWith(" DEBUG", Qt::CaseInsensitive) )
            _applicationName = _applicationName.left(_applicationName.length()-6);

        // Pop "/bin/" tail added by the configured build process:
        if( _applicationPath.section('/', -1) == "bin" )
            _applicationPath = _applicationPath.section('/', 0, -2);

        if( _applicationName.isEmpty() )
            _applicationName = "log";
    }

    //---- Forward Qt's messages ---------------------------------------------
    forwarder = qInstallMessageHandler(Qt5Error);

    //---- Instanciate 2 appenders -------------------------------------------
    new LogConsole(argc, argv);
    new LogFile(argc, argv);
}

//////////////////////////////////////////////////////////////////////////////

void Log::close()
{
    LOG_TRACE("Closing logs.");
    foreach(LogAppender* i, LogAppender::list())
        delete i;
}

//////////////////////////////////////////////////////////////////////////////

Log::Log(Level level, const char *f, int line, const char *function)
: level(level),
  line(line),
  function(function)
{
    // Skip trailing "../" in file name (due to QtCreator build dir):
    while( f && f[0] == '.' && f[1] == '.' && (f[2] == '\\' || f[2] == '/') )
        f += 3;
    const_cast<QString&>(file) = QString::fromLatin1(f).replace('\\', '/');
}

LogAction::LogAction(Level level, const char *file, int line, const char *function)
: Log(level, file, line, function)
{}

//////////////////////////////////////////////////////////////////////////////
LogAction::~LogAction()
{
    LogAppender::all(*this);
}

//////////////////////////////////////////////////////////////////////////////
LogAction &LogAction::operator<<(char msg)
{
    if( message.length() < (LOG_MAX_MESSAGE_LENGTH-1) )
        message += QChar(msg);  // UTF-8 by default.
    return *this;
}

LogAction &LogAction::operator<<(unsigned char msg)
{
    if( message.length() < (LOG_MAX_MESSAGE_LENGTH-1) )
        message += QChar(msg);  // UTF-8 by default.
    return *this;
}

LogAction& LogAction::operator<<(const char* msg)
{
    if( message.length()+strlen(msg) < (LOG_MAX_MESSAGE_LENGTH) )
        message += QString::fromUtf8(msg);
    return *this;
}

//////////////////////////////////////////////////////////////////////////////
LogAction& LogAction::operator<<(const QString& msg)
{
    if( message.length()+msg.length() < (LOG_MAX_MESSAGE_LENGTH) )
        message += msg;
    return *this;
}

LogAction &LogAction::operator <<(const QStringList& list)
{
    *this << "{";
    for(int i=0; i<list.count(); ++i)
    {
        if( i > 0 )
            *this << ", ";
        *this << list[i];
    }
    *this << "}";

    return *this;
}


//////////////////////////////////////////////////////////////////////////////
LogAction& LogAction::operator<<(const QByteArray& msg)
{
    operator<<(QString::fromUtf8(msg));
    return *this;
}

//////////////////////////////////////////////////////////////////////////////
LogAction& LogAction::operator<<(double value)
{
    operator<<(QString::number(value));
    return *this;
}

//////////////////////////////////////////////////////////////////////////////
LogAction& LogAction::operator<<(int value)
{
    operator<<(QString::number(value));
    return *this;
}

LogAction& LogAction::operator<<(unsigned int value)
{
    operator<<(QString::number(value));
    return *this;
}

//////////////////////////////////////////////////////////////////////////////

LogAction &LogAction::operator <<(unsigned long value)
{
    operator<<(QString::number((qulonglong)value));
    return *this;
}

LogAction &LogAction::operator <<(long value)
{
    operator<<(QString::number((qlonglong)value));
    return *this;
}

//////////////////////////////////////////////////////////////////////////////

LogAction &LogAction::operator <<(unsigned long long value)
{
    operator<<(QString::number(value));
    return *this;
}

LogAction &LogAction::operator <<(long long value)
{
    operator<<(QString::number(value));
    return *this;
}

//////////////////////////////////////////////////////////////////////////////
LogAction &LogAction::operator <<(const void *ptr)
{
    operator<<("0x");
    operator<<(QString::number((qulonglong)ptr, 16));

    return *this;
}