Skip to content

Commit 0554a4f

Browse files
authored
adding logs signal (#934)
* initial work on logs signal * supressing psalm error * "fixing" 7.4 segfault through trial and error, I found that it was consistently segfaulting in the sdk autoloader tests, but by running them in a different order, the failures went away. * adding logs tests * rename example per feedback * move log context injection into processors, per spec * correctly set context on logs * update logger name per feedback * adding variables, map psr3 to otel severity, fix timestamp * psr-3 loggers * tests, psr3 v3 fix * remove LogRecordDate and refactor SDK log record classes * tests, tidy, improvements * remove todos * documentation * check for valid span context * removing psr-3 compatibility, per spec and feedback * use InstrumentationScopeFactoryInterface * apply attribute limits on readablelogrecord creation per feedback, this should be more memory efficient for the processors * group log record by resource/scope * remove psr3 references from readme * ignoring psalm error * add trace context in Logger rather than processors per feedback, and following the example in java and python SIGs, we can add trace context to logs once, in the logger, rather than having processors do it. The Context passed to processors is no longer used, but retained for spec compliance.
1 parent 75bcb6d commit 0554a4f

12 files changed

+298
-2
lines changed

Common/Instrumentation/CachedInstrumentation.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use ArrayAccess;
88
use function assert;
99
use function class_exists;
10+
use OpenTelemetry\API\Logs\LoggerInterface;
11+
use OpenTelemetry\API\Logs\LoggerProviderInterface;
1012
use OpenTelemetry\API\Metrics\MeterInterface;
1113
use OpenTelemetry\API\Metrics\MeterProviderInterface;
1214
use OpenTelemetry\API\Trace\TracerInterface;
@@ -31,6 +33,8 @@ final class CachedInstrumentation
3133
private ?ArrayAccess $tracers;
3234
/** @var ArrayAccess<MeterProviderInterface, MeterInterface>|null */
3335
private ?ArrayAccess $meters;
36+
/** @var ArrayAccess<LoggerProviderInterface, LoggerInterface>|null */
37+
private ?ArrayAccess $loggers;
3438

3539
public function __construct(string $name, ?string $version = null, ?string $schemaUrl = null, iterable $attributes = [])
3640
{
@@ -40,6 +44,7 @@ public function __construct(string $name, ?string $version = null, ?string $sche
4044
$this->attributes = $attributes;
4145
$this->tracers = self::createWeakMap();
4246
$this->meters = self::createWeakMap();
47+
$this->loggers = self::createWeakMap();
4348
}
4449

4550
private static function createWeakMap(): ?ArrayAccess
@@ -78,4 +83,15 @@ public function meter(): MeterInterface
7883

7984
return $this->meters[$meterProvider] ??= $meterProvider->getMeter($this->name, $this->version, $this->schemaUrl, $this->attributes);
8085
}
86+
public function logger(): LoggerInterface
87+
{
88+
$loggerProvider = Globals::loggerProvider();
89+
90+
if ($this->loggers === null) {
91+
//@todo configurable includeTraceContext?
92+
return $loggerProvider->getLogger($this->name, $this->version, $this->schemaUrl, true, $this->attributes);
93+
}
94+
95+
return $this->loggers[$loggerProvider] ??= $loggerProvider->getLogger($this->name, $this->version, $this->schemaUrl, true, $this->attributes);
96+
}
8197
}

Common/Instrumentation/Configurator.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace OpenTelemetry\API\Common\Instrumentation;
66

7+
use OpenTelemetry\API\Logs\LoggerProviderInterface;
8+
use OpenTelemetry\API\Logs\NoopLoggerProvider;
79
use OpenTelemetry\API\Metrics\MeterProviderInterface;
810
use OpenTelemetry\API\Metrics\Noop\NoopMeterProvider;
911
use OpenTelemetry\API\Trace\NoopTracerProvider;
@@ -25,6 +27,7 @@ final class Configurator implements ImplicitContextKeyedInterface
2527
private ?TracerProviderInterface $tracerProvider = null;
2628
private ?MeterProviderInterface $meterProvider = null;
2729
private ?TextMapPropagatorInterface $propagator = null;
30+
private ?LoggerProviderInterface $loggerProvider = null;
2831

2932
private function __construct()
3033
{
@@ -47,6 +50,7 @@ public static function createNoop(): Configurator
4750
->withTracerProvider(new NoopTracerProvider())
4851
->withMeterProvider(new NoopMeterProvider())
4952
->withPropagator(new NoopTextMapPropagator())
53+
->withLoggerProvider(new NoopLoggerProvider())
5054
;
5155
}
5256

