1 // Module: Log4CPLUS
2 // File: fileappender.cxx
3 // Created: 6/2001
4 // Author: Tad E. Smith
5 //
6 //
7 // Copyright 2001-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
21 #include "dcmtk/oflog/fileap.h"
22 #include "dcmtk/oflog/layout.h"
23 #include "dcmtk/oflog/streams.h"
24 #include "dcmtk/oflog/helpers/loglog.h"
25 #include "dcmtk/oflog/helpers/strhelp.h"
26 #include "dcmtk/oflog/helpers/timehelp.h"
27 #include "dcmtk/oflog/helpers/property.h"
28 #include "dcmtk/oflog/helpers/fileinfo.h"
29 #include "dcmtk/oflog/spi/logevent.h"
30 #include "dcmtk/oflog/spi/factory.h"
31 #include "dcmtk/oflog/thread/syncpub.h"
32 #include "dcmtk/oflog/internal/internal.h"
33 #include <algorithm>
34 #include <sstream>
35 #include <cstdio>
36 #include <stdexcept>
37
38 #if defined (__BORLANDC__)
39 // For _wrename() and _wremove() on Windows.
40 # include <stdio.h>
41 #endif
42 #include <cerrno>
43 #ifdef DCMTK_LOG4CPLUS_HAVE_ERRNO_H
44 #include <errno.h>
45 #endif
46
47 #ifdef MAX
48 #undef MAX
49 #endif
50
51 #define MAX(a, b) ((a) < (b) ? (b) : (a))
52
53 namespace dcmtk
54 {
55 namespace log4cplus
56 {
57
58 using helpers::Properties;
59 using helpers::Time;
60
61
62 const long DEFAULT_ROLLING_LOG_SIZE = 10 * 1024 * 1024L;
63 const long MINIMUM_ROLLING_LOG_SIZE = 200*1024L;
64
65
66 ///////////////////////////////////////////////////////////////////////////////
67 // File LOCAL definitions
68 ///////////////////////////////////////////////////////////////////////////////
69
70 namespace
71 {
72
73 long const DCMTK_LOG4CPLUS_FILE_NOT_FOUND = ENOENT;
74
75
76 static
77 long
file_rename(tstring const & src,tstring const & target)78 file_rename (tstring const & src, tstring const & target)
79 {
80 #if defined (DCMTK_OFLOG_UNICODE) && defined (_WIN32)
81 if (_wrename (src.c_str (), target.c_str ()) == 0)
82 return 0;
83 else
84 return errno;
85
86 #else
87 if (rename (DCMTK_LOG4CPLUS_TSTRING_TO_STRING (src).c_str (),
88 DCMTK_LOG4CPLUS_TSTRING_TO_STRING (target).c_str ()) == 0)
89 return 0;
90 else
91 return errno;
92
93 #endif
94 }
95
96
97 static
98 long
file_remove(tstring const & src)99 file_remove (tstring const & src)
100 {
101 #if defined (DCMTK_OFLOG_UNICODE) && defined (_WIN32)
102 if (_wremove (src.c_str ()) == 0)
103 return 0;
104 else
105 return errno;
106
107 #else
108 if (remove (DCMTK_LOG4CPLUS_TSTRING_TO_STRING (src).c_str ()) == 0)
109 return 0;
110 else
111 return errno;
112
113 #endif
114 }
115
116
117 static
118 void
loglog_renaming_result(helpers::LogLog & loglog,tstring const & src,tstring const & target,long ret)119 loglog_renaming_result (helpers::LogLog & loglog, tstring const & src,
120 tstring const & target, long ret)
121 {
122 if (ret == 0)
123 {
124 loglog.debug (
125 DCMTK_LOG4CPLUS_TEXT("Renamed file ")
126 + src
127 + DCMTK_LOG4CPLUS_TEXT(" to ")
128 + target);
129 }
130 else if (ret != DCMTK_LOG4CPLUS_FILE_NOT_FOUND)
131 {
132 tostringstream oss;
133 oss << DCMTK_LOG4CPLUS_TEXT("Failed to rename file from ")
134 << src
135 << DCMTK_LOG4CPLUS_TEXT(" to ")
136 << target
137 << DCMTK_LOG4CPLUS_TEXT("; error ")
138 << ret;
139 loglog.error (OFString(oss.str().c_str(), oss.str().length()));
140 }
141 }
142
143
144 static
145 void
loglog_opening_result(helpers::LogLog & loglog,log4cplus::tostream const & os,tstring const & filename)146 loglog_opening_result (helpers::LogLog & loglog,
147 log4cplus::tostream const & os, tstring const & filename)
148 {
149 if (! os)
150 {
151 loglog.error (
152 DCMTK_LOG4CPLUS_TEXT("Failed to open file ")
153 + filename);
154 }
155 }
156
157
158 static
159 void
rolloverFiles(const tstring & filename,unsigned int maxBackupIndex)160 rolloverFiles(const tstring& filename, unsigned int maxBackupIndex)
161 {
162 helpers::LogLog * loglog = helpers::LogLog::getLogLog();
163
164 // Delete the oldest file
165 tostringstream buffer;
166 buffer << filename << DCMTK_LOG4CPLUS_TEXT(".") << maxBackupIndex;
167 long ret = file_remove (OFString(buffer.str().c_str(), buffer.str().length()));
168
169 tostringstream source_oss;
170 tostringstream target_oss;
171
172 // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
173 for (int i = maxBackupIndex - 1; i >= 1; --i)
174 {
175 source_oss.str(DCMTK_LOG4CPLUS_TEXT(""));
176 target_oss.str(DCMTK_LOG4CPLUS_TEXT(""));
177
178 source_oss << filename << DCMTK_LOG4CPLUS_TEXT(".") << i;
179 target_oss << filename << DCMTK_LOG4CPLUS_TEXT(".") << (i+1);
180
181 tstring const source (OFString(source_oss.str().c_str(), source_oss.str().length()));
182 tstring const target (OFString(target_oss.str().c_str(), target_oss.str().length()));
183
184 #if defined (_WIN32)
185 // Try to remove the target first. It seems it is not
186 // possible to rename over existing file.
187 ret = file_remove (target);
188 #endif
189
190 ret = file_rename (source, target);
191 loglog_renaming_result (*loglog, source, target, ret);
192 }
193 } // end rolloverFiles()
194
195
196 static
197 STD_NAMESPACE locale
get_locale_by_name(tstring const & locale_name)198 get_locale_by_name (tstring const & locale_name)
199 {
200 try {
201 spi::LocaleFactoryRegistry & reg = spi::getLocaleFactoryRegistry ();
202 spi::LocaleFactory * fact = reg.get (locale_name);
203 if (fact)
204 {
205 helpers::Properties props;
206 props.setProperty (DCMTK_LOG4CPLUS_TEXT ("Locale"), locale_name);
207 return fact->createObject (props);
208 }
209 else
210 return STD_NAMESPACE locale (DCMTK_LOG4CPLUS_TSTRING_TO_STRING (locale_name).c_str ());
211 }
212 catch (STD_NAMESPACE runtime_error const &)
213 {
214 helpers::getLogLog ().error (
215 DCMTK_LOG4CPLUS_TEXT ("Failed to create locale " + locale_name));
216 return STD_NAMESPACE locale ();
217 }
218 }
219
220 } // namespace
221
222
223 ///////////////////////////////////////////////////////////////////////////////
224 // FileAppender ctors and dtor
225 ///////////////////////////////////////////////////////////////////////////////
226
FileAppender(const tstring & filename_,STD_NAMESPACE ios_base::openmode mode_,bool immediateFlush_)227 FileAppender::FileAppender(const tstring& filename_,
228 STD_NAMESPACE ios_base::openmode mode_, bool immediateFlush_)
229 : immediateFlush(immediateFlush_)
230 , reopenDelay(1)
231 , bufferSize (0)
232 , buffer (0)
233 , out ()
234 , filename ()
235 , localeName (DCMTK_LOG4CPLUS_TEXT ("DEFAULT"))
236 , reopen_time ()
237 {
238 init(filename_, mode_, internal::empty_str);
239 }
240
241
FileAppender(const Properties & props,STD_NAMESPACE ios_base::openmode mode_)242 FileAppender::FileAppender(const Properties& props,
243 STD_NAMESPACE ios_base::openmode mode_)
244 : Appender(props)
245 , immediateFlush(true)
246 , reopenDelay(1)
247 , bufferSize (0)
248 , buffer (0)
249 , out ()
250 , filename ()
251 , localeName ()
252 , reopen_time ()
253 {
254 bool app = (mode_ == STD_NAMESPACE ios::app);
255 tstring const & fn = props.getProperty( DCMTK_LOG4CPLUS_TEXT("File") );
256 if (fn.empty())
257 {
258 getErrorHandler()->error( DCMTK_LOG4CPLUS_TEXT("Invalid filename") );
259 return;
260 }
261
262 props.getBool (immediateFlush, DCMTK_LOG4CPLUS_TEXT("ImmediateFlush"));
263 props.getBool (app, DCMTK_LOG4CPLUS_TEXT("Append"));
264 props.getInt (reopenDelay, DCMTK_LOG4CPLUS_TEXT("ReopenDelay"));
265 props.getULong (bufferSize, DCMTK_LOG4CPLUS_TEXT("BufferSize"));
266
267 tstring lockFileName = props.getProperty (DCMTK_LOG4CPLUS_TEXT ("LockFile"));
268 if (useLockFile && lockFileName.empty ())
269 {
270 lockFileName = fn;
271 lockFileName += DCMTK_LOG4CPLUS_TEXT(".lock");
272 }
273
274 localeName = props.getProperty (DCMTK_LOG4CPLUS_TEXT ("Locale"),
275 DCMTK_LOG4CPLUS_TEXT ("DEFAULT"));
276
277 init(fn, (app ? STD_NAMESPACE ios::app : STD_NAMESPACE ios::trunc), lockFileName);
278 }
279
280
281
282 void
init(const tstring & filename_,STD_NAMESPACE ios_base::openmode mode_,const log4cplus::tstring & lockFileName_)283 FileAppender::init(const tstring& filename_,
284 STD_NAMESPACE ios_base::openmode mode_,
285 const log4cplus::tstring& lockFileName_)
286 {
287 filename = filename_;
288
289 if (bufferSize != 0)
290 {
291 delete[] buffer;
292 buffer = new tchar[bufferSize];
293 out.rdbuf ()->pubsetbuf (buffer, bufferSize);
294 }
295
296 helpers::LockFileGuard guard;
297 if (useLockFile && ! lockFile.get ())
298 {
299 try
300 {
301 lockFile.reset (new helpers::LockFile (lockFileName_));
302 guard.attach_and_lock (*lockFile);
303 }
304 catch (STD_NAMESPACE runtime_error const &)
305 {
306 // We do not need to do any logging here as the internals
307 // of LockFile already use LogLog to report the failure.
308 return;
309 }
310 }
311
312 open(mode_);
313 imbue (get_locale_by_name (localeName));
314
315 if(!out.good()) {
316 getErrorHandler()->error( DCMTK_LOG4CPLUS_TEXT("Unable to open file: ")
317 + filename);
318 return;
319 }
320 helpers::getLogLog().debug(DCMTK_LOG4CPLUS_TEXT("Just opened file: ") + filename);
321 }
322
323
324
~FileAppender()325 FileAppender::~FileAppender()
326 {
327 destructorImpl();
328 }
329
330
331
332 ///////////////////////////////////////////////////////////////////////////////
333 // FileAppender public methods
334 ///////////////////////////////////////////////////////////////////////////////
335
336 void
close()337 FileAppender::close()
338 {
339 thread::MutexGuard guard (access_mutex);
340
341 out.close();
342 delete[] buffer;
343 buffer = 0;
344 closed = true;
345 }
346
347
348 STD_NAMESPACE locale
imbue(STD_NAMESPACE locale const & loc)349 FileAppender::imbue(STD_NAMESPACE locale const& loc)
350 {
351 return out.imbue (loc);
352 }
353
354
355 STD_NAMESPACE locale
getloc() const356 FileAppender::getloc () const
357 {
358 return out.getloc ();
359 }
360
361
362 ///////////////////////////////////////////////////////////////////////////////
363 // FileAppender protected methods
364 ///////////////////////////////////////////////////////////////////////////////
365
366 // This method does not need to be locked since it is called by
367 // doAppend() which performs the locking
368 void
append(const spi::InternalLoggingEvent & event)369 FileAppender::append(const spi::InternalLoggingEvent& event)
370 {
371 if(!out.good()) {
372 if(!reopen()) {
373 getErrorHandler()->error( DCMTK_LOG4CPLUS_TEXT("file is not open: ")
374 + filename);
375 return;
376 }
377 // Resets the error handler to make it
378 // ready to handle a future append error.
379 else
380 getErrorHandler()->reset();
381 }
382
383 if (useLockFile)
384 out.seekp (0, STD_NAMESPACE ios_base::end);
385
386 layout->formatAndAppend(out, event);
387
388 if(immediateFlush || useLockFile)
389 out.flush();
390 }
391
392 void
open(STD_NAMESPACE ios::openmode mode)393 FileAppender::open(STD_NAMESPACE ios::openmode mode)
394 {
395 out.open(DCMTK_LOG4CPLUS_FSTREAM_PREFERED_FILE_NAME(filename).c_str(), mode);
396 }
397
398 bool
reopen()399 FileAppender::reopen()
400 {
401 // When append never failed and the file re-open attempt must
402 // be delayed, set the time when reopen should take place.
403 if (reopen_time == log4cplus::helpers::Time () && reopenDelay != 0)
404 reopen_time = log4cplus::helpers::Time::gettimeofday()
405 + log4cplus::helpers::Time(reopenDelay);
406 else
407 {
408 // Otherwise, check for end of the delay (or absence of delay)
409 // to re-open the file.
410 if (reopen_time <= log4cplus::helpers::Time::gettimeofday()
411 || reopenDelay == 0)
412 {
413 // Close the current file
414 out.close();
415 out.clear(); // reset flags since the C++ standard specified that all the
416 // flags should remain unchanged on a close
417
418 // Re-open the file.
419 open(STD_NAMESPACE ios_base::out | STD_NAMESPACE ios_base::ate);
420
421 // Reset last fail time.
422 reopen_time = log4cplus::helpers::Time ();
423
424 // Succeed if no errors are found.
425 if(out.good())
426 return true;
427 }
428 }
429 return false;
430 }
431
432 ///////////////////////////////////////////////////////////////////////////////
433 // RollingFileAppender ctors and dtor
434 ///////////////////////////////////////////////////////////////////////////////
435
RollingFileAppender(const tstring & filename_,long maxFileSize_,int maxBackupIndex_,bool immediateFlush_)436 RollingFileAppender::RollingFileAppender(const tstring& filename_,
437 long maxFileSize_, int maxBackupIndex_, bool immediateFlush_)
438 : FileAppender(filename_, STD_NAMESPACE ios::app, immediateFlush_), maxFileSize (), maxBackupIndex ()
439 {
440 init(maxFileSize_, maxBackupIndex_);
441 }
442
443
RollingFileAppender(const Properties & properties)444 RollingFileAppender::RollingFileAppender(const Properties& properties)
445 : FileAppender(properties, STD_NAMESPACE ios::app), maxFileSize (), maxBackupIndex ()
446 {
447 long tmpMaxFileSize = DEFAULT_ROLLING_LOG_SIZE;
448 int tmpMaxBackupIndex = 1;
449 tstring tmp (
450 helpers::toUpper (
451 properties.getProperty (DCMTK_LOG4CPLUS_TEXT ("MaxFileSize"))));
452 if (! tmp.empty ())
453 {
454 tmpMaxFileSize = atoi(DCMTK_LOG4CPLUS_TSTRING_TO_STRING(tmp).c_str());
455 if (tmpMaxFileSize != 0)
456 {
457 tstring::size_type const len = tmp.length();
458 if (len > 2
459 && tmp.compare (len - 2, 2, DCMTK_LOG4CPLUS_TEXT("MB")) == 0)
460 tmpMaxFileSize *= (1024 * 1024); // convert to megabytes
461 else if (len > 2
462 && tmp.compare (len - 2, 2, DCMTK_LOG4CPLUS_TEXT("KB")) == 0)
463 tmpMaxFileSize *= 1024; // convert to kilobytes
464 }
465 tmpMaxFileSize = MAX(tmpMaxFileSize, MINIMUM_ROLLING_LOG_SIZE);
466 }
467
468 properties.getInt (tmpMaxBackupIndex, DCMTK_LOG4CPLUS_TEXT("MaxBackupIndex"));
469
470 init(tmpMaxFileSize, tmpMaxBackupIndex);
471 }
472
473
474 void
init(long maxFileSize_,int maxBackupIndex_)475 RollingFileAppender::init(long maxFileSize_, int maxBackupIndex_)
476 {
477 if (maxFileSize_ < MINIMUM_ROLLING_LOG_SIZE)
478 {
479 tostringstream oss;
480 oss << DCMTK_LOG4CPLUS_TEXT ("RollingFileAppender: MaxFileSize property")
481 DCMTK_LOG4CPLUS_TEXT (" value is too small. Resetting to ")
482 << MINIMUM_ROLLING_LOG_SIZE << ".";
483 helpers::getLogLog ().warn (OFString(oss.str().c_str(), oss.str().length()));
484 maxFileSize_ = MINIMUM_ROLLING_LOG_SIZE;
485 }
486
487 maxFileSize = maxFileSize_;
488 maxBackupIndex = MAX(maxBackupIndex_, 1);
489 }
490
491
~RollingFileAppender()492 RollingFileAppender::~RollingFileAppender()
493 {
494 destructorImpl();
495 }
496
497
498 ///////////////////////////////////////////////////////////////////////////////
499 // RollingFileAppender protected methods
500 ///////////////////////////////////////////////////////////////////////////////
501
502 // This method does not need to be locked since it is called by
503 // doAppend() which performs the locking
504 void
append(const spi::InternalLoggingEvent & event)505 RollingFileAppender::append(const spi::InternalLoggingEvent& event)
506 {
507 FileAppender::append(event);
508
509 if(out.tellp() > maxFileSize) {
510 rollover(true);
511 }
512 }
513
514
515 void
rollover(bool alreadyLocked)516 RollingFileAppender::rollover(bool alreadyLocked)
517 {
518 helpers::LogLog & loglog = helpers::getLogLog();
519 helpers::LockFileGuard guard;
520
521 // Close the current file
522 out.close();
523 // Reset flags since the C++ standard specified that all the flags
524 // should remain unchanged on a close.
525 out.clear();
526
527 if (useLockFile)
528 {
529 if (! alreadyLocked)
530 {
531 try
532 {
533 guard.attach_and_lock (*lockFile);
534 }
535 catch (STD_NAMESPACE runtime_error const &)
536 {
537 return;
538 }
539 }
540
541 // Recheck the condition as there is a window where another
542 // process can rollover the file before us.
543
544 helpers::FileInfo fi;
545 if (helpers::getFileInfo (&fi, filename) == -1
546 || fi.size < maxFileSize)
547 {
548 // The file has already been rolled by another
549 // process. Just reopen with the new file.
550
551 // Open it up again.
552 open (STD_NAMESPACE ios::out | STD_NAMESPACE ios::ate);
553 loglog_opening_result (loglog, out, filename);
554
555 return;
556 }
557 }
558
559 // If maxBackups <= 0, then there is no file renaming to be done.
560 if (maxBackupIndex > 0)
561 {
562 rolloverFiles(filename, maxBackupIndex);
563
564 // Rename fileName to fileName.1
565 tstring target = filename + DCMTK_LOG4CPLUS_TEXT(".1");
566
567 long ret;
568
569 #if defined (_WIN32)
570 // Try to remove the target first. It seems it is not
571 // possible to rename over existing file.
572 ret = file_remove (target);
573 #endif
574
575 loglog.debug (
576 DCMTK_LOG4CPLUS_TEXT("Renaming file ")
577 + filename
578 + DCMTK_LOG4CPLUS_TEXT(" to ")
579 + target);
580 ret = file_rename (filename, target);
581 loglog_renaming_result (loglog, filename, target, ret);
582 }
583 else
584 {
585 loglog.debug (filename + DCMTK_LOG4CPLUS_TEXT(" has no backups specified"));
586 }
587
588 // Open it up again in truncation mode
589 open(STD_NAMESPACE ios::out | STD_NAMESPACE ios::trunc);
590 loglog_opening_result (loglog, out, filename);
591 }
592
593
594 ///////////////////////////////////////////////////////////////////////////////
595 // DailyRollingFileAppender ctors and dtor
596 ///////////////////////////////////////////////////////////////////////////////
597
DailyRollingFileAppender(const tstring & filename_,DailyRollingFileSchedule schedule_,bool immediateFlush_,int maxBackupIndex_)598 DailyRollingFileAppender::DailyRollingFileAppender(
599 const tstring& filename_, DailyRollingFileSchedule schedule_,
600 bool immediateFlush_, int maxBackupIndex_)
601 : FileAppender(filename_, STD_NAMESPACE ios::app, immediateFlush_)
602 , schedule()
603 , scheduledFilename()
604 , nextRolloverTime()
605 , maxBackupIndex(maxBackupIndex_)
606 {
607 init(schedule_);
608 }
609
610
611
DailyRollingFileAppender(const Properties & properties)612 DailyRollingFileAppender::DailyRollingFileAppender(
613 const Properties& properties)
614 : FileAppender(properties, STD_NAMESPACE ios::app)
615 , schedule()
616 , scheduledFilename()
617 , nextRolloverTime()
618 , maxBackupIndex(10)
619 {
620 DailyRollingFileSchedule theSchedule = DAILY;
621 tstring scheduleStr (helpers::toUpper (
622 properties.getProperty (DCMTK_LOG4CPLUS_TEXT ("Schedule"))));
623
624 if(scheduleStr == DCMTK_LOG4CPLUS_TEXT("MONTHLY"))
625 theSchedule = MONTHLY;
626 else if(scheduleStr == DCMTK_LOG4CPLUS_TEXT("WEEKLY"))
627 theSchedule = WEEKLY;
628 else if(scheduleStr == DCMTK_LOG4CPLUS_TEXT("DAILY"))
629 theSchedule = DAILY;
630 else if(scheduleStr == DCMTK_LOG4CPLUS_TEXT("TWICE_DAILY"))
631 theSchedule = TWICE_DAILY;
632 else if(scheduleStr == DCMTK_LOG4CPLUS_TEXT("HOURLY"))
633 theSchedule = HOURLY;
634 else if(scheduleStr == DCMTK_LOG4CPLUS_TEXT("MINUTELY"))
635 theSchedule = MINUTELY;
636 else {
637 helpers::getLogLog().warn(
638 DCMTK_LOG4CPLUS_TEXT("DailyRollingFileAppender::ctor()")
639 DCMTK_LOG4CPLUS_TEXT("- \"Schedule\" not valid: ")
640 + properties.getProperty(DCMTK_LOG4CPLUS_TEXT("Schedule")));
641 theSchedule = DAILY;
642 }
643
644 properties.getInt (maxBackupIndex, DCMTK_LOG4CPLUS_TEXT("MaxBackupIndex"));
645
646 init(theSchedule);
647 }
648
649
650
651 void
init(DailyRollingFileSchedule sch)652 DailyRollingFileAppender::init(DailyRollingFileSchedule sch)
653 {
654 this->schedule = sch;
655
656 Time now = Time::gettimeofday();
657 now.usec(0);
658 struct tm time;
659 now.localtime(&time);
660
661 time.tm_sec = 0;
662 switch (schedule)
663 {
664 case MONTHLY:
665 time.tm_mday = 1;
666 time.tm_hour = 0;
667 time.tm_min = 0;
668 break;
669
670 case WEEKLY:
671 time.tm_mday -= (time.tm_wday % 7);
672 time.tm_hour = 0;
673 time.tm_min = 0;
674 break;
675
676 case DAILY:
677 time.tm_hour = 0;
678 time.tm_min = 0;
679 break;
680
681 case TWICE_DAILY:
682 if(time.tm_hour >= 12) {
683 time.tm_hour = 12;
684 }
685 else {
686 time.tm_hour = 0;
687 }
688 time.tm_min = 0;
689 break;
690
691 case HOURLY:
692 time.tm_min = 0;
693 break;
694
695 case MINUTELY:
696 break;
697 };
698 now.setTime(&time);
699
700 scheduledFilename = getFilename(now);
701 nextRolloverTime = calculateNextRolloverTime(now);
702 }
703
704
705
~DailyRollingFileAppender()706 DailyRollingFileAppender::~DailyRollingFileAppender()
707 {
708 destructorImpl();
709 }
710
711
712
713
714 ///////////////////////////////////////////////////////////////////////////////
715 // DailyRollingFileAppender public methods
716 ///////////////////////////////////////////////////////////////////////////////
717
718 void
close()719 DailyRollingFileAppender::close()
720 {
721 rollover();
722 FileAppender::close();
723 }
724
725
726
727 ///////////////////////////////////////////////////////////////////////////////
728 // DailyRollingFileAppender protected methods
729 ///////////////////////////////////////////////////////////////////////////////
730
731 // This method does not need to be locked since it is called by
732 // doAppend() which performs the locking
733 void
append(const spi::InternalLoggingEvent & event)734 DailyRollingFileAppender::append(const spi::InternalLoggingEvent& event)
735 {
736 if(event.getTimestamp() >= nextRolloverTime) {
737 rollover(true);
738 }
739
740 FileAppender::append(event);
741 }
742
743
744
745 void
rollover(bool alreadyLocked)746 DailyRollingFileAppender::rollover(bool alreadyLocked)
747 {
748 helpers::LockFileGuard guard;
749
750 if (useLockFile && ! alreadyLocked)
751 {
752 try
753 {
754 guard.attach_and_lock (*lockFile);
755 }
756 catch (STD_NAMESPACE runtime_error const &)
757 {
758 return;
759 }
760 }
761
762 // Close the current file
763 out.close();
764 out.clear(); // reset flags since the C++ standard specified that all the
765 // flags should remain unchanged on a close
766
767 // If we've already rolled over this time period, we'll make sure that we
768 // don't overwrite any of those previous files.
769 // E.g. if "log.2009-11-07.1" already exists we rename it
770 // to "log.2009-11-07.2", etc.
771 rolloverFiles(scheduledFilename, maxBackupIndex);
772
773 // Do not overwriet the newest file either, e.g. if "log.2009-11-07"
774 // already exists rename it to "log.2009-11-07.1"
775 tostringstream backup_target_oss;
776 backup_target_oss << scheduledFilename << DCMTK_LOG4CPLUS_TEXT(".") << 1;
777 tstring backupTarget(backup_target_oss.str().c_str(), backup_target_oss.str().length());
778
779 helpers::LogLog & loglog = helpers::getLogLog();
780 long ret;
781
782 #if defined (_WIN32)
783 // Try to remove the target first. It seems it is not
784 // possible to rename over existing file, e.g. "log.2009-11-07.1".
785 ret = file_remove (backupTarget);
786 #endif
787
788 // Rename e.g. "log.2009-11-07" to "log.2009-11-07.1".
789 ret = file_rename (scheduledFilename, backupTarget);
790 loglog_renaming_result (loglog, scheduledFilename, backupTarget, ret);
791
792 #if defined (_WIN32)
793 // Try to remove the target first. It seems it is not
794 // possible to rename over existing file, e.g. "log.2009-11-07".
795 ret = file_remove (scheduledFilename);
796 #endif
797
798 // Rename filename to scheduledFilename,
799 // e.g. rename "log" to "log.2009-11-07".
800 loglog.debug(
801 DCMTK_LOG4CPLUS_TEXT("Renaming file ")
802 + filename
803 + DCMTK_LOG4CPLUS_TEXT(" to ")
804 + scheduledFilename);
805 ret = file_rename (filename, scheduledFilename);
806 loglog_renaming_result (loglog, filename, scheduledFilename, ret);
807
808 // Open a new file, e.g. "log".
809 open(STD_NAMESPACE ios::out | STD_NAMESPACE ios::trunc);
810 loglog_opening_result (loglog, out, filename);
811
812 // Calculate the next rollover time
813 log4cplus::helpers::Time now = Time::gettimeofday();
814 if (now >= nextRolloverTime)
815 {
816 scheduledFilename = getFilename(now);
817 nextRolloverTime = calculateNextRolloverTime(now);
818 }
819 }
820
821
822
823 Time
calculateNextRolloverTime(const Time & t) const824 DailyRollingFileAppender::calculateNextRolloverTime(const Time& t) const
825 {
826 switch(schedule)
827 {
828 case MONTHLY:
829 {
830 struct tm nextMonthTime;
831 t.localtime(&nextMonthTime);
832 nextMonthTime.tm_mon += 1;
833 nextMonthTime.tm_isdst = 0;
834
835 Time ret;
836 if(ret.setTime(&nextMonthTime) == -1) {
837 helpers::getLogLog().error(
838 DCMTK_LOG4CPLUS_TEXT("DailyRollingFileAppender::calculateNextRolloverTime()-")
839 DCMTK_LOG4CPLUS_TEXT(" setTime() returned error"));
840 // Set next rollover to 31 days in future.
841 ret = (t + Time(2678400));
842 }
843
844 return ret;
845 }
846
847 case WEEKLY:
848 return (t + Time(7 * 24 * 60 * 60));
849
850 default:
851 helpers::getLogLog ().error (
852 DCMTK_LOG4CPLUS_TEXT ("DailyRollingFileAppender::calculateNextRolloverTime()-")
853 DCMTK_LOG4CPLUS_TEXT (" invalid schedule value"));
854 // Fall through.
855
856 case DAILY:
857 return (t + Time(24 * 60 * 60));
858
859 case TWICE_DAILY:
860 return (t + Time(12 * 60 * 60));
861
862 case HOURLY:
863 return (t + Time(60 * 60));
864
865 case MINUTELY:
866 return (t + Time(60));
867 };
868 }
869
870
871
872 tstring
getFilename(const Time & t) const873 DailyRollingFileAppender::getFilename(const Time& t) const
874 {
875 tchar const * pattern = 0;
876 switch (schedule)
877 {
878 case MONTHLY:
879 pattern = DCMTK_LOG4CPLUS_TEXT("%Y-%m");
880 break;
881
882 case WEEKLY:
883 pattern = DCMTK_LOG4CPLUS_TEXT("%Y-%W");
884 break;
885
886 default:
887 helpers::getLogLog ().error (
888 DCMTK_LOG4CPLUS_TEXT ("DailyRollingFileAppender::getFilename()-")
889 DCMTK_LOG4CPLUS_TEXT (" invalid schedule value"));
890 // Fall through.
891
892 case DAILY:
893 pattern = DCMTK_LOG4CPLUS_TEXT("%Y-%m-%d");
894 break;
895
896 case TWICE_DAILY:
897 pattern = DCMTK_LOG4CPLUS_TEXT("%Y-%m-%d-%p");
898 break;
899
900 case HOURLY:
901 pattern = DCMTK_LOG4CPLUS_TEXT("%Y-%m-%d-%H");
902 break;
903
904 case MINUTELY:
905 pattern = DCMTK_LOG4CPLUS_TEXT("%Y-%m-%d-%H-%M");
906 break;
907 };
908
909 tstring result (filename);
910 result += DCMTK_LOG4CPLUS_TEXT(".");
911 result += t.getFormattedTime(pattern, false);
912 return result;
913 }
914
915 } // namespace log4cplus
916 } // end namespace dcmtk
917