library/log4php/LoggerNDC.php
changeset 46 f11c31f7fa3e
parent 45 a56e7f9a0463
child 47 03388ec805b4
equal deleted inserted replaced
45:a56e7f9a0463 46:f11c31f7fa3e
     1 <?php
       
     2 /**
       
     3  * Licensed to the Apache Software Foundation (ASF) under one or more
       
     4  * contributor license agreements. See the NOTICE file distributed with
       
     5  * this work for additional information regarding copyright ownership.
       
     6  * The ASF licenses this file to You under the Apache License, Version 2.0
       
     7  * (the "License"); you may not use this file except in compliance with
       
     8  * the License. You may obtain a copy of the License at
       
     9  *
       
    10  *       http://www.apache.org/licenses/LICENSE-2.0
       
    11  *
       
    12  * Unless required by applicable law or agreed to in writing, software
       
    13  * distributed under the License is distributed on an "AS IS" BASIS,
       
    14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    15  * See the License for the specific language governing permissions and
       
    16  * limitations under the License.
       
    17  *
       
    18  * @package log4php
       
    19  */
       
    20 
       
    21 /**
       
    22  * The NDC class implements <i>nested diagnostic contexts</i>.
       
    23  *
       
    24  * NDC was defined by Neil Harrison in the article "Patterns for Logging
       
    25  * Diagnostic Messages" part of the book <i>"Pattern Languages of
       
    26  * Program Design 3"</i> edited by Martin et al.
       
    27  *
       
    28  * A Nested Diagnostic Context, or NDC in short, is an instrument
       
    29  * to distinguish interleaved log output from different sources. Log
       
    30  * output is typically interleaved when a server handles multiple
       
    31  * clients near-simultaneously.
       
    32  *
       
    33  * This class is similar to the {@link LoggerMDC} class except that it is
       
    34  * based on a stack instead of a map.
       
    35  *
       
    36  * Interleaved log output can still be meaningful if each log entry
       
    37  * from different contexts had a distinctive stamp. This is where NDCs
       
    38  * come into play.
       
    39  *
       
    40  * <b>Note that NDCs are managed on a per thread basis</b>.
       
    41  *
       
    42  * NDC operations such as {@link push()}, {@link pop()},
       
    43  * {@link clear()}, {@link getDepth()} and {@link setMaxDepth()}
       
    44  * affect the NDC of the <i>current</i> thread only. NDCs of other
       
    45  * threads remain unaffected.
       
    46  *
       
    47  * For example, a servlet can build a per client request NDC
       
    48  * consisting the clients host name and other information contained in
       
    49  * the the request. <i>Cookies</i> are another source of distinctive
       
    50  * information. To build an NDC one uses the {@link push()}
       
    51  * operation.
       
    52  *
       
    53  * Simply put,
       
    54  *
       
    55  * - Contexts can be nested.
       
    56  * - When entering a context, call <kbd>LoggerNDC::push()</kbd>
       
    57  *     As a side effect, if there is no nested diagnostic context for the
       
    58  *     current thread, this method will create it.
       
    59  * - When leaving a context, call <kbd>LoggerNDC::pop()</kbd>
       
    60  * - <b>When exiting a thread make sure to call {@link remove()}</b>
       
    61  *
       
    62  * There is no penalty for forgetting to match each
       
    63  * <kbd>push</kbd> operation with a corresponding <kbd>pop</kbd>,
       
    64  * except the obvious mismatch between the real application context
       
    65  * and the context set in the NDC.
       
    66  *
       
    67  * If configured to do so, {@link LoggerPatternLayout} and {@link LoggerLayoutTTCC}
       
    68  * instances automatically retrieve the nested diagnostic
       
    69  * context for the current thread without any user intervention.
       
    70  * Hence, even if a servlet is serving multiple clients
       
    71  * simultaneously, the logs emanating from the same code (belonging to
       
    72  * the same category) can still be distinguished because each client
       
    73  * request will have a different NDC tag.
       
    74  *
       
    75  * Example:
       
    76  *
       
    77  * {@example ../../examples/php/ndc.php 19}<br>
       
    78  *
       
    79  * With the properties file:
       
    80  *
       
    81  * {@example ../../examples/resources/ndc.properties 18}<br>
       
    82  *
       
    83  * Will result in the following (notice the conn and client ids):
       
    84  *
       
    85  * <pre>
       
    86  * 2009-09-13 19:04:27 DEBUG root conn=1234: just received a new connection in src/examples/php/ndc.php at 23
       
    87  * 2009-09-13 19:04:27 DEBUG root conn=1234 client=ab23: some more messages that can in src/examples/php/ndc.php at 25
       
    88  * 2009-09-13 19:04:27 DEBUG root conn=1234 client=ab23: now related to a client in src/examples/php/ndc.php at 26
       
    89  * 2009-09-13 19:04:27 DEBUG root : back and waiting for new connections in src/examples/php/ndc.php at 29
       
    90  * </pre>
       
    91  *
       
    92  * @version $Revision: 1350602 $
       
    93  * @package log4php
       
    94  * @since 0.3
       
    95  */
       
    96 class LoggerNDC {
       
    97 
       
    98     /** This is the repository of NDC stack */
       
    99     private static $stack = array();
       
   100 
       
   101     /**
       
   102      * Clear any nested diagnostic information if any. This method is
       
   103      * useful in cases where the same thread can be potentially used
       
   104      * over and over in different unrelated contexts.
       
   105      *
       
   106      * <p>This method is equivalent to calling the {@link setMaxDepth()}
       
   107      * method with a zero <var>maxDepth</var> argument.
       
   108      */
       
   109     public static function clear() {
       
   110         self::$stack = array();
       
   111     }
       
   112 
       
   113     /**
       
   114      * Never use this method directly, use the {@link LoggerLoggingEvent::getNDC()} method instead.
       
   115      * @return array
       
   116      */
       
   117     public static function get() {
       
   118         return implode(' ', self::$stack);
       
   119     }
       
   120 
       
   121     /**
       
   122      * Get the current nesting depth of this diagnostic context.
       
   123      *
       
   124      * @see setMaxDepth()
       
   125      * @return integer
       
   126      */
       
   127     public static function getDepth() {
       
   128         return count(self::$stack);
       
   129     }
       
   130 
       
   131     /**
       
   132      * Clients should call this method before leaving a diagnostic
       
   133      * context.
       
   134      *
       
   135      * <p>The returned value is the value that was pushed last. If no
       
   136      * context is available, then the empty string "" is returned.</p>
       
   137      *
       
   138      * @return string The innermost diagnostic context.
       
   139      */
       
   140     public static function pop() {
       
   141         if (count(self::$stack) > 0) {
       
   142             return array_pop(self::$stack);
       
   143         } else {
       
   144             return '';
       
   145         }
       
   146     }
       
   147 
       
   148     /**
       
   149      * Looks at the last diagnostic context at the top of this NDC
       
   150      * without removing it.
       
   151      *
       
   152      * <p>The returned value is the value that was pushed last. If no
       
   153      * context is available, then the empty string "" is returned.</p>
       
   154      * @return string The innermost diagnostic context.
       
   155      */
       
   156     public static function peek() {
       
   157         if (count(self::$stack) > 0) {
       
   158             return end(self::$stack);
       
   159         } else {
       
   160             return '';
       
   161         }
       
   162     }
       
   163 
       
   164     /**
       
   165      * Push new diagnostic context information for the current thread.
       
   166      *
       
   167      * <p>The contents of the <var>message</var> parameter is
       
   168      * determined solely by the client.
       
   169      *
       
   170      * @param string $message The new diagnostic context information.
       
   171      */
       
   172     public static function push($message) {
       
   173         array_push(self::$stack, (string)$message);
       
   174     }
       
   175 
       
   176     /**
       
   177      * Remove the diagnostic context for this thread.
       
   178      */
       
   179     public static function remove() {
       
   180         LoggerNDC::clear();
       
   181     }
       
   182 
       
   183     /**
       
   184      * Set maximum depth of this diagnostic context. If the current
       
   185      * depth is smaller or equal to <var>maxDepth</var>, then no
       
   186      * action is taken.
       
   187      *
       
   188      * <p>This method is a convenient alternative to multiple
       
   189      * {@link pop()} calls. Moreover, it is often the case that at
       
   190      * the end of complex call sequences, the depth of the NDC is
       
   191      * unpredictable. The {@link setMaxDepth()} method circumvents
       
   192      * this problem.
       
   193      *
       
   194      * @param integer $maxDepth
       
   195      * @see getDepth()
       
   196      */
       
   197     public static function setMaxDepth($maxDepth) {
       
   198         $maxDepth = (int)$maxDepth;
       
   199         if (LoggerNDC::getDepth() > $maxDepth) {
       
   200             self::$stack = array_slice(self::$stack, 0, $maxDepth);
       
   201         }
       
   202     }
       
   203 }