logging

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
laada
Člen | 35
+
0
-

Ahoj,
existuje v Nette nativne neco na logovani? Pripadne nejake rozsireni?

Nejde mi o logovani chyb, ale o logovani udalosti.

Vim ze na internetu je toho mraky, ale dal bych prednost necemu integrovanymu.

dik
laada

David Grudl
Nette Core | 8136
+
0
-

Integrované je jen logování chyb.

Pokud si potřebuju logovat něco jiného, obvykle si vystačím s

file_put_contents('log.txt', $message, FILE_APPEND);
peci1
Člen | 60
+
0
-

Ahoj, zrovna jsem se nad chybejicim logovanim taky podivoval. Ja osobne bych ho v Nette klidne videl integrovany… Pokud clovek potrebuje neco jednoduchyho, tak file_put_contents mozna staci, ale co treba logovani do databaze? Inu, napsal jsem si vlastni logger. Je napsany (snad)tak, aby se dal hodit i do distribuce, ale nechavam to na zvazeni vyssich instanci :)

ILogger.php

<?php

/**
 * Interface for an object, that is able to log messages
 *
 * @package Nette\Logger
 * @version $id$
 * @copyright (c) 2009 Martin Pecka (Clevis)
 * @author Martin Pecka <martin.pecka@clevis.cz>
 * @license
 */
interface /*Nette\Logger\*/ILogger
{

    /**
     * Append the message to the log
     *
     * @param string $message The message to log
     * @param string|NULL $class Optional class (severity etc...) of the message
     * @return void
     */
    function logMessage($message, $class = NULL);

}

?>

FileLogger.php

<?php

/**
 * This is a logger that logs the messages into a file
 *
 * @package
 * @version $id$
 * @copyright (c) 2009 Martin Pecka (Clevis)
 * @author Martin Pecka <martin.pecka@clevis.cz>
 * @license
 */
class /*Nette\Logger\*/FileLogger extends /*Nette\*/Object implements /*Nette\Logger\*/ILogger
{

    /** @var string Name of the log file */
    protected $logFile;

    /** @var resource Handle of the log file */
    protected $logHandle = NULL;

    /** @var bool If true, log newer messages at the top of the file */
    protected $newerHigher;

    /**
     * Initialize the logger, setup the log file
     *
     * @param string $logFile The file we want to log into
     * @param bool $newerHigher Log newer messages at the beggining of the
     * file? BEWARE - this rapidly reduces performance!!!
     *
     * @return void
     */
    public function __construct($logFile, $newerHigher = FALSE)
    {
        if (!is_string($logFile))
            throw new InvalidArgumentException(
                'Argument 1 passed to FileLogger\'s constructor must be string');

        $this->logFile = $logFile;
        $this->newerHigher = $newerHigher;
    }

    /**
     * Close the log file if needed
     *
     * @return void
     */
    public function __destruct()
    {
        if ($this->logHandle !== NULL)
            fclose($this->logHandle);
    }

    /**
     * Log the message to file, with optional class
     *
     * @param string $message
     * @param string|NULL $class
     * @return void
     */
    public function logMessage($message, $class = NULL)
    {
        $message = date('Y-m-d H:i:s') . ': ' . $message . "\n\r";
        if ($class !== NULL)
            $message = "[$class] " . $message;

        $this->writeToFile($message);
    }

    /**
     * Physically write the message to the file using safestream
     *
     * If $newerHigher, write the message before the beginning
     *
     * @param mixed $message
     * @return void
     */
    protected function writeToFile($message)
    {
        if ($this->logHandle === NULL) {
            $this->openLogFile();
        }

        if ($this->newerHigher) {
            $contents = fread($this->logHandle, 1000000);
            $message .= $contents;
            rewind($this->logHandle);
        }

        fwrite($this->logHandle, $message);
    }

