Merge branch 'master' into feature/setup-wizard-7163

Conflicts:
	application/forms/Config/General/LoggingConfigForm.php
This commit is contained in:
Johannes Meyer 2014-10-17 13:04:59 +02:00
commit 9f127be1dc
23 changed files with 303 additions and 249 deletions

View File

@ -5,6 +5,7 @@
namespace Icinga\Form\Config\General;
use Icinga\Application\Icinga;
use Icinga\Logger\Logger;
use Icinga\Web\Form;
use Icinga\Web\Form\Validator\WritablePathValidator;
@ -19,30 +20,14 @@ class LoggingConfigForm extends Form
}
/**
* @see Form::createElements()
* (non-PHPDoc)
* @see Form::createElements() For the method documentation.
*/
public function createElements(array $formData)
{
$this->addElement(
'select',
'logging_level',
array(
'value' => 1,
'required' => true,
'label' => t('Logging Level'),
'description' => t('The maximum loglevel to emit.'),
'multiOptions' => array(
0 => t('None', 'app.config.logging.level'),
1 => t('Error', 'app.config.logging.level'),
2 => t('Warning', 'app.config.logging.level'),
3 => t('Information', 'app.config.logging.level'),
4 => t('Debug', 'app.config.logging.level')
)
)
);
$this->addElement(
'select',
'logging_type',
'logging_log',
array(
'required' => true,
'autosubmit' => true,
@ -50,13 +35,32 @@ class LoggingConfigForm extends Form
'description' => t('The type of logging to utilize.'),
'multiOptions' => array(
'syslog' => 'Syslog',
'file' => t('File', 'app.config.logging.type')
'file' => t('File', 'app.config.logging.type'),
'none' => t('None', 'app.config.logging.type')
)
)
);
if (false === isset($formData['logging_type']) || $formData['logging_type'] === 'syslog') {
$this->addElement(
if (! isset($formData['logging_log']) || $formData['logging_log'] !== 'none') {
$this->addElement(
'select',
'logging_level',
array(
'required' => true,
'label' => t('Logging Level'),
'description' => t('The maximum logging level to emit.'),
'multiOptions' => array(
Logger::$levels[Logger::ERROR] => t('Error', 'app.config.logging.level'),
Logger::$levels[Logger::WARNING] => t('Warning', 'app.config.logging.level'),
Logger::$levels[Logger::INFO] => t('Information', 'app.config.logging.level'),
Logger::$levels[Logger::DEBUG] => t('Debug', 'app.config.logging.level')
)
)
);
}
if (isset($formData['logging_log']) && $formData['logging_log'] === 'syslog') {
$this->addElement(
'text',
'logging_application',
array(
@ -78,26 +82,30 @@ class LoggingConfigForm extends Form
)
)
);
$this->addElement(
'select',
'logging_facility',
array(
'required' => true,
'label' => t('Facility'),
'description' => t('The Syslog facility to utilize.'),
'multiOptions' => array(
'LOG_USER' => 'LOG_USER'
)
)
);
} elseif ($formData['logging_type'] === 'file') {
/*
* Note(el): Since we provide only one possible value for the syslog facility, I opt against exposing
* this configuration.
*/
// $this->addElement(
// 'select',
// 'logging_facility',
// array(
// 'required' => true,
// 'label' => t('Facility'),
// 'description' => t('The syslog facility to utilize.'),
// 'multiOptions' => array(
// 'user' => 'LOG_USER'
// )
// )
// );
} elseif (isset($formData['logging_log']) && $formData['logging_log'] === 'file') {
$this->addElement(
'text',
'logging_target',
'logging_file',
array(
'required' => true,
'label' => t('Filepath'),
'description' => t('The logfile to write messages to.'),
'label' => t('File path'),
'description' => t('The full path to the log file to write messages to.'),
'value' => $this->getDefaultLogDir(),
'validators' => array(new WritablePathValidator())
)
@ -108,9 +116,9 @@ class LoggingConfigForm extends Form
}
/**
* Return the default logging directory for type "file"
* Return the default logging directory for type 'file'
*
* @return string
* @return string
*/
protected function getDefaultLogDir()
{

View File

@ -40,13 +40,16 @@ class GeneralConfigForm extends ConfigForm
*/
public function onSuccess(Request $request)
{
$sections = array();
foreach ($this->getValues() as $sectionAndPropertyName => $value) {
list($section, $property) = explode('_', $sectionAndPropertyName);
if (isset($this->config->{$section})) {
$this->config->{$section}->{$property} = $value;
} else {
$this->config->{$section} = array($property => $value);
if (! isset($sections[$section])) {
$sections[$section] = array();
}
$sections[$section][$property] = $value;
}
foreach ($sections as $section => $config) {
$this->config->{$section} = $config;
}
if ($this->save()) {

View File

@ -1,3 +1,6 @@
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Create New Authentication Backend'); ?></h4>
<p>
<?= $this->translate(

View File

@ -1,2 +1,5 @@
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Edit Backend'); ?></h4>
<?= $form; ?>

View File

@ -1,2 +1,5 @@
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Remove Backend'); ?></h4>
<?= $form; ?>

View File

@ -1,3 +1,6 @@
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Create A New Resource'); ?></h4>
<p><?= $this->translate('Resources are entities that provide data to Icinga Web 2.'); ?></p>
<?= $form; ?>

View File

@ -1,2 +1,5 @@
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Edit Existing Resource'); ?></h4>
<?= $form; ?>

View File

@ -1,2 +1,5 @@
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Remove Existing Resource'); ?></h4>
<?= $form; ?>

View File

@ -4,10 +4,10 @@
namespace Icinga\Application;
use ErrorException;
use Exception;
use Zend_Config;
use Icinga\Application\Modules\Manager as ModuleManager;
use Icinga\Application\Config;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
@ -348,11 +348,7 @@ abstract class ApplicationBootstrap
Logger::create(
new Zend_Config(
array(
'enable' => true,
'level' => Logger::$ERROR,
'type' => 'syslog',
'facility' => 'LOG_USER',
'application' => 'icingaweb'
'log' => 'syslog'
)
)
);
@ -386,6 +382,17 @@ abstract class ApplicationBootstrap
error_reporting(E_ALL | E_STRICT);
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
if (error_reporting() === 0) {
// Error was suppressed with the @-operator
return false; // Continue with the normal error handler
}
switch($errno) {
case E_STRICT:
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
return false; // Continue with the normal error handler
});
return $this;
}

View File

@ -51,10 +51,9 @@ class Cli extends ApplicationBootstrap
Logger::create(
new Zend_Config(
array(
'enable' => true,
'level' => Logger::$INFO,
'type' => 'file',
'target' => 'php://stderr'
'level' => Logger::INFO,
'log' => 'file',
'file' => 'php://stderr'
)
)
);

View File

@ -6,61 +6,110 @@ namespace Icinga\Logger;
use Exception;
use Zend_Config;
use LogicException;
use Icinga\Exception\ConfigurationError;
use Icinga\Logger\Writer\FileWriter;
use Icinga\Logger\Writer\SyslogWriter;
/**
* Singleton logger
* Logger
*/
class Logger
{
/**
* Debug message
*/
const DEBUG = 1;
/**
* Informational message
*/
const INFO = 2;
/**
* Warning message
*/
const WARNING = 4;
/**
* Error message
*/
const ERROR = 8;
/**
* Log levels
*
* @var array
*/
public static $levels = array(
Logger::DEBUG => 'DEBUG',
Logger::INFO => 'INFO',
Logger::WARNING => 'WARNING',
Logger::ERROR => 'ERROR'
);
/**
* This logger's instance
*
* @var Logger
* @var static
*/
protected static $instance;
/**
* The log writer to use
* Log writer
*
* @var \Icinga\Logger\LogWriter
*/
protected $writer;
/**
* The configured type
*
* @string Type (syslog, file)
*/
protected $type = 'none';
/**
* The maximum severity to emit
* Maximum level to emit
*
* @var int
*/
protected $verbosity;
/**
* The supported severities
*/
public static $NONE = 0;
public static $ERROR = 1;
public static $WARNING = 2;
public static $INFO = 3;
public static $DEBUG = 4;
protected $level;
/**
* Create a new logger object
*
* @param Zend_Config $config
* @param Zend_Config $config
*
* @throws ConfigurationError If the logging configuration directive 'log' is missing or if the logging level is
* not defined
*/
public function __construct(Zend_Config $config)
{
$this->verbosity = $config->level;
if ($config->log === null) {
throw new ConfigurationError('Required logging configuration directive \'log\' missing');
}
if ($config->enable) {
if (($level = $config->level) !== null) {
if (is_numeric($level)) {
if (! isset(static::$levels[(int) $level])) {
throw new ConfigurationError(
'Can\'t set logging level %d. Logging level is not defined. Use one of %s or one of the'
. ' Logger\'s constants.',
$level,
implode(', ', array_keys(static::$levels))
);
}
$this->level = static::$levels[(int) $level];
} else {
$level = strtoupper($level);
$levels = array_flip(static::$levels);
if (! isset($levels[$level])) {
throw new ConfigurationError(
'Can\'t set logging level "%s". Logging level is not defined. Use one of %s.',
$level,
implode(', ', array_keys($levels))
);
}
$this->level = $levels[$level];
}
} else {
$this->level = static::ERROR;
}
if (strtolower($config->get('log', 'syslog')) !== 'none') {
$this->writer = $this->createWriter($config);
}
}
@ -69,14 +118,17 @@ class Logger
* Create a new logger object
*
* @param Zend_Config $config
*
* @return static
*/
public static function create(Zend_Config $config)
{
static::$instance = new static($config);
return static::$instance;
}
/**
* Return a log writer
* Create a log writer
*
* @param Zend_Config $config The configuration to initialize the writer with
*
@ -85,34 +137,26 @@ class Logger
*/
protected function createWriter(Zend_Config $config)
{
$class = 'Icinga\\Logger\\Writer\\' . ucfirst(strtolower($config->type)) . 'Writer';
if (!class_exists($class)) {
$class = 'Icinga\\Logger\\Writer\\' . ucfirst(strtolower($config->log)) . 'Writer';
if (! class_exists($class)) {
throw new ConfigurationError(
'Cannot find log writer of type "%s"',
$config->type
$config->log
);
}
$this->type = $config->type;
return new $class($config);
}
/**
* Write a message to the log
* Log a message
*
* @param string $message The message to write
* @param int $severity The severity to use
*
* @throws LogicException In case $severity equals self::$NONE
* @param int $level The logging level
* @param string $message The log message
*/
public function log($message, $severity)
public function log($level, $message)
{
if ($severity === static::$NONE) {
throw new LogicException("`None' (0) is not a valid severity to log messages");
}
if ($this->writer !== null && $this->verbosity >= $severity) {
$this->writer->log($severity, $message);
if ($this->writer !== null && $this->level >= $level) {
$this->writer->log($level, $message);
}
}
@ -170,7 +214,7 @@ class Logger
public static function error()
{
if (static::$instance !== null && func_num_args() > 0) {
static::$instance->log(static::formatMessage(func_get_args()), static::$ERROR);
static::$instance->log(static::ERROR, static::formatMessage(func_get_args()));
}
}
@ -182,7 +226,7 @@ class Logger
public static function warning()
{
if (static::$instance !== null && func_num_args() > 0) {
static::$instance->log(static::formatMessage(func_get_args()), static::$WARNING);
static::$instance->log(static::WARNING, static::formatMessage(func_get_args()));
}
}
@ -194,7 +238,7 @@ class Logger
public static function info()
{
if (static::$instance !== null && func_num_args() > 0) {
static::$instance->log(static::formatMessage(func_get_args()), static::$INFO);
static::$instance->log(static::INFO, static::formatMessage(func_get_args()));
}
}
@ -206,7 +250,7 @@ class Logger
public static function debug()
{
if (static::$instance !== null && func_num_args() > 0) {
static::$instance->log(static::formatMessage(func_get_args()), static::$DEBUG);
static::$instance->log(static::DEBUG, static::formatMessage(func_get_args()));
}
}
@ -220,20 +264,30 @@ class Logger
return $this->writer;
}
/**
* Is the logger writing to Syslog?
*
* @return bool
*/
public static function writesToSyslog()
{
return static::$instance && static::$instance->type === 'syslog';
return static::$instance && static::$instance instanceof SyslogWriter;
}
/**
* Is the logger writing to a file?
*
* @return bool
*/
public static function writesToFile()
{
return static::$instance && static::$instance->type === 'file';
return static::$instance && static::$instance instanceof FileWriter;
}
/**
* Get this' instance
*
* @return Logger
* @return static
*/
public static function getInstance()
{

View File

@ -5,38 +5,43 @@
namespace Icinga\Logger\Writer;
use Exception;
use Icinga\Exception\IcingaException;
use Zend_Config;
use Icinga\Util\File;
use Icinga\Exception\ConfigurationError;
use Icinga\Logger\Logger;
use Icinga\Logger\LogWriter;
use Icinga\Exception\ConfigurationError;
use Icinga\Util\File;
/**
* Class to write log messages to a file
* Log to a file
*/
class FileWriter extends LogWriter
{
/**
* The path to the file
* Path to the file
*
* @var string
*/
protected $path;
protected $file;
/**
* Create a new log writer initialized with the given configuration
* Create a new file log writer
*
* @throws ConfigurationError In case the log path does not exist
* @param Zend_Config $config
*
* @throws ConfigurationError If the configuration directive 'file' is missing or if the path to 'file' does
* not exist or if writing to 'file' is not possible
*/
public function __construct(Zend_Config $config)
{
$this->path = $config->target;
if ($config->file === null) {
throw new ConfigurationError('Required logging configuration directive \'file\' missing');
}
$this->file = $config->file;
if (substr($this->path, 0, 6) !== 'php://' && false === file_exists(dirname($this->path))) {
if (substr($this->file, 0, 6) !== 'php://' && ! file_exists(dirname($this->file))) {
throw new ConfigurationError(
'Log path "%s" does not exist',
dirname($this->path)
dirname($this->file)
);
}
@ -45,70 +50,32 @@ class FileWriter extends LogWriter
} catch (Exception $e) {
throw new ConfigurationError(
'Cannot write to log file "%s" (%s)',
$this->path,
$this->file,
$e->getMessage()
);
}
}
/**
* Log a message with the given severity
* Log a message
*
* @param int $severity The severity to use
* @param string $message The message to log
* @param int $level The logging level
* @param string $message The log message
*/
public function log($severity, $message)
public function log($level, $message)
{
$this->write(date('c') . ' ' . $this->getSeverityString($severity) . ' ' . $message . PHP_EOL);
$this->write(date('c') . ' - ' . Logger::$levels[$level] . ' - ' . $message . PHP_EOL);
}
/**
* Return a string representation for the given severity
* Write a message to the log
*
* @param string $severity The severity to use
*
* @return string The string representation of the severity
*
* @throws IcingaException In case the given severity is unknown
* @param string $message
*/
protected function getSeverityString($severity)
protected function write($message)
{
switch ($severity) {
case Logger::$ERROR:
return '- ERROR -';
case Logger::$WARNING:
return '- WARNING -';
case Logger::$INFO:
return '- INFO -';
case Logger::$DEBUG:
return '- DEBUG -';
default:
throw new IcingaException(
'Unknown severity "%s"',
$severity
);
}
}
/**
* Write a message to the path
*
* @param string $text The message to write
*
* @throws Exception In case write acess to the path failed
*/
protected function write($text)
{
$file = new File($this->path, 'a');
$file->fwrite($text);
$file = new File($this->file, 'a');
$file->fwrite($message);
$file->fflush();
}
/**
* @return string
*/
public function getPath()
{
return $this->path;
}
}

View File

@ -4,27 +4,24 @@
namespace Icinga\Logger\Writer;
use Exception;
use Zend_Config;
use Icinga\Logger\Logger;
use Icinga\Logger\LogWriter;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\IcingaException;
/**
* Class to write messages to syslog
* Log to the syslog service
*/
class SyslogWriter extends LogWriter
{
/**
* The facility where to write messages to
* Syslog facility
*
* @var string
* @var int
*/
protected $facility;
/**
* The prefix to prepend to each message
* Prefix to prepend to each message
*
* @var string
*/
@ -35,79 +32,42 @@ class SyslogWriter extends LogWriter
*
* @var array
*/
protected $facilities = array(
'LOG_USER' => LOG_USER
public static $facilities = array(
'user' => LOG_USER
);
/**
* Create a new log writer initialized with the given configuration
* Log level to syslog severity map
*
* @var array
*/
public static $severityMap = array(
Logger::ERROR => LOG_ERR,
Logger::WARNING => LOG_WARNING,
Logger::INFO => LOG_INFO,
Logger::DEBUG => LOG_DEBUG
);
/**
* Create a new syslog log writer
*
* @param Zend_Config $config
*/
public function __construct(Zend_Config $config)
{
if (!array_key_exists($config->facility, $this->facilities)) {
throw new ConfigurationError(
'Cannot create syslog writer with unknown facility "%s"',
$config->facility
);
}
$this->ident = $config->application;
$this->facility = $this->facilities[$config->facility];
$this->ident = $config->get('application', 'icingaweb');
$this->facility = static::$facilities['user'];
}
/**
* Log a message with the given severity
* Log a message
*
* @param int $severity The severity to use
* @param string $message The message to log
*
* @throws Exception In case the given severity cannot be mapped to a valid syslog priority
* @param int $level The logging level
* @param string $message The log message
*/
public function log($severity, $message)
public function log($level, $message)
{
$priorities = array(
Logger::$ERROR => LOG_ERR,
Logger::$WARNING => LOG_WARNING,
Logger::$INFO => LOG_INFO,
Logger::$DEBUG => LOG_DEBUG
);
if (!array_key_exists($severity, $priorities)) {
throw new IcingaException(
'Severity "%s" cannot be mapped to a valid syslog priority',
$severity
);
}
$this->open();
$this->write($priorities[$severity], $message);
$this->close();
}
/**
* Open a new syslog connection
*/
protected function open()
{
openlog($this->ident, 0, $this->facility);
}
/**
* Write a message to the syslog connection
*
* @param int $priority The priority to use
* @param string $message The message to write
*/
protected function write($priority, $message)
{
syslog($priority, $message);
}
/**
* Close the syslog connection
*/
protected function close()
{
closelog();
openlog($this->ident, LOG_PID, $this->facility);
syslog(static::$severityMap[$level], $message);
}
}

View File

@ -131,7 +131,7 @@ class File extends SplFileObject
set_error_handler(
function ($errno, $errstr, $errfile, $errline) {
restore_error_handler();
throw new ErrorException($errno, $errstr, $errfile, $errline);
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
},
E_WARNING
);

View File

@ -74,6 +74,13 @@ EOT;
*/
private $dropdownTabs = array();
/**
* Whether only the close-button should by rendered for this tab
*
* @var bool
*/
private $closeButtonOnly = false;
/**
* Whether the tabs should contain a close-button
*
@ -275,14 +282,19 @@ EOT;
*/
public function render()
{
if (empty($this->tabs)) {
return '';
if (empty($this->tabs) || true === $this->closeButtonOnly) {
$tabs = '';
$drop = '';
} else {
$tabs = $this->renderTabs();
$drop = $this->renderDropdownTabs();
}
$close = $this->closeTab ? $this->renderCloseTab() : '';
$html = $this->baseTpl;
$html = str_replace('{TABS}', $this->renderTabs(), $html);
$html = str_replace('{DROPDOWN}', $this->renderDropdownTabs(), $html);
$html = str_replace('{CLOSE}', $this->closeTab ? $this->renderCloseTab() : '', $html);
$html = str_replace('{TABS}', $tabs, $html);
$html = str_replace('{DROPDOWN}', $drop, $html);
$html = str_replace('{CLOSE}', $close, $html);
return $html;
}
@ -318,6 +330,18 @@ EOT;
return $this->tabs;
}
/**
* Whether to hide all elements except of the close button
*
* @param bool $value
* @return Tabs fluent interface
*/
public function showOnlyCloseButton($value = true)
{
$this->closeButtonOnly = $value;
return $this;
}
/**
* Apply a Tabextension on this tabs object
*

View File

@ -259,7 +259,11 @@ class Monitoring_AlertsummaryController extends Controller
}
$out = new stdClass();
$out->avg = sprintf('%.2f', array_sum($slots) / count($slots));
if (! empty($slots)) {
$out->avg = sprintf('%.2f', array_sum($slots) / count($slots));
} else {
$out->avg = '0.0';
}
$out->last = array_shift($slots);
return $out;
@ -275,7 +279,7 @@ class Monitoring_AlertsummaryController extends Controller
$interval = $this->getInterval();
$query = $this->backend->select()->from(
'eventhistory',
'EventHistory',
array(
'host_name',
'service_description',

View File

@ -1,3 +1,7 @@
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<div class="content">
<h1><?= $title ?></h1>
<table class="objectlist">

View File

@ -1,3 +1,6 @@
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<div class="content">
<h1><?= $title ?></h1>
<?php if ((bool) $programStatus->notifications_enabled === false): ?>

View File

@ -1,5 +1,6 @@
<?php $contactHelper = $this->getHelper('ContactFlags') ?>
<div class="controls">
<?= $this->tabs ?>
<h1><?= $this->translate('Contact details') ?></h1>
<div class="circular" style="margin-top: 1em; margin-left: 2em; width: 120px; height: 120px; float: left; background-image: url('<?=
$this->href('static/gravatar', array('email' => $contact->contact_email))

View File

@ -8,6 +8,7 @@ $firstRow = !$beingExtended;
?>
<?php if (!$beingExtended): ?>
<div class="controls">
<?= $this->tabs ?>
<div style="margin: 1em;" class="dontprint">
<?= $intervalBox; ?>
</div>

View File

@ -16,6 +16,7 @@ abstract class IcingaCommand
*/
public function getName()
{
return substr_replace(end(explode('\\', get_called_class())), '', -7); // Remove 'Command' Suffix
$nsParts = explode('\\', get_called_class());
return substr_replace(end($nsParts), '', -7); // Remove 'Command' Suffix
}
}

View File

@ -723,9 +723,6 @@
}
this.icinga.ui.assignUniqueContainerIds();
if (scrollPos !== false) {
$container.scrollTop(scrollPos);
}
if (origFocus) {
$(origFocus).focus();
}
@ -733,6 +730,9 @@
// TODO: this.icinga.events.refreshContainer(container);
$container.trigger('rendered');
if (scrollPos !== false) {
$container.scrollTop(scrollPos);
}
var icinga = this.icinga;
//icinga.events.applyHandlers($container);
icinga.ui.initializeControls($container);

View File

@ -9,7 +9,7 @@ use Icinga\Logger\Logger;
use Icinga\Test\BaseTestCase;
use Icinga\Logger\Writer\FileWriter;
class LoggerTest extends BaseTestCase
class StreamWriterTest extends BaseTestCase
{
public function setUp()
{
@ -27,7 +27,7 @@ class LoggerTest extends BaseTestCase
public function testWhetherStreamWriterCreatesMissingFiles()
{
new FileWriter(new Zend_Config(array('target' => $this->target)));
new FileWriter(new Zend_Config(array('file' => $this->target)));
$this->assertFileExists($this->target, 'StreamWriter does not create missing files on initialization');
}
@ -36,8 +36,8 @@ class LoggerTest extends BaseTestCase
*/
public function testWhetherStreamWriterWritesMessages()
{
$writer = new FileWriter(new Zend_Config(array('target' => $this->target)));
$writer->log(Logger::$ERROR, 'This is a test error');
$writer = new FileWriter(new Zend_Config(array('file' => $this->target)));
$writer->log(Logger::ERROR, 'This is a test error');
$log = file_get_contents($this->target);
$this->assertContains('This is a test error', $log, 'StreamWriter does not write log messages');
}