-
-
Notifications
You must be signed in to change notification settings - Fork 189
/
Copy pathConsoleIntegration.php
129 lines (106 loc) · 4.23 KB
/
ConsoleIntegration.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<?php
namespace Sentry\Laravel\Features;
use Illuminate\Console\Application as ConsoleApplication;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Console\Events as ConsoleEvents;
use Sentry\Breadcrumb;
use Sentry\Laravel\Features\Concerns\TracksPushedScopesAndSpans;
use Sentry\Laravel\Integration;
use Sentry\SentrySdk;
use Sentry\State\Scope;
use Sentry\Tracing\SpanStatus;
use Sentry\Tracing\TransactionContext;
use Sentry\Tracing\TransactionSource;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
class ConsoleIntegration extends Feature
{
use TracksPushedScopesAndSpans;
private const FEATURE_KEY = 'command_info';
public function isApplicable(): bool
{
return true;
}
public function onBoot(Dispatcher $events): void
{
ConsoleApplication::starting(static function (ConsoleApplication $console) {
$console->getDefinition()->addOption(
new InputOption('--sentry-trace', null, InputOption::VALUE_OPTIONAL, 'Trace the execution of this command using the Sentry SDK')
);
});
$events->listen(ConsoleEvents\CommandStarting::class, [$this, 'commandStarting']);
$events->listen(ConsoleEvents\CommandFinished::class, [$this, 'commandFinished']);
}
public function commandStarting(ConsoleEvents\CommandStarting $event): void
{
if (!$event->command) {
return;
}
$shouldTrace = $event->input->hasOption('sentry-trace') && $event->input->getParameterOption('--sentry-trace') !== false;
// If `--sentry-trace` is passed, we start a new transaction and optionally take the operation name from the option value
if ($shouldTrace) {
$sentryTraceOp = $event->input->getOption('sentry-trace');
$context = TransactionContext::make()
->setName($event->command)
->setSource(TransactionSource::task())
->setOp($sentryTraceOp ?? 'console.command')
->setStartTimestamp(microtime(true));
$transaction = SentrySdk::getCurrentHub()->startTransaction($context);
$this->pushSpan($transaction);
}
Integration::configureScope(static function (Scope $scope) use ($event): void {
$scope->setTag('command', $event->command);
});
if ($this->isBreadcrumbFeatureEnabled(self::FEATURE_KEY)) {
Integration::addBreadcrumb(new Breadcrumb(
Breadcrumb::LEVEL_INFO,
Breadcrumb::TYPE_DEFAULT,
'artisan.command',
'Starting Artisan command: ' . $event->command,
[
'input' => $this->extractConsoleCommandInput($event->input),
]
));
}
}
public function commandFinished(ConsoleEvents\CommandFinished $event): void
{
if ($this->isBreadcrumbFeatureEnabled(self::FEATURE_KEY)) {
Integration::addBreadcrumb(new Breadcrumb(
Breadcrumb::LEVEL_INFO,
Breadcrumb::TYPE_DEFAULT,
'artisan.command',
'Finished Artisan command: ' . $event->command,
[
'exit' => $event->exitCode,
'input' => $this->extractConsoleCommandInput($event->input),
]
));
}
$span = $this->maybePopSpan();
if ($span) {
$span->finish();
$span->setStatus($event->exitCode === 0 ? SpanStatus::ok() : SpanStatus::internalError());
}
// Flush any and all events that were possibly generated by the command
Integration::flushEvents();
Integration::configureScope(static function (Scope $scope): void {
$scope->removeTag('command');
});
}
/**
* Extract the command input arguments if possible.
*
* @param \Symfony\Component\Console\Input\InputInterface|null $input
*
* @return string|null
*/
private function extractConsoleCommandInput(?InputInterface $input): ?string
{
if ($input instanceof ArgvInput) {
return (string)$input;
}
return null;
}
}