MessageFormatter.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. namespace GuzzleHttp;
  3. use Psr\Http\Message\MessageInterface;
  4. use Psr\Http\Message\RequestInterface;
  5. use Psr\Http\Message\ResponseInterface;
  6. /**
  7. * Formats log messages using variable substitutions for requests, responses,
  8. * and other transactional data.
  9. *
  10. * The following variable substitutions are supported:
  11. *
  12. * - {request}: Full HTTP request message
  13. * - {response}: Full HTTP response message
  14. * - {ts}: ISO 8601 date in GMT
  15. * - {date_iso_8601} ISO 8601 date in GMT
  16. * - {date_common_log} Apache common log date using the configured timezone.
  17. * - {host}: Host of the request
  18. * - {method}: Method of the request
  19. * - {uri}: URI of the request
  20. * - {version}: Protocol version
  21. * - {target}: Request target of the request (path + query + fragment)
  22. * - {hostname}: Hostname of the machine that sent the request
  23. * - {code}: Status code of the response (if available)
  24. * - {phrase}: Reason phrase of the response (if available)
  25. * - {error}: Any error messages (if available)
  26. * - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
  27. * - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
  28. * - {req_headers}: Request headers
  29. * - {res_headers}: Response headers
  30. * - {req_body}: Request body
  31. * - {res_body}: Response body
  32. */
  33. class MessageFormatter
  34. {
  35. /**
  36. * Apache Common Log Format.
  37. * @link http://httpd.apache.org/docs/2.4/logs.html#common
  38. * @var string
  39. */
  40. const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
  41. const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
  42. const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
  43. /** @var string Template used to format log messages */
  44. private $template;
  45. /**
  46. * @param string $template Log message template
  47. */
  48. public function __construct($template = self::CLF)
  49. {
  50. $this->template = $template ?: self::CLF;
  51. }
  52. /**
  53. * Returns a formatted message string.
  54. *
  55. * @param RequestInterface $request Request that was sent
  56. * @param ResponseInterface $response Response that was received
  57. * @param \Exception $error Exception that was received
  58. *
  59. * @return string
  60. */
  61. public function format(
  62. RequestInterface $request,
  63. ResponseInterface $response = null,
  64. \Exception $error = null
  65. ) {
  66. $cache = [];
  67. return preg_replace_callback(
  68. '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
  69. function (array $matches) use ($request, $response, $error, &$cache) {
  70. if (isset($cache[$matches[1]])) {
  71. return $cache[$matches[1]];
  72. }
  73. $result = '';
  74. switch ($matches[1]) {
  75. case 'request':
  76. $result = Psr7\str($request);
  77. break;
  78. case 'response':
  79. $result = $response ? Psr7\str($response) : '';
  80. break;
  81. case 'req_headers':
  82. $result = trim($request->getMethod()
  83. . ' ' . $request->getRequestTarget())
  84. . ' HTTP/' . $request->getProtocolVersion() . "\r\n"
  85. . $this->headers($request);
  86. break;
  87. case 'res_headers':
  88. $result = $response ?
  89. sprintf(
  90. 'HTTP/%s %d %s',
  91. $response->getProtocolVersion(),
  92. $response->getStatusCode(),
  93. $response->getReasonPhrase()
  94. ) . "\r\n" . $this->headers($response)
  95. : 'NULL';
  96. break;
  97. case 'req_body':
  98. $result = $request->getBody();
  99. break;
  100. case 'res_body':
  101. $result = $response ? $response->getBody() : 'NULL';
  102. break;
  103. case 'ts':
  104. case 'date_iso_8601':
  105. $result = gmdate('c');
  106. break;
  107. case 'date_common_log':
  108. $result = date('d/M/Y:H:i:s O');
  109. break;
  110. case 'method':
  111. $result = $request->getMethod();
  112. break;
  113. case 'version':
  114. $result = $request->getProtocolVersion();
  115. break;
  116. case 'uri':
  117. case 'url':
  118. $result = $request->getUri();
  119. break;
  120. case 'target':
  121. $result = $request->getRequestTarget();
  122. break;
  123. case 'req_version':
  124. $result = $request->getProtocolVersion();
  125. break;
  126. case 'res_version':
  127. $result = $response
  128. ? $response->getProtocolVersion()
  129. : 'NULL';
  130. break;
  131. case 'host':
  132. $result = $request->getHeaderLine('Host');
  133. break;
  134. case 'hostname':
  135. $result = gethostname();
  136. break;
  137. case 'code':
  138. $result = $response ? $response->getStatusCode() : 'NULL';
  139. break;
  140. case 'phrase':
  141. $result = $response ? $response->getReasonPhrase() : 'NULL';
  142. break;
  143. case 'error':
  144. $result = $error ? $error->getMessage() : 'NULL';
  145. break;
  146. default:
  147. // handle prefixed dynamic headers
  148. if (strpos($matches[1], 'req_header_') === 0) {
  149. $result = $request->getHeaderLine(substr($matches[1], 11));
  150. } elseif (strpos($matches[1], 'res_header_') === 0) {
  151. $result = $response
  152. ? $response->getHeaderLine(substr($matches[1], 11))
  153. : 'NULL';
  154. }
  155. }
  156. $cache[$matches[1]] = $result;
  157. return $result;
  158. },
  159. $this->template
  160. );
  161. }
  162. private function headers(MessageInterface $message)
  163. {
  164. $result = '';
  165. foreach ($message->getHeaders() as $name => $values) {
  166. $result .= $name . ': ' . implode(', ', $values) . "\r\n";
  167. }
  168. return trim($result);
  169. }
  170. }