diff --git a/src/Pluf/Log.php b/src/Pluf/Log.php
new file mode 100644
index 0000000..9ace63f
--- /dev/null
+++ b/src/Pluf/Log.php
@@ -0,0 +1,272 @@
+ 'ALL',
+ 5 => 'DEBUG',
+ 6 => 'INFO',
+ 7 => 'WARN',
+ 8 => 'ERROR',
+ 9 => 'FATAL');
+
+ /**
+ * Current log level.
+ *
+ * By default, set to 6, which is the INFO level.
+ */
+ public static $level = 6;
+
+ /**
+ * Current message in the assert log.
+ */
+ public static $assert_mess = null;
+
+ /**
+ * Current level of the message in the assert log.
+ */
+ public static $assert_level = 6;
+
+ /**
+ * Log the information in the stack.
+ *
+ * Flush the information if needed.
+ *
+ * @param $level Level to log
+ * @param $message Message to log
+ */
+ private static function _log($level, $message)
+ {
+
+ if (self::$level >= $level and self::$level != 10) {
+ self::$stack[] = array(microtime(true), $level, $message);
+ if (!Pluf::f('log_delayed', false)) {
+ self::flush();
+ }
+ }
+ }
+
+ /**
+ * Base assert logger.
+ *
+ * The assert logging is a two step process as one need to go
+ * through the assertion callback.
+ *
+ * @param $level Level to log
+ * @param $message Message to log
+ * @return bool false
+ */
+ private static function _alog($level, $message)
+ {
+ self::$assert_level = $level;
+ self::$assert_mess = $message;
+ return false; // This will trigger the assert handler.
+ }
+
+ /**
+ * Log at the ALL level.
+ *
+ * @param $message Message to log
+ */
+ public static function log($message)
+ {
+ return self::_log(self::ALL, $message);
+ }
+
+ /**
+ * Log at the DEBUG level.
+ *
+ * @param $message Message to log
+ */
+ public static function debug($message)
+ {
+ self::_log(self::DEBUG, $message);
+ }
+
+ public static function info($message)
+ {
+ self::_log(self::INFO, $message);
+ }
+
+ public static function warn($message)
+ {
+ self::_log(self::WARN, $message);
+ }
+
+ public static function error($message)
+ {
+ self::_log(self::ERROR, $message);
+ }
+
+ public static function fatal($message)
+ {
+ self::_log(self::FATAL, $message);
+ }
+
+ /**
+ * Assert log at the ALL level.
+ *
+ * @param $message Message to log
+ */
+ public static function alog($message)
+ {
+ return self::_alog(self::ALL, $message);
+ }
+
+ /**
+ * Assert log at the DEBUG level.
+ *
+ * @param $message Message to log
+ */
+ public static function adebug($message)
+ {
+ self::_alog(self::DEBUG, $message);
+ }
+
+ public static function ainfo($message)
+ {
+ self::_alog(self::INFO, $message);
+ }
+
+ public static function awarn($message)
+ {
+ self::_alog(self::WARN, $message);
+ }
+
+ public static function aerror($message)
+ {
+ self::_alog(self::ERROR, $message);
+ }
+
+ public static function afatal($message)
+ {
+ self::_alog(self::FATAL, $message);
+ }
+
+ /**
+ * Flush the data to the writer.
+ *
+ * This reset the stack.
+ */
+ public static function flush()
+ {
+ $writer = Pluf::f('log_handler', 'Pluf_Log_File');
+ call_user_func(array($writer, 'write'), self::$stack);
+ self::$stack = array();
+ }
+
+ /**
+ * Signal handler to flush the log.
+ *
+ * The name of the signal and the parameters are not used.
+ *
+ * @param $signal Name of the signal
+ * @param &$params Parameters
+ */
+ public static function flushHandler($signal, &$params)
+ {
+ self::flush();
+ }
+
+ /**
+ * Activation of the low impact logging.
+ *
+ * When called, it enabled the assertions for debugging.
+ */
+ public static function activeAssert()
+ {
+ assert_options(ASSERT_ACTIVE, 1);
+ assert_options(ASSERT_WARNING, 0);
+ assert_options(ASSERT_QUIET_EVAL, 1);
+ assert_options(ASSERT_CALLBACK, 'Pluf_Log_assert');
+ }
+}
+
+/**
+ * Assertion handler.
+ *
+ * @param $file Name of the file where the assert is called
+ * @param $line Line number of the file where the assert is called
+ * @param $code Code evaluated by the assert call
+ */
+function Pluf_Log_assert($file, $line, $code)
+{
+ if (Pluf_Log::$level >= Pluf_Log::$assert_level and
+ Pluf_Log::$level != 10) {
+ Pluf_Log::$stack[] = array(
+ microtime(true),
+ Pluf_Log::$assert_level,
+ Pluf_Log::$assert_mess,
+ $file, $line, $code);
+ if (!Pluf::f('log_delayed', false)) {
+ Pluf_Log::flush();
+ }
+
+ }
+ Pluf_Log::$assert_level = 6;
+ Pluf_Log::$assert_mess = null;
+}
\ No newline at end of file
diff --git a/src/Pluf/Log/File.php b/src/Pluf/Log/File.php
new file mode 100644
index 0000000..ad2b035
--- /dev/null
+++ b/src/Pluf/Log/File.php
@@ -0,0 +1,61 @@
+Pluf_Log class.
+ *
+ * The only required static method of a log writer is
+ * write
, which takes the stack to write as parameter.
+ *
+ * The only configuration variable of the file writer is the path to
+ * the log file 'pluf_log_file'. By default it creates a
+ * pluf.log
in the configured tmp folder.
+ *
+ */
+class Pluf_Log_File
+{
+ /**
+ * Flush the stack to the disk.
+ *
+ * @param $stack Array
+ */
+ public static function write($stack)
+ {
+ $file = Pluf::f('pluf_log_file',
+ Pluf::f('tmp_folder', '/tmp').'/pluf.log');
+ $out = array();
+ foreach ($stack as $elt) {
+ $out[] = date(DATE_ISO8601, (int) $elt[0]).' '.
+ Pluf_Log::$reverse[$elt[1]].': '.
+ (string) $elt[2];
+ }
+ $fp = fopen($file, 'a');
+ flock($fp, LOCK_EX); // Blocking call.
+ fputs($fp, implode(PHP_EOL, $out).PHP_EOL);
+ fclose($fp) ; // release the lock
+ }
+}
diff --git a/src/Pluf/Log/Remote.php b/src/Pluf/Log/Remote.php
new file mode 100644
index 0000000..3deaaca
--- /dev/null
+++ b/src/Pluf/Log/Remote.php
@@ -0,0 +1,64 @@
+$val) {
+ $out .= $key.': '.$val."\r\n";
+ }
+ $out.= 'Connection: Close'."\r\n\r\n";
+ $out.= $payload;
+ $fp = fsockopen(Pluf::f('log_remote_server', 'localhost'),
+ Pluf::f('log_remote_port', 8000),
+ $errno, $errstr, 5);
+ fwrite($fp, $out);
+ fclose($fp);
+ }
+}
diff --git a/src/Pluf/Tests/Log/TestLog.php b/src/Pluf/Tests/Log/TestLog.php
new file mode 100644
index 0000000..d02229c
--- /dev/null
+++ b/src/Pluf/Tests/Log/TestLog.php
@@ -0,0 +1,91 @@
+logfile = Pluf::f('pluf_log_file',
+ Pluf::f('tmp_folder', '/tmp').'/pluf.log');
+ @unlink($this->logfile);
+ }
+
+ function tearDown()
+ {
+ @unlink($this->logfile);
+ }
+
+ function testSimple()
+ {
+ $GLOBALS['_PX_config']['log_delayed'] = true;
+ Pluf_Log::log('hello');
+ $this->assertEqual(count(Pluf_Log::$stack), 1);
+ $GLOBALS['_PX_config']['log_delayed'] = false;
+ Pluf_Log::log('hello');
+ $this->assertEqual(count(Pluf_Log::$stack), 0);
+ $this->assertEqual(2, count(file($this->logfile)));
+ }
+
+ function testAssertLog()
+ {
+ Pluf_Log::activeAssert();
+ $GLOBALS['_PX_config']['log_delayed'] = true;
+ assert('Pluf_Log::alog("hello")');
+ $this->assertEqual(count(Pluf_Log::$stack), 1);
+ $GLOBALS['_PX_config']['log_delayed'] = false;
+ assert('Pluf_Log::alog("hello")');
+ $this->assertEqual(count(Pluf_Log::$stack), 0);
+ $this->assertEqual(2, count(file($this->logfile)));
+
+ }
+
+ /**
+ function testPerformance()
+ {
+ $start = microtime(true);
+ $GLOBALS['_PX_config']['log_delayed'] = false;
+ for ($i=0;$i<100;$i++) {
+ Pluf_Log::log('hello'.$i);
+ }
+ $end = microtime(true);
+ print "File: ".($end-$start)."s\n";
+ $start = microtime(true);
+ $GLOBALS['_PX_config']['log_delayed'] = true;
+ for ($i=0;$i<100;$i++) {
+ Pluf_Log::log('hello'.$i);
+ }
+ Pluf_Log::flush();
+ $end = microtime(true);
+ print "File delayed: ".($end-$start)."s\n";
+ }
+ **/
+}
\ No newline at end of file
diff --git a/src/Pluf/Tests/Log/TestRemote.php b/src/Pluf/Tests/Log/TestRemote.php
new file mode 100644
index 0000000..b34fe90
--- /dev/null
+++ b/src/Pluf/Tests/Log/TestRemote.php
@@ -0,0 +1,86 @@
+assertEqual(count(Pluf_Log::$stack), 1);
+ $GLOBALS['_PX_config']['log_delayed'] = false;
+ Pluf_Log::log('hello');
+ $this->assertEqual(count(Pluf_Log::$stack), 0);
+ }
+
+ function testAssertLog()
+ {
+ Pluf_Log::activeAssert();
+ $GLOBALS['_PX_config']['log_delayed'] = true;
+ assert('Pluf_Log::alog("hello")');
+ $this->assertEqual(count(Pluf_Log::$stack), 1);
+ $GLOBALS['_PX_config']['log_delayed'] = false;
+ assert('Pluf_Log::alog("hello")');
+ $this->assertEqual(count(Pluf_Log::$stack), 0);
+ }
+
+ /**
+ function testPerformance()
+ {
+ $start = microtime(true);
+ $GLOBALS['_PX_config']['log_delayed'] = false;
+ for ($i=0;$i<100;$i++) {
+ Pluf_Log::log('hello'.$i);
+ }
+ $end = microtime(true);
+ print "Remote: ".($end-$start)."s\n";
+ $start = microtime(true);
+ $GLOBALS['_PX_config']['log_delayed'] = true;
+ for ($i=0;$i<100;$i++) {
+ Pluf_Log::log('hello'.$i);
+ }
+ Pluf_Log::flush();
+ $end = microtime(true);
+ print "Remote delayed: ".($end-$start)."s\n";
+ }
+ **/
+}
\ No newline at end of file