diff --git a/composer.json b/composer.json index 3ffc5aaf..a510c201 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "homepage": "https://github.com/zendframework/zend-log", "autoload": { "psr-4": { - "Zend\\Log": "src/" + "Zend\\Log\\": "src/" } }, "require": { diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index c1e89950..58042a69 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -1,21 +1,11 @@ events[] = $event; + return true; + } +} diff --git a/src/Filter/Priority.php b/src/Filter/Priority.php index 565efe42..8c717a85 100644 --- a/src/Filter/Priority.php +++ b/src/Filter/Priority.php @@ -1,34 +1,22 @@ regex = $regex; } @@ -67,7 +65,7 @@ public function filter(array $event) { $message = $event['message']; if (is_array($event['message'])) { - $message = var_export($message, TRUE); + $message = var_export($message, TRUE); } return preg_match($this->regex, $message) > 0; } diff --git a/src/Filter/SuppressFilter.php b/src/Filter/SuppressFilter.php index 662d2a2c..bd7f1d3d 100644 --- a/src/Filter/SuppressFilter.php +++ b/src/Filter/SuppressFilter.php @@ -1,34 +1,20 @@ validator = $validator; } @@ -55,7 +56,7 @@ public function __construct(ZendValidator $validator) * Returns TRUE to accept the message, FALSE to block it. * * @param array $event event data - * @return boolean + * @return boolean */ public function filter(array $event) { diff --git a/src/Formatter/Base.php b/src/Formatter/Base.php new file mode 100644 index 00000000..a7fd4acf --- /dev/null +++ b/src/Formatter/Base.php @@ -0,0 +1,121 @@ +dateTimeFormat = $dateTimeFormat; + } + } + + /** + * Formats data to be written by the writer. + * + * @param array $event event data + * @return array + */ + public function format($event) + { + foreach ($event as $key => $value) { + // Keep extra as an array + if ('extra' === $key) { + $event[$key] = self::format($value); + } else { + $event[$key] = $this->normalize($value); + } + } + + return $event; + } + + /** + * Normalize all non-scalar data types (except null) in a string value + * + * @param mixed $value + * @return mixed + */ + protected function normalize($value) + { + if (is_scalar($value) || null === $value) { + return $value; + } + + if ($value instanceof DateTime) { + $value = $value->format($this->getDateTimeFormat()); + } elseif (is_array($value) || $value instanceof Traversable) { + if ($value instanceof Traversable) { + $value = iterator_to_array($value); + } + foreach ($value as $key => $subvalue) { + $value[$key] = $this->normalize($subvalue); + } + $value = json_encode($value); + } elseif (is_object($value) && !method_exists($value,'__toString')) { + $value = sprintf('object(%s) %s', get_class($value), json_encode($value)); + } elseif (is_resource($value)) { + $value = sprintf('resource(%s)', get_resource_type($value)); + } elseif (!is_object($value)) { + $value = gettype($value); + } + + return (string) $value; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormat() + { + return $this->dateTimeFormat; + } + + /** + * {@inheritDoc} + */ + public function setDateTimeFormat($dateTimeFormat) + { + $this->dateTimeFormat = (string) $dateTimeFormat; + return $this; + } +} diff --git a/src/Formatter/ChromePhp.php b/src/Formatter/ChromePhp.php new file mode 100644 index 00000000..158f5b09 --- /dev/null +++ b/src/Formatter/ChromePhp.php @@ -0,0 +1,51 @@ +setDateTimeFormat($dateTimeFormat); + } + } + + /** + * Formats data to be written by the writer. + * + * @param array $event event data + * @return array + */ + public function format($event) + { + $format = $this->getDateTimeFormat(); + array_walk_recursive($event, function (&$value) use ($format) { + if ($value instanceof DateTime) { + $value = $value->format($format); + } + }); + + return $event; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormat() + { + return $this->dateTimeFormat; + } + + /** + * {@inheritDoc} + */ + public function setDateTimeFormat($dateTimeFormat) + { + $this->dateTimeFormat = (string) $dateTimeFormat; + return $this; + } +} diff --git a/src/Formatter/ErrorHandler.php b/src/Formatter/ErrorHandler.php index f1d6e964..f8c74151 100644 --- a/src/Formatter/ErrorHandler.php +++ b/src/Formatter/ErrorHandler.php @@ -1,84 +1,75 @@ format; + + if (isset($event['timestamp']) && $event['timestamp'] instanceof DateTime) { + $event['timestamp'] = $event['timestamp']->format($this->getDateTimeFormat()); } - if (!is_string($format)) { - throw new Exception\InvalidArgumentException('Format must be a string'); + foreach ($this->buildReplacementsFromArray($event) as $name => $value) { + $output = str_replace("%$name%", $value, $output); } - $this->format = $format; + return $output; } /** - * This method formats the event for the PHP Error Handler. + * Flatten the multi-dimensional $event array into a single dimensional + * array * - * @param array $event - * @return string + * @param array $event + * @param string $key + * @return array */ - public function format($event) + protected function buildReplacementsFromArray ($event, $key = null) { - $output = $this->format; - foreach ($event as $name => $value) { - if (is_array($value)) { - foreach ($value as $sname => $svalue) { - $output = str_replace("%{$name}[{$sname}]%", $svalue, $output); + $result = array(); + foreach ($event as $index => $value) { + $nextIndex = $key === null ? $index : $key . '[' . $index . ']'; + if ($value === null) { + continue; + } + if (! is_array($value)) { + if ($key === null) { + $result[$nextIndex] = $value; + } else { + if (! is_object($value) || method_exists($value, "__toString")) { + $result[$nextIndex] = $value; + } } } else { - $output = str_replace("%$name%", $value, $output); + $result = array_merge($result, $this->buildReplacementsFromArray($value, $nextIndex)); } } - return $output; + return $result; } } diff --git a/src/Formatter/ExceptionHandler.php b/src/Formatter/ExceptionHandler.php index 7937d152..bb1e2bc5 100644 --- a/src/Formatter/ExceptionHandler.php +++ b/src/Formatter/ExceptionHandler.php @@ -1,35 +1,32 @@ format($this->getDateTimeFormat()); + } + $output = $event['timestamp'] . ' ' . $event['priorityName'] . ' (' . $event['priority'] . ') ' . $event['message'] .' in ' . $event['extra']['file'] . ' on line ' . $event['extra']['line']; @@ -58,11 +59,28 @@ public function format($event) return $output; } + /** + * {@inheritDoc} + */ + public function getDateTimeFormat() + { + return $this->dateTimeFormat; + } + + /** + * {@inheritDoc} + */ + public function setDateTimeFormat($dateTimeFormat) + { + $this->dateTimeFormat = (string) $dateTimeFormat; + return $this; + } + /** * Get the type of a function - * + * * @param string $type - * @return string + * @return string */ protected function getType($type) { diff --git a/src/Formatter/FirePhp.php b/src/Formatter/FirePhp.php new file mode 100644 index 00000000..51b1e3d3 --- /dev/null +++ b/src/Formatter/FirePhp.php @@ -0,0 +1,59 @@ +format = $format; + $this->format = isset($format) ? $format : static::DEFAULT_FORMAT; + + parent::__construct($dateTimeFormat); } /** @@ -69,19 +67,22 @@ public function format($event) { $output = $this->format; - if (!isset($event['info'])) { - $event['info'] = ''; - } + $event = parent::format($event); foreach ($event as $name => $value) { - if ((is_object($value) && !method_exists($value,'__toString')) - || is_array($value) - ) { - $value = gettype($value); + if ('extra' == $name && count($value)) { + $value = $this->normalize($value); + } elseif ('extra' == $name) { + // Don't print an empty array + $value = ''; } - $output = str_replace("%$name%", $value, $output); } + if (isset($event['extra']) && empty($event['extra']) + && false !== strpos($this->format, '%extra%') + ) { + $output = rtrim($output, ' '); + } return $output; } } diff --git a/src/Formatter/Xml.php b/src/Formatter/Xml.php index 616eeb4e..c28d849e 100644 --- a/src/Formatter/Xml.php +++ b/src/Formatter/Xml.php @@ -1,37 +1,26 @@ elementMap = $options['elementMap']; } + + if (array_key_exists('dateTimeFormat', $options)) { + $this->setDateTimeFormat($options['dateTimeFormat']); + } } /** @@ -117,6 +127,33 @@ public function setEncoding($value) return $this; } + /** + * Set Escaper instance + * + * @param Escaper $escaper + * @return Xml + */ + public function setEscaper(Escaper $escaper) + { + $this->escaper = $escaper; + return $this; + } + + /** + * Get Escaper instance + * + * Lazy-loads an instance with the current encoding if none registered. + * + * @return Escaper + */ + public function getEscaper() + { + if (null === $this->escaper) { + $this->setEscaper(new Escaper($this->getEncoding())); + } + return $this->escaper; + } + /** * Formats data into a single line to be written by the writer. * @@ -125,6 +162,10 @@ public function setEncoding($value) */ public function format($event) { + if (isset($event['timestamp']) && $event['timestamp'] instanceof DateTime) { + $event['timestamp'] = $event['timestamp']->format($this->getDateTimeFormat()); + } + if ($this->elementMap === null) { $dataToInsert = $event; } else { @@ -134,9 +175,10 @@ public function format($event) } } - $enc = $this->getEncoding(); - $dom = new DOMDocument('1.0', $enc); - $elt = $dom->appendChild(new DOMElement($this->rootElement)); + $enc = $this->getEncoding(); + $escaper = $this->getEscaper(); + $dom = new DOMDocument('1.0', $enc); + $elt = $dom->appendChild(new DOMElement($this->rootElement)); foreach ($dataToInsert as $key => $value) { if (empty($value) @@ -144,7 +186,9 @@ public function format($event) || (is_object($value) && method_exists($value,'__toString')) ) { if ($key == "message") { - $value = htmlspecialchars($value, ENT_COMPAT, $enc); + $value = $escaper->escapeHtml($value); + } elseif ($key == "extra" && empty($value)) { + continue; } $elt->appendChild(new DOMElement($key, (string)$value)); } @@ -155,4 +199,21 @@ public function format($event) return $xml . PHP_EOL; } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormat() + { + return $this->dateTimeFormat; + } + + /** + * {@inheritDoc} + */ + public function setDateTimeFormat($dateTimeFormat) + { + $this->dateTimeFormat = (string) $dateTimeFormat; + return $this; + } } diff --git a/src/Logger.php b/src/Logger.php index 914240a4..3a4cd0c3 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -1,37 +1,25 @@ priority (short) name * @@ -79,6 +59,13 @@ class Logger implements LoggerInterface */ protected $writers; + /** + * Processors + * + * @var SplPriorityQueue + */ + protected $processors; + /** * Writer plugins * @@ -86,6 +73,13 @@ class Logger implements LoggerInterface */ protected $writerPlugins; + /** + * Processor plugins + * + * @var ProcessorPluginManager + */ + protected $processorPlugins; + /** * Registered error handler * @@ -103,12 +97,50 @@ class Logger implements LoggerInterface /** * Constructor * - * @todo support configuration (writers, dateTimeFormat, and writer plugin manager) + * Set options for an logger. Accepted options are: + * - writers: array of writers to add to this logger + * - exceptionhandler: if true register this logger as exceptionhandler + * - errorhandler: if true register this logger as errorhandler + * + * @param array|\Traversable $options * @return Logger + * @throws Exception\InvalidArgumentException */ - public function __construct() + public function __construct(array $options = null) { $this->writers = new SplPriorityQueue(); + + if ($options instanceof Traversable) { + $options = ArrayUtils::iteratorToArray($options); + } + + if (is_array($options)) { + + if(isset($options['writers']) && is_array($options['writers'])) { + foreach($options['writers'] as $writer) { + + if(!isset($writer['name'])) { + throw new Exception\InvalidArgumentException('Options must contain a name for the writer'); + } + + $priority = (isset($writer['priority'])) ? $writer['priority'] : null; + $writerOptions = (isset($writer['options'])) ? $writer['options'] : null; + + $this->addWriter($writer['name'], $priority, $writerOptions); + } + } + + if(isset($options['exceptionhandler']) && $options['exceptionhandler'] === true) { + self::registerExceptionHandler($this); + } + + if(isset($options['errorhandler']) && $options['errorhandler'] === true) { + self::registerErrorHandler($this); + } + + } + + $this->processors = new SplPriorityQueue(); } /** @@ -125,38 +157,15 @@ public function __destruct() } } - /** - * Return the format of DateTime - * - * @return string - */ - public function getDateTimeFormat() - { - return $this->dateTimeFormat; - } - - /** - * Set the format of DateTime - * - * @see http://nl3.php.net/manual/en/function.date.php - * @param string $format - * @return Logger - */ - public function setDateTimeFormat($format) - { - $this->dateTimeFormat = (string) $format; - return $this; - } - /** * Get writer plugin manager * * @return WriterPluginManager */ - public function getPluginManager() + public function getWriterPluginManager() { if (null === $this->writerPlugins) { - $this->setPluginManager(new WriterPluginManager()); + $this->setWriterPluginManager(new WriterPluginManager()); } return $this->writerPlugins; } @@ -164,11 +173,11 @@ public function getPluginManager() /** * Set writer plugin manager * - * @param string|WriterPluginManager $plugins + * @param string|WriterPluginManager $plugins * @return Logger * @throws Exception\InvalidArgumentException */ - public function setPluginManager($plugins) + public function setWriterPluginManager($plugins) { if (is_string($plugins)) { $plugins = new $plugins; @@ -190,32 +199,32 @@ public function setPluginManager($plugins) * * @param string $name * @param array|null $options - * @return Writer + * @return Writer\WriterInterface */ - public function plugin($name, array $options = null) + public function writerPlugin($name, array $options = null) { - return $this->getPluginManager()->get($name, $options); + return $this->getWriterPluginManager()->get($name, $options); } /** * Add a writer to a logger * - * @param string|Writer $writer + * @param string|Writer\WriterInterface $writer * @param int $priority + * @param array|null $options * @return Logger * @throws Exception\InvalidArgumentException */ - public function addWriter($writer, $priority=1) + public function addWriter($writer, $priority = 1, array $options = null) { if (is_string($writer)) { - $writer = $this->plugin($writer); + $writer = $this->writerPlugin($writer, $options); } elseif (!$writer instanceof Writer\WriterInterface) { throw new Exception\InvalidArgumentException(sprintf( 'Writer must implement Zend\Log\Writer; received "%s"', is_object($writer) ? get_class($writer) : gettype($writer) )); } - $this->writers->insert($writer, $priority); return $this; @@ -249,6 +258,90 @@ public function setWriters(SplPriorityQueue $writers) return $this; } + + /** + * Get processor plugin manager + * + * @return ProcessorPluginManager + */ + public function getProcessorPluginManager() + { + if (null === $this->processorPlugins) { + $this->setProcessorPluginManager(new ProcessorPluginManager()); + } + return $this->processorPlugins; + } + + /** + * Set processor plugin manager + * + * @param string|ProcessorPluginManager $plugins + * @return Logger + * @throws Exception\InvalidArgumentException + */ + public function setProcessorPluginManager($plugins) + { + if (is_string($plugins)) { + $plugins = new $plugins; + } + if (!$plugins instanceof ProcessorPluginManager) { + throw new Exception\InvalidArgumentException(sprintf( + 'processor plugin manager must extend %s\ProcessorPluginManager; received %s', + __NAMESPACE__, + is_object($plugins) ? get_class($plugins) : gettype($plugins) + )); + } + + $this->processorPlugins = $plugins; + return $this; + } + + /** + * Get processor instance + * + * @param string $name + * @param array|null $options + * @return Processor\ProcessorInterface + */ + public function processorPlugin($name, array $options = null) + { + return $this->getProcessorPluginManager()->get($name, $options); + } + + /** + * Add a processor to a logger + * + * @param string|Processor\ProcessorInterface $processor + * @param int $priority + * @param array|null $options + * @return Logger + * @throws Exception\InvalidArgumentException + */ + public function addProcessor($processor, $priority = 1, array $options = null) + { + if (is_string($processor)) { + $processor = $this->processorPlugin($processor, $options); + } elseif (!$processor instanceof Processor\ProcessorInterface) { + throw new Exception\InvalidArgumentException(sprintf( + 'Processor must implement Zend\Log\ProcessorInterface; received "%s"', + is_object($processor) ? get_class($processor) : gettype($processor) + )); + } + $this->processors->insert($processor, $priority); + + return $this; + } + + /** + * Get processors + * + * @return SplPriorityQueue + */ + public function getProcessors() + { + return $this->processors; + } + /** * Add a message as a log entry * @@ -258,6 +351,7 @@ public function setWriters(SplPriorityQueue $writers) * @return Logger * @throws Exception\InvalidArgumentException if message can't be cast to string * @throws Exception\InvalidArgumentException if extra can't be iterated over + * @throws Exception\RuntimeException if no log writer specified */ public function log($priority, $message, $extra = array()) { @@ -286,21 +380,26 @@ public function log($priority, $message, $extra = array()) throw new Exception\RuntimeException('No log writer specified'); } - $date = new DateTime(); - $timestamp = $date->format($this->getDateTimeFormat()); + $timestamp = new DateTime(); if (is_array($message)) { $message = var_export($message, true); } + $event = array( + 'timestamp' => $timestamp, + 'priority' => (int) $priority, + 'priorityName' => $this->priorities[$priority], + 'message' => (string) $message, + 'extra' => $extra + ); + + foreach($this->processors->toArray() as $processor) { + $event = $processor->process($event); + } + foreach ($this->writers->toArray() as $writer) { - $writer->write(array( - 'timestamp' => $timestamp, - 'priority' => (int) $priority, - 'priorityName' => $this->priorities[$priority], - 'message' => (string) $message, - 'extra' => $extra - )); + $writer->write($event); } return $this; @@ -397,7 +496,7 @@ public function debug($message, $extra = array()) public static function registerErrorHandler(Logger $logger) { // Only register once per instance - if (self::$registeredErrorHandler) { + if (static::$registeredErrorHandler) { return false; } @@ -423,7 +522,7 @@ public static function registerErrorHandler(Logger $logger) set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext) use ($errorHandlerMap, $logger) { $errorLevel = error_reporting(); - if ($errorLevel && $errno) { + if ($errorLevel & $errno) { if (isset($errorHandlerMap[$errno])) { $priority = $errorHandlerMap[$errno]; } else { @@ -437,7 +536,7 @@ public static function registerErrorHandler(Logger $logger) )); } }); - self::$registeredErrorHandler = true; + static::$registeredErrorHandler = true; return true; } @@ -448,7 +547,7 @@ public static function registerErrorHandler(Logger $logger) public static function unregisterErrorHandler() { restore_error_handler(); - self::$registeredErrorHandler = false; + static::$registeredErrorHandler = false; } /** @@ -462,7 +561,7 @@ public static function unregisterErrorHandler() public static function registerExceptionHandler(Logger $logger) { // Only register once per instance - if (self::$registeredExceptionHandler) { + if (static::$registeredExceptionHandler) { return false; } @@ -470,7 +569,7 @@ public static function registerExceptionHandler(Logger $logger) throw new Exception\InvalidArgumentException('Invalid Logger specified'); } - set_exception_handler(function ($exception) use ($logger){ + set_exception_handler(function ($exception) use ($logger) { $extra = array( 'file' => $exception->getFile(), 'line' => $exception->getLine(), @@ -481,7 +580,7 @@ public static function registerExceptionHandler(Logger $logger) } $logger->log(Logger::ERR, $exception->getMessage(), $extra); }); - self::$registeredExceptionHandler = true; + static::$registeredExceptionHandler = true; return true; } @@ -491,6 +590,6 @@ public static function registerExceptionHandler(Logger $logger) public static function unregisterExceptionHandler() { restore_exception_handler(); - self::$registeredExceptionHandler = false; + static::$registeredExceptionHandler = false; } } diff --git a/src/LoggerAwareInterface.php b/src/LoggerAwareInterface.php index cfb2c9fc..647be793 100644 --- a/src/LoggerAwareInterface.php +++ b/src/LoggerAwareInterface.php @@ -1,21 +1,11 @@ logger = $logger; + + return $this; + } +} diff --git a/src/LoggerInterface.php b/src/LoggerInterface.php index 394302b3..d32ceabf 100644 --- a/src/LoggerInterface.php +++ b/src/LoggerInterface.php @@ -1,86 +1,76 @@ get('Config'); + $logConfig = isset($config['log']) ? $config['log'] : array(); + $logger = new Logger($logConfig); + return $logger; + } +} diff --git a/src/Processor/Backtrace.php b/src/Processor/Backtrace.php new file mode 100644 index 00000000..9c566127 --- /dev/null +++ b/src/Processor/Backtrace.php @@ -0,0 +1,85 @@ + 5.4.0) + * @var int + */ + protected $traceLimit = 10; + + /** + * Classes within this namespace in the stack are ignored + * @var string + */ + protected $ignoredNamespace = 'Zend\\Log'; + + /** + * Adds the origin of the log() call to the event extras + * + * @param array $event event data + * @return array event data + */ + public function process(array $event) + { + $trace = $this->getBacktrace(); + + array_shift($trace); // ignore $this->getBacktrace(); + array_shift($trace); // ignore $this->process() + + $i = 0; + while (isset($trace[$i]['class']) + && false !== strpos($trace[$i]['class'], $this->ignoredNamespace) + ) { + $i++; + } + + $origin = array( + 'file' => isset($trace[$i-1]['file']) ? $trace[$i-1]['file'] : null, + 'line' => isset($trace[$i-1]['line']) ? $trace[$i-1]['line'] : null, + 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, + 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, + ); + + $extra = $origin; + if (isset($event['extra'])) { + $extra = array_merge($origin, $event['extra']); + } + $event['extra'] = $extra; + + return $event; + } + + /** + * Provide backtrace as slim as posible + * + * @return array: + */ + protected function getBacktrace() + { + if (version_compare(PHP_VERSION, '5.4.0') >= 0) { + return debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $this->traceLimit); + } + + if (version_compare(PHP_VERSION, '5.3.6') >= 0) { + return debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + + return debug_backtrace(); + } +} diff --git a/src/Processor/ProcessorInterface.php b/src/Processor/ProcessorInterface.php new file mode 100644 index 00000000..27de2ac6 --- /dev/null +++ b/src/Processor/ProcessorInterface.php @@ -0,0 +1,27 @@ +getIdentifier(); + return $event; + } + + /** + * Provide unique identifier for a request + * + * @return string + */ + protected function getIdentifier() + { + if ($this->identifier) { + return $this->identifier; + } + + $requestTime = (version_compare(PHP_VERSION, '5.4.0') >= 0) + ? $_SERVER['REQUEST_TIME_FLOAT'] + : $_SERVER['REQUEST_TIME']; + + if (Console::isConsole()) { + $this->identifier = md5($requestTime); + return $this->identifier; + } + + if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $this->identifier = md5($requestTime . $_SERVER['HTTP_X_FORWARDED_FOR']); + return $this->identifier; + } + + $this->identifier = md5($requestTime . $_SERVER['REMOTE_ADDR']); + return $this->identifier; + } +} diff --git a/src/ProcessorPluginManager.php b/src/ProcessorPluginManager.php new file mode 100644 index 00000000..86b1cf9c --- /dev/null +++ b/src/ProcessorPluginManager.php @@ -0,0 +1,60 @@ + 'Zend\Log\Processor\Backtrace', + 'requestid' => 'Zend\Log\Processor\RequestId', + ); + + /** + * Allow many writers of the same type + * + * @var bool + */ + protected $shareByDefault = false; + + /** + * Validate the plugin + * + * Checks that the processor loaded is an instance of Processor\ProcessorInterface. + * + * @param mixed $plugin + * @return void + * @throws Exception\InvalidArgumentException if invalid + */ + public function validatePlugin($plugin) + { + if ($plugin instanceof Processor\ProcessorInterface) { + // we're okay + return; + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Plugin of type %s is invalid; must implement %s\Processor\ProcessorInterface', + (is_object($plugin) ? get_class($plugin) : gettype($plugin)), + __NAMESPACE__ + )); + } +} diff --git a/src/Writer/AbstractWriter.php b/src/Writer/AbstractWriter.php index be2e1091..d8c2e286 100644 --- a/src/Writer/AbstractWriter.php +++ b/src/Writer/AbstractWriter.php @@ -1,43 +1,46 @@ addFilter($filter['name'], $filterOptions); + } + } + + if(isset($options['formatter'])) { + $formatter = $options['formatter']; + if(is_string($formatter) || $formatter instanceof Formatter\FormatterInterface) { + $this->setFormatter($formatter); + } elseif(is_array($formatter)) { + if(!isset($formatter['name'])) { + throw new Exception\InvalidArgumentException('Options must contain a name for the formatter'); + } + $formatterOptions = (isset($formatter['options'])) ? $formatter['options'] : null; + $this->setFormatter($formatter['name'], $formatterOptions); + } + } + } + } + /** * Add a filter specific to this writer. * - * @param Filter\FilterInterface|int $filter + * @param int|string|Filter\FilterInterface $filter + * @param array|null $options * @return AbstractWriter * @throws Exception\InvalidArgumentException */ - public function addFilter($filter) + public function addFilter($filter, array $options = null) { if (is_int($filter)) { $filter = new Filter\Priority($filter); - } elseif (!$filter instanceof Filter\FilterInterface) { + } + + if (is_string($filter)) { + $filter = $this->filterPlugin($filter, $options); + } + + if (!$filter instanceof Filter\FilterInterface) { throw new Exception\InvalidArgumentException(sprintf( - 'Filter must implement Zend\Log\Filter; received %s', + 'Writer must implement %s\Filter\FilterInterface; received "%s"', + __NAMESPACE__, is_object($filter) ? get_class($filter) : gettype($filter) )); } @@ -70,6 +139,105 @@ public function addFilter($filter) return $this; } + /** + * Get filter plugin manager + * + * @return FilterPluginManager + */ + public function getFilterPluginManager() + { + if (null === $this->filterPlugins) { + $this->setFilterPluginManager(new FilterPluginManager()); + } + return $this->filterPlugins; + } + + /** + * Set filter plugin manager + * + * @param string|FilterPluginManager $plugins + * @return self + * @throws Exception\InvalidArgumentException + */ + public function setFilterPluginManager($plugins) + { + if (is_string($plugins)) { + $plugins = new $plugins; + } + if (!$plugins instanceof FilterPluginManager) { + throw new Exception\InvalidArgumentException(sprintf( + 'Writer plugin manager must extend %s\FilterPluginManager; received %s', + __NAMESPACE__, + is_object($plugins) ? get_class($plugins) : gettype($plugins) + )); + } + + $this->filterPlugins = $plugins; + return $this; + } + + /** + * Get filter instance + * + * @param string $name + * @param array|null $options + * @return Filter\FilterInterface + */ + public function filterPlugin($name, array $options = null) + { + return $this->getFilterPluginManager()->get($name, $options); + } + + /** + * Get formatter plugin manager + * + * @return FormatterPluginManager + */ + public function getFormatterPluginManager() + { + if (null === $this->formatterPlugins) { + $this->setFormatterPluginManager(new FormatterPluginManager()); + } + return $this->formatterPlugins; + } + + /** + * Set formatter plugin manager + * + * @param string|FormatterPluginManager $plugins + * @return self + * @throws Exception\InvalidArgumentException + */ + public function setFormatterPluginManager($plugins) + { + if (is_string($plugins)) { + $plugins = new $plugins; + } + if (!$plugins instanceof FormatterPluginManager) { + throw new Exception\InvalidArgumentException(sprintf( + 'Writer plugin manager must extend %s\FormatterPluginManager; received %s', + __NAMESPACE__, + is_object($plugins) ? get_class($plugins) : gettype($plugins) + )); + } + + $this->formatterPlugins = $plugins; + return $this; + } + + + /** + * Get formatter instance + * + * @param string $name + * @param array|null $options + * @return Formatter\FormatterInterface + */ + public function formatterPlugin($name, array $options = null) + { + return $this->getFormatterPluginManager()->get($name, $options); + } + /** * Log a message to this writer. * @@ -84,24 +252,69 @@ public function write(array $event) } } - // exception occurs on error - $this->doWrite($event); + $errorHandlerStarted = false; + + if ($this->convertWriteErrorsToExceptions && !ErrorHandler::started()) { + ErrorHandler::start($this->errorsToExceptionsConversionLevel); + $errorHandlerStarted = true; + } + + try { + $this->doWrite($event); + } catch (\Exception $e) { + if ($errorHandlerStarted) { + ErrorHandler::stop(); + $errorHandlerStarted = false; + } + throw $e; + } + + if ($errorHandlerStarted) { + $error = ErrorHandler::stop(); + $errorHandlerStarted = false; + if ($error) { + throw new Exception\RuntimeException("Unable to write", 0, $error); + } + } } /** * Set a new formatter for this writer * - * @param Formatter $formatter + * @param string|Formatter\FormatterInterface $formatter * @return self + * @throws Exception\InvalidArgumentException */ - public function setFormatter(Formatter $formatter) + public function setFormatter($formatter, array $options = null) { + if (is_string($formatter)) { + $formatter = $this->formatterPlugin($formatter, $options); + } + + if (!$formatter instanceof Formatter\FormatterInterface) { + throw new Exception\InvalidArgumentException(sprintf( + 'Formatter must implement %s\Formatter\FormatterInterface; received "%s"', + __NAMESPACE__, + is_object($formatter) ? get_class($formatter) : gettype($formatter) + )); + } + $this->formatter = $formatter; return $this; } /** - * Perform shutdown activites such as closing open resources + * Set convert write errors to exception flag + * + * @param bool $ignoreWriteErrors + */ + public function setConvertWriteErrorsToExceptions($convertErrors) + { + $this->convertWriteErrorsToExceptions = $convertErrors; + } + + /** + * Perform shutdown activities such as closing open resources * * @return void */ diff --git a/src/Writer/ChromePhp.php b/src/Writer/ChromePhp.php new file mode 100644 index 00000000..73428c55 --- /dev/null +++ b/src/Writer/ChromePhp.php @@ -0,0 +1,105 @@ +chromephp = $instance === null ? $this->getChromePhp() : $instance; + $this->formatter = new ChromePhpFormatter(); + } + + /** + * Write a message to the log. + * + * @param array $event event data + * @return void + */ + protected function doWrite(array $event) + { + $line = $this->formatter->format($event); + + switch ($event['priority']) { + case Logger::EMERG: + case Logger::ALERT: + case Logger::CRIT: + case Logger::ERR: + $this->chromephp->error($line); + break; + case Logger::WARN: + $this->chromephp->warn($line); + break; + case Logger::NOTICE: + case Logger::INFO: + $this->chromephp->info($line); + break; + case Logger::DEBUG: + $this->chromephp->trace($line); + break; + default: + $this->chromephp->log($line); + break; + } + } + + /** + * Gets the ChromePhpInterface instance that is used for logging. + * + * @return ChromePhpInterface + */ + public function getChromePhp() + { + // Remember: class names in strings are absolute; thus the class_exists + // here references the canonical name for the ChromePhp class + if (!$this->chromephp instanceof ChromePhpInterface + && class_exists('ChromePhp') + ) { + $this->setChromePhp(new ChromePhpBridge()); + } + return $this->chromephp; + } + + /** + * Sets the ChromePhpInterface instance that is used for logging. + * + * @param ChromePhpInterface $instance The instance to set. + * @return ChromePhp + */ + public function setChromePhp(ChromePhpInterface $instance) + { + $this->chromephp = $instance; + return $this; + } +} diff --git a/src/Writer/ChromePhp/ChromePhpBridge.php b/src/Writer/ChromePhp/ChromePhpBridge.php new file mode 100644 index 00000000..d6a97655 --- /dev/null +++ b/src/Writer/ChromePhp/ChromePhpBridge.php @@ -0,0 +1,71 @@ +db = $db; $this->tableName = $tableName; $this->columnMap = $columnMap; - + if (!empty($separator)) { $this->separator = $separator; } - } - /** - * Formatting is not possible on this writer - * - * @param Formatter\FormatterInterface $formatter - * @return void - * @throws Exception\InvalidArgumentException - */ - public function setFormatter(Formatter\FormatterInterface $formatter) - { - throw new Exception\InvalidArgumentException(get_class() . ' does not support formatting'); + $this->setFormatter(new DbFormatter()); } /** @@ -124,6 +118,8 @@ protected function doWrite(array $event) throw new Exception\RuntimeException('Database adapter is null'); } + $event = $this->formatter->format($event); + // Transform the event array into fields if (null === $this->columnMap) { $dataToInsert = $this->eventIntoColumn($event); @@ -133,34 +129,35 @@ protected function doWrite(array $event) $statement = $this->db->query($this->prepareInsert($this->db, $this->tableName, $dataToInsert)); $statement->execute($dataToInsert); - + } /** * Prepare the INSERT SQL statement - * + * * @param Adapter $db * @param string $tableName * @param array $fields - * @return string + * @return string */ - protected function prepareInsert(Adapter $db, $tableName, array $fields) - { + protected function prepareInsert(Adapter $db, $tableName, array $fields) + { + $keys = array_keys($fields); $sql = 'INSERT INTO ' . $db->platform->quoteIdentifier($tableName) . ' (' . - implode(",",array_map(array($db->platform, 'quoteIdentifier'), $fields)) . ') VALUES (' . - implode(",",array_map(array($db->driver, 'formatParameterName'), $fields)) . ')'; - + implode(",",array_map(array($db->platform, 'quoteIdentifier'), $keys)) . ') VALUES (' . + implode(",",array_map(array($db->driver, 'formatParameterName'), $keys)) . ')'; + return $sql; } /** * Map event into column using the $columnMap array - * + * * @param array $event * @param array $columnMap - * @return array + * @return array */ - protected function mapEventIntoColumn(array $event, array $columnMap = null) + protected function mapEventIntoColumn(array $event, array $columnMap = null) { if (empty($event)) { return array(); @@ -176,18 +173,18 @@ protected function mapEventIntoColumn(array $event, array $columnMap = null) } } elseif (isset($columnMap[$name])) { $data[$columnMap[$name]] = $value; - } + } } return $data; } /** * Transform event into column for the db table - * + * * @param array $event - * @return array + * @return array */ - protected function eventIntoColumn(array $event) + protected function eventIntoColumn(array $event) { if (empty($event)) { return array(); diff --git a/src/Writer/FilterPluginManager.php b/src/Writer/FilterPluginManager.php new file mode 100644 index 00000000..e5a16f6e --- /dev/null +++ b/src/Writer/FilterPluginManager.php @@ -0,0 +1,66 @@ + 'Zend\Log\Filter\Mock', + 'priority' => 'Zend\Log\Filter\Priority', + 'regex' => 'Zend\Log\Filter\Regex', + 'suppress' => 'Zend\Log\Filter\suppressFilter', + 'suppressfilter' => 'Zend\Log\Filter\suppressFilter', + 'validator' => 'Zend\Log\Filter\Validator', + ); + + /** + * Allow many filters of the same type + * + * @var bool + */ + protected $shareByDefault = false; + + /** + * Validate the plugin + * + * Checks that the filter loaded is an instance of Filter\FilterInterface. + * + * @param mixed $plugin + * @return void + * @throws Exception\InvalidArgumentException if invalid + */ + public function validatePlugin($plugin) + { + if ($plugin instanceof Filter\FilterInterface) { + // we're okay + return; + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Plugin of type %s is invalid; must implement %s\Filter\FilterInterface', + (is_object($plugin) ? get_class($plugin) : gettype($plugin)), + __NAMESPACE__ + )); + } +} diff --git a/src/Writer/FingersCrossed.php b/src/Writer/FingersCrossed.php new file mode 100644 index 00000000..4e70a32f --- /dev/null +++ b/src/Writer/FingersCrossed.php @@ -0,0 +1,268 @@ +writer = $writer; + + if ($writer instanceof Traversable) { + $writer = ArrayUtils::iteratorToArray($writer); + } + + if (is_array($writer)) { + $filterOrPriority = isset($writer['priority']) ? $writer['priority'] : null; + $bufferSize = isset($writer['bufferSize']) ? $writer['bufferSize'] : null; + $writer = isset($writer['writer']) ? $writer['writer'] : null; + } + + if (null === $filterOrPriority) { + $filterOrPriority = new PriorityFilter(Logger::WARN); + } elseif (!$filterOrPriority instanceof FilterInterface) { + $filterOrPriority = new PriorityFilter($filterOrPriority); + } + + if (is_array($writer) && isset($writer['name'])) { + $this->setWriter($writer['name'], $writer['options']); + } else { + $this->setWriter($writer); + } + $this->addFilter($filterOrPriority); + $this->bufferSize = $bufferSize; + } + + /** + * Set a new formatter for this writer + * + * @param string|Formatter\FormatterInterface $formatter + * @return self + * @throws Exception\InvalidArgumentException + */ + public function setWriter($writer, array $options = null) + { + if (is_string($writer)) { + $writer = $this->writerPlugin($writer, $options); + } + + if (!$writer instanceof WriterInterface) { + throw new Exception\InvalidArgumentException(sprintf( + 'Formatter must implement %s\Formatter\FormatterInterface; received "%s"', + __NAMESPACE__, + is_object($writer) ? get_class($writer) : gettype($writer) + )); + } + + $this->writer = $writer; + return $this; + } + + /** + * Get writer plugin manager + * + * @return WriterPluginManager + */ + public function getWriterPluginManager() + { + if (null === $this->writerPlugins) { + $this->setWriterPluginManager(new WriterPluginManager()); + } + return $this->writerPlugins; + } + + /** + * Set writer plugin manager + * + * @param string|WriterPluginManager $plugins + * @return Logger + * @throws Exception\InvalidArgumentException + */ + public function setWriterPluginManager($plugins) + { + if (is_string($plugins)) { + $plugins = new $plugins; + } + if (!$plugins instanceof WriterPluginManager) { + throw new Exception\InvalidArgumentException(sprintf( + 'Writer plugin manager must extend %s\WriterPluginManager; received %s', + __NAMESPACE__, + is_object($plugins) ? get_class($plugins) : gettype($plugins) + )); + } + + $this->writerPlugins = $plugins; + return $this; + } + + /** + * Get writer instance + * + * @param string $name + * @param array|null $options + * @return Writer\WriterInterface + */ + public function writerPlugin($name, array $options = null) + { + return $this->getWriterPluginManager()->get($name, $options); + } + + /** + * Log a message to this writer. + * + * @param array $event log data event + * @return void + */ + public function write(array $event) + { + $this->doWrite($event); + } + + /** + * Check if buffered data should be flushed + * + * @param array $event event data + * @return boolean true if buffered data should be flushed + */ + protected function isActivated(array $event) + { + foreach ($this->filters as $filter) { + if (!$filter->filter($event)) { + return false; + } + } + return true; + } + + /** + * Write message to buffer or delegate event data to the wrapped writer + * + * @param array $event event data + * @return void + */ + protected function doWrite(array $event) + { + if (!$this->buffering) { + $this->writer->write($event); + return; + } + + $this->buffer[] = $event; + + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + + if (!$this->isActivated($event)) { + return; + } + + $this->buffering = false; + + foreach ($this->buffer as $bufferedEvent) { + $this->writer->write($bufferedEvent); + } + } + + /** + * Resets the state of the handler. + * Stops forwarding records to the wrapped writer + */ + public function reset() + { + $this->buffering = true; + } + + /** + * Stub in accordance to parent method signature. + * Fomatters must be set on the wrapped writer. + * + * @param string|Formatter\FormatterInterface $formatter + * @return WriterInterface + */ + public function setFormatter($formatter) + { + return $this->writer; + } + + /** + * Record shutdown + * + * @return void + */ + public function shutdown() + { + $this->writer->shutdown(); + $this->buffer = null; + } +} diff --git a/src/Writer/FirePhp.php b/src/Writer/FirePhp.php new file mode 100644 index 00000000..a080fa93 --- /dev/null +++ b/src/Writer/FirePhp.php @@ -0,0 +1,123 @@ +firephp = $instance; + $this->formatter = new FirePhpFormatter(); + } + + /** + * Write a message to the log. + * + * @param array $event event data + * @return void + */ + protected function doWrite(array $event) + { + $firephp = $this->getFirePhp(); + + if (!$firephp->getEnabled()) { + return; + } + + list($line, $label) = $this->formatter->format($event); + + switch ($event['priority']) { + case Logger::EMERG: + case Logger::ALERT: + case Logger::CRIT: + case Logger::ERR: + $firephp->error($line, $label); + break; + case Logger::WARN: + $firephp->warn($line, $label); + break; + case Logger::NOTICE: + case Logger::INFO: + $firephp->info($line, $label); + break; + case Logger::DEBUG: + $firephp->trace($line); + break; + default: + $firephp->log($line, $label); + break; + } + } + + /** + * Gets the FirePhpInterface instance that is used for logging. + * + * @return FirePhp\FirePhpInterface + * @throws Exception\RuntimeException + */ + public function getFirePhp() + { + if (!$this->firephp instanceof FirePhp\FirePhpInterface + && !class_exists('FirePHP') + ) { + // No FirePHP instance, and no way to create one + throw new Exception\RuntimeException('FirePHP Class not found'); + } + + // Remember: class names in strings are absolute; thus the class_exists + // here references the canonical name for the FirePHP class + if (!$this->firephp instanceof FirePhp\FirePhpInterface + && class_exists('FirePHP') + ) { + // FirePHPService is an alias for FirePHP; otherwise the class + // names would clash in this file on this line. + $this->setFirePhp(new FirePhp\FirePhpBridge(new FirePHPService())); + } + + return $this->firephp; + } + + /** + * Sets the FirePhpInterface instance that is used for logging. + * + * @param FirePhp\FirePhpInterface $instance A FirePhpInterface instance to set. + * @return FirePhp + */ + public function setFirePhp(FirePhp\FirePhpInterface $instance) + { + $this->firephp = $instance; + + return $this; + } +} diff --git a/src/Writer/FirePhp/FirePhpBridge.php b/src/Writer/FirePhp/FirePhpBridge.php new file mode 100644 index 00000000..806c81d3 --- /dev/null +++ b/src/Writer/FirePhp/FirePhpBridge.php @@ -0,0 +1,113 @@ +firephp = $firephp; + } + + /** + * Retrieve FirePHP instance + * + * @return FirePHP + */ + public function getFirePhp() + { + return $this->firephp; + } + + /** + * Determine whether or not FirePHP is enabled + * + * @return bool + */ + public function getEnabled() + { + return $this->firephp->getEnabled(); + } + + /** + * Log an error message + * + * @param string $line + * @return void + */ + public function error($line) + { + return $this->firephp->error($line); + } + + /** + * Log a warning + * + * @param string $line + * @return void + */ + public function warn($line) + { + return $this->firephp->warn($line); + } + + /** + * Log informational message + * + * @param string $line + * @return void + */ + public function info($line) + { + return $this->firephp->info($line); + } + + /** + * Log a trace + * + * @param string $line + * @return void + */ + public function trace($line) + { + return $this->firephp->trace($line); + } + + /** + * Log a message + * + * @param string $line + * @return void + */ + public function log($line) + { + return $this->firephp->trace($line); + } +} diff --git a/src/Writer/FirePhp/FirePhpInterface.php b/src/Writer/FirePhp/FirePhpInterface.php new file mode 100644 index 00000000..23cf4b9d --- /dev/null +++ b/src/Writer/FirePhp/FirePhpInterface.php @@ -0,0 +1,61 @@ + 'Zend\Log\Formatter\Base', + 'simple' => 'Zend\Log\Formatter\Simple', + 'xml' => 'Zend\Log\Formatter\Xml', + 'db' => 'Zend\Log\Formatter\Db', + 'errorhandler' => 'Zend\Log\Formatter\ErrorHandler', + 'exceptionhandler' => 'Zend\Log\Formatter\ExceptionHandler', + ); + + /** + * Allow many filters of the same type + * + * @var bool + */ + protected $shareByDefault = false; + + /** + * Validate the plugin + * + * Checks that the formatter loaded is an instance of Formatter\FormatterInterface. + * + * @param mixed $plugin + * @return void + * @throws Exception\InvalidArgumentException if invalid + */ + public function validatePlugin($plugin) + { + if ($plugin instanceof Formatter\FormatterInterface) { + // we're okay + return; + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Plugin of type %s is invalid; must implement %s\Formatter\FormatterInterface', + (is_object($plugin) ? get_class($plugin) : gettype($plugin)), + __NAMESPACE__ + )); + } +} diff --git a/src/Writer/Mail.php b/src/Writer/Mail.php index a50cc54f..becc6184 100644 --- a/src/Writer/Mail.php +++ b/src/Writer/Mail.php @@ -1,28 +1,18 @@ mail = $mail; - if (null !== $transport) { - $this->setTransport($transport); - } else { - // default transport - $this->setTransport(new Transport\Sendmail()); + + // Ensure we have a valid mail transport + if (null === $transport) { + $transport = new Transport\Sendmail(); } + if (!$transport instanceof Transport\TransportInterface) { + throw new Exception\InvalidArgumentException(sprintf( + 'Transport parameter of type %s is invalid; must be of type Zend\Mail\Transport\TransportInterface', + (is_object($transport) ? get_class($transport) : gettype($transport)) + )); + } + $this->setTransport($transport); + $this->formatter = new SimpleFormatter(); } @@ -170,7 +181,7 @@ public function shutdown() } // Always provide events to mail as plaintext. - $this->mail->setBody(implode('', $this->eventsToMail)); + $this->mail->setBody(implode(PHP_EOL, $this->eventsToMail)); // Finally, send the mail. If an exception occurs, convert it into a // warning-level message so we can avoid an exception thrown without a diff --git a/src/Writer/Mock.php b/src/Writer/Mock.php index 17c441a2..995c38a4 100644 --- a/src/Writer/Mock.php +++ b/src/Writer/Mock.php @@ -1,22 +1,11 @@ mongoCollection = $mongo->selectCollection($database, $collection); + $this->saveOptions = $saveOptions; + } + + /** + * This writer does not support formatting. + * + * @param string|Zend\Log\Formatter\FormatterInterface $formatter + * @return WriterInterface + */ + public function setFormatter($formatter) + { + return $this; + } + + /** + * Write a message to the log. + * + * @param array $event Event data + * @return void + * @throws Zend\Log\Exception\RuntimeException + */ + protected function doWrite(array $event) + { + if (null === $this->mongoCollection) { + throw new RuntimeException('MongoCollection must be defined'); + } + + if (isset($event['timestamp']) && $event['timestamp'] instanceof DateTime) { + $event['timestamp'] = new MongoDate($event['timestamp']->getTimestamp()); + } + + $this->mongoCollection->save($event, $this->saveOptions); + } +} diff --git a/src/Writer/Null.php b/src/Writer/Null.php index dba17aeb..d6f53202 100644 --- a/src/Writer/Null.php +++ b/src/Writer/Null.php @@ -1,22 +1,11 @@ stream = $streamOrUrl; } else { - if (is_array($streamOrUrl) && isset($streamOrUrl['stream'])) { - $streamOrUrl = $streamOrUrl['stream']; - } - - if (! $this->stream = @fopen($streamOrUrl, $mode, false)) { + ErrorHandler::start(); + $this->stream = fopen($streamOrUrl, $mode, false); + $error = ErrorHandler::stop(); + if (!$this->stream) { throw new Exception\RuntimeException(sprintf( '"%s" cannot be opened with mode "%s"', $streamOrUrl, $mode - )); + ), 0, $error); } } + if (null !== $logSeparator) { + $this->setLogSeparator($logSeparator); + } + $this->formatter = new SimpleFormatter(); } @@ -98,11 +108,30 @@ public function __construct($streamOrUrl, $mode = null) */ protected function doWrite(array $event) { - $line = $this->formatter->format($event); + $line = $this->formatter->format($event) . $this->logSeparator; + fwrite($this->stream, $line); + } - if (false === @fwrite($this->stream, $line)) { - throw new Exception\RuntimeException("Unable to write to stream"); - } + /** + * Set log separator string + * + * @param string $logSeparator + * @return Stream + */ + public function setLogSeparator($logSeparator) + { + $this->logSeparator = (string) $logSeparator; + return $this; + } + + /** + * Get log separator string + * + * @return string + */ + public function getLogSeparator() + { + return $this->logSeparator; } /** diff --git a/src/Writer/Syslog.php b/src/Writer/Syslog.php index dca550ca..469a5cf8 100644 --- a/src/Writer/Syslog.php +++ b/src/Writer/Syslog.php @@ -1,29 +1,18 @@ application = $params['application']; + $this->appName = $params['application']; } $runInitializeSyslog = true; @@ -115,6 +102,8 @@ public function __construct(array $params = array()) if ($runInitializeSyslog) { $this->initializeSyslog(); } + + $this->setFormatter(new SimpleFormatter('%message%')); } /** @@ -160,8 +149,8 @@ protected function initializeValidFacilities() */ protected function initializeSyslog() { - self::$lastApplication = $this->appName; - self::$lastFacility = $this->facility; + static::$lastApplication = $this->appName; + static::$lastFacility = $this->facility; openlog($this->appName, LOG_PID, $this->facility); } @@ -184,7 +173,7 @@ public function setFacility($facility) if (!in_array($facility, $this->validFacilities)) { throw new Exception\InvalidArgumentException( - 'Invalid log facility provided; please see http://php.net/openlog for a list of valid facility values' + 'Invalid log facility provided; please see http://php.net/openlog for a list of valid facility values' ); } @@ -242,17 +231,13 @@ protected function doWrite(array $event) $priority = $this->defaultPriority; } - if ($this->appName !== self::$lastApplication - || $this->facility !== self::$lastFacility + if ($this->appName !== static::$lastApplication + || $this->facility !== static::$lastFacility ) { $this->initializeSyslog(); } - if ($this->formatter instanceof Formatter) { - $message = $this->formatter->format($event); - } else { - $message = $event['message']; - } + $message = $this->formatter->format($event); syslog($priority, $message); } diff --git a/src/Writer/WriterInterface.php b/src/Writer/WriterInterface.php index 83ab5a50..e625f68c 100644 --- a/src/Writer/WriterInterface.php +++ b/src/Writer/WriterInterface.php @@ -1,40 +1,28 @@ 'Zend\Log\Writer\Db', - 'firebug' => 'Zend\Log\Writer\Firebug', - 'mail' => 'Zend\Log\Writer\Mail', - 'mock' => 'Zend\Log\Writer\Mock', - 'null' => 'Zend\Log\Writer\Null', - 'stream' => 'Zend\Log\Writer\Stream', - 'syslog' => 'Zend\Log\Writer\Syslog', - 'zendmonitor' => 'Zend\Log\Writer\ZendMonitor', + 'db' => 'Zend\Log\Writer\Db', + 'fingerscrossed' => 'Zend\Log\Writer\FingersCrossed', + 'firephp' => 'Zend\Log\Writer\FirePhp', + 'mail' => 'Zend\Log\Writer\Mail', + 'mock' => 'Zend\Log\Writer\Mock', + 'null' => 'Zend\Log\Writer\Null', + 'stream' => 'Zend\Log\Writer\Stream', + 'syslog' => 'Zend\Log\Writer\Syslog', + 'zendmonitor' => 'Zend\Log\Writer\ZendMonitor', ); + /** + * Allow many writers of the same type + * + * @var bool + */ + protected $shareByDefault = false; + /** * Validate the plugin * * Checks that the writer loaded is an instance of Writer\WriterInterface. - * - * @param mixed $plugin + * + * @param mixed $plugin * @return void * @throws Exception\InvalidArgumentException if invalid */ diff --git a/test/Filter/ChainingTest.php b/test/Filter/ChainingTest.php deleted file mode 100644 index b55c2116..00000000 --- a/test/Filter/ChainingTest.php +++ /dev/null @@ -1,90 +0,0 @@ -log = fopen('php://memory', 'w'); - $this->logger = new Logger(); - $this->logger->addWriter(new Writer\Stream($this->log)); - } - - public function tearDown() - { - fclose($this->log); - } - - /** - * @group disable - */ - public function testFilterAllWriters() - { - // filter out anything above a WARNing for all writers - $this->logger->addFilter(Logger::WARN); - - $this->logger->info($ignored = 'info-message-ignored'); - $this->logger->warn($logged = 'warn-message-logged'); - - rewind($this->log); - $logdata = stream_get_contents($this->log); - - $this->assertNotContains($ignored, $logdata); - $this->assertContains($logged, $logdata); - } - - public function testFilterOnSpecificWriter() - { - $log2 = fopen('php://memory', 'w'); - $writer2 = new Writer\Stream($log2); - $writer2->addFilter(Logger::ERR); - - $this->logger->addWriter($writer2); - - $this->logger->warn($warn = 'warn-message'); - $this->logger->err($err = 'err-message'); - - rewind($this->log); - $logdata = stream_get_contents($this->log); - - $this->assertContains($warn, $logdata); - $this->assertContains($err, $logdata); - - rewind($log2); - $logdata = stream_get_contents($log2); - - $this->assertContains($err, $logdata); - $this->assertNotContains($warn, $logdata); - } -} diff --git a/test/Filter/MockTest.php b/test/Filter/MockTest.php new file mode 100644 index 00000000..451f8130 --- /dev/null +++ b/test/Filter/MockTest.php @@ -0,0 +1,32 @@ +assertSame(array(), $filter->events); + + $fields = array('foo' => 'bar'); + $this->assertTrue($filter->filter($fields)); + $this->assertSame(array($fields), $filter->events); + } +} diff --git a/test/Filter/PriorityTest.php b/test/Filter/PriorityTest.php index 65f97d14..27bd074f 100644 --- a/test/Filter/PriorityTest.php +++ b/test/Filter/PriorityTest.php @@ -1,35 +1,21 @@ assertFalse($filter->filter(array('message' => 'test123'))); $this->assertFalse($filter->filter(array('message' => '(%$'))); } - + public function testValidatorChain() { $validatorChain = new ValidatorChain(); diff --git a/test/Formatter/BaseTest.php b/test/Formatter/BaseTest.php new file mode 100644 index 00000000..a5e1396b --- /dev/null +++ b/test/Formatter/BaseTest.php @@ -0,0 +1,124 @@ +assertEquals(BaseFormatter::DEFAULT_DATETIME_FORMAT, $formatter->getDateTimeFormat()); + } + + /** + * @dataProvider provideDateTimeFormats + */ + public function testAllowsSpecifyingDateTimeFormatAsConstructorArgument($dateTimeFormat) + { + $formatter = new BaseFormatter($dateTimeFormat); + + $this->assertEquals($dateTimeFormat, $formatter->getDateTimeFormat()); + } + + /** + * @return array + */ + public function provideDateTimeFormats() + { + return array( + array('r'), + array('U'), + array(DateTime::RSS), + ); + } + + /** + * @dataProvider provideDateTimeFormats + */ + public function testSetDateTimeFormat($dateTimeFormat) + { + $formatter = new BaseFormatter(); + $formatter->setDateTimeFormat($dateTimeFormat); + + $this->assertEquals($dateTimeFormat, $formatter->getDateTimeFormat()); + } + + /** + * @dataProvider provideDateTimeFormats + */ + public function testSetDateTimeFormatInConstructor($dateTimeFormat) + { + $options = array('dateTimeFormat' => $dateTimeFormat); + $formatter = new BaseFormatter($options); + + $this->assertEquals($dateTimeFormat, $formatter->getDateTimeFormat()); + } + + public function testFormatAllTypes() + { + $datetime = new DateTime(); + $object = new stdClass(); + $object->foo = 'bar'; + $formatter = new BaseFormatter(); + + $event = array( + 'timestamp' => $datetime, + 'priority' => 1, + 'message' => 'tottakai', + 'extra' => array( + 'float' => 0.2, + 'boolean' => false, + 'array_empty' => array(), + 'array' => range(0, 4), + 'traversable_empty' => new EmptyIterator(), + 'traversable' => new ArrayIterator(array('id', 42)), + 'null' => null, + 'object_empty' => new stdClass(), + 'object' => $object, + 'string object' => new StringObject(), + 'resource' => fopen('php://stdout', 'w'), + ), + ); + $outputExpected = array( + 'timestamp' => $datetime->format($formatter->getDateTimeFormat()), + 'priority' => 1, + 'message' => 'tottakai', + 'extra' => array( + 'boolean' => false, + 'float' => 0.2, + 'array_empty' => '[]', + 'array' => '[0,1,2,3,4]', + 'traversable_empty' => '[]', + 'traversable' => '["id",42]', + 'null' => null, + 'object_empty' => 'object(stdClass) {}', + 'object' => 'object(stdClass) {"foo":"bar"}', + 'string object' => 'Hello World', + 'resource' => 'resource(stream)', + ), + ); + + $this->assertEquals($outputExpected, $formatter->format($event)); + } +} diff --git a/test/Formatter/DbTest.php b/test/Formatter/DbTest.php new file mode 100644 index 00000000..847d8eb4 --- /dev/null +++ b/test/Formatter/DbTest.php @@ -0,0 +1,72 @@ +assertEquals(DbFormatter::DEFAULT_DATETIME_FORMAT, $formatter->getDateTimeFormat()); + } + + /** + * @dataProvider provideDateTimeFormats + */ + public function testSetDateTimeFormat($dateTimeFormat) + { + $formatter = new DbFormatter(); + $formatter->setDateTimeFormat($dateTimeFormat); + + $this->assertEquals($dateTimeFormat, $formatter->getDateTimeFormat()); + } + + /** + * @return array + */ + public function provideDateTimeFormats() + { + return array( + array('r'), + array('U'), + array(DateTime::RSS), + ); + } + + /** + * @dataProvider provideDateTimeFormats + */ + public function testAllowsSpecifyingDateTimeFormatAsConstructorArgument($dateTimeFormat) + { + $formatter = new DbFormatter($dateTimeFormat); + + $this->assertEquals($dateTimeFormat, $formatter->getDateTimeFormat()); + } + + public function testFormatDateTimeInEvent() + { + $datetime = new DateTime(); + $event = array('timestamp' => $datetime); + $formatter = new DbFormatter(); + + $format = DbFormatter::DEFAULT_DATETIME_FORMAT; + $this->assertContains($datetime->format($format), $formatter->format($event)); + } +} diff --git a/test/Formatter/ErrorHandlerTest.php b/test/Formatter/ErrorHandlerTest.php index e797ef99..fa8eff84 100644 --- a/test/Formatter/ErrorHandlerTest.php +++ b/test/Formatter/ErrorHandlerTest.php @@ -1,41 +1,32 @@ $date, 'message' => 'test', @@ -44,12 +35,53 @@ public function testFormat() 'extra' => array ( 'errno' => 1, 'file' => 'test.php', - 'line' => 1 + 'line' => 1, + 'context' => array('object' => new DateTime(), 'string' => 'test') ) ); $formatter = new ErrorHandler(); $output = $formatter->format($event); - $this->assertEquals($date . ' CRIT (1) test (errno 1) in test.php on line 1', $output); + $this->assertEquals($date->format('c') . ' CRIT (1) test (errno 1) in test.php on line 1', $output); } + + public function testSetDateTimeFormat() + { + $formatter = new ErrorHandler(); + + $this->assertEquals('c', $formatter->getDateTimeFormat()); + $this->assertSame($formatter, $formatter->setDateTimeFormat('r')); + $this->assertEquals('r', $formatter->getDateTimeFormat()); + } + + public function testComplexEvent () + { + $date = new DateTime(); + $stringObject = new StringObject(); + $event = array( + 'timestamp' => $date, + 'message' => 'test', + 'priority' => 1, + 'priorityName' => 'CRIT', + 'extra' => array( + 'errno' => 1, + 'file' => 'test.php', + 'line' => 1, + 'context' => array( + 'object1' => new StringObject(), + 'object2' => new NotStringObject(), + 'string' => 'test1', + 'array' => array( + 'key' => 'test2' + ) + ) + ) + ); + $formatString = '%extra[context][object1]% %extra[context][object2]% %extra[context][string]% %extra[context][array]% %extra[context][array][key]%'; + $formatter = new ErrorHandler($formatString); + $output = $formatter->format($event); + $this->assertEquals($stringObject->__toString() .' %extra[context][object2]% test1 %extra[context][array]% test2', $output); + } + + } diff --git a/test/Formatter/ExceptionHandlerTest.php b/test/Formatter/ExceptionHandlerTest.php index 903923aa..20cae5bc 100644 --- a/test/Formatter/ExceptionHandlerTest.php +++ b/test/Formatter/ExceptionHandlerTest.php @@ -1,46 +1,36 @@ '2012-06-12T09:00:00+02:00', + 'timestamp' => $date, 'message' => 'test', 'priority' => 1, 'priorityName' => 'CRIT', - 'extra' => array ( + 'extra' => array( 'file' => 'test.php', 'line' => 1, 'trace' => array( @@ -63,35 +53,68 @@ public function testFormat() ) ) ); - $expected = << 1 -) - -File : test.php -Line : 2 -Func : test -Class : Test -Type : static -Args : Array -( - [0] => 1 -) - -EOF; + // The formatter ends with unix style line endings so make sure we expect that + // output as well: + $expected = $date->format('c') . " CRIT (1) test in test.php on line 1\n"; + $expected .= "[Trace]\n"; + $expected .= "File : test.php\n"; + $expected .= "Line : 1\n"; + $expected .= "Func : test\n"; + $expected .= "Class : Test\n"; + $expected .= "Type : static\n"; + $expected .= "Args : Array\n"; + $expected .= "(\n"; + $expected .= " [0] => 1\n"; + $expected .= ")\n\n"; + $expected .= "File : test.php\n"; + $expected .= "Line : 2\n"; + $expected .= "Func : test\n"; + $expected .= "Class : Test\n"; + $expected .= "Type : static\n"; + $expected .= "Args : Array\n"; + $expected .= "(\n"; + $expected .= " [0] => 1\n"; + $expected .= ")\n\n"; $formatter = new ExceptionHandler(); $output = $formatter->format($event); $this->assertEquals($expected, $output); } + + /** + * @dataProvider provideDateTimeFormats + */ + public function testSetDateTimeFormat($dateTimeFormat) + { + $date = new DateTime(); + + $event = array( + 'timestamp' => $date, + 'message' => 'test', + 'priority' => 1, + 'priorityName' => 'CRIT', + 'extra' => array( + 'file' => 'test.php', + 'line' => 1, + ), + ); + + $expected = $date->format($dateTimeFormat) . ' CRIT (1) test in test.php on line 1'; + + $formatter = new ExceptionHandler(); + + $this->assertSame($formatter, $formatter->setDateTimeFormat($dateTimeFormat)); + $this->assertEquals($dateTimeFormat, $formatter->getDateTimeFormat()); + $this->assertEquals($expected, $formatter->format($event)); + } + + public function provideDateTimeFormats() + { + return array( + array('r'), + array('U'), + ); + } } diff --git a/test/Formatter/FirePhpTest.php b/test/Formatter/FirePhpTest.php new file mode 100644 index 00000000..24165d31 --- /dev/null +++ b/test/Formatter/FirePhpTest.php @@ -0,0 +1,79 @@ + 'foo', + 'extra' => new \stdClass() ); + + $f = new FirePhp(); + list($line, $label) = $f->format($fields); + + $this->assertContains($fields['message'], $label); + $this->assertEquals($fields['extra'], $line); + } + + public function testFormatWithoutExtra() + { + $fields = array( 'message' => 'foo' ); + + $f = new FirePhp(); + list($line, $label) = $f->format($fields); + + $this->assertContains($fields['message'], $line); + $this->assertNull($label); + } + + public function testFormatWithEmptyExtra() + { + $fields = array( 'message' => 'foo', + 'extra' => array() ); + + $f = new FirePhp(); + list($line, $label) = $f->format($fields); + + $this->assertContains($fields['message'], $line); + $this->assertNull($label); + } + + public function testSetDateTimeFormatDoesNothing() + { + $formatter = new FirePhp(); + + $this->assertEquals('', $formatter->getDateTimeFormat()); + $this->assertSame($formatter, $formatter->setDateTimeFormat('r')); + $this->assertEquals('', $formatter->getDateTimeFormat()); + } +} diff --git a/test/Formatter/SimpleTest.php b/test/Formatter/SimpleTest.php index 954c71d6..d3bcc95e 100644 --- a/test/Formatter/SimpleTest.php +++ b/test/Formatter/SimpleTest.php @@ -1,35 +1,23 @@ 0, - 'message' => 'foo', - 'priority' => 42, - 'priorityName' => 'bar'); - - $f = new Simple(); - $line = $f->format($fields); - - $this->assertContains((string)$fields['timestamp'], $line); - $this->assertContains($fields['message'], $line); - $this->assertContains($fields['priorityName'], $line); - $this->assertContains((string)$fields['priority'], $line); + $options = array('dateTimeFormat' => $dateTimeFormat, 'format' => '%timestamp%'); + $formatter = new Simple($options); + + $this->assertEquals($dateTimeFormat, $formatter->getDateTimeFormat()); + $this->assertAttributeEquals('%timestamp%', 'format', $formatter); } - function testComplexValues() + public function testDefaultFormat() { - $fields = array('timestamp' => 0, - 'priority' => 42, - 'priorityName' => 'bar'); - - $f = new Simple(); - - $fields['message'] = 'Foo'; - $line = $f->format($fields); - $this->assertContains($fields['message'], $line); - - $fields['message'] = 10; - $line = $f->format($fields); - $this->assertContains($fields['message'], $line); + $date = new DateTime('2012-08-28T18:15:00Z'); + $fields = array( + 'timestamp' => $date, + 'message' => 'foo', + 'priority' => 42, + 'priorityName' => 'bar', + 'extra' => array() + ); + + $outputExpected = '2012-08-28T18:15:00+00:00 bar (42): foo'; + $formatter = new Simple(); - $fields['message'] = 10.5; - $line = $f->format($fields); - $this->assertContains($fields['message'], $line); + $this->assertEquals($outputExpected, $formatter->format($fields)); + } - $fields['message'] = true; - $line = $f->format($fields); - $this->assertContains('1', $line); + /** + * @dataProvider provideDateTimeFormats + */ + public function testCustomDateTimeFormat($dateTimeFormat) + { + $date = new DateTime(); + $event = array('timestamp' => $date); + $formatter = new Simple('%timestamp%', $dateTimeFormat); - $fields['message'] = fopen('php://stdout', 'w'); - $line = $f->format($fields); - $this->assertContains('Resource id ', $line); - fclose($fields['message']); + $this->assertEquals($date->format($dateTimeFormat), $formatter->format($event)); + } - $fields['message'] = range(1,10); - $line = $f->format($fields); - $this->assertContains('array', $line); + /** + * @dataProvider provideDateTimeFormats + */ + public function testSetDateTimeFormat($dateTimeFormat) + { + $date = new DateTime(); + $event = array('timestamp' => $date); + $formatter = new Simple('%timestamp%'); - $fields['message'] = new StringObject(); - $line = $f->format($fields); - $this->assertContains($fields['message']->__toString(), $line); + $this->assertSame($formatter, $formatter->setDateTimeFormat($dateTimeFormat)); + $this->assertEquals($dateTimeFormat, $formatter->getDateTimeFormat()); + $this->assertEquals($date->format($dateTimeFormat), $formatter->format($event)); + } - $fields['message'] = new \stdClass(); - $line = $f->format($fields); - $this->assertContains('object', $line); + public function provideDateTimeFormats() + { + return array( + array('r'), + array('U'), + ); } - + /** * @group ZF-10427 */ public function testDefaultFormatShouldDisplayExtraInformations() { - $message = 'custom message'; - $exception = new \RuntimeException($message); - $event = array( - 'timestamp' => date('c'), - 'message' => 'Application error', - 'priority' => 2, - 'priorityName' => 'CRIT', - 'info' => $exception, - ); + $message = 'custom message'; + $exception = new RuntimeException($message); + $event = array( + 'timestamp' => new DateTime(), + 'message' => 'Application error', + 'priority' => 2, + 'priorityName' => 'CRIT', + 'extra' => array($exception), + ); $formatter = new Simple(); $output = $formatter->format($event); $this->assertContains($message, $output); } + + public function testAllowsSpecifyingFormatAsConstructorArgument() + { + $format = '[%timestamp%] %message%'; + $formatter = new Simple($format); + $this->assertEquals($format, $formatter->format(array())); + } } diff --git a/test/Formatter/XmlTest.php b/test/Formatter/XmlTest.php index a095d475..660aeaee 100644 --- a/test/Formatter/XmlTest.php +++ b/test/Formatter/XmlTest.php @@ -1,26 +1,16 @@ format(array('message' => 'foo', 'priority' => 42)); + $line = $f->format(array('timestamp' => $date, 'message' => 'foo', 'priority' => 42)); + $this->assertContains($date->format('c'), $line); $this->assertContains('foo', $line); $this->assertContains((string)42, $line); } @@ -50,6 +40,36 @@ public function testConfiguringElementMapping() $this->assertContains('baz', $line); } + /** + * @dataProvider provideDateTimeFormats + */ + public function testConfiguringDateTimeFormat($dateTimeFormat) + { + $date = new DateTime(); + $f = new XmlFormatter('log', null, 'UTF-8', $dateTimeFormat); + $this->assertContains($date->format($dateTimeFormat), $f->format(array('timestamp' => $date))); + } + + /** + * @dataProvider provideDateTimeFormats + */ + public function testSetDateTimeFormat($dateTimeFormat) + { + $date = new DateTime(); + $f = new XmlFormatter(); + $this->assertSame($f, $f->setDateTimeFormat($dateTimeFormat)); + $this->assertContains($dateTimeFormat, $f->getDateTimeFormat()); + $this->assertContains($date->format($dateTimeFormat), $f->format(array('timestamp' => $date))); + } + + public function provideDateTimeFormats() + { + return array( + array('r'), + array('U'), + ); + } + public function testXmlDeclarationIsStripped() { $f = new XmlFormatter(); @@ -94,18 +114,22 @@ public function testFixingBrokenCharsSoXmlIsValid() public function testConstructorWithArray() { + $date = new DateTime(); $options = array( 'rootElement' => 'log', 'elementMap' => array( + 'date' => 'timestamp', 'word' => 'message', 'priority' => 'priority' - ) + ), + 'dateTimeFormat' => 'r', ); $event = array( + 'timestamp' => $date, 'message' => 'tottakai', 'priority' => 4 ); - $expected = 'tottakai4'; + $expected = sprintf('%stottakai4', $date->format('r')); $formatter = new XmlFormatter($options); $output = $formatter->format($event); @@ -154,4 +178,23 @@ public function testObjectsWithStringSerializationAreIncludedInFormattedString() $output = $formatter->format($event); $this->assertContains($expected, $output); } + + /** + * @group ZF2-453 + */ + public function testFormatWillRemoveExtraEmptyArrayFromEvent() + { + $formatter = new XmlFormatter; + $d = new DateTime('2001-01-01T12:00:00-06:00'); + $event = array( + 'timestamp' => $d, + 'message' => 'test', + 'priority' => 1, + 'priorityName' => 'CRIT', + 'extra' => array() + ); + $expected = '2001-01-01T12:00:00-06:00test1CRIT'; + $expected .= PHP_EOL . PHP_EOL; + $this->assertEquals($expected, $formatter->format($event)); + } } diff --git a/test/LoggerAwareTraitTest.php b/test/LoggerAwareTraitTest.php new file mode 100644 index 00000000..55e9ad78 --- /dev/null +++ b/test/LoggerAwareTraitTest.php @@ -0,0 +1,33 @@ +getObjectForTrait('\Zend\Log\LoggerAwareTrait'); + + $this->assertAttributeEquals(null, 'logger', $object); + + $logger = new Logger; + + $object->setLogger($logger); + + $this->assertAttributeEquals($logger, 'logger', $object); + } +} diff --git a/test/LoggerTest.php b/test/LoggerTest.php index 64423d33..8b42173a 100644 --- a/test/LoggerTest.php +++ b/test/LoggerTest.php @@ -1,36 +1,27 @@ logger = new Logger; } - public function testUsesDateFormatIso8601ByDefault() - { - $this->assertEquals('c', $this->logger->getDateTimeFormat()); - } - - public function testPassingStringToSetDateTimeFormat() - { - $this->logger->setDateTimeFormat('U'); - $this->assertEquals('U', $this->logger->getDateTimeFormat()); - } - public function testUsesWriterPluginManagerByDefault() { - $this->assertInstanceOf('Zend\Log\WriterPluginManager', $this->logger->getPluginManager()); + $this->assertInstanceOf('Zend\Log\WriterPluginManager', $this->logger->getWriterPluginManager()); } public function testPassingValidStringClassToSetPluginManager() { - $this->logger->setPluginManager('Zend\Log\WriterPluginManager'); - $this->assertInstanceOf('Zend\Log\WriterPluginManager', $this->logger->getPluginManager()); + $this->logger->setWriterPluginManager('Zend\Log\WriterPluginManager'); + $this->assertInstanceOf('Zend\Log\WriterPluginManager', $this->logger->getWriterPluginManager()); } public static function provideInvalidClasses() @@ -76,12 +56,12 @@ public static function provideInvalidClasses() public function testPassingInvalidArgumentToSetPluginManagerRaisesException($plugins) { $this->setExpectedException('Zend\Log\Exception\InvalidArgumentException'); - $this->logger->setPluginManager($plugins); + $this->logger->setWriterPluginManager($plugins); } public function testPassingShortNameToPluginReturnsWriterByThatName() { - $writer = $this->logger->plugin('mock'); + $writer = $this->logger->writerPlugin('mock'); $this->assertInstanceOf('Zend\Log\Writer\Mock', $writer); } @@ -91,7 +71,7 @@ public function testPassWriterAsString() $writers = $this->logger->getWriters(); $this->assertInstanceOf('Zend\Stdlib\SplPriorityQueue', $writers); } - + /** * @dataProvider provideInvalidClasses */ @@ -106,16 +86,16 @@ public function testEmptyWriter() $this->setExpectedException('Zend\Log\Exception\RuntimeException', 'No log writer specified'); $this->logger->log(Logger::INFO, 'test'); } - + public function testSetWriters() { - $writer1 = $this->logger->plugin('mock'); - $writer2 = $this->logger->plugin('null'); + $writer1 = $this->logger->writerPlugin('mock'); + $writer2 = $this->logger->writerPlugin('null'); $writers = new SplPriorityQueue(); $writers->insert($writer1, 1); $writers->insert($writer2, 2); $this->logger->setWriters($writers); - + $writers = $this->logger->getWriters(); $this->assertInstanceOf('Zend\Stdlib\SplPriorityQueue', $writers); $writer = $writers->extract(); @@ -123,12 +103,12 @@ public function testSetWriters() $writer = $writers->extract(); $this->assertTrue($writer instanceof \Zend\Log\Writer\Mock); } - + public function testAddWriterWithPriority() { - $writer1 = $this->logger->plugin('mock'); + $writer1 = $this->logger->writerPlugin('mock'); $this->logger->addWriter($writer1,1); - $writer2 = $this->logger->plugin('null'); + $writer2 = $this->logger->writerPlugin('null'); $this->logger->addWriter($writer2,2); $writers = $this->logger->getWriters(); @@ -137,14 +117,14 @@ public function testAddWriterWithPriority() $this->assertTrue($writer instanceof \Zend\Log\Writer\Null); $writer = $writers->extract(); $this->assertTrue($writer instanceof \Zend\Log\Writer\Mock); - + } - + public function testAddWithSamePriority() { - $writer1 = $this->logger->plugin('mock'); + $writer1 = $this->logger->writerPlugin('mock'); $this->logger->addWriter($writer1,1); - $writer2 = $this->logger->plugin('null'); + $writer2 = $this->logger->writerPlugin('null'); $this->logger->addWriter($writer2,1); $writers = $this->logger->getWriters(); @@ -154,17 +134,17 @@ public function testAddWithSamePriority() $writer = $writers->extract(); $this->assertTrue($writer instanceof \Zend\Log\Writer\Null); } - + public function testLogging() { $writer = new MockWriter; $this->logger->addWriter($writer); $this->logger->log(Logger::INFO, 'tottakai'); - + $this->assertEquals(count($writer->events), 1); $this->assertContains('tottakai', $writer->events[0]['message']); } - + public function testLoggingArray() { $writer = new MockWriter; @@ -174,7 +154,56 @@ public function testLoggingArray() $this->assertEquals(count($writer->events), 1); $this->assertContains('test', $writer->events[0]['message']); } - + + public function testAddFilter() + { + $writer = new MockWriter; + $filter = new MockFilter; + $writer->addFilter($filter); + $this->logger->addWriter($writer); + $this->logger->log(Logger::INFO, array('test')); + + $this->assertEquals(count($filter->events), 1); + $this->assertContains('test', $filter->events[0]['message']); + } + + public function testAddFilterByName() + { + $writer = new MockWriter; + $writer->addFilter('mock'); + $this->logger->addWriter($writer); + $this->logger->log(Logger::INFO, array('test')); + + $this->assertEquals(count($writer->events), 1); + $this->assertContains('test', $writer->events[0]['message']); + } + + /** + * provideTestFilters + */ + public function provideTestFilters() + { + return array( + array('priority', array('priority' => Logger::INFO)), + array('regex', array( 'regex' => '/[0-9]+/' )), + array('validator', array('validator' => new DigitsFilter)), + ); + } + + /** + * @dataProvider provideTestFilters + */ + public function testAddFilterByNameWithParams($filter, $options) + { + $writer = new MockWriter; + $writer->addFilter($filter, $options); + $this->logger->addWriter($writer); + + $this->logger->log(Logger::INFO, '123'); + $this->assertEquals(count($writer->events), 1); + $this->assertContains('123', $writer->events[0]['message']); + } + public static function provideAttributes() { return array( @@ -218,7 +247,7 @@ public function testPassingInvalidArgumentToLogRaisesException($message, $extra) $this->setExpectedException('Zend\Log\Exception\InvalidArgumentException'); $this->logger->log(Logger::ERR, $message, $extra); } - + public function testRegisterErrorHandler() { $writer = new MockWriter; @@ -232,4 +261,69 @@ public function testRegisterErrorHandler() Logger::unregisterErrorHandler(); $this->assertEquals($writer->events[0]['message'], 'Undefined variable: test'); } + + public function testOptionsWithMock() + { + $options = array('writers' => array( + 'first_writer' => array( + 'name' => 'mock', + ) + )); + $logger = new Logger($options); + + $writers = $logger->getWriters()->toArray(); + $this->assertCount(1, $writers); + $this->assertInstanceOf('Zend\Log\Writer\Mock', $writers[0]); + } + + public function testOptionsWithWriterOptions() + { + $options = array('writers' => array( + array( + 'name' => 'stream', + 'options' => array( + 'stream' => 'php://output', + 'log_separator' => 'foo' + ), + ) + )); + $logger = new Logger($options); + + $writers = $logger->getWriters()->toArray(); + $this->assertCount(1, $writers); + $this->assertInstanceOf('Zend\Log\Writer\Stream', $writers[0]); + $this->assertEquals('foo', $writers[0]->getLogSeparator()); + } + + public function testAddProcessor() + { + $processor = new Backtrace(); + $this->logger->addProcessor($processor); + + $processors = $this->logger->getProcessors()->toArray(); + $this->assertEquals($processor, $processors[0]); + } + + public function testAddProcessorByName() + { + $this->logger->addProcessor('backtrace'); + + $processors = $this->logger->getProcessors()->toArray(); + $this->assertInstanceOf('Zend\Log\Processor\Backtrace', $processors[0]); + + $writer = new MockWriter; + $this->logger->addWriter($writer); + $this->logger->log(Logger::ERR, 'foo'); + } + + public function testProcessorOutputAdded() + { + $processor = new Backtrace(); + $this->logger->addProcessor($processor); + $writer = new MockWriter; + $this->logger->addWriter($writer); + + $this->logger->log(Logger::ERR, 'foo'); + $this->assertEquals(__FILE__, $writer->events[0]['extra']['file']); + } } diff --git a/test/Processor/BacktraceTest.php b/test/Processor/BacktraceTest.php new file mode 100644 index 00000000..5fbc9bae --- /dev/null +++ b/test/Processor/BacktraceTest.php @@ -0,0 +1,43 @@ + '', + 'priority' => 1, + 'priorityName' => 'ALERT', + 'message' => 'foo', + 'extra' => array() + ); + + $event = $processor->process($event); + + $this->assertArrayHasKey('file', $event['extra']); + $this->assertArrayHasKey('line', $event['extra']); + $this->assertArrayHasKey('class', $event['extra']); + $this->assertArrayHasKey('function', $event['extra']); + } +} diff --git a/test/Processor/RequestIdTest.php b/test/Processor/RequestIdTest.php new file mode 100644 index 00000000..4b0c8627 --- /dev/null +++ b/test/Processor/RequestIdTest.php @@ -0,0 +1,44 @@ + '', + 'priority' => 1, + 'priorityName' => 'ALERT', + 'message' => 'foo', + 'extra' => array() + ); + + $eventA = $processor->process($event); + $this->assertArrayHasKey('requestId', $eventA['extra']); + + $eventB = $processor->process($event); + $this->assertArrayHasKey('requestId', $eventB['extra']); + + $this->assertEquals($eventA['extra']['requestId'], $eventB['extra']['requestId']); + } +} diff --git a/test/TestAsset/ConcreteWriter.php b/test/TestAsset/ConcreteWriter.php index 8ef1a554..bb4644da 100644 --- a/test/TestAsset/ConcreteWriter.php +++ b/test/TestAsset/ConcreteWriter.php @@ -1,4 +1,13 @@ facility; } + + public function getApplicationName() + { + return $this->appName; + } } diff --git a/test/TestAsset/ErrorGeneratingWriter.php b/test/TestAsset/ErrorGeneratingWriter.php new file mode 100644 index 00000000..f629adce --- /dev/null +++ b/test/TestAsset/ErrorGeneratingWriter.php @@ -0,0 +1,23 @@ +enabled = $enabled; + } + + public function getEnabled() + { + return $this->enabled; + } + + public function error($line) + { + $this->calls['error'][] = $line; + } + + public function warn($line) + { + $this->calls['warn'][] = $line; + } + + public function info($line) + { + $this->calls['info'][] = $line; + } + + public function trace($line) + { + $this->calls['trace'][] = $line; + } + + public function log($line) + { + $this->calls['log'][] = $line; + } +} diff --git a/test/TestAsset/MockDbAdapter.php b/test/TestAsset/MockDbAdapter.php index 6e210846..6f8eec29 100644 --- a/test/TestAsset/MockDbAdapter.php +++ b/test/TestAsset/MockDbAdapter.php @@ -1,4 +1,13 @@ calls[$method][] = $params; } - + public function __construct() { $this->platform = new MockDbPlatform; $this->driver = new MockDbDriver; - + } public function query($sql, $parametersOrQueryMode = DbAdapter::QUERY_MODE_PREPARE) { diff --git a/test/TestAsset/MockDbDriver.php b/test/TestAsset/MockDbDriver.php index 260ba95c..56c667f7 100644 --- a/test/TestAsset/MockDbDriver.php +++ b/test/TestAsset/MockDbDriver.php @@ -1,4 +1,13 @@ calls[$method][] = $params; } - + } diff --git a/test/TestAsset/MockDbPlatform.php b/test/TestAsset/MockDbPlatform.php index 412be2be..a0d9415c 100644 --- a/test/TestAsset/MockDbPlatform.php +++ b/test/TestAsset/MockDbPlatform.php @@ -1,4 +1,13 @@ calls[$method][] = $params; } - + } diff --git a/test/TestAsset/MockFirePhp.php b/test/TestAsset/MockFirePhp.php new file mode 100644 index 00000000..41b4933f --- /dev/null +++ b/test/TestAsset/MockFirePhp.php @@ -0,0 +1,46 @@ +enabled = $enabled; + } + + public function getEnabled() + { + return $this->enabled; + } + + public function error($line) + { + $this->calls['error'][] = $line; + } + + public function warn($line) + { + $this->calls['warn'][] = $line; + } + + public function info($line) + { + $this->calls['info'][] = $line; + } + + public function trace($line) + { + $this->calls['trace'][] = $line; + } + + public function log($line) + { + $this->calls['log'][] = $line; + } +} diff --git a/test/TestAsset/NotStringObject.php b/test/TestAsset/NotStringObject.php new file mode 100644 index 00000000..989fc4bf --- /dev/null +++ b/test/TestAsset/NotStringObject.php @@ -0,0 +1,16 @@ +_writer = new ConcreteWriter(); } - /** - * @group ZF-6085 - */ - public function testSetFormatter() + public function testSetSimpleFormatterByName() { - $this->_writer->setFormatter(new SimpleFormatter()); - $this->setExpectedException('PHPUnit_Framework_Error'); - $this->_writer->setFormatter(new \StdClass()); + $instance = $this->_writer->setFormatter('simple'); + $this->assertAttributeInstanceOf('Zend\Log\Formatter\Simple', 'formatter', $instance); } public function testAddFilter() @@ -60,6 +44,18 @@ public function testAddFilter() $this->_writer->addFilter(new \StdClass()); } + public function testAddMockFilterByName() + { + $instance = $this->_writer->addFilter('mock'); + $this->assertTrue($instance instanceof ConcreteWriter); + } + + public function testAddRegexFilterWithParamsByName() + { + $instance = $this->_writer->addFilter('regex', array( 'regex' => '/mess/' )); + $this->assertTrue($instance instanceof ConcreteWriter); + } + /** * @group ZF-8953 */ @@ -70,4 +66,44 @@ public function testFluentInterface() $this->assertTrue($instance instanceof ConcreteWriter); } + + public function testConvertErrorsToException() + { + $writer = new ErrorGeneratingWriter(); + $this->setExpectedException('Zend\Log\Exception\RuntimeException'); + $writer->write(array('message' => 'test')); + + $writer->setConvertWriteErrorsToExceptions(false); + $this->setExpectedException('PHPUnit_Framework_Error_Warning'); + $writer->write(array('message' => 'test')); + } + + public function testConstructorWithOptions() + { + $options = array('filters' => array( + array( + 'name' => 'mock', + ), + array( + 'name' => 'priority', + 'options' => array( + 'priority' => 3, + ), + ), + ), + 'formatter' => array( + 'name' => 'base', + ), + ); + + $writer = new ConcreteWriter($options); + + $this->assertAttributeInstanceOf('Zend\Log\Formatter\Base', 'formatter', $writer); + + $filters = $this->readAttribute($writer, 'filters'); + $this->assertCount(2, $filters); + + $this->assertInstanceOf('Zend\Log\Filter\Priority', $filters[1]); + $this->assertEquals(3, $this->readAttribute($filters[1], 'priority')); + } } diff --git a/test/Writer/ChromePhpTest.php b/test/Writer/ChromePhpTest.php new file mode 100644 index 00000000..52784fb1 --- /dev/null +++ b/test/Writer/ChromePhpTest.php @@ -0,0 +1,83 @@ +chromephp = new MockChromePhp(); + + } + + public function testGetChromePhp() + { + $writer = new ChromePhp($this->chromephp); + $this->assertTrue($writer->getChromePhp() instanceof ChromePhpInterface); + } + + public function testSetChromePhp() + { + $writer = new ChromePhp($this->chromephp); + $chromephp2 = new MockChromePhp(); + + $writer->setChromePhp($chromephp2); + $this->assertTrue($writer->getChromePhp() instanceof ChromePhpInterface); + $this->assertEquals($chromephp2, $writer->getChromePhp()); + } + + public function testWrite() + { + $writer = new ChromePhp($this->chromephp); + $writer->write(array( + 'message' => 'my msg', + 'priority' => Logger::DEBUG + )); + $this->assertEquals('my msg', $this->chromephp->calls['trace'][0]); + } + + public function testWriteDisabled() + { + $chromephp = new MockChromePhp(false); + $writer = new ChromePhp($chromephp); + $writer->write(array( + 'message' => 'my msg', + 'priority' => Logger::DEBUG + )); + $this->assertTrue(empty($this->chromephp->calls)); + } +} diff --git a/test/Writer/DbTest.php b/test/Writer/DbTest.php index 0b4eb344..41dca67a 100644 --- a/test/Writer/DbTest.php +++ b/test/Writer/DbTest.php @@ -1,38 +1,24 @@ writer = new DbWriter($this->db, $this->tableName); } - public function testFormattingIsNotSupported() + public function testNotPassingTableNameToConstructorThrowsException() + { + $this->setExpectedException('Zend\Log\Exception\InvalidArgumentException', 'table name'); + $writer = new DbWriter($this->db); + } + + public function testNotPassingDbToConstructorThrowsException() { - $this->setExpectedException('Zend\Log\Exception\InvalidArgumentException', 'does not support formatting'); - $this->writer->setFormatter(new SimpleFormatter); + $this->setExpectedException('Zend\Log\Exception\InvalidArgumentException', 'Adapter'); + $writer = new DbWriter(array()); + } + + public function testPassingTableNameAsArgIsOK() + { + $options = array( + 'db' => $this->db, + 'table' => $this->tableName, + ); + $writer = new DbWriter($options); + $this->assertInstanceOf('Zend\Log\Writer\Db', $writer); + $this->assertAttributeEquals($this->tableName, 'tableName', $writer); } public function testWriteWithDefaults() @@ -70,7 +73,7 @@ public function testWriteWithDefaults() } public function testWriteWithDefaultsUsingArray() - { + { // log to the mock db adapter $message = 'message-to-log'; $priority = 2; @@ -94,11 +97,11 @@ public function testWriteWithDefaultsUsingArray() ); $this->assertEquals(array($binds), $this->db->calls['execute'][0]); } - + public function testWriteWithDefaultsUsingArrayAndSeparator() - { + { $this->writer = new DbWriter($this->db, $this->tableName, null, '-'); - + // log to the mock db adapter $message = 'message-to-log'; $priority = 2; @@ -122,12 +125,12 @@ public function testWriteWithDefaultsUsingArrayAndSeparator() ); $this->assertEquals(array($binds), $this->db->calls['execute'][0]); } - + public function testWriteUsesOptionalCustomColumnNames() { $this->writer = new DbWriter($this->db, $this->tableName, array( 'message' => 'new-message-field' , - 'priority' => 'new-priority-field' + 'priority' => 'new-priority-field' )); // log to the mock db adapter @@ -149,7 +152,7 @@ public function testWriteUsesOptionalCustomColumnNames() ); $this->assertEquals(array($binds), $this->db->calls['execute'][0]); } - + public function testWriteUsesParamsWithArray() { $this->writer = new DbWriter($this->db, $this->tableName, array( @@ -160,7 +163,7 @@ public function testWriteUsesParamsWithArray() 'file' => 'new-file' ) )); - + // log to the mock db adapter $message = 'message-to-log'; $priority = 2; @@ -194,12 +197,35 @@ public function testShutdownRemovesReferenceToDatabaseInstance() $this->writer->write(array('message' => 'this should fail')); } - /** - * @group ZF-10089 - */ - public function testThrowStrictSetFormatter() + public function testWriteDateTimeAsTimestamp() + { + $date = new DateTime(); + $event = array('timestamp'=> $date); + $this->writer->write($event); + + $this->assertContains('query', array_keys($this->db->calls)); + $this->assertEquals(1, count($this->db->calls['query'])); + + $this->assertEquals(array(array( + 'timestamp' => $date->format(FormatterInterface::DEFAULT_DATETIME_FORMAT) + )), $this->db->calls['execute'][0]); + } + + public function testWriteDateTimeAsExtraValue() { - $this->setExpectedException('PHPUnit_Framework_Error'); - $this->writer->setFormatter(new \StdClass()); + $date = new DateTime(); + $event = array( + 'extra'=> array( + 'request_time' => $date + ) + ); + $this->writer->write($event); + + $this->assertContains('query', array_keys($this->db->calls)); + $this->assertEquals(1, count($this->db->calls['query'])); + + $this->assertEquals(array(array( + 'extra_request_time' => $date->format(FormatterInterface::DEFAULT_DATETIME_FORMAT) + )), $this->db->calls['execute'][0]); } } diff --git a/test/Writer/FingersCrossedTest.php b/test/Writer/FingersCrossedTest.php new file mode 100644 index 00000000..cd5ca228 --- /dev/null +++ b/test/Writer/FingersCrossedTest.php @@ -0,0 +1,84 @@ +write(array('priority' => 3, 'message' => 'foo')); + + $this->assertSame(count($wrappedWriter->events), 0); + } + + public function testFlushing() + { + $wrappedWriter = new MockWriter(); + $writer = new FingersCrossedWriter($wrappedWriter, 2); + + $writer->write(array('priority' => 3, 'message' => 'foo')); + $writer->write(array('priority' => 1, 'message' => 'bar')); + + $this->assertSame(count($wrappedWriter->events), 2); + } + + public function testAfterFlushing() + { + $wrappedWriter = new MockWriter(); + $writer = new FingersCrossedWriter($wrappedWriter, 2); + + $writer->write(array('priority' => 3, 'message' => 'foo')); + $writer->write(array('priority' => 1, 'message' => 'bar')); + $writer->write(array('priority' => 3, 'message' => 'bar')); + + $this->assertSame(count($wrappedWriter->events), 3); + } + + public function setWriterByName() + { + $writer = new FingersCrossedWriter('mock'); + $this->assertAttributeInstanceOf('Zend\Log\Writer\Mock', 'writer', $writer); + } + + public function testConstructorOptions() + { + $options = array('writer' => 'mock', 'priority' => 3); + $writer = new FingersCrossedWriter($options); + $this->assertAttributeInstanceOf('Zend\Log\Writer\Mock', 'writer', $writer); + + $filters = $this->readAttribute($writer, 'filters'); + $this->assertCount(1, $filters); + $this->assertInstanceOf('Zend\Log\Filter\Priority', $filters[0]); + $this->assertAttributeEquals(3, 'priority', $filters[0]); + } + + public function testFormattingIsNotSupported() + { + $options = array('writer' => 'mock', 'priority' => 3); + $writer = new FingersCrossedWriter($options); + + $writer->setFormatter($this->getMock('Zend\Log\Formatter\FormatterInterface')); + $this->assertAttributeEmpty('formatter', $writer); + } +} diff --git a/test/Writer/FirePhpTest.php b/test/Writer/FirePhpTest.php new file mode 100644 index 00000000..6225f058 --- /dev/null +++ b/test/Writer/FirePhpTest.php @@ -0,0 +1,91 @@ +firephp = new MockFirePhp(); + + } + /** + * Test get FirePhp + */ + public function testGetFirePhp() + { + $writer = new FirePhp($this->firephp); + $this->assertTrue($writer->getFirePhp() instanceof FirePhpInterface); + } + /** + * Test set firephp + */ + public function testSetFirePhp() + { + $writer = new FirePhp($this->firephp); + $firephp2 = new MockFirePhp(); + + $writer->setFirePhp($firephp2); + $this->assertTrue($writer->getFirePhp() instanceof FirePhpInterface); + $this->assertEquals($firephp2, $writer->getFirePhp()); + } + /** + * Test write + */ + public function testWrite() + { + $writer = new FirePhp($this->firephp); + $writer->write(array( + 'message' => 'my msg', + 'priority' => Logger::DEBUG + )); + $this->assertEquals('my msg', $this->firephp->calls['trace'][0]); + } + /** + * Test write with FirePhp disabled + */ + public function testWriteDisabled() + { + $firephp = new MockFirePhp(false); + $writer = new FirePhp($firephp); + $writer->write(array( + 'message' => 'my msg', + 'priority' => Logger::DEBUG + )); + $this->assertTrue(empty($this->firephp->calls)); + } +} diff --git a/test/Writer/MailTest.php b/test/Writer/MailTest.php index aa086b06..5c734f99 100644 --- a/test/Writer/MailTest.php +++ b/test/Writer/MailTest.php @@ -1,29 +1,17 @@ writer = new MailWriter($message, $transport); $this->log = new Logger(); - $this->log->addWriter($this->writer); + $this->log->addWriter($this->writer); } protected function tearDown() @@ -75,11 +61,11 @@ protected function tearDown() * @return void */ public function testNormalLoggingMultiplePerLevel() - { + { $this->log->info('an info message'); $this->log->info('a second info message'); unset($this->log); - + $contents = file_get_contents(__DIR__ . '/' . self::FILENAME); $this->assertContains('an info message', $contents); $this->assertContains('a second info message', $contents); @@ -88,11 +74,11 @@ public function testNormalLoggingMultiplePerLevel() public function testSetSubjectPrependText() { $this->writer->setSubjectPrependText('test'); - + $this->log->info('an info message'); $this->log->info('a second info message'); unset($this->log); - + $contents = file_get_contents(__DIR__ . '/' . self::FILENAME); $this->assertContains('an info message', $contents); $this->assertContains('Subject: test', $contents); diff --git a/test/Writer/MockTest.php b/test/Writer/MockTest.php index 68960e42..1a009a5c 100644 --- a/test/Writer/MockTest.php +++ b/test/Writer/MockTest.php @@ -1,35 +1,21 @@ markTestSkipped('The mongo PHP extension is not available'); + } + + $this->database = 'zf2_test'; + $this->collection = 'logs'; + + $this->mongo = $this->getMockBuilder('Mongo') + ->disableOriginalConstructor() + ->setMethods(array('selectCollection')) + ->getMock(); + + $this->mongoCollection = $this->getMockBuilder('MongoCollection') + ->disableOriginalConstructor() + ->setMethods(array('save')) + ->getMock(); + + $this->mongo->expects($this->any()) + ->method('selectCollection') + ->with($this->database, $this->collection) + ->will($this->returnValue($this->mongoCollection)); + } + + public function testFormattingIsNotSupported() + { + $writer = new MongoDBWriter($this->mongo, $this->database, $this->collection); + + $writer->setFormatter($this->getMock('Zend\Log\Formatter\FormatterInterface')); + $this->assertAttributeEmpty('formatter', $writer); + } + + public function testWriteWithDefaultSaveOptions() + { + $event = array('message'=> 'foo', 'priority' => 42); + + $this->mongoCollection->expects($this->once()) + ->method('save') + ->with($event, array()); + + $writer = new MongoDBWriter($this->mongo, $this->database, $this->collection); + + $writer->write($event); + } + + public function testWriteWithCustomSaveOptions() + { + $event = array('message' => 'foo', 'priority' => 42); + $saveOptions = array('safe' => false, 'fsync' => false, 'timeout' => 100); + + $this->mongoCollection->expects($this->once()) + ->method('save') + ->with($event, $saveOptions); + + $writer = new MongoDBWriter($this->mongo, $this->database, $this->collection, $saveOptions); + + $writer->write($event); + } + + public function testWriteConvertsDateTimeToMongoDate() + { + $date = new DateTime(); + $event = array('timestamp'=> $date); + + $this->mongoCollection->expects($this->once()) + ->method('save') + ->with($this->contains(new MongoDate($date->getTimestamp()), false)); + + $writer = new MongoDBWriter($this->mongo, $this->database, $this->collection); + + $writer->write($event); + } +} diff --git a/test/Writer/NullTest.php b/test/Writer/NullTest.php index ab4e039b..16aec489 100644 --- a/test/Writer/NullTest.php +++ b/test/Writer/NullTest.php @@ -1,35 +1,21 @@ assertContains($expected, $contents); } + + public function testAllowSpecifyingLogSeparator() + { + $stream = fopen('php://memory', 'w+'); + $writer = new StreamWriter($stream); + $writer->setLogSeparator('::'); + + $fields = array('message' => 'message1'); + $writer->write($fields); + $fields['message'] = 'message2'; + $writer->write($fields); + + rewind($stream); + $contents = stream_get_contents($stream); + fclose($stream); + + $this->assertRegexp('/message1.*?::.*?message2/', $contents); + $this->assertNotContains(PHP_EOL, $contents); + } + + public function testAllowsSpecifyingLogSeparatorAsConstructorArgument() + { + $writer = new StreamWriter('php://memory', 'w+', '::'); + $this->assertEquals('::', $writer->getLogSeparator()); + } + + public function testAllowsSpecifyingLogSeparatorWithinArrayPassedToConstructor() + { + $options = array( + 'stream' => 'php://memory', + 'mode' => 'w+', + 'log_separator' => '::', + ); + $writer = new StreamWriter($options); + $this->assertEquals('::', $writer->getLogSeparator()); + } } diff --git a/test/Writer/SyslogTest.php b/test/Writer/SyslogTest.php index d374f5f8..b1ff76da 100644 --- a/test/Writer/SyslogTest.php +++ b/test/Writer/SyslogTest.php @@ -1,22 +1,11 @@ write($event); } + + /** + * @group ZF2-534 + */ + public function testPassApplicationNameViaConstructor() + { + $writer = new CustomSyslogWriter(array('application' => 'test_app')); + $this->assertEquals('test_app', $writer->getApplicationName()); + } } diff --git a/test/Writer/ZendMonitorTest.php b/test/Writer/ZendMonitorTest.php index a109e51a..a1ce8616 100644 --- a/test/Writer/ZendMonitorTest.php +++ b/test/Writer/ZendMonitorTest.php @@ -1,22 +1,11 @@ plugins = new WriterPluginManager(); @@ -43,4 +35,10 @@ public function testRegisteringInvalidWriterRaisesException() $this->setExpectedException('Zend\Log\Exception\InvalidArgumentException', 'must implement'); $this->plugins->setService('test', $this); } + + public function testInvokableClassFirephp() + { + $firephp = $this->plugins->get('firephp'); + $this->assertInstanceOf('Zend\Log\Writer\Firephp', $firephp); + } }