    /**
     * Open the file as safestream for appending
     *
     * If not newerHigher, move the cursor to the end
     *
     * @return void
     */
    protected function openLogFile()
    {
        SafeStream::register();

        //this is not stream safe, but will be called just once
        if (!file_exists($this->logFile))
            touch($this->logFile);

        if (($this->logHandle = @fopen('safe://' . $this->logFile, 'r+')) === FALSE) {
            throw new InvalidArgumentException('Cannot open logFile: ' . $this->logFile);
        }

        if (!$this->newerHigher) {
            //seek to the end of the file
            fseek($this->logHandle, -1, SEEK_END);
        }
    }

}

?>

A pak treba do bootstrap.php

<?php
Environment::getServiceLocator()->addService('ILogger',
  new FileLogger(APP_DIR . '/log/logfile.txt'));
Environment::setServiceAlias('ILogger', 'Logger');
?>

A pak uz si vesele muzete volat

<?php
Environment::getLogger()->logMessage('Message');
Environment::getLogger()->logMessage('Mess', 'age');
?>

Jeste bych podotknul, ze FileLogger vyuziva Nette SafeStream, takze by mel byt bezpecny pri vice zadostech o zapis soucasne (doufam tedy, ze jsem praci se safeStream pochopil dobre).

A na zaver opravdu varuju pred pouzivanim $newerHigher, v podstate se pri kazdem zapisu musi zapsat cely logfile znovu…

Editoval peci1 (14. 10. 2009 0:36)

kravčo
Člen | 721
+
0
-

peci1 napsal(a):

Koukal jsem, ze Environment ma __callStatic, ale asi nevim, jak ho pouzit. Zkousel jsem Environment::ILoggger()->logMessage(). Kde delam chybu?

Správne má byť:

Environment::setServiceAlias('Whatever\ILogger', 'Logger');
Environment::getLogger()->logMessage(...);

A na zaver opravdu varuju pred pouzivanim $newerHigher, v podstate se pri kazdem zapisu musi zapsat cely logfile znovu…

Prečo ho teda implementuješ?

Filip Procházka
Moderator | 4668
+
0
-

Protože to má i mnohem lepší řešení, sice delší, ale pamětově nenáročné
""stačilo by"" (je to jeden z několika jednoduchým postupů):

  • vytvoříš nový soubor „…/log/logger(2).txt“ do něj zapíšeš log
  • pomocí zápisového módu append tam přepíšeš úplně všechny řádky z prvního souboru načítané pomocí fgets()
  • smažeš soubor logger.txt
  • přejmenuješ logger(2).txt na logger.txt

btw, bude to chtít zámek vytvořenej pomocí safe_stream :)

jasir
Člen | 746
+
0
-

Zeptám se ze zvědavosti – k čemu je dobrý zápis na začátek souboru?

Jakub Šulák
Člen | 222
+
0
-

jen se vrátím k původní myšlence, zda implementovat do nette:
myslím že by to nebylo špatné, ale jen pod podmínkou, že to bude postaveno na adaptéru (výběr kam ukladat). ale pak se ptám, má to cenu tedy dávat vůbec do nette? nebo to nechat na programatorovi.

Filip Procházka
Moderator | 4668
+
0
-

jasir napsal(a):

Zeptám se ze zvědavosti – k čemu je dobrý zápis na začátek souboru?

třeba kdysi, když jsem se přisockoval na jeden server tak tam měli logy ze hry zapisované podle data od nejstaršího, a pak když chtěl člověk přes webové rozhraní prohlížet logy tak musel čekat až se celé načtou a že nebyly malé. Ale to je zase otázka implementace že, jaký si to uděláš…

peci1
Člen | 60
+
0
-

Jakub Šulák napsal(a):

ale jen pod podmínkou, že to bude postaveno na adaptéru (výběr kam ukladat)

mno, proto jsem napsal ten interface… neni treba adapter, proste si v bootstrapu nastavis takovej logger, kterej zrovna potrebujes

