Skip to content

Commit c7ec648

Browse files
committed
Add Sentry logs
1 parent 4ed1625 commit c7ec648

File tree

8 files changed

+251
-0
lines changed

8 files changed

+251
-0
lines changed

src/Event.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ final class Event
6161
*/
6262
private $checkIn;
6363

64+
/**
65+
* @var array|null
66+
*/
67+
private $logs;
68+
6469
/**
6570
* @var string|null The name of the server (e.g. the host name)
6671
*/
@@ -216,6 +221,11 @@ public static function createCheckIn(?EventId $eventId = null): self
216221
return new self($eventId, EventType::checkIn());
217222
}
218223

224+
public static function createLogs(?EventId $eventId = null): self
225+
{
226+
return new self($eventId, EventType::logs());
227+
}
228+
219229
/**
220230
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
221231
*/
@@ -368,6 +378,18 @@ public function setCheckIn(?CheckIn $checkIn): self
368378
return $this;
369379
}
370380

381+
public function getLogs(): array
382+
{
383+
return $this->logs;
384+
}
385+
386+
public function setLogs(array $logs): self
387+
{
388+
$this->logs = $logs;
389+
390+
return $this;
391+
}
392+
371393
/**
372394
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
373395
*/

src/EventType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ public static function checkIn(): self
4242
return self::getInstance('check_in');
4343
}
4444

45+
public static function logs(): self
46+
{
47+
return self::getInstance('log');
48+
}
49+
4550
/**
4651
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
4752
*/
@@ -61,6 +66,7 @@ public static function cases(): array
6166
self::event(),
6267
self::transaction(),
6368
self::checkIn(),
69+
self::logs(),
6470
self::metrics(),
6571
];
6672
}

src/Logs/LogLevel.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\Logs;
6+
7+
class LogLevel
8+
{
9+
/**
10+
* @var string The value of the enum instance
11+
*/
12+
private $value;
13+
14+
/**
15+
* @var array<string, self> A list of cached enum instances
16+
*/
17+
private static $instances = [];
18+
19+
private function __construct(string $value)
20+
{
21+
$this->value = $value;
22+
}
23+
24+
public static function info(): self
25+
{
26+
return self::getInstance('info');
27+
}
28+
29+
public function __toString(): string
30+
{
31+
return $this->value;
32+
}
33+
34+
private static function getInstance(string $value): self
35+
{
36+
if (!isset(self::$instances[$value])) {
37+
self::$instances[$value] = new self($value);
38+
}
39+
40+
return self::$instances[$value];
41+
}
42+
}

src/Logs/Logs.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\Logs;
6+
7+
use Sentry\EventId;
8+
9+
class Logs
10+
{
11+
/**
12+
* @var self|null
13+
*/
14+
private static $instance;
15+
16+
/**
17+
* @var LogsAggregator
18+
*/
19+
private $aggregator;
20+
21+
private function __construct()
22+
{
23+
$this->aggregator = new LogsAggregator();
24+
}
25+
26+
public static function getInstance(): self
27+
{
28+
if (self::$instance === null) {
29+
self::$instance = new self();
30+
}
31+
32+
return self::$instance;
33+
}
34+
35+
public function info(string $message): void
36+
{
37+
$this->aggregator->add(LogLevel::info(), $message);
38+
}
39+
40+
public function flush(): ?EventId
41+
{
42+
return $this->aggregator->flush();
43+
}
44+
}

