How to write and use Monolog handlers and channels - php

I have read some docs here but still not clear to me how to write and use a custom Monolog handler and channel. Let me explain a bit what I want to achieve. I have a custom function and I want that log to be logged into a file called custom.log. I have enabled Doctrine logging into another file by setting this in config.yml file:
monolog:
handlers:
#Logs Doctrine to a different channel
doctrine:
level: debug
type: stream
path: "%kernel.logs_dir%/doctrine.log"
channels: [doctrine]
How do I achieve the same for a custom.log?

You can try that way,
monolog:
channels: ["testchannel"]
handlers:
test:
# log all messages (since debug is the lowest level)
level: debug
type: stream
path: "%kernel.logs_dir%/testchannel.log"
channels: ["testchannel"]
And in the controller you can get the logger and do your thing;
class DefaultController extends Controller
{
public function indexAction()
{
$logger = $this->get('monolog.logger.testchannel');
$logger->info("This one goes to test channel!!");
return $this->render('AcmeBundle:Default:index.html.twig');
}
}
Also you can check which monolog handlers and loggers are registered by running the command php app/console container:debug monolog

Related

Symfony 4 custom logger with multiple channel

I want to make one Logger class which can be use in different modules in Symfony and when logging to have different channels. Here is my configurations:
service.yaml
App\Logger\MLogger:
arguments: ['#logger']
tags:
- { name: monolog.logger, channel: 'mailchimp' }
monolog.yaml - dev
monolog:
handlers:
main:
type: stream
path: "php://stderr"
level: debug
channels: ["mailchimp"]
console:
type: stream
path: "php://stderr"
process_psr_3_messages: false
channels: ["!event", "!doctrine"]
deprecation:
type: error_log
deprecation_filter:
type: filter
handler: deprecation
max_level: info
channels: ["php"]
With this I have the custom logger and I load it with dependency injection.
Example of method inclusion:
public function send(Request $request, MLogger $logger)
The same logger class I want to use for different module and with difference channel.
How can I configure it so the Mlogger class could be used with different channels?

What isn't right with logging in Symfony 3

I have configured logger for different channel in different files, but it does not work for me. It's work but it writes in console not in pointed file.
And I need write log to file in channel search.
Here is my code:
#app/config/config_dev.yml
monolog:
handlers:
search:
type: stream
level: error
path: "%kernel.logs_dir%/search_log.log"
channels: [search]
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: [!event, !search]
console:
type: console
channels: [!event, !doctrine, !search]
Defined service:
#MyBundle/Resources/config/services.yml
services:
app.logger_search:
class: Symfony\Bridge\Monolog\Logger
arguments: ["#logger"]
tags:
- {name: monolog.logger, channel: search}
Now use it service, try to test it:
#MyController.php
/**
* #Route("/test")
*/
public function test()
{
$this->get("app.logger_search")->error("Test");
return $this->json("test");
}
But it writes into console insted of file.
Console I meant where I ran my server: php bin\console server:run.
Creating your own Channel. This is done either via the configuration or by tagging your service with monolog.logger and specifying which channel the service should log to (just as you have done).
Both ways are valid and in both cases you logger will be named:
monolog.logger.<you-channel-name>
So use monolog.logger.search instead of your service id app.logger_search to fix the issue.
I you don't have a strong reason to change the logger behavior, I suggest to configure additional channels without tagged services:
# app/config/config.yml
monolog:
channels: ['foo', 'bar']
With this, you can now send log messages to the foo channel by using the automatically registered logger service monolog.logger.foo.

How to disable query logging in console while load Doctrine fixtures?