peci1
Člen | 60
+
0
-

HosipLan napsal(a):

jasir napsal(a):

Zeptám se ze zvědavosti – k čemu je dobrý zápis na začátek souboru?

třeba kdysi, když jsem se přisockoval na jeden server tak tam měli logy ze hry zapisované podle data od nejstaršího, a pak když chtěl člověk přes webové rozhraní prohlížet logy tak musel čekat až se celé načtou a že nebyly malé. Ale to je zase otázka implementace že, jaký si to uděláš…

Tohle je presne ten duvod, proc jsem to implementoval. Logy s „appendem na zacatek“ jsou v mnoha ohledech (krome vytvareni) praktictejsi

peci1
Člen | 60
+
0
-

HosipLan napsal(a):

Protože to má i mnohem lepší řešení, sice delší, ale pamětově nenáročné
""stačilo by"" (je to jeden z několika jednoduchým postupů):

  • vytvoříš nový soubor „…/log/logger(2).txt“ do něj zapíšeš log
  • pomocí zápisového módu append tam přepíšeš úplně všechny řádky z prvního souboru načítané pomocí fgets()
  • smažeš soubor logger.txt
  • přejmenuješ logger(2).txt na logger.txt

btw, bude to chtít zámek vytvořenej pomocí safe_stream :)

hmm, o pametove narocnosti jsem zase tolik nepremyslel… nicmene se nezbavis toho, abys na disku zkopiroval cely log z mista A na misto B… to je ta hruza, pred kterou jsem varoval

peci1
Člen | 60
+
0
-

kravčo napsal(a):

Správne má byť:

Environment::setServiceAlias('Whatever\ILogger', 'Logger');
Environment::getLogger()->logMessage(...);

Diky moc, neco takovyho jsem hledal… Editnu svuj prvni post ;)

Filip Procházka
Moderator | 4668
+
0
-

každopádně myslím že by nebylo od věci když se log přiblíží určité velikosti vytvořit pro něj další soubor a popřípadě původní zarchivovat pro zmenšení velikosti, jako to dělají unixy se svýmy logy :)

Editoval HosipLan (14. 10. 2009 9:25)

laada
Člen | 35
+
0
-

HosipLan napsal(a):

každopádně myslím že by nebylo od věci když se log přiblíží určité velikosti vytvořit pro něj další soubor a popřípadě původní zarchivovat pro zmenšení velikosti, jako to dělají unixy se svýmy logy :)

jo to by bylo pekne, ale v unixech se o to staraji separatni aplikace. Ono kopirovat treba 2GB neni uplne hned a kdyby na to mel uzivatel cekat …

Asi by se to muselo resit nejakym forkem.

peci1
Člen | 60
+
0
-

laada napsal(a):

HosipLan napsal(a):

každopádně myslím že by nebylo od věci když se log přiblíží určité velikosti vytvořit pro něj další soubor a popřípadě původní zarchivovat pro zmenšení velikosti, jako to dělají unixy se svýmy logy :)

jo to by bylo pekne, ale v unixech se o to staraji separatni aplikace. Ono kopirovat treba 2GB neni uplne hned a kdyby na to mel uzivatel cekat …

Asi by se to muselo resit nejakym forkem.

Ahoj, myslim, ze je jasne, ze fork neni ta prava cesta. Jak by se pak web testoval na windows? Mam dojem, ze takove veci do Nette nepatri. Ale moznost delat to ve skriptu je taky samozrejme nedobra :)

Programator si to muze osetrit i jinymi zpusoby, napr. registrovat logger se jmenem souboru, kde je treba den ci mesic v nazvu… At si sam zvazi, jak huste logy ma a jestli se mu vyplati radit je bezne a nebo novejsi nahoru.

kravčo
Člen | 721
+
0
-

peci1 napsal(a):

Logy s „appendem na zacatek“ jsou v mnoha ohledech (krome vytvareni) praktictejsi

