Passed
Pull Request — master (#19)
by
unknown
04:36 queued 02:12
created

SentryTraceConsoleListener::addAppBootstrapSpan()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 17
nc 6
nop 0
dl 0
loc 31
ccs 0
cts 18
cp 0
crap 30
rs 9.3888
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Sentry\Tracing;
6
7
use Sentry\SentrySdk;
8
use Sentry\State\HubInterface;
9
use Sentry\Tracing\Span;
10
use Sentry\Tracing\SpanContext;
11
use Sentry\Tracing\Transaction;
12
use Sentry\Tracing\TransactionContext;
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Event\ConsoleCommandEvent;
15
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Yiisoft\Yii\Console\Event\ApplicationShutdown;
18
19
final class SentryTraceConsoleListener
20
{
21
    /**
22
     * The current active transaction.
23
     *
24
     * @psalm-suppress PropertyNotSetInConstructor
25
     *
26
     * @var Transaction|null
27
     */
28
    protected ?Transaction $transaction;
29
    /**
30
     * The span for the `app.handle` part of the application.
31
     *
32
     * @psalm-suppress PropertyNotSetInConstructor
33
     *
34
     * @var Span|null
35
     */
36
    protected ?Span $appSpan;
37
    /**
38
     * The span for the `app.handle` part of the application.
39
     *
40
     * @psalm-suppress PropertyNotSetInConstructor
41
     *
42
     * @var Span|null
43
     */
44
    protected ?Span $bootSpan;
45
    /**
46
     * The timestamp of application bootstrap completion.
47
     *
48
     * @var float|null
49
     */
50
    private ?float $bootedTimestamp;
51
52
    public function __construct(
53
        private HubInterface $hub,
54
    ) {
55
        $this->bootedTimestamp = microtime(true);
0 ignored issues
show
Documentation Bug introduced by
It seems like microtime(true) can also be of type string. However, the property $bootedTimestamp is declared as type double|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
56
    }
57
58
    /**
59
     * @return Transaction|null
60
     */
61
    public function getTransaction(): ?Transaction
62
    {
63
        return $this->transaction;
64
    }
65
66
    /**
67
     * @param Transaction|null $transaction
68
     *
69
     * @return self
70
     */
71
    public function setTransaction(?Transaction $transaction): self
72
    {
73
        $this->transaction = $transaction;
74
75
        return $this;
76
    }
77
78
    public function listenAppStart(): void
79
    {
80
        $this->startTransaction();
81
    }
82
83
    private function startTransaction(): void
84
    {
85
        $requestStartTime = (defined('APP_START_TIME')
86
            ? (float)APP_START_TIME
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Yii\Sentry\Tracing\APP_START_TIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
87
            : microtime(true));
88
        $context = new TransactionContext();
89
90
        $context->setOp('console');
91
92
        $context->setStartTimestamp($requestStartTime);
0 ignored issues
show
Bug introduced by
It seems like $requestStartTime can also be of type string; however, parameter $startTimestamp of Sentry\Tracing\SpanContext::setStartTimestamp() does only seem to accept double|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

92
        $context->setStartTimestamp(/** @scrutinizer ignore-type */ $requestStartTime);
Loading history...
93
94
        $this->transaction = $this->hub->startTransaction($context);
95
        $this->transaction->setName('undefined command');
96
        // Setting the Transaction on the Hub
97
        SentrySdk::getCurrentHub()->setSpan($this->transaction);
98
99
        $this->addAppBootstrapSpan();
100
    }
101
102
    private function addAppBootstrapSpan(): void
103
    {
104
        if ($this->bootedTimestamp === null) {
105
            return;
106
        }
107
        if (null === $this->transaction) {
108
            return;
109
        }
110
111
        $appStartTime = defined('APP_START_TIME')
112
            ? (float)APP_START_TIME
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Yii\Sentry\Tracing\APP_START_TIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
113
            : null;
114
115
        if ($appStartTime === null) {
116
            return;
117
        }
118
119
        $spanContextStart = new SpanContext();
120
        $spanContextStart->setOp('app.bootstrap');
121
        $spanContextStart->setStartTimestamp($appStartTime);
122
        $spanContextStart->setEndTimestamp($this->bootedTimestamp);
123
124
        $span = $this->transaction->startChild($spanContextStart);
125
126
        // Consume the booted timestamp, because we don't want to report the bootstrap span more than once
127
        $this->bootedTimestamp = null;
128
129
        // Add more information about the bootstrap section if possible
130
        $this->addBootDetailTimeSpans($span);
131
132
        $this->bootSpan = $span;
133
    }
134
135
    private function addBootDetailTimeSpans(Span $bootstrap): void
136
    {
137
        if (
138
            !defined('SENTRY_AUTOLOAD')
139
            || !SENTRY_AUTOLOAD
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Yii\Sentry\Tracing\SENTRY_AUTOLOAD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
140
            || !is_numeric(SENTRY_AUTOLOAD)
141
        ) {
142
            return;
143
        }
144
145
        $autoload = new SpanContext();
146
        $autoload->setOp('autoload');
147
        $autoload->setStartTimestamp($bootstrap->getStartTimestamp());
148
        $autoload->setEndTimestamp((float)SENTRY_AUTOLOAD);
149
150
        $bootstrap->startChild($autoload);
151
    }
152
153
    public function listenBeginCommand(ConsoleCommandEvent $event): void
154
    {
155
        $command = $event->getCommand();
156
        $input = $event->getInput();
157
        $this->startCommand($command, $input);
158
    }
159
160
    private function startCommand(?Command $command, InputInterface $input): void
161
    {
162
        if (null === $this->transaction) {
163
            return;
164
        }
165
        $name = $command?->getName() ?? 'undefined command';
166
        $inputArgs = [
167
            'arguments' => $input->getArguments(),
168
            'options' => $input->getOptions(),
169
        ];
170
        $this->transaction->setData([
171
            'name' => $name,
172
            'input' => $inputArgs,
173
        ]);
174
        $this->transaction->setName($name);
175
176
        $bootstrapSpan = $this->bootSpan;
177
178
        $appContextStart = new SpanContext();
179
        $appContextStart->setOp('app.handle');
180
        $appContextStart->setStartTimestamp(
181
            $bootstrapSpan
0 ignored issues
show
Bug introduced by
It seems like $bootstrapSpan ? $bootst...amp() : microtime(true) can also be of type string; however, parameter $startTimestamp of Sentry\Tracing\SpanContext::setStartTimestamp() does only seem to accept double|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

181
            /** @scrutinizer ignore-type */ $bootstrapSpan
Loading history...
182
                ? $bootstrapSpan->getEndTimestamp()
183
                : microtime(true)
184
        );
185
186
        $this->appSpan = $this->transaction->startChild($appContextStart);
187
188
        SentrySdk::getCurrentHub()->setSpan($this->appSpan);
189
    }
190
191
    public function listenShutdown(ApplicationShutdown $event): void
192
    {
193
        $exitCode = $event->getExitCode();
194
        $this->terminate($exitCode);
195
    }
196
197
    public function terminate(int $exitCode): void
198
    {
199
        if ($this->transaction !== null) {
200
            $this->appSpan?->finish();
201
            $this->appSpan = null;
202
203
            $this->transaction->setTags(['exitCode' => (string)$exitCode]);
204
            // Make sure we set the transaction and not have a child span in the Sentry SDK
205
            // If the transaction is not on the scope during finish, the trace.context is wrong
206
            SentrySdk::getCurrentHub()->setSpan($this->transaction);
207
208
            $this->transaction->finish();
209
            $this->transaction = null;
210
        }
211
    }
212
213
    public function listenCommandTerminate(?ConsoleTerminateEvent $terminateEvent): void
0 ignored issues
show
Unused Code introduced by
The parameter $terminateEvent is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

213
    public function listenCommandTerminate(/** @scrutinizer ignore-unused */ ?ConsoleTerminateEvent $terminateEvent): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
214
    {
215
        $this->appSpan?->finish(microtime(true));
0 ignored issues
show
Bug introduced by
It seems like microtime(true) can also be of type string; however, parameter $endTimestamp of Sentry\Tracing\Span::finish() does only seem to accept double|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

215
        $this->appSpan?->finish(/** @scrutinizer ignore-type */ microtime(true));
Loading history...
216
    }
217
218
    /**
219
     * @return Span|null
220
     */
221
    public function getAppSpan(): ?Span
222
    {
223
        return $this->appSpan;
224
    }
225
226
    /**
227
     * @param Span|null $appSpan
228
     */
229
    public function setAppSpan(?Span $appSpan): void
230
    {
231
        $this->appSpan = $appSpan;
232
    }
233
}
234