limit log file and create a new one in php slim3 monolog - php

I am using slim3 with monolog from composer
my dependencies is looks like that:
// monolog
$container['logger'] = function ($c) {
$settings = $c->get('settings')['logger'];
$logger = new Monolog\Logger($settings['name']);
$logger->pushProcessor(new Monolog\Processor\UidProcessor());
$logger->pushHandler(new Monolog\Handler\RotatingFileHandler($settings['path'], $settings['level']));
return $logger;
};
I did not manage to set limit(filesize) for those logs
basically, my log file name looks like: server-[date].log
I want to create a new log when it exceeded the 5MB for example:
server-[todaydate].log
server-[todaydate]-1.log
server-[todaydate]-2.log

Related

How to call for a monolog from the index.php?

There is example of using Monolog with PHP-DI (from manual, there are 2 files - index.php and config.php:
<?php
// config.php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
return [
// ...
Psr\Log\LoggerInterface::class => DI\factory(function () {
$logger = new Logger('mylog');
$fileHandler = new StreamHandler('path/to/your.log', Logger::DEBUG);
$fileHandler->setFormatter(new LineFormatter());
$logger->pushHandler($fileHandler);
return $logger;
}),
];
This config.php is using in the below code:
// index.php
use DI\ContainerBuilder;
require __DIR__ . '/../vendor/autoload.php';
$containerBuilder = new ContainerBuilder;
$containerBuilder->addDefinitions(__DIR__ . '/config.php');
$container = $containerBuilder->build();
How could I use it now in index.php? Regularly I use Monolog this way:
$log = new Logger('name');
$log->warning('Foo');
But how to call it with Container?
I was able to do it in simple $container->set(), $container->get() mode. But in this way using Container Builder I can't find a way to do it. Moreover when I make var_dump($container) there are no any logger's signs in it.
You need to replace $log = new Logger('name') with:
$log = $container->get(\Psr\Log\LoggerInterface::class);
See this part of the documentation: http://php-di.org/doc/getting-started.html#3-create-the-objects

I can't add sentry to php slim

I need to add php sentry error handler to my slim 3 project.
how can I do so ?
where should put sentry integration code?
what I'm doing now is :
// monolog
$container['logger'] = function ($c) {
$settings = $c->get('settings')['logger'];
$logger = new Monolog\Logger($settings['name']);
$logger->pushProcessor(new Monolog\Processor\UidProcessor());
$logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], $settings['level']));
$client = new Raven_Client(
'http://key#ip:9000/2'
);
$handler = new Monolog\Handler\RavenHandler($client);
$handler->setFormatter(new Monolog\Formatter\LineFormatter("%message% %context% %extra%\n"));
$logger->pushHandler($handler);
return $logger;
};
but I'm not getting all errors in my sentry dashboard.
for example accessing undefined array indexes.
thanks.
I think the best way is to just do the following (I did not test this or have ever used Slim but looking at the Slim docs this is a way to do it):
In your index.php (which should be the app entrypoint) just after require '../../vendor/autoload.php'; (the composer autoload).
Add the Raven initialization code:
$sentry = new Raven_Client('http://key#ip:9000/2');
$sentry->install();
This will configure the SDK to handle (and send) all errors, no need for the Monolog handler anymore.
If you want to integration it in a ErrorHandler class you created looking at this skeleton project might give you some ideas.
I am using a custom error handler to catch exceptions. This way i can use the default slim error handler and Sentry error reporting at the same time.
This is my code:
// initalize sentry
Sentry\init(['dsn' => 'your_dsn' ]);
// Run app
$app = (new App())->get();
// register custom error handler
$c = $app->getContainer();
$c['errorHandler'] = function ($c) {
return function ($request, $response, $exception) use ($c) {
// send error to sentry
Sentry\captureException($exception);
// invoke default error handler
$handler = new Slim\Handlers\Error();
return $handler->__invoke($request, $response, $exception);
};
};
$app->run();
Not sure if this is the "recommended" way, but it works.

How to use a caching system (memcached, redis or any other) with slim 3

