Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
TimingMetric
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
7 / 7
10
100.00% covered (success)
100.00%
1 / 1
 start
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 stop
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 observeNanoseconds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 observeSeconds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 observe
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addSample
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getTypeIndicator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 * @file
18 */
19
20declare( strict_types=1 );
21
22namespace Wikimedia\Stats\Metrics;
23
24use Wikimedia\Stats\Exceptions\IllegalOperationException;
25use Wikimedia\Stats\Sample;
26
27/**
28 * Timing Metric Implementation
29 *
30 * Timing metrics track duration data which can be broken into histograms.
31 * They are identified by type "ms".
32 *
33 * @author Cole White
34 * @since 1.38
35 */
36class TimingMetric implements MetricInterface {
37    use MetricTrait;
38
39    /**
40     * The StatsD protocol type indicator:
41     * https://github.com/statsd/statsd/blob/v0.9.0/docs/metric_types.md
42     * https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics
43     */
44    private const TYPE_INDICATOR = "ms";
45
46    private ?float $startTime = null;
47
48    /**
49     * Start the timer.
50     *
51     * Example:
52     *
53     * ```php
54     * $timer = StatsFactory->getTiming( 'example_seconds' )
55     *     ->setLabel( 'foo', 'bar' )
56     *     ->start();
57     * # work to be measured...
58     * $timer->stop();
59     * ```
60     *
61     * @return $this
62     */
63    public function start() {
64        $this->startTime = hrtime( true );
65        return $this;
66    }
67
68    /**
69     * Stop the running timer.
70     */
71    public function stop(): void {
72        if ( $this->startTime === null ) {
73            trigger_error( "Stats: stop() called before start() for metric '{$this->getName()}'", E_USER_WARNING );
74            return;
75        }
76        $this->observeNanoseconds( hrtime( true ) - $this->startTime );
77        $this->startTime = null;
78    }
79
80    /**
81     * Record a previously calculated observation in nanoseconds.
82     *
83     * Example:
84     *
85     * ```php
86     * $startTime = hrtime( true )
87     * # work to be measured...
88     * $metric->observeNanoseconds( hrtime( true ) - $startTime )
89     * ```
90     *
91     * @param float $nanoseconds
92     * @return void
93     * @since 1.43
94     */
95    public function observeNanoseconds( float $nanoseconds ): void {
96        $this->addSample( $nanoseconds * 1e-6 );
97    }
98
99    /**
100     * Record a previously calculated observation in seconds.
101     *
102     * This method is provided for tracking externally-generated values, timestamp deltas, and
103     * situations where the expected input value is the expected Prometheus graphed value.
104     *
105     * Performance measurements in process should be done with hrtime() and observeNanoseconds()
106     * to ensure monotonic time is used and not wall-clock time.
107     *
108     * Example:
109     *
110     * ```php
111     * $startTime = microtime( true )
112     * # work to be measured...
113     * $metric->observeSeconds( microtime( true ) - $startTime )
114     * ```
115     *
116     * @param float $seconds
117     * @return void
118     * @since 1.43
119     */
120    public function observeSeconds( float $seconds ): void {
121        $this->addSample( $seconds * 1000 );
122    }
123
124    /**
125     * Record a previously calculated observation in milliseconds.
126     *
127     * NOTE: You MUST pass values converted to milliseconds.
128     *
129     * This method is discouraged in new code, because PHP does not measure
130     * time in milliseconds. It will be less error-prone if you use start()
131     * and stop(), or pass values from hrtime() directly to observeNanoseconds()
132     * without manual multiplication to another unit.
133     *
134     * @param float $milliseconds
135     * @return void
136     */
137    public function observe( float $milliseconds ): void {
138        $this->addSample( $milliseconds );
139    }
140
141    private function addSample( float $milliseconds ): void {
142        foreach ( $this->baseMetric->getStatsdNamespaces() as $namespace ) {
143            $this->baseMetric->getStatsdDataFactory()->timing( $namespace, $milliseconds );
144        }
145
146        try {
147            $this->baseMetric->addSample( new Sample( $this->baseMetric->getLabelValues(), $milliseconds ) );
148        } catch ( IllegalOperationException $ex ) {
149            // Log the condition and give the caller something that will absorb calls.
150            trigger_error( $ex->getMessage(), E_USER_WARNING );
151        }
152    }
153
154    /** @inheritDoc */
155    public function getTypeIndicator(): string {
156        return self::TYPE_INDICATOR;
157    }
158}