Skip to content

Commit dc31502

Browse files
Litarnuscleptric
andauthored
feat(metrics): introduce metrics (#1968)
Co-authored-by: Michael Hoffmann <michi@sentry.io>
1 parent c6f5826 commit dc31502

22 files changed

+1309
-54
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,11 @@ parameters:
2525
count: 1
2626
path: src/Dsn.php
2727

28-
-
29-
message: "#^Method Sentry\\\\Event\\:\\:getMetrics\\(\\) return type has no value type specified in iterable type array\\.$#"
30-
count: 1
31-
path: src/Event.php
32-
3328
-
3429
message: "#^Method Sentry\\\\Event\\:\\:getMetricsSummary\\(\\) return type has no value type specified in iterable type array\\.$#"
3530
count: 1
3631
path: src/Event.php
3732

38-
-
39-
message: "#^Method Sentry\\\\Event\\:\\:setMetrics\\(\\) has parameter \\$metrics with no value type specified in iterable type array\\.$#"
40-
count: 1
41-
path: src/Event.php
42-
4333
-
4434
message: "#^Method Sentry\\\\Event\\:\\:setMetricsSummary\\(\\) has parameter \\$metricsSummary with no value type specified in iterable type array\\.$#"
4535
count: 1

src/Event.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Sentry\Context\OsContext;
88
use Sentry\Context\RuntimeContext;
99
use Sentry\Logs\Log;
10+
use Sentry\Metrics\Types\Metric;
1011
use Sentry\Profiling\Profile;
1112
use Sentry\Tracing\Span;
1213

@@ -71,6 +72,11 @@ final class Event
7172
*/
7273
private $logs = [];
7374

75+
/**
76+
* @var Metric[]
77+
*/
78+
private $metrics = [];
79+
7480
/**
7581
* @var string|null The name of the server (e.g. the host name)
7682
*/
@@ -241,9 +247,6 @@ public static function createLogs(?EventId $eventId = null): self
241247
return new self($eventId, EventType::logs());
242248
}
243249

244-
/**
245-
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
246-
*/
247250
public static function createMetrics(?EventId $eventId = null): self
248251
{
249252
return new self($eventId, EventType::metrics());
@@ -446,18 +449,20 @@ public function setLogs(array $logs): self
446449
}
447450

448451
/**
449-
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
452+
* @return Metric[]
450453
*/
451454
public function getMetrics(): array
452455
{
453-
return [];
456+
return $this->metrics;
454457
}
455458

456459
/**
457-
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
460+
* @param Metric[] $metrics
458461
*/
459462
public function setMetrics(array $metrics): self
460463
{
464+
$this->metrics = $metrics;
465+
461466
return $this;
462467
}
463468

src/EventType.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,9 @@ public static function logs(): self
4747
return self::getInstance('log');
4848
}
4949

50-
/**
51-
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
52-
*/
5350
public static function metrics(): self
5451
{
55-
return self::getInstance('metrics');
52+
return self::getInstance('trace_metric');
5653
}
5754

