Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Utils
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • Bar
  • BlueScreen
  • Debugger
  • Dumper
  • FireLogger
  • Helpers
  • Logger
  • OutputDebugger

Interfaces

  • IBarPanel
  • ILogger
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Tracy (https://tracy.nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Tracy;
  9: 
 10: 
 11: /**
 12:  * Logger.
 13:  */
 14: class Logger implements ILogger
 15: {
 16:     /** @var string name of the directory where errors should be logged */
 17:     public $directory;
 18: 
 19:     /** @var string|array email or emails to which send error notifications */
 20:     public $email;
 21: 
 22:     /** @var string sender of email notifications */
 23:     public $fromEmail;
 24: 
 25:     /** @var mixed interval for sending email is 2 days */
 26:     public $emailSnooze = '2 days';
 27: 
 28:     /** @var callable handler for sending emails */
 29:     public $mailer;
 30: 
 31:     /** @var BlueScreen */
 32:     private $blueScreen;
 33: 
 34: 
 35:     public function __construct($directory, $email = NULL, BlueScreen $blueScreen = NULL)
 36:     {
 37:         $this->directory = $directory;
 38:         $this->email = $email;
 39:         $this->blueScreen = $blueScreen;
 40:         $this->mailer = array($this, 'defaultMailer');
 41:     }
 42: 
 43: 
 44:     /**
 45:      * Logs message or exception to file and sends email notification.
 46:      * @param  string|\Exception|\Throwable
 47:      * @param  int   one of constant ILogger::INFO, WARNING, ERROR (sends email), EXCEPTION (sends email), CRITICAL (sends email)
 48:      * @return string logged error filename
 49:      */
 50:     public function log($message, $priority = self::INFO)
 51:     {
 52:         if (!$this->directory) {
 53:             throw new \LogicException('Directory is not specified.');
 54:         } elseif (!is_dir($this->directory)) {
 55:             throw new \RuntimeException("Directory '$this->directory' is not found or is not directory.");
 56:         }
 57: 
 58:         $exceptionFile = $message instanceof \Exception || $message instanceof \Throwable
 59:             ? $this->getExceptionFile($message)
 60:             : NULL;
 61:         $line = $this->formatLogLine($message, $exceptionFile);
 62:         $file = $this->directory . '/' . strtolower($priority ?: self::INFO) . '.log';
 63: 
 64:         if (!@file_put_contents($file, $line . PHP_EOL, FILE_APPEND | LOCK_EX)) { // @ is escalated to exception
 65:             throw new \RuntimeException("Unable to write to log file '$file'. Is directory writable?");
 66:         }
 67: 
 68:         if ($exceptionFile) {
 69:             $this->logException($message, $exceptionFile);
 70:         }
 71: 
 72:         if (in_array($priority, array(self::ERROR, self::EXCEPTION, self::CRITICAL), TRUE)) {
 73:             $this->sendEmail($message);
 74:         }
 75: 
 76:         return $exceptionFile;
 77:     }
 78: 
 79: 
 80:     /**
 81:      * @param  string|\Exception|\Throwable
 82:      * @return string
 83:      */
 84:     protected function formatMessage($message)
 85:     {
 86:         if ($message instanceof \Exception || $message instanceof \Throwable) {
 87:             while ($message) {
 88:                 $tmp[] = ($message instanceof \ErrorException
 89:                     ? Helpers::errorTypeToString($message->getSeverity()) . ': ' . $message->getMessage()
 90:                     : Helpers::getClass($message) . ': ' . $message->getMessage()
 91:                 ) . ' in ' . $message->getFile() . ':' . $message->getLine();
 92:                 $message = $message->getPrevious();
 93:             }
 94:             $message = implode($tmp, "\ncaused by ");
 95: 
 96:         } elseif (!is_string($message)) {
 97:             $message = Dumper::toText($message);
 98:         }
 99: 
100:         return trim($message);
101:     }
102: 
103: 
104:     /**
105:      * @param  string|\Exception|\Throwable
106:      * @return string
107:      */
108:     protected function formatLogLine($message, $exceptionFile = NULL)
109:     {
110:         return implode(' ', array(
111:             @date('[Y-m-d H-i-s]'), // @ timezone may not be set
112:             preg_replace('#\s*\r?\n\s*#', ' ', $this->formatMessage($message)),
113:             ' @  ' . Helpers::getSource(),
114:             $exceptionFile ? ' @@  ' . basename($exceptionFile) : NULL,
115:         ));
116:     }
117: 
118: 
119:     /**
120:      * @param  \Exception|\Throwable
121:      * @return string
122:      */
123:     public function getExceptionFile($exception)
124:     {
125:         $dir = strtr($this->directory . '/', '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR);
126:         $hash = substr(md5(preg_replace('~(Resource id #)\d+~', '$1', $exception)), 0, 10);
127:         foreach (new \DirectoryIterator($this->directory) as $file) {
128:             if (strpos($file, $hash)) {
129:                 return $dir . $file;
130:             }
131:         }
132:         return $dir . 'exception--' . @date('Y-m-d--H-i') . "--$hash.html"; // @ timezone may not be set
133:     }
134: 
135: 
136:     /**
137:      * Logs exception to the file if file doesn't exist.
138:      * @param  \Exception|\Throwable
139:      * @return string logged error filename
140:      */
141:     protected function logException($exception, $file = NULL)
142:     {
143:         $file = $file ?: $this->getExceptionFile($exception);
144:         if ($handle = @fopen($file, 'x')) { // @ file may already exist
145:             ob_start(); // double buffer prevents sending HTTP headers in some PHP
146:             ob_start(function ($buffer) use ($handle) { fwrite($handle, $buffer); }, 4096);
147:             $bs = $this->blueScreen ?: new BlueScreen;
148:             $bs->render($exception);
149:             ob_end_flush();
150:             ob_end_clean();
151:             fclose($handle);
152:         }
153:         return $file;
154:     }
155: 
156: 
157:     /**
158:      * @param  string|\Exception|\Throwable
159:      * @return void
160:      */
161:     protected function sendEmail($message)
162:     {
163:         $snooze = is_numeric($this->emailSnooze)
164:             ? $this->emailSnooze
165:             : @strtotime($this->emailSnooze) - time(); // @ timezone may not be set
166: 
167:         if ($this->email && $this->mailer
168:             && @filemtime($this->directory . '/email-sent') + $snooze < time() // @ file may not exist
169:             && @file_put_contents($this->directory . '/email-sent', 'sent') // @ file may not be writable
170:         ) {
171:             call_user_func($this->mailer, $message, implode(', ', (array) $this->email));
172:         }
173:     }
174: 
175: 
176:     /**
177:      * Default mailer.
178:      * @param  string|\Exception|\Throwable
179:      * @param  string
180:      * @return void
181:      * @internal
182:      */
183:     public function defaultMailer($message, $email)
184:     {
185:         $host = preg_replace('#[^\w.-]+#', '', isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n'));
186:         $parts = str_replace(
187:             array("\r\n", "\n"),
188:             array("\n", PHP_EOL),
189:             array(
190:                 'headers' => implode("\n", array(
191:                     'From: ' . ($this->fromEmail ?: "noreply@$host"),
192:                     'X-Mailer: Tracy',
193:                     'Content-Type: text/plain; charset=UTF-8',
194:                     'Content-Transfer-Encoding: 8bit',
195:                 )) . "\n",
196:                 'subject' => "PHP: An error occurred on the server $host",
197:                 'body' => $this->formatMessage($message) . "\n\nsource: " . Helpers::getSource(),
198:             )
199:         );
200: 
201:         mail($email, $parts['subject'], $parts['body'], $parts['headers']);
202:     }
203: 
204: }
205: 
Nette 2.3-20161221 API API documentation generated by ApiGen 2.8.0