Podstatné je, že pri logovaní určite viac záleží na tom, ako rýchle je vytváranie – to je totiž kritické, keďže aplikácia logujúca udalosti je žiadosťami o zápis do logu popretkávaná… Nie je problém cronom cez noc logy obrátiť, problém by bol, ak by sa stránka načítavala 2 sekundy, kvôli „praktickosti“ logov…

Patrik Votoček
Člen | 2221
+
0
-

Logrotate… To je asi to jediné řešení…

Filip Procházka
Moderator | 4668
+
0
-

Viděls někdy logy v *nixech ? já mám třeba kubuntu a ani jeden jediný soubor .log nemá víc jak 2MB a 2MB přesuneš za úplně titěrnej čas, každopádně já si udělám verzi pro databázi, protože potřebuju logovat akce v administraci a na to se texťáky nehodí.

Panda
Člen | 569
+
0
-

HosipLan napsal(a):

Viděls někdy logy v *nixech ? já mám třeba kubuntu a ani jeden jediný soubor .log nemá víc jak 2MB a 2MB přesuneš za úplně titěrnej čas, každopádně já si udělám verzi pro databázi, protože potřebuju logovat akce v administraci a na to se texťáky nehodí.

Viděl jsi někdy logy v *nixech na serveru? Na našem firemním má jen maillog za poslední 2 dny 55MB. Logy Apache za den přiberou zhruba 90MB. A to ten server zas tak zatížený není… Mohu Tě ujistit, že na serveru v produkčním prostředí jsi s časem a velikostmi někde úplně jinde.

Každopádně teď přepisuji jednu mojí starší třídu na logování, která toho umí o trošku víc, než zde uveřejněná třída. A logování na začátek souboru naštěstí neumí…

Panda
Člen | 569
+
0
-

Jak jsem před chvílí řekl, tak teď činím.

Třída se používá téměř stejně, jako zde uveřejněná, ale je tam několik rozdílů.

Místo parametru $class používá parametr $level, který určuje vážnost logované zprávy. Jako hodnoty se dosazují konstanty z ILogger. Metodou setLogLevel() lze nastavit minimální vážnost zpráv, která se bude logovat. Ve vývojovém prostředí se jako výchozí hodnota nastaví ILogger::DEBUG, v produkčním ILogger::INFO. Nastavením na ILogger::NONE se logování vypne.

Dalším docela podstatným rozdílem je nastavení masky názvy souboru. Děje se tak prostřednictvím metody setFilenameMask(). Tato maska se při prvním zápisu pro danou instanci prožene funkcí strftime(), což umožní pojmenování souborů logů podle aktuálního času. Výchozí je hodnota log-%Y-%m-%d.log, každý den se tedy vytváří nový soubor.

Ještě zajímavější je nastavení něčeho, co jsem nazval granularity (zrnitost, nespojitost, lepší název jsem nevymyslel). Určuje časový interval v sekundách, na jaký se čas při vytváření souborů zaokrouhluje. Pokud nastavíme masku souboru na log-%Y-%m-%d-%H-%M.log, bude se nám každou sekundu vytvářet nový log. To není moc ideální, proto můžeme nastavit „zrnitost“ – pokud nastavíme hodnotu na 1800s, tedy půl hodiny, bude se nám soubor s logy vytvářet každé půl hodiny. Kombinací těchto hodnot lze krásně ovlivnit členění logů. Jako název se vždy zvolí počátek intervalu, při zmíněném nastavení se budou vytvářet logy s názvy typu log-2009-10-14-17-30.log i v případě, že by první zápis přišel až 17:55.

Toho také můžete využít pro vytváření logů podle týdnů: nastavením hodnoty na Tools::WEEK (604800s) se budou vytvářet logy každý týden. Názvy budou vždy podle prvního dne týdne, dneska by se tedy při výchozí masce vytvořil soubor log-2009-10-12.log (pondělí bylo 12.).

