1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Log4Qt 初始化過程

        共 22652字,需瀏覽 46分鐘

         ·

        2021-08-05 13:36

        星標/置頂公眾號??,硬核文章第一時間送達!


        1

        初始化過程


        在前面的章節(jié)中,我們分享了三種方式來配置 Log4Qt,它們分別是:


        • 環(huán)境變量 - LOG4QT_DEBUG、LOG4QT_DEFAULTINITOVERRIDELOG4QT_CONFIGURATION

        • 應(yīng)用程序設(shè)置 - QSettings

        • 默認配置文件 - log4qt.properties


        的確,這些方式在使用上比較簡單,但是你可曾想過:


        • 為什么環(huán)境變量會是 LOG4QT_DEBUG 及其它兩個?

        • 為什么 QSettings 要有 Log4Qt/Properties 分組?

        • 為什么默認的配置文件要是 log4qt.properties 呢?


        要搞明白這些問題,就必須要了解 Log4Qt 的初始化過程。Log4Qt 的初始化分兩個階段進行:


        • 階段一:在靜態(tài)初始化期間發(fā)生

        • 階段二:在創(chuàng)建 LogManager 單例時發(fā)生


        為了更加清晰的理解這個過程,讓我們再次走進源碼!



        2

        階段


        在靜態(tài)初始化期間,會創(chuàng)建 InitialisationHelper 單例。在構(gòu)造過程中:


        InitialisationHelper::InitialisationHelper() :
            mStartTime(QDateTime::currentDateTime().toMSecsSinceEpoch())
        {
            doRegisterTypes();
            doInitialiseEnvironmentSettings();
        }


        它會使用 Qt 類型系統(tǒng)注冊一些自定義類型:


        void InitialisationHelper::doRegisterTypes()
        {
            qRegisterMetaType<Log4Qt::LogError>("Log4Qt::LogError");
            qRegisterMetaType<Log4Qt::Level>("Log4Qt::Level");
            qRegisterMetaType<Log4Qt::LoggingEvent>("Log4Qt::LoggingEvent");

        #ifndef QT_NO_DATASTREAM
        #if QT_VERSION < 0x060000
            qRegisterMetaTypeStreamOperators<Log4Qt::LogError>("Log4Qt::LogError");
            qRegisterMetaTypeStreamOperators<Log4Qt::Level>("Log4Qt::Level");
            qRegisterMetaTypeStreamOperators<LoggingEvent>("Log4Qt::LoggingEvent");
        #endif
        #endif

        }


        并從系統(tǒng)環(huán)境中讀取所需的值:


        void InitialisationHelper::doInitialiseEnvironmentSettings()
        {
            // Is Process::systemEnvironment() safe to be used before a QCoreApplication
            // object has been created?

            QStringList setting_keys;
            setting_keys << QStringLiteral("Debug");
            setting_keys << QStringLiteral("DefaultInitOverride");
            setting_keys << QStringLiteral("Configuration");

            QHash<QString, QString> env_keys;
            for (const auto &entry : qAsConst(setting_keys))
                env_keys.insert(QStringLiteral("log4qt_").append(entry).toUpper(), entry);

            QStringList sys_env = QProcess::systemEnvironment();
            for (const auto &entry : qAsConst(sys_env))
            {
                int i = entry.indexOf(QLatin1Char('='));
                if (i == -1)
                    continue;
                QString key = entry.left(i);
                QString value = entry.mid(i + 1).trimmed();
                if (env_keys.contains(key))
                    mEnvironmentSettings.insert(env_keys.value(key), value);
            }
        }


        最終,環(huán)境變量的值會被存儲在 mEnvironmentSettings(QHash)中。


        注意:QHash 中的 key 去掉了前綴 log4qt_。例如,LOG4QT_DEBUG 對應(yīng)的 key 是 Debug



        3

        階段


        LogManager 單例是在首次使用時創(chuàng)建的,這個創(chuàng)建通常由 Logger 對象的請求觸發(fā)。Logger::logger() 的調(diào)用被傳遞給 LogManager::logger(),在創(chuàng)建時,LogManager 將創(chuàng)建一個 Hierarchy 對象作為 logger repository:


        LogManager::LogManager() :
        #if QT_VERSION < 0x050E00
            mObjectGuard(QMutex::Recursive), // Recursive for doStartup() to call doConfigureLogLogger()
        #endif
            mLoggerRepository(new Hierarchy()),
            mHandleQtMessages(false),
            mWatchThisFile(false),
            mQtMsgHandler(nullptr)
        {
        }


        LogManager *LogManager::instance()
        {
            // Do not use Q_GLOBAL_STATIC. The LogManager is rather expensive
            // to construct, an exit handler must be set and doStartup must be
            // called.

            if (!mInstance)
            {
                QMutexLocker locker(singleton_guard());
                if (!mInstance)
                {
                    mInstance = new LogManager;
                    atexit(shutdown);
                    mInstance->doConfigureLogLogger();
                    mInstance->welcome();
                    mInstance->doStartup();
                }
            }
            return mInstance;
        }


        在創(chuàng)建單例之后,首先會調(diào)用 LogManager::doConfigureLogLogger() 對 logLogger() 進行配置。Level <= INFO 的消息將使用 ConsoleAppender 寫入到 stdout,而 Level >= WARN 的消息則使用第二個 ConsoleAppender 寫入到 stderr。


        日志級別是通過 InitialisationHelper::setting()(key 為 Debug) 從系統(tǒng)環(huán)境或應(yīng)用程序設(shè)置中讀取的,如果找到一個級別值,但它不是有效的級別字符串,則使用 Level::DEBUG_INT。如果沒有找到級別字符串,則使用 Level::ERROR_INT


        void LogManager::doConfigureLogLogger()
        {
            QMutexLocker locker(&instance()->mObjectGuard);

            // Level
            QString value = InitialisationHelper::setting(QStringLiteral("Debug"),
                            QStringLiteral("ERROR"));
            logLogger()->setLevel(OptionConverter::toLevel(value, Level::DEBUG_INT));

            // Common layout
            LayoutSharedPtr p_layout(new TTCCLayout());
            p_layout->setName(QStringLiteral("LogLog TTCC"));
            static_cast<TTCCLayout *>(p_layout.data())->setContextPrinting(false);
            p_layout->activateOptions();

            // Common deny all filter
            FilterSharedPtr p_denyall(new DenyAllFilter());
            p_denyall->activateOptions();

            // ConsoleAppender on stdout for all events <= INFO
            ConsoleAppender *p_appender;
            p_appender = new ConsoleAppender(p_layout, ConsoleAppender::STDOUT_TARGET);
            auto *pFilterStdout = new LevelRangeFilter();
            pFilterStdout->setNext(p_denyall);
            pFilterStdout->setLevelMin(Level::NULL_INT);
            pFilterStdout->setLevelMax(Level::INFO_INT);
            pFilterStdout->activateOptions();
            p_appender->setName(QStringLiteral("LogLog stdout"));
            p_appender->addFilter(FilterSharedPtr(pFilterStdout));
            p_appender->activateOptions();
            logLogger()->addAppender(AppenderSharedPtr(p_appender));

            // ConsoleAppender on stderr for all events >= WARN
            p_appender = new ConsoleAppender(p_layout, ConsoleAppender::STDERR_TARGET);
            auto *pFilterStderr = new LevelRangeFilter();
            pFilterStderr->setNext(p_denyall);
            pFilterStderr->setLevelMin(Level::WARN_INT);
            pFilterStderr->setLevelMax(Level::OFF_INT);
            pFilterStderr->activateOptions();
            p_appender->setName(QStringLiteral("LogLog stderr"));
            p_appender->addFilter(FilterSharedPtr(pFilterStderr));
            p_appender->activateOptions();
            logLogger()->addAppender(AppenderSharedPtr(p_appender));
        }


        一旦配置了日志,就會調(diào)用 LogManager::welcome() 來輸出一些有用的內(nèi)部信息。當 static_logger() 的日志級別為 Debug 時,會輸出程序啟動時間、logLogger() 所使用的日志級別;而當級別為 Trace 時,則會輸出環(huán)境變量配置、應(yīng)用程序配置:


        void LogManager::welcome()
        {
            static_logger()->info(QStringLiteral("Initialising Log4Qt %1"),
                                  QStringLiteral(LOG4QT_VERSION_STR));

            // Debug: Info
            if (static_logger()->isDebugEnabled())
            {
                // Create a nice timestamp with UTC offset
                DateTime start_time = QDateTime::fromMSecsSinceEpoch(InitialisationHelper::startTime());
                QString offset;
                {
                    QDateTime utc = start_time.toUTC();
                    QDateTime local = start_time.toLocalTime();
                    QDateTime local_as_utc = QDateTime(local.date(), local.time(), Qt::UTC);
                    int min = utc.secsTo(local_as_utc) / 60;
                    if (min < 0)
                        offset += QLatin1Char('-');
                    else
                        offset += QLatin1Char('+');
                    min = abs(min);
                    offset += QString::number(min / 60).rightJustified(2, QLatin1Char('0'));
                    offset += QLatin1Char(':');
                    offset += QString::number(min % 60).rightJustified(2, QLatin1Char('0'));
                }
                static_logger()->debug(QStringLiteral("Program startup time is %1 (UTC%2)"),
                                       start_time.toString(QStringLiteral("ISO8601")),
                                       offset);
                static_logger()->debug(QStringLiteral("Internal logging uses the level %1"),
                                       logLogger()->level().toString());
            }

            // Trace: Dump settings
            if (static_logger()->isTraceEnabled())
            {
                static_logger()->trace(QStringLiteral("Settings from the system environment:"));
                auto settings = InitialisationHelper::environmentSettings();
                for (auto pos = std::begin(settings);pos != std::end(settings);++pos)
                    static_logger()->trace(QStringLiteral("    %1: '%2'"), pos.key(), pos.value());

                static_logger()->trace(QStringLiteral("Settings from the application settings:"));
                if (QCoreApplication::instance())
                {
                    const QLatin1String log4qt_group("Log4Qt");
                    const QLatin1String properties_group("Properties");
                    static_logger()->trace(QStringLiteral("    %1:"), log4qt_group);
                    QSettings s;
                    s.beginGroup(log4qt_group);
                    for (const auto &entry : s.childKeys())
                        static_logger()->trace(QStringLiteral("        %1: '%2'"),
                                               entry,
                                               s.value(entry).toString());
                    static_logger()->trace(QStringLiteral("    %1/%2:"), log4qt_group, properties_group);
                    s.beginGroup(properties_group);
                    for (const auto &entry : s.childKeys())
                        static_logger()->trace(QStringLiteral("        %1: '%2'"),
                                               entry,
                                               s.value(entry).toString());
                }
                else
                    static_logger()->trace(QStringLiteral("    QCoreApplication::instance() is not available"));
            }
        }


        最后,是調(diào)用 LogManager::doStartup() 來初始化包,該函數(shù)將使用 InitialisationHelper::setting() 測試系統(tǒng)環(huán)境和應(yīng)用程序設(shè)置中的 DefaultInitOverride 設(shè)置,如果該值存在并被設(shè)置為任何非 false 的值,初始化將會被中止。


        隨后是獲取 Configuration 的值,如果找到并且是一個有效的文件路徑,則將會使用該文件并通過 PropertyConfigurator::configure() 來配置日志。倘若 Configuration 不可用并且存在 QCoreApplication 對象,則應(yīng)用程序設(shè)置將針對組 Log4Qt/Properties 進行測試。如果該組存在,則使用 PropertyConfigurator::configure() 對日志進行配置。倘若配置文件和配置設(shè)置都沒有被找到,則會在當前工作目錄中搜索 log4qt.properties 文件。如果找到,則使用 PropertyConfigurator::configure() 進行配置:


        void LogManager::doStartup()
        {
            QMutexLocker locker(&instance()->mObjectGuard);

            // Override
            QString default_value = QStringLiteral("false");
            QString value = InitialisationHelper::setting(QStringLiteral("DefaultInitOverride"),
                            default_value);
            if (value != default_value)
            {
                static_logger()->debug(QStringLiteral("DefaultInitOverride is set. Aborting default initialisation"));
                return;
            }

            // Configuration using setting Configuration
            value = InitialisationHelper::setting(QStringLiteral("Configuration"));
            if (!value.isEmpty() && QFile::exists(value))
            {
                static_logger()->debug(QStringLiteral("Default initialisation configures from file '%1' specified by Configure"), value);
                PropertyConfigurator::configure(value);
                return;
            }

            const QString default_file(QStringLiteral("log4qt.properties"));
            QStringList filesToCheck;

            // Configuration using setting
            if (auto app = QCoreApplication::instance())
            {
                Q_UNUSED(app)
                const QLatin1String log4qt_group("Log4Qt");
                const QLatin1String properties_group("Properties");
                QSettings s;
                s.beginGroup(log4qt_group);
                if (s.childGroups().contains(properties_group))
                {
                    static_logger()->debug(QStringLiteral("Default initialisation configures from setting '%1/%2'"), log4qt_group, properties_group);
                    s.beginGroup(properties_group);
                    PropertyConfigurator::configure(s);
                    return;
                }

                // Configuration using executable file name + .log4qt.properties
                QString binConfigFile = QCoreApplication::applicationFilePath() + QLatin1Char('.') + default_file;

                filesToCheck << binConfigFile;
                if (binConfigFile.contains(QLatin1String(".exe."), Qt::CaseInsensitive))
                {
                    binConfigFile.replace(QLatin1String(".exe."), QLatin1String("."), Qt::CaseInsensitive);
                    filesToCheck << binConfigFile;
                }

                filesToCheck << QFileInfo(QCoreApplication::applicationFilePath()).path() + QLatin1Char('/') + default_file;
            }

            filesToCheck << default_file;

            for (const auto &configFileName: qAsConst(filesToCheck))
            {
                // Configuration using default file
                if (QFile::exists(configFileName))
                {
                    static_logger()->debug(QStringLiteral("Default initialisation configures from default file '%1'"), configFileName);
                    PropertyConfigurator::configure(configFileName);
                    if (mWatchThisFile)
                       ConfiguratorHelper::setConfigurationFile(configFileName, PropertyConfigurator::configure);
                    return;
                }
            }

            static_logger()->debug(QStringLiteral("Default initialisation leaves package unconfigured"));
        }


        建議:結(jié)合《使用環(huán)境變量配置 Log4Qt》 、《使用 QSettings 配置 Log4Qt》、《使用 log4qt.properties 配置 Log4Qt》來理解這個過程,效果會更佳!


        如果實在理解不了,就用流程圖把它畫出來,O(∩_∩)O哈哈~!


        往期推薦




        專輯 | 趣味設(shè)計模式
        專輯 | 音視頻開發(fā)
        專輯 | C++ 進階
        專輯 | 超硬核 Qt
        專輯 | 玩轉(zhuǎn) Linux
        專輯 | GitHub 開源推薦
        專輯 | 程序人生


        關(guān)注公眾「高效程序員」??,一起優(yōu)秀!

        回復“入群”進技術(shù)交流群,回復“1024”獲取海量學習資源。
        瀏覽 68
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美国产精品久久五月天一区二区 | Chinese肥熟乱伦BBW | 办公室秘书高h荡肉呻吟 | 性生活一级免费视频 | 天天综合视频 中文字幕 | 青娱乐亚洲高清在线视频 | 一区二区三区免费播放 | 日逼电影网站 | 欧美成人电影在线观看 | 五月天色婷婷在线 |