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