Další nastavení jsou, myslím, celkem jasná.

Pokud máte někdo nějaké připomínky či nápady, tak sem s nimi.

Kód:

<?php
/**
 * Message logger.
 * Slightly inspired by ILogger by Martin Pecka <martin.pecka@clevis.cz>:
 *   https://forum.nette.org/cs/viewtopic.php?pid=19667#p19667
 *
 * @author     Jan Smitka <jan@smitka.org>
 * @copyright  Copyright (c) 2009 Jan Smitka
 */
interface ILogger
{
	/**#@+ Severity levels */
	const NONE = 0; // used only for disabling the logging
	const ERROR = 1;
	const WARNING = 2;
	const INFO = 3;
	const DEBUG = 4;
	/**#@-*/

	/**
	 * Sets the log verbosity.
	 * @param int $level Severity level
	 * @return void
	 * @throws InvalidArgumentException
	 */
	public function setLogLevel($level);

	/**
	 * Log a message.
	 * @param string $message The message to log
	 * @param int $level Severity of the message
	 * @throws InvalidArgumentException
	 */
	public function logMessage($message, $level = ILogger::WARNING);
}
?>
<?php
/**
 * Filesystem-based implementation of ILogger.
 * Slightly inspired by FileLogger by Martin Pecka <martin.pecka@clevis.cz>:
 *   https://forum.nette.org/cs/viewtopic.php?pid=19667#p19667
 *
 * @author     Jan Smitka <jan@smitka.org>
 * @copyright  Copyright (c) 2009 Jan Smitka
 */
class FileLogger extends Object implements ILogger
{
	/** @var string */
	private $filenameMask = 'log-%Y-%m-%d.log';

	/** @var string */
	private $logDir = '%logDir%';

	/** @var string internal file path */
	private $file;

	/** @var int */
	private $logLevel;

	/** @var int seconds */
	private $granularity = 0;

	/** @var string for date() */
	private $dateFormat = 'r';



	public function __construct($logLevel = NULL, $logDir = NULL, $filenameMask = NULL, $granularity = 0)
	{
		if ($logLevel === NULL)
			$logLevel = Environment::isDebugging() ? ILogger::DEBUG : ILogger::INFO;

		$this->setLogLevel($logLevel);
		if ($logDir !== NULL)
			$this->logDir = $logDir;
		if ($filenameMask !== NULL)
			$this->filenameMask = $filenameMask;
		$this->granularity = $granularity;
	}



	/**
	 * Returns the logger verbosity.
	 * @return int
	 */
	public function getLogLevel()
	{
		return $this->logLevel;
	}

	/**
	 * Sets the logger verbosity.
	 * @param int $level one of the ILogger severity constants
	 * @return void
	 * @throws InvalidArgumentException
	 */
	public function setLogLevel($level)
	{
		if ($level > ILogger::DEBUG || $level < ILogger::NONE)
			throw new InvalidArgumentException('Log level must be one of the ILogger severity constants.');
		$this->logLevel = $level;
	}



	/**
	 * Returns the filename mask of log file.
	 * @return string
	 */
	public function getFilenameMask()
	{
		return $this->filenameMask;
	}

	/**
	 * Sets the filename mask for log files.
	 * You can use the strftime specifiers.
	 * @param string $filenameMask
	 * @return void
	 * @see strftime()
	 */
	public function setFilenameMask($filenameMask)
	{
		$this->filenameMask = $filenameMask;
		$this->file = NULL;
	}



	/**
	 * Returns the directory path where log files reside.
	 * @return string with untranslated environment variables
	 */
	public function getLogDir()
	{
		return $this->logDir;
	}

	/**
	 * Sets the directory path where log files reside.
	 * You can use environment variables.
	 * @param string $logDir
	 * @return void
	 * @see Environment::expand()
	 */
	public function setLogDir($logDir)
	{
		$this->logDir = $logDir;
		$this->file = NULL;
	}



