2019-05-24 16:40:34 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Ardent\LoggerBundle\Service;
|
|
|
|
|
|
|
|
use App\Ardent\LoggerBundle\Entity\LogEntry;
|
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
|
|
use Psr\Log\InvalidArgumentException;
|
|
|
|
|
|
|
|
class LoggerService
|
|
|
|
{
|
|
|
|
/**
|
2019-05-28 16:06:03 +02:00
|
|
|
* Detailed debug information.
|
2019-05-24 16:40:34 +02:00
|
|
|
*/
|
|
|
|
const DEBUG = 100;
|
|
|
|
|
|
|
|
/**
|
2019-05-28 16:06:03 +02:00
|
|
|
* Interesting events.
|
2019-05-24 16:40:34 +02:00
|
|
|
*
|
|
|
|
* Examples: User logs in, SQL logs.
|
|
|
|
*/
|
|
|
|
const INFO = 200;
|
|
|
|
|
|
|
|
/**
|
2019-05-28 16:06:03 +02:00
|
|
|
* Uncommon events.
|
2019-05-24 16:40:34 +02:00
|
|
|
*/
|
|
|
|
const NOTICE = 250;
|
|
|
|
|
|
|
|
/**
|
2019-05-28 16:06:03 +02:00
|
|
|
* Exceptional occurrences that are not errors.
|
2019-05-24 16:40:34 +02:00
|
|
|
*
|
|
|
|
* Examples: Use of deprecated APIs, poor use of an API,
|
|
|
|
* undesirable things that are not necessarily wrong.
|
|
|
|
*/
|
|
|
|
const WARNING = 300;
|
|
|
|
|
|
|
|
/**
|
2019-05-28 16:06:03 +02:00
|
|
|
* Runtime errors.
|
2019-05-24 16:40:34 +02:00
|
|
|
*/
|
|
|
|
const ERROR = 400;
|
|
|
|
|
|
|
|
/**
|
2019-05-28 16:06:03 +02:00
|
|
|
* Critical conditions.
|
2019-05-24 16:40:34 +02:00
|
|
|
*
|
|
|
|
* Example: Application component unavailable, unexpected exception.
|
|
|
|
*/
|
|
|
|
const CRITICAL = 500;
|
|
|
|
|
|
|
|
/**
|
2019-05-28 16:06:03 +02:00
|
|
|
* Action must be taken immediately.
|
2019-05-24 16:40:34 +02:00
|
|
|
*
|
|
|
|
* Example: Entire website down, database unavailable, etc.
|
|
|
|
* This should trigger the SMS alerts and wake you up.
|
|
|
|
*/
|
|
|
|
const ALERT = 550;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Urgent alert.
|
|
|
|
*/
|
|
|
|
const EMERGENCY = 600;
|
|
|
|
|
|
|
|
/**
|
2019-05-28 16:06:03 +02:00
|
|
|
* Logging levels from syslog protocol defined in RFC 5424.
|
2019-05-24 16:40:34 +02:00
|
|
|
*
|
2019-05-28 16:06:03 +02:00
|
|
|
* @var array Logging levels
|
2019-05-24 16:40:34 +02:00
|
|
|
*/
|
|
|
|
protected static $levels = array(
|
2019-05-28 16:06:03 +02:00
|
|
|
self::DEBUG => 'DEBUG',
|
|
|
|
self::INFO => 'INFO',
|
|
|
|
self::NOTICE => 'NOTICE',
|
|
|
|
self::WARNING => 'WARNING',
|
|
|
|
self::ERROR => 'ERROR',
|
|
|
|
self::CRITICAL => 'CRITICAL',
|
|
|
|
self::ALERT => 'ALERT',
|
2019-05-24 16:40:34 +02:00
|
|
|
self::EMERGENCY => 'EMERGENCY',
|
|
|
|
);
|
|
|
|
|
|
|
|
private $name;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var EntityManagerInterface
|
|
|
|
*/
|
|
|
|
private $em;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* LoggerService constructor.
|
2019-05-28 16:06:03 +02:00
|
|
|
*
|
2019-05-24 16:40:34 +02:00
|
|
|
* @param EntityManagerInterface $em
|
|
|
|
*/
|
|
|
|
public function __construct(EntityManagerInterface $em)
|
|
|
|
{
|
2019-05-28 16:06:03 +02:00
|
|
|
$this->name = '';
|
2019-05-24 16:40:34 +02:00
|
|
|
$this->em = $em;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function addNotice($message, $context = [])
|
|
|
|
{
|
|
|
|
return $this->addRecord(self::NOTICE, $message, $context);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function addWarning($message, $context = [])
|
|
|
|
{
|
|
|
|
return $this->addRecord(self::WARNING, $message, $context);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function addError($message, $context = [])
|
|
|
|
{
|
|
|
|
return $this->addRecord(self::ERROR, $message, $context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a log record.
|
|
|
|
*
|
2019-05-28 16:06:03 +02:00
|
|
|
* @param int $level The logging level
|
|
|
|
* @param string $message The log message
|
|
|
|
* @param array $context The log context
|
2019-05-24 16:40:34 +02:00
|
|
|
*
|
|
|
|
* @return bool Whether the record has been processed
|
|
|
|
*/
|
|
|
|
public function addRecord($level, $message, $context = [])
|
|
|
|
{
|
|
|
|
$levelName = static::getLevelName($level);
|
|
|
|
|
2019-05-28 16:06:03 +02:00
|
|
|
if ($level > self::WARNING) {
|
|
|
|
$context = array_merge($context, $this->automaticContext());
|
2019-05-24 16:40:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$logEntry = (new LogEntry())
|
|
|
|
->setMessage((string) $message)
|
|
|
|
->setContext($context)
|
|
|
|
->setLevel($level)
|
|
|
|
->setLevelName($levelName)
|
|
|
|
->setChannel($this->name)
|
|
|
|
;
|
|
|
|
|
|
|
|
$this->em->persist($logEntry);
|
|
|
|
$this->em->flush();
|
|
|
|
|
2019-05-28 16:06:03 +02:00
|
|
|
return $logEntry->getId();
|
2019-05-24 16:40:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private function automaticContext()
|
|
|
|
{
|
2019-05-28 16:06:03 +02:00
|
|
|
// filter the kernel classes and itself
|
2019-05-24 16:40:34 +02:00
|
|
|
$classFilters = [
|
|
|
|
'Ardent\LoggerBundle\Service\LoggerService',
|
|
|
|
'HttpKernel\Kernel',
|
|
|
|
'HttpKernel\HttpKernel',
|
|
|
|
];
|
|
|
|
$backtraces = debug_backtrace();
|
|
|
|
$context = [];
|
|
|
|
$traces = 0;
|
|
|
|
foreach ($backtraces as $trace) {
|
|
|
|
$skip = false;
|
2019-07-26 15:26:30 +02:00
|
|
|
// Applying the filters, $skip if match is found
|
2019-05-24 16:40:34 +02:00
|
|
|
foreach ($classFilters as $class) {
|
2019-05-28 16:06:03 +02:00
|
|
|
if (strpos($trace['class'], $class)) {
|
2019-05-24 16:40:34 +02:00
|
|
|
$skip = true;
|
|
|
|
}
|
|
|
|
}
|
2019-05-28 16:06:03 +02:00
|
|
|
if ($skip) {
|
2019-05-24 16:40:34 +02:00
|
|
|
continue;
|
|
|
|
}
|
2019-07-26 15:26:30 +02:00
|
|
|
// Save the function and class of this level
|
2019-05-24 16:40:34 +02:00
|
|
|
$context[] = "function[$traces]: ".$trace['function'];
|
|
|
|
$context[] = "class[$traces]: ".$trace['class'];
|
|
|
|
++$traces;
|
|
|
|
}
|
2019-05-28 16:06:03 +02:00
|
|
|
|
2019-05-24 16:40:34 +02:00
|
|
|
return $context;
|
|
|
|
}
|
|
|
|
|
2019-07-26 15:26:30 +02:00
|
|
|
/**
|
|
|
|
* Get the latest log entry limited by $limit and of at least log level $level.
|
|
|
|
*
|
|
|
|
* @param int $limit The maximum amount of entries
|
|
|
|
* @param int $level The minimum log level for the entries
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function getLatest($limit = 1, $level = self::DEBUG)
|
2019-05-24 16:40:34 +02:00
|
|
|
{
|
|
|
|
$qb = $this->em->createQueryBuilder();
|
|
|
|
|
|
|
|
$qb
|
|
|
|
->select('l')
|
|
|
|
->from('ArdentLoggerBundle:LogEntry', 'l')
|
|
|
|
->where($qb->expr()->gte('l.level', '?1'))
|
|
|
|
->orderBy('l.createdAt', 'DESC')
|
|
|
|
->setParameter(1, $level)
|
2019-07-26 15:26:30 +02:00
|
|
|
->setMaxResults($limit);
|
2019-05-24 16:40:34 +02:00
|
|
|
|
|
|
|
return $qb->getQuery()->getResult();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the name of the logging level.
|
|
|
|
*
|
2019-05-28 16:06:03 +02:00
|
|
|
* @param int $level
|
|
|
|
*
|
2019-05-24 16:40:34 +02:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public static function getLevelName($level)
|
|
|
|
{
|
|
|
|
if (!isset(static::$levels[$level])) {
|
|
|
|
throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
|
|
|
|
}
|
|
|
|
|
|
|
|
return static::$levels[$level];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function getName()
|
|
|
|
{
|
|
|
|
return $this->name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param mixed $name
|
2019-05-28 16:06:03 +02:00
|
|
|
*
|
2019-05-24 16:40:34 +02:00
|
|
|
* @return LoggerService
|
|
|
|
*/
|
|
|
|
public function setName($name)
|
|
|
|
{
|
|
|
|
$this->name = $name;
|
2019-05-28 16:06:03 +02:00
|
|
|
|
2019-05-24 16:40:34 +02:00
|
|
|
return $this;
|
|
|
|
}
|
2019-05-28 16:06:03 +02:00
|
|
|
}
|