1: <?php
2:
3: class LoggerAppenderDailyRollingFile extends LoggerAppenderFile {
4:
5: /**
6: * The 'datePattern' parameter.
7: * Determines how date will be formatted in file name.
8: * @var string
9: */
10: protected $datePattern = "Ymd";
11:
12: /**
13: * Current date which was used when opening a file.
14: * Used to determine if a rollover is needed when the date changes.
15: * @var string
16: */
17: protected $currentDate;
18:
19: /**
20: * The maximum size (in bytes) that the output file is allowed to reach
21: * before being rolled over to backup files.
22: *
23: * The default maximum file size is 10MB (10485760 bytes). Maximum value
24: * for this option may depend on the file system.
25: *
26: * @var integer
27: */
28: protected $maxFileSize = 10485760;
29:
30: /**
31: * Set the maximum number of backup files to keep around.
32: *
33: * Determines how many backup files are kept before the oldest is erased.
34: * This option takes a positive integer value. If set to zero, then there
35: * will be no backup files and the log file will be truncated when it
36: * reaches <var>maxFileSize</var>.
37: *
38: * There is one backup file by default.
39: *
40: * @var integer
41: */
42: protected $maxBackupIndex = 1;
43:
44:
45:
46: /**
47: * Appends a logging event.
48: *
49: * If the target file changes because of passage of time (e.g. at midnight)
50: * the current file is closed. A new file, with the new date, will be
51: * opened by the write() method.
52: */
53: public function append(LoggerLoggingEvent $event) {
54: $eventDate = $this->getDate($event->getTimestamp());
55:
56: // Initial setting of current date
57: if (!isset($this->currentDate)) {
58: $this->currentDate = $eventDate;
59: }
60:
61: // Check if rollover is needed
62: else if ($this->currentDate !== $eventDate) {
63: $this->currentDate = $eventDate;
64:
65:
66: // Close the file if it's open.
67: // Note: $this->close() is not called here because it would set
68: // $this->closed to true and the appender would not recieve
69: // any more logging requests
70:
71: if (is_resource($this->fp)) {
72: $this->write($this->layout->getFooter());
73: fclose($this->fp);
74: }
75: $this->fp = null;
76: }
77: //$this->rollOver();
78: //var_dump($this);
79: //parent::append($event);
80: $this->write($this->layout->getFooter());
81: }
82:
83: /**
84: * Writes a string to the target file. Opens file if not already open.
85: * @param string $string Data to write.
86: */
87: protected function write($string) {
88: // Lazy file open
89: if(!isset($this->fp)) {
90: if ($this->openFile() === false) {
91: return; // Do not write if file open failed.
92: }
93: }
94:
95: // Lock the file while writing and possible rolling over
96: if(flock($this->fp, LOCK_EX)) {
97:
98: // Write to locked file
99: if(fwrite($this->fp, $string) === false) {
100: $this->warn("Failed writing to file. Closing appender.");
101: $this->closed = true;
102: }
103:
104: // Rollover if needed
105: if (filesize($this->file) > $this->maxFileSize) {
106: try {
107: $this->rollOver();
108: } catch (LoggerException $ex) {
109: $this->warn("Rollover failed: " . $ex->getMessage() . " Closing appender.");
110: $this->closed = true;
111: }
112: }
113:
114: flock($this->fp, LOCK_UN);
115: } else {
116: $this->warn("Failed locking file for writing. Closing appender.");
117: $this->closed = true;
118: }
119: }
120:
121: private function renameArchievedLogs($fileName) {
122: for($i = $this->maxBackupIndex - 1; $i >= 1; $i--) {
123:
124: $source = $fileName . "." . $i;
125:
126:
127: if(file_exists($source)) {
128: $target = $fileName . '.' . ($i + 1);
129:
130: rename($source, $target);
131: }
132: }
133: }
134:
135: /**
136: * Implements the usual roll over behaviour.
137: *
138: * If MaxBackupIndex is positive, then files File.1, ..., File.MaxBackupIndex -1 are renamed to File.2, ..., File.MaxBackupIndex.
139: * Moreover, File is renamed File.1 and closed. A new File is created to receive further log output.
140: *
141: * If MaxBackupIndex is equal to zero, then the File is truncated with no backup files created.
142: *
143: * Rollover must be called while the file is locked so that it is safe for concurrent access.
144: *
145: * @throws LoggerException If any part of the rollover procedure fails.
146: */
147: private function rollOver() {
148: // If maxBackups <= 0, then there is no file renaming to be done.
149: if($this->maxBackupIndex > 0) {
150: var_dump($this->maxBackupIndex);
151: // Delete the oldest file, to keep Windows happy.
152: $file = $this->file . '.' . $this->maxBackupIndex;
153:
154: if (file_exists($file) && !unlink($file)) {
155: throw new LoggerException("Unable to delete oldest backup file from [$file].");
156: }
157:
158: // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
159: $this->renameArchievedLogs($this->file);
160:
161: }
162:
163: // Truncate the active file
164: //ftruncate($this->fp, 0);
165: //rewind($this->fp);
166: }
167:
168: /** Additional validation for the date pattern. */
169: public function activateOptions() {
170: parent::activateOptions();
171:
172: if (empty($this->datePattern)) {
173: $this->warn("Required parameter 'datePattern' not set. Closing appender.");
174: $this->closed = true;
175: return;
176: }
177: }
178:
179: /** Renders the date using the configured <var>datePattern<var>. */
180: protected function getDate($timestamp = null) {
181: return date($this->datePattern, $timestamp);
182: }
183:
184: /**
185: * Determines target file. Replaces %s in file path with a date.
186: */
187: protected function getTargetFile() {
188: return str_replace('%s', $this->currentDate, $this->file);
189: }
190:
191: /**
192: * Sets the 'datePattern' parameter.
193: * @param string $datePattern
194: */
195: public function setDatePattern($datePattern) {
196: $this->setString('datePattern', $datePattern);
197: }
198:
199: /**
200: * Returns the 'datePattern' parameter.
201: * @return string
202: */
203: public function getDatePattern() {
204: return $this->datePattern;
205: }
206:
207: /**
208: * Get the maximum size that the output file is allowed to reach
209: * before being rolled over to backup files.
210: * @return integer
211: */
212: public function getMaximumFileSize() {
213: return $this->maxFileSize;
214: }
215:
216: /**
217: * Set the 'maxBackupIndex' parameter.
218: * @param integer $maxBackupIndex
219: */
220: public function setMaxBackupIndex($maxBackupIndex) {
221: $this->setPositiveInteger('maxBackupIndex', $maxBackupIndex);
222: }
223:
224: /**
225: * Returns the 'maxBackupIndex' parameter.
226: * @return integer
227: */
228: public function getMaxBackupIndex() {
229: return $this->maxBackupIndex;
230: }
231:
232: /**
233: * Set the 'maxFileSize' parameter.
234: * @param mixed $maxFileSize
235: */
236: public function setMaxFileSize($maxFileSize) {
237: $this->setFileSize('maxFileSize', $maxFileSize);
238: }
239:
240: /**
241: * Returns the 'maxFileSize' parameter.
242: * @return integer
243: */
244: public function getMaxFileSize() {
245: return $this->maxFileSize;
246: }
247:
248: /**
249: * Set the 'maxFileSize' parameter (kept for backward compatibility).
250: * @param mixed $maxFileSize
251: * @deprecated Use setMaxFileSize() instead.
252: */
253: public function setMaximumFileSize($maxFileSize) {
254: $this->warn("The 'maximumFileSize' parameter is deprecated. Use 'maxFileSize' instead.");
255: return $this->setMaxFileSize($maxFileSize);
256: }
257: }