Completed
Push — master ( 2e1409...ba1d63 )
by Terry
02:49
created

Logger::getResource()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 5.667

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 15
ccs 4
cts 12
cp 0.3333
rs 9.4285
cc 3
eloc 9
nc 3
nop 0
crap 5.667
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
    protected $addDate          = true;
20
21
    protected $separator        = ' | ';
22
23
    protected $formatter        = null;
24
25
    protected $lastLogEntry     = '';
26
27
    protected $gzipFile         = null;
28
29
    protected $useLocking       = false;
30
31
    /**
32
     * @var array $logLevels List of supported levels
33
     */
34
    protected $logLevels       = [
35
        LogLevel::EMERGENCY => [1, 'white', 'red', 'default', 'EMERG'],
36
        LogLevel::ALERT     => [2, 'white', 'yellow', 'default', 'ALERT'],
37
        LogLevel::CRITICAL  => [3,'red', 'default', 'bold' , 'CRIT'],
38
        LogLevel::ERROR     => [4,'red', 'default', 'default', 'ERROR'],
39
        LogLevel::WARNING   => [5, 'yellow', 'default', 'default', 'WARN'],
40
        LogLevel::NOTICE    => [6, 'cyan', 'default', 'default', 'NOTE'],
41
        LogLevel::INFO      => [7, 'green', 'default', 'default', 'INFO'],
42
        LogLevel::DEBUG     => [8, 'dark_gray', 'default', 'default', 'DEBUG'],
43
    ];
44
45
    /**
46
     * @var array
47
     */
48
    static protected $colours   = [
49
        'fore' => [
50
            'black'         => '0;30',
51
            'dark_gray'     => '1;30',
52
            'blue'          => '0;34',
53
            'light_blue'    => '1;34',
54
            'green'         => '0;32',
55
            'light_green'   => '1;32',
56
            'cyan'          => '0;36',
57
            'light_cyan'    => '1;36',
58
            'red'           => '0;31',
59
            'light_red'     => '1;31',
60
            'purple'        => '0;35',
61
            'light_purple'  => '1;35',
62
            'brown'         => '0;33',
63
            'yellow'        => '1;33',
64
            'magenta'       => '0;35',
65
            'light_gray'    => '0;37',
66
            'white'         => '1;37',
67
        ],
68
        'back'  => [
69
            'default'       => '49',
70
            'black'         => '40',
71
            'red'           => '41',
72
            'green'         => '42',
73
            'yellow'        => '43',
74
            'blue'          => '44',
75
            'magenta'       => '45',
76
            'cyan'          => '46',
77
            'light_gray'    => '47',
78
        ],
79
        'bold' => [],
80
    ];
81
82
    /**
83
     * @param mixed  $resource
84
     * @param string $level
85
     * @param bool   $useLocking
86
     * @param bool   $gzipFile
87
     * @param bool   $addDate
88
     */
89 33
    public function __construct($resource, $level=LogLevel::INFO, $useLocking=false, $gzipFile=false, $addDate=true)
90
    {
91 33
        $this->resource     = $resource;
92 33
        $this->setLogLevel($level);
93 33
        $this->useLocking   = $useLocking;
94 33
        $this->gzipFile     = $gzipFile;
95 33
        $this->addDate      = $addDate;
96 33
    }
97
98
    /**
99
     * @param $resource
100
     * @return $this
101
     */
102
    public function setLogFile($resource)
103
    {
104
        $this->resource     = $resource;
105
        return $this;
106
    }
107
108
    /**
109
     * @param      $string
110
     * @param null $foregroundColor
111
     * @param null $backgroundColor
112
     * @param bool $bold
113
     * @return string
114
     */
115 27
    public static function addColour($string, $foregroundColor=null, $backgroundColor=null, $bold=false)
0 ignored issues
show
Unused Code introduced by
The parameter $bold is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
116
    {
117 27
        $coloredString = '';
118
        // Check if given foreground color found
119 27
        if ( isset(static::$colours['fore'][$foregroundColor]) )
120 18
        {
121 24
            $coloredString .= "\033[" . static::$colours['fore'][$foregroundColor] . "m";
122 16
        }
123
        // Check if given background color found
124 27
        if ( isset(static::$colours['back'][$backgroundColor]) )
125 18
        {
126 24
            $coloredString .= "\033[" . static::$colours['back'][$backgroundColor] . "m";
127 16
        }
128
        // Add string and end coloring
129 27
        $coloredString .=  $string . "\033[0m";
130 27
        return $coloredString;
131
    }
132
133
    /**
134
     * @param            $string
135
     * @param null       $foregroundColor
136
     * @param null       $backgroundColor
137
     * @param bool|false $bold
138
     * @return string
139
     */
140 27
    public function colourize($string, $foregroundColor = null, $backgroundColor = null, $bold=false)
141
    {
142 27
        return static::addColour($string, $foregroundColor, $backgroundColor, $bold);
143
    }
144
145
    /**
146
     * @param string $level Ignore logging attempts at a level less the $level
147
     * @return static
148
     */
149 33
    public function setLogLevel($level)
150
    {
151 33
        if ( ! isset($this->logLevels[$level]) )
152 22
        {
153
            throw new \InvalidArgumentException("Log level is invalid");
154
        }
155 33
        $this->level = $this->logLevels[$level][0];
156 33
        return $this;
157
    }
158
159
    /**
160
     * @return $this
161
     */
162
    public function lock()
163
    {
164
        $this->useLocking = true;
165
        return $this;
166
    }
167
168
    /**
169
     * @return $this
170
     */
171 3
    public function gzipped()
172
    {
173 3
        $this->gzipFile = true;
174 3
        return $this;
175
    }
176
177
    /**
178
     * @param callable $fnFormatter
179
     *
180
     * @return $this
181
     */
182
    public function formatter(callable $fnFormatter)
183
    {
184
        $this->formatter = $fnFormatter;
185
        return $this;
186
    }
187
188
    /**
189
     * Log messages to resource
190
     *
191
     * @param mixed          $level    The level of the log message
192
     * @param string|object  $message  If an object is passed it must implement __toString()
193
     * @param array          $context  Placeholders to be substituted in the message
194
     *
195
     * @return static
196
     */
197 24
    public function log($level, $message, array $context = [])
198
    {
199 24
        $level = isset($this->logLevels[$level]) ? $level : LogLevel::INFO;
200 24
        list($logLevel, $fore, $back, $style) = $this->logLevels[$level];
201 24
        if ( $logLevel > $this->level )
202 16
        {
203 18
            return $this;
204
        }
205 24
        if ( is_callable($this->formatter) )
206 16
        {
207
            $message = $this->formatter->__invoke($this->logLevels[$level][4], $message, $context);
0 ignored issues
show
Bug introduced by
The method __invoke cannot be called on $this->formatter (of type callable).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

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