Logger.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <?php
  2. namespace yzh52521\EasyHttp;
  3. use GuzzleHttp\Exception\RequestException;
  4. use GuzzleHttp\MessageFormatter;
  5. use GuzzleHttp\Promise;
  6. use Psr\Http\Message\RequestInterface;
  7. use Psr\Http\Message\ResponseInterface;
  8. use Psr\Log\LogLevel;
  9. use Psr\Log\LoggerInterface;
  10. use InvalidArgumentException;
  11. /**
  12. * Guzzle middleware which logs a request and its response.
  13. */
  14. class Logger
  15. {
  16. /**
  17. * @var \Psr\Log\LoggerInterface|callable
  18. */
  19. protected $logger;
  20. /**
  21. * @var \GuzzleHttp\MessageFormatter|callable
  22. */
  23. protected $formatter;
  24. /**
  25. * @var string|callable Constant or callable that accepts a Response.
  26. */
  27. protected $logLevel;
  28. /**
  29. * @var boolean Whether or not to log requests as they are made.
  30. */
  31. protected $logRequests;
  32. /**
  33. * Creates a callable middleware for logging requests and responses.
  34. *
  35. * @param LoggerInterface|callable $logger
  36. * @param string|callable Constant or callable that accepts a Response.
  37. */
  38. public function __construct($logger, $formatter = null)
  39. {
  40. // Use the setters to take care of type validation
  41. $this->setLogger($logger);
  42. $this->setFormatter($formatter ?: $this->getDefaultFormatter());
  43. }
  44. /**
  45. * Returns the default formatter;
  46. *
  47. * @return MessageFormatter
  48. */
  49. protected function getDefaultFormatter()
  50. {
  51. return new MessageFormatter();
  52. }
  53. /**
  54. * Sets whether requests should be logged before the response is received.
  55. *
  56. * @param boolean $logRequests
  57. */
  58. public function setRequestLoggingEnabled($logRequests = true)
  59. {
  60. $this->logRequests = (bool) $logRequests;
  61. }
  62. /**
  63. * Sets the logger, which can be a PSR-3 logger or a callable that accepts
  64. * a log level, message, and array context.
  65. *
  66. * @param LoggerInterface|callable $logger
  67. *
  68. * @throws InvalidArgumentException
  69. */
  70. public function setLogger($logger)
  71. {
  72. if ($logger instanceof LoggerInterface || is_callable($logger)) {
  73. $this->logger = $logger;
  74. } else {
  75. throw new InvalidArgumentException(
  76. "Logger has to be a Psr\Log\LoggerInterface or callable"
  77. );
  78. }
  79. }
  80. /**
  81. * Sets the formatter, which can be a MessageFormatter or callable that
  82. * accepts a request, response, and a reason if an error has occurred.
  83. *
  84. * @param MessageFormatter|callable $formatter
  85. *
  86. * @throws InvalidArgumentException
  87. */
  88. public function setFormatter($formatter)
  89. {
  90. if ($formatter instanceof MessageFormatter || is_callable($formatter)) {
  91. $this->formatter = $formatter;
  92. } else {
  93. throw new InvalidArgumentException(
  94. "Formatter has to be a \GuzzleHttp\MessageFormatter or callable"
  95. );
  96. }
  97. }
  98. /**
  99. * Sets the log level to use, which can be either a string or a callable
  100. * that accepts a response (which could be null). A log level could also
  101. * be null, which indicates that the default log level should be used.
  102. *
  103. * @param string|callable|null
  104. */
  105. public function setLogLevel($logLevel)
  106. {
  107. $this->logLevel = $logLevel;
  108. }
  109. /**
  110. * Logs a request and/or a response.
  111. *
  112. * @param RequestInterface $request
  113. * @param ResponseInterface|null $response
  114. * @param $reason
  115. * @return mixed
  116. */
  117. protected function log(
  118. RequestInterface $request,
  119. ResponseInterface $response = null,
  120. $reason = null
  121. ) {
  122. if ($reason instanceof RequestException) {
  123. $response = $reason->getResponse();
  124. }
  125. $level = $this->getLogLevel($response);
  126. $message = $this->getLogMessage($request, $response, $reason);
  127. $context = compact('request', 'response', 'reason');
  128. // Make sure that the content of the body is available again.
  129. if ($response) {
  130. $response->getBody()->seek(0);;
  131. }
  132. if (is_callable($this->logger)) {
  133. return call_user_func($this->logger, $level, $message, $context);
  134. }
  135. $this->logger->log($level, $message, $context);
  136. }
  137. /**
  138. * Formats a request and response as a log message.
  139. *
  140. * @param RequestInterface $request
  141. * @param ResponseInterface|null $response
  142. * @param mixed $reason
  143. *
  144. * @return string The formatted message.
  145. */
  146. protected function getLogMessage(
  147. RequestInterface $request,
  148. ResponseInterface $response = null,
  149. $reason = null
  150. ) {
  151. if ($this->formatter instanceof MessageFormatter) {
  152. return $this->formatter->format(
  153. $request,
  154. $response,
  155. $reason
  156. );
  157. }
  158. return call_user_func($this->formatter, $request, $response, $reason);
  159. }
  160. /**
  161. * Returns a log level for a given response.
  162. *
  163. * @param ResponseInterface $response The response being logged.
  164. *
  165. * @return string LogLevel
  166. */
  167. protected function getLogLevel(ResponseInterface $response = null)
  168. {
  169. if ( ! $this->logLevel) {
  170. return $this->getDefaultLogLevel($response);
  171. }
  172. if (is_callable($this->logLevel)) {
  173. return call_user_func($this->logLevel, $response);
  174. }
  175. return (string) $this->logLevel;
  176. }
  177. /**
  178. * Returns the default log level for a response.
  179. *
  180. * @param ResponseInterface $response
  181. *
  182. * @return string LogLevel
  183. */
  184. protected function getDefaultLogLevel(ResponseInterface $response = null) {
  185. if ($response && $response->getStatusCode() >= 300) {
  186. return LogLevel::NOTICE;
  187. }
  188. return LogLevel::INFO;
  189. }
  190. /**
  191. * Returns a function which is handled when a request was successful.
  192. *
  193. * @param RequestInterface $request
  194. *
  195. * @return \Closure
  196. */
  197. protected function onSuccess(RequestInterface $request)
  198. {
  199. return function ($response) use ($request) {
  200. $this->log($request, $response);
  201. return $response;
  202. };
  203. }
  204. /**
  205. * Returns a function which is handled when a request was rejected.
  206. *
  207. * @param RequestInterface $request
  208. *
  209. * @return \Closure
  210. */
  211. protected function onFailure(RequestInterface $request)
  212. {
  213. return function ($reason) use ($request) {
  214. // Only log a rejected request if it hasn't already been logged.
  215. if ( ! $this->logRequests) {
  216. $this->log($request, null, $reason);
  217. }
  218. return Promise\rejection_for($reason);
  219. };
  220. }
  221. /**
  222. * Called when the middleware is handled by the client.
  223. *
  224. * @param callable $handler
  225. *
  226. * @return \Closure
  227. */
  228. public function __invoke(callable $handler)
  229. {
  230. return function ($request, array $options) use ($handler) {
  231. // Only log requests if explicitly set to do so
  232. if ($this->logRequests) {
  233. $this->log($request);
  234. }
  235. return $handler($request, $options)->then(
  236. $this->onSuccess($request),
  237. $this->onFailure($request)
  238. );
  239. };
  240. }
  241. }