MediaWiki  master
Profiler.php
Go to the documentation of this file.
1 <?php
22 use Psr\Log\LoggerInterface;
24 use Wikimedia\ScopedCallback;
25 
36 abstract class Profiler {
38  protected $profileID = false;
40  protected $params = [];
42  protected $context = null;
44  protected $trxProfiler;
46  protected $logger;
48  private $allowOutput = false;
49 
51  private static $instance = null;
52 
56  public function __construct( array $params ) {
57  if ( isset( $params['profileID'] ) ) {
58  $this->profileID = $params['profileID'];
59  }
60  $this->params = $params;
61  $this->trxProfiler = new TransactionProfiler();
62  $this->logger = LoggerFactory::getInstance( 'profiler' );
63  }
64 
69  final public static function init( array $profilerConf ): void {
70  $params = $profilerConf + [
71  'class' => ProfilerStub::class,
72  'sampling' => 1,
73  'threshold' => 0.0,
74  'output' => [],
75  'cliEnable' => false,
76  ];
77 
78  // Avoid global func wfIsCLI() during setup
79  $isCLI = ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
80  $inSample = $params['sampling'] === 1 ? true : ( mt_rand( 0, $params['sampling'] - 1 ) === 0 );
81  if (
82  !$inSample ||
83  // On CLI, profiling is disabled by default, and can be explicitly enabled
84  // via the `--profiler` option, which MediaWiki\Maintenance\MaintenanceRunner::setup
85  // translates into 'cliEnable'.
86  // See also $wgProfiler docs.
87  //
88  // For this to work, Setup.php must call Profiler::init() after handling of
89  // MW_FINAL_SETUP_CALLBACK, which is what doMaintenance.php uses to call
90  // MaintenanceRunner::setup.
91  ( $isCLI && !$params['cliEnable'] )
92  ) {
93  $params['class'] = ProfilerStub::class;
94  }
95 
96  if ( !is_array( $params['output'] ) ) {
97  $params['output'] = [ $params['output'] ];
98  }
99 
100  self::$instance = new $params['class']( $params );
101  }
102 
107  final public static function instance() {
108  if ( !self::$instance ) {
109  trigger_error( 'Called Profiler::instance before settings are loaded', E_USER_WARNING );
110  self::init( [] );
111  }
112 
113  return self::$instance;
114  }
115 
119  public function setProfileID( $id ) {
120  $this->profileID = $id;
121  }
122 
126  public function getProfileID() {
127  if ( $this->profileID === false ) {
128  return WikiMap::getCurrentWikiDbDomain()->getId();
129  } else {
130  return $this->profileID;
131  }
132  }
133 
139  public function setContext( $context ) {
140  wfDeprecated( __METHOD__, '1.38' );
141  $this->context = $context;
142  }
143 
149  public function getContext() {
150  wfDeprecated( __METHOD__, '1.38' );
151  return $this->context ?? RequestContext::getMain();
152  }
153 
162  abstract public function scopedProfileIn( $section );
163 
167  public function scopedProfileOut( SectionProfileCallback &$section = null ) {
168  $section = null;
169  }
170 
175  public function getTransactionProfiler() {
176  return $this->trxProfiler;
177  }
178 
182  abstract public function close();
183 
191  private function getOutputs() {
192  $outputs = [];
193  foreach ( $this->params['output'] as $outputType ) {
194  // The class may be specified as either the full class name (for
195  // example, 'ProfilerOutputStats') or (for backward compatibility)
196  // the trailing portion of the class name (for example, 'stats').
197  $outputClass = strpos( $outputType, 'ProfilerOutput' ) === false
198  ? 'ProfilerOutput' . ucfirst( $outputType )
199  : $outputType;
200  if ( !class_exists( $outputClass ) ) {
201  throw new MWException( "'$outputType' is an invalid output type" );
202  }
203  $outputInstance = new $outputClass( $this, $this->params );
204  if ( $outputInstance->canUse() ) {
205  $outputs[] = $outputInstance;
206  }
207  }
208  return $outputs;
209  }
210 
219  public function logData() {
220  if ( $this->params['threshold'] > 0.0 ) {
221  // Note, this is also valid for CLI processes.
222  $timeElapsed = microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT'];
223  if ( $timeElapsed <= $this->params['threshold'] ) {
224  return;
225  }
226  }
227 
228  $outputs = [];
229  foreach ( $this->getOutputs() as $output ) {
230  if ( !$output->logsToOutput() ) {
231  $outputs[] = $output;
232  }
233  }
234 
235  if ( $outputs ) {
236  $stats = $this->getFunctionStats();
237  foreach ( $outputs as $output ) {
238  $output->log( $stats );
239  }
240  }
241  }
242 
249  public function logDataPageOutputOnly() {
250  if ( !$this->allowOutput ) {
251  return;
252  }
253 
254  $outputs = [];
255  foreach ( $this->getOutputs() as $output ) {
256  if ( $output->logsToOutput() ) {
257  $outputs[] = $output;
258  }
259  }
260 
261  if ( $outputs ) {
262  $stats = $this->getFunctionStats();
263  foreach ( $outputs as $output ) {
264  $output->log( $stats );
265  }
266  }
267  }
268 
278  public function getContentType() {
279  if ( $this->allowOutput ) {
280  foreach ( headers_list() as $header ) {
281  if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
282  return $m[1];
283  }
284  }
285  }
286  return null;
287  }
288 
294  public function setAllowOutput() {
295  $this->allowOutput = true;
296  }
297 
304  public function getAllowOutput() {
305  return $this->allowOutput;
306  }
307 
334  abstract public function getFunctionStats();
335 
341  abstract public function getOutput();
342 }
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
MediaWiki exception.
Definition: MWException.php:30
PSR-3 logger instance factory.
Stub profiler that does nothing.
Profiler base class that defines the interface and some shared functionality.
Definition: Profiler.php:36
setAllowOutput()
Enable appending profiles to standard output.
Definition: Profiler.php:294
setProfileID( $id)
Definition: Profiler.php:119
getTransactionProfiler()
Definition: Profiler.php:175
setContext( $context)
Definition: Profiler.php:139
IContextSource $context
Current request context.
Definition: Profiler.php:42
close()
Close opened profiling sections.
logData()
Log data to all the applicable backing stores.
Definition: Profiler.php:219
TransactionProfiler $trxProfiler
Definition: Profiler.php:44
__construct(array $params)
Definition: Profiler.php:56
string false $profileID
Profiler ID for bucketing data.
Definition: Profiler.php:38
logDataPageOutputOnly()
Log the data to the script/request output for all ProfilerOutput instances that do so.
Definition: Profiler.php:249
getOutput()
Returns a profiling output to be stored in debug file.
static init(array $profilerConf)
Definition: Profiler.php:69
static instance()
Singleton.
Definition: Profiler.php:107
getContentType()
Get the Content-Type for deciding how to format appended profile output.
Definition: Profiler.php:278
getFunctionStats()
Get the aggregated inclusive profiling data for each method.
getProfileID()
Definition: Profiler.php:126
scopedProfileIn( $section)
Mark the start of a custom profiling frame (e.g.
getAllowOutput()
Whether appending profiles is allowed.
Definition: Profiler.php:304
scopedProfileOut(SectionProfileCallback &$section=null)
Definition: Profiler.php:167
LoggerInterface $logger
Definition: Profiler.php:46
array $params
All of the params passed from $wgProfiler.
Definition: Profiler.php:40
getContext()
Definition: Profiler.php:149
static getMain()
Get the RequestContext object associated with the main request.
Subclass ScopedCallback to avoid call_user_func_array(), which is slow.
static getCurrentWikiDbDomain()
Definition: WikiMap.php:290
Detect high-contention DB queries via profiling calls.
return true
Definition: router.php:90
$header