Last active
April 25, 2019 15:29
-
-
Save aljana/d0454ff39c19bdc71693 to your computer and use it in GitHub Desktop.
Symfony2 graylog integration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
graylog: | |
ip: 54.76.221.242 | |
port: 12201 | |
enabled: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* File containing RequestListener class | |
* | |
* @author Aljana Polanc <aljana.polanc@dlabs.si> | |
* @copyright 2015 DLabs (http://www.dlabs.si) | |
*/ | |
namespace RPS\ServiceBundle\EventListener; | |
use Doctrine\DBAL\Logging\DebugStack; | |
use Doctrine\ORM\EntityManager; | |
use Gelf\Message; | |
use Gelf\Publisher; | |
use Gelf\Transport\HttpTransport; | |
use Gelf\Transport\UdpTransport; | |
use RPS\ServiceBundle\Entity\User; | |
use Symfony\Bridge\Monolog\Logger; | |
use Symfony\Component\Console\Event\ConsoleCommandEvent; | |
use Symfony\Component\Console\Event\ConsoleExceptionEvent; | |
use Symfony\Component\Console\Event\ConsoleTerminateEvent; | |
use Symfony\Component\HttpFoundation\JsonResponse; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpKernel\Event\FinishRequestEvent; | |
use Symfony\Component\HttpKernel\Event\GetResponseEvent; | |
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; | |
use Symfony\Component\HttpKernel\Event\PostResponseEvent; | |
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; | |
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; | |
/** | |
* Class RequestListener | |
* | |
* @package RPS\ServiceBundle\EventListener | |
*/ | |
class RequestListener { | |
/** Maximum bytes per field */ | |
const GRAYLOG_MAX_BYTES = 32766; | |
/** @var Request */ | |
protected $request; | |
/** @var array */ | |
protected $params; | |
/** @var TokenStorageInterface */ | |
protected $tokenStorage; | |
/** @var Logger */ | |
protected $logger; | |
/** @var float */ | |
protected $startTime; | |
/** @var array */ | |
protected $exception; | |
/** @var array */ | |
protected $session; | |
/** @var EntityManager */ | |
protected $entityManager; | |
/** | |
* Construct. | |
* | |
* @param TokenStorageInterface $tokenStorage | |
* @param Logger $logger | |
* @param EntityManager $entityManager | |
* @param array | |
*/ | |
public function __construct(TokenStorageInterface $tokenStorage, Logger $logger, EntityManager $entityManager, $params) { | |
$this->tokenStorage = $tokenStorage; | |
$this->logger = $logger; | |
$this->params = $params; | |
$this->entityManager = $entityManager; | |
} | |
/** | |
* Handle on kernel request event. | |
* | |
* @param GetResponseEvent $event | |
*/ | |
public function onKernelRequest(GetResponseEvent $event) { | |
if (!$this->params["enabled"]) { | |
return false; | |
} | |
$this->request = $event->getRequest(); | |
// TODO: move this to onControllerFinish action or something, otherwise session can be out-dated. | |
$this->session = $this->request->getSession()->all(); | |
$this->startTime = microtime(true); | |
// Setup queries logging | |
$this->entityManager | |
->getConnection() | |
->getConfiguration() | |
->setSQLLogger(new \Doctrine\DBAL\Logging\DebugStack()) | |
; | |
} | |
/** | |
* Handle on kernel terminate event. | |
* | |
* @param PostResponseEvent $event | |
*/ | |
public function onKernelTerminate(PostResponseEvent $event) { | |
if (!$this->params["enabled"]) { | |
return false; | |
} | |
$token = $this->tokenStorage->getToken(); | |
$user = $token ? $token->getUser() : null; | |
$userInfo = []; | |
if ($user instanceof User) { | |
$userInfo = [ | |
"id" => $user->getId(), | |
"email" => $user->getEmail() | |
]; | |
} | |
$request = $event->getRequest(); | |
// Filter out password | |
$post = []; | |
foreach ($request->request->all() as $key => $val) { | |
if (stristr($key, "password") === false) { | |
$post[$key] = $val; | |
} | |
} | |
$post = json_encode($post, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); | |
$rawPost = $request->getContent(); | |
$compressedPost = ""; | |
$compressedRawPost = ""; | |
if (strlen($post) > static::GRAYLOG_MAX_BYTES) { | |
$compressedPost = base64_encode(gzcompress($post)); | |
$post = substr($post, 0, static::GRAYLOG_MAX_BYTES); | |
} | |
if (strlen($rawPost) > static::GRAYLOG_MAX_BYTES) { | |
$compressedRawPost = base64_encode(gzcompress($rawPost)); | |
$rawPost = substr($rawPost, 0, static::GRAYLOG_MAX_BYTES); | |
} | |
// TODO: check if compressed raw still exceeds max bytes. | |
$responseTime = number_format(microtime(true) - $this->startTime, 4); | |
$component = (strpos($request->getRequestUri(), "/api") === 0) | |
? "api" | |
: "web" | |
; | |
/** @var DebugStack $sqlLogger */ | |
$sqlLogger = $this->entityManager | |
->getConnection() | |
->getConfiguration() | |
->getSQLLogger() | |
; | |
$logInfo = [ | |
"url" => $request->getUri(), | |
"route" => $request->get("_route"), | |
"method" => $request->getMethod(), | |
"section" => "http/" . $component, | |
"post" => $post, | |
"post_compressed" => $compressedPost, | |
"raw_post" => !$post ? $rawPost : null, | |
"raw_post_compressed" => !$post ? $compressedRawPost : null, | |
"get" => $request->getQueryString(), | |
"cookies" => $request->cookies->all(), | |
"headers" => $request->headers->all(), | |
"session" => $this->session, | |
"ip" => $request->getClientIps(), | |
"userId" => $userInfo ? $userInfo["id"] : null, | |
"userEmail" => $userInfo ? $userInfo["email"] : null, | |
"response_time" => $responseTime, | |
"response_time_ms" => $responseTime * 1000, | |
"exception" => $this->exception, | |
"controller" => $request->attributes->get('_controller'), | |
"status" => $event->getResponse()->getStatusCode(), | |
"queries" => count($sqlLogger->queries), | |
"mem_usage_mb" => memory_get_peak_usage(true) / 1024 / 1024 | |
]; | |
// Log response for API | |
if ($component === "api") { | |
$response = $event->getResponse()->getContent(); | |
$compressedResponse = ""; | |
if (strlen($response) > static::GRAYLOG_MAX_BYTES) { | |
$compressedResponse = base64_encode(gzcompress($response)); | |
$response = substr($response, 0, static::GRAYLOG_MAX_BYTES); | |
} | |
// TODO: check if compressed response still over limit | |
$logInfo["response"] = $response; | |
$logInfo["response_compressed"] = $compressedResponse; | |
} | |
$summary = sprintf("[%d] %s %s (%.2fs)", | |
$event->getResponse()->getStatusCode(), | |
$request->getMethod(), | |
$request->getUri(), | |
$logInfo["response_time"] | |
); | |
$this->logEvent($summary, $logInfo); | |
} | |
/** | |
* Handle the kernel exception event | |
* | |
* @param GetResponseForExceptionEvent $event event | |
*/ | |
public function onKernelException(GetResponseForExceptionEvent $event) { | |
$this->exception = $event->getException()->getMessage(); | |
} | |
/** | |
* Handle the console exception the event | |
* | |
* @param ConsoleExceptionEvent $event event | |
*/ | |
public function onConsoleException(ConsoleExceptionEvent $event) { | |
$this->exception = $event->getException()->getMessage(); | |
} | |
/** | |
* Handle the console command event. | |
* | |
* @param ConsoleCommandEvent $event event | |
*/ | |
public function onConsoleCommand(ConsoleCommandEvent $event) { | |
$this->startTime = microtime(true); | |
// Setup queries logging | |
$this->entityManager | |
->getConnection() | |
->getConfiguration() | |
->setSQLLogger(new \Doctrine\DBAL\Logging\DebugStack()) | |
; | |
} | |
/** | |
* Handle the console terminate event. | |
* | |
* @param ConsoleTerminateEvent $event event | |
*/ | |
public function onConsoleTerminate(ConsoleTerminateEvent $event) { | |
$responseTime = number_format(microtime(true) - $this->startTime, 4); | |
$summary = sprintf("[%d] %s (%.2fs)", $event->getExitCode(), $event->getCommand()->getName(), $responseTime); | |
/** @var DebugStack $sqlLogger */ | |
$sqlLogger = $this->entityManager | |
->getConnection() | |
->getConfiguration() | |
->getSQLLogger() | |
; | |
$logInfo = [ | |
"command" => $event->getCommand()->getName(), | |
"exit_code" => $event->getExitCode(), | |
"exception" => $this->exception, | |
"response_time" => $responseTime, | |
"response_time_ms" => $responseTime * 1000, | |
"section" => "console", | |
"queries" => count($sqlLogger->queries), | |
"mem_usage_mb" => memory_get_peak_usage(true) / 1024 / 1024 | |
]; | |
$this->logEvent($summary, $logInfo); | |
} | |
/** | |
* Write to GrayLog. | |
* | |
* @param $summary | |
* @param $details | |
*/ | |
protected function logEvent($summary, $details) { | |
$jsonOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; | |
$transport = new UdpTransport($this->params["ip"], $this->params["port"], UdpTransport::CHUNK_SIZE_LAN); | |
$publisher = new Publisher(); | |
$publisher->addTransport($transport); | |
$logger = new \Gelf\Logger($publisher, "rps"); | |
foreach ($details as $key => $values) { | |
$details[$key] = is_array($values) ? json_encode($values, $jsonOptions) : $values; | |
} | |
if ($this->exception) { | |
$logger->error($summary, $details); | |
} else { | |
$logger->notice($summary, $details); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<service id="rps.request.listener" class="RPS\ServiceBundle\EventListener\RequestListener"> | |
<argument type="service" id="security.token_storage"></argument> | |
<argument type="service" id="logger" on-invalid="null" /> | |
<argument type="service" id="doctrine.orm.default_entity_manager" on-invalid="null" /> | |
<argument key="graylog">%graylog%</argument> | |
<tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" priority="0"/> | |
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="0"/> | |
<tag name="kernel.event_listener" event="kernel.terminate" method="onKernelTerminate" priority="0"/> | |
<tag name="kernel.event_listener" event="console.exception" method="onConsoleException" priority="0"/> | |
<tag name="kernel.event_listener" event="console.command" method="onConsoleCommand" priority="0"/> | |
<tag name="kernel.event_listener" event="console.terminate" method="onConsoleTerminate" priority="0"/> | |
</service> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment