Completed
Push — master ( d391d3...b0efab )
by Derek
02:15
created

StaticLogger   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 245
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 15
c 2
b 0
f 0
lcom 1
cbo 1
dl 0
loc 245
ccs 58
cts 58
cp 1
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A setHandler() 0 4 1
A log() 0 23 2
A __callStatic() 0 14 2
A silenceLogging() 0 4 1
A unSilenceLogging() 0 4 1
A getSilence() 0 4 1
A validateLogLevel() 0 8 2
B processContext() 0 26 3
A checkContextException() 0 10 2
1
<?php
2
namespace Subreality\Dilmun\Nabu;
3
4
use \Subreality\Dilmun\Nabu\LoggerHandler\HandlerInterface as handler;
5
6
/**
7
 * Static logger framework that follows the PHP-FIG PSR-3 standard for logging.
8
 *
9
 * @see http://www.php-fig.org/psr/psr-3/
10
 *
11
 * @author Derek DeHart
12
 *
13
 * @todo Set minimum log level (e.g. log only notices and higher)
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
14
 * @todo Create handler stack to allow different logging mechanisms
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
15
 * @todo Specify different handlers for different log levels (e.g. file on info but file and email on alert)
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
16
 */
17
class StaticLogger
18
{
19
    /**
20
     * Emergency: system is unusable
21
     */
22
    const EMERGENCY = 'emergency';
23
    /**
24
     * Alert: action must be taken immediately
25
     */
26
    const ALERT = 'alert';
27
    /**
28
     * Critical: critical conditions
29
     */
30
    const CRITICAL = 'critical';
31
    /**
32
     * Error: error conditions
33
     */
34
    const ERROR = 'error';
35
    /**
36
     * Warning: warning conditions
37
     */
38
    const WARNING = 'warning';
39
    /**
40
     * Notice: normal but significant condition
41
     */
42
    const NOTICE = 'notice';
43
    /**
44
     * Informational: informational messages
45
     */
46
    const INFO = 'info';
47
    /**
48
     * Debug: debug-level messages
49
     */
50
    const DEBUG = 'debug';
51
52
    /**
53
     * @var handler
54
     */
55
    private static $handler;
56
    private static $silence = false;
57
58
    private static $valid_levels = array(
59
        "EMERGENCY",
60
        "ALERT",
61
        "CRITICAL",
62
        "ERROR",
63
        "WARNING",
64
        "NOTICE",
65
        "INFO",
66
        "DEBUG"
67
    );
68
69
    /**
70
     * Sets the handler (e.g. a File) to be used to log messages.
71
     *
72
     * @param handler $handler  The handler to be used to write a log message.
73
     */
74 24
    public static function setHandler(handler $handler)
75
    {
76 24
        self::$handler = $handler;
77 24
    }
78
79
    /**
80
     * @param string $level     Must contain a valid log level:
81
     *                              "emergency"
82
     *                              "alert"
83
     *                              "critical"
84
     *                              "error"
85
     *                              "warning"
86
     *                              "notice"
87
     *                              "info"
88
     *                              "debug"
89
     * @param string $message   Message to be logged
90
     * @param array $context    PSR-3 context for log entry
91
     *
92
     * @return string           Returns the log entry string.
93
     */
94 24
    public static function log($level, $message, array $context = array())
95
    {
96 24
        self::validateLogLevel($level);
97
98 23
        $u_level = strtoupper($level);
99
100 23
        $log_trace = debug_backtrace();
101
102 23
        $trace_origin = end($log_trace);
103
104 23
        $trace_line = $trace_origin["line"];
105 23
        $trace_file = $trace_origin["file"];
106
107 23
        $processed_message = self::processContext($message, $context);
108
109 23
        $log_entry = "$u_level: $trace_file, $trace_line: $processed_message";
110
111 23
        if (!self::$silence) {
112 17
            self::$handler->write($log_entry);
113 17
        }
114
115 23
        return $log_entry;
116
    }
117
118
    /**
119
     * Generates a call to the log method given a supplied $level.
120
     *
121
     * @param string $level         Must contain a valid log level:
122
     *                                  "emergency"
123
     *                                  "alert"
124
     *                                  "critical"
125
     *                                  "error"
126
     *                                  "warning"
127
     *                                  "notice"
128
     *                                  "info"
129
     *                                  "debug"
130
     *                              Not specified inherently but rather derived from the overload.
131
     * @param array $log_arguments  The arguments passed to the log method by the __callStatic framework
132
     * @return string               The entry logged to the handler.
133
     *
134
     * @see StaticLogger::log
135
     * @see StaticLogger::validateLogLevel
136
     *
137
     * @todo Split out debug logging to provide more debugging information (vardumps, etc.)
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
138
     */
139 8
    public static function __callStatic($level, array $log_arguments)
140
    {
141 8
        self::validateLogLevel($level);
142
143
        //If there's only one argument passed to the logging function...
144 8
        if (count($log_arguments) == 1) {
145
            //Set the the second array element as an empty array.
146 8
            $log_arguments[1] = array();
147 8
        }
148
149 8
        $log_entry = self::log($level, $log_arguments[0], $log_arguments[1]);
150
151 8
        return $log_entry;
152
    }
153
154
    /**
155
     * Globally silences all logging.
156
     */
157 3
    public static function silenceLogging()
158
    {
159 3
        self::$silence = true;
160 3
    }
161
162
    /**
163
     * Globally unsliences all logging.
164
     */
165 19
    public static function unSilenceLogging()
166
    {
167 19
        self::$silence = false;
168 19
    }
169
170
    /**
171
     * Returns the current state of the StaticLogger's "silence" property
172
     *
173
     * @return bool The current state of silence
174
     */
175 1
    public static function getSilence()
176
    {
177 1
        return self::$silence;
178
    }
179
180
    /**
181
     * Validates that a given $level is a valid PSR-3 log level
182
     *
183
     * @param string $level                 The log level being validated
184
     * @throws \InvalidArgumentException    Generates an Invalid Argument Exception if the supplied level is invalid
185
     *
186
     * @see StaticLogger::valid_levels
187
     */
188 24
    private static function validateLogLevel($level)
189
    {
190 24
        $u_level = strtoupper($level);
191
192 24
        if (!in_array($u_level, self::$valid_levels)) {
193 1
            throw new \InvalidArgumentException("Unknown log level");
194
        }
195 23
    }
196
197
    /**
198
     * Processes context as supplied by the log method, replacing templated strings with data from the context array
199
     * or processing exception data via checkContextException.
200
     *
201
     * Context array elements with the "exception" key are processed by pulling Exception line, file, and message into
202
     * the log message provided appropriate templates within the message: {line}, {file}, and {message}, respectively.
203
     * Note that any line, file, or message templates provided outside of an exception will be overwritten by context
204
     * processing, and so the order in which the context array data are stacked is relevant.
205
     *
206
     *
207
     *
208
     * @param string $message   The original log message to be processed with context.
209
     * @param array $context    An array of key => value pairs with additional data to be inserted into the log
210
     *                          message provided {templated text} (i.e. surrounded by curly braces.
211
     *
212
     * @return string           The processed log message
213
     *
214
     * @todo Determine if there are more relevant data in Exceptions to be used in templating.
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
215
     */
216 23
    private static function processContext($message, array $context = array())
217
    {
218 23
        $replace = array();
219
220 23
        foreach ($context as $key => $value) {
221 6
            $templated = "{" . $key . "}";
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 11 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
222 6
            $replace[$templated] = $value;
223 23
        }
224
225 23
        if (self::checkContextException($context)) {
226
            /**
227
             * @var \Exception $e
228
             */
229 1
            $e = $context["exception"];
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $e. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
230
231 1
            $e_line = $e->getLine();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
232 1
            $e_file = $e->getFile();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
233 1
            $e_message = $e->getMessage();
234
235 1
            $replace["{line}"] = $e_line;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
236 1
            $replace["{file}"] = $e_file;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
237 1
            $replace["{message}"] = $e_message;
238 1
        }
239
240 23
        return strtr($message, $replace);
241
    }
242
243
    /**
244
     * Determines whether a context array contains an Exception.
245
     *
246
     * @param array $context    PSR-3 context array.
247
     * @return bool             Returns true if the context array contains an element identified by an "exception" key
248
     *                          AND the value that corresponds with the "exception" key is an Exception.
249
     *                          Returns false otherwise.
250
     */
251 23
    private static function checkContextException(array $context = array())
252
    {
253 23
        if (isset($context["exception"])) {
254 1
            $includes_exception = $context["exception"] instanceof \Exception;
255 1
        } else {
256 22
            $includes_exception = false;
257
        }
258
259 23
        return $includes_exception;
260
    }
261
}
262