Passed
Pull Request — master (#19)
by
unknown
02:57
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 = null;
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 = null;
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 = null;
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
    public function getTransaction(): ?Transaction
59
    {
60
        return $this->transaction;
61
    }
62
63
    public function setTransaction(?Transaction $transaction): self
64
    {
65
        $this->transaction = $transaction;
66
67
        return $this;
68
    }
69
70
    public function listenAppStart(): void
71
    {
72
        $this->startTransaction();
73
    }
74
75
    private function startTransaction(): void
76
    {
77
        $requestStartTime = (defined('APP_START_TIME')
78
            ? (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...
79
            : microtime(true));
80
        $context = new TransactionContext();
81
82
        $context->setOp('console');
83
84
        $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

84
        $context->setStartTimestamp(/** @scrutinizer ignore-type */ $requestStartTime);
Loading history...
85
86
        $this->transaction = $this->hub->startTransaction($context);
87
        $this->transaction->setName('undefined command');
88
        // Setting the Transaction on the Hub
89
        SentrySdk::getCurrentHub()->setSpan($this->transaction);
90
91
        $this->addAppBootstrapSpan();
92
    }
93
94
    private function addAppBootstrapSpan(): void
95
    {
96
        if ($this->bootedTimestamp === null) {
97
            return;
98
        }
99
        if (null === $this->transaction) {
100
            return;
101
        }
102
103
        $appStartTime = defined('APP_START_TIME')
104
            ? (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...
105
            : null;
106
107
        if ($appStartTime === null) {
108
            return;
109
        }
110
111
        $spanContextStart = new SpanContext();
112
        $spanContextStart->setOp('app.bootstrap');
113
        $spanContextStart->setStartTimestamp($appStartTime);
114
        $spanContextStart->setEndTimestamp($this->bootedTimestamp);
115
116
        $span = $this->transaction->startChild($spanContextStart);
117
118
        // Consume the booted timestamp, because we don't want to report the bootstrap span more than once
119
        $this->bootedTimestamp = null;
120
121
        // Add more information about the bootstrap section if possible
122
        $this->addBootDetailTimeSpans($span);
123
124
        $this->bootSpan = $span;
125
    }
126
127
    private function addBootDetailTimeSpans(Span $bootstrap): void
128
    {
129
        if (
130
            !defined('SENTRY_AUTOLOAD')
131
            || !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...
132
            || !is_numeric(SENTRY_AUTOLOAD)
133
        ) {
134
            return;
135
        }
136
137
        $autoload = new SpanContext();
138
        $autoload->setOp('autoload');
139
        $autoload->setStartTimestamp($bootstrap->getStartTimestamp());
140
        $autoload->setEndTimestamp((float)SENTRY_AUTOLOAD);
141
142
        $bootstrap->startChild($autoload);
143
    }
144
145
    public function listenBeginCommand(ConsoleCommandEvent $event): void
146
    {
147
        $command = $event->getCommand();
148
        $input = $event->getInput();
149
        $this->startCommand($command, $input);
150
    }
151
152
    private function startCommand(?Command $command, InputInterface $input): void
153
    {
154
        if (null === $this->transaction) {
155
            return;
156
        }
157
        $name = $command?->getName() ?? 'undefined command';
158
        $inputArgs = [
159
            'arguments' => $input->getArguments(),
160
            'options' => $input->getOptions(),
161
        ];
162
        $this->transaction->setData([
163
            'name' => $name,
164
            'input' => $inputArgs,
165
        ]);
166
        $this->transaction->setName($name);
167
168
        $bootstrapSpan = $this->bootSpan;
169
170
        $appContextStart = new SpanContext();
171
        $appContextStart->setOp('app.handle');
172
        $appContextStart->setStartTimestamp(
173
            $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

173
            /** @scrutinizer ignore-type */ $bootstrapSpan
Loading history...
174
                ? $bootstrapSpan->getEndTimestamp()
175
                : microtime(true)
176
        );
177
178
        $this->appSpan = $this->transaction->startChild($appContextStart);
179
180
        SentrySdk::getCurrentHub()->setSpan($this->appSpan);
181
    }
182
183
    public function listenShutdown(ApplicationShutdown $event): void
184
    {
185
        $exitCode = $event->getExitCode();
186
        $this->terminate($exitCode);
187
    }
188
189
    public function terminate(int $exitCode): void
190
    {
191
        if ($this->transaction !== null) {
192
            $this->appSpan?->finish();
193
            $this->appSpan = null;
194
195
            $this->transaction->setTags(['exitCode' => (string)$exitCode]);
196
            // Make sure we set the transaction and not have a child span in the Sentry SDK
197
            // If the transaction is not on the scope during finish, the trace.context is wrong
198
            SentrySdk::getCurrentHub()->setSpan($this->transaction);
199
200
            $this->transaction->finish();
201
            $this->transaction = null;
202
        }
203
    }
204
205
    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

205
    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...
206
    {
207
        $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

207
        $this->appSpan?->finish(/** @scrutinizer ignore-type */ microtime(true));
Loading history...
208
    }
209
210
    public function getAppSpan(): ?Span
211
    {
212
        return $this->appSpan;
213
    }
214
215
    public function setAppSpan(?Span $appSpan): void
216
    {
217
        $this->appSpan = $appSpan;
218
    }
219
}
220