component = StatsUtils::normalizeString( $component ); } $this->cache = $cache; $this->emitter = $emitter; $this->logger = $logger; } /** * Returns a new StatsFactory instance prefixed by component. * * @param string $component * @return StatsFactory */ public function withComponent( string $component ): StatsFactory { $statsFactory = new StatsFactory( $this->cache, $this->emitter, $this->logger, $component ); return $statsFactory->withStatsdDataFactory( $this->statsdDataFactory ); } public function withStatsdDataFactory( ?IBufferingStatsdDataFactory $statsdDataFactory ): StatsFactory { $this->statsdDataFactory = $statsdDataFactory; return $this; } /** * Makes a new CounterMetric or fetches one from cache. * * If a collision occurs, returns a NullMetric to suppress exceptions. * * @param string $name * @return CounterMetric|NullMetric */ public function getCounter( string $name ) { return $this->getMetric( $name, CounterMetric::class ); } /** * Makes a new GaugeMetric or fetches one from cache. * * If a collision occurs, returns a NullMetric to suppress exceptions. * * @param string $name * @return GaugeMetric|NullMetric */ public function getGauge( string $name ) { return $this->getMetric( $name, GaugeMetric::class ); } /** * Makes a new TimingMetric or fetches one from cache. * * If a collision occurs, returns a NullMetric to suppress exceptions. * * @param string $name * @return TimingMetric|NullMetric */ public function getTiming( string $name ) { return $this->getMetric( $name, TimingMetric::class ); } /** * Send all buffered metrics to the target and destroy the cache. */ public function flush(): void { $this->trackUsage(); $this->emitter->send(); $this->cache->clear(); } /** * Get a total of the number of samples in cache. */ public function getCacheCount(): int { $accumulator = 0; foreach ( $this->cache->getAllMetrics() as $metric ) { $accumulator += $metric->getSampleCount(); } return $accumulator; } /** * Create a metric totaling all samples in the cache. */ private function trackUsage(): void { $this->getCounter( 'stats_buffered_total' ) ->copyToStatsdAt( 'stats.statslib.buffered' ) ->incrementBy( $this->getCacheCount() ); } /** * Fetches a metric from cache or makes a new metric. * * If a metric name collision occurs, returns a NullMetric to suppress runtime exceptions. * * @param string $name * @param string $className * @return CounterMetric|TimingMetric|GaugeMetric|NullMetric */ private function getMetric( string $name, string $className ) { $name = StatsUtils::normalizeString( $name ); StatsUtils::validateMetricName( $name ); try { $metric = $this->cache->get( $this->component, $name, $className ); } catch ( TypeError | InvalidArgumentException | InvalidConfigurationException $ex ) { // Log the condition and give the caller something that will absorb calls. trigger_error( $ex->getMessage(), E_USER_WARNING ); return new NullMetric; } if ( $metric === null ) { $baseMetric = new BaseMetric( $this->component, $name ); $metric = new $className( $baseMetric->withStatsdDataFactory( $this->statsdDataFactory ), $this->logger ); $this->cache->set( $this->component, $name, $metric ); } return $metric->fresh(); } /** * Returns an instance of StatsFactory as a NULL value object * as a default for consumer code to fall back to. This can also * be used in tests environment where we don't need the full * UDP emitter object. * * @since 1.42 * * @return self */ public static function newNull(): self { return new self( new StatsCache(), new NullEmitter(), new NullLogger() ); } /** * Returns an instance of UnitTestingHelper. * * Example: * ```php * $unitTestingHelper = StatsFactory::newUnitTestingHelper(); * $statsFactory = $unitTestingHelper->getStatsFactory() * MyClass( $statsFactory )->execute(); * $this->assertEquals( 1, $unitTestingHelper->count( 'example_executions_total{fooLabel="bar"}' ) ); * ``` * * @since 1.44 * @return UnitTestingHelper */ public static function newUnitTestingHelper() { return new UnitTestingHelper(); } }