1: <?php
2:
3: namespace Guzzle\Log;
4:
5: use Guzzle\Http\Curl\CurlHandle;
6: use Guzzle\Http\Message\RequestInterface;
7: use Guzzle\Http\Message\EntityEnclosingRequestInterface;
8: use Guzzle\Http\Message\Response;
9:
10: /**
11: * Message formatter used in various places in the framework
12: *
13: * Format messages using a template that can contain the the following variables:
14: *
15: * - {request}: Full HTTP request message
16: * - {response}: Full HTTP response message
17: * - {ts}: Timestamp
18: * - {host}: Host of the request
19: * - {method}: Method of the request
20: * - {url}: URL of the request
21: * - {host}: Host of the request
22: * - {protocol}: Request protocol
23: * - {version}: Protocol version
24: * - {resource}: Resource of the request (path + query + fragment)
25: * - {port}: Port of the request
26: * - {hostname}: Hostname of the machine that sent the request
27: * - {code}: Status code of the response (if available)
28: * - {phrase}: Reason phrase of the response (if available)
29: * - {curl_error}: Curl error message (if available)
30: * - {curl_code}: Curl error code (if available)
31: * - {curl_stderr}: Curl standard error (if available)
32: * - {connect_time}: Time in seconds it took to establish the connection (if available)
33: * - {total_time}: Total transaction time in seconds for last transfer (if available)
34: * - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
35: * - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
36: * - {req_body}: Request body
37: * - {res_body}: Response body
38: */
39: class MessageFormatter
40: {
41: const DEFAULT_FORMAT = "{hostname} {req_header_User-Agent} - [{ts}] \"{method} {resource} {protocol}/{version}\" {code} {res_header_Content-Length}";
42: const DEBUG_FORMAT = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}";
43: const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}';
44:
45: /**
46: * @var string Template used to format log messages
47: */
48: protected $template;
49:
50: /**
51: * @param string $template Log message template
52: */
53: public function __construct($template = self::DEFAULT_FORMAT)
54: {
55: $this->template = $template ?: self::DEFAULT_FORMAT;
56: }
57:
58: /**
59: * Set the template to use for logging
60: *
61: * @param string $template Log message template
62: *
63: * @return self
64: */
65: public function setTemplate($template)
66: {
67: $this->template = $template;
68:
69: return $this;
70: }
71:
72: /**
73: * Returns a formatted message
74: *
75: * @param RequestInterface $request Request that was sent
76: * @param Response $response Response that was received
77: * @param CurlHandle $handle Curl handle associated with the message
78: * @param array $customData Associative array of custom template data
79: *
80: * @return string
81: */
82: public function format(
83: RequestInterface $request,
84: Response $response = null,
85: CurlHandle $handle = null,
86: array $customData = array()
87: ) {
88: $cache = $customData;
89:
90: return preg_replace_callback(
91: '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
92: function (array $matches) use ($request, $response, $handle, &$cache) {
93:
94: if (array_key_exists($matches[1], $cache)) {
95: return $cache[$matches[1]];
96: }
97:
98: $result = '';
99: switch ($matches[1]) {
100: case 'request':
101: $result = (string) $request;
102: break;
103: case 'response':
104: $result = (string) $response;
105: break;
106: case 'req_body':
107: $result = $request instanceof EntityEnclosingRequestInterface
108: ? (string) $request->getBody() : '';
109: break;
110: case 'res_body':
111: $result = $response ? $response->getBody(true) : '';
112: break;
113: case 'ts':
114: $result = gmdate('c');
115: break;
116: case 'method':
117: $result = $request->getMethod();
118: break;
119: case 'url':
120: $result = (string) $request->getUrl();
121: break;
122: case 'resource':
123: $result = $request->getResource();
124: break;
125: case 'protocol':
126: $result = 'HTTP';
127: break;
128: case 'version':
129: $result = $request->getProtocolVersion();
130: break;
131: case 'host':
132: $result = $request->getHost();
133: break;
134: case 'hostname':
135: $result = gethostname();
136: break;
137: case 'port':
138: $result = $request->getPort();
139: break;
140: case 'code':
141: $result = $response ? $response->getStatusCode() : '';
142: break;
143: case 'phrase':
144: $result = $response ? $response->getReasonPhrase() : '';
145: break;
146: case 'connect_time':
147: if ($handle) {
148: $result = $handle->getInfo(CURLINFO_CONNECT_TIME);
149: } elseif ($response) {
150: $result = $response->getInfo('connect_time');
151: }
152: break;
153: case 'total_time':
154: if ($handle) {
155: $result = $handle->getInfo(CURLINFO_TOTAL_TIME);
156: } elseif ($response) {
157: $result = $response->getInfo('total_time');
158: }
159: break;
160: case 'curl_error':
161: $result = $handle ? $handle->getError() : '';
162: break;
163: case 'curl_code':
164: $result = $handle ? $handle->getErrorNo() : '';
165: break;
166: case 'curl_stderr':
167: $result = $handle ? $handle->getStderr() : '';
168: break;
169: default:
170: if (strpos($matches[1], 'req_header_') === 0) {
171: $result = $request->getHeader(substr($matches[1], 11));
172: } elseif (strpos($matches[1], 'res_header_') === 0) {
173: $result = $response->getHeader(substr($matches[1], 11));
174: }
175: }
176:
177: $cache[$matches[1]] = $result;
178: return $result;
179: },
180: $this->template
181: );
182: }
183: }
184: