You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

749 lines
21 KiB

  1. // Module: LOG4CPLUS
  2. // File: configurator.cxx
  3. // Created: 3/2003
  4. // Author: Tad E. Smith
  5. //
  6. //
  7. // Copyright 2003-2010 Tad E. Smith
  8. //
  9. // Licensed under the Apache License, Version 2.0 (the "License");
  10. // you may not use this file except in compliance with the License.
  11. // You may obtain a copy of the License at
  12. //
  13. // http://www.apache.org/licenses/LICENSE-2.0
  14. //
  15. // Unless required by applicable law or agreed to in writing, software
  16. // distributed under the License is distributed on an "AS IS" BASIS,
  17. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. // See the License for the specific language governing permissions and
  19. // limitations under the License.
  20. #include <log4cplus/configurator.h>
  21. #include <log4cplus/hierarchylocker.h>
  22. #include <log4cplus/hierarchy.h>
  23. #include <log4cplus/helpers/loglog.h>
  24. #include <log4cplus/helpers/sleep.h>
  25. #include <log4cplus/helpers/stringhelper.h>
  26. #include <log4cplus/helpers/property.h>
  27. #include <log4cplus/helpers/timehelper.h>
  28. #include <log4cplus/helpers/fileinfo.h>
  29. #include <log4cplus/thread/threads.h>
  30. #include <log4cplus/thread/syncprims-pub-impl.h>
  31. #include <log4cplus/spi/factory.h>
  32. #include <log4cplus/spi/loggerimpl.h>
  33. #include <log4cplus/internal/env.h>
  34. #ifdef LOG4CPLUS_HAVE_SYS_TYPES_H
  35. #include <sys/types.h>
  36. #endif
  37. #ifdef LOG4CPLUS_HAVE_SYS_STAT_H
  38. #include <sys/stat.h>
  39. #endif
  40. #ifdef LOG4CPLUS_HAVE_STDLIB_H
  41. #include <stdlib.h>
  42. #endif
  43. #if defined (_WIN32)
  44. #include <tchar.h>
  45. #endif
  46. #include <algorithm>
  47. #include <cstdlib>
  48. #include <iterator>
  49. #include <sstream>
  50. #include <functional>
  51. namespace log4cplus
  52. {
  53. void initializeLog4cplus();
  54. namespace
  55. {
  56. static tchar const DELIM_START[] = LOG4CPLUS_TEXT("${");
  57. static tchar const DELIM_STOP[] = LOG4CPLUS_TEXT("}");
  58. static std::size_t const DELIM_START_LEN = 2;
  59. static std::size_t const DELIM_STOP_LEN = 1;
  60. /**
  61. * Perform variable substitution in string <code>val</code> from
  62. * environment variables.
  63. *
  64. * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
  65. *
  66. * <p>For example, if the System properties contains "key=value", then
  67. * the call
  68. * <pre>
  69. * string s;
  70. * substEnvironVars(s, "Value of key is ${key}.");
  71. * </pre>
  72. *
  73. * will set the variable <code>s</code> to "Value of key is value.".
  74. *
  75. * <p>If no value could be found for the specified key, then
  76. * substitution defaults to the empty string.
  77. *
  78. * <p>For example, if there is no environment variable "inexistentKey",
  79. * then the call
  80. *
  81. * <pre>
  82. * string s;
  83. * substEnvironVars(s, "Value of inexistentKey is [${inexistentKey}]");
  84. * </pre>
  85. * will set <code>s</code> to "Value of inexistentKey is []"
  86. *
  87. * @param val The string on which variable substitution is performed.
  88. * @param dest The result.
  89. */
  90. static
  91. bool
  92. substVars (tstring & dest, const tstring & val,
  93. helpers::Properties const & props, helpers::LogLog& loglog,
  94. unsigned flags)
  95. {
  96. tstring::size_type i = 0;
  97. tstring::size_type var_start, var_end;
  98. tstring pattern (val);
  99. tstring key;
  100. tstring replacement;
  101. bool changed = false;
  102. bool const empty_vars
  103. = !! (flags & PropertyConfigurator::fAllowEmptyVars);
  104. bool const shadow_env
  105. = !! (flags & PropertyConfigurator::fShadowEnvironment);
  106. bool const rec_exp
  107. = !! (flags & PropertyConfigurator::fRecursiveExpansion);
  108. while (true)
  109. {
  110. // Find opening paren of variable substitution.
  111. var_start = pattern.find(DELIM_START, i);
  112. if (var_start == tstring::npos)
  113. {
  114. dest = pattern;
  115. return changed;
  116. }
  117. // Find closing paren of variable substitution.
  118. var_end = pattern.find(DELIM_STOP, var_start);
  119. if (var_end == tstring::npos)
  120. {
  121. tostringstream buffer;
  122. buffer << '"' << pattern
  123. << "\" has no closing brace. "
  124. << "Opening brace at position " << var_start << ".";
  125. loglog.error(buffer.str());
  126. dest = val;
  127. return false;
  128. }
  129. key.assign (pattern, var_start + DELIM_START_LEN,
  130. var_end - (var_start + DELIM_START_LEN));
  131. replacement.clear ();
  132. if (shadow_env)
  133. replacement = props.getProperty (key);
  134. if (! shadow_env || (! empty_vars && replacement.empty ()))
  135. internal::get_env_var (replacement, key);
  136. if (empty_vars || ! replacement.empty ())
  137. {
  138. // Substitute the variable with its value in place.
  139. pattern.replace (var_start, var_end - var_start + DELIM_STOP_LEN,
  140. replacement);
  141. changed = true;
  142. if (rec_exp)
  143. // Retry expansion on the same spot.
  144. continue;
  145. else
  146. // Move beyond the just substituted part.
  147. i = var_start + replacement.size ();
  148. }
  149. else
  150. // Nothing has been subtituted, just move beyond the
  151. // unexpanded variable.
  152. i = var_end + DELIM_STOP_LEN;
  153. } // end while loop
  154. } // end substVars()
  155. //! Translates encoding in ProtpertyConfigurator::PCFlags
  156. //! to helpers::Properties::PFlags
  157. static
  158. unsigned
  159. pcflag_to_pflags_encoding (unsigned pcflags)
  160. {
  161. switch (pcflags
  162. & (PropertyConfigurator::fEncodingMask
  163. << PropertyConfigurator::fEncodingShift))
  164. {
  165. #if defined (LOG4CPLUS_HAVE_CODECVT_UTF8_FACET) && defined (UNICODE)
  166. case PropertyConfigurator::fUTF8:
  167. return helpers::Properties::fUTF8;
  168. #endif
  169. #if (defined (LOG4CPLUS_HAVE_CODECVT_UTF16_FACET) || defined (WIN32)) \
  170. && defined (UNICODE)
  171. case PropertyConfigurator::fUTF16:
  172. return helpers::Properties::fUTF16;
  173. #endif
  174. #if defined (LOG4CPLUS_HAVE_CODECVT_UTF32_FACET) && defined (UNICODE)
  175. case PropertyConfigurator::fUTF32:
  176. return helpers::Properties::fUTF32;
  177. #endif
  178. case PropertyConfigurator::fUnspecEncoding:;
  179. default:
  180. return 0;
  181. }
  182. }
  183. } // namespace
  184. //////////////////////////////////////////////////////////////////////////////
  185. // PropertyConfigurator ctor and dtor
  186. //////////////////////////////////////////////////////////////////////////////
  187. PropertyConfigurator::PropertyConfigurator(const tstring& propertyFile,
  188. Hierarchy& hier, unsigned f)
  189. : h(hier)
  190. , propertyFilename(propertyFile)
  191. , properties(propertyFile, pcflag_to_pflags_encoding (f))
  192. , flags (f)
  193. {
  194. init();
  195. }
  196. PropertyConfigurator::PropertyConfigurator(const helpers::Properties& props,
  197. Hierarchy& hier, unsigned f)
  198. : h(hier)
  199. , propertyFilename( LOG4CPLUS_TEXT("UNAVAILABLE") )
  200. , properties( props )
  201. , flags (f)
  202. {
  203. init();
  204. }
  205. PropertyConfigurator::PropertyConfigurator(tistream& propertyStream,
  206. Hierarchy& hier, unsigned f)
  207. : h(hier)
  208. , propertyFilename( LOG4CPLUS_TEXT("UNAVAILABLE") )
  209. , properties(propertyStream)
  210. , flags (f)
  211. {
  212. init();
  213. }
  214. void
  215. PropertyConfigurator::init()
  216. {
  217. replaceEnvironVariables();
  218. properties = properties.getPropertySubset( LOG4CPLUS_TEXT("log4cplus.") );
  219. }
  220. PropertyConfigurator::~PropertyConfigurator()
  221. {
  222. }
  223. //////////////////////////////////////////////////////////////////////////////
  224. // PropertyConfigurator static methods
  225. //////////////////////////////////////////////////////////////////////////////
  226. void
  227. PropertyConfigurator::doConfigure(const tstring& file, Hierarchy& h,
  228. unsigned flags)
  229. {
  230. PropertyConfigurator tmp(file, h, flags);
  231. tmp.configure();
  232. }
  233. //////////////////////////////////////////////////////////////////////////////
  234. // PropertyConfigurator public methods
  235. //////////////////////////////////////////////////////////////////////////////
  236. void
  237. PropertyConfigurator::configure()
  238. {
  239. // Configure log4cplus internals.
  240. bool internal_debugging = false;
  241. if (properties.getBool (internal_debugging, LOG4CPLUS_TEXT ("configDebug")))
  242. helpers::getLogLog ().setInternalDebugging (internal_debugging);
  243. bool quiet_mode = false;
  244. if (properties.getBool (quiet_mode, LOG4CPLUS_TEXT ("quietMode")))
  245. helpers::getLogLog ().setQuietMode (quiet_mode);
  246. bool disable_override = false;
  247. if (properties.getBool (disable_override,
  248. LOG4CPLUS_TEXT ("disableOverride")))
  249. initializeLog4cplus();
  250. configureAppenders();
  251. configureLoggers();
  252. configureAdditivity();
  253. if (disable_override)
  254. h.disable (Hierarchy::DISABLE_OVERRIDE);
  255. // Erase the appenders so that we are not artificially keeping them "alive".
  256. appenders.clear ();
  257. }
  258. helpers::Properties const &
  259. PropertyConfigurator::getProperties () const
  260. {
  261. return properties;
  262. }
  263. log4cplus::tstring const &
  264. PropertyConfigurator::getPropertyFilename () const
  265. {
  266. return propertyFilename;
  267. }
  268. //////////////////////////////////////////////////////////////////////////////
  269. // PropertyConfigurator protected methods
  270. //////////////////////////////////////////////////////////////////////////////
  271. void
  272. PropertyConfigurator::reconfigure()
  273. {
  274. properties = helpers::Properties(propertyFilename);
  275. init();
  276. configure();
  277. }
  278. void
  279. PropertyConfigurator::replaceEnvironVariables()
  280. {
  281. tstring val, subKey, subVal;
  282. std::vector<tstring> keys;
  283. bool const rec_exp
  284. = !! (flags & PropertyConfigurator::fRecursiveExpansion);
  285. bool changed;
  286. do
  287. {
  288. changed = false;
  289. properties.propertyNames().swap (keys);
  290. for (std::vector<tstring>::const_iterator it = keys.begin();
  291. it != keys.end(); ++it)
  292. {
  293. tstring const & key = *it;
  294. val = properties.getProperty(key);
  295. subKey.clear ();
  296. if (substVars(subKey, key, properties, helpers::getLogLog(), flags))
  297. {
  298. properties.removeProperty(key);
  299. properties.setProperty(subKey, val);
  300. changed = true;
  301. }
  302. subVal.clear ();
  303. if (substVars(subVal, val, properties, helpers::getLogLog(), flags))
  304. {
  305. properties.setProperty(subKey, subVal);
  306. changed = true;
  307. }
  308. }
  309. }
  310. while (changed && rec_exp);
  311. }
  312. void
  313. PropertyConfigurator::configureLoggers()
  314. {
  315. if(properties.exists( LOG4CPLUS_TEXT("rootLogger") ))
  316. {
  317. Logger root = h.getRoot();
  318. configureLogger(root,
  319. properties.getProperty(LOG4CPLUS_TEXT("rootLogger")));
  320. }
  321. helpers::Properties loggerProperties
  322. = properties.getPropertySubset(LOG4CPLUS_TEXT("logger."));
  323. std::vector<tstring> loggers = loggerProperties.propertyNames();
  324. for(std::vector<tstring>::iterator it=loggers.begin(); it!=loggers.end();
  325. ++it)
  326. {
  327. Logger log = getLogger(*it);
  328. configureLogger(log, loggerProperties.getProperty(*it));
  329. }
  330. }
  331. void
  332. PropertyConfigurator::configureLogger(Logger logger, const tstring& config)
  333. {
  334. // Remove all spaces from config
  335. tstring configString;
  336. std::remove_copy_if(config.begin(), config.end(),
  337. std::back_inserter (configString),
  338. std::bind1st(std::equal_to<tchar>(), LOG4CPLUS_TEXT(' ')));
  339. // "Tokenize" configString
  340. std::vector<tstring> tokens;
  341. helpers::tokenize(configString, LOG4CPLUS_TEXT(','),
  342. std::back_insert_iterator<std::vector<tstring> >(tokens));
  343. if (tokens.empty ())
  344. {
  345. helpers::getLogLog().error(
  346. LOG4CPLUS_TEXT("PropertyConfigurator::configureLogger()")
  347. LOG4CPLUS_TEXT("- Invalid config string(Logger = ")
  348. + logger.getName()
  349. + LOG4CPLUS_TEXT("): \"")
  350. + config
  351. + LOG4CPLUS_TEXT("\""));
  352. return;
  353. }
  354. // Set the loglevel
  355. tstring const & loglevel = tokens[0];
  356. if (loglevel != LOG4CPLUS_TEXT("INHERITED"))
  357. logger.setLogLevel( getLogLevelManager().fromString(loglevel) );
  358. else
  359. logger.setLogLevel (NOT_SET_LOG_LEVEL);
  360. // Remove all existing appenders first so that we do not duplicate output.
  361. logger.removeAllAppenders ();
  362. // Set the Appenders
  363. for(std::vector<tstring>::size_type j=1; j<tokens.size(); ++j)
  364. {
  365. AppenderMap::iterator appenderIt = appenders.find(tokens[j]);
  366. if (appenderIt == appenders.end())
  367. {
  368. helpers::getLogLog().error(
  369. LOG4CPLUS_TEXT("PropertyConfigurator::configureLogger()")
  370. LOG4CPLUS_TEXT("- Invalid appender: ")
  371. + tokens[j]);
  372. continue;
  373. }
  374. addAppender(logger, appenderIt->second);
  375. }
  376. }
  377. void
  378. PropertyConfigurator::configureAppenders()
  379. {
  380. helpers::Properties appenderProperties =
  381. properties.getPropertySubset(LOG4CPLUS_TEXT("appender."));
  382. std::vector<tstring> appendersProps = appenderProperties.propertyNames();
  383. tstring factoryName;
  384. for(std::vector<tstring>::iterator it=appendersProps.begin();
  385. it != appendersProps.end(); ++it)
  386. {
  387. if( it->find( LOG4CPLUS_TEXT('.') ) == tstring::npos )
  388. {
  389. factoryName = appenderProperties.getProperty(*it);
  390. spi::AppenderFactory* factory
  391. = spi::getAppenderFactoryRegistry().get(factoryName);
  392. if (! factory)
  393. {
  394. tstring err =
  395. LOG4CPLUS_TEXT("PropertyConfigurator::configureAppenders()")
  396. LOG4CPLUS_TEXT("- Cannot find AppenderFactory: ");
  397. helpers::getLogLog().error(err + factoryName);
  398. continue;
  399. }
  400. helpers::Properties props_subset
  401. = appenderProperties.getPropertySubset((*it)
  402. + LOG4CPLUS_TEXT("."));
  403. try
  404. {
  405. SharedAppenderPtr appender
  406. = factory->createObject(props_subset);
  407. if (! appender)
  408. {
  409. tstring err =
  410. LOG4CPLUS_TEXT("PropertyConfigurator::")
  411. LOG4CPLUS_TEXT("configureAppenders()")
  412. LOG4CPLUS_TEXT("- Failed to create appender: ");
  413. helpers::getLogLog().error(err + *it);
  414. }
  415. else
  416. {
  417. appender->setName(*it);
  418. appenders[*it] = appender;
  419. }
  420. }
  421. catch(std::exception const & e)
  422. {
  423. tstring err =
  424. LOG4CPLUS_TEXT("PropertyConfigurator::")
  425. LOG4CPLUS_TEXT("configureAppenders()")
  426. LOG4CPLUS_TEXT("- Error while creating Appender: ");
  427. helpers::getLogLog().error(err + LOG4CPLUS_C_STR_TO_TSTRING(e.what()));
  428. }
  429. }
  430. } // end for loop
  431. }
  432. void
  433. PropertyConfigurator::configureAdditivity()
  434. {
  435. helpers::Properties additivityProperties =
  436. properties.getPropertySubset(LOG4CPLUS_TEXT("additivity."));
  437. std::vector<tstring> additivitysProps
  438. = additivityProperties.propertyNames();
  439. for(std::vector<tstring>::const_iterator it = additivitysProps.begin();
  440. it != additivitysProps.end(); ++it)
  441. {
  442. Logger logger = getLogger(*it);
  443. bool additivity;
  444. if (additivityProperties.getBool (additivity, *it))
  445. logger.setAdditivity (additivity);
  446. }
  447. }
  448. Logger
  449. PropertyConfigurator::getLogger(const tstring& name)
  450. {
  451. return h.getInstance(name);
  452. }
  453. void
  454. PropertyConfigurator::addAppender(Logger &logger, SharedAppenderPtr& appender)
  455. {
  456. logger.addAppender(appender);
  457. }
  458. //////////////////////////////////////////////////////////////////////////////
  459. // BasicConfigurator ctor and dtor
  460. //////////////////////////////////////////////////////////////////////////////
  461. log4cplus::tstring DISABLE_OVERRIDE_KEY (
  462. LOG4CPLUS_TEXT ("log4cplus.disableOverride"));
  463. BasicConfigurator::BasicConfigurator(Hierarchy& hier, bool logToStdErr)
  464. : PropertyConfigurator( LOG4CPLUS_TEXT(""), hier )
  465. {
  466. properties.setProperty(LOG4CPLUS_TEXT("rootLogger"),
  467. LOG4CPLUS_TEXT("DEBUG, STDOUT"));
  468. properties.setProperty(LOG4CPLUS_TEXT("appender.STDOUT"),
  469. LOG4CPLUS_TEXT("log4cplus::ConsoleAppender"));
  470. properties.setProperty(LOG4CPLUS_TEXT("appender.STDOUT.logToStdErr"),
  471. logToStdErr ? LOG4CPLUS_TEXT("1")
  472. : LOG4CPLUS_TEXT("0"));
  473. }
  474. BasicConfigurator::~BasicConfigurator()
  475. {
  476. }
  477. //////////////////////////////////////////////////////////////////////////////
  478. // BasicConfigurator static methods
  479. //////////////////////////////////////////////////////////////////////////////
  480. void
  481. BasicConfigurator::doConfigure(Hierarchy& h, bool logToStdErr)
  482. {
  483. BasicConfigurator tmp(h, logToStdErr);
  484. tmp.configure();
  485. }
  486. #if !defined(LOG4CPLUS_SINGLE_THREADED)
  487. //////////////////////////////////////////////////////////////////////////////
  488. // ConfigurationWatchDogThread implementation
  489. //////////////////////////////////////////////////////////////////////////////
  490. class ConfigurationWatchDogThread
  491. : public thread::AbstractThread,
  492. public PropertyConfigurator
  493. {
  494. public:
  495. ConfigurationWatchDogThread(const tstring& file, unsigned int millis)
  496. : PropertyConfigurator(file)
  497. , waitMillis(millis < 1000 ? 1000 : millis)
  498. , shouldTerminate(false)
  499. , lock(NULL)
  500. {
  501. lastFileInfo.mtime = helpers::Time::gettimeofday ();
  502. lastFileInfo.size = 0;
  503. lastFileInfo.is_link = false;
  504. updateLastModInfo();
  505. }
  506. virtual ~ConfigurationWatchDogThread ()
  507. { }
  508. void terminate ()
  509. {
  510. shouldTerminate.signal ();
  511. join ();
  512. }
  513. protected:
  514. virtual void run();
  515. virtual Logger getLogger(const tstring& name);
  516. virtual void addAppender(Logger &logger, SharedAppenderPtr& appender);
  517. bool checkForFileModification();
  518. void updateLastModInfo();
  519. private:
  520. ConfigurationWatchDogThread (ConfigurationWatchDogThread const &);
  521. ConfigurationWatchDogThread & operator = (
  522. ConfigurationWatchDogThread const &);
  523. unsigned int const waitMillis;
  524. thread::ManualResetEvent shouldTerminate;
  525. helpers::FileInfo lastFileInfo;
  526. HierarchyLocker* lock;
  527. };
  528. void
  529. ConfigurationWatchDogThread::run()
  530. {
  531. while (! shouldTerminate.timed_wait (waitMillis))
  532. {
  533. bool modified = checkForFileModification();
  534. if(modified) {
  535. // Lock the Hierarchy
  536. HierarchyLocker theLock(h);
  537. lock = &theLock;
  538. // reconfigure the Hierarchy
  539. theLock.resetConfiguration();
  540. reconfigure();
  541. updateLastModInfo();
  542. // release the lock
  543. lock = NULL;
  544. }
  545. }
  546. }
  547. Logger
  548. ConfigurationWatchDogThread::getLogger(const tstring& name)
  549. {
  550. if(lock)
  551. return lock->getInstance(name);
  552. else
  553. return PropertyConfigurator::getLogger(name);
  554. }
  555. void
  556. ConfigurationWatchDogThread::addAppender(Logger& logger,
  557. SharedAppenderPtr& appender)
  558. {
  559. if(lock)
  560. lock->addAppender(logger, appender);
  561. else
  562. PropertyConfigurator::addAppender(logger, appender);
  563. }
  564. bool
  565. ConfigurationWatchDogThread::checkForFileModification()
  566. {
  567. helpers::FileInfo fi;
  568. if (helpers::getFileInfo (&fi, propertyFilename) != 0)
  569. return false;
  570. bool modified = fi.mtime > lastFileInfo.mtime
  571. || fi.size != lastFileInfo.size;
  572. #if defined(LOG4CPLUS_HAVE_LSTAT)
  573. if (!modified && fi.is_link)
  574. {
  575. struct stat fileStatus;
  576. if (lstat(LOG4CPLUS_TSTRING_TO_STRING(propertyFilename).c_str(),
  577. &fileStatus) == -1)
  578. return false;
  579. helpers::Time linkModTime(fileStatus.st_mtime);
  580. modified = (linkModTime > fi.mtime);
  581. }
  582. #endif
  583. return modified;
  584. }
  585. void
  586. ConfigurationWatchDogThread::updateLastModInfo()
  587. {
  588. helpers::FileInfo fi;
  589. if (helpers::getFileInfo (&fi, propertyFilename) == 0)
  590. lastFileInfo = fi;
  591. }
  592. //////////////////////////////////////////////////////////////////////////////
  593. // PropertyConfiguratorWatchDog ctor and dtor
  594. //////////////////////////////////////////////////////////////////////////////
  595. ConfigureAndWatchThread::ConfigureAndWatchThread(const tstring& file,
  596. unsigned int millis)
  597. : watchDogThread(0)
  598. {
  599. watchDogThread = new ConfigurationWatchDogThread(file, millis);
  600. watchDogThread->addReference ();
  601. watchDogThread->configure();
  602. watchDogThread->start();
  603. }
  604. ConfigureAndWatchThread::~ConfigureAndWatchThread()
  605. {
  606. if (watchDogThread)
  607. {
  608. watchDogThread->terminate();
  609. watchDogThread->removeReference ();
  610. }
  611. }
  612. #endif
  613. } // namespace log4cplus