I have a fixtures that loads a huge amount of data and all the time I run into this error:
Fatal error: Allowed memory size of 2147483648 bytes exhausted (tried
to allocate 16777224 bytes) in
/var/www/html/platform-cm/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php
on line 65
[Symfony\Component\Debug\Exception\OutOfMemoryException] Error:
Allowed memory size of 2147483648 bytes exhausted (tried to allocate
16777224 bytes)
After research a bit I found this post where I read that logging could be the cause of the issue because AppKernel is instantiated with debug set to true by default and then the SQL commands get stored in memory for each iteration.
The first attempt without disable the debug at AppKernel was run the command as:
doctrine:fixtures:load --no-debug
But I didn't get luck since the same error still.
The second attempt was disable the debug at config_dev.yml but this is not recommended since I am getting ride of every logs but didn't work neither.
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
# console:
# type: console
# bubble: false
# verbosity_levels:
# VERBOSITY_VERBOSE: INFO
# VERBOSITY_VERY_VERBOSE: DEBUG
# channels: ["!doctrine"]
# console_very_verbose:
# type: console
# bubble: false
# verbosity_levels:
# VERBOSITY_VERBOSE: NOTICE
# VERBOSITY_VERY_VERBOSE: NOTICE
# VERBOSITY_DEBUG: DEBUG
# channels: ["doctrine"]
So, this is how my fixture looks like:
class LoadMsisdn extends AbstractFixture implements OrderedFixtureInterface
{
public function getOrder()
{
return 13;
}
public function load(ObjectManager $manager)
{
$content = file_get_contents('number.txt');
$numbers = explode(',', $content);
shuffle($numbers);
foreach ($numbers as $key => $number) {
$msisdn = new Msisdn();
$msisdn->setMsisdn($number);
$msisdn->setBlocked((rand(1, 1000) % 10) < 7);
$msisdn->setOperator($this->getReference('operator-' . rand(45, 47)));
$this->addReference('msisdn-' . $key, $msisdn);
$manager->persist($msisdn);
}
$manager->flush();
}
}
How do I disable the logger if I need to do it from EntityManager as shown in a answer on the same post?
$em->getConnection()->getConfiguration()->setSQLLogger(null);
The object manager that is being passed into the load method is an instance of the the entity manager (Doctrine\Common\Persistence\ObjectManager is just an interface that the entity/document/etc managers implement).
This mean that you can use the same command as in your question to nullify the SQL logger like..
$manager->getConnection()->getConfiguration()->setSQLLogger(null);
One thing to note is that the default logging setting for a DBAL connection is %kernel.debug% meaning that, unless you have overridden it in your config, the logging should only happen in the dev environment. I can see you have tried using the --no-debug option but I can only assume that, as the logger is set during the container compilation, that it doesn't unset it to the container not being rebuilt.
Symfony 3/4/5 approach in test case
final class SomeTestCase extends KernelTestCase
{
protected function setUp(): void
{
$this->bootKernel('...');
// #see https://stackoverflow.com/a/35222045/1348344
// disable Doctrine logs in tests output
$entityManager = self::$container->get(EntityManagerInterface::class);
$entityManager->getConfiguration();
$connection = $entityManager->getConnection();
/** #var Configuration $configuration */
$configuration = $connection->getConfiguration();
$configuration->setSQLLogger(null);
}
}
I am not sure if it will help with the memory limit issue, but you can also try to change logging (more comfortably) via YAML configuration:
Disable Doctrine log channel
(as in your commented code)
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!doctrine"] # "!event", "!php"
or Setting a custom dummy logger
In services.yaml set the class for the default Doctrine logger:
doctrine.dbal.logger:
class: App\ORM\Doctrine\DBAL\Logging\DummySQLLogger
And enabling it in doctrine.yaml:
doctrine:
dbal:
driver: 'pdo_mysql'
logging: true
The custom logger must implement the interface Doctrine\DBAL\Logging\SQLLogger. The concrete methods startQuery and stopQuery can be left empty, to skip logging; or just use error_log instead (like in my case).
I am not sure though if this will prevent the memory exceeded problem. Eventually increase the PHP_MEMORY_LIMIT (via env variables if supported by your environment, or via ini_set()). And pay attention to use the correct unit symbol (M for Megabytes, G for Gigabytes), I so often write it wrongly as MB.

