Vendor
This commit is contained in:
+167
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Tracing;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use Nuwave\Lighthouse\Events\StartRequest;
|
||||
use Nuwave\Lighthouse\Events\ManipulateAST;
|
||||
use Nuwave\Lighthouse\Schema\AST\ASTHelper;
|
||||
use Nuwave\Lighthouse\Events\StartExecution;
|
||||
use Nuwave\Lighthouse\Schema\AST\PartialParser;
|
||||
use Nuwave\Lighthouse\Execution\ExtensionsResponse;
|
||||
use Nuwave\Lighthouse\Events\BuildExtensionsResponse;
|
||||
|
||||
class Tracing
|
||||
{
|
||||
/**
|
||||
* The timestamp the request was initially started.
|
||||
*
|
||||
* @var \Carbon\Carbon
|
||||
*/
|
||||
protected $requestStart;
|
||||
|
||||
/**
|
||||
* The precise point in time where the request was initially started.
|
||||
*
|
||||
* This is either in seconds with microsecond precision (float) or nanoseconds (int).
|
||||
*
|
||||
* @var float|int
|
||||
*/
|
||||
protected $requestStartPrecise;
|
||||
|
||||
/**
|
||||
* Trace entries for a single query execution.
|
||||
*
|
||||
* Is reset between batches.
|
||||
*
|
||||
* @var array[]
|
||||
*/
|
||||
protected $resolverTraces = [];
|
||||
|
||||
/**
|
||||
* Set the tracing directive on all fields of the query to enable tracing them.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Events\ManipulateAST $manipulateAST
|
||||
* @return void
|
||||
*/
|
||||
public function handleManipulateAST(ManipulateAST $manipulateAST): void
|
||||
{
|
||||
ASTHelper::attachDirectiveToObjectTypeFields(
|
||||
$manipulateAST->documentAST,
|
||||
PartialParser::directive('@tracing')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle request start.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Events\StartRequest $startRequest
|
||||
* @return void
|
||||
*/
|
||||
public function handleStartRequest(StartRequest $startRequest): void
|
||||
{
|
||||
$this->requestStart = Carbon::now();
|
||||
$this->requestStartPrecise = $this->getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle batch request start.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Events\StartExecution $startExecution
|
||||
* @return void
|
||||
*/
|
||||
public function handleStartExecution(StartExecution $startExecution): void
|
||||
{
|
||||
$this->resolverTraces = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return additional information for the result.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Events\BuildExtensionsResponse $buildExtensionsResponse
|
||||
* @return \Nuwave\Lighthouse\Execution\ExtensionsResponse
|
||||
*/
|
||||
public function handleBuildExtensionsResponse(BuildExtensionsResponse $buildExtensionsResponse): ExtensionsResponse
|
||||
{
|
||||
$requestEnd = Carbon::now();
|
||||
$requestEndPrecise = $this->getTime();
|
||||
|
||||
return new ExtensionsResponse(
|
||||
'tracing',
|
||||
[
|
||||
'version' => 1,
|
||||
'startTime' => $this->requestStart->format(Carbon::RFC3339_EXTENDED),
|
||||
'endTime' => $requestEnd->format(Carbon::RFC3339_EXTENDED),
|
||||
'duration' => $this->diffTimeInNanoseconds($this->requestStartPrecise, $requestEndPrecise),
|
||||
'execution' => [
|
||||
'resolvers' => $this->resolverTraces,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record resolver execution time.
|
||||
*
|
||||
* @param \GraphQL\Type\Definition\ResolveInfo $resolveInfo
|
||||
* @param float|int $start
|
||||
* @param float|int $end
|
||||
* @return void
|
||||
*/
|
||||
public function record(ResolveInfo $resolveInfo, $start, $end): void
|
||||
{
|
||||
$this->resolverTraces [] = [
|
||||
'path' => $resolveInfo->path,
|
||||
'parentType' => $resolveInfo->parentType->name,
|
||||
'returnType' => $resolveInfo->returnType->__toString(),
|
||||
'fieldName' => $resolveInfo->fieldName,
|
||||
'startOffset' => $this->diffTimeInNanoseconds($this->requestStartPrecise, $start),
|
||||
'duration' => $this->diffTimeInNanoseconds($start, $end),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the system's highest resolution of time possible.
|
||||
*
|
||||
* This is either in seconds with microsecond precision (float) or nanoseconds (int).
|
||||
*
|
||||
* @return float|int
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
return $this->platformSupportsNanoseconds()
|
||||
? hrtime(true)
|
||||
: microtime(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff the time results to each other and convert to nanoseconds if needed.
|
||||
*
|
||||
* @param float|int $start
|
||||
* @param float|int $end
|
||||
* @return int
|
||||
*/
|
||||
protected function diffTimeInNanoseconds($start, $end): int
|
||||
{
|
||||
if ($this->platformSupportsNanoseconds()) {
|
||||
return $end - $start;
|
||||
}
|
||||
|
||||
// Difference is in seconds (with microsecond precision)
|
||||
// * 1000 to get to milliseconds
|
||||
// * 1000 to get to microseconds
|
||||
// * 1000 to get to nanoseconds
|
||||
return (int) (($end - $start) * 1000 * 1000 * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the current PHP version has the `hrtime` function available to get a nanosecond precision point in time.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function platformSupportsNanoseconds(): bool
|
||||
{
|
||||
return function_exists('hrtime');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Tracing;
|
||||
|
||||
use Closure;
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use Nuwave\Lighthouse\Schema\Values\FieldValue;
|
||||
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
|
||||
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
|
||||
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
|
||||
use Nuwave\Lighthouse\Support\Contracts\DefinedDirective;
|
||||
|
||||
class TracingDirective extends BaseDirective implements FieldMiddleware, DefinedDirective
|
||||
{
|
||||
const NAME = 'tracing';
|
||||
|
||||
/**
|
||||
* @var \Nuwave\Lighthouse\Tracing\Tracing
|
||||
*/
|
||||
protected $tracing;
|
||||
|
||||
/**
|
||||
* TracingDirective constructor.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Tracing\Tracing $tracing
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Tracing $tracing)
|
||||
{
|
||||
$this->tracing = $tracing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of the directive.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
public static function definition(): string
|
||||
{
|
||||
return /* @lang GraphQL */ <<<'SDL'
|
||||
"""
|
||||
Do not use this directive directly, it is automatically added to the schema
|
||||
when using the tracing extension.
|
||||
"""
|
||||
directive @tracing on FIELD_DEFINITION
|
||||
SDL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the field directive.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\Values\FieldValue $fieldValue
|
||||
* @param \Closure $next
|
||||
* @return \Nuwave\Lighthouse\Schema\Values\FieldValue
|
||||
*/
|
||||
public function handleField(FieldValue $fieldValue, Closure $next): FieldValue
|
||||
{
|
||||
$fieldValue = $next($fieldValue);
|
||||
|
||||
$resolver = $fieldValue->getResolver();
|
||||
|
||||
return $fieldValue->setResolver(function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($resolver) {
|
||||
$start = $this->tracing->getTime();
|
||||
|
||||
$result = $resolver($root, $args, $context, $resolveInfo);
|
||||
|
||||
$end = $this->tracing->getTime();
|
||||
|
||||
if ($result instanceof Deferred) {
|
||||
$result->then(function () use ($resolveInfo, $start, $end): void {
|
||||
$this->tracing->record($resolveInfo, $start, $end);
|
||||
});
|
||||
} else {
|
||||
$this->tracing->record($resolveInfo, $start, $end);
|
||||
}
|
||||
|
||||
return $result;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Nuwave\Lighthouse\Tracing;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Nuwave\Lighthouse\Events\StartRequest;
|
||||
use Nuwave\Lighthouse\Events\ManipulateAST;
|
||||
use Nuwave\Lighthouse\Events\StartExecution;
|
||||
use Nuwave\Lighthouse\Events\BuildExtensionsResponse;
|
||||
use Nuwave\Lighthouse\Schema\Factories\DirectiveFactory;
|
||||
use Illuminate\Contracts\Events\Dispatcher as EventsDispatcher;
|
||||
|
||||
class TracingServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Schema\Factories\DirectiveFactory $directiveFactory
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $eventsDispatcher
|
||||
* @return void
|
||||
*/
|
||||
public function boot(DirectiveFactory $directiveFactory, EventsDispatcher $eventsDispatcher): void
|
||||
{
|
||||
$directiveFactory->addResolved(
|
||||
TracingDirective::NAME,
|
||||
TracingDirective::class
|
||||
);
|
||||
|
||||
$eventsDispatcher->listen(
|
||||
ManipulateAST::class,
|
||||
Tracing::class.'@handleManipulateAST'
|
||||
);
|
||||
|
||||
$eventsDispatcher->listen(
|
||||
StartRequest::class,
|
||||
Tracing::class.'@handleStartRequest'
|
||||
);
|
||||
|
||||
$eventsDispatcher->listen(
|
||||
StartExecution::class,
|
||||
Tracing::class.'@handleStartExecution'
|
||||
);
|
||||
|
||||
$eventsDispatcher->listen(
|
||||
BuildExtensionsResponse::class,
|
||||
Tracing::class.'@handleBuildExtensionsResponse'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->singleton(Tracing::class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user