Stats: add copy to statsd feature
Enables copying a metric to statsd at the specified namespace. Bug: T240685 Change-Id: I15d6f100a31fbb98ad89568a2024e926cb47f937
This commit is contained in:
parent
7c4c423cce
commit
13f556ab3a
9 changed files with 106 additions and 3 deletions
|
|
@ -1957,7 +1957,8 @@ return [
|
|||
\Wikimedia\Stats\OutputFormats::getNewFormatter( $format ),
|
||||
$config->get( MainConfigNames::StatsTarget )
|
||||
);
|
||||
return new StatsFactory( 'core', $cache, $emitter, LoggerFactory::getInstance( 'Stats' ) );
|
||||
$factory = new StatsFactory( 'core', $cache, $emitter, LoggerFactory::getInstance( 'Stats' ) );
|
||||
return $factory->withStatsdDataFactory( $services->getStatsdDataFactory() );
|
||||
},
|
||||
|
||||
'TalkPageNotificationManager' => static function (
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ declare( strict_types=1 );
|
|||
|
||||
namespace Wikimedia\Stats\Metrics;
|
||||
|
||||
use IBufferingStatsdDataFactory;
|
||||
use Wikimedia\Stats\Exceptions\IllegalOperationException;
|
||||
use Wikimedia\Stats\Sample;
|
||||
use Wikimedia\Stats\StatsUtils;
|
||||
|
|
@ -61,6 +62,9 @@ class BaseMetric implements BaseMetricInterface {
|
|||
/** @var Sample[] */
|
||||
private array $samples = [];
|
||||
|
||||
/** @var IBufferingStatsdDataFactory|null */
|
||||
private ?IBufferingStatsdDataFactory $statsdDataFactory = null;
|
||||
|
||||
/** @inheritDoc */
|
||||
public function __construct( string $component, string $name ) {
|
||||
$this->component = $component;
|
||||
|
|
@ -113,6 +117,17 @@ class BaseMetric implements BaseMetricInterface {
|
|||
$this->workingLabels[$key] = StatsUtils::normalizeString( $value );
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getStatsdDataFactory() {
|
||||
return $this->statsdDataFactory;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function withStatsdDataFactory( $statsdDataFactory ): BaseMetric {
|
||||
$this->statsdDataFactory = $statsdDataFactory;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a label key
|
||||
*
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ declare( strict_types=1 );
|
|||
|
||||
namespace Wikimedia\Stats\Metrics;
|
||||
|
||||
use IBufferingStatsdDataFactory;
|
||||
use Wikimedia\Stats\Sample;
|
||||
|
||||
/**
|
||||
|
|
@ -120,4 +121,17 @@ interface BaseMetricInterface {
|
|||
* @return void
|
||||
*/
|
||||
public function clearLabels(): void;
|
||||
|
||||
/**
|
||||
* Gets StatsD Data Factory instance or null.
|
||||
*/
|
||||
public function getStatsdDataFactory();
|
||||
|
||||
/**
|
||||
* StatsD Data Factory instance to copy metrics to.
|
||||
*
|
||||
* @param IBufferingStatsdDataFactory $statsdDataFactory
|
||||
* @return BaseMetricInterface
|
||||
*/
|
||||
public function withStatsdDataFactory( IBufferingStatsdDataFactory $statsdDataFactory );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ class CounterMetric implements MetricInterface {
|
|||
*/
|
||||
private const TYPE_INDICATOR = "c";
|
||||
|
||||
/** @var string|null */
|
||||
private ?string $statsdNamespace = null;
|
||||
|
||||
/** @var BaseMetricInterface */
|
||||
private BaseMetricInterface $baseMetric;
|
||||
|
||||
|
|
@ -74,6 +77,10 @@ class CounterMetric implements MetricInterface {
|
|||
* @return void
|
||||
*/
|
||||
public function incrementBy( int $value, array $labels = [] ): void {
|
||||
if ( $this->statsdNamespace !== null ) {
|
||||
$this->baseMetric->getStatsdDataFactory()->updateCount( $this->statsdNamespace, $value );
|
||||
$this->statsdNamespace = null;
|
||||
}
|
||||
$this->baseMetric->addSample( new Sample( $this->baseMetric->getLabelValues(), $value ) );
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +138,14 @@ class CounterMetric implements MetricInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function copyToStatsdAt( string $statsdNamespace ): CounterMetric {
|
||||
if ( $this->baseMetric->getStatsdDataFactory() !== null ) {
|
||||
$this->statsdNamespace = $statsdNamespace;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function fresh(): CounterMetric {
|
||||
$this->baseMetric->clearLabels();
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ class GaugeMetric implements MetricInterface {
|
|||
*/
|
||||
private const TYPE_INDICATOR = "g";
|
||||
|
||||
/** @var string|null */
|
||||
private ?string $statsdNamespace = null;
|
||||
|
||||
/** @var BaseMetricInterface */
|
||||
private BaseMetricInterface $baseMetric;
|
||||
|
||||
|
|
@ -64,6 +67,10 @@ class GaugeMetric implements MetricInterface {
|
|||
* @return void
|
||||
*/
|
||||
public function set( float $value ): void {
|
||||
if ( $this->statsdNamespace !== null ) {
|
||||
$this->baseMetric->getStatsdDataFactory()->updateCount( $this->statsdNamespace, $value );
|
||||
$this->statsdNamespace = null;
|
||||
}
|
||||
$this->baseMetric->addSample( new Sample( $this->baseMetric->getLabelValues(), $value ) );
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +128,14 @@ class GaugeMetric implements MetricInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function copyToStatsdAt( string $statsdNamespace ) {
|
||||
if ( $this->baseMetric->getStatsdDataFactory() !== null ) {
|
||||
$this->statsdNamespace = $statsdNamespace;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function fresh(): GaugeMetric {
|
||||
$this->baseMetric->clearLabels();
|
||||
|
|
|
|||
|
|
@ -78,6 +78,14 @@ interface MetricInterface {
|
|||
*/
|
||||
public function withLabel( string $key, string $value );
|
||||
|
||||
/**
|
||||
* Copies metric operation to StatsD at provided namespace.
|
||||
*
|
||||
* @param string $statsdNamespace
|
||||
* @return CounterMetric|GaugeMetric|TimingMetric|NullMetric
|
||||
*/
|
||||
public function copyToStatsdAt( string $statsdNamespace );
|
||||
|
||||
/**
|
||||
* Returns metric with cleared labels.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ class TimingMetric implements MetricInterface {
|
|||
*/
|
||||
private const TYPE_INDICATOR = "ms";
|
||||
|
||||
/** @var string|null */
|
||||
private ?string $statsdNamespace = null;
|
||||
|
||||
/** @var BaseMetricInterface */
|
||||
private BaseMetricInterface $baseMetric;
|
||||
|
||||
|
|
@ -91,6 +94,10 @@ class TimingMetric implements MetricInterface {
|
|||
* @return void
|
||||
*/
|
||||
public function observe( float $value ): void {
|
||||
if ( $this->statsdNamespace !== null ) {
|
||||
$this->baseMetric->getStatsdDataFactory()->timing( $this->statsdNamespace, $value );
|
||||
$this->statsdNamespace = null;
|
||||
}
|
||||
$this->baseMetric->addSample( new Sample( $this->baseMetric->getLabelValues(), $value ) );
|
||||
}
|
||||
|
||||
|
|
@ -148,6 +155,14 @@ class TimingMetric implements MetricInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function copyToStatsdAt( string $statsdNamespace ) {
|
||||
if ( $this->baseMetric->getStatsdDataFactory() !== null ) {
|
||||
$this->statsdNamespace = $statsdNamespace;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function fresh(): TimingMetric {
|
||||
$this->baseMetric->clearLabels();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ declare( strict_types=1 );
|
|||
|
||||
namespace Wikimedia\Stats;
|
||||
|
||||
use IBufferingStatsdDataFactory;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use TypeError;
|
||||
|
|
@ -63,6 +64,9 @@ class StatsFactory {
|
|||
/** @var LoggerInterface */
|
||||
private LoggerInterface $logger;
|
||||
|
||||
/** @var IBufferingStatsdDataFactory|null */
|
||||
private ?IBufferingStatsdDataFactory $statsdDataFactory = null;
|
||||
|
||||
/**
|
||||
* StatsFactory builds, configures, and caches Metrics.
|
||||
*
|
||||
|
|
@ -113,6 +117,11 @@ class StatsFactory {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withStatsdDataFactory( IBufferingStatsdDataFactory $statsdDataFactory ): StatsFactory {
|
||||
$this->statsdDataFactory = $statsdDataFactory;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a new CounterMetric or fetches one from cache.
|
||||
*
|
||||
|
|
@ -179,7 +188,9 @@ class StatsFactory {
|
|||
if ( $metric === null ) {
|
||||
$baseMetric = new BaseMetric( $this->component, $name );
|
||||
$metric = new $className(
|
||||
$baseMetric->withStaticLabels( $this->staticLabelKeys, $this->staticLabelValues ),
|
||||
$baseMetric
|
||||
->withStatsdDataFactory( $this->statsdDataFactory )
|
||||
->withStaticLabels( $this->staticLabelKeys, $this->staticLabelValues ),
|
||||
$this->logger
|
||||
);
|
||||
$this->cache->set( $this->component, $name, $metric );
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Wikimedia\Tests\Stats;
|
||||
|
||||
use IBufferingStatsdDataFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use UDPTransport;
|
||||
|
|
@ -19,6 +20,10 @@ use Wikimedia\Stats\StatsFactory;
|
|||
class StatsEmitterTest extends TestCase {
|
||||
|
||||
public function testSend() {
|
||||
// set up a mock statsd data factory
|
||||
$statsd = $this->createMock( IBufferingStatsdDataFactory::class );
|
||||
$statsd->expects( $this->exactly( 1 ) )->method( "updateCount" );
|
||||
|
||||
// initialize cache
|
||||
$cache = new StatsCache();
|
||||
|
||||
|
|
@ -40,7 +45,11 @@ class StatsEmitterTest extends TestCase {
|
|||
// initialize metrics factory
|
||||
$m = new StatsFactory( 'test', $cache, $emitter, new NullLogger );
|
||||
|
||||
$m->getCounter( 'bar' )->increment();
|
||||
// inject statsd factory
|
||||
$m->withStatsdDataFactory( $statsd );
|
||||
|
||||
// populate metric with statsd copy
|
||||
$m->getCounter( 'bar' )->copyToStatsdAt( 'test.metric' )->increment();
|
||||
|
||||
// fetch same metric from cache and use it
|
||||
$metric = $m->getCounter( 'bar' );
|
||||
|
|
|
|||
Loading…
Reference in a new issue