<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Log4Qt 初始化過(guò)程

          共 22652字,需瀏覽 46分鐘

           ·

          2021-08-05 13:36

          星標(biāo)/置頂公眾號(hào)??,硬核文章第一時(shí)間送達(dá)


          1

          初始化過(guò)程


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


          • 環(huán)境變量 - LOG4QT_DEBUGLOG4QT_DEFAULTINITOVERRIDE、LOG4QT_CONFIGURATION

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

          • 默認(rèn)配置文件 - log4qt.properties


          的確,這些方式在使用上比較簡(jiǎn)單,但是你可曾想過(guò):


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

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

          • 為什么默認(rèn)的配置文件要是 log4qt.properties 呢?


          要搞明白這些問(wèn)題,就必須要了解 Log4Qt 的初始化過(guò)程。Log4Qt 的初始化分兩個(gè)階段進(jìn)行:


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

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


          為了更加清晰的理解這個(gè)過(guò)程,讓我們?cè)俅巫哌M(jìn)源碼!



          2

          階段


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


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


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


          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)境變量的值會(huì)被存儲(chǔ)在 mEnvironmentSettings(QHash)中。


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



          3

          階段


          LogManager 單例是在首次使用時(shí)創(chuàng)建的,這個(gè)創(chuàng)建通常由 Logger 對(duì)象的請(qǐng)求觸發(fā)。Logger::logger() 的調(diào)用被傳遞給 LogManager::logger(),在創(chuàng)建時(shí),LogManager 將創(chuàng)建一個(gè) Hierarchy 對(duì)象作為 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)建單例之后,首先會(huì)調(diào)用 LogManager::doConfigureLogLogger() 對(duì) logLogger() 進(jìn)行配置。Level <= INFO 的消息將使用 ConsoleAppender 寫入到 stdout,而 Level >= WARN 的消息則使用第二個(gè) ConsoleAppender 寫入到 stderr。


          日志級(jí)別是通過(guò) InitialisationHelper::setting()(key 為 Debug) 從系統(tǒng)環(huán)境或應(yīng)用程序設(shè)置中讀取的,如果找到一個(gè)級(jí)別值,但它不是有效的級(jí)別字符串,則使用 Level::DEBUG_INT。如果沒(méi)有找到級(jí)別字符串,則使用 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));
          }


          一旦配置了日志,就會(huì)調(diào)用 LogManager::welcome() 來(lái)輸出一些有用的內(nèi)部信息。當(dāng) static_logger() 的日志級(jí)別為 Debug 時(shí),會(huì)輸出程序啟動(dòng)時(shí)間、logLogger() 所使用的日志級(jí)別;而當(dāng)級(jí)別為 Trace 時(shí),則會(huì)輸出環(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() 來(lái)初始化包,該函數(shù)將使用 InitialisationHelper::setting() 測(cè)試系統(tǒng)環(huán)境和應(yīng)用程序設(shè)置中的 DefaultInitOverride 設(shè)置,如果該值存在并被設(shè)置為任何非 false 的值,初始化將會(huì)被中止。


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


          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》來(lái)理解這個(gè)過(guò)程,效果會(huì)更佳!


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


          往期推薦




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


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

          回復(fù)“入群”進(jìn)技術(shù)交流群,回復(fù)“1024”獲取海量學(xué)習(xí)資源。
          瀏覽 68
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产色婷婷综合 | 日本黄色视频网站在线观看 | 狠狠热视频 | 久久影音一区二区三区 | 免费黄网在线观看 |