I browsed the internet and didn't find much information on how to use any caching library with Slim framework 3.
Can anyone help me with this issue?
I use symfony/cache with Slim 3. You can use any other cache library, but I give an example setup for this specific library. And I should mention, this is actually independent of Slim or any other framework.
First you need to include this library in your project, I recommend using composer. I also will iinclude predis/predis to be able to use Redis adapter:
composer require symfony/cache predis/predis
Then I'll use Dependency Injection Container to setup cache pool to make it available to other objects which need to use caching features:
// If you created your project using slim skeleton app
// this should probably be placed in depndencies.php
$container['cache'] = function ($c) {
$config = [
'schema' => 'tcp',
'host' => 'localhost',
'port' => 6379,
// other options
];
$connection = new Predis\Client($config);
return new Symfony\Component\Cache\Adapter\RedisAdapter($connection);
}
Now you have a cache item pool in $container['cache'] which has methods defined in PSR-6.
Here is a sample code using it:
class SampleClass {
protected $cache;
public function __construct($cache) {
$this->cache = $cache;
}
public function doSomething() {
$item = $this->cache->getItem('unique-cache-key');
if ($item->isHit()) {
return 'I was previously called at ' . $item->get();
}
else {
$item->set(time());
$item->expiresAfter(3600);
$this->cache->save($item);
return 'I am being called for the first time, I will return results from cache for the next 3600 seconds.';
}
}
}
Now when you want to create new instance of SampleClass you should pass this cache item pool from the DIC, for example in a route callback:
$app->get('/foo', function (){
$bar = new SampleClass($this->get('cache'));
return $bar->doSomething();
});
$memcached = new \Memcached();
$memcached->addServer($cachedHost, $cachedPort);
$metadataCache = new \Doctrine\Common\Cache\MemcachedCache();
$metadataCache->setMemcached($memcached);
$queryCache = new \Doctrine\Common\Cache\MemcachedCache();
$queryCache->setMemcached($memcached);

Is this data being overwritten by another component?

I'm doing some programming in Silex with the symfony components and I think I have found a bug with the symfony/serializer and the symfony/validator components.
First let me explain what I'm traing to achieve, then let's go to the code.
My objective is to annotate a class with information like serialization directives as well as validation directives. As the reading of these annotations can cost a litle cpu, I like to cache them in memory. For this purpose, I'm using memcache wrapper in the Doctrine/Common/Cache package.
The problem I face is that both the symfony/serializer and the symfony/validator write Metadata to the cache using the class name as key. When they try to retrieve the metadata later, they throw an exception, because the cache has invalid metadata, either an instance of Symfony\Component\Validator\Mapping\ClassMetadata or Symfony\Component\Serializer\Mapping\ClassMetadataInterface.
Following is a reproductible example (sorry if its big, I tried to make as small as possible):
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
class Foo
{
/**
* #var int
* #Assert\NotBlank(message="This field cannot be empty")
*/
private $someProperty;
/**
* #return int
* #Groups({"some_group"})
*/
public function getSomeProperty() {
return $this->someProperty;
}
}
use Doctrine\Common\Annotations\AnnotationReader;
use \Memcache as MemcachePHP;
use Doctrine\Common\Cache\MemcacheCache as MemcacheWrapper;
$loader = require_once __DIR__ . '/../vendor/autoload.php';
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader([$loader, 'loadClass']);
$memcache = new MemcachePHP();
if (! $memcache->connect('localhost', '11211')) {
throw new \Exception('Unable to connect to memcache server');
}
$cacheDriver = new MemcacheWrapper();
$cacheDriver->setMemcache($memcache);
$app = new \Silex\Application();
$app->register(new Silex\Provider\SerializerServiceProvider());
$app['serializer.normalizers'] = function () use ($app, $cacheDriver) {
$classMetadataFactory = new Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory(
new Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader(new AnnotationReader()), $cacheDriver);
return [new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer($classMetadataFactory) ];
};
$app->register(new Silex\Provider\ValidatorServiceProvider(), [
'validator.mapping.class_metadata_factory' =>
new \Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory(
new \Symfony\Component\Validator\Mapping\Loader\AnnotationLoader(new AnnotationReader()),
new \Symfony\Component\Validator\Mapping\Cache\DoctrineCache($cacheDriver)
)
]);
$app->get('/', function(\Silex\Application $app) {
$foo = new Foo();
$app['validator']->validate($foo);
$json = $app['serializer']->serialize($foo, 'json');
return new \Symfony\Component\HttpFoundation\JsonResponse($json, \Symfony\Component\HttpFoundation\Response::HTTP_OK, [], true);
});
$app->error(function (\Exception $e, \Symfony\Component\HttpFoundation\Request $request, $code) {
return new \Symfony\Component\HttpFoundation\Response('We are sorry, but something went terribly wrong.' . $e->getMessage());
});
$app->run();
After running this example you get fatal errors.
Can anyone confirm that I'm not making a hard mistake here?
Currently my workaround for this is rewrite the DoctrineCache class making use of a namespace for the cache keys. Its working, but I think its ugly.
I think what you need to do is two separate CacheDrivers. See https://github.com/doctrine/cache/blob/master/lib/Doctrine/Common/Cache/CacheProvider.php for how namespaces are used there.
You could:
$validatorCacheDriver = new MemcacheWrapper();
$validatorCacheDriver->setMemcache($memcache);
$validatorCacheDriver->setNamespace('symfony_validator');
$serializerCacheDriver = new MemcacheWrapper();
$serializerCacheDriver->setMemcache($memcache);
$serializerCacheDriver->setNamespace('symfony_serializer');
// note that the two drivers are using the same memcache instance,
// so only one connection will be used.
$app['serializer.normalizers'] = function () use ($app, $serializerCacheDriver) {
$classMetadataFactory = new Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory(
new Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader(new AnnotationReader()), $serializerCacheDriver);
return [new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer($classMetadataFactory) ];
};
$app->register(new Silex\Provider\ValidatorServiceProvider(), [
'validator.mapping.class_metadata_factory' =>
new \Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory(
new \Symfony\Component\Validator\Mapping\Loader\AnnotationLoader(new AnnotationReader()),
new \Symfony\Component\Validator\Mapping\Cache\DoctrineCache($validatorCacheDriver)
)
]);
I've trimmed the code to only show the parts that play some part in the solution. I hope this helps!

