$this->doPostOutputShutdown( 'normal' );
}
+ /**
+ * @see MediaWiki::preOutputCommit()
+ * @since 1.26
+ */
+ public function doPreOutputCommit() {
+ self::preOutputCommit( $this->context );
+ }
+
/**
* This function commits all DB changes as needed before
* the user can receive a response (in case commit fails)
*
- * @since 1.26
+ * @param IContextSource $context
+ * @since 1.27
*/
- public function doPreOutputCommit() {
+ public static function preOutputCommit( IContextSource $context ) {
// Either all DBs should commit or none
ignore_user_abort( true );
// Set a cookie to tell all CDN edge nodes to "stick" the user to the
// DC that handles this POST request (e.g. the "master" data center)
- $request = $this->context->getRequest();
+ $request = $context->getRequest();
+ $config = $context->getConfig();
if ( $request->wasPosted() && $factory->hasOrMadeRecentMasterChanges() ) {
- $expires = time() + $this->config->get( 'DataCenterUpdateStickTTL' );
- $request->response()->setCookie( 'UseDC', 'master', $expires );
+ $expires = time() + $config->get( 'DataCenterUpdateStickTTL' );
+ $request->response()->setCookie( 'UseDC', 'master', $expires, array( 'prefix' => '' ) );
}
// Avoid letting a few seconds of slave lag cause a month of stale data
if ( $factory->laggedSlaveUsed() ) {
- $maxAge = $this->config->get( 'CdnMaxageLagged' );
- $this->context->getOutput()->lowerCdnMaxage( $maxAge );
+ $maxAge = $config->get( 'CdnMaxageLagged' );
+ $context->getOutput()->lowerCdnMaxage( $maxAge );
$request->response()->header( "X-Database-Lagged: true" );
wfDebugLog( 'replication', "Lagged DB used; CDN cache TTL limited to $maxAge seconds" );
}
// Commit and close up!
$factory = wfGetLBFactory();
$factory->commitMasterChanges();
- $factory->shutdown();
+ $factory->shutdown( LBFactory::SHUTDOWN_NO_CHRONPROT );
wfDebug( "Request ended normally\n" );
}
'phpfm' => 'ApiFormatPhp',
'xml' => 'ApiFormatXml',
'xmlfm' => 'ApiFormatXml',
- 'yaml' => 'ApiFormatYaml',
- 'yamlfm' => 'ApiFormatYaml',
'rawfm' => 'ApiFormatJson',
- 'txt' => 'ApiFormatTxt',
- 'txtfm' => 'ApiFormatTxt',
- 'dbg' => 'ApiFormatDbg',
- 'dbgfm' => 'ApiFormatDbg',
'none' => 'ApiFormatNone',
);
// Log the request whether or not there was an error
$this->logRequest( microtime( true ) - $t );
+ // Commit DBs and send any related cookies and headers
+ MediaWiki::preOutputCommit( $this->getContext() );
+
// Send cache headers after any code which might generate an error, to
// avoid sending public cache headers for errors.
$this->sendCacheHeaders( $isError );
/**
* Log the preceding request
- * @param int $time Time in seconds
+ * @param float $time Time in seconds
*/
protected function logRequest( $time ) {
$request = $this->getRequest();
- $milliseconds = $time === null ? '?' : round( $time * 1000 );
- $s = 'API' .
- ' ' . $request->getMethod() .
- ' ' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) .
- ' ' . $request->getIP() .
- ' T=' . $milliseconds . 'ms';
+ $logCtx = array(
+ 'dt' => date( 'c' ),
+ 'client_ip' => $request->getIP(),
+ 'user_agent' => $this->getUserAgent(),
+ 'wiki' => wfWikiId(),
+ 'time_backend_ms' => round( $time * 1000 ),
+ 'params' => array(),
+ );
+
+ // Construct space separated message for 'api' log channel
+ $msg = "API {$request->getMethod()} " .
+ wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) .
+ " {$logCtx['client_ip']} " .
+ "T={$logCtx['time_backend_ms']}ms";
+
foreach ( $this->getParamsUsed() as $name ) {
$value = $request->getVal( $name );
if ( $value === null ) {
continue;
}
- $s .= ' ' . $name . '=';
+
if ( strlen( $value ) > 256 ) {
- $encValue = $this->encodeRequestLogValue( substr( $value, 0, 256 ) );
- $s .= $encValue . '[...]';
+ $value = substr( $value, 0, 256 );
+ $encValue = $this->encodeRequestLogValue( $value ) . '[...]';
} else {
- $s .= $this->encodeRequestLogValue( $value );
+ $encValue = $this->encodeRequestLogValue( $value );
}
+
+ $logCtx['params'][$name] = $value;
+ $msg .= " {$name}={$encValue}";
}
- $s .= "\n";
- wfDebugLog( 'api', $s, 'private' );
+
+ wfDebugLog( 'api', $msg, 'private' );
+ // ApiRequest channel is for structured data consumers
+ wfDebugLog( 'ApiRequest', '', 'private', $logCtx );
}
/**