r41901 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r41900‎ | r41901 | r41902 >
Date:21:33, 9 October 2008
Author:hashar
Status:old
Tags:
Comment:
A basic syslog class. Work in progress.
Modified paths:
  • /branches/hashar/syslog/includes/syslog.php (added) (history)

Diff [purge]

Index: branches/hashar/syslog/includes/syslog.php
@@ -0,0 +1,223 @@
 2+<?php
 3+/**
 4+ * A class to send BSD syslog messages to a remote host.
 5+ *
 6+ * @see http://tools.ietf.org/html/rfc3164
 7+ * @author Ashar Voultoiz <hashar _at_ free _dot_ fr>
 8+ *
 9+ */
 10+
 11+class mw_syslog {
 12+ /**
 13+ * Used to get/set the severity:
 14+ * 0 Emergency: system is unusable
 15+ * 1 Alert: action must be taken immediately
 16+ * 2 Critical: critical conditions
 17+ * 3 Error: error conditions
 18+ * 4 Warning: warning conditions
 19+ * 5 Notice: normal but significant condition
 20+ * 6 Informational: informational messages
 21+ * 7 Debug: debug-level messages
 22+ */
 23+
 24+ /** Internal class default options */
 25+ private $allowed_options = array(
 26+ 'facility' => 1, # user-level
 27+ 'severity' => 5, # LOG_NOTICE
 28+ 'server' => 'localhost',
 29+ 'port' => '514',
 30+ 'timeout' => '0',
 31+ 'prefix' => null,
 32+ );
 33+ private $options = array();
 34+
 35+ /** An UDP socket */
 36+ private $_socket = null;
 37+
 38+### PUBLIC METHODS ###
 39+ /**
 40+ * Create a syslog object with initial options.
 41+ * @param array $initial_options An array of options to initialize the object with.
 42+ */
 43+ public function __construct( $initial_options = null ) {
 44+ $this->options = $this->build_options_with( $this->allowed_options, $initial_options );
 45+ }
 46+
 47+ /**
 48+ * Send a message to the remote syslog.
 49+ * You can temporarly override the initial options.
 50+ */
 51+ public function send($msg, $options = null ) {
 52+ $this->reallySend($msg, $this->with_options( $options ) );
 53+ }
 54+
 55+### STATIC METHODS ###
 56+ /**
 57+ * Given a unix timestamp format the date according to RFC 3164 (syslog)
 58+ * A date looks like 'Dec 8 20:39:14', with a space padded day number.
 59+ *
 60+ * @param mixed $an_unix_timestamp Unix epoch timestamp
 61+ * @return Date formatted according to RFC 3164
 62+ */
 63+ public static function rfc3164_timestamp_of( $an_unix_timestamp ) {
 64+ # Per RFC syslog message MUST use localtime
 65+ $time = time();
 66+
 67+ # Pad day with a space:
 68+ $padded_day = str_pad(date('j', $time ), 2, ' ', STR_PAD_LEFT);
 69+
 70+ $timestamp = date( 'M ' . $padded_day. ' H:i:s', $time );
 71+
 72+ return $timestamp;
 73+ }
 74+
 75+
 76+### PRIVATE METHODS ###
 77+ /**
 78+ * Merge two array of options but only retains allowed one.
 79+ *
 80+ * @param array $base_opt Default options.
 81+ * @param array $new_opt Options we want to override.
 82+ **/
 83+ private static function build_options_with( $base_opt, $new_opt = null) {
 84+ if( is_null( $new_opt ) ) {
 85+ return $base_opt;
 86+ }
 87+
 88+ $build_up = array();
 89+
 90+ foreach( $this->allowed_options as $name => $default_value ) {
 91+ if( isset($new_opt[$name]) ) {
 92+ $build_up[$name] = $new_opt[$name];
 93+ } else {
 94+ $build_up[$name] = $default_value;
 95+ }
 96+ }
 97+ return $build_up;
 98+ }
 99+
 100+ /**
 101+ * Compute the priority based on facility and severity.
 102+ * The priority is a simple arithmetic operation but this function also
 103+ * sanitize the parameters according to RFC 3164 :
 104+ *
 105+ * Facility is >= 0 and <= 23
 106+ * Severity is >= 0 and <= 7
 107+ *
 108+ * @param Integer $facility
 109+ * @param $severity
 110+ * @return priority
 111+ */
 112+ private static function priority_of( $facility, $severity ) {
 113+
 114+ $facility = ($facility < 0) ? 0 : $facility;
 115+ $facility = ($facility > 23) ? 23 : $facility;
 116+
 117+ $severity = ($severity < 0) ? 0 : $severity;
 118+ $severity = ($severity > 7) ? 7 : $severity;
 119+
 120+ return (int) ($facility * 8 + $severity);
 121+ }
 122+
 123+ private function with_options( $given_options ) {
 124+ return self::build_options_with( $this->options, $given_options );
 125+ }
 126+
 127+
 128+ /**
 129+ * Build a packet using the RECOMMENDED RFC 3164 format.
 130+ * A packet is simply made of three parts: PRI, HEADER and MSG. For more
 131+ * details, have a look at http://tools.ietf.org/html/rfc3164 .
 132+ *
 133+ * @param integer $facility 0-23
 134+ * @param integer $severity 0-7
 135+ * @param integer $unix_ts An UNIX timestamp, will be represented in localtime
 136+ * @param string $hostname SHOULD be a hostname without FQDN but MAY be IPv4 or IPv6
 137+ * @param string $msg Freeform message. Please avoid stranges chars such as \\r ;)
 138+ *
 139+ * @return string The packet !
 140+ */
 141+ private static function build_packet(
 142+ $facility,
 143+ $severity,
 144+ $unix_ts,
 145+ $hostname,
 146+ $msg
 147+ ) {
 148+
 149+
 150+ ####################################################
 151+ # PRI, § 4.1.1
 152+ ####################################################
 153+ // MUST be 3 to 5 chars, enclosed by angle brackets
 154+ $priority = self::priority_of( $facility, $severity );
 155+ $PRI = "<$priority>";
 156+
 157+
 158+ ####################################################
 159+ # HEADER, § 4.1.2
 160+ ####################################################
 161+ $timestamp = self::rfc3164_timestamp_of( $unix_ts );
 162+ // FIXME hostname MUST be without fqdn!
 163+ $hostname = $hostname;
 164+ $HEADER = "$timestamp $hostname "; // fields MUST be terminated with a space
 165+
 166+
 167+ ####################################################
 168+ # MSG, § 4.1.3
 169+ ####################################################
 170+ $tag = 'MediaWiki'; // MUST NOT exceed 32 alpha numerics characters
 171+ $content = $msg;
 172+ # TAG and CONTENT are separated by a non alpha numeric char usually
 173+ # followed by a space.
 174+ # Also see § 5.3 if you want to add PID. Prefix should be in content
 175+ $MSG = "$tag: $content";
 176+
 177+ // Send back the build packet
 178+ return $PRI.$HEADER.$MSG ;
 179+
 180+ }
 181+
 182+ /**
 183+ * @Param string $server
 184+ * @Param integer $port
 185+ * @Param integer $timeout
 186+ */
 187+ private function openSocket( $server, $port, $timeout ) {
 188+ $_erno = $_erstr = 0;
 189+ $this->_socket = fsockopen( "udp://$server", $port, $_erno, $_erstr, $timeout );
 190+ if( !$this->_socket ) {
 191+ die("failed to open socket : $_erno, $_erstr\n"); # FIXME
 192+ }
 193+ return (true === $this->_socket) ;
 194+ }
 195+
 196+ /** Close the socket */
 197+ private function closeSocket() {
 198+ if( $this->_socket ) {
 199+ fclose( $this->_socket );
 200+ $this->_socket = null;
 201+ }
 202+ }
 203+
 204+ /**
 205+ * @param string $msg
 206+ * @param $opt Our syslog options.
 207+ */
 208+ private function reallySend( $msg, $opt ) {
 209+ $packet = self::build_packet(
 210+ $opt['facility'],
 211+ $opt['severity'],
 212+ time(),
 213+ wfHostname(), # FIXME is this always correct AND cached ?
 214+ $msg
 215+ );
 216+
 217+ # Open socket, send message and close it. If you ever change it to use
 218+ # a persistent file handle, beware !! server and port can be overriden
 219+ # on a per message basis.
 220+ $this->openSocket( $opt['server'], $opt['port'], $opt['timeout'] );
 221+ fwrite( $this->_socket, $packet );
 222+ $this->closeSocket();
 223+ }
 224+}

Status & tagging log