, * Michael Stilkerich * * This file is part of RCMCardDAV. * * RCMCardDAV is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * RCMCardDAV is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RCMCardDAV. If not, see . */ declare(strict_types=1); namespace MStilkerich\RCMCardDAV; use Psr\Log\{AbstractLogger,LogLevel,InvalidArgumentException}; use Exception; use rcube; /** * @psalm-type PsrLogLevel = LogLevel::* */ class RoundcubeLogger extends AbstractLogger { /** * @var array Assigns each log level a numerical severity value. */ private const LOGLEVELS = [ LogLevel::DEBUG => 1, LogLevel::INFO => 2, LogLevel::NOTICE => 3, LogLevel::WARNING => 4, LogLevel::ERROR => 5, LogLevel::CRITICAL => 6, LogLevel::ALERT => 7, LogLevel::EMERGENCY => 8 ]; /** * @var array Assigns each short name to each log level. */ private const LOGLEVELS_SHORT = [ LogLevel::DEBUG => "DBG", LogLevel::INFO => "NFO", LogLevel::NOTICE => "NTC", LogLevel::WARNING => "WRN", LogLevel::ERROR => "ERR", LogLevel::CRITICAL => "CRT", LogLevel::ALERT => "ALT", LogLevel::EMERGENCY => "EMG" ]; /** @var string $logfile Name of the roundcube logfile that this logger logs to */ private $logfile; /** @var int<1,8> $loglevel The minimum log level for that messages are reported */ private $loglevel = self::LOGLEVELS[LogLevel::ERROR]; /** @var bool $redact If true, attempt to redact confidential information from HTTP logs */ private $redact; /** * @param PsrLogLevel $loglevel */ public function __construct(string $logfile, string $loglevel = LogLevel::ERROR, bool $redact = true) { $this->logfile = $logfile; $this->redact = $redact; try { $this->setLogLevel($loglevel); } catch (Exception $e) { $this->error($e->getMessage()); } } /** * Sets the minimum log level for that messages are logged. * * @param string $loglevel One of the Psr\Log\LogLevel constants for log levels. */ public function setLogLevel(string $loglevel): void { if (isset(self::LOGLEVELS[$loglevel])) { $this->loglevel = self::LOGLEVELS[$loglevel]; } else { throw new Exception("Attempt to set unknown loglevel for RoundcubeLogger"); } } /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message * @param array $context * @return void */ public function log($level, $message, array $context = array()): void { if (is_string($level) && isset(self::LOGLEVELS[$level])) { if (self::LOGLEVELS[$level] >= $this->loglevel) { $ctx = empty($context) ? "" : json_encode($context); $message = "$message $ctx"; if ($this->redact) { $message = $this->redactMessage($message); } $levelNumeric = self::LOGLEVELS[$level]; $levelShort = self::LOGLEVELS_SHORT[$level]; $message = "[$levelNumeric $levelShort] $message"; rcube::write_log($this->logfile, $message); } } else { throw new InvalidArgumentException("Unknown loglevel " . print_r($level, true)); } } private function redactMessage(string $message): string { // Authorization header return preg_replace( "/^Authorization: .*$/m", "Authorization: --- REDACTED BY " . self::class . " ---", $message ); } } // vim: ts=4:sw=4:expandtab:fenc=utf8:ff=unix:tw=120