feature #30964 [HttpKernel] Add a "short" trace header format, make header configurable (mpdude)
This PR was squashed before being merged into the 4.3-dev branch (closes #30964).
Discussion
----------
[HttpKernel] Add a "short" trace header format, make header configurable
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets |
| License | MIT
| Doc PR | symfony/symfony-docs#11340
This pull requests adds the first usage of `array_key_first` to the Symfony code base. Additionally, it makes it possible to configure the `HttpCache` to also add a "trace" header in production.
The `HttpCache` is a convenient, low-barrier yet performant way of accelerating the application. By having the "trace" information returned as a header in production as well, you can write it to server log files. For example, with Apache you can use `%{X-Symfony-Cache}o` in the `LogFormat` directive to log response headers.
With the information in the log files, you can easily process it from logfile processing/system metrics tools to find out about cache performance, efficiency and the URLs that might need extra cache tweaking.
<img width="1040" alt="Bildschirmfoto 2019-04-07 um 11 43 23" src="https://user-images.githubusercontent.com/1202333/55681763-6e90e980-592a-11e9-900f-e096350531c2.png">
The "short" format will only output information for the main request to avoid leaking internal URLs for ESI subrequests. I also chose a concise format like `stale/valid/store` because that's much easier to parse out of logfiles (no whitespace, no need for quotes etc.).
If you're not comfortable with having `Symfony` in the header name that way, the header name can be changed through a configuration setting as well.
#FOSSHackathon
Commits
-------
9a2fcc9392
[HttpKernel] Add a \"short\" trace header format, make header configurable
This commit is contained in:
commit
226b36e99a
@ -24,6 +24,7 @@ CHANGELOG
|
|||||||
* renamed `GetResponseForExceptionEvent` to `ExceptionEvent`
|
* renamed `GetResponseForExceptionEvent` to `ExceptionEvent`
|
||||||
* renamed `PostResponseEvent` to `TerminateEvent`
|
* renamed `PostResponseEvent` to `TerminateEvent`
|
||||||
* added `HttpClientKernel` for handling requests with an `HttpClientInterface` instance
|
* added `HttpClientKernel` for handling requests with an `HttpClientInterface` instance
|
||||||
|
* added `trace_header` and `trace_level` configuration options to `HttpCache`
|
||||||
|
|
||||||
4.2.0
|
4.2.0
|
||||||
-----
|
-----
|
||||||
|
@ -40,7 +40,14 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
|||||||
*
|
*
|
||||||
* The available options are:
|
* The available options are:
|
||||||
*
|
*
|
||||||
* * debug: If true, the traces are added as a HTTP header to ease debugging
|
* * debug If true, exceptions are thrown when things go wrong. Otherwise, the cache
|
||||||
|
* will try to carry on and deliver a meaningful response.
|
||||||
|
*
|
||||||
|
* * trace_level May be one of 'none', 'short' and 'full'. For 'short', a concise trace of the
|
||||||
|
* master request will be added as an HTTP header. 'full' will add traces for all
|
||||||
|
* requests (including ESI subrequests). (default: 'full' if in debug; 'none' otherwise)
|
||||||
|
*
|
||||||
|
* * trace_header Header name to use for traces. (default: X-Symfony-Cache)
|
||||||
*
|
*
|
||||||
* * default_ttl The number of seconds that a cache entry should be considered
|
* * default_ttl The number of seconds that a cache entry should be considered
|
||||||
* fresh when no explicit freshness information is provided in
|
* fresh when no explicit freshness information is provided in
|
||||||
@ -87,7 +94,13 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
|||||||
'allow_revalidate' => false,
|
'allow_revalidate' => false,
|
||||||
'stale_while_revalidate' => 2,
|
'stale_while_revalidate' => 2,
|
||||||
'stale_if_error' => 60,
|
'stale_if_error' => 60,
|
||||||
|
'trace_level' => 'none',
|
||||||
|
'trace_header' => 'X-Symfony-Cache',
|
||||||
], $options);
|
], $options);
|
||||||
|
|
||||||
|
if (!isset($options['trace_level']) && $this->options['debug']) {
|
||||||
|
$this->options['trace_level'] = 'full';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,6 +123,23 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
|||||||
return $this->traces;
|
return $this->traces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addTraces(Response $response)
|
||||||
|
{
|
||||||
|
$traceString = null;
|
||||||
|
|
||||||
|
if ('full' === $this->options['trace_level']) {
|
||||||
|
$traceString = $this->getLog();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('short' === $this->options['trace_level'] && $masterId = array_key_first($this->traces)) {
|
||||||
|
$traceString = implode('/', $this->traces[$masterId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $traceString) {
|
||||||
|
$response->headers->add([$this->options['trace_header'] => $traceString]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a log message for the events of the last request processing.
|
* Returns a log message for the events of the last request processing.
|
||||||
*
|
*
|
||||||
@ -194,8 +224,8 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
|||||||
|
|
||||||
$this->restoreResponseBody($request, $response);
|
$this->restoreResponseBody($request, $response);
|
||||||
|
|
||||||
if (HttpKernelInterface::MASTER_REQUEST === $type && $this->options['debug']) {
|
if (HttpKernelInterface::MASTER_REQUEST === $type) {
|
||||||
$response->headers->set('X-Symfony-Cache', $this->getLog());
|
$this->addTraces($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $this->surrogate) {
|
if (null !== $this->surrogate) {
|
||||||
|
@ -1508,6 +1508,44 @@ class HttpCacheTest extends HttpCacheTestCase
|
|||||||
// Surrogate request
|
// Surrogate request
|
||||||
$cache->handle($request, HttpKernelInterface::SUB_REQUEST);
|
$cache->handle($request, HttpKernelInterface::SUB_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testTraceHeaderNameCanBeChanged()
|
||||||
|
{
|
||||||
|
$this->cacheConfig['trace_header'] = 'X-My-Header';
|
||||||
|
$this->setNextResponse();
|
||||||
|
$this->request('GET', '/');
|
||||||
|
|
||||||
|
$this->assertTrue($this->response->headers->has('X-My-Header'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTraceLevelDefaultsToFullIfDebug()
|
||||||
|
{
|
||||||
|
$this->setNextResponse();
|
||||||
|
$this->request('GET', '/');
|
||||||
|
|
||||||
|
$this->assertTrue($this->response->headers->has('X-Symfony-Cache'));
|
||||||
|
$this->assertEquals('GET /: miss', $this->response->headers->get('X-Symfony-Cache'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTraceLevelDefaultsToNoneIfNotDebug()
|
||||||
|
{
|
||||||
|
$this->cacheConfig['debug'] = false;
|
||||||
|
$this->setNextResponse();
|
||||||
|
$this->request('GET', '/');
|
||||||
|
|
||||||
|
$this->assertFalse($this->response->headers->has('X-Symfony-Cache'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTraceLevelShort()
|
||||||
|
{
|
||||||
|
$this->cacheConfig['trace_level'] = 'short';
|
||||||
|
|
||||||
|
$this->setNextResponse();
|
||||||
|
$this->request('GET', '/');
|
||||||
|
|
||||||
|
$this->assertTrue($this->response->headers->has('X-Symfony-Cache'));
|
||||||
|
$this->assertEquals('miss', $this->response->headers->get('X-Symfony-Cache'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestKernel implements HttpKernelInterface
|
class TestKernel implements HttpKernelInterface
|
||||||
|
@ -122,7 +122,9 @@ class HttpCacheTestCase extends TestCase
|
|||||||
|
|
||||||
$this->store = new Store(sys_get_temp_dir().'/http_cache');
|
$this->store = new Store(sys_get_temp_dir().'/http_cache');
|
||||||
|
|
||||||
$this->cacheConfig['debug'] = true;
|
if (!isset($this->cacheConfig['debug'])) {
|
||||||
|
$this->cacheConfig['debug'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
$this->esi = $esi ? new Esi() : null;
|
$this->esi = $esi ? new Esi() : null;
|
||||||
$this->cache = new HttpCache($this->kernel, $this->store, $this->esi, $this->cacheConfig);
|
$this->cache = new HttpCache($this->kernel, $this->store, $this->esi, $this->cacheConfig);
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"symfony/http-foundation": "^4.1.1",
|
"symfony/http-foundation": "^4.1.1",
|
||||||
"symfony/debug": "~3.4|~4.0",
|
"symfony/debug": "~3.4|~4.0",
|
||||||
"symfony/polyfill-ctype": "~1.8",
|
"symfony/polyfill-ctype": "~1.8",
|
||||||
|
"symfony/polyfill-php73": "^1.9",
|
||||||
"psr/log": "~1.0"
|
"psr/log": "~1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
Reference in New Issue
Block a user