	/**
	 * Returns log files granularity.
	 * @return int in seconds
	 */
	public function getGranularity()
	{
		return $this->granularity;
	}

	/**
	 * Sets log files granularity.
	 * Please note that real granularity is also determined by filename mask.
	 * @param int $granularity
	 * @return void
	 * @throws InvalidArgumentException
	 */
	public function setGranularity($granularity)
	{
		if ($granularity < 0)
			throw new InvalidArgumentException('Granularity must be greater than or equal to 0.');

		$this->granularity = $granularity;
		$this->file = NULL;
	}



	/**
	 * Returns the date format used inside log files.
	 * @return string
	 */
	public function getDateFormat()
	{
		return $this->dateFormat;
	}

	/**
	 * Sets the date format used inside log files.
	 * Format is the same as used by date() function.
	 * @param string $dateFormat
	 * @return void
	 * @see date()
	 */
	public function setDateFormat($dateFormat)
	{
		$this->dateFormat = $dateFormat;
	}





	/**
	 * Returns the full path to the current log file.
	 * @return string
	 * @throws InvalidStateException
	 */
	public function getFile()
	{
		if ($this->file === NULL) {
			// granularity calculations
			if ($this->granularity > 1) {
				$offset = 345600 - (int) date('Z');
				$timestamp = $offset + floor((time() - $offset) / $this->granularity) * $this->granularity;
			} else
				$timestamp = time();

			$this->file = ($path = Environment::expand($this->logDir))
			            . (String::endsWith($path, '/') ? '' : '/')
			            . strftime($this->filenameMask, $timestamp);
		}

		return $this->file;
	}



	/**
	 * Log a message.
	 * @param string $message
	 * @param int $level one of the ILogger severity constants
	 * @return void
	 * @throws InvalidArgumentException
	 * @throws InvalidStateException
	 */
	public function logMessage($message, $level = ILogger::INFO)
	{
		if ($level > ILogger::DEBUG || $level <= ILogger::NONE)
			throw new InvalidArgumentException('Log level must be one of the ILogger severity constants except ILogger::NONE.');

		if ($level <= $this->logLevel) {
			$message = sprintf("%s %s: %s\r\n", date($this->dateFormat), $this->logLevelToString($level), $message);
			file_put_contents($this->getFile(), $message, FILE_APPEND);
			// Please note that FILE_APPEND operation is atomic (tested):
			// http://us2.php.net/manual/en/function.file-put-contents.php
		}
	}



	/**
	 * Translate log severity level into a readable string.
	 * @param int $level
	 * @return void
	 */
	protected function logLevelToString($level)
	{
		switch ($level) {
		case ILogger::ERROR:
			return 'ERROR';

		case ILogger::WARNING:
			return 'WARNING';

		case ILogger::INFO:
			return 'INFO';

		case ILogger::DEBUG:
			return 'DEBUG';

		default:
			throw new InvalidArgumentException('Unknown severity level.');
		}
	}
}
?>

Editoval Panda (14. 10. 2009 18:00)

peci1
Člen | 60
+
0
-

Krasa, Pando :)

Jeste jsem si ji trochu upravil, aby brala printf-like argumenty.
Taky v ni pouzivas deprecated Environment::isDebugging().
A taky jsem prehazel parametry konstruktoru podle meho nazoru, ktere clovek potrebuje nejcasteji prepisovat.

Co me zaujalo, je, ze mi prestal fungovat setServiceAlias (resp. ten funguje, ale __callStatic se nezavola – vsechno testuju na PHP 5.2, takze se ani zavolat nema. Jenze proc mi to s mym loggerem fungovalo??? A proc neni v dokumentaci Nette napsano, ze Alias je PHP 5.3 only?)

