bb10d1b5c514bf2c10b2042bfd84701931b01b0f
[yaffs-website] / vendor / consolidation / log / src / Logger.php
1 <?php
2 namespace Consolidation\Log;
3
4 use Psr\Log\AbstractLogger;
5 use Psr\Log\InvalidArgumentException;
6 use Psr\Log\LogLevel;
7 use Symfony\Component\Console\Output\OutputInterface;
8 use Symfony\Component\Console\Output\ConsoleOutputInterface;
9
10 use Symfony\Component\Console\Style\SymfonyStyle;
11 use Symfony\Component\Console\Input\StringInput;
12
13 /**
14  * Replacement for Symfony\Component\Console\Logger\ConsoleLogger.
15  * Each of the different log level messages are routed through the
16  * corresponding SymfonyStyle formatting method.  Log messages are
17  * always sent to stderr if the provided output object implements
18  * ConsoleOutputInterface.
19  *
20  * Note that this class could extend ConsoleLogger if some methods
21  * of that class were declared 'protected' instead of 'private'.
22  *
23  * @author Greg Anderson <greg.1.anderson@greenknowe.org>
24  */
25 class Logger extends AbstractLogger // extends ConsoleLogger
26 {
27     /**
28      * @var OutputInterface
29      */
30     protected $output;
31     /**
32      * @var OutputInterface
33      */
34     protected $error;
35     /**
36      * @var LogOutputStylerInterface
37      */
38     protected $outputStyler;
39     /**
40      * @var OutputInterface|SymfonyStyle|other
41      */
42     protected $outputStreamWrapper;
43     protected $errorStreamWrapper;
44
45     protected $formatFunctionMap = [
46         LogLevel::EMERGENCY => 'error',
47         LogLevel::ALERT => 'error',
48         LogLevel::CRITICAL => 'error',
49         LogLevel::ERROR => 'error',
50         LogLevel::WARNING => 'warning',
51         LogLevel::NOTICE => 'note',
52         LogLevel::INFO => 'note',
53         LogLevel::DEBUG => 'note',
54         ConsoleLogLevel::SUCCESS => 'success',
55     ];
56
57     /**
58      * @param OutputInterface $output
59      * @param array           $verbosityLevelMap
60      * @param array           $formatLevelMap
61      * @param array           $formatFunctionMap
62      */
63     public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array(), array $formatFunctionMap = array())
64     {
65         $this->output = $output;
66
67         $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
68         $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
69         $this->formatFunctionMap = $formatFunctionMap + $this->formatFunctionMap;
70     }
71
72     public function setLogOutputStyler(LogOutputStylerInterface $outputStyler, array $formatFunctionMap = array())
73     {
74         $this->outputStyler = $outputStyler;
75         $this->formatFunctionMap = $formatFunctionMap + $this->formatFunctionMap;
76         $this->outputStreamWrapper = null;
77         $this->errorStreamWrapper = null;
78     }
79
80     public function getLogOutputStyler()
81     {
82         if (!isset($this->outputStyler)) {
83             $this->outputStyler = new SymfonyLogOutputStyler();
84         }
85         return $this->outputStyler;
86     }
87
88     protected function getOutputStream()
89     {
90         return $this->output;
91     }
92
93     protected function getErrorStream()
94     {
95         if (!isset($this->error)) {
96             $output = $this->getOutputStream();
97             if ($output instanceof ConsoleOutputInterface) {
98                 $output = $output->getErrorOutput();
99             }
100             $this->error = $output;
101         }
102         return $this->error;
103     }
104
105     public function setOutputStream($output)
106     {
107         $this->output = $output;
108         $this->outputStreamWrapper = null;
109     }
110
111     public function setErrorStream($error)
112     {
113         $this->error = $error;
114         $this->errorStreamWrapper = null;
115     }
116
117     protected function getOutputStreamWrapper()
118     {
119         if (!isset($this->outputStreamWrapper)) {
120             $this->outputStreamWrapper = $this->getLogOutputStyler()->createOutputWrapper($this->getOutputStream());
121         }
122         return $this->outputStreamWrapper;
123     }
124
125     protected function getErrorStreamWrapper()
126     {
127         if (!isset($this->errorStreamWrapper)) {
128             $this->errorStreamWrapper = $this->getLogOutputStyler()->createOutputWrapper($this->getErrorStream());
129         }
130         return $this->errorStreamWrapper;
131     }
132
133     protected function getOutputStreamForLogLevel($level)
134     {
135         // Write to the error output if necessary and available.
136         // Usually, loggers that log to a terminal should send
137         // all log messages to stderr.
138         if (array_key_exists($level, $this->formatLevelMap) && ($this->formatLevelMap[$level] !== self::ERROR)) {
139             return $this->getOutputStreamWrapper();
140         }
141         return $this->getErrorStreamWrapper();
142     }
143
144     /**
145      * {@inheritdoc}
146      */
147     public function log($level, $message, array $context = array())
148     {
149         // We use the '_level' context variable to allow log messages
150         // to be logged at one level (e.g. NOTICE) and formatted at another
151         // level (e.g. SUCCESS). This helps in instances where we want
152         // to style log messages at a custom log level that might not
153         // be available in all loggers. If the logger does not recognize
154         // the log level, then it is treated like the original log level.
155         if (array_key_exists('_level', $context) && array_key_exists($context['_level'], $this->verbosityLevelMap)) {
156             $level = $context['_level'];
157         }
158         // It is a runtime error if someone logs at a log level that
159         // we do not recognize.
160         if (!isset($this->verbosityLevelMap[$level])) {
161             throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
162         }
163
164         // Write to the error output if necessary and available.
165         // Usually, loggers that log to a terminal should send
166         // all log messages to stderr.
167         $outputStreamWrapper = $this->getOutputStreamForLogLevel($level);
168
169         // Ignore messages that are not at the right verbosity level
170         if ($this->getOutputStream()->getVerbosity() >= $this->verbosityLevelMap[$level]) {
171             $this->doLog($outputStreamWrapper, $level, $message, $context);
172         }
173     }
174
175     /**
176      * Interpolate and style the message, and then send it to the log.
177      */
178     protected function doLog($outputStreamWrapper, $level, $message, $context)
179     {
180         $formatFunction = 'log';
181         if (array_key_exists($level, $this->formatFunctionMap)) {
182             $formatFunction = $this->formatFunctionMap[$level];
183         }
184         $interpolated = $this->interpolate(
185             $message,
186             $this->getLogOutputStyler()->style($context)
187         );
188         $this->getLogOutputStyler()->$formatFunction(
189             $outputStreamWrapper,
190             $level,
191             $interpolated,
192             $context
193         );
194     }
195
196     public function success($message, array $context = array())
197     {
198         $this->log(ConsoleLogLevel::SUCCESS, $message, $context);
199     }
200
201     // The functions below could be eliminated if made `protected` intead
202     // of `private` in ConsoleLogger
203
204     const INFO = 'info';
205     const ERROR = 'error';
206
207     /**
208      * @var OutputInterface
209      */
210     //private $output;
211     /**
212      * @var array
213      */
214     private $verbosityLevelMap = [
215         LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
216         LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
217         LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
218         LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
219         LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
220         LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE,
221         LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE,
222         LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG,
223         ConsoleLogLevel::SUCCESS => OutputInterface::VERBOSITY_NORMAL,
224     ];
225
226     /**
227      * @var array
228      *
229      * Send all log messages to stderr. Symfony should have the same default.
230      * See: https://en.wikipedia.org/wiki/Standard_streams
231      *   "Standard error was added to Unix after several wasted phototypesetting runs ended with error messages being typeset instead of displayed on the user's terminal."
232      *
233      */
234     private $formatLevelMap = [
235         LogLevel::EMERGENCY => self::ERROR,
236         LogLevel::ALERT => self::ERROR,
237         LogLevel::CRITICAL => self::ERROR,
238         LogLevel::ERROR => self::ERROR,
239         LogLevel::WARNING => self::ERROR,
240         LogLevel::NOTICE => self::ERROR,
241         LogLevel::INFO => self::ERROR,
242         LogLevel::DEBUG => self::ERROR,
243         ConsoleLogLevel::SUCCESS => self::ERROR,
244     ];
245
246     /**
247      * Interpolates context values into the message placeholders.
248      *
249      * @author PHP Framework Interoperability Group
250      *
251      * @param string $message
252      * @param array  $context
253      *
254      * @return string
255      */
256     private function interpolate($message, array $context)
257     {
258         // build a replacement array with braces around the context keys
259         $replace = array();
260         foreach ($context as $key => $val) {
261             if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
262                 $replace[sprintf('{%s}', $key)] = $val;
263             }
264         }
265
266         // interpolate replacement values into the message and return
267         return strtr($message, $replace);
268     }
269 }