Symfony2: Circular reference exception - Monolog & custom exception handler - php

In an attempt to combine Monolog e-mailing errors with a custom exception handler I get the following:
[Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException]
Circular reference detected for service "router", path: "router ->
monolog.logger.router -> monolog.handler.grouped ->
mana.exception.listener -> templating -> twig ->
templating.helper.logout_url".
I make no claims to really know what I'm doing here, as evidenced by all the errors.
Services excerpt:
mana.exception.listener:
class: Mana\ClientBundle\EventListener\ExceptionListener
arguments: [#templating, #kernel]
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
Config excerpt:
monolog:
handlers:
main:
type: stream
path: %kernel.logs_dir%/%kernel.environment%.log
level: debug
handler: grouped
grouped:
type: group
members: [mail, custom]
mail:
type: fingers_crossed
action_level: error
handler: buffered
buffered:
type: buffer
handler: swift
swift:
type: swift_mailer
from_email: error#projectmana.org
to_email: truckeetrout#yahoo.com
subject: An Error Occurred!
level: debug
custom:
type: service
id: mana.exception.listener
Custom handler:
class ExceptionListener {
protected $templating;
protected $kernel;
public function __construct(EngineInterface $templating, $kernel) {
$this->templating = $templating;
$this->kernel = $kernel;
}
public function onKernelException(GetResponseForExceptionEvent $event) {
// provide the better way to display a enhanced error page only in prod environment, if you want
if ('prod' == $this->kernel->getEnvironment()) {
// exception object
$exception = $event->getException();
// new Response object
$response = new Response();
// set response content
$response->setContent(
// create you custom template AcmeFooBundle:Exception:exception.html.twig
$this->templating->render(
'ManaClientBundle:Exception:exception.html.twig', array('exception' => $exception)
)
);
// HttpExceptionInterface is a special type of exception
// that holds status code and header details
if ($exception instanceof HttpExceptionInterface) {
$response->setStatusCode($exception->getStatusCode());
$response->headers->replace($exception->getHeaders());
} else {
$response->setStatusCode(500);
}
// set the new $response object to the $event
$event->setResponse($response);
}
}

It's your Exception Listener. Delete #kernel from the arguments array and you're fine
mana.exception.listener:
class: Mana\ClientBundle\EventListener\ExceptionListener
arguments: [#templating]
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }

Your custom exception listener probably depends on monolog either in the templating or kernel service.

Related

How not to display 404 errors in symfony logs (monolog) in a dev environment?

I would like to make a configuration for my symfony logs in dev environment that allows to log all messages except 404 errors.
I tested the following configuration:
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404]
buffer_size: 50
nested:
type: rotating_file
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
max_files: 30
With this configuration, 404 messages don't appear in the logs but the problem is that messages like "event.DEBUG" or "request.INFO" don't appear either and I would like them to appear because I am in a dev environment.
So I thought that the "action_level" was too high so I modified it:
monolog:
handlers:
main:
type: fingers_crossed
action_level: debug <========== I replaced error with debug
handler: nested
excluded_http_codes: [404]
buffer_size: 50
nested:
type: rotating_file
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
max_files: 30
Now in the logs, I can see "event.DEBUG" or "request.INFO" messages but I also see 404 errors...
It's like the excluded_http_codes is not working.
My question is how can I do to not show 404 errors in the logs while keeping messages like "event.DEBUG" or "request.INFO"?
Thank you (and apologies for my English)
You can create a custom ErrorListener which is responsible for HTTP error logging and overrides the default ErrorListener. In the code below, the dev environment is determined by $this->debug property which contains APP_DEBUG env variable.
namespace App\EventListener;
use Symfony\Component\HttpKernel\EventListener\ErrorListener as BaseErrorListener;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class ErrorListener extends BaseErrorListener
{
protected function logException(\Throwable $exception, string $message, string $logLevel = null): void
{
if (null !== $this->logger) {
if (null !== $logLevel) {
$this->logger->log($logLevel, $message, ['exception' => $exception]);
} elseif (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) {
$this->logger->critical($message, ['exception' => $exception]);
} else {
// beginning of changes
if ($exception->getStatusCode() !== 404 || !$this->debug) {
$this->logger->error($message, ['exception' => $exception]);
}
// ending of changes
}
}
}
}
And register this listener
namespace App\DependencyInjection\Compiler;
use App\EventListener\ErrorListener;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
class ErrorListenerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
// override `exception_listener`
$exceptionListenerDefinition = $container->getDefinition('exception_listener');
$exceptionListenerDefinition->setClass(ErrorListener::class);
}
}