<?php
/**
 * Filesystem-based implementation of ILogger.
 * Slightly inspired by FileLogger by Martin Pecka <martin.pecka@clevis.cz>:
 *   https://forum.nette.org/cs/viewtopic.php?pid=19667#p19667
 *
 * @author     Jan Smitka <jan@smitka.org>
 * @copyright  Copyright (c) 2009 Jan Smitka
 */
class FileLogger extends Object implements ILogger
{
        /** @var string Mask of the log filename, can contain strftime formatting */
        private $filenameMask = 'log-%Y-%m-%d.log';

        /** @var string The directory where we save the logs */
        private $logDir = '%logDir%';

        /** @var string The log file path */
        private $file;

        /** @var int Maximum level of events logged */
        private $logLevel;

        /** @var int seconds If > 0, defines a time span used for one log
         *
         * eg. if you want to save two logs a day, you can define mask
         * "%Y-%m-%d-%H" and set this to half a day of seconds, so the logs
         * won't create each hour (as defined in the mask) */
        private $granularity = 0;

        /** @var string Log date format as in date() */
        private $dateFormat = 'r';



        /**
         * Just set member variables given as parameters
         *
         * @param string $filenameMask Mask of the log filename, can contain strftime formatting
         * @param int $logLevel One of the ILogger constants, maximum level of events logged
         * @param mixed $logDir Directory for saving the logs
         * @param int $granularity If > 0, defines a time span used for one log
         *
         * @return void
         */
        public function __construct($filenameMask = NULL, $logLevel = NULL, $logDir = NULL, $granularity = 0)
        {
                if ($logLevel === NULL)
                        $logLevel = Environment::isProduction() ? ILogger::INFO : ILogger::DEBUG;
                $this->setLogLevel($logLevel);

                if ($logDir !== NULL)
                        $this->logDir = $logDir;

                if ($filenameMask !== NULL)
                        $this->filenameMask = $filenameMask;

                $this->granularity = $granularity;
        }



        /**
         * Returns the logger verbosity.
         *
         * @return int
         */
        public function getLogLevel()
        {
                return $this->logLevel;
        }

        /**
         * Sets the logger verbosity.
         * @param int $level one of the ILogger severity constants
         *
         * @return void
         *
         * @throws InvalidArgumentException If the given level is not one of
         * ILogger's constants
         */
        public function setLogLevel($level)
        {
                if ($level > ILogger::DEBUG || $level < ILogger::NONE)
                        throw new InvalidArgumentException('Log level must be one of the ILogger severity constants.');
                $this->logLevel = $level;
        }



        /**
         * Returns the filename mask of log file.
         *
         * @return string
         */
        public function getFilenameMask()
        {
                return $this->filenameMask;
        }

        /**
         * Sets the filename mask for log files.
         * You can use the strftime specifiers.
         *
         * @param string $filenameMask
         *
         * @return void
         *
         * @see strftime()
         */
        public function setFilenameMask($filenameMask)
        {
                $this->filenameMask = $filenameMask;
                $this->file = NULL;
        }



        /**
         * Returns the directory path where log files reside.
         *
         * @return string with untranslated environment variables
         */
        public function getLogDir()
        {
                return $this->logDir;
        }

        /**
         * Sets the directory path where log files reside.
         * You can use environment variables.
         *
         * @param string $logDir
         *
         * @return void
         *
         * @see Environment::expand()
         */
        public function setLogDir($logDir)
        {
                $this->logDir = $logDir;
                $this->file = NULL;
        }



        /**
         * Returns log files granularity.
         *
         * @return int in seconds
         */
        public function getGranularity()
        {
                return $this->granularity;
        }

        /**
         * Sets log files granularity.
         * Please note that real granularity is also determined by filename mask.
         *
         * @param int $granularity
         *
         * @return void
         *
         * @throws InvalidArgumentException If the grannularity is not non-negative number
         */
        public function setGranularity($granularity)
        {
                if ($granularity < 0)
                        throw new InvalidArgumentException('Granularity must be greater than or equal to 0.');

                $this->granularity = $granularity;
                $this->file = NULL;
        }