only log certain logs with monolog using a channel in symfony

I'm having a hard time understanding how to configure logging in my symfony app to do what i need to. I don't understand the difference/relationship between handlers and channels. I have the following in my config.yml
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
dynamic_request:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%_dynamic.log"
level: debug
I then have this defined in my services.yml
jsonstub.dynamic.response_provider:
class: ProgrammingAreHard\JsonStub\CoreBundle\Domain\Dynamic\EventListener\DynamicResponseProvider
arguments:
- #security.context
- #logger
- %kernel.environment%
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onAuthentication }
- { name: monolog.logger, channel: dynamic_request }
The desired behavior is for only error logs to be logged to %kernel.logs_dir%/%kernel.environment%.log and then i would like to inject a logger into my DynamicResponseProvider that logs to %kernel.logs_dir%/%kernel.environment%_dynamic.log. The documentation is confusing me. It states the following:
configuration defines a stack of handlers which will be called in the order where they are defined
Does this mean that because i have the dynamic_request handler defined any debug logs will be logged here? That is not my desired behavior. What i would like is that ONLY the DynamicResponseProvider will use that handler. How can i achieve this?
You can specify a channel for a handler.
So, if you want that the 'dynamic_request' handler is used only for 'dynamic_request' logs, your config.yml must be :
dynamic_request:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%_dynamic.log"
level: debug
channels: dynamic_request
See http://symfony.com/doc/current/cookbook/logging/channels_handlers.html for more example

Why does Symfony still log to a dev.log file, even when I didn't define it in a loghandler?

During the execution of Symfony Commands, I want to log messages to a different file. I have read the Symfony and Monolog documentation, and it should work like I describe here. (Note that I know messages from the 'doctrine', 'event', ... channels will still be logged by the main handler, but that doesn't matter for me)
In my config.yml, I have this:
monolog:
channels: [commandline]
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.main.log"
level: debug
channels: [!commandline]
commandline:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.commandline.log"
level: debug
channels: commandline
stdout:
type: stream
path: "php://stdout"
level: debug
channels: commandline
mail:
type: stream
action_level: alert
handler: buffered_mail
buffered_mail:
type: buffer
handler: swift
swift:
type: swift_mailer
from_email: some#email.com
to_email: some#email.com
subject: "Something went wrong"
level: alert
I'm expecting to have 2 log-files: dev.main.log and dev.commandline.log.
But I'm still having a third log-file: dev.log that logs all messages.
I don't seem to find where that loghandler is defined and how I can prevent it from logging things...
If anyone could point me in the right direction, that would be nice!
btw, i'm using:
symfony 2.3
monolog-bundle 2.4
EDIT
There is no monolog section in the config_dev.yml
REMOVE monolog.handlers.mainfrom config_dev.yml.
It usally contains path: "%kernel.logs_dir%/%kernel.environment%.log"
config _dev.yml (default)
monolog:
handlers:
main: # <- remove this handler
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log" #<- logs/dev.log
level: debug
Remove the main handler from this config file.
If anyone comes across this and is still interested in why this happens, the debug handler is injected in the \Symfony\Bundle\MonologBundle\DependencyInjection\Compiler\DebugHandlerPass::process() method...
class DebugHandlerPass implements CompilerPassInterface
{
// ...
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('profiler')) {
return;
}
if (!$container->getParameter('kernel.debug')) {
return;
}
$debugHandler = new Definition('%monolog.handler.debug.class%', array(Logger::DEBUG, true));
$container->setDefinition('monolog.handler.debug', $debugHandler);
foreach ($this->channelPass->getChannels() as $channel) {
$container
->getDefinition($channel === 'app' ? 'monolog.logger' : 'monolog.logger.'.$channel)
->addMethodCall('pushHandler', array(new Reference('monolog.handler.debug')));
}
}
}
As you can see, this pushes a new handler on to every registered channel, thus overriding any other handlers that might already have been added.

Categories