RabbitMQBundle does not acknowledge message when producing message from consumer

How to acknowledge the message and produce a new message at the same time?
When I start my consumer from the command line, the messages will stay in the original queue. But new ones will be created in the new queue, in an infinite loop. Because it keeps consuming the messages that are not being acknowledged.
Even though TRUE is returned in the execute() function of the consumer. Which should acknowledge it, like it says in the documentation.
I am producing messages from a callback inside a consumer. This producer is injected using the standard Symfony DI.
If I remove the method that publishes the new message, the messages are acknowledged just fine...
services.yml
services:
my_importlog_repository:
class: Doctrine\ORM\EntityRepository
factory_service: doctrine.orm.default_entity_manager
factory_method: getRepository
arguments: [AppBundle\Entity\MyImportlogEntity]
my_distributor:
class: AppBundle\DistributorImport\MyDistributor
arguments: [#my_importlog_repository,#logger,#old_sound_rabbit_mq.my_download_producer, %my_config%]
my_download:
class: AppBundle\Consumer\MyDownloadConsumer
arguments: [#logger,#old_sound_rabbit_mq.my_extract_producer,#my_distributor,%my_config%]
my_extract:
class: AppBundle\Consumer\MyExtractConsumer
arguments: [#logger,#old_sound_rabbit_mq.my_convert_producer,#my_distributor,%my_config%]
config.yml
# rabbitmq
old_sound_rabbit_mq:
connections:
default:
host: '192.168.99.100'
port: 5672
user: 'guest'
password: 'guest'
vhost: '/'
lazy: false
connection_timeout: 60
read_write_timeout: 60
# requires php-amqplib v2.4.1+ and PHP5.4+
keepalive: false
# requires php-amqplib v2.4.1+
heartbeat: 30
producers:
# my producers
my_download:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_download'}
my_extract:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_extract'}
my_convert:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_convert'}
consumers:
# my consumers
my_download:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_download'}
callback: my_download
qos_options: {prefetch_size: 0, prefetch_count: 1, global: false}
idle_timeout: 60
my_extract:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_extract'}
callback: my_extract
qos_options: {prefetch_size: 0, prefetch_count: 1, global: false}
idle_timeout: 60
MyDownloadConsumer.php
<?php
namespace AppBundle\Consumer;
use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
class MyDownloadConsumer implements ConsumerInterface
{
private $logger;
private $producer;
private $distributor;
private $config;
public function __construct(\Symfony\Component\HttpKernel\Log\LoggerInterface $logger, \OldSound\RabbitMqBundle\RabbitMq\Producer $producer, \AppBundle\DistributorImport\MyDistributor $distributor, Array $config)
{
$this->logger = $logger;
$this->producer = $producer;
$this->distributor = $distributor;
$this->config = $config;
}
public function execute(\PhpAmqpLib\Message\AMQPMessage $message)
{
$data = unserialize($message->body);
$this->producer->publish(serialize($data));
return true;
}
}
If I remove
$data = unserialize($message->body);
$this->producer->publish(serialize($data));
It works like it should...
Was able to publish a message from inside my Consumer execute() method, while also acknowledging the current message being consumed. Using the following code.
$message->delivery_info['channel']
->basic_publish(
new AMQPMessage (serialize($data)),
'name_of_my_exchange',
'key.of.my.routing'
);
Publishing directly on the channel of the message that is being consumed.

OneupUploaderBundle(blueimp) - save file

I have problem with configure blueimp. When I send file (click: start upload) in jsconsole is:
POST XXXapp_dev.php/_uploader/gallery/upload 500 (Internal Server
Error)
I used:
Simple example of the entity file to be uploaded with OneupUploaderBundle
https://github.com/1up-lab/OneupUploaderBundle/issues/51#issuecomment-24878715
My file:
services.yml:
Cms.upload_listener:
class: Cms\AdminBundle\EventListener\UploadListener
arguments: [#doctrine]
tags:
- { name: kernel.event_listener, event: oneup_uploader.post_upload, method: onUpload }
config.yml:
oneup_uploader:
mappings:
gallery:
frontend: blueimp
storage:
service: ~
type: filesystem
filesystem: ~
directory: web/uploads/gallery
stream_wrapper: ~
sync_buffer_size: 100K
routing.yml
oneup_uploader:
resource: .
type: uploader
~Entity/File.php
/* ... */
/**
* #ORM\Entity
* #ORM\Table(name="file")
*/
class file {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $filename;
}
~/EventListner/UploadListner.php
class UploadListener
{
protected $manager;
public function __construct(EntityManager $manager)
{
$this->manager = $manager;
}
public function onUpload(PostPersistEvent $event)
{
$file = $event->getFile();
$object = new file();
$object->setFilename($file->getPathName());
$this->manager->persist($object);
$this->manager->flush();
}
}
debug route:
_uploader_upload_gallery POST ANY ANY
/_uploader/gallery/upload
Directory /web/uploads/gallery is on the server.
What am I trying to achieve? I need upload files to server and add record to db.
I think that config is incorrect, but where?
Edit 1:
Maybe help
INFO - An exception was thrown while getting the uncalled listeners
(Catchable Fatal Error: Argument 1 passed to
Cms\AdminBundle\EventListener\UploadListener::__construct() must be an
instance of Cms\AdminBundle\EventListener\EntityManager, instance of
Doctrine\Bundle\DoctrineBundle\Registry given, called in
/home/cms/public_html/app/cache/dev/appDevDebugProjectContainer.php on
line 455 and defined) Context:
{"exception":"Object(Symfony\Component\Debug\Exception\ContextErrorException)"}
CRITICAL - Uncaught PHP Exception
Symfony\Component\HttpFoundation\File\Exception\FileException: "Unable
to create the "/uploads/gallery" directory" at
/home/cms/public_html/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/File.php
line 134 Context:
{"exception":"Object(Symfony\Component\HttpFoundation\File\Exception\FileException)"}
I created uploads/gallery! chmod 777. I don't understand.
Edit 2:
Cms.upload_listener:
class: Cms\AdminBundle\EventListener\UploadListener
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: kernel.event_listener, event: oneup_uploader.post_upload, method: onUpload }
and:
directory: ./uploads/gallery
Now created, uploaded file in "uploads/gallery/..."
But...
CRITICAL - Uncaught PHP Exception
Symfony\Component\Debug\Exception\ContextErrorException: "Catchable
Fatal Error: Argument 1 passed to
Cms\AdminBundle\EventListener\UploadListener::onUpload() must be an
instance of Oneup\UploaderBundle\Event\PostPersistEvent, instance of
Oneup\UploaderBundle\Event\PostUploadEvent given" at
/home/cms/public_html/src/Cms/AdminBundle/EventListener/UploadListener.php
line 17 Context:
{"exception":"Object(Symfony\Component\Debug\Exception\ContextErrorException)"}
Returns blueimp "Internal Server Error ..." and doesn't add records in the Entity.

OneupUploaderBundle: Files are not uploaded and UploadListener not persist to DB, why?

I'm trying to upload one or more files using OneupUploaderBundle but I can not get it to work since files aren't uploaded and nothing is persisted to DB. I'll explain what I've done til now:
config.yml:
oneup_uploader:
mappings:
recaudos:
frontend: blueimp
storage:
service: ~
type: filesystem
filesystem: ~
directory: %kernel.root_dir%/../web/uploads/recaudos
stream_wrapper: ~
sync_buffer_size: 100K
allowed_mimetypes: [application/msword,image/jpeg,image/pjpeg,image/png,application/pdf,application/vnd.oasis.opendocument.text]
#disallowed_mimetypes: []
error_handler: oneup_uploader.error_handler.noop
# Set max_size to -1 for gracefully downgrade this number to the systems max upload size.
#max_size: 9223372036854775807
use_orphanage: true
enable_progress: true
enable_cancelation: true
namer: oneup_uploader.namer.uniqid
At Twig template only this:
<input id="fileUpload3" class="fileUpload" type="file" name="fileUpload3[]" data-url="{{ oneup_uploader_endpoint('recaudos') }}" multiple />
$(document).ready(function(){
$('fileUpload3').fileupload();
});
And latest this is the Listener for onUpload event:
namespace AppBundle\EventListener;
use Oneup\UploaderBundle\Event\PostPersistEvent;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Entity;
class UploadListener
{
protected $doctrine;
public function __construct($doctrine)
{
$this->doctrine = $doctrine;
}
public function onUpload(PostPersistEvent $event, Request $request)
{
$session = $request->getSession();
$em = $this->doctrine->getManager();
$request = $event->getRequest();
$tempfile = $event->getFile();
$productoSolicitud = $em->getRepository('SolicitudProducto')->find($session->get('productoSolicitudId'));
$recaudosTramiteProductoSolicitud = new Entity\RecaudosTramitesProductoSolicitud();
$recaudosTramiteProductoSolicitud->setProductoSolicitud($productoSolicitud);
$filenamePart = explode("--", $tempfile->getName());
$pathinfo = pathinfo($tempfile->getName());
$recaudosTramiteProductoSolicitud->setArchivo($pathinfo['filename']);
$em->persist($recaudosTramiteProductoSolicitud);
$em->flush();
}
}
And of course I have service defined also:
appbundle.upload_listener:
class: "AppBundle\EventListener\UploadListener"
arguments: [#doctrine]
tags:
- { name: 'kernel.event_listener', event: "oneup_uploader.post_persist", method: "onUpload" }
Did I miss something? Why file are not uploaded and data is not persisted? As you may see in the attached image there is a post request to _uploader/recaudos/upload, any advice?
I just checked my own OneupUploader implementation:
Rename your function "onUpload" to "onPostUpload" and change your service configuration to this:
appbundle.upload_listener:
class: "AppBundle\EventListener\UploadListener"
arguments: [#doctrine]
tags:
- { name: 'kernel.event_listener', event: oneup_uploader.post_upload, method: onPostUpload }
Should work, works for me.
If you need your original file name you also need an onUpload Method and this:
tags:
# - {...
- { name: kernel.event_listener, event: oneup_uploader.pre_upload, method: onUpload }
Regarding your comment:
//UploadListener
class UploadListener
{
protected $originalName;
public function onUpload(PreUploadEvent $event)
{
$file = $event->getFile();
$this->originalName = $file->getClientOriginalName();
}
public function onPostUpload(PostUploadEvent $event)
{
$fileEntity = new YourFileEntity();
$fileEntity->setYourOriginalNameProperty($this->originalName);
//...
}
}

Log errors even more verbosely with Symfony2

I have used the following configuration for my production logging:
monolog:
handlers:
mail:
type: fingers_crossed
action_level: error
handler: grouped
grouped:
type: group
members: [streamed, buffered]
streamed:
type: stream
path: %kernel.logs_dir%/%kernel.environment%.log
level: debug
# buffered is used to accumulate errors and send them as batch to the email address
buffered:
type: buffer
handler: swift
swift:
type: swift_mailer
from_email: info#....com
to_email: info#....com
subject: Error Occurred!
level: debug
This sends emails like this:
[2012-03-21 21:24:09] security.DEBUG: Read SecurityContext from the
session [] []
[2012-03-21 21:24:09] security.DEBUG: Reloading user from user
provider. [] []
[2012-03-21 21:24:09] security.DEBUG: Username "jakob.asdf" was
reloaded from user provider. [] [] [2012-03-21 21:24:09]
request.INFO: Matched route "_user_settings" (parameters:
"_controller": "...Bundle\Controller\UserController::settingsAction",
"username": "Jakob.asdf", "_route": "_user_settings") [] []
[2012-03-21 21:24:09] request.ERROR:
Symfony\Component\HttpKernel\Exception\NotFoundHttpException:
...Bundle\Entity\User object not found. (uncaught exception) at
/var/www/.../vendor/bundles/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DoctrineParamConverter.php
line 50 [] []
[2012-03-21 21:24:09] security.DEBUG: Write SecurityContext in the
session [] []
I would really love to have a stack trace here, or at least the line number in my controller which triggered the error. Otherwise it's really a lot of guessing of what could have gone wrong.
Now, the question: Is there any way to achieve such an even more verbose logging?
Yes it can be achievable.
Create a ExceptionListener class.
//namespace declarations
class ExceptionListener{
/**
* #var \Symfony\Component\HttpKernel\Log\LoggerInterface
*/
private $logger =null;
/**
* #param null|\Symfony\Component\HttpKernel\Log\LoggerInterface $logger
*/
public function __construct(LoggerInterface $logger = null)
{
$this->logger = $logger;
}
/**
* #param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
*/
public function onKernelException(GetResponseForExceptionEvent $event)
{
if($this->logger === null)
return;
$exception = $event->getException();
$flattenException = FlattenException::create($exception);
$this->logger->err('Stack trace');
foreach ($flattenException->getTrace() as $trace) {
$traceMessage = sprintf(' at %s line %s', $trace['file'], $trace['line']);
$this->logger->err($traceMessage);
}
}
}
And then register listener.
kernel.listener.your_listener_name:
class: FQCN\Of\ExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException , priority: -1}
- { name: monolog.logger, channel: mychannel }
arguments:
- "#logger"
You can tweak it as your requirement.
I like the solution from the Symfony docs. All you have to do is add the following code to your services.yml file:
services:
my_service:
class: Monolog\Processor\IntrospectionProcessor
tags:
- { name: monolog.processor }
This uses the IntrospectionProcessor, a tested processor to add more information to your log. It pulls out the information you care about too probably.

Categories