I writing a module, actually a custom command in Magento 2. Magento 2 console application is proudly powered by Symfony Console, obviously. And my concernation is how to disable output from $output for specified command?
For example:
$setupUpgradeCommand = $this->getApplication()->find('setup:upgrade');
$setupUpgradeArguments = array(
'command' => 'setup:upgrade',
'--quiet' => true,
);
$setupUpgradeInput = new ArrayInput($setupUpgradeArguments);
$start = microtime(true);
$output->writeln('<info>Start upgrading module schemas...</info>');
$setupUpgradeCommand->run($setupUpgradeInput, $output);
$output->writeln('...............................<info>OK</info>');
// My long logic-code start from here....
Unfortunately, even I set --quiet to true, output of this command setup:upgrade still there.
Any ideas?
As answered in a comment.. although almost exactly the same as the answer by #toooni.
You can insert the NullOutput rather than inserting the actual output object supplied by the command.
use Symfony\Component\Console\Output\NullOutput;
$setupUpgradeCommand->run($setupUpgradeInput, new NullOutput());
You can use BufferedOutput:
use Symfony\Component\Console\Output\BufferedOutput;
...
$setupUpgradeCommand->run($setupUpgradeInput, new BufferedOutput());
The usage is described here:
http://symfony.com/doc/current/cookbook/console/command_in_controller.html
Another option would be to use the logger and have this spewed on your CLI if you ask for it. You can read more about it in this (by now old) news post: http://symfony.com/blog/new-in-symfony-2-4-show-logs-in-console
A full example where you can even format the output taken from the post:
services:
my_formatter:
class: Symfony\Bridge\Monolog\Formatter\ConsoleFormatter
arguments:
- "[%%datetime%%] %%start_tag%%%%message%%%%end_tag%% (%%level_name%%) %%context%% %%extra%%\n"
monolog:
handlers:
console:
type: console
verbosity_levels:
VERBOSITY_NORMAL: NOTICE
channels: my_channel
formatter: my_formatter
You can find the documentation here: http://symfony.com/doc/current/cookbook/logging/monolog.html
Related
I use a rotating monolog handler
monolog:
channels: ['import']
handlers:
import_client:
level: debug
type: rotating_file
max_files: 10
path: '%kernel.logs_dir%/import.log'
channels: [import_client]
All works fine except I don't like the filename. I get import-2018-02-22.log.
Does it exist a way to change this format?
I would like the filename to be like import-"date(YmdHis)".log.
Is possible to rewrite the filename format? Did you have any solutions ?
I found the solution, need to add in config handler a new parameter :
date_format: 'YmdHms'
The RotatingFileHandler Logs records to a file and creates one logfile per day. It will also delete files older than $maxFiles. You should use logrotate for high profile setups though, this is just meant as a quick and dirty solution.
As you can see in the original RotatingFileHandler: you could possibly change the rotate dateformat
public function setFilenameFormat($filenameFormat, $dateFormat)
But I don't see any configuration option in the symfony monolog reference.
You could call a service using
services:
app.custom_rotating_service:
# ...
calls:
- method: setFilenameFormat
arguments:
- 'yourFilenameFormat'
- 'Ymd'
It seems to me you would get into soemthing complex for no added value of a date format.
TLDR
It's not possible to have a logfile By Hour/Minute with monolog
Changing Date format or the rotation frequency to months/year of the handler seems (to me) doable but not supported by the symfony monolog configuration. you could create a service and try to call the method automatically on service isntance creation
You should use logrotate if you have a custom need of rotating log
I have a console command and I'm initializing ConsoleLogger in the initialize method:
$this->logger = new ConsoleLogger($output);
But the date is not outputted in the console. Is it possible to prefix the output with the datetime?
I know this is an old topic, but it's the first result when you google for "symfony console log date", that's why I'd like to share what I found when trying to solve this.
According to the Symfony docs, you can specify a custom formatter (found in old Symfony 2.6 docs)
https://symfony.com/doc/2.6//cookbook/logging/monolog_console.html
# app/config/services.yml
services:
my_formatter:
class: Symfony\Bridge\Monolog\Formatter\ConsoleFormatter
arguments:
# NOTE
# The placeholder to use in the `format` is enclosed in single `%` (ex.: `%datetime%`)
# However, here we escape `%` characters so Symfony will not interpret them as service parameters.
# https://symfony.com/doc/current/configuration.html#configuration-parameters
- "[%%datetime%%] %%start_tag%%%%message%%%%end_tag%% (%%level_name%%) %%context%% %%extra%%\n"
But that's not doing the trick already.
Taking a look at the source of ConsoleFormatter, I found this:
const SIMPLE_FORMAT = "%datetime% %start_tag%%level_name%%end_tag% <comment>[%channel%]</> %message%%context%%extra%\n";
const SIMPLE_DATE = 'H:i:s';
So there date is set as H:i:s here.
Luckily this date can be overwritten with the date_format option.
This is how I solved it now:
In my monolog.yaml I added the formatter
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine", "!console"]
verbosity_levels:
VERBOSITY_NORMAL: DEBUG
formatter: console_log_formatter
And in services.yml I added the custom formatter with my date_format set
console_log_formatter:
class: Symfony\Bridge\Monolog\Formatter\ConsoleFormatter
arguments:
- date_format: 'Y-m-d H:i:s'
maybe you need to define the verbosity level
$this->consoleLogger = new ConsoleLogger($output, [LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL]);
Hope this is what you are looking for
In monolog's fingers_crossed handler, the log records are held back until a certain level triggers to unload/show all those with-held records. So far, so good.
When those withheld messages are dumped after a fingers_crossed trigger, you may now actually want to know all the source lines. (And NOT before the fingers_crossed). For this the IntrospectionProcessor is used - but, as its creator specifies: 'Warning: This only works if the handler processes the logs directly. If you put the processor on a handler that is behind a FingersCrossedHandler for example, the processor will only be called once the trigger level is reached, and all the log records will have the same file/line/.. data from the call that triggered the FingersCrossedHandler.' - not good!.
This issue was raised for the 'Laravel 5' similar issue, without a satisfying answer. Here I present a working Symfony (2/3) solution.
src/AppBundle/IntrospectionPreprocessor.php:
<?php
namespace AppBundle;
class IntrospectionPreprocessor // ATTENTION TO THE 'PRE'
extends \Monolog\Processor\IntrospectionProcessor // SO USE THE ORIGINAL CLASS
{
public function processRecord(array $record)
{
return $this( $record ); // CALL TO ITS __INVOKE(..)
}
}
The services.logger.yml:
services:
monolog.processor.introspection_patch:
class: AppBundle\IntrospectionPreprocessor
arguments: [ DEBUG, [Helper, Google] ] # FILTER OUT CLASSES THAT CONTAIN [..], similar to IntrospectionProcessor
tags:
- { name: monolog.processor, method: processRecord }
This now inserts the correct file/line/class/function info, AFTER fingers_crossed triggers subsequent handlers.
Q. Is there a way to properly make this a part of my Symfony framework, so not in my application area (AppBundle), but neither touching the Symfony code (eg. gets lost with an upgrade?)
I am new on Symfony2 and I got blocked when trying to run an asynchronous command like this:
class MyCommand extends ContainerAwareCommand{
protected function configure()
{
$this
->setName('my:command')
->setDescription('My command')
->addArgument(
'country',
InputArgument::REQUIRED,
'Which country?'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$country = $input->getArgument('country');
// Obtain the doctrine manager
$dm = $this->getContainer()->get('doctrine_mongodb.odm.document_manager');
$users = $dm->getRepository('MyBundle:User')
->findBy( array('country'=>$country));
}}
That works perfectly when I call it from my command line:
php app/console my:command uk
But it doesn't work when I call it trowh a Symfony2 Process:
$process = new Process("php ../app/console my:command $country");
$process->start();
I get a database error: "[MongoWriteConcernException] 127.0.0.1:27017: not master"
I think that means that the process is not getting my database configuration...
I just want to run an asynchronous process, is there other way to do it?
Maybe a way to call the Application Command that do not require the answer to keep going ?
Maybe I need to use injection?
PS: My current command is just a test, at the end it should be an 'expensive' operation...
Well, I found out what happened...
I use multiple environments: DEV, TEST and PROD.
And I also use differents servers.
So the DEV environment is my own machine with a simple mongodb configuration.
But the TEST environment is on other server with a replica set configuration...
Now the error get full sense: "[MongoWriteConcernException] 127.0.0.1:27017: not master"
To solve it, I've just added the environment parameter (--env=) to the process and everything worked like a charm:
$process = new Process("php ../app/console my:command $country --env=test");
Actually, to get the correct environment I use this:
$this->get('kernel')->getEnvironment();
Which let's my code as follows:
$process = new Process("php ../app/console my:command $country --env=".$this->get('kernel')->getEnvironment());
Maybe is not a beautifull way to do it, but it works for me :)
Disclamer: This might be a bit overkill for what you're trying to do :)
I would choose an opposite way to do it: pthreads
First, quick examination of StackOverflow showed me a really nice example of using pthreads: Multi-threading is possible in php
Then, knowing that you could invoke your command from another command:
http://www.craftitonline.com/2011/06/calling-commands-within-commands-in-symfony2/
... lets you piece all the parts. It's a bit complicated but it does the job.
In case you want to execute your code completely async in Symfony2/3 there is AsyncServiceCallBundle for that.
You should just call it like this:
$this->get('krlove.async')->call('your_service_id', 'method_name', [$arg1, $arg2]);
Internally it uses this approach to run your code in background.
Using Symfon2 and its Monolog framework to for logging, I am having some trouble.
On the one hand, I have a service configured with my own logger. It is working properly and I get to "info" and "err" messages without problems.
services:
my_logger:
class: Monolog\Logger
arguments: [my_info]
calls:
- [pushHandler, [#my_log_handler]]
my_log_handler:
class: Monolog\Handler\StreamHandler
arguments: [%kernel.root_dir%/logs/my_info.log, 100]
Using the following in the controller causes proper messages to be written
$this->get('my_logger')->info('info message');
$this->get('my_logger')->err('error message');
Here comes my question
Once I have placed planty of those ->err and ->info logging messages, how I tell the configuration to just write those written through the err method?
At the beginning may be I need many information, and for that reason I would write many info messages. But in a while, I may prefer to level up the logging messages through setting the action level to warning or error, avoiding the info logs to be written.
Any idea?
You just need to tweak the level at which your handler is listening. In this case it means changing the 100 to something higher like 400 for errors. So that gives you:
my_log_handler:
class: Monolog\Handler\StreamHandler
arguments: [%kernel.root_dir%/logs/my_info.log, 400]