@@ -68,6 +72,9 @@ public function storeInContext(?ContextInterface $context = null): ContextInterf
6872
if ($this->propagator !== null) {
6973
$context = $context->with(ContextKeys::propagator(), $this->propagator);
7074
}
75+
if ($this->loggerProvider !== null) {
76+
$context = $context->with(ContextKeys::loggerProvider(), $this->loggerProvider);
77+
}
7178

7279
return $context;
7380
}
@@ -93,6 +100,13 @@ public function withPropagator(?TextMapPropagatorInterface $propagator): Configu
93100
$self = clone $this;
94101
$self->propagator = $propagator;
95102

103+
return $self;
104+
}
105+
public function withLoggerProvider(?LoggerProviderInterface $loggerProvider): Configurator
106+
{
107+
$self = clone $this;
108+
$self->loggerProvider = $loggerProvider;
109+
96110
return $self;
97111
}
98112
}

Common/Instrumentation/ContextKeys.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace OpenTelemetry\API\Common\Instrumentation;
66

7+
use OpenTelemetry\API\Logs\LoggerProviderInterface;
78
use OpenTelemetry\API\Metrics\MeterProviderInterface;
89
use OpenTelemetry\API\Trace\TracerProviderInterface;
910
use OpenTelemetry\Context\Context;
@@ -44,4 +45,14 @@ public static function propagator(): ContextKeyInterface
4445

4546
return $instance ??= Context::createKey(TextMapPropagatorInterface::class);
4647
}
48+
49+
/**
50+
* @return ContextKeyInterface<LoggerProviderInterface>
51+
*/
52+
public static function loggerProvider(): ContextKeyInterface
53+
{
54+
static $instance;
55+
56+
return $instance ??= Context::createKey(LoggerProviderInterface::class);
57+
}
4758
}

Common/Instrumentation/Globals.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use function assert;
88
use Closure;
99
use const E_USER_WARNING;
10+
use OpenTelemetry\API\Logs\LoggerProviderInterface;
1011
use OpenTelemetry\API\Metrics\MeterProviderInterface;
1112
use OpenTelemetry\API\Trace\TracerProviderInterface;
1213
use OpenTelemetry\Context\Context;
@@ -27,14 +28,17 @@ final class Globals
2728
private TracerProviderInterface $tracerProvider;
2829
private MeterProviderInterface $meterProvider;
2930
private TextMapPropagatorInterface $propagator;
31+
private LoggerProviderInterface $loggerProvider;
3032

3133
public function __construct(
3234
TracerProviderInterface $tracerProvider,
3335
MeterProviderInterface $meterProvider,
36+
LoggerProviderInterface $loggerProvider,
3437
TextMapPropagatorInterface $propagator
3538
) {
3639
$this->tracerProvider = $tracerProvider;
3740
$this->meterProvider = $meterProvider;
41+
$this->loggerProvider = $loggerProvider;
3842
$this->propagator = $propagator;
3943
}
4044

@@ -53,6 +57,11 @@ public static function propagator(): TextMapPropagatorInterface
5357
return Context::getCurrent()->get(ContextKeys::propagator()) ?? self::globals()->propagator;
5458
}
5559

60+
public static function loggerProvider(): LoggerProviderInterface
61+
{
62+
return Context::getCurrent()->get(ContextKeys::loggerProvider()) ?? self::globals()->loggerProvider;
63+
}
64+
5665
/**
5766
* @param Closure(Configurator): Configurator $initializer
5867
*
@@ -92,10 +101,11 @@ private static function globals(): self
92101
$tracerProvider = $context->get(ContextKeys::tracerProvider());
93102
$meterProvider = $context->get(ContextKeys::meterProvider());
94103
$propagator = $context->get(ContextKeys::propagator());
104+
$loggerProvider = $context->get(ContextKeys::loggerProvider());
95105

96-
assert(isset($tracerProvider, $meterProvider, $propagator));
106+
assert(isset($tracerProvider, $meterProvider, $loggerProvider, $propagator));
97107

98-
return self::$globals = new self($tracerProvider, $meterProvider, $propagator);
108+
return self::$globals = new self($tracerProvider, $meterProvider, $loggerProvider, $propagator);
99109
}
100110

101111
/**
@@ -104,5 +114,6 @@ private static function globals(): self
104114
public static function reset(): void
105115
{
106116
self::$globals = null;
117+
self::$initializers = [];
107118
}
108119
}

Logs/EventLogger.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\API\Logs;
6+
7+
class EventLogger implements EventLoggerInterface
8+
{
9+
private LoggerInterface $logger;
10+
private string $domain;
11+
12+
public function __construct(LoggerInterface $logger, string $domain)
13+
{
14+
$this->logger = $logger;
15+
$this->domain = $domain;
16+
}
17+
18+
public function logEvent(string $eventName, LogRecord $logRecord): void
19+
{
20+
$logRecord->setAttributes([
21+
'event.name' => $eventName,
22+
'event.domain' => $this->domain,
23+
]);
24+
$this->logger->logRecord($logRecord);
25+
}
26+
}

Logs/EventLoggerInterface.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\API\Logs;
6+
7+
/**
8+
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/event-api.md#events-api-interface
9+
*/
10+
interface EventLoggerInterface
11+
{
12+
public function logEvent(string $eventName, LogRecord $logRecord): void;
13+
}

