Completed
Push — logger ( 98144a )
by Craig
04:53 queued 02:33
created

Logger::log()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 3
1
<?php
2
3
namespace League\CLImate;
4
5
use League\CLImate\CLImate;
6
use Psr\Log\AbstractLogger;
7
use Psr\Log\LogLevel;
8
9
/**
10
 * A PSR-3 compatible logger that uses CLImate for output.
11
 */
12
class Logger extends AbstractLogger
13
{
14
    /**
15
     * @var array $levels Conversion of the level strings to their numeric representations.
16
     */
17
    private $levels = [
18
        LogLevel::EMERGENCY =>  1,
19
        LogLevel::ALERT     =>  2,
20
        LogLevel::CRITICAL  =>  3,
21
        LogLevel::ERROR     =>  4,
22
        LogLevel::WARNING   =>  5,
23
        LogLevel::NOTICE    =>  6,
24
        LogLevel::INFO      =>  7,
25
        LogLevel::DEBUG     =>  8,
26
    ];
27
28
    /**
29
     * @var int $level Ignore logging attempts at a level less than this.
30
     */
31
    private $level;
32
33
    /**
34
     * @var CLImate $climate The underlying climate instance we are using for output.
35
     */
36
    private $climate;
37
38
    /**
39
     * Create a new Logger instance.
40
     *
41
     * @param mixed   $level   Either a LogLevel constant, or a string (eg 'debug'), or a number (1-8)
42
     * @param CLImate $climate An existing CLImate instance to use for output
43
     */
44
    public function __construct($level = LogLevel::INFO, CLImate $climate = null)
45
    {
46
        $this->setLogLevel($level);
47
48
        if ($climate === null) {
49
            $climate = new CLImate;
50
        }
51
        $this->climate = $climate;
52
53
        # Define some default styles to use for the output
54
        $commands = [
55
            "emergency" =>  ["white", "bold", "background_red"],
56
            "alert"     =>  ["white", "background_yellow"],
57
            "critical"  =>  ["red", "bold"],
58
            "error"     =>  ["red"],
59
            "warning"   =>  "yellow",
60
            "notice"    =>  "light_cyan",
61
            "info"      =>  "green",
62
            "debug"     =>  "dark_gray",
63
        ];
64
65
        # If any of the required styles are not defined then define them now
66
        foreach ($commands as $command => $style) {
67
            if (!$this->climate->style->get($command)) {
68
                $this->climate->style->addCommand($command, $style);
69
            }
70
        }
71
    }
72
73
74
    /**
75
     * Get a numeric log level for the passed parameter.
76
     *
77
     * @param mixed $level Either a LogLevel constant, or a string (eg 'debug'), or a number (1-8)
78
     *
79
     * @return int
80
     */
81
    private function convertLevel($level)
82
    {
83
        # If this is one of the defined string log levels then return it's numeric value
84
        $key = strtolower($level);
85
        if (isset($this->levels[$key])) {
86
            return $this->levels[$key];
87
        }
88
89
        # If it doesn't look like a number, default to the most severe log level
90
        if (!is_numeric($level)) {
91
            return 1;
92
        }
93
94
        # If it's lower than the lowest level then default to the lowest level
95
        if ($level < 1) {
96
            return 1;
97
        }
98
99
        # If it's higher than the highest level then default to the highest
100
        if ($level > 8) {
101
            return 8;
102
        }
103
104
        # If it's already a valid numeric log level then return it
105
        return $level;
106
    }
107
108
109
    /**
110
     * Set the current level we are logging at.
111
     *
112
     * @param mixed $level Ignore logging attempts at a level less the $level
113
     *
114
     * @return static
115
     */
116
    public function setLogLevel($level)
117
    {
118
        $this->level = $this->convertLevel($level);
119
120
        return $this;
121
    }
122
123
124
    /**
125
     * Log messages to a CLImate instance.
126
     *
127
     * @param mixed          $level    The level of the log message
128
     * @param string|object  $message  If an object is passed it must implement __toString()
129
     * @param array          $context  Placeholders to be substituted in the message
130
     *
131
     * @return static
132
     */
133
    public function log($level, $message, array $context = [])
134
    {
135
        if ($this->convertLevel($level) > $this->level) {
136
            return $this;
137
        }
138
139
        # Handle objects implementing __toString
140
        $message = (string) $message;
141
142
        # Handle any placeholders in the $context array
143
        foreach ($context as $key => $val) {
144
            $placeholder = "{" . $key . "}";
145
146
            # If this context key is used as a placeholder, then replace it, and remove it from the $context array
147
            if (strpos($message, $placeholder) !== false) {
148
                $val = (string) $val;
149
                $message = str_replace($placeholder, $val, $message);
150
                unset($context[$key]);
151
            }
152
        }
153
154
        # Send the message to the climate instance
155
        $this->climate->{$level}($message);
156
157
        # Append any context information not used as placeholders
158
        $this->outputRecursiveContext($level, $context, 1);
159
160
        return $this;
161
    }
162
163
164
    /**
165
     * Handle recursive arrays in the logging context.
166
     *
167
     * @param mixed  $level    The level of the log message
168
     * @param array  $context  The array of context to output
169
     * @param int    $indent   The current level of indentation to be used
170
     *
171
     * @return void
172
     */
173
    private function outputRecursiveContext($level, array $context, $indent)
174
    {
175
        foreach ($context as $key => $val) {
176
            $this->climate->tab($indent);
177
178
            $this->climate->{$level}()->inline("{$key}: ");
179
180
            if (is_array($val)) {
181
                $this->climate->{$level}("[");
182
                $this->outputRecursiveContext($level, $val, $indent + 1);
183
                $this->climate->tab($indent)->{$level}("]");
184
            } else {
185
                $this->climate->{$level}((string) $val);
186
            }
187
        }
188
    }
189
}
190