1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #if defined(_MSC_VER)
19 #pragma warning ( disable: 4231 4251 4275 4786 )
20 #endif
21
22 #ifdef LOG4CXX_MULTI_PROCESS
23 #include <apr_portable.h>
24 #include <libgen.h>
25 #include <apr_file_io.h>
26 #include <apr_atomic.h>
27 #include <apr_mmap.h>
28 #ifndef MAX_FILE_LEN
29 #define MAX_FILE_LEN 2048
30 #endif
31 #include <log4cxx/pattern/filedatepatternconverter.h>
32 #include <log4cxx/helpers/date.h>
33 #endif
34
35 #include <log4cxx/rolling/rollingfileappender.h>
36 #include <log4cxx/helpers/loglog.h>
37 #include <log4cxx/helpers/synchronized.h>
38 #include <log4cxx/rolling/rolloverdescription.h>
39 #include <log4cxx/helpers/fileoutputstream.h>
40 #include <log4cxx/helpers/bytebuffer.h>
41 #include <log4cxx/rolling/fixedwindowrollingpolicy.h>
42 #include <log4cxx/rolling/manualtriggeringpolicy.h>
43
44 using namespace log4cxx;
45 using namespace log4cxx::rolling;
46 using namespace log4cxx::helpers;
47 using namespace log4cxx::spi;
48
49
50 IMPLEMENT_LOG4CXX_OBJECT(RollingFileAppenderSkeleton)
IMPLEMENT_LOG4CXX_OBJECT(RollingFileAppender)51 IMPLEMENT_LOG4CXX_OBJECT(RollingFileAppender)
52
53
54 /**
55 * Construct a new instance.
56 */
57 RollingFileAppenderSkeleton::RollingFileAppenderSkeleton() : _event(NULL)
58 {
59 }
60
RollingFileAppender()61 RollingFileAppender::RollingFileAppender()
62 {
63 }
64
65 /**
66 * Prepare instance of use.
67 */
activateOptions(Pool & p)68 void RollingFileAppenderSkeleton::activateOptions(Pool& p)
69 {
70 if (rollingPolicy == NULL)
71 {
72 FixedWindowRollingPolicy* fwrp = new FixedWindowRollingPolicy();
73 fwrp->setFileNamePattern(getFile() + LOG4CXX_STR(".%i"));
74 rollingPolicy = fwrp;
75 }
76
77 //
78 // if no explicit triggering policy and rolling policy is both.
79 //
80 if (triggeringPolicy == NULL)
81 {
82 TriggeringPolicyPtr trig(rollingPolicy);
83
84 if (trig != NULL)
85 {
86 triggeringPolicy = trig;
87 }
88 }
89
90 if (triggeringPolicy == NULL)
91 {
92 triggeringPolicy = new ManualTriggeringPolicy();
93 }
94
95 {
96 LOCK_W sync(mutex);
97 triggeringPolicy->activateOptions(p);
98 rollingPolicy->activateOptions(p);
99
100 try
101 {
102 RolloverDescriptionPtr rollover1 =
103 rollingPolicy->initialize(getFile(), getAppend(), p);
104
105 if (rollover1 != NULL)
106 {
107 ActionPtr syncAction(rollover1->getSynchronous());
108
109 if (syncAction != NULL)
110 {
111 syncAction->execute(p);
112 }
113
114 setFile(rollover1->getActiveFileName());
115 setAppend(rollover1->getAppend());
116
117 //
118 // async action not yet implemented
119 //
120 ActionPtr asyncAction(rollover1->getAsynchronous());
121
122 if (asyncAction != NULL)
123 {
124 asyncAction->execute(p);
125 }
126 }
127
128 File activeFile;
129 activeFile.setPath(getFile());
130
131 if (getAppend())
132 {
133 fileLength = activeFile.length(p);
134 }
135 else
136 {
137 fileLength = 0;
138 }
139
140 FileAppender::activateOptions(p);
141 }
142 catch (std::exception&)
143 {
144 LogLog::warn(
145 LogString(LOG4CXX_STR("Exception will initializing RollingFileAppender named "))
146 + getName());
147 }
148 }
149 }
150
151 #ifdef LOG4CXX_MULTI_PROCESS
releaseFileLock(apr_file_t * lock_file)152 void RollingFileAppenderSkeleton::releaseFileLock(apr_file_t* lock_file)
153 {
154 if (lock_file)
155 {
156 apr_status_t stat = apr_file_unlock(lock_file);
157
158 if (stat != APR_SUCCESS)
159 {
160 LogLog::warn(LOG4CXX_STR("flock: unlock failed"));
161 }
162
163 apr_file_close(lock_file);
164 lock_file = NULL;
165 }
166 }
167 #endif
168 /**
169 Implements the usual roll over behaviour.
170
171 <p>If <code>MaxBackupIndex</code> is positive, then files
172 {<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code>}
173 are renamed to {<code>File.2</code>, ...,
174 <code>File.MaxBackupIndex</code>}. Moreover, <code>File</code> is
175 renamed <code>File.1</code> and closed. A new <code>File</code> is
176 created to receive further log output.
177
178 <p>If <code>MaxBackupIndex</code> is equal to zero, then the
179 <code>File</code> is truncated with no backup files created.
180
181 * @return true if rollover performed.
182 */
rollover(Pool & p)183 bool RollingFileAppenderSkeleton::rollover(Pool& p)
184 {
185 //
186 // can't roll without a policy
187 //
188 if (rollingPolicy != NULL)
189 {
190
191 {
192 LOCK_W sync(mutex);
193
194 #ifdef LOG4CXX_MULTI_PROCESS
195 std::string fileName(getFile());
196 RollingPolicyBase* basePolicy = dynamic_cast<RollingPolicyBase* >(&(*rollingPolicy));
197 apr_time_t n = apr_time_now();
198 ObjectPtr obj(new Date(n));
199 LogString fileNamePattern;
200
201 if (basePolicy)
202 {
203 if (basePolicy->getPatternConverterList().size())
204 {
205 (*(basePolicy->getPatternConverterList().begin()))->format(obj, fileNamePattern, p);
206 fileName = std::string(fileNamePattern);
207 }
208 }
209
210 bool bAlreadyRolled = true;
211 char szDirName[MAX_FILE_LEN] = {'\0'};
212 char szBaseName[MAX_FILE_LEN] = {'\0'};
213 char szUid[MAX_FILE_LEN] = {'\0'};
214 memcpy(szDirName, fileName.c_str(), fileName.size() > MAX_FILE_LEN ? MAX_FILE_LEN : fileName.size());
215 memcpy(szBaseName, fileName.c_str(), fileName.size() > MAX_FILE_LEN ? MAX_FILE_LEN : fileName.size());
216 apr_uid_t uid;
217 apr_gid_t groupid;
218 apr_status_t stat = apr_uid_current(&uid, &groupid, pool.getAPRPool());
219
220 if (stat == APR_SUCCESS)
221 {
222 snprintf(szUid, MAX_FILE_LEN, "%u", uid);
223 }
224
225 const std::string lockname = std::string(::dirname(szDirName)) + "/." + ::basename(szBaseName) + szUid + ".lock";
226 apr_file_t* lock_file;
227 stat = apr_file_open(&lock_file, lockname.c_str(), APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, p.getAPRPool());
228
229 if (stat != APR_SUCCESS)
230 {
231 std::string err = "lockfile return error: open lockfile failed. ";
232 err += (strerror(errno));
233 LogLog::warn(LOG4CXX_STR(err.c_str()));
234 bAlreadyRolled = false;
235 lock_file = NULL;
236 }
237 else
238 {
239 stat = apr_file_lock(lock_file, APR_FLOCK_EXCLUSIVE);
240
241 if (stat != APR_SUCCESS)
242 {
243 std::string err = "apr_file_lock: lock failed. ";
244 err += (strerror(errno));
245 LogLog::warn(LOG4CXX_STR(err.c_str()));
246 bAlreadyRolled = false;
247 }
248 else
249 {
250 if (_event)
251 {
252 triggeringPolicy->isTriggeringEvent(this, *_event, getFile(), getFileLength());
253 }
254 }
255 }
256
257 if (bAlreadyRolled)
258 {
259 apr_finfo_t finfo1, finfo2;
260 apr_status_t st1, st2;
261 apr_file_t* _fd = getWriter()->getOutPutStreamPtr()->getFileOutPutStreamPtr().getFilePtr();
262 st1 = apr_file_info_get(&finfo1, APR_FINFO_IDENT, _fd);
263
264 if (st1 != APR_SUCCESS)
265 {
266 LogLog::warn(LOG4CXX_STR("apr_file_info_get failed"));
267 }
268
269 st2 = apr_stat(&finfo2, std::string(getFile()).c_str(), APR_FINFO_IDENT, p.getAPRPool());
270
271 if (st2 != APR_SUCCESS)
272 {
273 LogLog::warn(LOG4CXX_STR("apr_stat failed."));
274 }
275
276 bAlreadyRolled = ((st1 == APR_SUCCESS) && (st2 == APR_SUCCESS)
277 && ((finfo1.device != finfo2.device) || (finfo1.inode != finfo2.inode)));
278 }
279
280 if (!bAlreadyRolled)
281 {
282 #endif
283
284 try
285 {
286 RolloverDescriptionPtr rollover1(rollingPolicy->rollover(this->getFile(), this->getAppend(), p));
287
288 if (rollover1 != NULL)
289 {
290 if (rollover1->getActiveFileName() == getFile())
291 {
292 closeWriter();
293
294 bool success = true;
295
296 if (rollover1->getSynchronous() != NULL)
297 {
298 success = false;
299
300 try
301 {
302 success = rollover1->getSynchronous()->execute(p);
303 }
304 catch (std::exception&)
305 {
306 LogLog::warn(LOG4CXX_STR("Exception on rollover"));
307 }
308 }
309
310 if (success)
311 {
312 if (rollover1->getAppend())
313 {
314 fileLength = File().setPath(rollover1->getActiveFileName()).length(p);
315 }
316 else
317 {
318 fileLength = 0;
319 }
320
321 //
322 // async action not yet implemented
323 //
324 ActionPtr asyncAction(rollover1->getAsynchronous());
325
326 if (asyncAction != NULL)
327 {
328 asyncAction->execute(p);
329 }
330
331 setFile(
332 rollover1->getActiveFileName(), rollover1->getAppend(),
333 bufferedIO, bufferSize, p);
334 }
335 else
336 {
337 setFile(
338 rollover1->getActiveFileName(), true, bufferedIO, bufferSize, p);
339 }
340 }
341 else
342 {
343 OutputStreamPtr os(new FileOutputStream(
344 rollover1->getActiveFileName(), rollover1->getAppend()));
345 WriterPtr newWriter(createWriter(os));
346 closeWriter();
347 setFile(rollover1->getActiveFileName());
348 setWriter(newWriter);
349
350 bool success = true;
351
352 if (rollover1->getSynchronous() != NULL)
353 {
354 success = false;
355
356 try
357 {
358 success = rollover1->getSynchronous()->execute(p);
359 }
360 catch (std::exception&)
361 {
362 LogLog::warn(LOG4CXX_STR("Exception during rollover"));
363 }
364 }
365
366 if (success)
367 {
368 if (rollover1->getAppend())
369 {
370 fileLength = File().setPath(rollover1->getActiveFileName()).length(p);
371 }
372 else
373 {
374 fileLength = 0;
375 }
376
377 //
378 // async action not yet implemented
379 //
380 ActionPtr asyncAction(rollover1->getAsynchronous());
381
382 if (asyncAction != NULL)
383 {
384 asyncAction->execute(p);
385 }
386 }
387
388 writeHeader(p);
389 }
390
391 #ifdef LOG4CXX_MULTI_PROCESS
392 releaseFileLock(lock_file);
393 #endif
394 return true;
395 }
396 }
397 catch (std::exception&)
398 {
399 LogLog::warn(LOG4CXX_STR("Exception during rollover"));
400 }
401
402 #ifdef LOG4CXX_MULTI_PROCESS
403 }
404 else
405 {
406 reopenLatestFile(p);
407 }
408
409 releaseFileLock(lock_file);
410 #endif
411 }
412 }
413
414 return false;
415 }
416
417 #ifdef LOG4CXX_MULTI_PROCESS
418 /**
419 * re-open current file when its own handler has been renamed
420 */
reopenLatestFile(Pool & p)421 void RollingFileAppenderSkeleton::reopenLatestFile(Pool& p)
422 {
423 closeWriter();
424 OutputStreamPtr os(new FileOutputStream(getFile(), true));
425 WriterPtr newWriter(createWriter(os));
426 setFile(getFile());
427 setWriter(newWriter);
428 fileLength = File().setPath(getFile()).length(p);
429 writeHeader(p);
430 }
431
432 #endif
433
434 /**
435 * {@inheritDoc}
436 */
subAppend(const LoggingEventPtr & event,Pool & p)437 void RollingFileAppenderSkeleton::subAppend(const LoggingEventPtr& event, Pool& p)
438 {
439 // The rollover check must precede actual writing. This is the
440 // only correct behavior for time driven triggers.
441 if (
442 triggeringPolicy->isTriggeringEvent(
443 this, event, getFile(), getFileLength()))
444 {
445 //
446 // wrap rollover request in try block since
447 // rollover may fail in case read access to directory
448 // is not provided. However appender should still be in good
449 // condition and the append should still happen.
450 try
451 {
452 _event = &(const_cast<LoggingEventPtr&>(event));
453 rollover(p);
454 }
455 catch (std::exception&)
456 {
457 LogLog::warn(LOG4CXX_STR("Exception during rollover attempt."));
458 }
459 }
460
461 #ifdef LOG4CXX_MULTI_PROCESS
462 //do re-check before every write
463 //
464 apr_finfo_t finfo1, finfo2;
465 apr_status_t st1, st2;
466 apr_file_t* _fd = getWriter()->getOutPutStreamPtr()->getFileOutPutStreamPtr().getFilePtr();
467 st1 = apr_file_info_get(&finfo1, APR_FINFO_IDENT, _fd);
468
469 if (st1 != APR_SUCCESS)
470 {
471 LogLog::warn(LOG4CXX_STR("apr_file_info_get failed"));
472 }
473
474 st2 = apr_stat(&finfo2, std::string(getFile()).c_str(), APR_FINFO_IDENT, p.getAPRPool());
475
476 if (st2 != APR_SUCCESS)
477 {
478 std::string err = "apr_stat failed. file:" + std::string(getFile());
479 LogLog::warn(LOG4CXX_STR(err.c_str()));
480 }
481
482 bool bAlreadyRolled = ((st1 == APR_SUCCESS) && (st2 == APR_SUCCESS)
483 && ((finfo1.device != finfo2.device) || (finfo1.inode != finfo2.inode)));
484
485 if (bAlreadyRolled)
486 {
487 reopenLatestFile(p);
488 }
489
490 #endif
491
492 FileAppender::subAppend(event, p);
493 }
494
495 /**
496 * Get rolling policy.
497 * @return rolling policy.
498 */
getRollingPolicy() const499 RollingPolicyPtr RollingFileAppenderSkeleton::getRollingPolicy() const
500 {
501 return rollingPolicy;
502 }
503
504 /**
505 * Get triggering policy.
506 * @return triggering policy.
507 */
getTriggeringPolicy() const508 TriggeringPolicyPtr RollingFileAppenderSkeleton::getTriggeringPolicy() const
509 {
510 return triggeringPolicy;
511 }
512
513 /**
514 * Sets the rolling policy.
515 * @param policy rolling policy.
516 */
setRollingPolicy(const RollingPolicyPtr & policy)517 void RollingFileAppenderSkeleton::setRollingPolicy(const RollingPolicyPtr& policy)
518 {
519 rollingPolicy = policy;
520 }
521
522 /**
523 * Set triggering policy.
524 * @param policy triggering policy.
525 */
setTriggeringPolicy(const TriggeringPolicyPtr & policy)526 void RollingFileAppenderSkeleton::setTriggeringPolicy(const TriggeringPolicyPtr& policy)
527 {
528 triggeringPolicy = policy;
529 }
530
531 /**
532 * Close appender. Waits for any asynchronous file compression actions to be completed.
533 */
close()534 void RollingFileAppenderSkeleton::close()
535 {
536 FileAppender::close();
537 }
538
539 namespace log4cxx
540 {
541 namespace rolling
542 {
543 /**
544 * Wrapper for OutputStream that will report all write
545 * operations back to this class for file length calculations.
546 */
547 class CountingOutputStream : public OutputStream
548 {
549 /**
550 * Wrapped output stream.
551 */
552 private:
553 OutputStreamPtr os;
554
555 /**
556 * Rolling file appender to inform of stream writes.
557 */
558 RollingFileAppenderSkeleton* rfa;
559
560 public:
561 /**
562 * Constructor.
563 * @param os output stream to wrap.
564 * @param rfa rolling file appender to inform.
565 */
CountingOutputStream(OutputStreamPtr & os1,RollingFileAppenderSkeleton * rfa1)566 CountingOutputStream(
567 OutputStreamPtr& os1, RollingFileAppenderSkeleton* rfa1) :
568 os(os1), rfa(rfa1)
569 {
570 }
571
572 /**
573 * {@inheritDoc}
574 */
close(Pool & p)575 void close(Pool& p)
576 {
577 os->close(p);
578 rfa = 0;
579 }
580
581 /**
582 * {@inheritDoc}
583 */
flush(Pool & p)584 void flush(Pool& p)
585 {
586 os->flush(p);
587 }
588
589 /**
590 * {@inheritDoc}
591 */
write(ByteBuffer & buf,Pool & p)592 void write(ByteBuffer& buf, Pool& p)
593 {
594 os->write(buf, p);
595
596 if (rfa != 0)
597 {
598 #ifndef LOG4CXX_MULTI_PROCESS
599 rfa->incrementFileLength(buf.limit());
600 #else
601 rfa->setFileLength(File().setPath(rfa->getFile()).length(p));
602 #endif
603 }
604 }
605
606 #ifdef LOG4CXX_MULTI_PROCESS
getFileOutPutStreamPtr()607 OutputStream& getFileOutPutStreamPtr()
608 {
609 return *os;
610 }
611 #endif
612 };
613 }
614 }
615
616 /**
617 Returns an OutputStreamWriter when passed an OutputStream. The
618 encoding used will depend on the value of the
619 <code>encoding</code> property. If the encoding value is
620 specified incorrectly the writer will be opened using the default
621 system encoding (an error message will be printed to the loglog.
622 @param os output stream, may not be null.
623 @return new writer.
624 */
createWriter(OutputStreamPtr & os)625 WriterPtr RollingFileAppenderSkeleton::createWriter(OutputStreamPtr& os)
626 {
627 OutputStreamPtr cos(new CountingOutputStream(os, this));
628 return FileAppender::createWriter(cos);
629 }
630
631 /**
632 * Get byte length of current active log file.
633 * @return byte length of current active log file.
634 */
getFileLength() const635 size_t RollingFileAppenderSkeleton::getFileLength() const
636 {
637 return fileLength;
638 }
639
640 #ifdef LOG4CXX_MULTI_PROCESS
setFileLength(size_t length)641 void RollingFileAppenderSkeleton::setFileLength(size_t length)
642 {
643 fileLength = length;
644 }
645 #endif
646
647 /**
648 * Increments estimated byte length of current active log file.
649 * @param increment additional bytes written to log file.
650 */
incrementFileLength(size_t increment)651 void RollingFileAppenderSkeleton::incrementFileLength(size_t increment)
652 {
653 fileLength += increment;
654 }
655