A Logger instance can be accessed via Yii::getLogger(). You can call the method Logger::log to record a single log message.
For convenience, a set of shortcut methods are provided for logging messages of various severity levels
via the [[Yii]] class:
- [[Yii::trace()]]
- [[Yii::error()]]
- [[Yii::warning()]]
- [[Yii::info()]]
- [[Yii::beginProfile()]]
- [[Yii::endProfile()]]
For more details and usage information on Logger, see the guide article on logging.
When the application ends or [[flushInterval]] is reached, Logger will call Logger::flush
to send logged messages to different log targets, such as [[FileTarget|file]], [[EmailTarget|email]],
or [[DbTarget|database]], with the help of the [[dispatcher]].
public function testLog() { $logger = new Logger(); $logger->log('test1', Logger::LEVEL_INFO); $this->assertEquals(1, count($logger->messages)); $this->assertEquals('test1', $logger->messages[0][0]); $this->assertEquals(Logger::LEVEL_INFO, $logger->messages[0][1]); $this->assertEquals('application', $logger->messages[0][2]); $logger->log('test2', Logger::LEVEL_ERROR, 'category'); $this->assertEquals(2, count($logger->messages)); $this->assertEquals('test2', $logger->messages[1][0]); $this->assertEquals(Logger::LEVEL_ERROR, $logger->messages[1][1]); $this->assertEquals('category', $logger->messages[1][2]); }
protected static function getLevelName($level) { if (in_array($level, [Logger::LEVEL_PROFILE, Logger::LEVEL_PROFILE_BEGIN, Logger::LEVEL_PROFILE_END, Logger::LEVEL_TRACE])) { return 'debug'; } return Logger::getLevelName($level); }
/** * Stores log messages to sentry. */ public function export() { foreach ($this->messages as $message) { list($msg, $level, $catagory, $timestamp, $traces) = $message; $errStr = ''; $options = ['level' => \yii\log\Logger::getLevelName($level), 'extra' => []]; $templateData = null; if (is_array($msg)) { $errStr = isset($msg['msg']) ? $msg['msg'] : ''; if (isset($msg['data'])) { $options['extra'] = $msg['data']; } } elseif (is_a($msg, \yii\base\Exception::class)) { $errStr = $msg->getMessage(); $traces = $msg->getTrace(); } else { $errStr = $msg; } // Store debug trace in extra data $traces = array_map(function ($v) { $file = isset($v['file']) ? $v['file'] : 'unknown file'; $line = isset($v['line']) ? $v['line'] : 'unknown line'; $class = isset($v['class']) ? $v['class'] : 'unknown class'; $function = isset($v['function']) ? $v['function'] : 'unknown function'; return $file . PHP_EOL . "{$class}::{$function} [{$line}]"; }, $traces); if (!empty($traces)) { $options['extra']['traces'] = $traces; } $this->client->captureMessage($errStr, [], $options, false); } }
public function __call($name, $params) { try { return parent::__call($name, $params); } catch (UnknownMethodException $e) { return call_user_func_array([$this->getOldLogger(), $name], $params); } }
/** * @inheritdoc */ public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); if (!is_string($text)) { $text = var_export($text, true); } $prefix = $this->prefix ? call_user_func($this->prefix, $message) : $this->getMessagePrefix($message); return "{$prefix}[{$level}][{$category}] {$text}"; }
/** * @inheritdoc */ public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); if (!is_string($text)) { $text = VarDumper::export($text); } $prefix = $this->getMessagePrefix($message); return "{$prefix}[{$level}][{$category}] {$text}"; }
/** * Logs a message to console and then to yii\log\Logger. */ public function log($message, $level, $category = 'application') { if ($level <= $this->getSpamLevel()) { $style = self::$styles[$level]; if ($style) { $message = Console::ansiFormat($message, $style); } Console::stdout($message . "\n"); } parent::log($message, $level, $category); }
public function log($message, $level, $category = 'application') { $mougrimLoggerLevel = $this->getMougrimPhpLoggerLevel($level); if ($mougrimLoggerLevel !== null) { $this->getMougrimLogger($category)->log($mougrimLoggerLevel, $message); if (!$this->getAlwaysYiiLoggerLog()) { return; } } parent::log($message, $level, $category); }
/** * Sends log message to Sentry instance. */ public function export() { foreach ($this->messages as $message) { $options = ['level' => Logger::getLevelName($message[1])]; $logMessage = $this->formatMessage($message); $text = $message[0]; if (!is_string($text) && $text instanceof \Exception) { $logMessage = $text->getMessage() . "\n\n" . $logMessage; } $this->getClient()->captureMessage($logMessage, [], $options); } }
/** * @dataProvider filters */ public function testFilter($filter, $expected) { static::$messages = []; $logger = new Logger(); $dispatcher = new Dispatcher(['logger' => $logger, 'targets' => [new TestTarget(array_merge($filter, ['logVars' => []]))], 'flushInterval' => 1]); $logger->log('testA', Logger::LEVEL_INFO); $logger->log('testB', Logger::LEVEL_ERROR); $logger->log('testC', Logger::LEVEL_WARNING); $logger->log('testD', Logger::LEVEL_TRACE); $logger->log('testE', Logger::LEVEL_INFO, 'application'); $logger->log('testF', Logger::LEVEL_INFO, 'application.components.Test'); $logger->log('testG', Logger::LEVEL_ERROR, 'yii.db.Command'); $logger->log('testH', Logger::LEVEL_ERROR, 'yii.db.Command.whatever'); $this->assertEquals(count($expected), count(static::$messages)); $i = 0; foreach ($expected as $e) { $this->assertEquals('test' . $e, static::$messages[$i++][0]); } }
/** * @dataProvider booleanDataProvider */ public function testRotate($rotateByCopy) { $logFile = Yii::getAlias('@yiiunit/runtime/log/filetargettest.log'); FileHelper::removeDirectory(dirname($logFile)); mkdir(dirname($logFile), 0777, true); $logger = new Logger(); $dispatcher = new Dispatcher(['logger' => $logger, 'targets' => ['file' => ['class' => 'yii\\log\\FileTarget', 'logFile' => $logFile, 'levels' => ['warning'], 'maxFileSize' => 1024, 'maxLogFiles' => 1, 'logVars' => [], 'rotateByCopy' => $rotateByCopy]]]); // one file $logger->log(str_repeat('x', 1024), Logger::LEVEL_WARNING); $logger->flush(true); clearstatcache(); $this->assertTrue(file_exists($logFile)); $this->assertFalse(file_exists($logFile . '.1')); $this->assertFalse(file_exists($logFile . '.2')); $this->assertFalse(file_exists($logFile . '.3')); $this->assertFalse(file_exists($logFile . '.4')); // exceed max size for ($i = 0; $i < 1024; $i++) { $logger->log(str_repeat('x', 1024), Logger::LEVEL_WARNING); } $logger->flush(true); // first rotate $logger->log(str_repeat('x', 1024), Logger::LEVEL_WARNING); $logger->flush(true); clearstatcache(); $this->assertTrue(file_exists($logFile)); $this->assertTrue(file_exists($logFile . '.1')); $this->assertFalse(file_exists($logFile . '.2')); $this->assertFalse(file_exists($logFile . '.3')); $this->assertFalse(file_exists($logFile . '.4')); // second rotate for ($i = 0; $i < 1024; $i++) { $logger->log(str_repeat('x', 1024), Logger::LEVEL_WARNING); } $logger->flush(true); clearstatcache(); $this->assertTrue(file_exists($logFile)); $this->assertTrue(file_exists($logFile . '.1')); $this->assertFalse(file_exists($logFile . '.2')); $this->assertFalse(file_exists($logFile . '.3')); $this->assertFalse(file_exists($logFile . '.4')); }
public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); if (!is_string($text)) { // exceptions may not be serializable if in the call stack somewhere is a Closure if ($text instanceof \Exception) { $text = (string) $text; } else { $text = VarDumper::export($text); } } $prefix = $this->getMessagePrefix($message); return "Level: " . ucfirst($level) . " \n\n Time: " . date('F j, Y, g:i a (T)', $timestamp) . "\n\n Message:\n {$text} \n\n {$prefix} \n\n"; }
/** * Exports log [[messages]] to a specific destination. */ public function export() { foreach ($this->messages as $message) { list($msg, $level, $category, $timestamp, $traces) = $message; $levelName = Logger::getLevelName($level); if (!in_array($levelName, ['error', 'warning', 'info'])) { $levelName = 'error'; } $data = ['timestamp' => gmdate('Y-m-d\\TH:i:s\\Z', $timestamp), 'level' => $levelName, 'tags' => ['category' => $category], 'message' => $msg]; if (!empty($traces)) { $data['sentry.interfaces.Stacktrace'] = ['frames' => Raven_Stacktrace::get_stack_info($traces)]; } $this->client->capture($data, false); } }
/** * @inheritdoc */ public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); if (!is_string($text)) { // exceptions may not be serializable if in the call stack somewhere is a Closure if ($text instanceof \Throwable || $text instanceof \Exception) { $text = (string) $text; } else { $text = VarDumper::export($text); } } $prefix = $this->getMessagePrefix($message); return "{$prefix}[{$level}][{$category}] {$text}"; }
/** * Writes log messages to a file. */ public function export() { $data = []; foreach ($this->messages as $message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); //$messageString = date('Y-m-d H:i:s', $timestamp) . " [$level][$category] $text"; //$messageArray = explode(PHP_EOL, $messageString); $needSkip = strpos($category, self::I18N_MESSAGE_KEY) !== false; if (strpos(LOG_LEVEL, $level) !== false && !$needSkip) { //$data[$level][] = $messageArray; $data[$level] = $text; } } foreach ($data as $key => $value) { LogUtil::$key($value); } }
/** * Calls the named method which is not a class method. * * This method will check whether PSR-3 or Yii2 format is used and will execute the corresponding method. * * @param string $name The method name. * @param array $arguments Method arguments. * * @return mixed The method return value. */ public function __call($name, array $arguments) { // Intercept calls to the "log()" method if ($name === 'log' && !empty($arguments)) { // PSR-3 format $reflection = new ReflectionClass(YiiLogger::className()); if (in_array($arguments[0], array_keys($this->_monolog->getLevels())) && !in_array($arguments[1], array_keys($reflection->getConstants())) && (!isset($arguments[3]) || isset($arguments[3]) && is_array($arguments[3]))) { return call_user_func_array([$this, 'log'], $arguments); } // Yii2 format return call_user_func_array([$this, 'yiiLog'], $arguments); } // Execute Monolog methods, if they exists if (method_exists($this->_monolog, $name) && !method_exists($this, $name)) { return call_user_func_array([$this->_monolog, $name], $arguments); } return parent::__call($name, $arguments); }
/** * (non-PHPdoc) * @see \yii\log\Target::formatMessage() */ public function formatMessage($message) { if (!$this->with_microtime) { return parent::formatMessage($message); } list($text, $level, $category, $timestamp) = $message; $level = \yii\log\Logger::getLevelName($level); if (!is_string($text)) { $text = VarDumper::export($text); } $traces = []; if (isset($message[4])) { foreach ($message[4] as $trace) { $traces[] = "in {$trace['file']}:{$trace['line']}"; } } $prefix = $this->getMessagePrefix($message); return (new \DateTime())->setTimestamp($timestamp)->format('Y-m-d H:i:s') . ':' . str_pad(round(($timestamp - floor($timestamp)) * 1000000), 6, '0', STR_PAD_RIGHT) . " {$prefix}[{$level}][{$category}] {$text}" . (empty($traces) ? '' : "\n " . implode("\n ", $traces)); }
/** * Formats a log message for display as a string. * @param array $message the log message to be formatted. * The message structure follows that in [[Logger::messages]]. * @return string the formatted message */ public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); if (!is_string($text)) { // exceptions may not be serializable if in the call stack somewhere is a Closure if ($text instanceof \Throwable || $text instanceof \Exception) { $text = sprintf('[%s] %s', $text->getCode(), $text->getMessage()); } elseif (is_array($text)) { /* * Only VarDump if array, don't want to inadvertently log objects with sensitive info */ $text = VarDumper::export($text); } else { $text = sprintf('Unsupported object of type %s sent to logger', get_class($text)); } } $prefix = $this->getMessagePrefix($message); return date('Y-m-d H:i:s', $timestamp) . " {$prefix}[{$level}][{$category}] {$text}"; }
public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = log\Logger::getLevelName($level); if (!is_string($text)) { $text = VarDumper::export($text); } $traces = []; if (isset($message[4])) { foreach ($message[4] as $trace) { $traces[] = "in {$trace['file']}:{$trace['line']}"; } } $prefix = $this->getMessagePrefix($message); $text = str_replace(array("\t", "\n"), '', $text); $logs = ['time' => date('Y-m-d H:i:s', $timestamp), 'ip' => $prefix['ip'], 'reqId' => $prefix['reqId'], 'module' => $prefix['module'], 'level' => $level, 'category' => $category, 'text' => $text]; if (!empty($traces)) { $logs['traces'] = $traces[0]; } return implode("\t", $logs); }
/** * Formats a log message for display as a string. * @param array $message the log message to be formatted. * The message structure follows that in Logger::messages. * @return string the formatted message */ public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); if (!is_string($text)) { // exceptions may not be serializable if in the call stack somewhere is a Closure if ($text instanceof \Exception) { $text = (string) $text; } else { $text = VarDumper::export($text); } } $traces = []; if (isset($message[4])) { foreach ($message[4] as $trace) { $traces[] = "in {$trace['file']}:{$trace['line']}"; } } $prefix = $this->getMessagePrefix($message); return date('Y-m-d H:i:s', $timestamp) . '.' . (strpos($timestamp, '.') ? str_pad(explode('.', (string) $timestamp)[1], 10, '0') : '0000000000') . " {$prefix}[{$level}][{$category}] {$text}" . (empty($traces) ? '' : "\n " . implode("\n ", $traces)); }
/** * @param $message * * @return string */ private function generateLabel($message) { $label = ''; //Add date to log if (true == $this->displayDate) { $label .= '[' . date($this->dateFormat, time()) . ']'; } //Add category to label if (true == $this->displayCategory) { $label .= "[" . $message[2] . "]"; } $level = Logger::getLevelName($message[1]); $tmpLevel = "[{$level}]"; if (Console::streamSupportsAnsiColors(\STDOUT)) { if (isset($this->color[$level])) { $tmpLevel = Console::ansiFormat($tmpLevel, [$this->color[$level]]); } else { $tmpLevel = Console::ansiFormat($tmpLevel, [Console::BOLD]); } } $label .= $tmpLevel; return $label; }
/** * Stores log messages to sentry. */ public function export() { foreach ($this->messages as $message) { list($msg, $level, $catagory, $timestamp, $traces) = $message; $options = ['level' => log\Logger::getLevelName($level), 'extra' => []]; $templateData = null; if (is_array($msg)) { $errStr = isset($msg['msg']) ? $msg['msg'] : ''; if (isset($msg['data'])) { $options['extra'] = $msg['data']; } } else { $errStr = $msg; } // Store debug trace in extra data $traces = array_map(function ($v) { return "{$v['file']}" . PHP_EOL . "{$v['class']}::{$v['function']} [{$v['line']}]"; }, $traces); if (!empty($traces)) { $options['extra']['traces'] = $traces; } $this->client->captureMessage($errStr, array(), $options, false); } }
/** * Exports log [[messages]] to a specific destination. */ public function export() { foreach ($this->messages as $message) { list($msg, $level, $category, $timestamp, $traces) = $message; $levelName = Logger::getLevelName($level); if (!in_array($levelName, ['error', 'warning', 'info'])) { $levelName = 'error'; } if (is_array($msg)) { if (isset($msg['data'])) { $new_extras = $msg['data']; unset($msg['data']); } if (isset($msg['msg'])) { $new_msg = $msg['msg']; unset($msg['msg']); } else { $new_msg = 'Unknown event format'; // deliver event data even if the format doesn't fit $new_extras = array_merge($new_extras, $msg); $new_tags = ['format' => 'unknown']; } } $data = ['timestamp' => gmdate('Y-m-d\\TH:i:s\\Z', $timestamp), 'level' => $levelName, 'tags' => ['category' => $category], 'message' => isset($new_msg) ? $new_msg : $msg]; if (isset($new_tags)) { $data['tags'] = array_merge($new_tags, $this->client->get_tags(), $this->client->context->extra); } if (isset($new_extras)) { $data['extra'] = array_merge($new_extras, $this->client->tags, $this->client->context->tags); } if (!empty($traces)) { $data['sentry.interfaces.Stacktrace'] = ['frames' => Raven_Stacktrace::get_stack_info($traces)]; } $this->client->capture($data, false); } }
} else { return $data['timestamp']; } }, 'headerOptions' => ['class' => 'sort-numerical text-align-center'], 'footerOptions' => ['class' => 'sort-numerical text-align-center font-weight-bold th'], 'contentOptions' => ['class' => 'nowrap font-size-10px text-align-center']], ['attribute' => 'level', 'value' => function ($data) { switch ($data['level']) { case Logger::LEVEL_ERROR: $class = 'label label-danger'; break; case Logger::LEVEL_WARNING: $class = 'label label-warning'; break; case Logger::LEVEL_INFO: $class = 'label label-primary'; break; default: $class = 'label label-default'; break; } return Html::tag('span', Logger::getLevelName($data['level']), ['class' => $class]); }, 'format' => 'html', 'filter' => [Logger::LEVEL_TRACE => ' Trace ', Logger::LEVEL_INFO => ' Info ', Logger::LEVEL_WARNING => ' Warning ', Logger::LEVEL_ERROR => ' Error '], 'filterInputOptions' => ['class' => 'form-control chosen-select', 'id' => null, 'prompt' => ' All '], 'headerOptions' => ['class' => 'text-align-center'], 'footerOptions' => ['class' => 'text-align-center font-weight-bold th'], 'contentOptions' => ['class' => 'text-align-center']], ['attribute' => 'category', 'headerOptions' => ['class' => 'text-align-center'], 'footerOptions' => ['class' => 'text-align-center font-weight-bold th'], 'contentOptions' => ['class' => 'font-size-10px']], ['attribute' => 'message', 'value' => function ($data) { $description = Html::encode(is_string($data['message']) ? $data['message'] : VarDumper::export($data['message'])); $message = '<a href="javascript:;" class="spoiler-title" data-title="" data-content="' . $description . '">' . Stringy::substr(Stringy::collapseWhitespace($description), 0, 60, 'UTF-8') . '...' . '</a>'; $trace = ''; if (!empty($data['trace'])) { $trace .= Html::ul($data['trace'], ['class' => 'trace', 'item' => function ($trace) { return "<li>{$trace['file']} ({$trace['line']})</li>"; }]); } $message .= '<div class="spoiler-content"><strong>Message: </strong><br /><pre>' . $description . '</pre>' . (!empty($trace) ? '<br /><p><strong>Trace: </strong>' . $trace . '</p>' : '') . '</div>'; return $message; }, 'format' => 'html', 'options' => ['width' => '50%'], 'contentOptions' => ['class' => 'spoiler']]]]);
use yii\grid\GridView; use yii\helpers\Html; /* @var $this yii\web\View */ /* @var $searchModel backend\models\search\SystemLogSearch */ /* @var $dataProvider yii\data\ActiveDataProvider */ $this->title = Yii::t('backend', 'System Logs'); $this->params['breadcrumbs'][] = $this->title; ?> <div class="system-log-index"> <p> <?php echo Html::a(Yii::t('backend', 'Clear'), false, ['class' => 'btn btn-danger', 'data-method' => 'delete']); ?> </p> <?php // echo $this->render('_search', ['model' => $searchModel]); ?> <?php echo GridView::widget(['dataProvider' => $dataProvider, 'filterModel' => $searchModel, 'columns' => [['class' => 'yii\\grid\\SerialColumn'], ['attribute' => 'level', 'value' => function ($model) { return \yii\log\Logger::getLevelName($model->level); }, 'filter' => [\yii\log\Logger::LEVEL_ERROR => 'error', \yii\log\Logger::LEVEL_WARNING => 'warning', \yii\log\Logger::LEVEL_INFO => 'info', \yii\log\Logger::LEVEL_TRACE => 'trace', \yii\log\Logger::LEVEL_PROFILE_BEGIN => 'profile begin', \yii\log\Logger::LEVEL_PROFILE_END => 'profile end']], 'category', 'prefix', ['attribute' => 'log_time', 'format' => 'datetime', 'value' => function ($model) { return (int) $model->log_time; }], ['class' => 'yii\\grid\\ActionColumn', 'template' => '{view}{delete}']]]); ?> </div>
<?php use yii\bootstrap\Html; use yii\grid\GridView; use yii\log\Logger; /* @var $this yii\web\View */ /* @var $searchModel backend\models\search\LogSearch */ /* @var $dataProvider yii\data\ActiveDataProvider */ $this->title = Yii::t('backend', 'Logs'); $this->params['breadcrumbs'][] = $this->title; ?> <div class="log-index"> <p> <?php echo Html::a(Yii::t('backend', 'Clear'), false, ['class' => 'btn btn-danger', 'data-method' => 'delete']); ?> </p> <?php echo GridView::widget(['dataProvider' => $dataProvider, 'filterModel' => $searchModel, 'columns' => [['attribute' => 'level', 'value' => function ($model) { return Logger::getLevelName($model->level); }, 'filter' => [Logger::LEVEL_ERROR => 'error', Logger::LEVEL_WARNING => 'warning', Logger::LEVEL_INFO => 'info', Logger::LEVEL_TRACE => 'trace', Logger::LEVEL_PROFILE_BEGIN => 'profile begin', Logger::LEVEL_PROFILE_END => 'profile end']], 'category', ['attribute' => 'log_time', 'format' => 'datetime', 'value' => function ($model) { return (int) $model->log_time; }], 'prefix', ['class' => 'yii\\grid\\ActionColumn', 'template' => '{view} {delete}']]]); ?> </div>
/** * Transform log message to assoc. * * @param array $message The log message. * * @return array */ protected function prepareMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); $timestamp = date('c', $timestamp); $result = ArrayHelper::merge($this->parseText($text), ['level' => $level, 'category' => $category, '@timestamp' => $timestamp]); if (isset($message[4]) === true) { $result['trace'] = $message[4]; } return $result; }
/** * Formats a log message for display as a string. * @param array $message the log message to be formatted. * The message structure follows that in [[Logger::messages]]. * @return string the formatted message */ public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); if (!is_string($text)) { $text = VarDumper::export($text); } $traces = []; if (isset($message[4])) { foreach ($message[4] as $trace) { $traces[] = "in {$trace['file']}:{$trace['line']}"; } } $prefix = $this->getMessagePrefix($message); return date('Y-m-d H:i:s', $timestamp) . " {$prefix}[{$level}][{$category}] {$text}" . (empty($traces) ? '' : "\n " . implode("\n ", $traces)); }
/** * Compile log message. Adds remote ip address and trail id if enabled. * * @param array $message * @return array */ public function formatMessage($message) { list($text, $level, $category, $timestamp) = $message; $level = Logger::getLevelName($level); $msg = ['timestamp' => date('Y/m/d H:i:s', $timestamp), 'level' => $level, 'category' => $category, 'message' => $text]; if ($this->enableIp) { $msg['ip'] = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'; } if ($this->enableTrail) { $msg['trail'] = $this->trail; } return $msg; }
/** * Default line formatting * * @param string $text the actual log-text * @param integer $level the log level * @param string $category the log category * @param integer $timestamp the events timestamp * @return string the formatted line */ public function formatLine($text, $level, $category, $timestamp) { return sprintf("%-19s [%-7s] [%s]\n %s", Yii::$app->formatter->asDatetime($timestamp, 'php:d.m.Y H:i:s'), strtoupper(Logger::getLevelName($level)), $category, $text); }