mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 15:34:12 +01:00
835 lines
19 KiB
PHP
835 lines
19 KiB
PHP
<?php
|
|
// Copyright (C) 2010-2021 Combodo SARL
|
|
//
|
|
// This file is part of iTop.
|
|
//
|
|
// iTop is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// iTop 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 Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
|
|
/**
|
|
* @since 2.7.0 N°2518 N°2793
|
|
*/
|
|
interface iLogFileNameBuilder
|
|
{
|
|
/**
|
|
* @param string $sLogFileFullPath full path name for the log file
|
|
*/
|
|
public function __construct($sLogFileFullPath = null);
|
|
|
|
/**
|
|
* @return string log file path we will write new log entry to
|
|
*/
|
|
public function GetLogFilePath();
|
|
}
|
|
|
|
class DefaultLogFileNameBuilder implements iLogFileNameBuilder
|
|
{
|
|
private $sLogFileFullPath;
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function __construct($sLogFileFullPath = null)
|
|
{
|
|
$this->sLogFileFullPath = $sLogFileFullPath;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function GetLogFilePath()
|
|
{
|
|
return $this->sLogFileFullPath;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a suffix to the filename
|
|
*
|
|
* @since 2.7.0 N°2518 N°2793
|
|
*/
|
|
abstract class RotatingLogFileNameBuilder implements iLogFileNameBuilder
|
|
{
|
|
/**
|
|
* Test is done each time to cover edge case like session beginning at 23:59 and ending at 00:01
|
|
* We are caching the file mtime though
|
|
* @var array with full file path as key and DateTime (file last modification time) as value
|
|
*/
|
|
protected static $aLogFileLastModified = array();
|
|
/** @var string */
|
|
protected $sLogFileFullPath;
|
|
/** @var string */
|
|
protected $sFilePath;
|
|
/** @var string */
|
|
protected $sFileBaseName;
|
|
/** @var string */
|
|
protected $sFileExtension;
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function __construct($sLogFileFullPath = null)
|
|
{
|
|
$this->sLogFileFullPath = $sLogFileFullPath;
|
|
}
|
|
|
|
protected function GetLastModifiedDateForFile()
|
|
{
|
|
if (isset(static::$aLogFileLastModified[$this->sLogFileFullPath]))
|
|
{
|
|
return static::$aLogFileLastModified[$this->sLogFileFullPath];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected function SetLastModifiedDateForFile($oDateTime)
|
|
{
|
|
static::$aLogFileLastModified[$this->sLogFileFullPath] = $oDateTime;
|
|
}
|
|
|
|
/**
|
|
* Need to be called when the file is rotated : actually the next call will need to check on the real date modified instead of using
|
|
* the previously cached value !
|
|
*/
|
|
public function ResetLastModifiedDateForFile()
|
|
{
|
|
static::$aLogFileLastModified[$this->sLogFileFullPath] = null;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*
|
|
* Doing the check before opening and writing the log file. There is also a iProcess but cron can be disabled...
|
|
*
|
|
* @see \LogFileRotationProcess the iProcess impl
|
|
*/
|
|
public function GetLogFilePath()
|
|
{
|
|
$this->CheckAndRotateLogFile();
|
|
return $this->sLogFileFullPath;
|
|
}
|
|
|
|
/**
|
|
* Check log last date modified. If too old then rotate the log file (move it to a new name with a suffix)
|
|
*
|
|
* @uses filemtime() to get log file date last modified
|
|
*/
|
|
public function CheckAndRotateLogFile()
|
|
{
|
|
$oConfig = utils::GetConfig();
|
|
utils::InitTimeZone($oConfig);
|
|
|
|
if ($this->GetLastModifiedDateForFile() === null)
|
|
{
|
|
if (!$this->IsLogFileExists())
|
|
{
|
|
return;
|
|
}
|
|
|
|
$iLogDateLastModifiedTimeStamp = filemtime($this->sLogFileFullPath);
|
|
if ($iLogDateLastModifiedTimeStamp === false)
|
|
{
|
|
return;
|
|
}
|
|
$oDateTime = DateTime::createFromFormat('U', $iLogDateLastModifiedTimeStamp);
|
|
$sItopTimeZone = $oConfig->Get('timezone');
|
|
$timezone = new DateTimeZone($sItopTimeZone);
|
|
$oDateTime->setTimezone($timezone);
|
|
$this->SetLastModifiedDateForFile($oDateTime);
|
|
}
|
|
|
|
$oNow = new DateTime();
|
|
$bShouldRotate = $this->ShouldRotate($this->GetLastModifiedDateForFile(), $oNow);
|
|
if (!$bShouldRotate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
$this->RotateLogFile($this->GetLastModifiedDateForFile());
|
|
}
|
|
|
|
/**
|
|
* Rotate current log file
|
|
*
|
|
* @param DateTime $oLogFileLastModified date when the log file was last modified
|
|
*
|
|
* @uses \iTopMutex instead of flock as doing a rename on a file with a flock cause an error on PHP 5.6.40 Windows (ok on 7.3.15 though)
|
|
* @uses GetRotatedFileName to get rotated file name
|
|
*/
|
|
protected function RotateLogFile($oLogFileLastModified)
|
|
{
|
|
if (!$this->IsLogFileExists()) // extra check, but useful for cron also !
|
|
{
|
|
return;
|
|
}
|
|
|
|
$oLock = null;
|
|
try
|
|
{
|
|
$oLock = new iTopMutex('log_rotation_'.$this->sLogFileFullPath);
|
|
$oLock->Lock();
|
|
if (!$this->IsLogFileExists()) // extra extra check if we were blocked and another process moved the file in the meantime
|
|
{
|
|
$oLock->Unlock();
|
|
return;
|
|
}
|
|
$this->ResetLastModifiedDateForFile();
|
|
$sNewLogFileName = $this->GetRotatedFileName($oLogFileLastModified);
|
|
rename($this->sLogFileFullPath, $sNewLogFileName);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
// nothing to do, cannot log... file will be renamed on the next call O:)
|
|
return;
|
|
}
|
|
finally
|
|
{
|
|
if (!is_null($oLock)) { $oLock->Unlock();}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param DateTime $oLogFileLastModified date when the log file was last modified
|
|
*
|
|
* @return string the full path of the rotated log file
|
|
* @uses static::$oLogFileLastModified
|
|
* @uses GetFileSuffix
|
|
*/
|
|
public function GetRotatedFileName($oLogFileLastModified)
|
|
{
|
|
$aPathParts = pathinfo($this->sLogFileFullPath);
|
|
$this->sFilePath = $aPathParts['dirname'];
|
|
$this->sFileBaseName = $aPathParts['filename'];
|
|
$this->sFileExtension = $aPathParts['extension'];
|
|
|
|
$sFileSuffix = $this->GetFileSuffix($oLogFileLastModified);
|
|
|
|
return $this->sFilePath.DIRECTORY_SEPARATOR
|
|
.$this->sFileBaseName
|
|
.'.'.$sFileSuffix
|
|
.'.'.$this->sFileExtension;
|
|
}
|
|
|
|
/**
|
|
* @return bool true if file exists and is readable
|
|
*/
|
|
public function IsLogFileExists()
|
|
{
|
|
if (!file_exists($this->sLogFileFullPath))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!is_readable($this->sLogFileFullPath))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* **Warning :** both DateTime params must have the same timezone set ! Should use the iTop timezone ('timezone' config parameter)
|
|
*
|
|
* @param DateTime $oLogFileLastModified date when the log file was last modified
|
|
* @param DateTime $oNow date/time of the log we want to write
|
|
*
|
|
* @return bool true if the file has older informations and we need to move it to an archive (rotate), false if we don't have to
|
|
*/
|
|
abstract public function ShouldRotate($oLogFileLastModified, $oNow);
|
|
|
|
/**
|
|
* @param DateTime $oDate log file last modification date
|
|
*
|
|
* @return string suffix for the rotated log file
|
|
*/
|
|
abstract protected function GetFileSuffix($oDate);
|
|
|
|
/**
|
|
* @see \LogFileRotationProcess
|
|
*
|
|
* @param \DateTime $oNow current date
|
|
*
|
|
* @return DateTime time when the cron process should run
|
|
*/
|
|
abstract public function GetCronProcessNextOccurrence(DateTime $oNow);
|
|
}
|
|
|
|
/**
|
|
* @since 2.7.0 N°2518 N°2793
|
|
*/
|
|
class DailyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
|
|
{
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
protected function GetFileSuffix($oDate)
|
|
{
|
|
return $oDate->format('Y-m-d');
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function ShouldRotate($oLogFileLastModified, $oNow)
|
|
{
|
|
$iLogYear = $oLogFileLastModified->format('Y');
|
|
$iLogDay = $oLogFileLastModified->format('z');
|
|
$iNowYear = $oNow->format('Y');
|
|
$iNowDay = $oNow->format('z');
|
|
|
|
if ($iLogYear !== $iNowYear)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ($iLogDay !== $iNowDay)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function GetCronProcessNextOccurrence(DateTime $oNow)
|
|
{
|
|
$oOccurrence = clone $oNow;
|
|
$oOccurrence->modify('tomorrow midnight');
|
|
|
|
return $oOccurrence;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @since 2.7.0 N°2518 N°2793
|
|
*/
|
|
class WeeklyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
|
|
{
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
protected function GetFileSuffix($oDate)
|
|
{
|
|
$sWeekYear = $oDate->format('o');
|
|
$sWeekNumber = $oDate->format('W');
|
|
|
|
return $sWeekYear.'-week'.$sWeekNumber;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function ShouldRotate($oLogFileLastModified, $oNow)
|
|
{
|
|
$iLogYear = $oLogFileLastModified->format('Y');
|
|
$iLogWeek = $oLogFileLastModified->format('W');
|
|
$iNowYear = $oNow->format('Y');
|
|
$iNowWeek = $oNow->format('W');
|
|
|
|
if ($iLogYear !== $iNowYear)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ($iLogWeek !== $iNowWeek)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function GetCronProcessNextOccurrence(DateTime $oNow)
|
|
{
|
|
$oOccurrence = clone $oNow;
|
|
$oOccurrence->modify('Monday next week midnight');
|
|
|
|
return $oOccurrence;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @since 2.7.0 N°2820
|
|
*/
|
|
class MonthlyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
|
|
{
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function ShouldRotate($oLogFileLastModified, $oNow)
|
|
{
|
|
$iLogYear = $oLogFileLastModified->format('Y');
|
|
$iLogMonth = $oLogFileLastModified->format('n');
|
|
$iNowYear = $oNow->format('Y');
|
|
$iNowMonth = $oNow->format('n');
|
|
|
|
if ($iLogYear !== $iNowYear)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ($iLogMonth !== $iNowMonth)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
protected function GetFileSuffix($oDate)
|
|
{
|
|
$sWeekYear = $oDate->format('o');
|
|
$sWeekNumber = $oDate->format('m');
|
|
|
|
return $sWeekYear.'-month'.$sWeekNumber;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function GetCronProcessNextOccurrence(DateTime $oNow)
|
|
{
|
|
$oOccurrence = clone $oNow;
|
|
$oOccurrence->modify('first day of next month midnight');
|
|
|
|
return $oOccurrence;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @since 2.7.0 N°2518
|
|
*/
|
|
class LogFileNameBuilderFactory
|
|
{
|
|
/**
|
|
* Uses the 'log_filename_builder_impl' config parameter
|
|
*
|
|
* @param string $sFileFullPath
|
|
*
|
|
* @return \iLogFileNameBuilder
|
|
* @throws \ConfigException
|
|
* @throws \CoreException
|
|
*/
|
|
public static function GetInstance($sFileFullPath)
|
|
{
|
|
$oConfig = utils::GetConfig();
|
|
$sFileNameBuilderImpl = $oConfig->Get('log_filename_builder_impl');
|
|
if (!is_a($sFileNameBuilderImpl, iLogFileNameBuilder::class, true))
|
|
{
|
|
$sFileNameBuilderImpl = 'DefaultLogFileNameBuilder';
|
|
}
|
|
|
|
return new $sFileNameBuilderImpl($sFileFullPath);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* File logging
|
|
*
|
|
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
|
* @license http://opensource.org/licenses/AGPL-3.0
|
|
* @since 2.7.0 N°2518 N°2793 file log rotation
|
|
*/
|
|
class FileLog
|
|
{
|
|
protected $oFileNameBuilder;
|
|
|
|
/**
|
|
* FileLog constructor.
|
|
*
|
|
* @param string $sFileName
|
|
*
|
|
* @throws \ConfigException
|
|
* @throws \CoreException
|
|
*/
|
|
public function __construct($sFileName = '')
|
|
{
|
|
$this->oFileNameBuilder = LogFileNameBuilderFactory::GetInstance($sFileName);
|
|
}
|
|
|
|
public function Error($sText, $sChannel = '', $aContext = array())
|
|
{
|
|
$this->Write($sText, __FUNCTION__, $sChannel, $aContext);
|
|
}
|
|
|
|
public function Warning($sText, $sChannel = '', $aContext = array())
|
|
{
|
|
$this->Write($sText, __FUNCTION__, $sChannel, $aContext);
|
|
}
|
|
|
|
public function Info($sText, $sChannel = '', $aContext = array())
|
|
{
|
|
$this->Write($sText, __FUNCTION__, $sChannel, $aContext);
|
|
}
|
|
|
|
public function Ok($sText, $sChannel = '', $aContext = array())
|
|
{
|
|
$this->Write($sText, __FUNCTION__, $sChannel, $aContext);
|
|
}
|
|
|
|
public function Debug($sText, $sChannel = '', $aContext = array())
|
|
{
|
|
$this->Write($sText, __FUNCTION__, $sChannel, $aContext);
|
|
}
|
|
|
|
public function Trace($sText, $sChannel = '', $aContext = array())
|
|
{
|
|
$this->Write($sText, __FUNCTION__, $sChannel, $aContext);
|
|
}
|
|
|
|
|
|
protected function Write($sText, $sLevel = '', $sChannel = '', $aContext = array())
|
|
{
|
|
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7).' | ');
|
|
$sTextSuffix = empty($sChannel) ? '' : " | $sChannel";
|
|
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
|
|
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
|
|
|
|
if (empty($sLogFilePath))
|
|
{
|
|
return;
|
|
}
|
|
|
|
$hLogFile = @fopen($sLogFilePath, 'a');
|
|
if ($hLogFile !== false)
|
|
{
|
|
flock($hLogFile, LOCK_EX);
|
|
$sDate = date('Y-m-d H:i:s');
|
|
if (empty($aContext))
|
|
{
|
|
fwrite($hLogFile, "$sDate | $sText\n");
|
|
}
|
|
else
|
|
{
|
|
$sContext = var_export($aContext, true);
|
|
fwrite($hLogFile, "$sDate | $sText\n$sContext\n");
|
|
}
|
|
fflush($hLogFile);
|
|
flock($hLogFile, LOCK_UN);
|
|
fclose($hLogFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
abstract class LogAPI
|
|
{
|
|
const CHANNEL_DEFAULT = '';
|
|
|
|
const LEVEL_ERROR = 'Error';
|
|
const LEVEL_WARNING = 'Warning';
|
|
const LEVEL_INFO = 'Info';
|
|
const LEVEL_OK = 'Ok';
|
|
const LEVEL_DEBUG = 'Debug';
|
|
const LEVEL_TRACE = 'Trace';
|
|
/**
|
|
* @var string default log level, can be overrided
|
|
* @see GetMinLogLevel
|
|
* @since 2.7.1 N°2977
|
|
*/
|
|
const LEVEL_DEFAULT = self::LEVEL_OK;
|
|
|
|
protected static $aLevelsPriority = array(
|
|
self::LEVEL_ERROR => 400,
|
|
self::LEVEL_WARNING => 300,
|
|
self::LEVEL_INFO => 200,
|
|
self::LEVEL_OK => 200,
|
|
self::LEVEL_DEBUG => 100,
|
|
self::LEVEL_TRACE => 50,
|
|
);
|
|
|
|
protected static $m_oMockMetaModelConfig = null;
|
|
|
|
public static function Enable($sTargetFile)
|
|
{
|
|
// m_oFileLog is not defined as a class attribute so that each impl will have its own
|
|
static::$m_oFileLog = new FileLog($sTargetFile);
|
|
}
|
|
|
|
public static function MockStaticObjects($oFileLog, $oMetaModelConfig=null)
|
|
{
|
|
static::$m_oFileLog = $oFileLog;
|
|
static::$m_oMockMetaModelConfig = $oMetaModelConfig;
|
|
}
|
|
|
|
public static function Error($sMessage, $sChannel = null, $aContext = array())
|
|
{
|
|
static::Log(self::LEVEL_ERROR, $sMessage, $sChannel, $aContext);
|
|
}
|
|
|
|
public static function Warning($sMessage, $sChannel = null, $aContext = array())
|
|
{
|
|
static::Log(self::LEVEL_WARNING, $sMessage, $sChannel, $aContext);
|
|
}
|
|
|
|
public static function Info($sMessage, $sChannel = null, $aContext = array())
|
|
{
|
|
static::Log(self::LEVEL_INFO, $sMessage, $sChannel, $aContext);
|
|
}
|
|
|
|
public static function Ok($sMessage, $sChannel = null, $aContext = array())
|
|
{
|
|
static::Log(self::LEVEL_OK, $sMessage, $sChannel, $aContext);
|
|
}
|
|
|
|
public static function Debug($sMessage, $sChannel = null, $aContext = array())
|
|
{
|
|
static::Log(self::LEVEL_DEBUG, $sMessage, $sChannel, $aContext);
|
|
}
|
|
|
|
public static function Trace($sMessage, $sChannel = null, $aContext = array())
|
|
{
|
|
static::Log(self::LEVEL_TRACE, $sMessage, $sChannel, $aContext);
|
|
}
|
|
|
|
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
|
|
{
|
|
if (! static::$m_oFileLog)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (! isset(self::$aLevelsPriority[$sLevel]))
|
|
{
|
|
IssueLog::Error("invalid log level '{$sLevel}'");
|
|
return;
|
|
}
|
|
|
|
if (is_null($sChannel))
|
|
{
|
|
$sChannel = static::CHANNEL_DEFAULT;
|
|
}
|
|
|
|
$sMinLogLevel = self::GetMinLogLevel($sChannel);
|
|
|
|
if ($sMinLogLevel === false || $sMinLogLevel === 'false')
|
|
{
|
|
return;
|
|
}
|
|
if (is_string($sMinLogLevel))
|
|
{
|
|
if (! isset(self::$aLevelsPriority[$sMinLogLevel]))
|
|
{
|
|
throw new Exception("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
|
|
}
|
|
elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel])
|
|
{
|
|
//priority too low regarding the conf, do not log this
|
|
return;
|
|
}
|
|
}
|
|
|
|
static::$m_oFileLog->$sLevel($sMessage, $sChannel, $aContext);
|
|
}
|
|
|
|
/**
|
|
* @param $sChannel
|
|
*
|
|
* @return string one of the LEVEL_* const value
|
|
* @uses \LogAPI::LEVEL_DEFAULT
|
|
*/
|
|
private static function GetMinLogLevel($sChannel)
|
|
{
|
|
$oConfig = (static::$m_oMockMetaModelConfig !== null) ? static::$m_oMockMetaModelConfig : \MetaModel::GetConfig();
|
|
if (!$oConfig instanceof Config)
|
|
{
|
|
return static::LEVEL_DEFAULT;
|
|
}
|
|
|
|
$sLogLevelMin = $oConfig->Get('log_level_min');
|
|
|
|
if (empty($sLogLevelMin))
|
|
{
|
|
return static::LEVEL_DEFAULT;
|
|
}
|
|
|
|
if (!is_array($sLogLevelMin))
|
|
{
|
|
return $sLogLevelMin;
|
|
}
|
|
|
|
if (isset($sLogLevelMin[$sChannel]))
|
|
{
|
|
return $sLogLevelMin[$sChannel];
|
|
}
|
|
|
|
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT]))
|
|
{
|
|
return $sLogLevelMin[$sChannel];
|
|
}
|
|
|
|
return static::LEVEL_DEFAULT;
|
|
}
|
|
|
|
}
|
|
|
|
class SetupLog extends LogAPI
|
|
{
|
|
const CHANNEL_DEFAULT = 'SetupLog';
|
|
/**
|
|
* @inheritDoc
|
|
*
|
|
* As this object is used during setup, without any conf file available, customizing the level can be done by changing this constant !
|
|
*/
|
|
const LEVEL_DEFAULT = self::LEVEL_INFO;
|
|
|
|
protected static $m_oFileLog = null;
|
|
}
|
|
|
|
class IssueLog extends LogAPI
|
|
{
|
|
const CHANNEL_DEFAULT = 'IssueLog';
|
|
|
|
protected static $m_oFileLog = null;
|
|
}
|
|
|
|
class ToolsLog extends LogAPI
|
|
{
|
|
const CHANNEL_DEFAULT = 'ToolsLog';
|
|
|
|
protected static $m_oFileLog = null;
|
|
}
|
|
|
|
/**
|
|
* @see \CMDBSource::LogDeadLock()
|
|
* @since 2.7.1
|
|
*/
|
|
class DeadLockLog extends LogAPI
|
|
{
|
|
const CHANNEL_WAIT_TIMEOUT = 'Deadlock-WaitTimeout';
|
|
const CHANNEL_DEADLOCK_FOUND = 'Deadlock-Found';
|
|
const CHANNEL_DEFAULT = self::CHANNEL_WAIT_TIMEOUT;
|
|
|
|
/** @var \FileLog we want our own instance ! */
|
|
protected static $m_oFileLog = null;
|
|
|
|
public static function Enable($sTargetFile = null)
|
|
{
|
|
if (empty($sTargetFile))
|
|
{
|
|
$sTargetFile = APPROOT.'log/deadlocks.log';
|
|
}
|
|
parent::Enable($sTargetFile);
|
|
}
|
|
|
|
private static function GetChannelFromMysqlErrorNo($iMysqlErrorNo)
|
|
{
|
|
switch ($iMysqlErrorNo)
|
|
{
|
|
case 1205:
|
|
return self::CHANNEL_WAIT_TIMEOUT;
|
|
break;
|
|
case 1213:
|
|
return self::CHANNEL_DEADLOCK_FOUND;
|
|
break;
|
|
default:
|
|
return self::CHANNEL_DEFAULT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param int $iMySQLErrNo will be converted to channel using {@link GetChannelFromMysqlErrorNo}
|
|
* @param string $sMessage
|
|
* @param null $iMysqlErroNo
|
|
* @param array $aContext
|
|
*
|
|
* @throws \Exception
|
|
*/
|
|
public static function Log($iMySQLErrNo, $sMessage, $iMysqlErroNo = null, $aContext = array())
|
|
{
|
|
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErroNo);
|
|
parent::Log($iMySQLErrNo, $sMessage, $sChannel, $aContext);
|
|
}
|
|
}
|
|
|
|
|
|
class LogFileRotationProcess implements iScheduledProcess
|
|
{
|
|
/**
|
|
* Cannot get this list from anywhere as log file name is provided by the caller using LogAPI::Enable
|
|
* @var string[]
|
|
*/
|
|
const LOGFILES_TO_ROTATE = array(
|
|
'setup.log',
|
|
'error.log',
|
|
'tools.log',
|
|
'itop-fence.log',
|
|
);
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function Process($iUnixTimeLimit)
|
|
{
|
|
$sLogFileNameBuilder = $this->GetLogFileNameBuilderClassName();
|
|
|
|
foreach (self::LOGFILES_TO_ROTATE as $sLogFileName)
|
|
{
|
|
$sLogFileFullPath = APPROOT
|
|
.DIRECTORY_SEPARATOR.'log'
|
|
.DIRECTORY_SEPARATOR.$sLogFileName;
|
|
|
|
/** @var \RotatingLogFileNameBuilder $oLogFileNameBuilder */
|
|
$oLogFileNameBuilder = new $sLogFileNameBuilder($sLogFileFullPath);
|
|
$oLogFileNameBuilder->ResetLastModifiedDateForFile();
|
|
$oLogFileNameBuilder->CheckAndRotateLogFile();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function GetNextOccurrence()
|
|
{
|
|
try
|
|
{
|
|
$sLogFileNameBuilder = $this->GetLogFileNameBuilderClassName();
|
|
}
|
|
catch (ProcessException $e)
|
|
{
|
|
return new DateTime('3000-01-01');
|
|
}
|
|
|
|
/** @var \RotatingLogFileNameBuilder $oLogFileNameBuilder */
|
|
$oLogFileNameBuilder = new $sLogFileNameBuilder();
|
|
return $oLogFileNameBuilder->GetCronProcessNextOccurrence(new DateTime());
|
|
}
|
|
|
|
/**
|
|
* @return string RotatingLogFileNameBuilder implementation configured
|
|
* @throws \ProcessException if the class is invalid
|
|
*/
|
|
private function GetLogFileNameBuilderClassName()
|
|
{
|
|
$sLogFileNameBuilder = MetaModel::GetConfig()->Get('log_filename_builder_impl');
|
|
if (is_a($sLogFileNameBuilder, RotatingLogFileNameBuilder::class, true))
|
|
{
|
|
return $sLogFileNameBuilder;
|
|
}
|
|
|
|
throw new ProcessException(self::class.' : The configured filename builder is invalid (log_filename_builder_impl="'.$sLogFileNameBuilder.'")');
|
|
}
|
|
}
|