src/Logs/LogsAggregator.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\Logs;
6+
7+
use Sentry\Event;
8+
use Sentry\EventId;
9+
use Sentry\SentrySdk;
10+
use Sentry\State\Scope;
11+
use Sentry\Tracing\TransactionSource;
12+
13+
/**
14+
* @internal
15+
*/
16+
final class LogsAggregator
17+
{
18+
private $logs = [];
19+
20+
public function add(
21+
LogLevel $level,
22+
string $message,
23+
): void {
24+
$timestamp = time();
25+
$traceId = null;
26+
27+
$hub = SentrySdk::getCurrentHub();
28+
$span = $hub->getSpan();
29+
if ($span !== null) {
30+
$traceId = $span->getTraceId();
31+
}
32+
33+
$this->logs[] = [
34+
'timestamp' => $timestamp,
35+
'trace_id' => (string) $traceId,
36+
'level' => (string) $level,
37+
'body' => $message,
38+
];
39+
}
40+
41+
public function flush(): ?EventId
42+
{
43+
if (empty($this->logs)) {
44+
return null;
45+
}
46+
47+
$hub = SentrySdk::getCurrentHub();
48+
$event = Event::createLogs()->setLogs($this->logs);
49+
50+
$this->logs = [];
51+
52+
return $hub->captureEvent($event);
53+
}
54+
55+
/**
56+
* @param array<string, string> $tags
57+
*
58+
* @return array<string, string>
59+
*/
60+
private function serializeTags(array $tags): array
61+
{
62+
$hub = SentrySdk::getCurrentHub();
63+
$client = $hub->getClient();
64+
65+
if ($client !== null) {
66+
$options = $client->getOptions();
67+
68+
$defaultTags = [
69+
'environment' => $options->getEnvironment() ?? Event::DEFAULT_ENVIRONMENT,
70+
];
71+
72+
$release = $options->getRelease();
73+
if ($release !== null) {
74+
$defaultTags['release'] = $release;
75+
}
76+
77+
$hub->configureScope(function (Scope $scope) use (&$defaultTags) {
78+
$transaction = $scope->getTransaction();
79+
if (
80+
$transaction !== null
81+
// Only include the transaction name if it has good quality
82+
&& $transaction->getMetadata()->getSource() !== TransactionSource::url()
83+
) {
84+
$defaultTags['transaction'] = $transaction->getName();
85+
}
86+
});
87+
88+
$tags = array_merge($defaultTags, $tags);
89+
}
90+
91+
// It's very important to sort the tags in order to obtain the same bucket key.
92+
ksort($tags);
93+
94+
return $tags;
95+
}
96+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\Serializer\EnvelopItems;
6+
7+
use Sentry\Event;
8+
use Sentry\Util\JSON;
9+
10+
/**
11+
* @internal
12+
*/
13+
class LogsItem implements EnvelopeItemInterface
14+
{
15+
public static function toEnvelopeItem(Event $event): string
16+
{
17+
$header = [
18+
'type' => (string) $event->getType(),
19+
'content_type' => 'application/json',
20+
];
21+
22+
$payload = '';
23+
24+
$logs = $event->getLogs();
25+
foreach ($logs as $log) {
26+
$payload .= \sprintf("%s\n%s", JSON::encode($header), JSON::encode($log));
27+
}
28+
29+
return $payload;
30+
}
31+
}

src/Serializer/PayloadSerializer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Sentry\Options;
1010
use Sentry\Serializer\EnvelopItems\CheckInItem;
1111
use Sentry\Serializer\EnvelopItems\EventItem;
12+
use Sentry\Serializer\EnvelopItems\LogsItem;
1213
use Sentry\Serializer\EnvelopItems\ProfileItem;
1314
use Sentry\Serializer\EnvelopItems\TransactionItem;
1415
use Sentry\Tracing\DynamicSamplingContext;
@@ -77,6 +78,9 @@ public function serialize(Event $event): string
7778
case EventType::checkIn():
7879
$items = CheckInItem::toEnvelopeItem($event);
7980
break;
81+
case EventType::logs():
82+
$items = LogsItem::toEnvelopeItem($event);
83+
break;
8084
}
8185

8286
return \sprintf("%s\n%s", JSON::encode($envelopeHeader), $items);

src/functions.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Psr\Log\LoggerInterface;
88
use Sentry\HttpClient\HttpClientInterface;
99
use Sentry\Integration\IntegrationInterface;
10+
use Sentry\Logs\Logs;
1011
use Sentry\Metrics\Metrics;
1112
use Sentry\State\Scope;
1213
use Sentry\Tracing\PropagationContext;
@@ -377,6 +378,11 @@ function continueTrace(string $sentryTrace, string $baggage): TransactionContext
377378
return TransactionContext::fromHeaders($sentryTrace, $baggage);
378379
}
379380

381+
function logger(): Logs
382+
{
383+
return Logs::getInstance();
384+
}
385+
380386
/**
381387
* @deprecated Metrics are no longer supported. Metrics API is a no-op and will be removed in 5.x.
382388
*/

0 commit comments

Comments
 (0)