summaryrefslogtreecommitdiffstats
path: root/library/Icinga/Application/Logger
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/Icinga/Application/Logger.php349
-rw-r--r--library/Icinga/Application/Logger/LogWriter.php30
-rw-r--r--library/Icinga/Application/Logger/Writer/FileWriter.php80
-rw-r--r--library/Icinga/Application/Logger/Writer/PhpWriter.php39
-rw-r--r--library/Icinga/Application/Logger/Writer/StderrWriter.php61
-rw-r--r--library/Icinga/Application/Logger/Writer/StdoutWriter.php13
-rw-r--r--library/Icinga/Application/Logger/Writer/SyslogWriter.php90
7 files changed, 662 insertions, 0 deletions
diff --git a/library/Icinga/Application/Logger.php b/library/Icinga/Application/Logger.php
new file mode 100644
index 0000000..347af44
--- /dev/null
+++ b/library/Icinga/Application/Logger.php
@@ -0,0 +1,349 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Application;
+
+use Exception;
+use Icinga\Data\ConfigObject;
+use Icinga\Application\Logger\Writer\FileWriter;
+use Icinga\Application\Logger\Writer\SyslogWriter;
+use Icinga\Exception\ConfigurationError;
+use Icinga\Exception\IcingaException;
+use Icinga\Util\Json;
+
+/**
+ * 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 static
+ */
+ protected static $instance;
+
+ /**
+ * Log writer
+ *
+ * @var \Icinga\Application\Logger\LogWriter
+ */
+ protected $writer;
+
+ /**
+ * Maximum level to emit
+ *
+ * @var int
+ */
+ protected $level;
+
+ /**
+ * Error messages to be displayed prior to any other log message
+ *
+ * @var array
+ */
+ protected $configErrors = array();
+
+ /**
+ * Create a new logger object
+ *
+ * @param ConfigObject $config
+ *
+ * @throws ConfigurationError If the logging configuration directive 'log' is missing or if the logging level is
+ * not defined
+ */
+ public function __construct(ConfigObject $config)
+ {
+ if ($config->log === null) {
+ throw new ConfigurationError('Required logging configuration directive \'log\' missing');
+ }
+
+ $this->setLevel($config->get('level', static::ERROR));
+
+ if (strtolower($config->get('log', 'syslog')) !== 'none') {
+ $this->writer = $this->createWriter($config);
+ }
+ }
+
+ /**
+ * Set the logging level to use
+ *
+ * @param mixed $level
+ *
+ * @return $this
+ *
+ * @throws ConfigurationError In case the given level is invalid
+ */
+ public function setLevel($level)
+ {
+ if (is_numeric($level)) {
+ $level = (int) $level;
+ if (! isset(static::$levels[$level])) {
+ throw new ConfigurationError(
+ 'Can\'t set logging level %d. Logging level is invalid. Use one of %s or one of the'
+ . ' Logger\'s constants.',
+ $level,
+ implode(', ', array_keys(static::$levels))
+ );
+ }
+
+ $this->level = $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 invalid. Use one of %s.',
+ $level,
+ implode(', ', array_keys($levels))
+ );
+ }
+
+ $this->level = $levels[$level];
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return the logging level being used
+ *
+ * @return int
+ */
+ public function getLevel()
+ {
+ return $this->level;
+ }
+
+ /**
+ * Register the given message as config error
+ *
+ * Config errors are logged every time a log message is being logged.
+ *
+ * @param mixed $arg,... A string, exception or format-string + substitutions
+ *
+ * @return $this
+ */
+ public function registerConfigError()
+ {
+ if (func_num_args() > 0) {
+ $this->configErrors[] = static::formatMessage(func_get_args());
+ }
+
+ return $this;
+ }
+
+ /**
+ * Create a new logger object
+ *
+ * @param ConfigObject $config
+ *
+ * @return static
+ */
+ public static function create(ConfigObject $config)
+ {
+ static::$instance = new static($config);
+ return static::$instance;
+ }
+
+ /**
+ * Create a log writer
+ *
+ * @param ConfigObject $config The configuration to initialize the writer with
+ *
+ * @return \Icinga\Application\Logger\LogWriter The requested log writer
+ * @throws ConfigurationError If the requested writer cannot be found
+ */
+ protected function createWriter(ConfigObject $config)
+ {
+ $class = 'Icinga\\Application\\Logger\\Writer\\' . ucfirst(strtolower($config->log)) . 'Writer';
+ if (! class_exists($class)) {
+ throw new ConfigurationError(
+ 'Cannot find log writer of type "%s"',
+ $config->log
+ );
+ }
+ return new $class($config);
+ }
+
+ /**
+ * Log a message
+ *
+ * @param int $level The logging level
+ * @param string $message The log message
+ */
+ public function log($level, $message)
+ {
+ if ($this->writer !== null && $this->level <= $level) {
+ foreach ($this->configErrors as $error_message) {
+ $this->writer->log(static::ERROR, $error_message);
+ }
+
+ $this->writer->log($level, $message);
+ }
+ }
+
+ /**
+ * Return a string representation of the passed arguments
+ *
+ * This method provides three different processing techniques:
+ * - If the only passed argument is a string it is returned unchanged
+ * - If the only passed argument is an exception it is formatted as follows:
+ * <name> in <file>:<line> with message: <message>[ <- <name> ...]
+ * - If multiple arguments are passed the first is interpreted as format-string
+ * that gets substituted with the remaining ones which can be of any type
+ *
+ * @param array $arguments The arguments to format
+ *
+ * @return string The formatted result
+ */
+ protected static function formatMessage(array $arguments)
+ {
+ if (count($arguments) === 1) {
+ $message = $arguments[0];
+
+ if ($message instanceof Exception) {
+ $messages = array();
+ $error = $message;
+ do {
+ $messages[] = IcingaException::describe($error);
+ } while ($error = $error->getPrevious());
+ $message = implode(' <- ', $messages);
+ }
+
+ return $message;
+ }
+
+ return vsprintf(
+ array_shift($arguments),
+ array_map(
+ function ($a) {
+ return is_string($a) ? $a : ($a instanceof Exception
+ ? IcingaException::describe($a)
+ : Json::encode($a));
+ },
+ $arguments
+ )
+ );
+ }
+
+ /**
+ * Log a message with severity ERROR
+ *
+ * @param mixed $arg,... A string, exception or format-string + substitutions
+ */
+ public static function error()
+ {
+ if (static::$instance !== null && func_num_args() > 0) {
+ static::$instance->log(static::ERROR, static::formatMessage(func_get_args()));
+ }
+ }
+
+ /**
+ * Log a message with severity WARNING
+ *
+ * @param mixed $arg,... A string, exception or format-string + substitutions
+ */
+ public static function warning()
+ {
+ if (static::$instance !== null && func_num_args() > 0) {
+ static::$instance->log(static::WARNING, static::formatMessage(func_get_args()));
+ }
+ }
+
+ /**
+ * Log a message with severity INFO
+ *
+ * @param mixed $arg,... A string, exception or format-string + substitutions
+ */
+ public static function info()
+ {
+ if (static::$instance !== null && func_num_args() > 0) {
+ static::$instance->log(static::INFO, static::formatMessage(func_get_args()));
+ }
+ }
+
+ /**
+ * Log a message with severity DEBUG
+ *
+ * @param mixed $arg,... A string, exception or format-string + substitutions
+ */
+ public static function debug()
+ {
+ if (static::$instance !== null && func_num_args() > 0) {
+ static::$instance->log(static::DEBUG, static::formatMessage(func_get_args()));
+ }
+ }
+
+ /**
+ * Get the log writer to use
+ *
+ * @return \Icinga\Application\Logger\LogWriter
+ */
+ public function getWriter()
+ {
+ return $this->writer;
+ }
+
+ /**
+ * Is the logger writing to Syslog?
+ *
+ * @return bool
+ */
+ public static function writesToSyslog()
+ {
+ return static::$instance && static::$instance->getWriter() instanceof SyslogWriter;
+ }
+
+ /**
+ * Is the logger writing to a file?
+ *
+ * @return bool
+ */
+ public static function writesToFile()
+ {
+ return static::$instance && static::$instance->getWriter() instanceof FileWriter;
+ }
+
+ /**
+ * Get this' instance
+ *
+ * @return static
+ */
+ public static function getInstance()
+ {
+ return static::$instance;
+ }
+}
diff --git a/library/Icinga/Application/Logger/LogWriter.php b/library/Icinga/Application/Logger/LogWriter.php
new file mode 100644
index 0000000..019bdad
--- /dev/null
+++ b/library/Icinga/Application/Logger/LogWriter.php
@@ -0,0 +1,30 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Application\Logger;
+
+use Icinga\Data\ConfigObject;
+
+/**
+ * Abstract class for writers that write messages to a log
+ */
+abstract class LogWriter
+{
+ /**
+ * @var ConfigObject
+ */
+ protected $config;
+
+ /**
+ * Create a new log writer initialized with the given configuration
+ */
+ public function __construct(ConfigObject $config)
+ {
+ $this->config = $config;
+ }
+
+ /**
+ * Log a message with the given severity
+ */
+ abstract public function log($severity, $message);
+}
diff --git a/library/Icinga/Application/Logger/Writer/FileWriter.php b/library/Icinga/Application/Logger/Writer/FileWriter.php
new file mode 100644
index 0000000..6b4ed54
--- /dev/null
+++ b/library/Icinga/Application/Logger/Writer/FileWriter.php
@@ -0,0 +1,80 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Application\Logger\Writer;
+
+use Exception;
+use Icinga\Data\ConfigObject;
+use Icinga\Application\Logger;
+use Icinga\Application\Logger\LogWriter;
+use Icinga\Exception\ConfigurationError;
+use Icinga\Util\File;
+
+/**
+ * Log to a file
+ */
+class FileWriter extends LogWriter
+{
+ /**
+ * Path to the file
+ *
+ * @var string
+ */
+ protected $file;
+
+ /**
+ * Create a new file log writer
+ *
+ * @param ConfigObject $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(ConfigObject $config)
+ {
+ if ($config->file === null) {
+ throw new ConfigurationError('Required logging configuration directive \'file\' missing');
+ }
+ $this->file = $config->file;
+
+ if (substr($this->file, 0, 6) !== 'php://' && ! file_exists(dirname($this->file))) {
+ throw new ConfigurationError(
+ 'Log path "%s" does not exist',
+ dirname($this->file)
+ );
+ }
+
+ try {
+ $this->write(''); // Avoid to handle such errors on every write access
+ } catch (Exception $e) {
+ throw new ConfigurationError(
+ 'Cannot write to log file "%s" (%s)',
+ $this->file,
+ $e->getMessage()
+ );
+ }
+ }
+
+ /**
+ * Log a message
+ *
+ * @param int $level The logging level
+ * @param string $message The log message
+ */
+ public function log($level, $message)
+ {
+ $this->write(date('c') . ' - ' . Logger::$levels[$level] . ' - ' . $message . PHP_EOL);
+ }
+
+ /**
+ * Write a message to the log
+ *
+ * @param string $message
+ */
+ protected function write($message)
+ {
+ $file = new File($this->file, 'a');
+ $file->fwrite($message);
+ $file->fflush();
+ }
+}
diff --git a/library/Icinga/Application/Logger/Writer/PhpWriter.php b/library/Icinga/Application/Logger/Writer/PhpWriter.php
new file mode 100644
index 0000000..dedb2bd
--- /dev/null
+++ b/library/Icinga/Application/Logger/Writer/PhpWriter.php
@@ -0,0 +1,39 @@
+<?php
+/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Application\Logger\Writer;
+
+use Icinga\Application\Logger;
+use Icinga\Application\Logger\LogWriter;
+use Icinga\Data\ConfigObject;
+use Icinga\Exception\NotWritableError;
+
+/**
+ * Log to the webserver log, a file or syslog
+ *
+ * @see https://secure.php.net/manual/en/errorfunc.configuration.php#ini.error-log
+ */
+class PhpWriter extends LogWriter
+{
+ /**
+ * Prefix to prepend to each message
+ *
+ * @var string
+ */
+ protected $ident;
+
+ public function __construct(ConfigObject $config)
+ {
+ parent::__construct($config);
+ $this->ident = $config->get('application', 'icingaweb2');
+ }
+
+ public function log($severity, $message)
+ {
+ if (ini_get('error_log') === 'syslog') {
+ $message = str_replace("\n", ' ', $message);
+ }
+
+ error_log($this->ident . ': ' . Logger::$levels[$severity] . ' - ' . $message);
+ }
+}
diff --git a/library/Icinga/Application/Logger/Writer/StderrWriter.php b/library/Icinga/Application/Logger/Writer/StderrWriter.php
new file mode 100644
index 0000000..2f35ff2
--- /dev/null
+++ b/library/Icinga/Application/Logger/Writer/StderrWriter.php
@@ -0,0 +1,61 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Application\Logger\Writer;
+
+use Icinga\Cli\Screen;
+use Icinga\Application\Logger;
+use Icinga\Application\Logger\LogWriter;
+
+/**
+ * Class to write log messages to STDERR
+ */
+class StderrWriter extends LogWriter
+{
+ /**
+ * The current Screen in use
+ *
+ * @var Screen
+ */
+ protected $screen;
+
+ /**
+ * Return the current Screen
+ *
+ * @return Screen
+ */
+ protected function screen()
+ {
+ if ($this->screen === null) {
+ $this->screen = Screen::instance(STDERR);
+ }
+
+ return $this->screen;
+ }
+
+ /**
+ * Log a message with the given severity
+ *
+ * @param int $severity The severity to use
+ * @param string $message The message to log
+ */
+ public function log($severity, $message)
+ {
+ switch ($severity) {
+ case Logger::ERROR:
+ $color = 'red';
+ break;
+ case Logger::WARNING:
+ $color = 'yellow';
+ break;
+ case Logger::INFO:
+ $color = 'green';
+ break;
+ case Logger::DEBUG:
+ $color = 'blue';
+ break;
+ }
+
+ file_put_contents('php://stderr', $this->screen()->colorize($message, $color) . "\n");
+ }
+}
diff --git a/library/Icinga/Application/Logger/Writer/StdoutWriter.php b/library/Icinga/Application/Logger/Writer/StdoutWriter.php
new file mode 100644
index 0000000..a6f43e5
--- /dev/null
+++ b/library/Icinga/Application/Logger/Writer/StdoutWriter.php
@@ -0,0 +1,13 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Application\Logger\Writer;
+
+/**
+ * Deprecated, compat only.
+ *
+ * Use Icinga\Application\Logger\Writer\StderrWriter instead.
+ */
+class StdoutWriter extends StderrWriter
+{
+}
diff --git a/library/Icinga/Application/Logger/Writer/SyslogWriter.php b/library/Icinga/Application/Logger/Writer/SyslogWriter.php
new file mode 100644
index 0000000..93efc2a
--- /dev/null
+++ b/library/Icinga/Application/Logger/Writer/SyslogWriter.php
@@ -0,0 +1,90 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Application\Logger\Writer;
+
+use Icinga\Data\ConfigObject;
+use Icinga\Application\Logger;
+use Icinga\Application\Logger\LogWriter;
+use Icinga\Exception\ConfigurationError;
+
+/**
+ * Log to the syslog service
+ */
+class SyslogWriter extends LogWriter
+{
+ /**
+ * Syslog facility
+ *
+ * @var int
+ */
+ protected $facility;
+
+ /**
+ * Prefix to prepend to each message
+ *
+ * @var string
+ */
+ protected $ident;
+
+ /**
+ * Known syslog facilities
+ *
+ * @var array
+ */
+ public static $facilities = array(
+ 'user' => LOG_USER,
+ 'local0' => LOG_LOCAL0,
+ 'local1' => LOG_LOCAL1,
+ 'local2' => LOG_LOCAL2,
+ 'local3' => LOG_LOCAL3,
+ 'local4' => LOG_LOCAL4,
+ 'local5' => LOG_LOCAL5,
+ 'local6' => LOG_LOCAL6,
+ 'local7' => LOG_LOCAL7
+ );
+
+ /**
+ * 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 ConfigObject $config
+ */
+ public function __construct(ConfigObject $config)
+ {
+ $this->ident = $config->get('application', 'icingaweb2');
+
+ $configuredFacility = $config->get('facility', 'user');
+ if (! isset(static::$facilities[$configuredFacility])) {
+ throw new ConfigurationError(
+ 'Invalid logging facility: "%s" (expected one of: %s)',
+ $configuredFacility,
+ implode(', ', array_keys(static::$facilities))
+ );
+ }
+ $this->facility = static::$facilities[$configuredFacility];
+ }
+
+ /**
+ * Log a message
+ *
+ * @param int $level The logging level
+ * @param string $message The log message
+ */
+ public function log($level, $message)
+ {
+ openlog($this->ident, LOG_PID, $this->facility);
+ syslog(static::$severityMap[$level], str_replace("\n", ' ', $message));
+ }
+}