ZF2 - Make onbootstrap events in one module supersede every other module

I have a ZF2 module called "Browsercheck", when used with other modules, checks the user's browser and shows an "Unsupported Browser" page if the browser is not supported. The issue I am having is, that the following code which is onBootstrap of the "Browsercheck" is not taking the first preference when other modules encounter an exception or a 404 error. IT works otherwise.
How can I make sure this code executes for every event and supersede any other event? Ideally in ZF1, I use code like this in index.php.. but not sure how it should be in ZF2. Basically if the browser is not supported, it shouldn't matter whether it's a 404 or an exception. It should go ahead and render the unsupported browser page.
public function onBootstrap(MvcEvent $e)
{
$sharedEvents = $e->getApplication()->getEventManager()->getSharedManager();
$sharedEvents->attach('Zend\Mvc\Controller\AbstractController','dispatch',
function($event)
{
$browser = new \BrowserCheck\Service\BrowserCheck();
if (!$browser->isCompatible())
{
$viewModel = new \Zend\View\Model\ViewModel();
$viewModel->setTerminal(true);
$request = $event->getRequest();
$response = $event->getResponse();
$viewModel->setTemplate('browser-check/index/index.phtml');
//Replace the entire view
$event->setViewModel($viewModel);
$event->stopPropagation();
$response->setStatusCode(200);
return $viewModel;
}
});
}
UPDATE:
$browserEventListener = function($event)
{
$browser = new \BrowserCheck\Service\BrowserCheck();
if (!$browser->isCompatible())
{
$viewModel = new \Zend\View\Model\ViewModel();
$viewModel->setTerminal(true);
$request = $event->getRequest();
$response = $event->getResponse();
$viewModel->setTemplate('browser-check/index/index.phtml');
//Replace the entire view
$event->setViewModel($viewModel);
$event->stopPropagation();
$response->setStatusCode(200);
return $viewModel;
}
};
$sharedEvents = $e->getApplication()->getEventManager()->getSharedManager();
$sharedEvents->attach('Zend\Mvc\Controller\AbstractController',
array('dispatch','dispatch.error'), $browserEventListener, 100);
The problem here is that you register your event listener on the dispatch event, which occurs after bootstrap and after route events. If the event listener triggered at route event cannot identify the route, you receive a 404 error page and the application lifecycle is terminated before any other dispatch event listener could be invoked.
In order to invoke your event listener before any routing is applied, you now have two options:
either register your event listener on bootstrap event with lower prio (e.g. lower than 10000)
or to register it on route listener with some higher priority, 10 should be enough
According to ZF2 doc page, onBoostrap() module methods are called with priority 10000 and onRoute() methods of \Zend\Mvc\ModuleRouteListener and \Zend\Mvc\RouteListener are called with priority 1 (higher the number higher the priority).
EDIT:
You should be doing something like this:
public function onBootstrap(MvcEvent $e)
{
/* ... all the default stuff here ... */
$this->registerBrowserCheckEvent($e);
}
private function registerBrowserCheckEvent(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$browserEventListener = function ($event) {
$browser = new \BrowserCheck\Service\BrowserCheck();
if (!$browser->isCompatible()) {
$viewModel = new \Zend\View\Model\ViewModel();
$viewModel->setTerminal(true);
$request = $event->getRequest();
$response = $event->getResponse();
$viewModel->setTemplate('browser-check/index/index.phtml');
//Replace the entire view
$event->setViewModel($viewModel);
$event->stopPropagation();
$response->setStatusCode(200);
return $viewModel;
}
};
$eventManager->attach(MvcEvent::EVENT_BOOTSTRAP, $browserEventListener, 100);
}
Also please notice that ZF2 uses PSR-2 (at least) and your code should follow this convention as well (mind the small differences between your code and mine).

Categories