      1: <?php
      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:
     19: /**
     20:  * LoggerAppenderFile appends log events to a file.
     21:  *
     22:  * This appender uses a layout.
     23:  *
     24:  * ## Configurable parameters: ##
     25:  *
     26:  * - **file** - Path to the target file. Relative paths are resolved based on
     27:  *     the working directory.
     28:  * - **append** - If set to true, the appender will append to the file,
     29:  *     otherwise the file contents will be overwritten.
     30:  *
     31:  * @version $Revision: 1382274 $
     32:  * @package log4php
     33:  * @subpackage appenders
     34:  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
     35:  * @link http://logging.apache.org/log4php/docs/appenders/file.html Appender documentation
     36:  */
     37: class LoggerAppenderFile extends LoggerAppender {
     38:
     39:     /**
     40:      * If set to true, the file is locked before appending. This allows
     41:      * concurrent access. However, appending without locking is faster so
     42:      * it should be used where appropriate.
     43:      *
     44:      * TODO: make this a configurable parameter
     45:      *
     46:      * @var boolean
     47:      */
     48:     protected $locking = true;
     49:
     50:     /**
     51:      * If set to true, appends to file. Otherwise overwrites it.
     52:      * @var boolean
     53:      */
     54:     protected $append = true;
     55:
     56:     /**
     57:      * Path to the target file.
     58:      * @var string
     59:      */
     60:     protected $file;
     61:
     62:     /**
     63:      * The file resource.
     64:      * @var resource
     65:      */
     66:     protected $fp;
     67:
     68:     /**
     69:      * Helper function which can be easily overriden by daily file appender.
     70:      */
     71:     protected function getTargetFile() {
     72:         return $this->file;
     73:     }
     74:
     75:     /**
     76:      * Acquires the target file resource, creates the destination folder if
     77:      * necessary. Writes layout header to file.
     78:      *
     79:      * @return boolean FALSE if opening failed
     80:      */
     81:     protected function openFile() {
     82:         $file = $this->getTargetFile();
     83:
     84:         // Create the target folder if needed
     85:         if(!is_file($file)) {
     86:             $dir = dirname($file);
     87:
     88:             if(!is_dir($dir)) {
     89:                 $success = mkdir($dir, 0777, true);
     90:                 if ($success === false) {
     91:                     $this->warn("Failed creating target directory [$dir]. Closing appender.");
     92:                     $this->closed = true;
     93:                     return false;
     94:                 }
     95:             }
     96:         }
     97:
     98:         $mode = $this->append ? 'a' : 'w';
     99:         $this->fp = fopen($file, $mode);
    100:         if ($this->fp === false) {
    101:             $this->warn("Failed opening target file. Closing appender.");
    102:             $this->fp = null;
    103:             $this->closed = true;
    104:             return false;
    105:         }
    106:
    107:         // Required when appending with concurrent access
    108:         if($this->append) {
    109:             fseek($this->fp, 0, SEEK_END);
    110:         }
    111:
    112:         // Write the header
    113:         $this->write($this->layout->getHeader());
    114:     }
    115:
    116:     /**
    117:      * Writes a string to the target file. Opens file if not already open.
    118:      * @param string $string Data to write.
    119:      */
    120:     protected function write($string) {
    121:         // Lazy file open
    122:         if(!isset($this->fp)) {
    123:             if ($this->openFile() === false) {
    124:                 return; // Do not write if file open failed.
    125:             }
    126:         }
    127:
    128:         if ($this->locking) {
    129:             $this->writeWithLocking($string);
    130:         } else {
    131:             $this->writeWithoutLocking($string);
    132:         }
    133:     }
    134:
    135:     protected function writeWithLocking($string) {
    136:         if(flock($this->fp, LOCK_EX)) {
    137:             if(fwrite($this->fp, $string) === false) {
    138:                 $this->warn("Failed writing to file. Closing appender.");
    139:                 $this->closed = true;
    140:             }
    141:             flock($this->fp, LOCK_UN);
    142:         } else {
    143:             $this->warn("Failed locking file for writing. Closing appender.");
    144:             $this->closed = true;
    145:         }
    146:     }
    147:
    148:     protected function writeWithoutLocking($string) {
    149:         if(fwrite($this->fp, $string) === false) {
    150:             $this->warn("Failed writing to file. Closing appender.");
    151:             $this->closed = true;
    152:         }
    153:     }
    154:
    155:     public function activateOptions() {
    156:         if (empty($this->file)) {
    157:             $this->warn("Required parameter 'file' not set. Closing appender.");
    158:             $this->closed = true;
    159:             return;
    160:         }
    161:     }
    162:
    163:     public function close() {
    164:         if (is_resource($this->fp)) {
    165:             $this->write($this->layout->getFooter());
    166:             fclose($this->fp);
    167:         }
    168:         $this->fp = null;
    169:         $this->closed = true;
    170:     }
    171:
    172:     public function append(LoggerLoggingEvent $event) {
    173:         $this->write($this->layout->format($event));
    174:     }
    175:
    176:     /**
    177:      * Sets the 'file' parameter.
    178:      * @param string $file
    179:      */
    180:     public function setFile($file) {
    181:         $this->setString('file', $file);
    182:     }
    183:
    184:     /**
    185:      * Returns the 'file' parameter.
    186:      * @return string
    187:      */
    188:     public function getFile() {
    189:         return $this->file;
    190:     }
    191:
    192:     /**
    193:      * Returns the 'append' parameter.
    194:      * @return boolean
    195:      */
    196:     public function getAppend() {
    197:         return $this->append;
    198:     }
    199:
    200:     /**
    201:      * Sets the 'append' parameter.
    202:      * @param boolean $append
    203:      */
    204:     public function setAppend($append) {
    205:         $this->setBoolean('append', $append);
    206:     }
    207:
    208:     /**
    209:      * Sets the 'file' parmeter. Left for legacy reasons.
    210:      * @param string $fileName
    211:      * @deprecated Use setFile() instead.
    212:      */
    213:     public function setFileName($fileName) {
    214:         $this->setFile($fileName);
    215:     }
    216:
    217:     /**
    218:      * Returns the 'file' parmeter. Left for legacy reasons.
    219:      * @return string
    220:      * @deprecated Use getFile() instead.
    221:      */
    222:     public function getFileName() {
    223:         return $this->getFile();
    224:     }
    225: }
    226: