REST: collect metrics on endpoint access
This is a modified version of Ie282bc5b5f5df0bbd6a40c8362ba73fcbbf36c2e
which was reverted in 5c7cca8776.
Bug: T321969
Change-Id: I566d54a473aa51c4cdaada21a49d63c0624aab93
This commit is contained in:
parent
fce85fd193
commit
f2e9d5108d
5 changed files with 70 additions and 5 deletions
|
|
@ -63,6 +63,8 @@ class EntryPoint {
|
||||||
$authority
|
$authority
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$stats = $services->getStatsdDataFactory();
|
||||||
|
|
||||||
return ( new Router(
|
return ( new Router(
|
||||||
self::getRouteFiles( $conf ),
|
self::getRouteFiles( $conf ),
|
||||||
ExtensionRegistry::getInstance()->getAttribute( 'RestRoutes' ),
|
ExtensionRegistry::getInstance()->getAttribute( 'RestRoutes' ),
|
||||||
|
|
@ -76,7 +78,9 @@ class EntryPoint {
|
||||||
new MWErrorReporter(),
|
new MWErrorReporter(),
|
||||||
$services->getHookContainer(),
|
$services->getHookContainer(),
|
||||||
$context->getRequest()->getSession()
|
$context->getRequest()->getSession()
|
||||||
) )->setCors( $cors );
|
) )
|
||||||
|
->setCors( $cors )
|
||||||
|
->setStats( $stats );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,15 @@ abstract class Handler {
|
||||||
$this->postInitSetup();
|
$this->postInitSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path this handler is bound to, including path variables.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPath(): string {
|
||||||
|
return $this->getConfig()['path'];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Router. The return type declaration causes it to raise
|
* Get the Router. The return type declaration causes it to raise
|
||||||
* a fatal error if init() has not yet been called.
|
* a fatal error if init() has not yet been called.
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace MediaWiki\Rest;
|
||||||
|
|
||||||
use AppendIterator;
|
use AppendIterator;
|
||||||
use BagOStuff;
|
use BagOStuff;
|
||||||
|
use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
|
||||||
use MediaWiki\Config\ServiceOptions;
|
use MediaWiki\Config\ServiceOptions;
|
||||||
use MediaWiki\HookContainer\HookContainer;
|
use MediaWiki\HookContainer\HookContainer;
|
||||||
use MediaWiki\MainConfigNames;
|
use MediaWiki\MainConfigNames;
|
||||||
|
|
@ -13,6 +14,7 @@ use MediaWiki\Rest\PathTemplateMatcher\PathMatcher;
|
||||||
use MediaWiki\Rest\Reporter\ErrorReporter;
|
use MediaWiki\Rest\Reporter\ErrorReporter;
|
||||||
use MediaWiki\Rest\Validator\Validator;
|
use MediaWiki\Rest\Validator\Validator;
|
||||||
use MediaWiki\Session\Session;
|
use MediaWiki\Session\Session;
|
||||||
|
use NullStatsdDataFactory;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Wikimedia\Message\MessageValue;
|
use Wikimedia\Message\MessageValue;
|
||||||
use Wikimedia\ObjectFactory\ObjectFactory;
|
use Wikimedia\ObjectFactory\ObjectFactory;
|
||||||
|
|
@ -80,6 +82,9 @@ class Router {
|
||||||
/** @var Session */
|
/** @var Session */
|
||||||
private $session;
|
private $session;
|
||||||
|
|
||||||
|
/** @var StatsdDataFactoryInterface */
|
||||||
|
private $stats;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
* @var array
|
* @var array
|
||||||
|
|
@ -135,6 +140,8 @@ class Router {
|
||||||
$this->errorReporter = $errorReporter;
|
$this->errorReporter = $errorReporter;
|
||||||
$this->hookContainer = $hookContainer;
|
$this->hookContainer = $hookContainer;
|
||||||
$this->session = $session;
|
$this->session = $session;
|
||||||
|
|
||||||
|
$this->stats = new NullStatsdDataFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -402,14 +409,34 @@ class Router {
|
||||||
$request->setPathParams( array_map( 'rawurldecode', $match['params'] ) );
|
$request->setPathParams( array_map( 'rawurldecode', $match['params'] ) );
|
||||||
$handler = $this->createHandler( $request, $match['userData'] );
|
$handler = $this->createHandler( $request, $match['userData'] );
|
||||||
|
|
||||||
|
// Replace any characters that may have a special meaning in the metrics DB.
|
||||||
|
$pathForMetrics = $handler->getPath();
|
||||||
|
$pathForMetrics = strtr( $pathForMetrics, '{}:', '-' );
|
||||||
|
$pathForMetrics = strtr( $pathForMetrics, '/.', '_' );
|
||||||
|
|
||||||
|
$statTime = microtime( true );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return $this->executeHandler( $handler );
|
$response = $this->executeHandler( $handler );
|
||||||
} catch ( HttpException $e ) {
|
} catch ( HttpException $e ) {
|
||||||
return $this->responseFactory->createFromException( $e );
|
$response = $this->responseFactory->createFromException( $e );
|
||||||
} catch ( Throwable $e ) {
|
} catch ( Throwable $e ) {
|
||||||
$this->errorReporter->reportError( $e, $handler, $request );
|
$this->errorReporter->reportError( $e, $handler, $request );
|
||||||
return $this->responseFactory->createFromException( $e );
|
$response = $this->responseFactory->createFromException( $e );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gather metrics
|
||||||
|
if ( $response->getStatusCode() >= 400 ) {
|
||||||
|
// count how often we return which error code
|
||||||
|
$statusCode = $response->getStatusCode();
|
||||||
|
$this->stats->increment( "rest_api_errors.$pathForMetrics.$requestMethod.$statusCode" );
|
||||||
|
} else {
|
||||||
|
// measure how long it takes to generate a response
|
||||||
|
$microtime = ( microtime( true ) - $statTime ) * 1000;
|
||||||
|
$this->stats->timing( "rest_api_latency.$pathForMetrics.$requestMethod", $microtime );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -507,4 +534,15 @@ class Router {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StatsdDataFactoryInterface $stats
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setStats( StatsdDataFactoryInterface $stats ): self {
|
||||||
|
$this->stats = $stats;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,19 @@ class RestStructureTest extends MediaWikiIntegrationTestCase {
|
||||||
|
|
||||||
$services = $this->createNoOpMock(
|
$services = $this->createNoOpMock(
|
||||||
MediaWikiServices::class,
|
MediaWikiServices::class,
|
||||||
[ 'getMainConfig', 'getHookContainer', 'getObjectFactory', 'getLocalServerObjectCache' ]
|
[
|
||||||
|
'getMainConfig',
|
||||||
|
'getHookContainer',
|
||||||
|
'getObjectFactory',
|
||||||
|
'getLocalServerObjectCache',
|
||||||
|
'getStatsdDataFactory',
|
||||||
|
]
|
||||||
);
|
);
|
||||||
$services->method( 'getMainConfig' )->willReturn( $config );
|
$services->method( 'getMainConfig' )->willReturn( $config );
|
||||||
$services->method( 'getHookContainer' )->willReturn( $hookContainer );
|
$services->method( 'getHookContainer' )->willReturn( $hookContainer );
|
||||||
$services->method( 'getObjectFactory' )->willReturn( $objectFactory );
|
$services->method( 'getObjectFactory' )->willReturn( $objectFactory );
|
||||||
$services->method( 'getLocalServerObjectCache' )->willReturn( new EmptyBagOStuff() );
|
$services->method( 'getLocalServerObjectCache' )->willReturn( new EmptyBagOStuff() );
|
||||||
|
$services->method( 'getStatsdDataFactory' )->willReturn( new NullStatsdDataFactory() );
|
||||||
|
|
||||||
return $services;
|
return $services;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,13 @@ class HandlerTest extends \MediaWikiUnitTestCase {
|
||||||
$this->assertStringEndsWith( $expected, $url );
|
$this->assertStringEndsWith( $expected, $url );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetPath() {
|
||||||
|
$handler = $this->newHandler();
|
||||||
|
$request = new RequestData();
|
||||||
|
$this->initHandler( $handler, $request, [ 'path' => 'just/some/path' ] );
|
||||||
|
$this->assertSame( 'just/some/path', $handler->getPath() );
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetResponseFactory() {
|
public function testGetResponseFactory() {
|
||||||
$handler = $this->newHandler();
|
$handler = $this->newHandler();
|
||||||
$this->initHandler( $handler, new RequestData() );
|
$this->initHandler( $handler, new RequestData() );
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue