upload
[pear] / Log.php
1 <?php
2 /**
3  * $Header$
4  * $Horde: horde/lib/Log.php,v 1.15 2000/06/29 23:39:45 jon Exp $
5  *
6  * @version $Revision: 284340 $
7  * @package Log
8  */
9
10 define('PEAR_LOG_EMERG',    0);     /* System is unusable */
11 define('PEAR_LOG_ALERT',    1);     /* Immediate action required */
12 define('PEAR_LOG_CRIT',     2);     /* Critical conditions */
13 define('PEAR_LOG_ERR',      3);     /* Error conditions */
14 define('PEAR_LOG_WARNING',  4);     /* Warning conditions */
15 define('PEAR_LOG_NOTICE',   5);     /* Normal but significant */
16 define('PEAR_LOG_INFO',     6);     /* Informational */
17 define('PEAR_LOG_DEBUG',    7);     /* Debug-level messages */
18
19 define('PEAR_LOG_ALL',      0xffffffff);    /* All messages */
20 define('PEAR_LOG_NONE',     0x00000000);    /* No message */
21
22 /* Log types for PHP's native error_log() function. */
23 define('PEAR_LOG_TYPE_SYSTEM',  0); /* Use PHP's system logger */
24 define('PEAR_LOG_TYPE_MAIL',    1); /* Use PHP's mail() function */
25 define('PEAR_LOG_TYPE_DEBUG',   2); /* Use PHP's debugging connection */
26 define('PEAR_LOG_TYPE_FILE',    3); /* Append to a file */
27
28 /**
29  * The Log:: class implements both an abstraction for various logging
30  * mechanisms and the Subject end of a Subject-Observer pattern.
31  *
32  * @author  Chuck Hagenbuch <chuck@horde.org>
33  * @author  Jon Parise <jon@php.net>
34  * @since   Horde 1.3
35  * @package Log
36  */
37 class Log
38 {
39     /**
40      * Indicates whether or not the log can been opened / connected.
41      *
42      * @var boolean
43      * @access protected
44      */
45     var $_opened = false;
46
47     /**
48      * Instance-specific unique identification number.
49      *
50      * @var integer
51      * @access protected
52      */
53     var $_id = 0;
54
55     /**
56      * The label that uniquely identifies this set of log messages.
57      *
58      * @var string
59      * @access protected
60      */
61     var $_ident = '';
62
63     /**
64      * The default priority to use when logging an event.
65      *
66      * @var integer
67      * @access protected
68      */
69     var $_priority = PEAR_LOG_INFO;
70
71     /**
72      * The bitmask of allowed log levels.
73      *
74      * @var integer
75      * @access protected
76      */
77     var $_mask = PEAR_LOG_ALL;
78
79     /**
80      * Holds all Log_observer objects that wish to be notified of new messages.
81      *
82      * @var array
83      * @access protected
84      */
85     var $_listeners = array();
86
87     /**
88      * Maps canonical format keys to position arguments for use in building
89      * "line format" strings.
90      *
91      * @var array
92      * @access protected
93      */
94     var $_formatMap = array('%{timestamp}'  => '%1$s',
95                             '%{ident}'      => '%2$s',
96                             '%{priority}'   => '%3$s',
97                             '%{message}'    => '%4$s',
98                             '%{file}'       => '%5$s',
99                             '%{line}'       => '%6$s',
100                             '%{function}'   => '%7$s',
101                             '%{class}'      => '%8$s',
102                             '%\{'           => '%%{');
103
104     /**
105      * Utility function which wraps PHP's class_exists() function to ensure
106      * consistent behavior between PHP versions 4 and 5.  Autoloading behavior
107      * is always disabled.
108      *
109      * @param string $class     The name of the class whose existence should
110      *                          be tested.
111      *
112      * @return bool             True if the class exists.
113      *
114      * @access private
115      * @since Log 1.9.13
116      */
117     function _classExists($class)
118     {
119         if (version_compare(PHP_VERSION, '5.0.0', 'ge')) {
120             return class_exists($class, false);
121         }
122
123         return class_exists($class);
124     }
125
126     /**
127      * Attempts to return a concrete Log instance of type $handler.
128      *
129      * @param string $handler   The type of concrete Log subclass to return.
130      *                          Attempt to dynamically include the code for
131      *                          this subclass. Currently, valid values are
132      *                          'console', 'syslog', 'sql', 'file', and 'mcal'.
133      *
134      * @param string $name      The name of the actually log file, table, or
135      *                          other specific store to use. Defaults to an
136      *                          empty string, with which the subclass will
137      *                          attempt to do something intelligent.
138      *
139      * @param string $ident     The identity reported to the log system.
140      *
141      * @param array  $conf      A hash containing any additional configuration
142      *                          information that a subclass might need.
143      *
144      * @param int $level        Log messages up to and including this level.
145      *
146      * @return object Log       The newly created concrete Log instance, or
147      *                          null on an error.
148      * @access public
149      * @since Log 1.0
150      */
151     function &factory($handler, $name = '', $ident = '', $conf = array(),
152                       $level = PEAR_LOG_DEBUG)
153     {
154         $handler = strtolower($handler);
155         $class = 'Log_' . $handler;
156         $classfile = 'Log/' . $handler . '.php';
157
158         /*
159          * Attempt to include our version of the named class, but don't treat
160          * a failure as fatal.  The caller may have already included their own
161          * version of the named class.
162          */
163         if (!Log::_classExists($class)) {
164             include_once $classfile;
165         }
166
167         /* If the class exists, return a new instance of it. */
168         if (Log::_classExists($class)) {
169             $obj = &new $class($name, $ident, $conf, $level);
170             return $obj;
171         }
172
173         $null = null;
174         return $null;
175     }
176
177     /**
178      * Attempts to return a reference to a concrete Log instance of type
179      * $handler, only creating a new instance if no log instance with the same
180      * parameters currently exists.
181      *
182      * You should use this if there are multiple places you might create a
183      * logger, you don't want to create multiple loggers, and you don't want to
184      * check for the existance of one each time. The singleton pattern does all
185      * the checking work for you.
186      *
187      * <b>You MUST call this method with the $var = &Log::singleton() syntax.
188      * Without the ampersand (&) in front of the method name, you will not get
189      * a reference, you will get a copy.</b>
190      *
191      * @param string $handler   The type of concrete Log subclass to return.
192      *                          Attempt to dynamically include the code for
193      *                          this subclass. Currently, valid values are
194      *                          'console', 'syslog', 'sql', 'file', and 'mcal'.
195      *
196      * @param string $name      The name of the actually log file, table, or
197      *                          other specific store to use.  Defaults to an
198      *                          empty string, with which the subclass will
199      *                          attempt to do something intelligent.
200      *
201      * @param string $ident     The identity reported to the log system.
202      *
203      * @param array $conf       A hash containing any additional configuration
204      *                          information that a subclass might need.
205      *
206      * @param int $level        Log messages up to and including this level.
207      *
208      * @return object Log       The newly created concrete Log instance, or
209      *                          null on an error.
210      * @access public
211      * @since Log 1.0
212      */
213     function &singleton($handler, $name = '', $ident = '', $conf = array(),
214                         $level = PEAR_LOG_DEBUG)
215     {
216         static $instances;
217         if (!isset($instances)) $instances = array();
218
219         $signature = serialize(array($handler, $name, $ident, $conf, $level));
220         if (!isset($instances[$signature])) {
221             $instances[$signature] = &Log::factory($handler, $name, $ident,
222                                                    $conf, $level);
223         }
224
225         return $instances[$signature];
226     }
227
228     /**
229      * Abstract implementation of the open() method.
230      * @since Log 1.0
231      */
232     function open()
233     {
234         return false;
235     }
236
237     /**
238      * Abstract implementation of the close() method.
239      * @since Log 1.0
240      */
241     function close()
242     {
243         return false;
244     }
245
246     /**
247      * Abstract implementation of the flush() method.
248      * @since Log 1.8.2
249      */
250     function flush()
251     {
252         return false;
253     }
254
255     /**
256      * Abstract implementation of the log() method.
257      * @since Log 1.0
258      */
259     function log($message, $priority = null)
260     {
261         return false;
262     }
263
264     /**
265      * A convenience function for logging a emergency event.  It will log a
266      * message at the PEAR_LOG_EMERG log level.
267      *
268      * @param   mixed   $message    String or object containing the message
269      *                              to log.
270      *
271      * @return  boolean True if the message was successfully logged.
272      *
273      * @access  public
274      * @since   Log 1.7.0
275      */
276     function emerg($message)
277     {
278         return $this->log($message, PEAR_LOG_EMERG);
279     }
280
281     /**
282      * A convenience function for logging an alert event.  It will log a
283      * message at the PEAR_LOG_ALERT log level.
284      *
285      * @param   mixed   $message    String or object containing the message
286      *                              to log.
287      *
288      * @return  boolean True if the message was successfully logged.
289      *
290      * @access  public
291      * @since   Log 1.7.0
292      */
293     function alert($message)
294     {
295         return $this->log($message, PEAR_LOG_ALERT);
296     }
297
298     /**
299      * A convenience function for logging a critical event.  It will log a
300      * message at the PEAR_LOG_CRIT log level.
301      *
302      * @param   mixed   $message    String or object containing the message
303      *                              to log.
304      *
305      * @return  boolean True if the message was successfully logged.
306      *
307      * @access  public
308      * @since   Log 1.7.0
309      */
310     function crit($message)
311     {
312         return $this->log($message, PEAR_LOG_CRIT);
313     }
314
315     /**
316      * A convenience function for logging a error event.  It will log a
317      * message at the PEAR_LOG_ERR log level.
318      *
319      * @param   mixed   $message    String or object containing the message
320      *                              to log.
321      *
322      * @return  boolean True if the message was successfully logged.
323      *
324      * @access  public
325      * @since   Log 1.7.0
326      */
327     function err($message)
328     {
329         return $this->log($message, PEAR_LOG_ERR);
330     }
331
332     /**
333      * A convenience function for logging a warning event.  It will log a
334      * message at the PEAR_LOG_WARNING log level.
335      *
336      * @param   mixed   $message    String or object containing the message
337      *                              to log.
338      *
339      * @return  boolean True if the message was successfully logged.
340      *
341      * @access  public
342      * @since   Log 1.7.0
343      */
344     function warning($message)
345     {
346         return $this->log($message, PEAR_LOG_WARNING);
347     }
348
349     /**
350      * A convenience function for logging a notice event.  It will log a
351      * message at the PEAR_LOG_NOTICE log level.
352      *
353      * @param   mixed   $message    String or object containing the message
354      *                              to log.
355      *
356      * @return  boolean True if the message was successfully logged.
357      *
358      * @access  public
359      * @since   Log 1.7.0
360      */
361     function notice($message)
362     {
363         return $this->log($message, PEAR_LOG_NOTICE);
364     }
365
366     /**
367      * A convenience function for logging a information event.  It will log a
368      * message at the PEAR_LOG_INFO log level.
369      *
370      * @param   mixed   $message    String or object containing the message
371      *                              to log.
372      *
373      * @return  boolean True if the message was successfully logged.
374      *
375      * @access  public
376      * @since   Log 1.7.0
377      */
378     function info($message)
379     {
380         return $this->log($message, PEAR_LOG_INFO);
381     }
382
383     /**
384      * A convenience function for logging a debug event.  It will log a
385      * message at the PEAR_LOG_DEBUG log level.
386      *
387      * @param   mixed   $message    String or object containing the message
388      *                              to log.
389      *
390      * @return  boolean True if the message was successfully logged.
391      *
392      * @access  public
393      * @since   Log 1.7.0
394      */
395     function debug($message)
396     {
397         return $this->log($message, PEAR_LOG_DEBUG);
398     }
399
400     /**
401      * Returns the string representation of the message data.
402      *
403      * If $message is an object, _extractMessage() will attempt to extract
404      * the message text using a known method (such as a PEAR_Error object's
405      * getMessage() method).  If a known method, cannot be found, the
406      * serialized representation of the object will be returned.
407      *
408      * If the message data is already a string, it will be returned unchanged.
409      *
410      * @param  mixed $message   The original message data.  This may be a
411      *                          string or any object.
412      *
413      * @return string           The string representation of the message.
414      *
415      * @access protected
416      */
417     function _extractMessage($message)
418     {
419         /*
420          * If we've been given an object, attempt to extract the message using
421          * a known method.  If we can't find such a method, default to the
422          * "human-readable" version of the object.
423          *
424          * We also use the human-readable format for arrays.
425          */
426         if (is_object($message)) {
427             if (method_exists($message, 'getmessage')) {
428                 $message = $message->getMessage();
429             } else if (method_exists($message, 'tostring')) {
430                 $message = $message->toString();
431             } else if (method_exists($message, '__tostring')) {
432                 if (version_compare(PHP_VERSION, '5.0.0', 'ge')) {
433                     $message = (string)$message;
434                 } else {
435                     $message = $message->__toString();
436                 }
437             } else {
438                 $message = var_export($message, true);
439             }
440         } else if (is_array($message)) {
441             if (isset($message['message'])) {
442                 if (is_scalar($message['message'])) {
443                     $message = $message['message'];
444                 } else {
445                     $message = var_export($message['message'], true);
446                 }
447             } else {
448                 $message = var_export($message, true);
449             }
450         } else if (is_bool($message) || $message === NULL) {
451             $message = var_export($message, true);
452         }
453
454         /* Otherwise, we assume the message is a string. */
455         return $message;
456     }
457
458     /**
459      * Using debug_backtrace(), returns the file, line, and enclosing function
460      * name of the source code context from which log() was invoked.
461      *
462      * @param   int     $depth  The initial number of frames we should step
463      *                          back into the trace.
464      *
465      * @return  array   Array containing four strings: the filename, the line,
466      *                  the function name, and the class name from which log()
467      *                  was called.
468      *
469      * @access  private
470      * @since   Log 1.9.4
471      */
472     function _getBacktraceVars($depth)
473     {
474         /* Start by generating a backtrace from the current call (here). */
475         $bt = debug_backtrace();
476
477         /*
478          * If we were ultimately invoked by the composite handler, we need to
479          * increase our depth one additional level to compensate.
480          */
481         $class = isset($bt[$depth+1]['class']) ? $bt[$depth+1]['class'] : null;
482         if ($class !== null && strcasecmp($class, 'Log_composite') == 0) {
483             $depth++;
484             $class = isset($bt[$depth + 1]) ? $bt[$depth + 1]['class'] : null;
485         }
486
487         /*
488          * We're interested in the frame which invoked the log() function, so
489          * we need to walk back some number of frames into the backtrace.  The
490          * $depth parameter tells us where to start looking.   We go one step
491          * further back to find the name of the encapsulating function from
492          * which log() was called.
493          */
494         $file = isset($bt[$depth])     ? $bt[$depth]['file'] : null;
495         $line = isset($bt[$depth])     ? $bt[$depth]['line'] : 0;
496         $func = isset($bt[$depth + 1]) ? $bt[$depth + 1]['function'] : null;
497
498         /*
499          * However, if log() was called from one of our "shortcut" functions,
500          * we're going to need to go back an additional step.
501          */
502         if (in_array($func, array('emerg', 'alert', 'crit', 'err', 'warning',
503                                   'notice', 'info', 'debug'))) {
504             $file = isset($bt[$depth + 1]) ? $bt[$depth + 1]['file'] : null;
505             $line = isset($bt[$depth + 1]) ? $bt[$depth + 1]['line'] : 0;
506             $func = isset($bt[$depth + 2]) ? $bt[$depth + 2]['function'] : null;
507             $class = isset($bt[$depth + 2]) ? $bt[$depth + 2]['class'] : null;
508         }
509
510         /*
511          * If we couldn't extract a function name (perhaps because we were
512          * executed from the "main" context), provide a default value.
513          */
514         if (is_null($func)) {
515             $func = '(none)';
516         }
517
518         /* Return a 4-tuple containing (file, line, function, class). */
519         return array($file, $line, $func, $class);
520     }
521
522     /**
523      * Produces a formatted log line based on a format string and a set of
524      * variables representing the current log record and state.
525      *
526      * @return  string  Formatted log string.
527      *
528      * @access  protected
529      * @since   Log 1.9.4
530      */
531     function _format($format, $timestamp, $priority, $message)
532     {
533         /*
534          * If the format string references any of the backtrace-driven
535          * variables (%5 %6,%7,%8), generate the backtrace and fetch them.
536          */
537         if (preg_match('/%[5678]/', $format)) {
538             list($file, $line, $func, $class) = $this->_getBacktraceVars(2);
539         }
540
541         /*
542          * Build the formatted string.  We use the sprintf() function's
543          * "argument swapping" capability to dynamically select and position
544          * the variables which will ultimately appear in the log string.
545          */
546         return sprintf($format,
547                        $timestamp,
548                        $this->_ident,
549                        $this->priorityToString($priority),
550                        $message,
551                        isset($file) ? $file : '',
552                        isset($line) ? $line : '',
553                        isset($func) ? $func : '',
554                        isset($class) ? $class : '');
555     }
556
557     /**
558      * Returns the string representation of a PEAR_LOG_* integer constant.
559      *
560      * @param int $priority     A PEAR_LOG_* integer constant.
561      *
562      * @return string           The string representation of $level.
563      *
564      * @access  public
565      * @since   Log 1.0
566      */
567     function priorityToString($priority)
568     {
569         $levels = array(
570             PEAR_LOG_EMERG   => 'emergency',
571             PEAR_LOG_ALERT   => 'alert',
572             PEAR_LOG_CRIT    => 'critical',
573             PEAR_LOG_ERR     => 'error',
574             PEAR_LOG_WARNING => 'warning',
575             PEAR_LOG_NOTICE  => 'notice',
576             PEAR_LOG_INFO    => 'info',
577             PEAR_LOG_DEBUG   => 'debug'
578         );
579
580         return $levels[$priority];
581     }
582
583     /**
584      * Returns the the PEAR_LOG_* integer constant for the given string
585      * representation of a priority name.  This function performs a
586      * case-insensitive search.
587      *
588      * @param string $name      String containing a priority name.
589      *
590      * @return string           The PEAR_LOG_* integer contstant corresponding
591      *                          the the specified priority name.
592      *
593      * @access  public
594      * @since   Log 1.9.0
595      */
596     function stringToPriority($name)
597     {
598         $levels = array(
599             'emergency' => PEAR_LOG_EMERG,
600             'alert'     => PEAR_LOG_ALERT,
601             'critical'  => PEAR_LOG_CRIT,
602             'error'     => PEAR_LOG_ERR,
603             'warning'   => PEAR_LOG_WARNING,
604             'notice'    => PEAR_LOG_NOTICE,
605             'info'      => PEAR_LOG_INFO,
606             'debug'     => PEAR_LOG_DEBUG
607         );
608
609         return $levels[strtolower($name)];
610     }
611
612     /**
613      * Calculate the log mask for the given priority.
614      *
615      * This method may be called statically.
616      *
617      * @param integer   $priority   The priority whose mask will be calculated.
618      *
619      * @return integer  The calculated log mask.
620      *
621      * @access  public
622      * @since   Log 1.7.0
623      */
624     function MASK($priority)
625     {
626         return (1 << $priority);
627     }
628
629     /**
630      * Calculate the log mask for all priorities up to the given priority.
631      *
632      * This method may be called statically.
633      *
634      * @param integer   $priority   The maximum priority covered by this mask.
635      *
636      * @return integer  The resulting log mask.
637      *
638      * @access  public
639      * @since   Log 1.7.0
640      *
641      * @deprecated deprecated since Log 1.9.4; use Log::MAX() instead
642      */
643     function UPTO($priority)
644     {
645         return Log::MAX($priority);
646     }
647
648     /**
649      * Calculate the log mask for all priorities greater than or equal to the
650      * given priority.  In other words, $priority will be the lowest priority
651      * matched by the resulting mask.
652      *
653      * This method may be called statically.
654      *
655      * @param integer   $priority   The minimum priority covered by this mask.
656      *
657      * @return integer  The resulting log mask.
658      *
659      * @access  public
660      * @since   Log 1.9.4
661      */
662     function MIN($priority)
663     {
664         return PEAR_LOG_ALL ^ ((1 << $priority) - 1);
665     }
666
667     /**
668      * Calculate the log mask for all priorities less than or equal to the
669      * given priority.  In other words, $priority will be the highests priority
670      * matched by the resulting mask.
671      *
672      * This method may be called statically.
673      *
674      * @param integer   $priority   The maximum priority covered by this mask.
675      *
676      * @return integer  The resulting log mask.
677      *
678      * @access  public
679      * @since   Log 1.9.4
680      */
681     function MAX($priority)
682     {
683         return ((1 << ($priority + 1)) - 1);
684     }
685
686     /**
687      * Set and return the level mask for the current Log instance.
688      *
689      * @param integer $mask     A bitwise mask of log levels.
690      *
691      * @return integer          The current level mask.
692      *
693      * @access  public
694      * @since   Log 1.7.0
695      */
696     function setMask($mask)
697     {
698         $this->_mask = $mask;
699
700         return $this->_mask;
701     }
702
703     /**
704      * Returns the current level mask.
705      *
706      * @return interger         The current level mask.
707      *
708      * @access  public
709      * @since   Log 1.7.0
710      */
711     function getMask()
712     {
713         return $this->_mask;
714     }
715
716     /**
717      * Check if the given priority is included in the current level mask.
718      *
719      * @param integer   $priority   The priority to check.
720      *
721      * @return boolean  True if the given priority is included in the current
722      *                  log mask.
723      *
724      * @access  protected
725      * @since   Log 1.7.0
726      */
727     function _isMasked($priority)
728     {
729         return (Log::MASK($priority) & $this->_mask);
730     }
731
732     /**
733      * Returns the current default priority.
734      *
735      * @return integer  The current default priority.
736      *
737      * @access  public
738      * @since   Log 1.8.4
739      */
740     function getPriority()
741     {
742         return $this->_priority;
743     }
744
745     /**
746      * Sets the default priority to the specified value.
747      *
748      * @param   integer $priority   The new default priority.
749      *
750      * @access  public
751      * @since   Log 1.8.4
752      */
753     function setPriority($priority)
754     {
755         $this->_priority = $priority;
756     }
757
758     /**
759      * Adds a Log_observer instance to the list of observers that are listening
760      * for messages emitted by this Log instance.
761      *
762      * @param object    $observer   The Log_observer instance to attach as a
763      *                              listener.
764      *
765      * @param boolean   True if the observer is successfully attached.
766      *
767      * @access  public
768      * @since   Log 1.0
769      */
770     function attach(&$observer)
771     {
772         if (!is_a($observer, 'Log_observer')) {
773             return false;
774         }
775
776         $this->_listeners[$observer->_id] = &$observer;
777
778         return true;
779     }
780
781     /**
782      * Removes a Log_observer instance from the list of observers.
783      *
784      * @param object    $observer   The Log_observer instance to detach from
785      *                              the list of listeners.
786      *
787      * @param boolean   True if the observer is successfully detached.
788      *
789      * @access  public
790      * @since   Log 1.0
791      */
792     function detach($observer)
793     {
794         if (!is_a($observer, 'Log_observer') ||
795             !isset($this->_listeners[$observer->_id])) {
796             return false;
797         }
798
799         unset($this->_listeners[$observer->_id]);
800
801         return true;
802     }
803
804     /**
805      * Informs each registered observer instance that a new message has been
806      * logged.
807      *
808      * @param array     $event      A hash describing the log event.
809      *
810      * @access protected
811      */
812     function _announce($event)
813     {
814         foreach ($this->_listeners as $id => $listener) {
815             if ($event['priority'] <= $this->_listeners[$id]->_priority) {
816                 $this->_listeners[$id]->notify($event);
817             }
818         }
819     }
820
821     /**
822      * Indicates whether this is a composite class.
823      *
824      * @return boolean          True if this is a composite class.
825      *
826      * @access  public
827      * @since   Log 1.0
828      */
829     function isComposite()
830     {
831         return false;
832     }
833
834     /**
835      * Sets this Log instance's identification string.
836      *
837      * @param string    $ident      The new identification string.
838      *
839      * @access  public
840      * @since   Log 1.6.3
841      */
842     function setIdent($ident)
843     {
844         $this->_ident = $ident;
845     }
846
847     /**
848      * Returns the current identification string.
849      *
850      * @return string   The current Log instance's identification string.
851      *
852      * @access  public
853      * @since   Log 1.6.3
854      */
855     function getIdent()
856     {
857         return $this->_ident;
858     }
859 }