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
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

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

Interfaces

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