5855
/**
@@ -71,6 +68,17 @@ public static function cases(): array
7168
];
7269
}
7370

71+
public function requiresEventId(): bool
72+
{
73+
switch ($this) {
74+
case self::metrics():
75+
case self::logs():
76+
return false;
77+
default:
78+
return true;
79+
}
80+
}
81+
7482
public function __toString(): string
7583
{
7684
return $this->value;

src/Metrics/Metrics.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66

77
use Sentry\EventId;
88
use Sentry\Tracing\SpanContext;
9+
use Sentry\Unit;
910

1011
use function Sentry\trace;
1112

13+
class_alias(Unit::class, '\Sentry\Metrics\MetricsUnit');
14+
15+
/**
16+
* @deprecated use TraceMetrics instead
17+
*/
1218
class Metrics
1319
{
1420
/**
@@ -28,12 +34,12 @@ public static function getInstance(): self
2834
/**
2935
* @param array<string, string> $tags
3036
*
31-
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
37+
* @deprecated Use TraceMetrics::count() instead. To be removed in 5.x.
3238
*/
3339
public function increment(
3440
string $key,
3541
float $value,
36-
?MetricsUnit $unit = null,
42+
?Unit $unit = null,
3743
array $tags = [],
3844
?int $timestamp = null,
3945
int $stackLevel = 0
@@ -43,12 +49,12 @@ public function increment(
4349
/**
4450
* @param array<string, string> $tags
4551
*
46-
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
52+
* @deprecated Use TraceMetrics::distribution() instead. Metrics API is a no-op and will be removed in 5.x.
4753
*/
4854
public function distribution(
4955
string $key,
5056
float $value,
51-
?MetricsUnit $unit = null,
57+
?Unit $unit = null,
5258
array $tags = [],
5359
?int $timestamp = null,
5460
int $stackLevel = 0
@@ -58,12 +64,12 @@ public function distribution(
5864
/**
5965
* @param array<string, string> $tags
6066
*
61-
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
67+
* @deprecated Use TraceMetrics::gauge() instead. To be removed in 5.x.
6268
*/
6369
public function gauge(
6470
string $key,
6571
float $value,
66-
?MetricsUnit $unit = null,
72+
?Unit $unit = null,
6773
array $tags = [],
6874
?int $timestamp = null,
6975
int $stackLevel = 0
@@ -79,7 +85,7 @@ public function gauge(
7985
public function set(
8086
string $key,
8187
$value,
82-
?MetricsUnit $unit = null,
88+
?Unit $unit = null,
8389
array $tags = [],
8490
?int $timestamp = null,
8591
int $stackLevel = 0

src/Metrics/MetricsAggregator.php

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\Metrics;
6+
7+
use Sentry\Client;
8+
use Sentry\Event;
9+
use Sentry\EventId;
10+
use Sentry\Metrics\Types\CounterMetric;
11+
use Sentry\Metrics\Types\DistributionMetric;
12+
use Sentry\Metrics\Types\GaugeMetric;
13+
use Sentry\Metrics\Types\Metric;
14+
use Sentry\SentrySdk;
15+
use Sentry\State\Scope;
16+
use Sentry\Unit;
17+
use Sentry\Util\RingBuffer;
18+
19+
/**
20+
* @internal
21+
*/
22+
final class MetricsAggregator
23+
{
24+
/**
25+
* @var int
26+
*/
27+
public const METRICS_BUFFER_SIZE = 1000;
28+
29+
/**
30+
* @var RingBuffer<Metric>
31+
*/
32+
private $metrics;
33+
34+
public function __construct()
35+
{
36+
$this->metrics = new RingBuffer(self::METRICS_BUFFER_SIZE);
37+
}
38+
39+
private const METRIC_TYPES = [
40+
CounterMetric::TYPE => CounterMetric::class,
41+
DistributionMetric::TYPE => DistributionMetric::class,
42+
GaugeMetric::TYPE => GaugeMetric::class,
43+
];
44+
45+
/**
46+
* @param int|float $value
47+
* @param array<string, int|float|string|bool> $attributes
48+
*/
49+
public function add(
50+
string $type,
51+
string $name,
52+
$value,
53+
array $attributes,
54+
?Unit $unit
55+
): void {
56+
$hub = SentrySdk::getCurrentHub();
57+
$client = $hub->getClient();
58+
59+
if ($client instanceof Client) {
60+
$options = $client->getOptions();
61+
62+
if ($options->getEnableMetrics() === false) {
63+
return;
64+
}
65+
66+
$defaultAttributes = [
67+
'sentry.sdk.name' => $client->getSdkIdentifier(),
68+
'sentry.sdk.version' => $client->getSdkVersion(),
69+
'sentry.environment' => $options->getEnvironment() ?? Event::DEFAULT_ENVIRONMENT,
70+
'server.address' => $options->getServerName(),
71+
];
72+
73+
if ($options->shouldSendDefaultPii()) {
74+
$hub->configureScope(function (Scope $scope) use (&$defaultAttributes) {
75+
$user = $scope->getUser();
76+
if ($user !== null) {
77+
if ($user->getId() !== null) {
78+
$defaultAttributes['user.id'] = $user->getId();
79+
}
80+
if ($user->getEmail() !== null) {
81+
$defaultAttributes['user.email'] = $user->getEmail();
82+
}
83+
if ($user->getUsername() !== null) {
84+
$defaultAttributes['user.name'] = $user->getUsername();
85+
}
86+
}
87+
});
88+
}
89+
90+
$release = $options->getRelease();
91+
if ($release !== null) {
92+
$defaultAttributes['sentry.release'] = $release;
93+
}
94+
95+
$attributes += $defaultAttributes;
96+
}
97+
98+
$spanId = null;
99+
$traceId = null;
100+
101+
$span = $hub->getSpan();
102+
if ($span !== null) {
103+
$spanId = $span->getSpanId();
104+
$traceId = $span->getTraceId();
105+
} else {
106+
$hub->configureScope(function (Scope $scope) use (&$traceId, &$spanId) {
107+
$propagationContext = $scope->getPropagationContext();
108+
$traceId = $propagationContext->getTraceId();
109+
$spanId = $propagationContext->getSpanId();
110+
});
111+
}
112+
113+
$metricTypeClass = self::METRIC_TYPES[$type];
114+
/** @var Metric $metric */
115+
/** @phpstan-ignore-next-line */
116+
$metric = new $metricTypeClass($name, $value, $traceId, $spanId, $attributes, microtime(true), $unit);
117+
118+
if ($client !== null) {
119+
$beforeSendMetric = $client->getOptions()->getBeforeSendMetricCallback();
120+
$metric = $beforeSendMetric($metric);
121+
if ($metric === null) {
122+
return;
123+
}
124+
}
125+
126+
$this->metrics->push($metric);
127+
}
128+
129+
public function flush(): ?EventId
130+
{
131+
if ($this->metrics->isEmpty()) {
132+
return null;
133+
}
134+
135+
$hub = SentrySdk::getCurrentHub();
136+
$event = Event::createMetrics()->setMetrics($this->metrics->drain());
137+
138+
return $hub->captureEvent($event);
139+
}
140+
}

0 commit comments

Comments
 (0)