propertyconfigurator.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. /******************************************************************************
  2. *
  3. * package: Logging
  4. * file: propertyconfigurator.cpp
  5. * created: September 2007
  6. * author: Martin Heinrich
  7. *
  8. *
  9. * changes Feb 2009, Martin Heinrich
  10. * - Fixed VS 2008 unreferenced formal parameter warning by using
  11. * Q_UNUSED in operator<<.
  12. *
  13. *
  14. * Copyright 2007 - 2009 Martin Heinrich
  15. *
  16. * Licensed under the Apache License, Version 2.0 (the "License");
  17. * you may not use this file except in compliance with the License.
  18. * You may obtain a copy of the License at
  19. *
  20. * http://www.apache.org/licenses/LICENSE-2.0
  21. *
  22. * Unless required by applicable law or agreed to in writing, software
  23. * distributed under the License is distributed on an "AS IS" BASIS,
  24. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  25. * See the License for the specific language governing permissions and
  26. * limitations under the License.
  27. *
  28. ******************************************************************************/
  29. /******************************************************************************
  30. * Dependencies
  31. ******************************************************************************/
  32. #include "log4qt/propertyconfigurator.h"
  33. #include <QtCore/QDebug>
  34. #include <QtCore/QFile>
  35. #include "log4qt/helpers/configuratorhelper.h"
  36. #include "log4qt/helpers/factory.h"
  37. #include "log4qt/helpers/optionconverter.h"
  38. #include "log4qt/helpers/properties.h"
  39. #include "log4qt/appender.h"
  40. #include "log4qt/layout.h"
  41. #include "log4qt/logger.h"
  42. #include "log4qt/logmanager.h"
  43. #include "log4qt/loggerrepository.h"
  44. #include "log4qt/varia/listappender.h"
  45. namespace Log4Qt
  46. {
  47. /**************************************************************************
  48. * Declarations
  49. **************************************************************************/
  50. /**************************************************************************
  51. * C helper functions
  52. **************************************************************************/
  53. LOG4QT_DECLARE_STATIC_LOGGER(logger, Log4Qt::PropertyConfigurator)
  54. /**************************************************************************
  55. * Class implementation: PropertyConfigurator
  56. **************************************************************************/
  57. bool PropertyConfigurator::doConfigure(const Properties &rProperties,
  58. LoggerRepository *pLoggerRepository)
  59. {
  60. startCaptureErrors();
  61. configureFromProperties(rProperties, pLoggerRepository);
  62. return stopCaptureErrors();
  63. }
  64. bool PropertyConfigurator::doConfigure(const QString &rConfigFileName,
  65. LoggerRepository *pLoggerRepository)
  66. {
  67. startCaptureErrors();
  68. configureFromFile(rConfigFileName, pLoggerRepository);
  69. return stopCaptureErrors();
  70. }
  71. bool PropertyConfigurator::doConfigure(const QSettings &rSettings,
  72. LoggerRepository *pLoggerRepository)
  73. {
  74. startCaptureErrors();
  75. configureFromSettings(rSettings, pLoggerRepository);
  76. return stopCaptureErrors();
  77. }
  78. bool PropertyConfigurator::configure(const Properties &rProperties)
  79. {
  80. PropertyConfigurator configurator;
  81. return configurator.doConfigure(rProperties);
  82. }
  83. bool PropertyConfigurator::configure(const QString &rConfigFilename)
  84. {
  85. PropertyConfigurator configurator;
  86. return configurator.doConfigure(rConfigFilename);
  87. }
  88. bool PropertyConfigurator::configure(const QSettings &rSettings)
  89. {
  90. PropertyConfigurator configurator;
  91. return configurator.doConfigure(rSettings);
  92. }
  93. bool PropertyConfigurator::configureAndWatch(const QString &rConfigFileName)
  94. {
  95. // Stop an existing watch to avoid a possible concurrent configuration
  96. ConfiguratorHelper::setConfigurationFile();
  97. if (rConfigFileName.isEmpty())
  98. return true;
  99. PropertyConfigurator configurator;
  100. bool result = configurator.doConfigure(rConfigFileName);
  101. ConfiguratorHelper::setConfigurationFile(rConfigFileName, configure);
  102. return result;
  103. }
  104. void PropertyConfigurator::configureFromFile(const QString &rConfigFileName,
  105. LoggerRepository *pLoggerRepository)
  106. {
  107. QFile file(rConfigFileName);
  108. if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
  109. {
  110. LogError e = LOG4QT_ERROR(QT_TR_NOOP("Unable to open property file '%1'"),
  111. CONFIGURATOR_OPENING_FILE_ERROR,
  112. "Log4Qt::PropertyConfigurator");
  113. e << rConfigFileName;
  114. e.addCausingError(LogError(file.errorString(), file.error()));
  115. logger()->error(e);
  116. return;
  117. }
  118. Properties properties;
  119. properties.load(&file);
  120. if (file.error())
  121. {
  122. LogError e = LOG4QT_ERROR(QT_TR_NOOP("Unable to read property file '%1'"),
  123. CONFIGURATOR_READING_FILE_ERROR,
  124. "Log4Qt::PropertyConfigurator");
  125. e << rConfigFileName;
  126. e.addCausingError(LogError(file.errorString(), file.error()));
  127. logger()->error(e);
  128. return;
  129. }
  130. configureFromProperties(properties, pLoggerRepository);
  131. }
  132. void PropertyConfigurator::configureFromProperties(const Properties &rProperties,
  133. LoggerRepository *pLoggerRepository)
  134. {
  135. if (!pLoggerRepository)
  136. pLoggerRepository = LogManager::loggerRepository();
  137. configureGlobalSettings(rProperties, pLoggerRepository);
  138. configureRootLogger(rProperties, pLoggerRepository);
  139. configureNonRootElements(rProperties, pLoggerRepository);
  140. mAppenderRegistry.clear();
  141. }
  142. void PropertyConfigurator::configureFromSettings(const QSettings &rSettings,
  143. LoggerRepository *pLoggerRepository)
  144. {
  145. Properties properties;
  146. properties.load(rSettings);
  147. configureFromProperties(properties, pLoggerRepository);
  148. }
  149. void PropertyConfigurator::configureGlobalSettings(const Properties &rProperties,
  150. LoggerRepository *pLoggerRepository) const
  151. {
  152. Q_ASSERT_X(pLoggerRepository, "PropertyConfigurator::configureGlobalSettings()", "pLoggerRepository must not be null.");
  153. const QLatin1String key_reset("log4j.reset");
  154. const QLatin1String key_debug("log4j.Debug");
  155. const QLatin1String key_config_debug("log4j.configDebug");
  156. const QLatin1String key_threshold("log4j.threshold");
  157. const QLatin1String key_handle_qt_messages("log4j.handleQtMessages");
  158. // Test each global setting and set it
  159. // - Reset: log4j.reset
  160. // - Debug: log4j.Debug, log4j.configDebug
  161. // - Threshold: log4j.threshold
  162. // - Handle Qt Messages: log4j.handleQtMessages
  163. // Reset
  164. QString value = rProperties.property(key_reset);
  165. if (!value.isEmpty() && OptionConverter::toBoolean(value, false))
  166. {
  167. // Use LogManager and not pLoggerRepository to reset internal
  168. // logging.
  169. LogManager::resetConfiguration();
  170. logger()->debug("Reset configuration");
  171. }
  172. // Debug
  173. value = rProperties.property(key_debug);
  174. if (value.isNull())
  175. {
  176. value = rProperties.property(key_config_debug);
  177. if (!value.isNull())
  178. logger()->warn("[%1] is deprecated. Use [%2] instead.", key_config_debug, key_debug);
  179. }
  180. if (!value.isNull())
  181. {
  182. // Don't use OptionConverter::toLevel(). Invalid level string is a valid setting
  183. bool ok;
  184. Level level = Level::fromString(value, &ok);
  185. if (!ok)
  186. level = Level::DEBUG_INT;
  187. LogManager::logLogger()->setLevel(level);
  188. logger()->debug("Set level for Log4Qt logging to %1",
  189. LogManager::logLogger()->level().toString());
  190. }
  191. // Threshold
  192. value = rProperties.property(key_threshold);
  193. if (!value.isNull())
  194. {
  195. pLoggerRepository->setThreshold(OptionConverter::toLevel(value, Level::ALL_INT));
  196. logger()->debug("Set threshold for LoggerRepository to %1",
  197. pLoggerRepository->threshold().toString());
  198. }
  199. // Handle Qt messages
  200. value = rProperties.property(key_handle_qt_messages);
  201. if (!value.isNull())
  202. {
  203. LogManager::setHandleQtMessages(OptionConverter::toBoolean(value, false));
  204. logger()->debug("Set handling of Qt messages LoggerRepository to %1",
  205. QVariant(LogManager::handleQtMessages()).toString());
  206. }
  207. }
  208. void PropertyConfigurator::configureNonRootElements(const Properties &rProperties,
  209. LoggerRepository *pLoggerRepository)
  210. {
  211. Q_ASSERT_X(pLoggerRepository, "PropertyConfigurator::configureNonRootElements()", "pLoggerRepository must not be null.");
  212. const QString logger_prefix = QLatin1String("log4j.logger.");
  213. const QString category_prefix = QLatin1String("log4j.category.");
  214. // Iterate through all entries:
  215. // - Test for the logger/category prefix
  216. // - Convert JAVA class names to C++ ones
  217. // - Parse logger data (Level, Appender)
  218. // - Parse logger additivity
  219. QStringList keys = rProperties.propertyNames();
  220. QString key;
  221. Q_FOREACH(key, keys)
  222. {
  223. QString java_name;
  224. if (key.startsWith(logger_prefix))
  225. java_name = key.mid(logger_prefix.length());
  226. else if (key.startsWith(category_prefix))
  227. java_name = key.mid(category_prefix.length());
  228. QString cpp_name = OptionConverter::classNameJavaToCpp(java_name);
  229. if (!java_name.isEmpty())
  230. {
  231. Logger *p_logger = pLoggerRepository->logger(cpp_name);
  232. QString value = OptionConverter::findAndSubst(rProperties, key);
  233. parseLogger(rProperties, p_logger, key, value);
  234. parseAdditivityForLogger(rProperties, p_logger, java_name);
  235. }
  236. }
  237. }
  238. void PropertyConfigurator::configureRootLogger(const Properties &rProperties,
  239. LoggerRepository *pLoggerRepository)
  240. {
  241. Q_ASSERT_X(pLoggerRepository, "PropertyConfigurator::configureRootLogger()", "pLoggerRepository must not be null.");
  242. const QLatin1String key_root_logger("log4j.rootLogger");
  243. const QLatin1String key_root_category("log4j.rootCategory");
  244. // - Test for the logger/category prefix
  245. // - Parse logger data for root logger
  246. QString key = key_root_logger;
  247. QString value = OptionConverter::findAndSubst(rProperties, key);
  248. if (value.isNull())
  249. {
  250. key = key_root_category;
  251. value = OptionConverter::findAndSubst(rProperties, key);
  252. if (!value.isNull())
  253. logger()->warn("[%1] is deprecated. Use [%2] instead.", key_root_category, key_root_logger);
  254. }
  255. if (value.isNull())
  256. logger()->debug("Could not find root logger information. Is this correct?");
  257. else
  258. parseLogger(rProperties, pLoggerRepository->rootLogger(), key, value);
  259. }
  260. void PropertyConfigurator::parseAdditivityForLogger(const Properties &rProperties,
  261. Logger *pLogger,
  262. const QString &rLog4jName) const
  263. {
  264. Q_ASSERT_X(pLogger, "parseAdditivityForLogger()", "pLogger must not be null.");
  265. const QLatin1String additivity_prefix("log4j.additivity.");
  266. // - Lookup additivity key for logger
  267. // - Set additivity, if specified
  268. QString key = additivity_prefix + rLog4jName;
  269. QString value = OptionConverter::findAndSubst(rProperties, key);
  270. logger()->debug("Parsing additivity for logger: key '%1', value '%2'", key, value);
  271. if (!value.isEmpty())
  272. {
  273. bool additivity = OptionConverter::toBoolean(value, true);
  274. logger()->debug("Setting additivity for logger '%1' to '%2'", pLogger->name(), QVariant(value).toString());
  275. pLogger->setAdditivity(additivity);
  276. }
  277. }
  278. LogObjectPtr<Appender> PropertyConfigurator::parseAppender(const Properties &rProperties,
  279. const QString &rName)
  280. {
  281. // - Test if appender has been parsed before
  282. // - Find appender key
  283. // - Create appender object
  284. // - Set layout, if required by appender
  285. // - Set properties
  286. // - Activate options
  287. // - Add appender to registry
  288. const QLatin1String appender_prefix("log4j.appender.");
  289. logger()->debug("Parsing appender named '%1'", rName);
  290. if (mAppenderRegistry.contains(rName))
  291. {
  292. logger()->debug("Appender '%1' was already parsed.", rName);
  293. return mAppenderRegistry.value(rName);
  294. }
  295. QString key = appender_prefix + rName;
  296. QString value = OptionConverter::findAndSubst(rProperties, key);
  297. if (value.isNull())
  298. {
  299. LogError e = LOG4QT_ERROR(QT_TR_NOOP("Missing appender definition for appender named '%1'"),
  300. CONFIGURATOR_MISSING_APPENDER_ERROR,
  301. "Log4Qt::PropertyConfigurator");
  302. e << rName;
  303. logger()->error(e);
  304. return 0;
  305. }
  306. LogObjectPtr<Appender> p_appender = Factory::createAppender(value);
  307. if (!p_appender)
  308. {
  309. LogError e = LOG4QT_ERROR(QT_TR_NOOP("Unable to create appender of class '%1' namd '%2'"),
  310. CONFIGURATOR_UNKNOWN_APPENDER_CLASS_ERROR,
  311. "Log4Qt::PropertyConfigurator");
  312. e << value << rName;
  313. logger()->error(e);
  314. return 0;
  315. }
  316. p_appender->setName(rName);
  317. if (p_appender->requiresLayout())
  318. {
  319. LogObjectPtr<Layout> p_layout = parseLayout(rProperties, key);
  320. if (p_layout)
  321. p_appender->setLayout(p_layout);
  322. else
  323. return 0;
  324. }
  325. QStringList exclusions;
  326. exclusions << QLatin1String("layout");
  327. setProperties(rProperties, key + QLatin1String("."), exclusions, p_appender);
  328. AppenderSkeleton *p_appenderskeleton = qobject_cast<AppenderSkeleton *>(p_appender);
  329. if (p_appenderskeleton)
  330. p_appenderskeleton->activateOptions();
  331. mAppenderRegistry.insert(rName, p_appender);
  332. return p_appender;
  333. }
  334. LogObjectPtr<Layout> PropertyConfigurator::parseLayout(const Properties &rProperties,
  335. const QString &rAppenderKey)
  336. {
  337. Q_ASSERT_X(!rAppenderKey.isEmpty(), "PropertyConfigurator::parseLayout()", "rAppenderKey must not be empty.");
  338. // - Find layout key
  339. // - Create layput object
  340. // - Set properties
  341. // - Activate options
  342. const QLatin1String layout_suffix(".layout");
  343. logger()->debug("Parsing layout for appender named '%1'", rAppenderKey);
  344. QString key = rAppenderKey + layout_suffix;
  345. QString value = OptionConverter::findAndSubst(rProperties, key);
  346. if (value.isNull())
  347. {
  348. LogError e = LOG4QT_ERROR(QT_TR_NOOP("Missing layout definition for appender '%1'"),
  349. CONFIGURATOR_MISSING_LAYOUT_ERROR,
  350. "Log4Qt::PropertyConfigurator");
  351. e << rAppenderKey;
  352. logger()->error(e);
  353. return 0;
  354. }
  355. LogObjectPtr<Layout> p_layout = Factory::createLayout(value);
  356. if (!p_layout)
  357. {
  358. LogError e = LOG4QT_ERROR(QT_TR_NOOP("Unable to create layoput of class '%1' requested by appender '%2'"),
  359. CONFIGURATOR_UNKNOWN_LAYOUT_CLASS_ERROR,
  360. "Log4Qt::PropertyConfigurator");
  361. e << value << rAppenderKey;
  362. logger()->error(e);
  363. return 0;
  364. }
  365. QStringList exclusions;
  366. setProperties(rProperties, key + QLatin1String("."), QStringList(), p_layout);
  367. p_layout->activateOptions();
  368. return p_layout;
  369. }
  370. void PropertyConfigurator::parseLogger(const Properties &rProperties,
  371. Logger *pLogger,
  372. const QString &rKey,
  373. const QString &rValue)
  374. {
  375. Q_ASSERT_X(pLogger, "PropertyConfigurator::parseLogger()", "pLogger must not be null.");
  376. Q_ASSERT_X(!rKey.isEmpty(), "PropertyConfigurator::parseLogger()", "rKey must not be empty.");
  377. const QLatin1String keyword_inherited("INHERITED");
  378. // - Split value on comma
  379. // - If level value, is specified
  380. // - Test for NULL and INHERITED
  381. // - Ensure root logger is not set to NULL
  382. // - Set level
  383. // - For each entry
  384. // - Create Appender
  385. logger()->debug("Parsing logger: key '%1', value '%2'", rKey, rValue);
  386. QStringList appenders = rValue.split(QLatin1Char(','));
  387. QStringListIterator i (appenders);
  388. // First entry is the level. There will be always one entry, even if the rValue is
  389. // empty or does not contain a comma.
  390. QString value = i.next().trimmed();
  391. if (!value.isEmpty())
  392. {
  393. Level level;
  394. if (value.compare(keyword_inherited,Qt::CaseInsensitive) == 0)
  395. level = Level::NULL_INT;
  396. else
  397. level = OptionConverter::toLevel(value, Level::DEBUG_INT);
  398. if (level == Level::NULL_INT && pLogger->name() == QString())
  399. logger()->warn("The root logger level cannot be set to NULL.");
  400. else
  401. {
  402. pLogger->setLevel(level);
  403. logger()->debug("Set level for logger '%1' to '%2'",
  404. pLogger->name(), pLogger->level().toString());
  405. }
  406. }
  407. pLogger->removeAllAppenders();
  408. while(i.hasNext())
  409. {
  410. value = i.next().trimmed();
  411. if(value.isEmpty())
  412. continue;
  413. LogObjectPtr<Appender> p_appender = parseAppender(rProperties, value);
  414. if (p_appender)
  415. pLogger->addAppender(p_appender);
  416. }
  417. }
  418. void PropertyConfigurator::setProperties(const Properties &rProperties,
  419. const QString &rPrefix,
  420. const QStringList &rExclusions,
  421. QObject *pObject)
  422. {
  423. Q_ASSERT_X(!rPrefix.isEmpty(), "PropertyConfigurator::setProperties()", "rPrefix must not be empty.");
  424. Q_ASSERT_X(pObject, "PropertyConfigurator::setProperties()", "pObject must not be null.");
  425. // Iterate through all entries:
  426. // - Test for prefix to determine, if setting is for object
  427. // - Skip empty property name
  428. // - Skip property names in exclusion list
  429. // - Set property on object
  430. logger()->debug("Setting properties for object of class '%1' from keys starting with '%2'",
  431. QLatin1String(pObject->metaObject()->className()),
  432. rPrefix);
  433. QStringList keys = rProperties.propertyNames();
  434. QString key;
  435. Q_FOREACH(key, keys)
  436. {
  437. if (!key.startsWith(rPrefix))
  438. continue;
  439. QString property = key.mid(rPrefix.length());
  440. if (property.isEmpty())
  441. continue;
  442. QStringList split_property = property.split(QLatin1Char('.'));
  443. if (rExclusions.contains(split_property.at(0), Qt::CaseInsensitive))
  444. continue;
  445. QString value = OptionConverter::findAndSubst(rProperties, key);
  446. Factory::setObjectProperty(pObject, property, value);
  447. }
  448. }
  449. void PropertyConfigurator::startCaptureErrors()
  450. {
  451. Q_ASSERT_X(!mpConfigureErrors, "PropertyConfigurator::startCaptureErrors()", "mpConfigureErrors must be empty.");
  452. mpConfigureErrors = new ListAppender;
  453. mpConfigureErrors->setName(QLatin1String("PropertyConfigurator"));
  454. mpConfigureErrors->setConfiguratorList(true);
  455. mpConfigureErrors->setThreshold(Level::ERROR_INT);
  456. LogManager::logLogger()->addAppender(mpConfigureErrors);
  457. }
  458. bool PropertyConfigurator::stopCaptureErrors()
  459. {
  460. Q_ASSERT_X(mpConfigureErrors, "PropertyConfigurator::stopCaptureErrors()", "mpConfigureErrors must not be empty.");
  461. LogManager::logLogger()->removeAppender(mpConfigureErrors);
  462. ConfiguratorHelper::setConfigureError(mpConfigureErrors->list());
  463. bool result = (mpConfigureErrors->list().count() == 0);
  464. mpConfigureErrors = 0;
  465. return result;
  466. }
  467. /**************************************************************************
  468. * Implementation: Operators, Helper
  469. **************************************************************************/
  470. #ifndef QT_NO_DEBUG_STREAM
  471. QDebug operator<<(QDebug debug,
  472. const PropertyConfigurator &rPropertyConfigurator)
  473. {
  474. Q_UNUSED(rPropertyConfigurator);
  475. debug.nospace() << "PropertyConfigurator("
  476. << ")";
  477. return debug.space();
  478. }
  479. #endif
  480. } // namespace Logging