Logs/LogRecord.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\API\Logs;
6+
7+
use OpenTelemetry\Context\ContextInterface;
8+
9+
class LogRecord
10+
{
11+
public const NANOS_PER_SECOND = 1_000_000_000;
12+
13+
protected ?int $timestamp = null;
14+
protected ?int $observedTimestamp = null;
15+
protected ?ContextInterface $context = null;
16+
protected int $severityNumber = 0;
17+
protected ?string $severityText = null;
18+
protected $body = null;
19+
protected array $attributes = [];
20+
21+
public function __construct($body = null)
22+
{
23+
$this->body = $body;
24+
}
25+
26+
public function setTimestamp(int $timestamp): self
27+
{
28+
$this->timestamp = $timestamp;
29+
30+
return $this;
31+
}
32+
33+
public function setContext(?ContextInterface $context = null): self
34+
{
35+
$this->context = $context;
36+
37+
return $this;
38+
}
39+
40+
public function setSeverityNumber(int $severityNumber): self
41+
{
42+
$this->severityNumber = $severityNumber;
43+
44+
return $this;
45+
}
46+
47+
public function setSeverityText(string $severityText): self
48+
{
49+
$this->severityText = $severityText;
50+
51+
return $this;
52+
}
53+
54+
public function setAttributes(iterable $attributes): self
55+
{
56+
foreach ($attributes as $name => $value) {
57+
$this->setAttribute($name, $value);
58+
}
59+
60+
return $this;
61+
}
62+
63+
public function setAttribute(string $name, $value): self
64+
{
65+
$this->attributes[$name] = $value;
66+
67+
return $this;
68+
}
69+
70+
public function setBody($body = null): self
71+
{
72+
$this->body = $body;
73+
74+
return $this;
75+
}
76+
77+
public function setObservedTimestamp(int $observedTimestamp = null): self
78+
{
79+
$this->observedTimestamp = $observedTimestamp;
80+
81+
return $this;
82+
}
83+
}

Logs/LoggerInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\API\Logs;
6+
7+
interface LoggerInterface
8+
{
9+
public function logRecord(LogRecord $logRecord): void;
10+
}

Logs/LoggerProviderInterface.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\API\Logs;
6+
7+
/**
8+
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/bridge-api.md#get-a-logger
9+
*/
10+
interface LoggerProviderInterface
11+
{
12+
public function getLogger(
13+
string $name,
14+
?string $version = null,
15+
?string $schemaUrl = null,
16+
bool $includeTraceContext = true,
17+
iterable $attributes = [] //instrumentation scope attributes
18+
): LoggerInterface;
19+
}

Logs/Map/Psr3.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\API\Logs\Map;
6+
7+
use Psr\Log\LogLevel;
8+
9+
class Psr3
10+
{
11+
/**
12+
* Maps PSR-3 severity level (string) to the appropriate opentelemetry severity number
13+
*
14+
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model-appendix.md#appendix-b-severitynumber-example-mappings
15+
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-severitynumber
16+
*/
17+
public static function severityNumber(string $level): int
18+
{
19+
switch (strtolower($level)) {
20+
case LogLevel::DEBUG:
21+
return 5;
22+
case LogLevel::INFO:
23+
return 9;
24+
case LogLevel::NOTICE:
25+
return 10;
26+
case LogLevel::WARNING:
27+
return 13;
28+
case LogLevel::ERROR:
29+
return 17;
30+
case LogLevel::CRITICAL:
31+
return 18;
32+
case LogLevel::ALERT:
33+
return 19;
34+
case LogLevel::EMERGENCY:
35+
return 21;
36+
default:
37+
return 0;
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)