FirePHPHandler.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. /*
  3. * This file is part of the Monolog package.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Monolog\Handler;
  11. use Monolog\Formatter\WildfireFormatter;
  12. /**
  13. * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
  14. *
  15. * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
  16. */
  17. class FirePHPHandler extends AbstractProcessingHandler
  18. {
  19. /**
  20. * WildFire JSON header message format
  21. */
  22. const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2';
  23. /**
  24. * FirePHP structure for parsing messages & their presentation
  25. */
  26. const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1';
  27. /**
  28. * Must reference a "known" plugin, otherwise headers won't display in FirePHP
  29. */
  30. const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3';
  31. /**
  32. * Header prefix for Wildfire to recognize & parse headers
  33. */
  34. const HEADER_PREFIX = 'X-Wf';
  35. /**
  36. * Whether or not Wildfire vendor-specific headers have been generated & sent yet
  37. */
  38. protected static $initialized = false;
  39. /**
  40. * Shared static message index between potentially multiple handlers
  41. * @var int
  42. */
  43. protected static $messageIndex = 1;
  44. protected static $sendHeaders = true;
  45. /**
  46. * Base header creation function used by init headers & record headers
  47. *
  48. * @param array $meta Wildfire Plugin, Protocol & Structure Indexes
  49. * @param string $message Log message
  50. * @return array Complete header string ready for the client as key and message as value
  51. */
  52. protected function createHeader(array $meta, $message)
  53. {
  54. $header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta));
  55. return array($header => $message);
  56. }
  57. /**
  58. * Creates message header from record
  59. *
  60. * @see createHeader()
  61. * @param array $record
  62. * @return string
  63. */
  64. protected function createRecordHeader(array $record)
  65. {
  66. // Wildfire is extensible to support multiple protocols & plugins in a single request,
  67. // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake.
  68. return $this->createHeader(
  69. array(1, 1, 1, self::$messageIndex++),
  70. $record['formatted']
  71. );
  72. }
  73. /**
  74. * {@inheritDoc}
  75. */
  76. protected function getDefaultFormatter()
  77. {
  78. return new WildfireFormatter();
  79. }
  80. /**
  81. * Wildfire initialization headers to enable message parsing
  82. *
  83. * @see createHeader()
  84. * @see sendHeader()
  85. * @return array
  86. */
  87. protected function getInitHeaders()
  88. {
  89. // Initial payload consists of required headers for Wildfire
  90. return array_merge(
  91. $this->createHeader(array('Protocol', 1), self::PROTOCOL_URI),
  92. $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI),
  93. $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI)
  94. );
  95. }
  96. /**
  97. * Send header string to the client
  98. *
  99. * @param string $header
  100. * @param string $content
  101. */
  102. protected function sendHeader($header, $content)
  103. {
  104. if (!headers_sent() && self::$sendHeaders) {
  105. header(sprintf('%s: %s', $header, $content));
  106. }
  107. }
  108. /**
  109. * Creates & sends header for a record, ensuring init headers have been sent prior
  110. *
  111. * @see sendHeader()
  112. * @see sendInitHeaders()
  113. * @param array $record
  114. */
  115. protected function write(array $record)
  116. {
  117. if (!self::$sendHeaders) {
  118. return;
  119. }
  120. // WildFire-specific headers must be sent prior to any messages
  121. if (!self::$initialized) {
  122. self::$initialized = true;
  123. self::$sendHeaders = $this->headersAccepted();
  124. if (!self::$sendHeaders) {
  125. return;
  126. }
  127. foreach ($this->getInitHeaders() as $header => $content) {
  128. $this->sendHeader($header, $content);
  129. }
  130. }
  131. $header = $this->createRecordHeader($record);
  132. if (trim(current($header)) !== '') {
  133. $this->sendHeader(key($header), current($header));
  134. }
  135. }
  136. /**
  137. * Verifies if the headers are accepted by the current user agent
  138. *
  139. * @return Boolean
  140. */
  141. protected function headersAccepted()
  142. {
  143. if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) {
  144. return true;
  145. }
  146. return isset($_SERVER['HTTP_X_FIREPHP_VERSION']);
  147. }
  148. /**
  149. * BC getter for the sendHeaders property that has been made static
  150. */
  151. public function __get($property)
  152. {
  153. if ('sendHeaders' !== $property) {
  154. throw new \InvalidArgumentException('Undefined property '.$property);
  155. }
  156. return static::$sendHeaders;
  157. }
  158. /**
  159. * BC setter for the sendHeaders property that has been made static
  160. */
  161. public function __set($property, $value)
  162. {
  163. if ('sendHeaders' !== $property) {
  164. throw new \InvalidArgumentException('Undefined property '.$property);
  165. }
  166. static::$sendHeaders = $value;
  167. }
  168. }