Reputation: 11
Imagine the situation: I have service(defined in serviceManager), witch used by OneController, TwoController. I want to log service actions to 'one.log', when service called from OneController, and to 'two.log' in other case. if service has many methods - injection logger in each method is ugly.
service constructor unavailable in controller( services obtained from serviceManager)
Creating several loggers in serviceManager is bad option, cause i want to restrict using all logger(or log files) in certain controllers a, except one specific. I think, i need some kind of "Dependency injection" log file to logger from controller. I can't define in module config controller-specific log files, because controller is undefined while configs a merging.
Is possible to use different logs in Controllers?
I can define log files in module.config.php
'logs' => [
'error' => APP_PATH . '/../var/logs/error.log'
],
... and force service manager factory to use it
//closure
'logger' => function (ServiceManager $sm) {
$log = new Logger();
$logSettings = $sm->get('config')['logs'];
//use log
$errorWriter = new LogWriterStream($logSettings['error']);
$log->addWriter($errorWriter);
return $log;
}
then, in controller we can obtain it in service, in controller.
...
$logger = $serviceManager->get('logger');
...
thus, i force specific module to use specific log file.
zf2 controllers has no their own configs, therefore, i need to find another way.
Any ideas?
Upvotes: 1
Views: 79
Reputation: 11
What we have done finally
in Main module
class AbstractController extends AbstractActionController {
/**
* error log name
* can override
* null - use module default
*/
const ERROR_LOG_FILE_NAME = null;
/**
* info log name
* can override
* null - use module default
*/
const INFO_LOG_FILE_NAME = null;
public function __construct()
{
/**
* switch log files if const defined
*/
if (static::ERROR_LOG_FILE_NAME || static::INFO_LOG_FILE_NAME) {
/** @var Logger $logger */
$logger = self::$sm->get(Factory::LOGGER);
$logConfig = self::$sm->get(Factory::CONFIG)['logs'];
//remove writers
$logger->setWriters(new SplPriorityQueue());
if (static::ERROR_LOG_FILE_NAME) {
$errorLogPath = LOG_PATH . '/' .static::ERROR_LOG_FILE_NAME;
} else {
//use default
$errorLogPath = $logConfig['error'];
}
if (static::INFO_LOG_FILE_NAME) {
$infoLogPath = LOG_PATH . '/' . static::INFO_LOG_FILE_NAME;
} else {
//use module default
$infoLogPath = $logConfig['info'];
}
$logger->addWriter(Factory::getErrorLogWriter($errorLogPath));
$logger->addWriter(Factory::getInfoLogWriter($infoLogPath));
}
}
}
in child controller
const ERROR_LOG_FILE_NAME = 'path/to/file.log'
so, if we use logger in service, called by child controller
$this->sm->get('logger')->log($message)
it writes to controller-defined file
Upvotes: 0
Reputation: 44422
So you need somehow to tell the logger which controller is calling. I would suggest you pass the event from the controller to the logger service. In the event you have access to all information you need:
You could use a controller plugin manager for this:
<?php
namespace Application\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\Mvc\MvcEvent;
class ControllerEventLogger extends AbstractPlugin{
protected $map = [
'Application\Controller\OneController' => 'one.log',
'Application\Controller\TwoController' => 'two.log'
];
/**
* @param MvcEvent $event
*/
public function __invoke(MvcEvent $event){
$routeMatch = $event->getRouteMatch();
$action = $routeMatch->getParam('action');
$controller = $routeMatch->getParam('controller');
// map your controller to a log file name here
$logFileName = $this->map[ $controller ];
// do your logging
}
}
In your module.config.php
:
<?php
return array(
// ...
'controller_plugins' => array(
'invokables' => array(
'ControllerEventLogger' => 'Application\Controller\Plugin\ControllerEventLogger',
)
),
// ...
);
In your controller class:
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class OneController extends AbstractActionController{
public function indexAction(){
$this->ControllerEventLogger(event);
return new ViewModel();
}
}
Note: To keep things even cleaner you could still make a separate logger service that you inject inside your controller plugin through a factory. Then your controller plugin is just glue between your controller and the logger service.
You could also make a logger that works event based. In such case you could simply log each dispatch event. Like this your controller won't even be aware of the logging (much cleaner since there will not be any logger code inside the controller at all). The listener would be something like:
<?php
namespace Application\Listener;
use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;
use Zend\Mvc\MvcEvent;
class ControllerLoggerListener extends AbstractListenerAggregate
{
protected $map = [
'Application\Controller\OneController' => 'one.log',
'Application\Controller\TwoController' => 'two.log'
];
/**
* @param EventManagerInterface $events
*/
public function attach(EventManagerInterface $events)
{
$this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
}
/**
* @param MvcEvent $event
*/
public function onDispatch(MvcEvent $event)
{
$routeMatch = $event->getRouteMatch();
$action = $routeMatch->getParam('action');
$controller = $routeMatch->getParam('controller');
// map your controller to a log file name here
$logFileName = $this->map[ $controller ];
// do your logging
}
}
There are like a hundred ways to do this, it all depends on your specific needs.
Upvotes: 0