        /**
         * Returns the date format used inside log files.
         *
         * @return string
         */
        public function getDateFormat()
        {
                return $this->dateFormat;
        }

        /**
         * Sets the date format used inside log files.
         * Format is the same as used by date() function.
         *
         * @param string $dateFormat
         *
         * @return void
         *
         * @see date()
         */
        public function setDateFormat($dateFormat)
        {
                $this->dateFormat = $dateFormat;
        }





        /**
         * Returns the full path to the current log file.
         *
         * @return string
         *
         * @throws InvalidStateException
         */
        public function getFile()
        {
                if ($this->file === NULL) {
                        // granularity calculations
                        if ($this->granularity > 1) {
                                $offset = 345600 - (int) date('Z');
                                $timestamp = $offset + floor((time() - $offset) / $this->granularity) * $this->granularity;
                        } else
                                $timestamp = time();

                        $this->file = ($path = Environment::expand($this->logDir))
                                    . (String::endsWith($path, '/') ? '' : '/')
                                    . strftime($this->filenameMask, $timestamp);
                }

                return $this->file;
        }



         /**
         * Log a message.
         *
         * The message can be formatted as in printf
         *
         * @param string $message The message to log
         * @param int $level Severity of the message
         * @param mixed $args Following arguments are interpreted as printf arguments
         *
         * @throws InvalidArgumentException If the given level is not one of
         * the interfaces' constants
         * @throws InvalidStateException If file operation failed
         */
        public function logMessage($message, $level = ILogger::INFO)
        {
                if ($level > ILogger::DEBUG || $level <= ILogger::NONE)
                        throw new InvalidArgumentException('Log level must be one of the ILogger severity constants except ILogger::NONE.');

                //had the user passed some printf arguments?
                if (func_num_args() > 2) {
                        $args = func_get_args();
                        array_shift($args);
                        array_shift($args);

                        $message = vsprintf($message, $args);
                }

                if ($level <= $this->logLevel) {
                        $message = sprintf("%s %s: %s\r\n", date($this->dateFormat), $this->logLevelToString($level), $message);
                        file_put_contents($this->getFile(), $message, FILE_APPEND);
                        // Please note that FILE_APPEND operation is atomic (tested):
                        // http://us2.php.net/manual/en/function.file-put-contents.php
                }
        }



        /**
         * Translate log severity level into a readable string.
         *
         * @param int $level One of ILogger's constants
         *
         * @return void
         *
         * @throws InvalidArgumentException If the level is unknown
         */
        protected function logLevelToString($level)
        {
                switch ($level) {
                case ILogger::ERROR:
                        return 'ERROR';

                case ILogger::WARNING:
                        return 'WARNING';

                case ILogger::INFO:
                        return 'INFO';

                case ILogger::DEBUG:
                        return 'DEBUG';

                default:
                        throw new InvalidArgumentException('Unknown severity level.');
                }
        }
}

?>
Jan Tvrdík
Nette guru | 2595
+
0
-

peci1 napsal(a):

Co me zaujalo, je, ze mi prestal fungovat setServiceAlias (resp. ten funguje, ale __callStatic se nezavola – vsechno testuju na PHP 5.2, takze se ani zavolat nema. )

Magická metoda __callStatic je k dispozici až od PHP 5.3. Nette s tím nic neudělá.

peci1
Člen | 60
+
0
-

Jan Tvrdík napsal(a):

peci1 napsal(a):

Co me zaujalo, je, ze mi prestal fungovat setServiceAlias (resp. ten funguje, ale __callStatic se nezavola – vsechno testuju na PHP 5.2, takze se ani zavolat nema. )

Magická metoda __callStatic je k dispozici až od PHP 5.3. Nette s tím nic neudělá.

No me prave udivuje, ze s mym puvodnim loggerem ten alias fungoval… :-/