RollingFileAppender.cs 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778
  1. #region Apache License
  2. //
  3. // Licensed to the Apache Software Foundation (ASF) under one or more
  4. // contributor license agreements. See the NOTICE file distributed with
  5. // this work for additional information regarding copyright ownership.
  6. // The ASF licenses this file to you under the Apache License, Version 2.0
  7. // (the "License"); you may not use this file except in compliance with
  8. // the License. You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. #endregion
  19. using System;
  20. using System.Collections;
  21. using System.Globalization;
  22. using System.IO;
  23. using log4net.Util;
  24. using log4net.Core;
  25. using System.Threading;
  26. namespace log4net.Appender
  27. {
  28. #if CONFIRM_WIN32_FILE_SHAREMODES
  29. // The following sounds good, and I though it was the case, but after
  30. // further testing on Windows I have not been able to confirm it.
  31. /// On the Windows platform if another process has a write lock on the file
  32. /// that is to be deleted, but allows shared read access to the file then the
  33. /// file can be moved, but cannot be deleted. If the other process also allows
  34. /// shared delete access to the file then the file will be deleted once that
  35. /// process closes the file. If it is necessary to open the log file or any
  36. /// of the backup files outside of this appender for either read or
  37. /// write access please ensure that read and delete share modes are enabled.
  38. #endif
  39. /// <summary>
  40. /// Appender that rolls log files based on size or date or both.
  41. /// </summary>
  42. /// <remarks>
  43. /// <para>
  44. /// RollingFileAppender can roll log files based on size or date or both
  45. /// depending on the setting of the <see cref="RollingStyle"/> property.
  46. /// When set to <see cref="RollingMode.Size"/> the log file will be rolled
  47. /// once its size exceeds the <see cref="MaximumFileSize"/>.
  48. /// When set to <see cref="RollingMode.Date"/> the log file will be rolled
  49. /// once the date boundary specified in the <see cref="DatePattern"/> property
  50. /// is crossed.
  51. /// When set to <see cref="RollingMode.Composite"/> the log file will be
  52. /// rolled once the date boundary specified in the <see cref="DatePattern"/> property
  53. /// is crossed, but within a date boundary the file will also be rolled
  54. /// once its size exceeds the <see cref="MaximumFileSize"/>.
  55. /// When set to <see cref="RollingMode.Once"/> the log file will be rolled when
  56. /// the appender is configured. This effectively means that the log file can be
  57. /// rolled once per program execution.
  58. /// </para>
  59. /// <para>
  60. /// A of few additional optional features have been added:
  61. /// <list type="bullet">
  62. /// <item>Attach date pattern for current log file <see cref="StaticLogFileName"/></item>
  63. /// <item>Backup number increments for newer files <see cref="CountDirection"/></item>
  64. /// <item>Infinite number of backups by file size <see cref="MaxSizeRollBackups"/></item>
  65. /// </list>
  66. /// </para>
  67. ///
  68. /// <note>
  69. /// <para>
  70. /// For large or infinite numbers of backup files a <see cref="CountDirection"/>
  71. /// greater than zero is highly recommended, otherwise all the backup files need
  72. /// to be renamed each time a new backup is created.
  73. /// </para>
  74. /// <para>
  75. /// When Date/Time based rolling is used setting <see cref="StaticLogFileName"/>
  76. /// to <see langword="true"/> will reduce the number of file renamings to few or none.
  77. /// </para>
  78. /// </note>
  79. ///
  80. /// <note type="caution">
  81. /// <para>
  82. /// Changing <see cref="StaticLogFileName"/> or <see cref="CountDirection"/> without clearing
  83. /// the log file directory of backup files will cause unexpected and unwanted side effects.
  84. /// </para>
  85. /// </note>
  86. ///
  87. /// <para>
  88. /// If Date/Time based rolling is enabled this appender will attempt to roll existing files
  89. /// in the directory without a Date/Time tag based on the last write date of the base log file.
  90. /// The appender only rolls the log file when a message is logged. If Date/Time based rolling
  91. /// is enabled then the appender will not roll the log file at the Date/Time boundary but
  92. /// at the point when the next message is logged after the boundary has been crossed.
  93. /// </para>
  94. ///
  95. /// <para>
  96. /// The <see cref="RollingFileAppender"/> extends the <see cref="FileAppender"/> and
  97. /// has the same behavior when opening the log file.
  98. /// The appender will first try to open the file for writing when <see cref="ActivateOptions"/>
  99. /// is called. This will typically be during configuration.
  100. /// If the file cannot be opened for writing the appender will attempt
  101. /// to open the file again each time a message is logged to the appender.
  102. /// If the file cannot be opened for writing when a message is logged then
  103. /// the message will be discarded by this appender.
  104. /// </para>
  105. /// <para>
  106. /// When rolling a backup file necessitates deleting an older backup file the
  107. /// file to be deleted is moved to a temporary name before being deleted.
  108. /// </para>
  109. ///
  110. /// <note type="caution">
  111. /// <para>
  112. /// A maximum number of backup files when rolling on date/time boundaries is not supported.
  113. /// </para>
  114. /// </note>
  115. /// </remarks>
  116. /// <author>Nicko Cadell</author>
  117. /// <author>Gert Driesen</author>
  118. /// <author>Aspi Havewala</author>
  119. /// <author>Douglas de la Torre</author>
  120. /// <author>Edward Smit</author>
  121. public class RollingFileAppender : FileAppender
  122. {
  123. #region Public Enums
  124. /// <summary>
  125. /// Style of rolling to use
  126. /// </summary>
  127. /// <remarks>
  128. /// <para>
  129. /// Style of rolling to use
  130. /// </para>
  131. /// </remarks>
  132. public enum RollingMode
  133. {
  134. /// <summary>
  135. /// Roll files once per program execution
  136. /// </summary>
  137. /// <remarks>
  138. /// <para>
  139. /// Roll files once per program execution.
  140. /// Well really once each time this appender is
  141. /// configured.
  142. /// </para>
  143. /// <para>
  144. /// Setting this option also sets <c>AppendToFile</c> to
  145. /// <c>false</c> on the <c>RollingFileAppender</c>, otherwise
  146. /// this appender would just be a normal file appender.
  147. /// </para>
  148. /// </remarks>
  149. Once = 0,
  150. /// <summary>
  151. /// Roll files based only on the size of the file
  152. /// </summary>
  153. Size = 1,
  154. /// <summary>
  155. /// Roll files based only on the date
  156. /// </summary>
  157. Date = 2,
  158. /// <summary>
  159. /// Roll files based on both the size and date of the file
  160. /// </summary>
  161. Composite = 3
  162. }
  163. #endregion
  164. #region Protected Enums
  165. /// <summary>
  166. /// The code assumes that the following 'time' constants are in a increasing sequence.
  167. /// </summary>
  168. /// <remarks>
  169. /// <para>
  170. /// The code assumes that the following 'time' constants are in a increasing sequence.
  171. /// </para>
  172. /// </remarks>
  173. protected enum RollPoint
  174. {
  175. /// <summary>
  176. /// Roll the log not based on the date
  177. /// </summary>
  178. InvalidRollPoint =-1,
  179. /// <summary>
  180. /// Roll the log for each minute
  181. /// </summary>
  182. TopOfMinute = 0,
  183. /// <summary>
  184. /// Roll the log for each hour
  185. /// </summary>
  186. TopOfHour = 1,
  187. /// <summary>
  188. /// Roll the log twice a day (midday and midnight)
  189. /// </summary>
  190. HalfDay = 2,
  191. /// <summary>
  192. /// Roll the log each day (midnight)
  193. /// </summary>
  194. TopOfDay = 3,
  195. /// <summary>
  196. /// Roll the log each week
  197. /// </summary>
  198. TopOfWeek = 4,
  199. /// <summary>
  200. /// Roll the log each month
  201. /// </summary>
  202. TopOfMonth = 5
  203. }
  204. #endregion Protected Enums
  205. #region Public Instance Constructors
  206. /// <summary>
  207. /// Initializes a new instance of the <see cref="RollingFileAppender" /> class.
  208. /// </summary>
  209. /// <remarks>
  210. /// <para>
  211. /// Default constructor.
  212. /// </para>
  213. /// </remarks>
  214. public RollingFileAppender()
  215. {
  216. }
  217. /// <summary>
  218. /// Cleans up all resources used by this appender.
  219. /// </summary>
  220. ~RollingFileAppender()
  221. {
  222. #if !NETCF
  223. if (m_mutexForRolling != null)
  224. {
  225. #if NET_4_0 || MONO_4_0 || NETSTANDARD1_3
  226. m_mutexForRolling.Dispose();
  227. #else
  228. m_mutexForRolling.Close();
  229. #endif
  230. m_mutexForRolling = null;
  231. }
  232. #endif
  233. }
  234. #endregion Public Instance Constructors
  235. #region Public Instance Properties
  236. #if !NET_1_0 && !CLI_1_0 && !NETCF
  237. /// <summary>
  238. /// Gets or sets the strategy for determining the current date and time. The default
  239. /// implementation is to use LocalDateTime which internally calls through to DateTime.Now.
  240. /// DateTime.UtcNow may be used on frameworks newer than .NET 1.0 by specifying
  241. /// <see cref="RollingFileAppender.UniversalDateTime"/>.
  242. /// </summary>
  243. /// <value>
  244. /// An implementation of the <see cref="RollingFileAppender.IDateTime"/> interface which returns the current date and time.
  245. /// </value>
  246. /// <remarks>
  247. /// <para>
  248. /// Gets or sets the <see cref="RollingFileAppender.IDateTime"/> used to return the current date and time.
  249. /// </para>
  250. /// <para>
  251. /// There are two built strategies for determining the current date and time,
  252. /// <see cref="RollingFileAppender.LocalDateTime"/>
  253. /// and <see cref="RollingFileAppender.UniversalDateTime"/>.
  254. /// </para>
  255. /// <para>
  256. /// The default strategy is <see cref="RollingFileAppender.LocalDateTime"/>.
  257. /// </para>
  258. /// </remarks>
  259. #else
  260. /// <summary>
  261. /// Gets or sets the strategy for determining the current date and time. The default
  262. /// implementation is to use LocalDateTime which internally calls through to DateTime.Now.
  263. /// </summary>
  264. /// <value>
  265. /// An implementation of the <see cref="RollingFileAppender.IDateTime"/> interface which returns the current date and time.
  266. /// </value>
  267. /// <remarks>
  268. /// <para>
  269. /// Gets or sets the <see cref="RollingFileAppender.IDateTime"/> used to return the current date and time.
  270. /// </para>
  271. /// <para>
  272. /// The default strategy is <see cref="RollingFileAppender.LocalDateTime"/>.
  273. /// </para>
  274. /// </remarks>
  275. #endif
  276. public IDateTime DateTimeStrategy
  277. {
  278. get { return m_dateTime; }
  279. set { m_dateTime = value; }
  280. }
  281. /// <summary>
  282. /// Gets or sets the date pattern to be used for generating file names
  283. /// when rolling over on date.
  284. /// </summary>
  285. /// <value>
  286. /// The date pattern to be used for generating file names when rolling
  287. /// over on date.
  288. /// </value>
  289. /// <remarks>
  290. /// <para>
  291. /// Takes a string in the same format as expected by
  292. /// <see cref="log4net.DateFormatter.SimpleDateFormatter" />.
  293. /// </para>
  294. /// <para>
  295. /// This property determines the rollover schedule when rolling over
  296. /// on date.
  297. /// </para>
  298. /// </remarks>
  299. public string DatePattern
  300. {
  301. get { return m_datePattern; }
  302. set { m_datePattern = value; }
  303. }
  304. /// <summary>
  305. /// Gets or sets the maximum number of backup files that are kept before
  306. /// the oldest is erased.
  307. /// </summary>
  308. /// <value>
  309. /// The maximum number of backup files that are kept before the oldest is
  310. /// erased.
  311. /// </value>
  312. /// <remarks>
  313. /// <para>
  314. /// If set to zero, then there will be no backup files and the log file
  315. /// will be truncated when it reaches <see cref="MaxFileSize"/>.
  316. /// </para>
  317. /// <para>
  318. /// If a negative number is supplied then no deletions will be made. Note
  319. /// that this could result in very slow performance as a large number of
  320. /// files are rolled over unless <see cref="CountDirection"/> is used.
  321. /// </para>
  322. /// <para>
  323. /// The maximum applies to <b>each</b> time based group of files and
  324. /// <b>not</b> the total.
  325. /// </para>
  326. /// </remarks>
  327. public int MaxSizeRollBackups
  328. {
  329. get { return m_maxSizeRollBackups; }
  330. set { m_maxSizeRollBackups = value; }
  331. }
  332. /// <summary>
  333. /// Gets or sets the maximum size that the output file is allowed to reach
  334. /// before being rolled over to backup files.
  335. /// </summary>
  336. /// <value>
  337. /// The maximum size in bytes that the output file is allowed to reach before being
  338. /// rolled over to backup files.
  339. /// </value>
  340. /// <remarks>
  341. /// <para>
  342. /// This property is equivalent to <see cref="MaximumFileSize"/> except
  343. /// that it is required for differentiating the setter taking a
  344. /// <see cref="long"/> argument from the setter taking a <see cref="string"/>
  345. /// argument.
  346. /// </para>
  347. /// <para>
  348. /// The default maximum file size is 10MB (10*1024*1024).
  349. /// </para>
  350. /// </remarks>
  351. public long MaxFileSize
  352. {
  353. get { return m_maxFileSize; }
  354. set { m_maxFileSize = value; }
  355. }
  356. /// <summary>
  357. /// Gets or sets the maximum size that the output file is allowed to reach
  358. /// before being rolled over to backup files.
  359. /// </summary>
  360. /// <value>
  361. /// The maximum size that the output file is allowed to reach before being
  362. /// rolled over to backup files.
  363. /// </value>
  364. /// <remarks>
  365. /// <para>
  366. /// This property allows you to specify the maximum size with the
  367. /// suffixes "KB", "MB" or "GB" so that the size is interpreted being
  368. /// expressed respectively in kilobytes, megabytes or gigabytes.
  369. /// </para>
  370. /// <para>
  371. /// For example, the value "10KB" will be interpreted as 10240 bytes.
  372. /// </para>
  373. /// <para>
  374. /// The default maximum file size is 10MB.
  375. /// </para>
  376. /// <para>
  377. /// If you have the option to set the maximum file size programmatically
  378. /// consider using the <see cref="MaxFileSize"/> property instead as this
  379. /// allows you to set the size in bytes as a <see cref="Int64"/>.
  380. /// </para>
  381. /// </remarks>
  382. public string MaximumFileSize
  383. {
  384. get { return m_maxFileSize.ToString(NumberFormatInfo.InvariantInfo); }
  385. set { m_maxFileSize = OptionConverter.ToFileSize(value, m_maxFileSize + 1); }
  386. }
  387. /// <summary>
  388. /// Gets or sets the rolling file count direction.
  389. /// </summary>
  390. /// <value>
  391. /// The rolling file count direction.
  392. /// </value>
  393. /// <remarks>
  394. /// <para>
  395. /// Indicates if the current file is the lowest numbered file or the
  396. /// highest numbered file.
  397. /// </para>
  398. /// <para>
  399. /// By default newer files have lower numbers (<see cref="CountDirection" /> &lt; 0),
  400. /// i.e. log.1 is most recent, log.5 is the 5th backup, etc...
  401. /// </para>
  402. /// <para>
  403. /// <see cref="CountDirection" /> &gt;= 0 does the opposite i.e.
  404. /// log.1 is the first backup made, log.5 is the 5th backup made, etc.
  405. /// For infinite backups use <see cref="CountDirection" /> &gt;= 0 to reduce
  406. /// rollover costs.
  407. /// </para>
  408. /// <para>The default file count direction is -1.</para>
  409. /// </remarks>
  410. public int CountDirection
  411. {
  412. get { return m_countDirection; }
  413. set { m_countDirection = value; }
  414. }
  415. /// <summary>
  416. /// Gets or sets the rolling style.
  417. /// </summary>
  418. /// <value>The rolling style.</value>
  419. /// <remarks>
  420. /// <para>
  421. /// The default rolling style is <see cref="RollingMode.Composite" />.
  422. /// </para>
  423. /// <para>
  424. /// When set to <see cref="RollingMode.Once"/> this appender's
  425. /// <see cref="FileAppender.AppendToFile"/> property is set to <c>false</c>, otherwise
  426. /// the appender would append to a single file rather than rolling
  427. /// the file each time it is opened.
  428. /// </para>
  429. /// </remarks>
  430. public RollingMode RollingStyle
  431. {
  432. get { return m_rollingStyle; }
  433. set
  434. {
  435. m_rollingStyle = value;
  436. switch (m_rollingStyle)
  437. {
  438. case RollingMode.Once:
  439. m_rollDate = false;
  440. m_rollSize = false;
  441. this.AppendToFile = false;
  442. break;
  443. case RollingMode.Size:
  444. m_rollDate = false;
  445. m_rollSize = true;
  446. break;
  447. case RollingMode.Date:
  448. m_rollDate = true;
  449. m_rollSize = false;
  450. break;
  451. case RollingMode.Composite:
  452. m_rollDate = true;
  453. m_rollSize = true;
  454. break;
  455. }
  456. }
  457. }
  458. /// <summary>
  459. /// Gets or sets a value indicating whether to preserve the file name extension when rolling.
  460. /// </summary>
  461. /// <value>
  462. /// <c>true</c> if the file name extension should be preserved.
  463. /// </value>
  464. /// <remarks>
  465. /// <para>
  466. /// By default file.log is rolled to file.log.yyyy-MM-dd or file.log.curSizeRollBackup.
  467. /// However, under Windows the new file name will loose any program associations as the
  468. /// extension is changed. Optionally file.log can be renamed to file.yyyy-MM-dd.log or
  469. /// file.curSizeRollBackup.log to maintain any program associations.
  470. /// </para>
  471. /// </remarks>
  472. public bool PreserveLogFileNameExtension
  473. {
  474. get { return m_preserveLogFileNameExtension; }
  475. set { m_preserveLogFileNameExtension = value; }
  476. }
  477. /// <summary>
  478. /// Gets or sets a value indicating whether to always log to
  479. /// the same file.
  480. /// </summary>
  481. /// <value>
  482. /// <c>true</c> if always should be logged to the same file, otherwise <c>false</c>.
  483. /// </value>
  484. /// <remarks>
  485. /// <para>
  486. /// By default file.log is always the current file. Optionally
  487. /// file.log.yyyy-mm-dd for current formatted datePattern can by the currently
  488. /// logging file (or file.log.curSizeRollBackup or even
  489. /// file.log.yyyy-mm-dd.curSizeRollBackup).
  490. /// </para>
  491. /// <para>
  492. /// This will make time based rollovers with a large number of backups
  493. /// much faster as the appender it won't have to rename all the backups!
  494. /// </para>
  495. /// </remarks>
  496. public bool StaticLogFileName
  497. {
  498. get { return m_staticLogFileName; }
  499. set { m_staticLogFileName = value; }
  500. }
  501. #endregion Public Instance Properties
  502. #region Private Static Fields
  503. /// <summary>
  504. /// The fully qualified type of the RollingFileAppender class.
  505. /// </summary>
  506. /// <remarks>
  507. /// Used by the internal logger to record the Type of the
  508. /// log message.
  509. /// </remarks>
  510. private readonly static Type declaringType = typeof(RollingFileAppender);
  511. #endregion Private Static Fields
  512. #region Override implementation of FileAppender
  513. /// <summary>
  514. /// Sets the quiet writer being used.
  515. /// </summary>
  516. /// <remarks>
  517. /// This method can be overridden by sub classes.
  518. /// </remarks>
  519. /// <param name="writer">the writer to set</param>
  520. override protected void SetQWForFiles(TextWriter writer)
  521. {
  522. QuietWriter = new CountingQuietTextWriter(writer, ErrorHandler);
  523. }
  524. /// <summary>
  525. /// Write out a logging event.
  526. /// </summary>
  527. /// <param name="loggingEvent">the event to write to file.</param>
  528. /// <remarks>
  529. /// <para>
  530. /// Handles append time behavior for RollingFileAppender. This checks
  531. /// if a roll over either by date (checked first) or time (checked second)
  532. /// is need and then appends to the file last.
  533. /// </para>
  534. /// </remarks>
  535. override protected void Append(LoggingEvent loggingEvent)
  536. {
  537. AdjustFileBeforeAppend();
  538. base.Append(loggingEvent);
  539. }
  540. /// <summary>
  541. /// Write out an array of logging events.
  542. /// </summary>
  543. /// <param name="loggingEvents">the events to write to file.</param>
  544. /// <remarks>
  545. /// <para>
  546. /// Handles append time behavior for RollingFileAppender. This checks
  547. /// if a roll over either by date (checked first) or time (checked second)
  548. /// is need and then appends to the file last.
  549. /// </para>
  550. /// </remarks>
  551. override protected void Append(LoggingEvent[] loggingEvents)
  552. {
  553. AdjustFileBeforeAppend();
  554. base.Append(loggingEvents);
  555. }
  556. /// <summary>
  557. /// Performs any required rolling before outputting the next event
  558. /// </summary>
  559. /// <remarks>
  560. /// <para>
  561. /// Handles append time behavior for RollingFileAppender. This checks
  562. /// if a roll over either by date (checked first) or time (checked second)
  563. /// is need and then appends to the file last.
  564. /// </para>
  565. /// </remarks>
  566. virtual protected void AdjustFileBeforeAppend()
  567. {
  568. // reuse the file appenders locking model to lock the rolling
  569. #if !NETCF
  570. try
  571. {
  572. // if rolling should be locked, acquire the lock
  573. if (m_mutexForRolling != null)
  574. {
  575. m_mutexForRolling.WaitOne();
  576. }
  577. #endif
  578. if (m_rollDate)
  579. {
  580. DateTime n = m_dateTime.Now;
  581. if (n >= m_nextCheck)
  582. {
  583. m_now = n;
  584. m_nextCheck = NextCheckDate(m_now, m_rollPoint);
  585. RollOverTime(true);
  586. }
  587. }
  588. if (m_rollSize)
  589. {
  590. if ((File != null) && ((CountingQuietTextWriter)QuietWriter).Count >= m_maxFileSize)
  591. {
  592. RollOverSize();
  593. }
  594. }
  595. #if !NETCF
  596. }
  597. finally
  598. {
  599. // if rolling should be locked, release the lock
  600. if (m_mutexForRolling != null)
  601. {
  602. m_mutexForRolling.ReleaseMutex();
  603. }
  604. }
  605. #endif
  606. }
  607. /// <summary>
  608. /// Creates and opens the file for logging. If <see cref="StaticLogFileName"/>
  609. /// is false then the fully qualified name is determined and used.
  610. /// </summary>
  611. /// <param name="fileName">the name of the file to open</param>
  612. /// <param name="append">true to append to existing file</param>
  613. /// <remarks>
  614. /// <para>This method will ensure that the directory structure
  615. /// for the <paramref name="fileName"/> specified exists.</para>
  616. /// </remarks>
  617. override protected void OpenFile(string fileName, bool append)
  618. {
  619. lock(this)
  620. {
  621. fileName = GetNextOutputFileName(fileName);
  622. // Calculate the current size of the file
  623. long currentCount = 0;
  624. if (append)
  625. {
  626. using(SecurityContext.Impersonate(this))
  627. {
  628. if (System.IO.File.Exists(fileName))
  629. {
  630. currentCount = (new FileInfo(fileName)).Length;
  631. }
  632. }
  633. }
  634. else
  635. {
  636. if (LogLog.IsErrorEnabled)
  637. {
  638. // Internal check that the file is not being overwritten
  639. // If not Appending to an existing file we should have rolled the file out of the
  640. // way. Therefore we should not be over-writing an existing file.
  641. // The only exception is if we are not allowed to roll the existing file away.
  642. if (m_maxSizeRollBackups != 0 && FileExists(fileName))
  643. {
  644. LogLog.Error(declaringType, "RollingFileAppender: INTERNAL ERROR. Append is False but OutputFile ["+fileName+"] already exists.");
  645. }
  646. }
  647. }
  648. if (!m_staticLogFileName)
  649. {
  650. m_scheduledFilename = fileName;
  651. }
  652. // Open the file (call the base class to do it)
  653. base.OpenFile(fileName, append);
  654. // Set the file size onto the counting writer
  655. ((CountingQuietTextWriter)QuietWriter).Count = currentCount;
  656. }
  657. }
  658. /// <summary>
  659. /// Get the current output file name
  660. /// </summary>
  661. /// <param name="fileName">the base file name</param>
  662. /// <returns>the output file name</returns>
  663. /// <remarks>
  664. /// The output file name is based on the base fileName specified.
  665. /// If <see cref="StaticLogFileName"/> is set then the output
  666. /// file name is the same as the base file passed in. Otherwise
  667. /// the output file depends on the date pattern, on the count
  668. /// direction or both.
  669. /// </remarks>
  670. protected string GetNextOutputFileName(string fileName)
  671. {
  672. if (!m_staticLogFileName)
  673. {
  674. fileName = fileName.Trim();
  675. if (m_rollDate)
  676. {
  677. fileName = CombinePath(fileName, m_now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo));
  678. }
  679. if (m_countDirection >= 0)
  680. {
  681. fileName = CombinePath(fileName, "." + m_curSizeRollBackups);
  682. }
  683. }
  684. return fileName;
  685. }
  686. #endregion
  687. #region Initialize Options
  688. /// <summary>
  689. /// Determines curSizeRollBackups (only within the current roll point)
  690. /// </summary>
  691. private void DetermineCurSizeRollBackups()
  692. {
  693. m_curSizeRollBackups = 0;
  694. string fullPath = null;
  695. string fileName = null;
  696. using(SecurityContext.Impersonate(this))
  697. {
  698. fullPath = System.IO.Path.GetFullPath(m_baseFileName);
  699. fileName = System.IO.Path.GetFileName(fullPath);
  700. }
  701. ArrayList arrayFiles = GetExistingFiles(fullPath);
  702. InitializeRollBackups(fileName, arrayFiles);
  703. LogLog.Debug(declaringType, "curSizeRollBackups starts at ["+m_curSizeRollBackups+"]");
  704. }
  705. /// <summary>
  706. /// Generates a wildcard pattern that can be used to find all files
  707. /// that are similar to the base file name.
  708. /// </summary>
  709. /// <param name="baseFileName"></param>
  710. /// <returns></returns>
  711. private string GetWildcardPatternForFile(string baseFileName)
  712. {
  713. if (m_preserveLogFileNameExtension)
  714. {
  715. return Path.GetFileNameWithoutExtension(baseFileName) + "*" + Path.GetExtension(baseFileName);
  716. }
  717. else
  718. {
  719. return baseFileName + '*';
  720. }
  721. }
  722. /// <summary>
  723. /// Builds a list of filenames for all files matching the base filename plus a file
  724. /// pattern.
  725. /// </summary>
  726. /// <param name="baseFilePath"></param>
  727. /// <returns></returns>
  728. private ArrayList GetExistingFiles(string baseFilePath)
  729. {
  730. ArrayList alFiles = new ArrayList();
  731. string directory = null;
  732. using(SecurityContext.Impersonate(this))
  733. {
  734. string fullPath = Path.GetFullPath(baseFilePath);
  735. directory = Path.GetDirectoryName(fullPath);
  736. if (Directory.Exists(directory))
  737. {
  738. string baseFileName = Path.GetFileName(fullPath);
  739. string[] files = Directory.GetFiles(directory, GetWildcardPatternForFile(baseFileName));
  740. if (files != null)
  741. {
  742. for (int i = 0; i < files.Length; i++)
  743. {
  744. string curFileName = Path.GetFileName(files[i]);
  745. if (curFileName.StartsWith(Path.GetFileNameWithoutExtension(baseFileName)))
  746. {
  747. alFiles.Add(curFileName);
  748. }
  749. }
  750. }
  751. }
  752. }
  753. LogLog.Debug(declaringType, "Searched for existing files in ["+directory+"]");
  754. return alFiles;
  755. }
  756. /// <summary>
  757. /// Initiates a roll over if needed for crossing a date boundary since the last run.
  758. /// </summary>
  759. private void RollOverIfDateBoundaryCrossing()
  760. {
  761. if (m_staticLogFileName && m_rollDate)
  762. {
  763. if (FileExists(m_baseFileName))
  764. {
  765. DateTime last;
  766. using(SecurityContext.Impersonate(this)) {
  767. #if !NET_1_0 && !CLI_1_0 && !NETCF
  768. if (DateTimeStrategy is UniversalDateTime)
  769. {
  770. last = System.IO.File.GetLastWriteTimeUtc(m_baseFileName);
  771. }
  772. else
  773. {
  774. #endif
  775. last = System.IO.File.GetLastWriteTime(m_baseFileName);
  776. #if !NET_1_0 && !CLI_1_0 && !NETCF
  777. }
  778. #endif
  779. }
  780. LogLog.Debug(declaringType, "["+last.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo)+"] vs. ["+m_now.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo)+"]");
  781. if (!(last.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo).Equals(m_now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo))))
  782. {
  783. m_scheduledFilename = CombinePath(m_baseFileName, last.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo));
  784. LogLog.Debug(declaringType, "Initial roll over to ["+m_scheduledFilename+"]");
  785. RollOverTime(false);
  786. LogLog.Debug(declaringType, "curSizeRollBackups after rollOver at ["+m_curSizeRollBackups+"]");
  787. }
  788. }
  789. }
  790. }
  791. /// <summary>
  792. /// Initializes based on existing conditions at time of <see cref="ActivateOptions"/>.
  793. /// </summary>
  794. /// <remarks>
  795. /// <para>
  796. /// Initializes based on existing conditions at time of <see cref="ActivateOptions"/>.
  797. /// The following is done
  798. /// <list type="bullet">
  799. /// <item>determine curSizeRollBackups (only within the current roll point)</item>
  800. /// <item>initiates a roll over if needed for crossing a date boundary since the last run.</item>
  801. /// </list>
  802. /// </para>
  803. /// </remarks>
  804. protected void ExistingInit()
  805. {
  806. DetermineCurSizeRollBackups();
  807. RollOverIfDateBoundaryCrossing();
  808. // If file exists and we are not appending then roll it out of the way
  809. if (AppendToFile == false)
  810. {
  811. bool fileExists = false;
  812. string fileName = GetNextOutputFileName(m_baseFileName);
  813. using(SecurityContext.Impersonate(this))
  814. {
  815. fileExists = System.IO.File.Exists(fileName);
  816. }
  817. if (fileExists)
  818. {
  819. if (m_maxSizeRollBackups == 0)
  820. {
  821. LogLog.Debug(declaringType, "Output file ["+fileName+"] already exists. MaxSizeRollBackups is 0; cannot roll. Overwriting existing file.");
  822. }
  823. else
  824. {
  825. LogLog.Debug(declaringType, "Output file ["+fileName+"] already exists. Not appending to file. Rolling existing file out of the way.");
  826. RollOverRenameFiles(fileName);
  827. }
  828. }
  829. }
  830. }
  831. /// <summary>
  832. /// Does the work of bumping the 'current' file counter higher
  833. /// to the highest count when an incremental file name is seen.
  834. /// The highest count is either the first file (when count direction
  835. /// is greater than 0) or the last file (when count direction less than 0).
  836. /// In either case, we want to know the highest count that is present.
  837. /// </summary>
  838. /// <param name="baseFile"></param>
  839. /// <param name="curFileName"></param>
  840. private void InitializeFromOneFile(string baseFile, string curFileName)
  841. {
  842. if (curFileName.StartsWith(Path.GetFileNameWithoutExtension(baseFile)) == false)
  843. {
  844. // This is not a log file, so ignore
  845. return;
  846. }
  847. if (curFileName.Equals(baseFile))
  848. {
  849. // Base log file is not an incremented logfile (.1 or .2, etc)
  850. return;
  851. }
  852. /*
  853. if (m_staticLogFileName)
  854. {
  855. int endLength = curFileName.Length - index;
  856. if (baseFile.Length + endLength != curFileName.Length)
  857. {
  858. // file is probably scheduledFilename + .x so I don't care
  859. return;
  860. }
  861. }
  862. */
  863. // Only look for files in the current roll point
  864. if (m_rollDate && !m_staticLogFileName)
  865. {
  866. string date = m_dateTime.Now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo);
  867. string prefix = m_preserveLogFileNameExtension ? Path.GetFileNameWithoutExtension(baseFile) + date : baseFile + date;
  868. string suffix = m_preserveLogFileNameExtension ? Path.GetExtension(baseFile) : "";
  869. if (!curFileName.StartsWith(prefix) || !curFileName.EndsWith(suffix))
  870. {
  871. LogLog.Debug(declaringType, "Ignoring file ["+curFileName+"] because it is from a different date period");
  872. return;
  873. }
  874. }
  875. try
  876. {
  877. // Bump the counter up to the highest count seen so far
  878. int backup = GetBackUpIndex(curFileName);
  879. // caution: we might get a false positive when certain
  880. // date patterns such as yyyyMMdd are used...those are
  881. // valid number but aren't the kind of back up index
  882. // we're looking for
  883. if (backup > m_curSizeRollBackups)
  884. {
  885. if (0 == m_maxSizeRollBackups)
  886. {
  887. // Stay at zero when zero backups are desired
  888. }
  889. else if (-1 == m_maxSizeRollBackups)
  890. {
  891. // Infinite backups, so go as high as the highest value
  892. m_curSizeRollBackups = backup;
  893. }
  894. else
  895. {
  896. // Backups limited to a finite number
  897. if (m_countDirection >= 0)
  898. {
  899. // Go with the highest file when counting up
  900. m_curSizeRollBackups = backup;
  901. }
  902. else
  903. {
  904. // Clip to the limit when counting down
  905. if (backup <= m_maxSizeRollBackups)
  906. {
  907. m_curSizeRollBackups = backup;
  908. }
  909. }
  910. }
  911. LogLog.Debug(declaringType, "File name [" + curFileName + "] moves current count to [" + m_curSizeRollBackups + "]");
  912. }
  913. }
  914. catch(FormatException)
  915. {
  916. //this happens when file.log -> file.log.yyyy-MM-dd which is normal
  917. //when staticLogFileName == false
  918. LogLog.Debug(declaringType, "Encountered a backup file not ending in .x ["+curFileName+"]");
  919. }
  920. }
  921. /// <summary>
  922. /// Attempts to extract a number from the end of the file name that indicates
  923. /// the number of the times the file has been rolled over.
  924. /// </summary>
  925. /// <remarks>
  926. /// Certain date pattern extensions like yyyyMMdd will be parsed as valid backup indexes.
  927. /// </remarks>
  928. /// <param name="curFileName"></param>
  929. /// <returns></returns>
  930. private int GetBackUpIndex(string curFileName)
  931. {
  932. int backUpIndex = -1;
  933. string fileName = curFileName;
  934. if (m_preserveLogFileNameExtension)
  935. {
  936. fileName = Path.GetFileNameWithoutExtension(fileName);
  937. }
  938. int index = fileName.LastIndexOf(".");
  939. if (index > 0)
  940. {
  941. // if the "yyyy-MM-dd" component of file.log.yyyy-MM-dd is passed to TryParse
  942. // it will gracefully fail and return backUpIndex will be 0
  943. SystemInfo.TryParse(fileName.Substring(index + 1), out backUpIndex);
  944. }
  945. return backUpIndex;
  946. }
  947. /// <summary>
  948. /// Takes a list of files and a base file name, and looks for
  949. /// 'incremented' versions of the base file. Bumps the max
  950. /// count up to the highest count seen.
  951. /// </summary>
  952. /// <param name="baseFile"></param>
  953. /// <param name="arrayFiles"></param>
  954. private void InitializeRollBackups(string baseFile, ArrayList arrayFiles)
  955. {
  956. if (null != arrayFiles)
  957. {
  958. string baseFileLower = baseFile.ToLower(System.Globalization.CultureInfo.InvariantCulture);
  959. foreach(string curFileName in arrayFiles)
  960. {
  961. InitializeFromOneFile(baseFileLower, curFileName.ToLower(System.Globalization.CultureInfo.InvariantCulture));
  962. }
  963. }
  964. }
  965. /// <summary>
  966. /// Calculates the RollPoint for the datePattern supplied.
  967. /// </summary>
  968. /// <param name="datePattern">the date pattern to calculate the check period for</param>
  969. /// <returns>The RollPoint that is most accurate for the date pattern supplied</returns>
  970. /// <remarks>
  971. /// Essentially the date pattern is examined to determine what the
  972. /// most suitable roll point is. The roll point chosen is the roll point
  973. /// with the smallest period that can be detected using the date pattern
  974. /// supplied. i.e. if the date pattern only outputs the year, month, day
  975. /// and hour then the smallest roll point that can be detected would be
  976. /// and hourly roll point as minutes could not be detected.
  977. /// </remarks>
  978. private RollPoint ComputeCheckPeriod(string datePattern)
  979. {
  980. // s_date1970 is 1970-01-01 00:00:00 this is UniversalSortableDateTimePattern
  981. // (based on ISO 8601) using universal time. This date is used for reference
  982. // purposes to calculate the resolution of the date pattern.
  983. // Get string representation of base line date
  984. string r0 = s_date1970.ToString(datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo);
  985. // Check each type of rolling mode starting with the smallest increment.
  986. for(int i = (int)RollPoint.TopOfMinute; i <= (int)RollPoint.TopOfMonth; i++)
  987. {
  988. // Get string representation of next pattern
  989. string r1 = NextCheckDate(s_date1970, (RollPoint)i).ToString(datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo);
  990. LogLog.Debug(declaringType, "Type = ["+i+"], r0 = ["+r0+"], r1 = ["+r1+"]");
  991. // Check if the string representations are different
  992. if (r0 != null && r1 != null && !r0.Equals(r1))
  993. {
  994. // Found highest precision roll point
  995. return (RollPoint)i;
  996. }
  997. }
  998. return RollPoint.InvalidRollPoint; // Deliberately head for trouble...
  999. }
  1000. /// <summary>
  1001. /// Initialize the appender based on the options set
  1002. /// </summary>
  1003. /// <remarks>
  1004. /// <para>
  1005. /// This is part of the <see cref="IOptionHandler"/> delayed object
  1006. /// activation scheme. The <see cref="ActivateOptions"/> method must
  1007. /// be called on this object after the configuration properties have
  1008. /// been set. Until <see cref="ActivateOptions"/> is called this
  1009. /// object is in an undefined state and must not be used.
  1010. /// </para>
  1011. /// <para>
  1012. /// If any of the configuration properties are modified then
  1013. /// <see cref="ActivateOptions"/> must be called again.
  1014. /// </para>
  1015. /// <para>
  1016. /// Sets initial conditions including date/time roll over information, first check,
  1017. /// scheduledFilename, and calls <see cref="ExistingInit"/> to initialize
  1018. /// the current number of backups.
  1019. /// </para>
  1020. /// </remarks>
  1021. override public void ActivateOptions()
  1022. {
  1023. if (m_dateTime == null)
  1024. {
  1025. m_dateTime = new LocalDateTime();
  1026. }
  1027. if (m_rollDate && m_datePattern != null)
  1028. {
  1029. m_now = m_dateTime.Now;
  1030. m_rollPoint = ComputeCheckPeriod(m_datePattern);
  1031. if (m_rollPoint == RollPoint.InvalidRollPoint)
  1032. {
  1033. throw new ArgumentException("Invalid RollPoint, unable to parse ["+m_datePattern+"]");
  1034. }
  1035. // next line added as this removes the name check in rollOver
  1036. m_nextCheck = NextCheckDate(m_now, m_rollPoint);
  1037. }
  1038. else
  1039. {
  1040. if (m_rollDate)
  1041. {
  1042. ErrorHandler.Error("Either DatePattern or rollingStyle options are not set for ["+Name+"].");
  1043. }
  1044. }
  1045. if (SecurityContext == null)
  1046. {
  1047. SecurityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
  1048. }
  1049. using(SecurityContext.Impersonate(this))
  1050. {
  1051. // Must convert the FileAppender's m_filePath to an absolute path before we
  1052. // call ExistingInit(). This will be done by the base.ActivateOptions() but
  1053. // we need to duplicate that functionality here first.
  1054. base.File = ConvertToFullPath(base.File.Trim());
  1055. // Store fully qualified base file name
  1056. m_baseFileName = base.File;
  1057. }
  1058. #if !NETCF
  1059. // initialize the mutex that is used to lock rolling
  1060. m_mutexForRolling = new Mutex(false, m_baseFileName.Replace("\\", "_").Replace(":", "_").Replace("/", "_"));
  1061. #endif
  1062. if (m_rollDate && File != null && m_scheduledFilename == null)
  1063. {
  1064. m_scheduledFilename = CombinePath(File, m_now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo));
  1065. }
  1066. ExistingInit();
  1067. base.ActivateOptions();
  1068. }
  1069. #endregion
  1070. #region Roll File
  1071. /// <summary>
  1072. ///
  1073. /// </summary>
  1074. /// <param name="path1"></param>
  1075. /// <param name="path2">.1, .2, .3, etc.</param>
  1076. /// <returns></returns>
  1077. private string CombinePath(string path1, string path2)
  1078. {
  1079. string extension = Path.GetExtension(path1);
  1080. if (m_preserveLogFileNameExtension && extension.Length > 0)
  1081. {
  1082. return Path.Combine(Path.GetDirectoryName(path1), Path.GetFileNameWithoutExtension(path1) + path2 + extension);
  1083. }
  1084. else
  1085. {
  1086. return path1 + path2;
  1087. }
  1088. }
  1089. /// <summary>
  1090. /// Rollover the file(s) to date/time tagged file(s).
  1091. /// </summary>
  1092. /// <param name="fileIsOpen">set to true if the file to be rolled is currently open</param>
  1093. /// <remarks>
  1094. /// <para>
  1095. /// Rollover the file(s) to date/time tagged file(s).
  1096. /// Resets curSizeRollBackups.
  1097. /// If fileIsOpen is set then the new file is opened (through SafeOpenFile).
  1098. /// </para>
  1099. /// </remarks>
  1100. protected void RollOverTime(bool fileIsOpen)
  1101. {
  1102. if (m_staticLogFileName)
  1103. {
  1104. // Compute filename, but only if datePattern is specified
  1105. if (m_datePattern == null)
  1106. {
  1107. ErrorHandler.Error("Missing DatePattern option in rollOver().");
  1108. return;
  1109. }
  1110. //is the new file name equivalent to the 'current' one
  1111. //something has gone wrong if we hit this -- we should only
  1112. //roll over if the new file will be different from the old
  1113. string dateFormat = m_now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo);
  1114. if (m_scheduledFilename.Equals(CombinePath(File, dateFormat)))
  1115. {
  1116. ErrorHandler.Error("Compare " + m_scheduledFilename + " : " + CombinePath(File, dateFormat));
  1117. return;
  1118. }
  1119. if (fileIsOpen)
  1120. {
  1121. // close current file, and rename it to datedFilename
  1122. this.CloseFile();
  1123. }
  1124. //we may have to roll over a large number of backups here
  1125. for (int i = 1; i <= m_curSizeRollBackups; i++)
  1126. {
  1127. string from = CombinePath(File, "." + i);
  1128. string to = CombinePath(m_scheduledFilename, "." + i);
  1129. RollFile(from, to);
  1130. }
  1131. RollFile(File, m_scheduledFilename);
  1132. }
  1133. //We've cleared out the old date and are ready for the new
  1134. m_curSizeRollBackups = 0;
  1135. //new scheduled name
  1136. m_scheduledFilename = CombinePath(File, m_now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo));
  1137. if (fileIsOpen)
  1138. {
  1139. // This will also close the file. This is OK since multiple close operations are safe.
  1140. SafeOpenFile(m_baseFileName, false);
  1141. }
  1142. }
  1143. /// <summary>
  1144. /// Renames file <paramref name="fromFile"/> to file <paramref name="toFile"/>.
  1145. /// </summary>
  1146. /// <param name="fromFile">Name of existing file to roll.</param>
  1147. /// <param name="toFile">New name for file.</param>
  1148. /// <remarks>
  1149. /// <para>
  1150. /// Renames file <paramref name="fromFile"/> to file <paramref name="toFile"/>. It
  1151. /// also checks for existence of target file and deletes if it does.
  1152. /// </para>
  1153. /// </remarks>
  1154. protected void RollFile(string fromFile, string toFile)
  1155. {
  1156. if (FileExists(fromFile))
  1157. {
  1158. // Delete the toFile if it exists
  1159. DeleteFile(toFile);
  1160. // We may not have permission to move the file, or the file may be locked
  1161. try
  1162. {
  1163. LogLog.Debug(declaringType, "Moving [" + fromFile + "] -> [" + toFile + "]");
  1164. using(SecurityContext.Impersonate(this))
  1165. {
  1166. System.IO.File.Move(fromFile, toFile);
  1167. }
  1168. }
  1169. catch(Exception moveEx)
  1170. {
  1171. ErrorHandler.Error("Exception while rolling file [" + fromFile + "] -> [" + toFile + "]", moveEx, ErrorCode.GenericFailure);
  1172. }
  1173. }
  1174. else
  1175. {
  1176. LogLog.Warn(declaringType, "Cannot RollFile [" + fromFile + "] -> [" + toFile + "]. Source does not exist");
  1177. }
  1178. }
  1179. /// <summary>
  1180. /// Test if a file exists at a specified path
  1181. /// </summary>
  1182. /// <param name="path">the path to the file</param>
  1183. /// <returns>true if the file exists</returns>
  1184. /// <remarks>
  1185. /// <para>
  1186. /// Test if a file exists at a specified path
  1187. /// </para>
  1188. /// </remarks>
  1189. protected bool FileExists(string path)
  1190. {
  1191. using(SecurityContext.Impersonate(this))
  1192. {
  1193. return System.IO.File.Exists(path);
  1194. }
  1195. }
  1196. /// <summary>
  1197. /// Deletes the specified file if it exists.
  1198. /// </summary>
  1199. /// <param name="fileName">The file to delete.</param>
  1200. /// <remarks>
  1201. /// <para>
  1202. /// Delete a file if is exists.
  1203. /// The file is first moved to a new filename then deleted.
  1204. /// This allows the file to be removed even when it cannot
  1205. /// be deleted, but it still can be moved.
  1206. /// </para>
  1207. /// </remarks>
  1208. protected void DeleteFile(string fileName)
  1209. {
  1210. if (FileExists(fileName))
  1211. {
  1212. // We may not have permission to delete the file, or the file may be locked
  1213. string fileToDelete = fileName;
  1214. // Try to move the file to temp name.
  1215. // If the file is locked we may still be able to move it
  1216. string tempFileName = fileName + "." + Environment.TickCount + ".DeletePending";
  1217. try
  1218. {
  1219. using(SecurityContext.Impersonate(this))
  1220. {
  1221. System.IO.File.Move(fileName, tempFileName);
  1222. }
  1223. fileToDelete = tempFileName;
  1224. }
  1225. catch(Exception moveEx)
  1226. {
  1227. LogLog.Debug(declaringType, "Exception while moving file to be deleted [" + fileName + "] -> [" + tempFileName + "]", moveEx);
  1228. }
  1229. // Try to delete the file (either the original or the moved file)
  1230. try
  1231. {
  1232. using(SecurityContext.Impersonate(this))
  1233. {
  1234. System.IO.File.Delete(fileToDelete);
  1235. }
  1236. LogLog.Debug(declaringType, "Deleted file [" + fileName + "]");
  1237. }
  1238. catch(Exception deleteEx)
  1239. {
  1240. if (fileToDelete == fileName)
  1241. {
  1242. // Unable to move or delete the file
  1243. ErrorHandler.Error("Exception while deleting file [" + fileToDelete + "]", deleteEx, ErrorCode.GenericFailure);
  1244. }
  1245. else
  1246. {
  1247. // Moved the file, but the delete failed. File is probably locked.
  1248. // The file should automatically be deleted when the lock is released.
  1249. LogLog.Debug(declaringType, "Exception while deleting temp file [" + fileToDelete + "]", deleteEx);
  1250. }
  1251. }
  1252. }
  1253. }
  1254. /// <summary>
  1255. /// Implements file roll base on file size.
  1256. /// </summary>
  1257. /// <remarks>
  1258. /// <para>
  1259. /// If the maximum number of size based backups is reached
  1260. /// (<c>curSizeRollBackups == maxSizeRollBackups</c>) then the oldest
  1261. /// file is deleted -- its index determined by the sign of countDirection.
  1262. /// If <c>countDirection</c> &lt; 0, then files
  1263. /// {<c>File.1</c>, ..., <c>File.curSizeRollBackups -1</c>}
  1264. /// are renamed to {<c>File.2</c>, ...,
  1265. /// <c>File.curSizeRollBackups</c>}. Moreover, <c>File</c> is
  1266. /// renamed <c>File.1</c> and closed.
  1267. /// </para>
  1268. /// <para>
  1269. /// A new file is created to receive further log output.
  1270. /// </para>
  1271. /// <para>
  1272. /// If <c>maxSizeRollBackups</c> is equal to zero, then the
  1273. /// <c>File</c> is truncated with no backup files created.
  1274. /// </para>
  1275. /// <para>
  1276. /// If <c>maxSizeRollBackups</c> &lt; 0, then <c>File</c> is
  1277. /// renamed if needed and no files are deleted.
  1278. /// </para>
  1279. /// </remarks>
  1280. protected void RollOverSize()
  1281. {
  1282. this.CloseFile(); // keep windows happy.
  1283. LogLog.Debug(declaringType, "rolling over count ["+((CountingQuietTextWriter)QuietWriter).Count+"]");
  1284. LogLog.Debug(declaringType, "maxSizeRollBackups ["+m_maxSizeRollBackups+"]");
  1285. LogLog.Debug(declaringType, "curSizeRollBackups ["+m_curSizeRollBackups+"]");
  1286. LogLog.Debug(declaringType, "countDirection ["+m_countDirection+"]");
  1287. RollOverRenameFiles(File);
  1288. if (!m_staticLogFileName && m_countDirection >= 0)
  1289. {
  1290. m_curSizeRollBackups++;
  1291. }
  1292. // This will also close the file. This is OK since multiple close operations are safe.
  1293. SafeOpenFile(m_baseFileName, false);
  1294. }
  1295. /// <summary>
  1296. /// Implements file roll.
  1297. /// </summary>
  1298. /// <param name="baseFileName">the base name to rename</param>
  1299. /// <remarks>
  1300. /// <para>
  1301. /// If the maximum number of size based backups is reached
  1302. /// (<c>curSizeRollBackups == maxSizeRollBackups</c>) then the oldest
  1303. /// file is deleted -- its index determined by the sign of countDirection.
  1304. /// If <c>countDirection</c> &lt; 0, then files
  1305. /// {<c>File.1</c>, ..., <c>File.curSizeRollBackups -1</c>}
  1306. /// are renamed to {<c>File.2</c>, ...,
  1307. /// <c>File.curSizeRollBackups</c>}.
  1308. /// </para>
  1309. /// <para>
  1310. /// If <c>maxSizeRollBackups</c> is equal to zero, then the
  1311. /// <c>File</c> is truncated with no backup files created.
  1312. /// </para>
  1313. /// <para>
  1314. /// If <c>maxSizeRollBackups</c> &lt; 0, then <c>File</c> is
  1315. /// renamed if needed and no files are deleted.
  1316. /// </para>
  1317. /// <para>
  1318. /// This is called by <see cref="RollOverSize"/> to rename the files.
  1319. /// </para>
  1320. /// </remarks>
  1321. protected void RollOverRenameFiles(string baseFileName)
  1322. {
  1323. // If maxBackups <= 0, then there is no file renaming to be done.
  1324. if (m_maxSizeRollBackups != 0)
  1325. {
  1326. if (m_countDirection < 0)
  1327. {
  1328. // Delete the oldest file, to keep Windows happy.
  1329. if (m_curSizeRollBackups == m_maxSizeRollBackups)
  1330. {
  1331. DeleteFile(CombinePath(baseFileName, "." + m_maxSizeRollBackups));
  1332. m_curSizeRollBackups--;
  1333. }
  1334. // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
  1335. for (int i = m_curSizeRollBackups; i >= 1; i--)
  1336. {
  1337. RollFile((CombinePath(baseFileName, "." + i)), (CombinePath(baseFileName, "." + (i + 1))));
  1338. }
  1339. m_curSizeRollBackups++;
  1340. // Rename fileName to fileName.1
  1341. RollFile(baseFileName, CombinePath(baseFileName, ".1"));
  1342. }
  1343. else
  1344. {
  1345. //countDirection >= 0
  1346. if (m_curSizeRollBackups >= m_maxSizeRollBackups && m_maxSizeRollBackups > 0)
  1347. {
  1348. //delete the first and keep counting up.
  1349. int oldestFileIndex = m_curSizeRollBackups - m_maxSizeRollBackups;
  1350. // If static then there is 1 file without a number, therefore 1 less archive
  1351. if (m_staticLogFileName)
  1352. {
  1353. oldestFileIndex++;
  1354. }
  1355. // If using a static log file then the base for the numbered sequence is the baseFileName passed in
  1356. // If not using a static log file then the baseFileName will already have a numbered postfix which
  1357. // we must remove, however it may have a date postfix which we must keep!
  1358. string archiveFileBaseName = baseFileName;
  1359. if (!m_staticLogFileName)
  1360. {
  1361. int lastDotIndex = archiveFileBaseName.LastIndexOf(".");
  1362. if (lastDotIndex >= 0)
  1363. {
  1364. archiveFileBaseName = archiveFileBaseName.Substring(0, lastDotIndex);
  1365. }
  1366. }
  1367. // Delete the archive file
  1368. DeleteFile(CombinePath(archiveFileBaseName, "." + oldestFileIndex));
  1369. }
  1370. if (m_staticLogFileName)
  1371. {
  1372. m_curSizeRollBackups++;
  1373. RollFile(baseFileName, CombinePath(baseFileName, "." + m_curSizeRollBackups));
  1374. }
  1375. }
  1376. }
  1377. }
  1378. #endregion
  1379. #region NextCheckDate
  1380. /// <summary>
  1381. /// Get the start time of the next window for the current rollpoint
  1382. /// </summary>
  1383. /// <param name="currentDateTime">the current date</param>
  1384. /// <param name="rollPoint">the type of roll point we are working with</param>
  1385. /// <returns>the start time for the next roll point an interval after the currentDateTime date</returns>
  1386. /// <remarks>
  1387. /// <para>
  1388. /// Returns the date of the next roll point after the currentDateTime date passed to the method.
  1389. /// </para>
  1390. /// <para>
  1391. /// The basic strategy is to subtract the time parts that are less significant
  1392. /// than the rollpoint from the current time. This should roll the time back to
  1393. /// the start of the time window for the current rollpoint. Then we add 1 window
  1394. /// worth of time and get the start time of the next window for the rollpoint.
  1395. /// </para>
  1396. /// </remarks>
  1397. protected DateTime NextCheckDate(DateTime currentDateTime, RollPoint rollPoint)
  1398. {
  1399. // Local variable to work on (this does not look very efficient)
  1400. DateTime current = currentDateTime;
  1401. // Do slightly different things depending on what the type of roll point we want.
  1402. switch(rollPoint)
  1403. {
  1404. case RollPoint.TopOfMinute:
  1405. current = current.AddMilliseconds(-current.Millisecond);
  1406. current = current.AddSeconds(-current.Second);
  1407. current = current.AddMinutes(1);
  1408. break;
  1409. case RollPoint.TopOfHour:
  1410. current = current.AddMilliseconds(-current.Millisecond);
  1411. current = current.AddSeconds(-current.Second);
  1412. current = current.AddMinutes(-current.Minute);
  1413. current = current.AddHours(1);
  1414. break;
  1415. case RollPoint.HalfDay:
  1416. current = current.AddMilliseconds(-current.Millisecond);
  1417. current = current.AddSeconds(-current.Second);
  1418. current = current.AddMinutes(-current.Minute);
  1419. if (current.Hour < 12)
  1420. {
  1421. current = current.AddHours(12 - current.Hour);
  1422. }
  1423. else
  1424. {
  1425. current = current.AddHours(-current.Hour);
  1426. current = current.AddDays(1);
  1427. }
  1428. break;
  1429. case RollPoint.TopOfDay:
  1430. current = current.AddMilliseconds(-current.Millisecond);
  1431. current = current.AddSeconds(-current.Second);
  1432. current = current.AddMinutes(-current.Minute);
  1433. current = current.AddHours(-current.Hour);
  1434. current = current.AddDays(1);
  1435. break;
  1436. case RollPoint.TopOfWeek:
  1437. current = current.AddMilliseconds(-current.Millisecond);
  1438. current = current.AddSeconds(-current.Second);
  1439. current = current.AddMinutes(-current.Minute);
  1440. current = current.AddHours(-current.Hour);
  1441. current = current.AddDays(7 - (int)current.DayOfWeek);
  1442. break;
  1443. case RollPoint.TopOfMonth:
  1444. current = current.AddMilliseconds(-current.Millisecond);
  1445. current = current.AddSeconds(-current.Second);
  1446. current = current.AddMinutes(-current.Minute);
  1447. current = current.AddHours(-current.Hour);
  1448. current = current.AddDays(1 - current.Day); /* first day of month is 1 not 0 */
  1449. current = current.AddMonths(1);
  1450. break;
  1451. }
  1452. return current;
  1453. }
  1454. #endregion
  1455. #region Private Instance Fields
  1456. /// <summary>
  1457. /// This object supplies the current date/time. Allows test code to plug in
  1458. /// a method to control this class when testing date/time based rolling. The default
  1459. /// implementation uses the underlying value of DateTime.Now.
  1460. /// </summary>
  1461. private IDateTime m_dateTime = null;
  1462. /// <summary>
  1463. /// The date pattern. By default, the pattern is set to <c>".yyyy-MM-dd"</c>
  1464. /// meaning daily rollover.
  1465. /// </summary>
  1466. private string m_datePattern = ".yyyy-MM-dd";
  1467. /// <summary>
  1468. /// The actual formatted filename that is currently being written to
  1469. /// or will be the file transferred to on roll over
  1470. /// (based on staticLogFileName).
  1471. /// </summary>
  1472. private string m_scheduledFilename = null;
  1473. /// <summary>
  1474. /// The timestamp when we shall next recompute the filename.
  1475. /// </summary>
  1476. private DateTime m_nextCheck = DateTime.MaxValue;
  1477. /// <summary>
  1478. /// Holds date of last roll over
  1479. /// </summary>
  1480. private DateTime m_now;
  1481. /// <summary>
  1482. /// The type of rolling done
  1483. /// </summary>
  1484. private RollPoint m_rollPoint;
  1485. /// <summary>
  1486. /// The default maximum file size is 10MB
  1487. /// </summary>
  1488. private long m_maxFileSize = 10*1024*1024;
  1489. /// <summary>
  1490. /// There is zero backup files by default
  1491. /// </summary>
  1492. private int m_maxSizeRollBackups = 0;
  1493. /// <summary>
  1494. /// How many sized based backups have been made so far
  1495. /// </summary>
  1496. private int m_curSizeRollBackups = 0;
  1497. /// <summary>
  1498. /// The rolling file count direction.
  1499. /// </summary>
  1500. private int m_countDirection = -1;
  1501. /// <summary>
  1502. /// The rolling mode used in this appender.
  1503. /// </summary>
  1504. private RollingMode m_rollingStyle = RollingMode.Composite;
  1505. /// <summary>
  1506. /// Cache flag set if we are rolling by date.
  1507. /// </summary>
  1508. private bool m_rollDate = true;
  1509. /// <summary>
  1510. /// Cache flag set if we are rolling by size.
  1511. /// </summary>
  1512. private bool m_rollSize = true;
  1513. /// <summary>
  1514. /// Value indicating whether to always log to the same file.
  1515. /// </summary>
  1516. private bool m_staticLogFileName = true;
  1517. /// <summary>
  1518. /// Value indicating whether to preserve the file name extension when rolling.
  1519. /// </summary>
  1520. private bool m_preserveLogFileNameExtension = false;
  1521. /// <summary>
  1522. /// FileName provided in configuration. Used for rolling properly
  1523. /// </summary>
  1524. private string m_baseFileName;
  1525. #if !NETCF
  1526. /// <summary>
  1527. /// A mutex that is used to lock rolling of files.
  1528. /// </summary>
  1529. private Mutex m_mutexForRolling;
  1530. #endif
  1531. #endregion Private Instance Fields
  1532. #region Static Members
  1533. /// <summary>
  1534. /// The 1st of January 1970 in UTC
  1535. /// </summary>
  1536. private static readonly DateTime s_date1970 = new DateTime(1970, 1, 1);
  1537. #endregion
  1538. #region DateTime
  1539. /// <summary>
  1540. /// This interface is used to supply Date/Time information to the <see cref="RollingFileAppender"/>.
  1541. /// </summary>
  1542. /// <remarks>
  1543. /// This interface is used to supply Date/Time information to the <see cref="RollingFileAppender"/>.
  1544. /// Used primarily to allow test classes to plug themselves in so they can
  1545. /// supply test date/times.
  1546. /// </remarks>
  1547. public interface IDateTime
  1548. {
  1549. /// <summary>
  1550. /// Gets the <i>current</i> time.
  1551. /// </summary>
  1552. /// <value>The <i>current</i> time.</value>
  1553. /// <remarks>
  1554. /// <para>
  1555. /// Gets the <i>current</i> time.
  1556. /// </para>
  1557. /// </remarks>
  1558. DateTime Now { get; }
  1559. }
  1560. /// <summary>
  1561. /// Default implementation of <see cref="IDateTime"/> that returns the current time.
  1562. /// </summary>
  1563. private class LocalDateTime : IDateTime
  1564. {
  1565. /// <summary>
  1566. /// Gets the <b>current</b> time.
  1567. /// </summary>
  1568. /// <value>The <b>current</b> time.</value>
  1569. /// <remarks>
  1570. /// <para>
  1571. /// Gets the <b>current</b> time.
  1572. /// </para>
  1573. /// </remarks>
  1574. public DateTime Now
  1575. {
  1576. get { return DateTime.Now; }
  1577. }
  1578. }
  1579. #if !NET_1_0 && !CLI_1_0 && !NETCF
  1580. /// <summary>
  1581. /// Implementation of <see cref="IDateTime"/> that returns the current time as the coordinated universal time (UTC).
  1582. /// </summary>
  1583. private class UniversalDateTime : IDateTime
  1584. {
  1585. /// <summary>
  1586. /// Gets the <b>current</b> time.
  1587. /// </summary>
  1588. /// <value>The <b>current</b> time.</value>
  1589. /// <remarks>
  1590. /// <para>
  1591. /// Gets the <b>current</b> time.
  1592. /// </para>
  1593. /// </remarks>
  1594. public DateTime Now
  1595. {
  1596. get { return DateTime.UtcNow; }
  1597. }
  1598. }
  1599. #endif
  1600. #endregion DateTime
  1601. }
  1602. }