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