Test Setup Failed
Push — master ( aa9039...94c501 )
by Derek
03:32
created

StaticLogger::processContext()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
rs 8.8571
cc 3
eloc 14
nc 4
nop 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)
14
 * @todo Create handler stack to allow different logging mechanisms
15
 * @todo Specify different handlers for different log levels (e.g. file on info but file and email on alert)
16
 */
17
class StaticLogger
18
{
19
    const EMERGENCY = 'emergency';
20
    const ALERT = 'alert';
21
    const CRITICAL = 'critical';
22
    const ERROR = 'error';
23
    const WARNING = 'warning';
24
    const NOTICE = 'notice';
25
    const INFO = 'info';
26
    const DEBUG = 'debug';
27
28
    /**
29
     * @var handler
30
     */
31
    private static $handler;
32
33
    private static $valid_levels = array(
34
        "EMERGENCY",
35
        "ALERT",
36
        "CRITICAL",
37
        "ERROR",
38
        "WARNING",
39
        "NOTICE",
40
        "INFO",
41
        "DEBUG"
42
    );
43
44
    /**
45
     * Sets the handler (e.g. a File) to be used to log messages.
46
     *
47
     * @param handler $handler  The handler to be used to write a log message.
48
     */
49
    public static function setHandler(handler $handler)
50
    {
51
        self::$handler = $handler;
52
    }
53
54
    /**
55
     * @param string $level     Must contain a valid log level:
56
     *                              "emergency"
57
     *                              "alert"
58
     *                              "critical"
59
     *                              "error"
60
     *                              "warning"
61
     *                              "notice"
62
     *                              "info"
63
     *                              "debug"
64
     * @param string $message   Message to be logged
65
     * @param array $context    PSR-3 context for log entry
66
     *
67
     * @return string           Returns the log entry string.
68
     */
69
    public static function log($level, $message, array $context = array())
70
    {
71
        self::validateLogLevel($level);
72
73
        $u_level = strtoupper($level);
74
75
        $log_trace = debug_backtrace();
76
77
        $trace_origin = end($log_trace);
78
79
        $trace_line = $trace_origin["line"];
80
        $trace_file = $trace_origin["file"];
81
82
        $processed_message = self::processContext($message, $context);
83
84
        $log_entry = "$u_level: $trace_file, $trace_line: $processed_message";
85
86
        self::$handler->write($log_entry);
87
88
        return $log_entry;
89
    }
90
91
    /**
92
     * Generates a call to the log method given a supplied $level.
93
     *
94
     * @param string $level         Must contain a valid log level:
95
     *                                  "emergency"
96
     *                                  "alert"
97
     *                                  "critical"
98
     *                                  "error"
99
     *                                  "warning"
100
     *                                  "notice"
101
     *                                  "info"
102
     *                                  "debug"
103
     *                              Not specified inherently but rather derived from the overload.
104
     * @param array $log_arguments  The arguments passed to the log method by the __callStatic framework
105
     * @return string               The entry logged to the handler.
106
     *
107
     * @see StaticLogger::log
108
     * @see StaticLogger::validateLogLevel
109
     *
110
     * @todo Split out debug logging to provide more debugging information (vardumps, etc.)
111
     */
112
    public static function __callStatic($level, array $log_arguments)
113
    {
114
        self::validateLogLevel($level);
115
116
        //If there's only one argument passed to the logging function...
117
        if (count($log_arguments) == 1) {
118
            //Set the the second array element as an empty array.
119
            $log_arguments[1] = array();
120
        }
121
122
        $log_entry = self::log($level, $log_arguments[0], $log_arguments[1]);
123
124
        return $log_entry;
125
    }
126
127
    /**
128
     * Validates that a given $level is a valid PSR-3 log level
129
     *
130
     * @param string $level                 The log level being validated
131
     * @throws \InvalidArgumentException    Generates an Invalid Argument Exception if the supplied level is invalid
132
     *
133
     * @see StaticLogger::valid_levels
134
     */
135
    private static function validateLogLevel($level)
136
    {
137
        $u_level = strtoupper($level);
138
139
        if (!in_array($u_level, self::$valid_levels)) {
140
            throw new \InvalidArgumentException("Unknown log level");
141
        }
142
    }
143
144
    /**
145
     * Processes context as supplied by the log method, replacing templated strings with data from the context array
146
     * or processing exception data via checkContextException.
147
     *
148
     * Context array elements with the "exception" key are processed by pulling Exception line, file, and message into
149
     * the log message provided appropriate templates within the message: {line}, {file}, and {message}, respectively.
150
     * Note that any line, file, or message templates provided outside of an exception will be overwritten by context
151
     * processing, and so the order in which the context array data are stacked is relevant.
152
     *
153
     *
154
     *
155
     * @param string $message   The original log message to be processed with context.
156
     * @param array $context    An array of key => value pairs with additional data to be inserted into the log
157
     *                          message provided {templated text} (i.e. surrounded by curly braces.
158
     *
159
     * @return string           The processed log message
160
     *
161
     * @todo Determine if there are more relevant data in Exceptions to be used in templating.
162
     */
163
    private static function processContext($message, array $context = array())
164
    {
165
        $replace = array();
166
167
        foreach ($context as $key => $value) {
168
            $templated = "{" . $key . "}";
169
            $replace[$templated] = $value;
170
        }
171
172
        if (self::checkContextException($context)) {
173
            /**
174
             * @var \Exception $e
175
             */
176
            $e = $context["exception"];
177
178
            $e_line = $e->getLine();
179
            $e_file = $e->getFile();
180
            $e_message = $e->getMessage();
181
182
            $replace["{line}"] = $e_line;
183
            $replace["{file}"] = $e_file;
184
            $replace["{message}"] = $e_message;
185
        }
186
187
        return strtr($message, $replace);
188
    }
189
190
    /**
191
     * Determines whether a context array contains an Exception.
192
     *
193
     * @param array $context    PSR-3 context array.
194
     * @return bool             Returns true if the context array contains an element identified by an "exception" key
195
     *                          AND the value that corresponds with the "exception" key is an Exception.
196
     *                          Returns false otherwise.
197
     */
198
    private static function checkContextException(array $context = array())
199
    {
200
        if (isset($context["exception"])) {
201
            $includes_exception = $context["exception"] instanceof \Exception;
202
        } else {
203
            $includes_exception = false;
204
        }
205
206
        return $includes_exception;
207
    }
208
}
209