Author: ihabunek
Date: Thu Dec 8 15:10:45 2011
New Revision: 1211929
URL: http://svn.apache.org/viewvc?rev=1211929&view=rev
Log:
LOG4PHP-154: Rewritten LoggerAppenderSocket to use a layout.
Added:
logging/log4php/trunk/src/test/php/appenders/socketServer.php
Modified:
logging/log4php/trunk/src/changes/changes.xml
logging/log4php/trunk/src/main/php/appenders/LoggerAppenderSocket.php
logging/log4php/trunk/src/site/xdoc/docs/appenders/socket.xml
logging/log4php/trunk/src/test/php/appenders/LoggerAppenderSocketTest.php
Modified: logging/log4php/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/logging/log4php/trunk/src/changes/changes.xml?rev=1211929&r1=1211928&r2=1211929&view=diff
==============================================================================
--- logging/log4php/trunk/src/changes/changes.xml (original)
+++ logging/log4php/trunk/src/changes/changes.xml Thu Dec 8 15:10:45 2011
@@ -21,6 +21,7 @@
</properties>
<body>
<release version="2.2.0" date="SVN">
+ <action date="2011-12-08" type="update" issue="LOG4PHP-154"
dev="Ivan Habunek">Rewritten LoggerAppenderSocket to use a layout.</action>
<action date="2011-12-04" type="add" issue="LOG4PHP-160"
dev="Ivan Habunek" due-to="Florian Semm" due-to-email="florian dot semm at gmx
dot de">Appeneders should use a default layout is no layout is specified in
configuration</action>
<action date="2011-10-23" type="add" issue="LOG4PHP-155"
dev="Ivan Habunek">Created a new layout LoggerLayoutSerialized which formats
events as serialized objects.</action>
<action date="2011-10-23" type="fix" issue="LOG4PHP-159"
dev="Ivan Habunek" due-to="Justin Cherniak" due-to-email="justin dot cherniak
at gmail dot com">Appenders do not close gracefully if a fatal error
occurs.</action>
Modified: logging/log4php/trunk/src/main/php/appenders/LoggerAppenderSocket.php
URL:
http://svn.apache.org/viewvc/logging/log4php/trunk/src/main/php/appenders/LoggerAppenderSocket.php?rev=1211929&r1=1211928&r2=1211929&view=diff
==============================================================================
--- logging/log4php/trunk/src/main/php/appenders/LoggerAppenderSocket.php
(original)
+++ logging/log4php/trunk/src/main/php/appenders/LoggerAppenderSocket.php Thu
Dec 8 15:10:45 2011
@@ -19,240 +19,114 @@
*/
/**
- * Serialize events and send them to a network socket.
+ * Appends events to a network socket.
*
* This appender can be configured by changing the following attributes:
*
- * - locationInfo - Sets the location info for the xml layout (true or
false)
- * - log4jNamespace - Sets the namespace for log4j (true or false)
- * - port - Sets the port of the socket.
- * - remoteHost - Sets the remote host
- * - timeout - Sets the timeout in ms
- * - useXml - true, if xml should be transmitted.
- * false, if a serialized php object should be
transmitted
- *
- * Parameters are {@link $remoteHost}, {@link $port}, {@link $timeout},
- * {@link $locationInfo}, {@link $useXml} and {@link $log4jNamespace}.
- *
- * An example:
- *
- * {@example ../../examples/php/appender_socket.php 19}
+ * - remoteHost - Target remote host.
+ * - port - Target port (optional, defaults to 4446).
+ * - timeout - Connection timeout in seconds (optional, defaults to
+ * 'default_socket_timeout' from php.ini)
*
- * {@example ../../examples/resources/appender_socket.properties 18}
+ * The socket will by default be opened in blocking mode.
*
* @version $Revision$
* @package log4php
* @subpackage appenders
*/
class LoggerAppenderSocket extends LoggerAppender {
-
- /**
- * This appender does not require a layout.
- */
- protected $requiresLayout = false;
- /**
- * @var mixed socket connection resource
+ /**
+ * Target host.
+ * @see http://php.net/manual/en/function.fsockopen.php
*/
- private $sp = false;
+ private $remoteHost;
- /**
- * Target host. On how to define remote hostaname see
- * {@link PHP_MANUAL#fsockopen}
- * @var string
- */
- private $remoteHost = '';
-
- /**
- * @var integer the network port.
- */
+ /** Target port */
private $port = 4446;
- /**
- * @var boolean get event's location info.
- */
- private $locationInfo = false;
+ /** Connection timeout in ms. */
+ private $timeout;
- /**
- * @var integer connection timeout
- */
- private $timeout = 30;
-
- /**
- * @var boolean output events via {@link LoggerXmlLayout}
- */
- private $useXml = false;
-
- /**
- * @var boolean forward this option to {@link LoggerXmlLayout}.
- * Ignored if {@link $useXml} is
<i>false</i>.
- */
- private $log4jNamespace = false;
-
- /**
- * @var LoggerXmlLayout
- */
- private $xmlLayout = null;
-
- /** @var indiciates if this appender should run in dry mode */
- private $dry = false;
+ // ******************************************
+ // *** Appender methods ***
+ // ******************************************
+
+ /** Override the default layout to use serialized. */
+ public function getDefaultLayout() {
+ return new LoggerLayoutSerialized();
+ }
- /**
- * Create a socket connection using defined parameters
- */
public function activateOptions() {
- if(!$this->dry) {
- $this->sp = @fsockopen($this->getRemoteHost(),
$this->getPort(), $errno, $errstr, $this->getTimeout());
- if ($this->sp === false) {
- throw new LoggerException("Could not open
socket to ".$this->getRemoteHost().":".$this->getPort().": $errstr ($errno)");
- }
- }
- if($this->getUseXml()) {
- $this->xmlLayout =
LoggerReflectionUtils::createObject('LoggerLayoutXml');
- if($this->xmlLayout === null) {
- $this->setUseXml(false);
- } else {
-
$this->xmlLayout->setLocationInfo($this->getLocationInfo());
-
$this->xmlLayout->setLog4jNamespace($this->getLog4jNamespace());
- $this->xmlLayout->activateOptions();
- }
+ if (empty($this->remoteHost)) {
+ $this->warn("Required parameter [remoteHost] not set.
Closing appender.");
+ $this->closed = true;
+ return;
+ }
+
+ if (empty($this->timeout)) {
+ $this->timeout = ini_get("default_socket_timeout");
}
+
$this->closed = false;
}
- public function close() {
- if($this->closed != true) {
- if(!$this->dry and $this->sp !== false) {
- fclose($this->sp);
- }
+ public function append(LoggerLoggingEvent $event) {
+ $socket = fsockopen($this->remoteHost, $this->port, $errno,
$errstr, $this->timeout);
+ if ($socket === false) {
+ $this->warn("Could not open socket to
{$this->remoteHost}:{$this->port}. Closing appender.");
$this->closed = true;
+ return;
}
- }
-
- public function setDry($dry) {
- $this->dry = $dry;
- }
- /**
- * @return string
- */
- public function getHostname() {
- return $this->getRemoteHost();
+ if (false === fwrite($socket, $this->layout->format($event))) {
+ $this->warn("Error writing to socket. Closing
appender.");
+ $this->closed = true;
+ }
+ fclose($socket);
}
- /**
- * @return boolean
- */
- public function getLocationInfo() {
- return $this->locationInfo;
- }
-
- /**
- * @return boolean
- */
- public function getLog4jNamespace() {
- return $this->log4jNamespace;
- }
-
- /**
- * @return integer
- */
- public function getPort() {
- return $this->port;
- }
+ // ******************************************
+ // *** Accessor methods ***
+ // ******************************************
- public function getRemoteHost() {
- return $this->remoteHost;
+ /** Sets the target host. */
+ public function setRemoteHost($hostname) {
+ $this->remoteHost = $hostname;
}
- /**
- * @return integer
- */
- public function getTimeout() {
- return $this->timeout;
+ /** Sets the target port */
+ public function setPort($port) {
+ try {
+ $this->port = LoggerOptionConverter::toIntegerEx($port,
null);
+ } catch (Exception $ex) {
+ $this->warn("Invalid value provided for 'port' [$port].
Expected an integer. Using default value [{$this->port}].");
+ }
}
-
- /**
- * @var boolean
- */
- public function getUseXml() {
- return $this->useXml;
- }
- public function reset() {
- $this->close();
- }
-
- /**
- * @param mixed
- */
- public function setLocationInfo($flag) {
- $this->locationInfo = LoggerOptionConverter::toBoolean($flag,
$this->getLocationInfo());
- }
-
- /**
- * @param mixed
- */
- public function setLog4jNamespace($flag) {
- $this->log4jNamespace = LoggerOptionConverter::toBoolean($flag,
$this->getLog4jNamespace());
- }
-
- /**
- * @param integer
- */
- public function setPort($port) {
- $port = LoggerOptionConverter::toInt($port, 0);
- if($port > 0 and $port < 65535) {
- $this->port = $port;
+ /** Sets the timeout. */
+ public function setTimeout($timeout) {
+ try {
+ $this->timeout =
LoggerOptionConverter::toIntegerEx($timeout);
+ } catch (Exception $ex) {
+ $value = var_export($timeout);
+ $default = var_export($this->timeout);
+ $this->warn("Invalid value provided for 'timeout'
[$value]. Expeceted an integer. Using default value [$default].");
}
}
- /**
- * @param string
- */
- public function setRemoteHost($hostname) {
- $this->remoteHost = $hostname;
+ /** Returns the target host. */
+ public function getRemoteHost() {
+ return $this->getRemoteHost();
}
- /**
- * @param integer
- */
- public function setTimeout($timeout) {
- $this->timeout = LoggerOptionConverter::toInt($timeout,
$this->getTimeout());
+ /** Returns the target port. */
+ public function getPort() {
+ return $this->port;
}
- /**
- * @param mixed
- */
- public function setUseXml($flag) {
- $this->useXml = LoggerOptionConverter::toBoolean($flag,
$this->getUseXml());
- }
-
- public function append(LoggerLoggingEvent $event) {
- if($this->sp || $this->dry) {
- if($this->getLocationInfo()) {
- $event->getLocationInformation();
- }
-
- if(!$this->getUseXml()) {
- $sEvent = serialize($event);
- if(!$this->dry) {
- fwrite($this->sp, $sEvent,
strlen($sEvent));
- } else {
- echo "DRY MODE OF SOCKET APPENDER:
".$sEvent;
- }
- } else {
- if(!$this->dry) {
- fwrite($this->sp,
$this->xmlLayout->format($event));
- } else {
- echo "DRY MODE OF SOCKET APPENDER:
".$this->xmlLayout->format($event);
- }
- }
-
- // not sure about it...
- if(!$this->dry) {
- fflush($this->sp);
- }
- }
+ /** Returns the timeout */
+ public function getTimeout() {
+ return $this->timeout;
}
}
Modified: logging/log4php/trunk/src/site/xdoc/docs/appenders/socket.xml
URL:
http://svn.apache.org/viewvc/logging/log4php/trunk/src/site/xdoc/docs/appenders/socket.xml?rev=1211929&r1=1211928&r2=1211929&view=diff
==============================================================================
--- logging/log4php/trunk/src/site/xdoc/docs/appenders/socket.xml (original)
+++ logging/log4php/trunk/src/site/xdoc/docs/appenders/socket.xml Thu Dec 8
15:10:45 2011
@@ -25,9 +25,11 @@
<body>
<section name="LoggerAppenderSocket">
- <p><code>LoggerAppenderSocket</code> serializes log
events and sends them to a network socket.</p>
+ <p><code>LoggerAppenderSocket</code> appends to a
network socket.</p>
- <p>This appender does not require a layout.</p>
+ <p>Unlike most other appenders, the default layout for
this appender is <code>LoggerLayoutSerialized</code>.</p>
+
+ <p>Prior to version <code>2.2</code>, a layout was not
required.</p>
<subsection name="Options">
<p>The following options are available:</p>
@@ -63,41 +65,16 @@
<td>timeout</td>
<td>integer</td>
<td>No</td>
- <td>30</td>
+
<td><code>ini_get('default_socket_timeout')</code></td>
<td>Timeout in ms.</td>
</tr>
- <tr>
- <td>useXml</td>
- <td>boolean</td>
- <td>No</td>
- <td>false</td>
- <td>If set to
<em>true</em> the appender will sent the event formatted in XML. Otherwise,
- the appender will send
the event as a serialized PHP object.</td>
- </tr>
- <tr>
- <td>locationInfo</td>
- <td>boolean</td>
- <td>No</td>
- <td>false</td>
- <td>Whether location
info is included for the XML event format. Ignored if XML format is not
used.</td>
- </tr>
- <tr>
- <td>log4jNamespace</td>
- <td>boolean</td>
- <td>No</td>
- <td>false</td>
- <td>In XML format,
<code>log4php</code> namespace is used by default. If set to true,
- <code>log4j</code>
namespace will be used instead.</td>
- </tr>
</tbody>
</table>
-
</subsection>
<subsection name="Examples">
- <p>In this example, log events are sent to
localhost:4242, using the serialized format. The host
- will recieve a serialized LoggerLoggingEvent
object.</p>
+ <p>In this example, log events are sent to
localhost:4242, using the default (serialized) layout.</p>
<p>XML configuration:</p>
Modified:
logging/log4php/trunk/src/test/php/appenders/LoggerAppenderSocketTest.php
URL:
http://svn.apache.org/viewvc/logging/log4php/trunk/src/test/php/appenders/LoggerAppenderSocketTest.php?rev=1211929&r1=1211928&r2=1211929&view=diff
==============================================================================
--- logging/log4php/trunk/src/test/php/appenders/LoggerAppenderSocketTest.php
(original)
+++ logging/log4php/trunk/src/test/php/appenders/LoggerAppenderSocketTest.php
Thu Dec 8 15:10:45 2011
@@ -6,16 +6,16 @@
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
- * @category tests
+ *
+ * @category tests
* @package log4php
* @subpackage appenders
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License,
Version 2.0
@@ -27,66 +27,123 @@
* @group appenders
*/
class LoggerAppenderSocketTest extends PHPUnit_Framework_TestCase {
-
+
+ /** Port on which the socket server will run. */
+ const SOCKET_PORT = 12345;
+
+ /** The socket server process resource. */
+ private $server;
+
+ /** The pipes array for the server process. */
+ private $pipes;
+
+ public function setUp() {
+ Logger::clear();
+ }
+
+ public function tearDown() {
+ Logger::clear();
+ }
+
public function testRequiresLayout() {
$appender = new LoggerAppenderSocket();
- self::assertFalse($appender->requiresLayout());
+ self::assertTrue($appender->requiresLayout());
}
- public function testSocketSerialized() {
- $appender = new LoggerAppenderSocket("myname ");
+ public function testLogging()
+ {
+ Logger::configure(array(
+ 'appenders' => array(
+ 'default' => array(
+ 'class' => 'LoggerAppenderSocket',
+ 'params' => array(
+ 'remoteHost' => 'localhost',
+ 'port' => self::SOCKET_PORT
+ ),
+ 'layout' => array(
+ 'class' => 'LoggerLayoutSimple'
+ )
+ ),
+ ),
+ 'rootLogger' => array(
+ 'appenders' => array('default'),
+ ),
+ ));
+
+ $this->startServer();
+
+ $logger = Logger::getLogger("myLogger");
+ $logger->trace("This message is a test");
+ $logger->debug("This message is a test");
+ $logger->info("This message is a test");
+ $logger->warn("This message is a test");
+ $logger->error("This message is a test");
+ $logger->fatal("This message is a test");
- $layout = new LoggerLayoutSimple();
- $appender->setLayout($layout);
- $appender->setDry(true);
-// $appender->setUseXml(true);
- $appender->activateOptions();
- $event = new LoggerLoggingEvent("LoggerAppenderSocketTest", new
Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-
- ob_start();
- $appender->append($event);
- $v = ob_get_contents();
- ob_end_clean();
- $s = serialize($event);
-
- $e = "DRY MODE OF SOCKET APPENDER: ".$s;
- self::assertEquals($e, $v);
- }
-
- public function testSocketXml() {
- $appender = new LoggerAppenderSocket("myname ");
-
- $appender->setDry(true);
- $appender->setUseXml(true);
- $appender->setLocationInfo(true);
- $appender->activateOptions();
- $event = new LoggerLoggingEvent("LoggerAppenderSocketTest", new
Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-
- ob_start();
- $appender->append($event);
- $v = ob_get_contents();
- ob_end_clean();
-
- $layout = new LoggerLayoutXml();
- $layout->setLog4jNamespace(false);
- $layout->activateOptions();
- $a = $layout->format($event);
- $e = "DRY MODE OF SOCKET APPENDER: ".$a;
- self::assertEquals($e, $v);
- }
-
- /** Tests Exception due to unreachable remote host.
- *
- * @expectedException LoggerException
- */
- public function testSocketProblem() {
- $appender = new LoggerAppenderSocket("myname ");
- $appender->setDry(false);
- $appender->setRemoteHost("does.not.exists");
- $appender->setPort(1234);
- $appender->activateOptions();
- $event = new LoggerLoggingEvent("LoggerAppenderSocketTest", new
Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-
- $appender->append($event);
- }
+ $actual = $this->getPlayback();
+ $this->stopServer();
+
+ $expected = "DEBUG - This message is a test" .
+ "INFO - This message is a test" .
+ "WARN - This message is a test" .
+ "ERROR - This message is a test" .
+ "FATAL - This message is a test";
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /** Starts a socket server in a separate process. */
+ private function startServer() {
+ $serverLog = PHPUNIT_TEMP_DIR . '/socketServer.log';
+ $descriptorspec = array(
+ 0 => array("pipe", "r"), // stdin
+ 1 => array("file", $serverLog, "a"),// stdout
+ 2 => array("file", $serverLog, "a") // stderr
+ );
+
+ $cmd = "php " . dirname(__FILE__) . '/socketServer.php';
+ $this->server = proc_open($cmd, $descriptorspec, $this->pipes);
+ if ($this->server === false) {
+ throw new Exception("Failed starting the socket server
process.");
+ }
+
+ // Sleep a bit to allow server to start
+ usleep(200000);
+
+ // Verify the server is running
+ $status = proc_get_status($this->server);
+ if (!$status['running']) {
+ throw new Exception("Socket server process failed to
start. Check the log at [$serverLog].");
+ }
+ }
+
+ /** Sends a message to the socket server and returns the reply. */
+ private function socketSend($msg) {
+ $sock = fsockopen('localhost', self::SOCKET_PORT, $errno,
$errstr);
+ if ($sock === false) {
+ throw new Exception("Unable to open socket. Error:
[$errno] $errstr");
+ }
+
+ fputs($sock, "$msg\n");
+ $reply = '';
+ while(!feof($sock)) {
+ $reply .= fgets($sock);
+ }
+ fclose($sock);
+ return trim($reply);
+ }
+
+ /** Retrieves a playback of all sent messages from the socket server. */
+ private function getPlayback() {
+ return $this->socketSend('playback');
+ }
+
+ /** Stops the socket server and closes the process. */
+ private function stopServer() {
+ $this->socketSend('shutdown');
+ foreach($this->pipes as $pipe) {
+ fclose($pipe);
+ }
+ proc_close($this->server);
+ }
}
Added: logging/log4php/trunk/src/test/php/appenders/socketServer.php
URL:
http://svn.apache.org/viewvc/logging/log4php/trunk/src/test/php/appenders/socketServer.php?rev=1211929&view=auto
==============================================================================
--- logging/log4php/trunk/src/test/php/appenders/socketServer.php (added)
+++ logging/log4php/trunk/src/test/php/appenders/socketServer.php Thu Dec 8
15:10:45 2011
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @category tests
+ * @package log4php
+ * @subpackage appenders
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License,
Version 2.0
+ * @version SVN: $Id$
+ * @link http://logging.apache.org/log4php
+ *
+ * A simple socket server used in LoggerAppenderSocketTest.
+ */
+
+// Port on which to start the server
+define('SERVER_PORT', 12345);
+
+// Prevent hangs
+set_time_limit(0);
+
+// Create a socket
+$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+if ($sock === false) {
+ die("Failed creating socket: " . socket_strerror(socket_last_error()));
+}
+
+if (socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1) === false) {
+ die("Failed setting socket options: " .
socket_strerror(socket_last_error()));
+}
+
+if (socket_bind($sock, 'localhost', SERVER_PORT) === false) {
+ die("Failed binding socket: " . socket_strerror(socket_last_error()));
+}
+
+if (socket_listen($sock, 100) === false) {
+ die("Failed binding socket: " . socket_strerror(socket_last_error()));
+}
+
+socket_getsockname($sock, $addr, $port);
+myLog("Server Listening on $addr:$port");
+
+// Buffer which will store incoming messages
+$playback = "";
+
+while(true) {
+ myLog("Waiting for incoming connections...");
+
+ $msgsock = socket_accept($sock);
+ if ($msgsock === false) {
+ myLog("Failed accepting a connection: " .
socket_strerror(socket_last_error()));
+ break;
+ }
+
+ $buf = socket_read($msgsock, 2048, PHP_NORMAL_READ);
+
+ myLog('Received: "' . trim($buf) . '"');
+
+ // Shutdown command
+ if (trim($buf) == 'shutdown') {
+ myLog("Shutting down.");
+ socket_close($msgsock);
+ break;
+ }
+ // Playback command
+ else if (trim($buf) == 'playback') {
+ myLog("Returning playback: \"$playback\"");
+ socket_write($msgsock, $playback);
+ }
+ // Default: add to playback buffer
+ else {
+ $playback .= trim($buf);
+ }
+
+ socket_close($msgsock);
+}
+
+myLog("Closing socket.");
+socket_close($sock);
+
+function myLog($msg) {
+ echo date("Y-m-d H:i:s") . " $msg\n";
+}
+
+?>