Completed
Push — master ( ba1d63...c32552 )
by Terry
02:45
created

Logger   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 302
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 62.89%

Importance

Changes 15
Bugs 0 Features 2
Metric Value
wmc 29
c 15
b 0
f 2
lcom 1
cbo 1
dl 0
loc 302
ccs 61
cts 97
cp 0.6289
rs 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A setLogFile() 0 5 1
A addColour() 0 19 3
A colourize() 0 4 1
A setLogLevel() 0 9 2
A lock() 0 5 1
A gzipped() 0 5 1
A formatter() 0 5 1
A log() 0 20 4
A formatMessage() 0 10 3
A write() 0 13 3
A getResource() 0 15 3
A getLastLogEntry() 0 4 1
A openResource() 0 8 2
A __destruct() 0 7 2
1
<?php
2
3
namespace Terah\ColourLog;
4
5
use Psr\Log\AbstractLogger;
6
use Psr\Log\LogLevel;
7
8
class Logger extends AbstractLogger
9
{
10
    /**  @var resource $resource The file handle */
11
    protected $resource         = null;
12
13
    /** @var string $level */
14
    protected $level            = LogLevel::INFO;
15
16
    /** @var bool $closeLocally */
17
    protected $closeLocally     = false;
18
19
    /** @var bool */
20
    protected $addDate          = true;
21
22
    /** @var string  */
23
    protected $separator        = ' | ';
24
25
    /** @var \Closure */
26
    protected $formatter        = null;
27
28
    /** @var string  */
29
    protected $lastLogEntry     = '';
30
31
    /** @var bool|null  */
32
    protected $gzipFile         = null;
33
34
    /** @var bool  */
35
    protected $useLocking       = false;
36
37
    /**
38
     * @var array $logLevels List of supported levels
39
     */
40
    protected $logLevels       = [
41
        LogLevel::EMERGENCY => [1, 'white', 'red', 'default', 'EMERG'],
42
        LogLevel::ALERT     => [2, 'white', 'yellow', 'default', 'ALERT'],
43
        LogLevel::CRITICAL  => [3,'red', 'default', 'bold' , 'CRIT'],
44
        LogLevel::ERROR     => [4,'red', 'default', 'default', 'ERROR'],
45
        LogLevel::WARNING   => [5, 'yellow', 'default', 'default', 'WARN'],
46
        LogLevel::NOTICE    => [6, 'cyan', 'default', 'default', 'NOTE'],
47
        LogLevel::INFO      => [7, 'green', 'default', 'default', 'INFO'],
48
        LogLevel::DEBUG     => [8, 'dark_gray', 'default', 'default', 'DEBUG'],
49
    ];
50
51
    /**
52
     * @var array
53
     */
54
    static protected $colours   = [
55
        'fore' => [
56
            'black'         => '0;30',
57
            'dark_gray'     => '1;30',
58
            'blue'          => '0;34',
59
            'light_blue'    => '1;34',
60
            'green'         => '0;32',
61
            'light_green'   => '1;32',
62
            'cyan'          => '0;36',
63
            'light_cyan'    => '1;36',
64
            'red'           => '0;31',
65
            'light_red'     => '1;31',
66
            'purple'        => '0;35',
67
            'light_purple'  => '1;35',
68
            'brown'         => '0;33',
69
            'yellow'        => '1;33',
70
            'magenta'       => '0;35',
71
            'light_gray'    => '0;37',
72
            'white'         => '1;37',
73
        ],
74
        'back'  => [
75
            'default'       => '49',
76
            'black'         => '40',
77
            'red'           => '41',
78
            'green'         => '42',
79
            'yellow'        => '43',
80
            'blue'          => '44',
81
            'magenta'       => '45',
82
            'cyan'          => '46',
83
            'light_gray'    => '47',
84
        ],
85
        'bold' => [],
86
    ];
87
88
    /**
89
     * @param mixed  $resource
90
     * @param string $level
91
     * @param bool   $useLocking
92
     * @param bool   $gzipFile
93
     * @param bool   $addDate
94
     */
95 33
    public function __construct($resource, $level=LogLevel::INFO, $useLocking=false, $gzipFile=false, $addDate=true)
96
    {
97 33
        $this->resource     = $resource;
98 33
        $this->setLogLevel($level);
99 33
        $this->useLocking   = $useLocking;
100 33
        $this->gzipFile     = $gzipFile;
101 33
        $this->addDate      = $addDate;
102 33
    }
103
104
    /**
105
     * @param $resource
106
     * @return $this
107
     */
108
    public function setLogFile($resource)
109
    {
110
        $this->resource     = $resource;
111
        return $this;
112
    }
113
114
    /**
115
     * @param      $string
116
     * @param null $foregroundColor
117
     * @param null $backgroundColor
118
     * @param bool $bold
119
     * @return string
120
     */
121 27
    public static function addColour($string, $foregroundColor=null, $backgroundColor=null, $bold=false)
122
    {
123
        // todo: support bold
124 27
        unset($bold);
125 27
        $coloredString = '';
126
        // Check if given foreground color found
127 27
        if ( isset(static::$colours['fore'][$foregroundColor]) )
128 18
        {
129 24
            $coloredString .= "\033[" . static::$colours['fore'][$foregroundColor] . "m";
130 16
        }
131
        // Check if given background color found
132 27
        if ( isset(static::$colours['back'][$backgroundColor]) )
133 18
        {
134 24
            $coloredString .= "\033[" . static::$colours['back'][$backgroundColor] . "m";
135 16
        }
136
        // Add string and end coloring
137 27
        $coloredString .=  $string . "\033[0m";
138 27
        return $coloredString;
139
    }
140
141
    /**
142
     * @param            $string
143
     * @param null       $foregroundColor
144
     * @param null       $backgroundColor
145
     * @param bool|false $bold
146
     * @return string
147
     */
148 27
    public function colourize($string, $foregroundColor = null, $backgroundColor = null, $bold=false)
149
    {
150 27
        return static::addColour($string, $foregroundColor, $backgroundColor, $bold);
151
    }
152
153
    /**
154
     * @param string $level Ignore logging attempts at a level less the $level
155
     * @return static
156
     */
157 33
    public function setLogLevel($level)
158
    {
159 33
        if ( ! isset($this->logLevels[$level]) )
160 22
        {
161
            throw new \InvalidArgumentException("Log level is invalid");
162
        }
163 33
        $this->level = $this->logLevels[$level][0];
164 33
        return $this;
165
    }
166
167
    /**
168
     * @return $this
169
     */
170
    public function lock()
171
    {
172
        $this->useLocking = true;
173
        return $this;
174
    }
175
176
    /**
177
     * @return $this
178
     */
179 3
    public function gzipped()
180
    {
181 3
        $this->gzipFile = true;
182 3
        return $this;
183
    }
184
185
    /**
186
     * @param callable $fnFormatter
187
     *
188
     * @return $this
189
     */
190
    public function formatter(callable $fnFormatter)
191
    {
192
        $this->formatter = $fnFormatter;
0 ignored issues
show
Documentation Bug introduced by
It seems like $fnFormatter of type callable is incompatible with the declared type object<Closure> of property $formatter.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
193
        return $this;
194
    }
195
196
    /**
197
     * Log messages to resource
198
     *
199
     * @param mixed          $level    The level of the log message
200
     * @param string|object  $message  If an object is passed it must implement __toString()
201
     * @param array          $context  Placeholders to be substituted in the message
202
     *
203
     * @return static
204
     */
205 24
    public function log($level, $message, array $context = [])
206
    {
207 24
        $level = isset($this->logLevels[$level]) ? $level : LogLevel::INFO;
208 24
        list($logLevel, $fore, $back, $style) = $this->logLevels[$level];
209 24
        if ( $logLevel > $this->level )
210 16
        {
211 18
            return $this;
212
        }
213 24
        if ( is_callable($this->formatter) )
214 16
        {
215
            $message = $this->formatter->__invoke($this->logLevels[$level][4], $message, $context);
216
        }
217
        else
218
        {
219 24
            $message = $this->formatMessage($level, $message, $context);
220
        }
221 24
        $this->lastLogEntry = $message;
222 24
        $this->write($this->colourize($message, $fore, $back, $style) . PHP_EOL);
223 24
        return $this;
224
    }
225
226
    /**
227
     * @param       $level
228
     * @param       $message
229
     * @param array $context
230
     * @return string
231
     */
232 24
    protected function formatMessage($level, $message, array $context = [])
233
    {
234
        # Handle objects implementing __toString
235 24
        $message            = (string) $message;
236 24
        $message            .= empty($context) ? '' : PHP_EOL . json_encode($context, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
237 24
        $data               = $this->addDate ? ['date' => date('Y-m-d H:i:s')] : [];
238 24
        $data['level']      = strtoupper(str_pad($this->logLevels[$level][4], 5, ' ', STR_PAD_RIGHT));
239 24
        $data['message']    = $message;
240 24
        return implode($this->separator, $data);
241
    }
242
243
    /**
244
     * Write the content to the stream
245
     *
246
     * @param  string $content
247
     */
248 27
    public function write($content)
249
    {
250 27
        $resource = $this->getResource();
251 27
        if ( $this->useLocking )
252 18
        {
253
            flock($resource, LOCK_EX);
254
        }
255 27
        gzwrite($resource, $content);
256 27
        if ( $this->useLocking )
257 18
        {
258
            flock($resource, LOCK_UN);
259
        }
260 27
    }
261
262
    /**
263
     * @return mixed|resource
264
     * @throws \Exception
265
     */
266 27
    protected function getResource()
267
    {
268 27
        if ( is_resource($this->resource) )
269 18
        {
270 27
            return $this->resource;
271
        }
272
        $fileName = $this->resource;
273
        $this->closeLocally = true;
274
        $this->resource = $this->openResource();
275
        if ( ! is_resource($this->resource) )
276
        {
277
            throw new \Exception("The resource ({$fileName}) could not be opened");
278
        }
279
        return $this->resource;
280
    }
281
282
    /**
283
     * @return string
284
     */
285
    public function getLastLogEntry()
286
    {
287
        return $this->lastLogEntry;
288
    }
289
290
    /**
291
     * @return resource
292
     */
293
    protected function openResource()
294
    {
295
        if ( $this->gzipFile )
296
        {
297
            return gzopen($this->resource, 'a');
298
        }
299
        return fopen($this->resource, 'a');
300
    }
301
302
    public function __destruct()
303
    {
304
        if ($this->closeLocally)
305
        {
306
            gzclose($this->getResource());
307
        }
308
    }
309
}
310
311