From 0b255cab7061712410b659d6057e8819ad79cd94 Mon Sep 17 00:00:00 2001 From: Brett McBride Date: Thu, 23 Jan 2025 11:11:47 +1100 Subject: [PATCH] implement mutable ReadWriteLogRecord (#1482) https://github.com/open-telemetry/opentelemetry-specification/pull/3907 implements some new requirements for logging: - ReadWriteLogRecord can mutate (eg by processors) - mutated ReadWriteLogRecord can be seen by later processors This is a breaking change because LogRecordProcessorInterface onEmit param changes to by-reference --- Logs/Exporter/InMemoryExporter.php | 18 +++++++++++++++++- Logs/LogRecordProcessorInterface.php | 2 +- Logs/Processor/BatchLogRecordProcessor.php | 2 +- Logs/Processor/MultiLogRecordProcessor.php | 2 +- Logs/Processor/NoopLogRecordProcessor.php | 2 +- Logs/Processor/SimpleLogRecordProcessor.php | 2 +- Logs/ReadWriteLogRecord.php | 13 +++++++++++++ Logs/ReadableLogRecord.php | 11 +++++------ 8 files changed, 40 insertions(+), 12 deletions(-) diff --git a/Logs/Exporter/InMemoryExporter.php b/Logs/Exporter/InMemoryExporter.php index 3ecaecd..52615d2 100644 --- a/Logs/Exporter/InMemoryExporter.php +++ b/Logs/Exporter/InMemoryExporter.php @@ -9,6 +9,7 @@ use OpenTelemetry\SDK\Common\Future\CompletedFuture; use OpenTelemetry\SDK\Common\Future\FutureInterface; use OpenTelemetry\SDK\Logs\LogRecordExporterInterface; +use OpenTelemetry\SDK\Logs\ReadableLogRecord; class InMemoryExporter implements LogRecordExporterInterface { @@ -22,7 +23,7 @@ public function __construct(private readonly ArrayObject $storage = new ArrayObj public function export(iterable $batch, ?CancellationInterface $cancellation = null): FutureInterface { foreach ($batch as $record) { - $this->storage->append($record); + $this->storage->append($this->convert($record)); } return new CompletedFuture(true); @@ -42,4 +43,19 @@ public function getStorage(): ArrayObject { return $this->storage; } + + private function convert(ReadableLogRecord $record): array + { + return [ + 'timestamp' => $record->getTimestamp(), + 'observed_timestamp' => $record->getObservedTimestamp(), + 'severity_number' => $record->getSeverityNumber(), + 'severity_text' => $record->getSeverityText(), + 'body' => $record->getBody(), + 'attributes' => $record->getAttributes()->toArray(), + 'trace_id' => $record->getSpanContext()?->getTraceId(), + 'span_id' => $record->getSpanContext()?->getSpanId(), + 'trace_flags' => $record->getSpanContext()?->getTraceFlags(), + ]; + } } diff --git a/Logs/LogRecordProcessorInterface.php b/Logs/LogRecordProcessorInterface.php index 1977d48..ba1519a 100644 --- a/Logs/LogRecordProcessorInterface.php +++ b/Logs/LogRecordProcessorInterface.php @@ -9,7 +9,7 @@ interface LogRecordProcessorInterface { - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void; + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void; public function shutdown(?CancellationInterface $cancellation = null): bool; public function forceFlush(?CancellationInterface $cancellation = null): bool; } diff --git a/Logs/Processor/BatchLogRecordProcessor.php b/Logs/Processor/BatchLogRecordProcessor.php index 8de2d0f..6fcd23e 100644 --- a/Logs/Processor/BatchLogRecordProcessor.php +++ b/Logs/Processor/BatchLogRecordProcessor.php @@ -134,7 +134,7 @@ public function __construct( }); } - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void { if ($this->closed) { return; diff --git a/Logs/Processor/MultiLogRecordProcessor.php b/Logs/Processor/MultiLogRecordProcessor.php index 6a5791f..4bb6c54 100644 --- a/Logs/Processor/MultiLogRecordProcessor.php +++ b/Logs/Processor/MultiLogRecordProcessor.php @@ -22,7 +22,7 @@ public function __construct(array $processors) } } - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void { foreach ($this->processors as $processor) { $processor->onEmit($record, $context); diff --git a/Logs/Processor/NoopLogRecordProcessor.php b/Logs/Processor/NoopLogRecordProcessor.php index 7028052..3fabcfd 100644 --- a/Logs/Processor/NoopLogRecordProcessor.php +++ b/Logs/Processor/NoopLogRecordProcessor.php @@ -21,7 +21,7 @@ public static function getInstance(): self /** * @codeCoverageIgnore */ - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void { } diff --git a/Logs/Processor/SimpleLogRecordProcessor.php b/Logs/Processor/SimpleLogRecordProcessor.php index 264a450..6df8a3f 100644 --- a/Logs/Processor/SimpleLogRecordProcessor.php +++ b/Logs/Processor/SimpleLogRecordProcessor.php @@ -19,7 +19,7 @@ public function __construct(private readonly LogRecordExporterInterface $exporte /** * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/sdk.md#onemit */ - public function onEmit(ReadWriteLogRecord $record, ?ContextInterface $context = null): void + public function onEmit(ReadWriteLogRecord &$record, ?ContextInterface $context = null): void { $this->exporter->export([$record]); } diff --git a/Logs/ReadWriteLogRecord.php b/Logs/ReadWriteLogRecord.php index 9bb4b15..74e1fb4 100644 --- a/Logs/ReadWriteLogRecord.php +++ b/Logs/ReadWriteLogRecord.php @@ -6,4 +6,17 @@ class ReadWriteLogRecord extends ReadableLogRecord { + public function setAttribute(string $name, mixed $value): self + { + $this->attributesBuilder->offsetSet($name, $value); + + return $this; + } + + public function removeAttribute(string $key): self + { + $this->attributesBuilder->offsetUnset($key); + + return $this; + } } diff --git a/Logs/ReadableLogRecord.php b/Logs/ReadableLogRecord.php index f1d09e5..e11839f 100644 --- a/Logs/ReadableLogRecord.php +++ b/Logs/ReadableLogRecord.php @@ -9,6 +9,7 @@ use OpenTelemetry\API\Trace\SpanContextInterface; use OpenTelemetry\Context\Context; use OpenTelemetry\Context\ContextInterface; +use OpenTelemetry\SDK\Common\Attribute\AttributesBuilderInterface; use OpenTelemetry\SDK\Common\Attribute\AttributesInterface; use OpenTelemetry\SDK\Common\Attribute\LogRecordAttributeValidator; use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeInterface; @@ -20,7 +21,7 @@ */ class ReadableLogRecord extends LogRecord { - protected AttributesInterface $convertedAttributes; + protected AttributesBuilderInterface $attributesBuilder; protected SpanContextInterface $spanContext; public function __construct( @@ -38,12 +39,10 @@ public function __construct( $this->severityNumber = $logRecord->severityNumber; $this->severityText = $logRecord->severityText; - //convert attributes now so that excess data is not sent to processors - $this->convertedAttributes = $this->loggerSharedState + $this->attributesBuilder = $this->loggerSharedState ->getLogRecordLimits() ->getAttributeFactory() - ->builder($logRecord->attributes, new LogRecordAttributeValidator()) - ->build(); + ->builder($logRecord->attributes, new LogRecordAttributeValidator()); } public function getInstrumentationScope(): InstrumentationScopeInterface @@ -96,6 +95,6 @@ public function getBody() public function getAttributes(): AttributesInterface { - return $this->convertedAttributes; + return $this->attributesBuilder->build(); } }