scratch – Blame information for rev 115

Subversion Repositories:
Rev:
Rev Author Line No. Line
115 office 1 <?php
2  
3 /*
4 * This file is part of the Monolog package.
5 *
6 * (c) Jordi Boggiano <j.boggiano@seld.be>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11  
12 namespace Monolog\Handler;
13  
14 use Monolog\Formatter\ChromePHPFormatter;
15 use Monolog\Logger;
16  
17 /**
18 * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
19 *
20 * This also works out of the box with Firefox 43+
21 *
22 * @author Christophe Coevoet <stof@notk.org>
23 */
24 class ChromePHPHandler extends AbstractProcessingHandler
25 {
26 /**
27 * Version of the extension
28 */
29 const VERSION = '4.0';
30  
31 /**
32 * Header name
33 */
34 const HEADER_NAME = 'X-ChromeLogger-Data';
35  
36 /**
37 * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+)
38 */
39 const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';
40  
41 protected static $initialized = false;
42  
43 /**
44 * Tracks whether we sent too much data
45 *
46 * Chrome limits the headers to 256KB, so when we sent 240KB we stop sending
47 *
48 * @var Boolean
49 */
50 protected static $overflowed = false;
51  
52 protected static $json = array(
53 'version' => self::VERSION,
54 'columns' => array('label', 'log', 'backtrace', 'type'),
55 'rows' => array(),
56 );
57  
58 protected static $sendHeaders = true;
59  
60 /**
61 * @param int $level The minimum logging level at which this handler will be triggered
62 * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
63 */
64 public function __construct($level = Logger::DEBUG, $bubble = true)
65 {
66 parent::__construct($level, $bubble);
67 if (!function_exists('json_encode')) {
68 throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler');
69 }
70 }
71  
72 /**
73 * {@inheritdoc}
74 */
75 public function handleBatch(array $records)
76 {
77 $messages = array();
78  
79 foreach ($records as $record) {
80 if ($record['level'] < $this->level) {
81 continue;
82 }
83 $messages[] = $this->processRecord($record);
84 }
85  
86 if (!empty($messages)) {
87 $messages = $this->getFormatter()->formatBatch($messages);
88 self::$json['rows'] = array_merge(self::$json['rows'], $messages);
89 $this->send();
90 }
91 }
92  
93 /**
94 * {@inheritDoc}
95 */
96 protected function getDefaultFormatter()
97 {
98 return new ChromePHPFormatter();
99 }
100  
101 /**
102 * Creates & sends header for a record
103 *
104 * @see sendHeader()
105 * @see send()
106 * @param array $record
107 */
108 protected function write(array $record)
109 {
110 self::$json['rows'][] = $record['formatted'];
111  
112 $this->send();
113 }
114  
115 /**
116 * Sends the log header
117 *
118 * @see sendHeader()
119 */
120 protected function send()
121 {
122 if (self::$overflowed || !self::$sendHeaders) {
123 return;
124 }
125  
126 if (!self::$initialized) {
127 self::$initialized = true;
128  
129 self::$sendHeaders = $this->headersAccepted();
130 if (!self::$sendHeaders) {
131 return;
132 }
133  
134 self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
135 }
136  
137 $json = @json_encode(self::$json);
138 $data = base64_encode(utf8_encode($json));
139 if (strlen($data) > 240 * 1024) {
140 self::$overflowed = true;
141  
142 $record = array(
143 'message' => 'Incomplete logs, chrome header size limit reached',
144 'context' => array(),
145 'level' => Logger::WARNING,
146 'level_name' => Logger::getLevelName(Logger::WARNING),
147 'channel' => 'monolog',
148 'datetime' => new \DateTime(),
149 'extra' => array(),
150 );
151 self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
152 $json = @json_encode(self::$json);
153 $data = base64_encode(utf8_encode($json));
154 }
155  
156 if (trim($data) !== '') {
157 $this->sendHeader(self::HEADER_NAME, $data);
158 }
159 }
160  
161 /**
162 * Send header string to the client
163 *
164 * @param string $header
165 * @param string $content
166 */
167 protected function sendHeader($header, $content)
168 {
169 if (!headers_sent() && self::$sendHeaders) {
170 header(sprintf('%s: %s', $header, $content));
171 }
172 }
173  
174 /**
175 * Verifies if the headers are accepted by the current user agent
176 *
177 * @return Boolean
178 */
179 protected function headersAccepted()
180 {
181 if (empty($_SERVER['HTTP_USER_AGENT'])) {
182 return false;
183 }
184  
185 return preg_match(self::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']);
186 }
187  
188 /**
189 * BC getter for the sendHeaders property that has been made static
190 */
191 public function __get($property)
192 {
193 if ('sendHeaders' !== $property) {
194 throw new \InvalidArgumentException('Undefined property '.$property);
195 }
196  
197 return static::$sendHeaders;
198 }
199  
200 /**
201 * BC setter for the sendHeaders property that has been made static
202 */
203 public function __set($property, $value)
204 {
205 if ('sendHeaders' !== $property) {
206 throw new \InvalidArgumentException('Undefined property '.$property);
207 }
208  
209 static::$sendHeaders = $value;
210 }
211 }