coming from Java development, I learned to appreciate LogLevel, how to set it in Logback or Log4j.
Monolog is used in my symphony 4.2 project.
I would like to see that from a certain controller/namespace the log entries with level info can also be seen in the production log file without all the other log entries with the info level filling the log file.
Unfortunately I didn't find any explanations.
After a little more research, I configured my monolog.yaml for dev and also prod this way
monolog:
channels: ['appinfo']
handlers:
custom:
channels: ['appinfo']
level: info
max_files: 30
path: "%kernel.logs_dir%/appinfo.log"
type: rotating_file
The important thing is the channel, appinfo in my case.
The handler, custom in my case, can be named any way you like.
Then in the services.yaml one has to define the "type" of the injected logger.
App\Controller\DefaultController:
arguments:
$logger: '#monolog.logger.appinfo'
This works with controllers as with services
Related
TL;DR
In Codeception test I am trying to $I->grabService(). Service works in controllers and has no custom config, but I get:
Fail Service App\Service\Car is not available in container
Full Story
I have a project with some Services which are basically classes, which do some processing. All the Services are accessible via service container. I am testing each class in functional suite (and some in unit) and everything worked fine till today.
So today I was adding a new Service and of course a test. I did:
root#9c80b567f681:/var/www/html# vendor/bin/codecept g:cest functional Service/Car
Test was created in /var/www/html/tests/functional/Service/CarCest.php
Test looks like this:
<?php
namespace App\Tests\Service;
use App\Service\Car;
use App\Tests\FunctionalTester;
class CarCest
{
public function _before(FunctionalTester $I)
{
$I->grabService(Car::class);
}
public function tryToTest(FunctionalTester $I)
{
}
}
Now I manually in PhpStorm create a new class. Class looks like this:
<?php
namespace App\Service;
class Car
{
}
This is output of my testing:
root#9c80b567f681:/var/www/html# vendor/bin/codecept run tests/functional/Service/CarCest.php
Codeception PHP Testing Framework v4.1.6
Powered by PHPUnit 9.2.6 by Sebastian Bergmann and contributors.
Running with seed:
App\Tests.functional Tests (1) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
✖ CarCest: Try to test (0.00s)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Time: 00:01.616, Memory: 34.00 MB
There was 1 failure:
---------
1) CarCest: Try to test
Test tests/functional/Service/CarCest.php:tryToTest
Step Grab service "App\Service\Car"
Fail Service App\Service\Car is not available in container
Scenario Steps:
1. $I->grabService("App\Service\Car") at tests/functional/Service/CarCest.php:12
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
TL;DR
Fail Service App\Service\Car is not available in container
Now most of other tests I have use the same concept: I get service in _before() and then test it. Everything passes except of any class I add today :) WTF?!?
BTW: If I replace $I->grabService(Car::class); with any other service created before, it works fine.
My services.yaml is the standard, out-of-the-box Symfony version. I always relied simply on the fact everything in src/* is already a service.
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
I spent the whole morning installing/reinstalling/restarting PC... I am completely lost and stupid. Anybody has any idea ?
EDIT:
I noticed something very interesting. If I manually add the service to services.yml and set public: true, then I can use it from Codeception. But note, that I don't have to do this for any other services I created before.
TL;DR
Problem seems to be that Symfony removes all unused services upon container compilation. You can see the code here on symfony project git page.
After I noticed, that my service works correctly when it's explicitly set to public, I started digging around that and I stumbled across git issue, where someone had the same problem. Some more digging (and talking to people smarten than me) got me to the link posted on top of this answer.
BOOM! Only took like 4 days...
I got an error 500 on production env so I spent a lot of time to search where error was displaying. I found that symfony log errors using logger who send output to stderr by default so apache's error log is used in my case. I want to send messages to symfony's env.log (e.g var/log/dev.log and var/log/prod.log)
Documentation says :
The minimum log level, the default output and the log format can also be changed by passing the appropriate arguments to the constructor of Logger. To do so, override the "logger" service definition.
But I can't figure out how I can change logger's output, for testing purpose I changed logger construct in vendor/symfony/http-kernel/Log/Logger.php as following with success:
public function __construct(string $minLevel = null, $output = 'abspath/to/project/var/log/dev.log', callable $formatter = null)
but I can't edit files in vendor dir.
So How I can override the "logger" service definition ?
Do not edit the vendor files. Those are included by composer, and overwriten on each install/update.
If you want to change the log path you can do this with using monolog and config
composer require symfony/monolog-bundle
monolog:
handlers:
# this "file_log" key could be anything
file_log:
type: stream
# log to var/log/(environment).log
path: "%kernel.logs_dir%/%kernel.environment%.log" #change path here
# log *all* messages (debug is lowest level)
level: debug
syslog_handler:
type: syslog
# log error-level messages and higher
level: error
Could you suggest how to print any debug data into browser console in Symfony 2?
Especially, is it possible to implement with Symfony VarDumper Component?
In Zend Framework you can use tool Zend_Log_Writer_Firebug to do so which is very helpful. Does Symfony has something like this from the box?
Monolog, the logger used by Symfony, has a built-in support for FirePHP and ChromePHP.
In Symfony Standard Edition you can configure monolog handlers in your application configuration.
FirePHP and ChromePHP handlers are even present in config_dev.yml, but are commented out:
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: [!event]
console:
type: console
channels: [!event, !doctrine]
# uncomment to get logging in your browser
# you may have to allow bigger header sizes in your Web server configuration
#firephp:
# type: firephp
# level: info
#chromephp:
# type: chromephp
# level: info
All you need to do to see your logs in the browser is to uncomment the needed handler.
Currently, the VarDumper component doesn't support dumping anything to the browser's console. However, you can see the dumped values in the web debug toolbar (or in html if you don't use the toolbar).
I want to log into a different file than the usual dev.log or prod.log
I know that this can be done with different channels and I used it in several services, but I'm not very clear about switching the Monolog channel in a controller.
In a service you just define the channel via the tags attribute in the service definition, but how can I do this in a controller or even better in a specific action?
I know that a possible solution would be this: Symfony 2 : Log into a specific file
But it seems overkill to define two new services just for logging to a custom file.
The only way to do this is to define your controller as a service and inject a custom logger with a custom channel.
Since the channels are created automatically there is currently no other way, but it's an interesting request and you're not the first, so I created an issue on MonologBundle to allow the definition of channels at the bundle configuration level. That way you could just fetch the proper logger from the controller using $this->get('monolog.logger.mychannel') (which you can already do if the channel exists, but not if you want a custom channel for the controller that nothing else uses).
Update:
As of symfony/monolog-bundle 2.4.0 you can define additional channels as:
monolog:
channels: ["foo", "bar"]
Then you can retrieve it as $this->get('monolog.logger.mychannel')
I know that this is an older post, but I ran into a similar need using symfony/monolog-bundle 2.1.x. I couldn't seem to find exactly what I needed in other threads, so I'm documenting my solution here, which was to create a logger container that used a custom channel.
In config.yml
monolog:
handlers:
user_actions:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%-user-actions.log"
level: info
channels: [user_actions]
In my bundle's services.yml
acme.logger.user_actions:
class: Acme\MyBundle\Monolog\UserActionsLogger
arguments: ['#logger']
tags:
- { name: monolog.logger, channel: user_actions }
In src/Acme/MyBundle/Monolog/UserActionsLogger.php
<?php
namespace Acme\MyBundle\Monolog;
class UserActionsLogger
{
public $logger;
public function __construct($logger)
{
$this->logger = $logger;
}
}
Then you can either inject the logger container into another service with:
acme.user.authenticationhandler:
class: %acme.user.authenticationhandler.class%
public: false
arguments: ['#router', '#security.context', '#acme.logger.user_actions']
Or, you could selectively use the logger container as a service in any controller:
$userActionsLogger = $this->get('acme.logger.user_actions');
Then you can access the actual logger by:
$userActionsLogger->logger->info('A thing happened!')
I am currently using symfony/monolog-bundle 2.3.0 and the following code works.
Configuration in config.yml
monolog:
handlers:
main:
type: stream
path: %kernel.logs_dir%/%kernel.environment%.log
level: info
doctrine:
type: stream
path: %kernel.logs_dir%/doctrine_%kernel.environment%.log
level: debug
channels: doctrine
On Controllers
$doctrineLogger = $this->get('monolog.logger.doctrine');
Hope it helps.
I have a Symfony2 web application.
Everything works well in local. But when moving to production environment, there are some things that don't work the same as local.
Now, to figure out what is going on on Production I am trying to write some logs.
For some reason, I am not getting to write anything to the production log. Could anyone tell me what I am doing wrong? This is my code in the Controller in (nothing too sophisticated, by the way):
$logger = $this -> get("logger");
$logger -> err("My Trace --> " . $my_inspect_variable);
The ./logs/prod.log is being writing properly by Symfony. But when I try to write something by myself, it just ignores me.
My config_prod.yml file:
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
nested:
type: stream
path: %kernel.logs_dir%/%kernel.environment%.log
level: debug