Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

Commit

Permalink
Merge branch 'feature/6' into develop
Browse files Browse the repository at this point in the history
Close #6
  • Loading branch information
weierophinney committed Jul 20, 2015
2 parents 722f597 + 14f06d6 commit 252b4c1
Show file tree
Hide file tree
Showing 11 changed files with 612 additions and 5 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Expand Up @@ -6,7 +6,17 @@ All notable changes to this project will be documented in this file, in reverse

### Added

- Nothing.
- [#6](https://github.com/zendframework/zend-log/pull/6) adds
[PSR-3](http://www.php-fig.org/psr/psr-3/) support to zend-log:
- `Zend\Log\PsrLoggerAdapter` allows you to decorate a
`Zend\Log\LoggerInterface` instance so it can be used wherever a PSR-3
logger is expected.
- `Zend\Log\Writer\Psr` allows you to decorate a PSR-3 logger instance for use
as a log writer with `Zend\Log\Logger`.
- `Zend\Log\Processor\PsrPlaceholder` allows you to use PSR-3-compliant
message placeholders in your log messages; they will be substituted from
corresponding keys of values passed in the `$extra` array when logging the
message.

### Deprecated

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -16,7 +16,8 @@
"require": {
"php": ">=5.5",
"zendframework/zend-servicemanager": "~2.5",
"zendframework/zend-stdlib": "~2.5"
"zendframework/zend-stdlib": "~2.5",
"psr/log": "~1.0"
},
"require-dev": {
"zendframework/zend-console": "~2.5",
Expand Down
61 changes: 61 additions & 0 deletions doc/book/psr3.md
@@ -0,0 +1,61 @@
# PSR-3 Logger Interface compatibility

[PSR-3 Logger Interface](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
is a standards recommendation defining a common interface for logging libraries. The `zend-log`
component predates it, and has minor incompatibilities, but starting with version 2.6 provides the
following compatibility features:

- PSR logger adapter
- PSR logger writer
- PSR placeholder processor

## PsrLoggerAdapter

`Zend\Log\PsrLoggerAdapter` wraps `Zend\Log\LoggerInterface`, allowing it to be used
anywhere `Psr\Log\LoggerInterface` is expected.

```php
$zendLogLogger = new Zend\Log\Logger;

$psrLogger = new Zend\Log\PsrLoggerAdapter($zendLogLogger);
$psrLogger->log(Psr\Log\LogLevel::INFO, 'We have a PSR-compatible logger');
```

## PSR-3 log writer

`Zend\Log\Writer\Psr` allows log messages and extras to be forwared to any PSR-3 compatible logger.
As with any log writer, this has the added benefit that you filters can be used to limit forwarded
messages.

The writer needs a `Psr\Logger\LoggerInterface` instance to be useful, and fallbacks to
`Psr\Log\NullLogger` if none is provided. There are three ways to provide the PSR logger instance to
the log writer:

```php
// Via constructor parameter:
$writer = new Zend\Log\Writer\Psr($psrLogger);

// Via option:
$writer = new Zend\Log\Writer\Psr(['logger' => $psrLogger]);

// Via setter injection:
$writer = new Zend\Log\Writer\Psr();
$writer->setLogger($psrLogger);
```

## PSR-3 placeholder processor

`Zend\Log\Processor\PsrPlaceholder` adds support for [PSR-3 message placeholders](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#12-message).
Placeholder names correspond to keys in the "extras" array passed when logging a message.

Values can be of arbitrary type, including all scalars, and objects implementing `__toString`;
objects not capable of string serialization will result in the fully-qualified class name being
substituted.

```php
$logger = new Zend\Log\Logger;
$logger->addProcessor(new Zend\Log\Processor\PsrPlaceholder);

$logger->info('User with email {email} registered', ['email' => 'user@example.org']);
// logs message 'User with email user@example.org registered'
```
50 changes: 50 additions & 0 deletions src/Processor/PsrPlaceholder.php
@@ -0,0 +1,50 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Log\Processor;

/**
* Processes an event message according to PSR-3 rules.
*
* This processor replaces `{foo}` with the value from `$extra['foo']`.
*/
class PsrPlaceholder implements ProcessorInterface
{
/**
* @param array $event event data
* @return array event data
*/
public function process(array $event)
{
if (false === strpos($event['message'], '{')) {
return $event;
}

$replacements = [];
foreach ($event['extra'] as $key => $val) {
if (is_null($val)
|| is_scalar($val)
|| (is_object($val) && method_exists($val, "__toString"))
) {
$replacements['{'.$key.'}'] = $val;
continue;
}

if (is_object($val)) {
$replacements['{'.$key.'}'] = '[object '.get_class($val).']';
continue;
}

$replacements['{'.$key.'}'] = '['.gettype($val).']';
}

$event['message'] = strtr($event['message'], $replacements);
return $event;
}
}
10 changes: 7 additions & 3 deletions src/ProcessorPluginManager.php
Expand Up @@ -11,6 +11,9 @@

use Zend\ServiceManager\AbstractPluginManager;

/**
* Plugin manager for log processors.
*/
class ProcessorPluginManager extends AbstractPluginManager
{
/**
Expand All @@ -19,9 +22,10 @@ class ProcessorPluginManager extends AbstractPluginManager
* @var array
*/
protected $invokableClasses = [
'backtrace' => 'Zend\Log\Processor\Backtrace',
'referenceid' => 'Zend\Log\Processor\ReferenceId',
'requestid' => 'Zend\Log\Processor\RequestId',
'backtrace' => 'Zend\Log\Processor\Backtrace',
'psrplaceholder' => 'Zend\Log\Processor\PsrPlaceholder',
'referenceid' => 'Zend\Log\Processor\ReferenceId',
'requestid' => 'Zend\Log\Processor\RequestId',
];

/**
Expand Down
88 changes: 88 additions & 0 deletions src/PsrLoggerAdapter.php
@@ -0,0 +1,88 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Log;

use Psr\Log\AbstractLogger as PsrAbstractLogger;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;

/**
* PSR-3 logger adapter for Zend\Log\LoggerInterface
*
* Decorates a LoggerInterface to allow it to be used anywhere a PSR-3 logger
* is expected.
*/
class PsrLoggerAdapter extends PsrAbstractLogger
{
/**
* Zend\Log logger
*
* @var LoggerInterface
*/
protected $logger;

/**
* Map PSR-3 LogLevels to priority
*
* @var array
*/
protected $psrPriorityMap = [
LogLevel::EMERGENCY => Logger::EMERG,
LogLevel::ALERT => Logger::ALERT,
LogLevel::CRITICAL => Logger::CRIT,
LogLevel::ERROR => Logger::ERR,
LogLevel::WARNING => Logger::WARN,
LogLevel::NOTICE => Logger::NOTICE,
LogLevel::INFO => Logger::INFO,
LogLevel::DEBUG => Logger::DEBUG,
];

/**
* Constructor
*
* @param LoggerInterface $logger
*/
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}

/**
* Returns composed LoggerInterface instance.
*
* @return LoggerInterface
*/
public function getLogger()
{
return $this->logger;
}

/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
* @return null
* @throws InvalidArgumentException if log level is not recognized
*/
public function log($level, $message, array $context = [])
{
if (! array_key_exists($level, $this->psrPriorityMap)) {
throw new InvalidArgumentException(sprintf(
'$level must be one of PSR-3 log levels; received %s',
var_export($level, 1)
));
}

$priority = $this->psrPriorityMap[$level];
$this->logger->log($priority, $message, $context);
}
}
99 changes: 99 additions & 0 deletions src/Writer/Psr.php
@@ -0,0 +1,99 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Log\Writer;

use Psr\Log\LogLevel;
use Psr\Log\LoggerAwareTrait as PsrLoggerAwareTrait;
use Psr\Log\LoggerInterface as PsrLoggerInterface;
use Psr\Log\NullLogger;
use Zend\Log\Logger;

/**
* Proxies log messages to an existing PSR-3 compliant logger.
*/
class Psr extends AbstractWriter
{
use PsrLoggerAwareTrait;

/**
* Map priority to PSR-3 LogLevels
*
* @var int[]
*/
protected $psrPriorityMap = [
Logger::EMERG => LogLevel::EMERGENCY,
Logger::ALERT => LogLevel::ALERT,
Logger::CRIT => LogLevel::CRITICAL,
Logger::ERR => LogLevel::ERROR,
Logger::WARN => LogLevel::WARNING,
Logger::NOTICE => LogLevel::NOTICE,
Logger::INFO => LogLevel::INFO,
Logger::DEBUG => LogLevel::DEBUG,
];

/**
* Default log level (warning)
*
* @var int
*/
protected $defaultLogLevel = LogLevel::WARNING;

/**
* Constructor
*
* Set options for a writer. Accepted options are:
*
* - filters: array of filters to add to this filter
* - formatter: formatter for this writer
* - logger: PsrLoggerInterface implementation
*
* @param array|Traversable|LoggerInterface $options
* @throws Exception\InvalidArgumentException
*/
public function __construct($options = null)
{
if ($options instanceof PsrLoggerInterface) {
$this->setLogger($options);
}

if ($options instanceof Traversable) {
$options = iterator_to_array($options);
}

if (is_array($options) && isset($options['logger'])) {
$this->setLogger($options['logger']);
}

parent::__construct($options);

if (null === $this->logger) {
$this->setLogger(new NullLogger);
}
}

/**
* Write a message to the PSR-3 compliant logger.
*
* @param array $event event data
* @return void
*/
protected function doWrite(array $event)
{
$priority = $event['priority'];
$message = $event['message'];
$context = $event['extra'];

$level = isset($this->psrPriorityMap[$priority])
? $this->psrPriorityMap[$priority]
: $this->defaultLogLevel;

$this->logger->log($level, $message, $context);
}
}
4 changes: 4 additions & 0 deletions src/WriterPluginManager.php
Expand Up @@ -11,6 +11,9 @@

use Zend\ServiceManager\AbstractPluginManager;

/**
* Plugin manager for log writers.
*/
class WriterPluginManager extends AbstractPluginManager
{
protected $aliases = [
Expand All @@ -31,6 +34,7 @@ class WriterPluginManager extends AbstractPluginManager
'mail' => 'Zend\Log\Writer\Mail',
'mock' => 'Zend\Log\Writer\Mock',
'noop' => 'Zend\Log\Writer\Noop',
'psr' => 'Zend\Log\Writer\Psr',
'stream' => 'Zend\Log\Writer\Stream',
'syslog' => 'Zend\Log\Writer\Syslog',
'zendmonitor' => 'Zend\Log\Writer\ZendMonitor',
Expand Down

0 comments on commit 252b4c1

Please sign in to comment.