Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding event dispatcher #708

Closed
wants to merge 16 commits into from
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"php-http/async-client-implementation": "^1.0",
"php-http/discovery": "^1.14",
"promphp/prometheus_client_php": "^2.2.1",
"psr/event-dispatcher": "^1",
"psr/http-factory-implementation": "^1.0",
"psr/log": "^1.1|^2.0|^3.0",
"symfony/polyfill-mbstring": "^1.23"
Expand Down
68 changes: 68 additions & 0 deletions examples/EventsExample.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';

use Monolog\Formatter\JsonFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use OpenTelemetry\Contrib\Zipkin\Exporter as ZipkinExporter;
use OpenTelemetry\SDK\Common\Event\Dispatcher;
use OpenTelemetry\SDK\Common\Event\Event\ErrorEvent;
use OpenTelemetry\SDK\Common\Event\EventType;
use OpenTelemetry\SDK\Common\Event\SimpleDispatcher;
use OpenTelemetry\SDK\Common\Event\SimpleListenerProvider;
use OpenTelemetry\SDK\Common\Log\LoggerHolder;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use Psr\Log\LogLevel;

/**
* This example shows how you can hook in to OpenTelemetry's events, such as when an error or warning is emitted by
* the library. OpenTelemetry will always attempt to log and continue on error, and may provide no-op implementations
* of classes if it was unable to create them as requested.
* The default event handlers will use a PSR-3 logger to log events - verbosity and output format can be controlled
* through the usual PSR-3 log levels and formatters (if available).
* If you wish to do something different with events, you may provide an alternative PSR-14 implementation, but be
* aware that you will need to register handlers for all events that you are interested in.
*/

//logger used by default event handlers
LoggerHolder::set(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tidal - this might be an opportunity to move LoggerHolder somewhere under SDK\Event ? A way to get rid of the static singleton could be to create something like an AbstractLoggableEvent (or trait) which accepts a LoggerInterface and is extended by our events as required?

new Logger('otel-php', [(new StreamHandler(STDOUT, LogLevel::DEBUG))->setFormatter(new JsonFormatter())])
);

/**
* Register event listeners - this only works for SimpleEventDispatcher. For other PSR-14 implementations, you will
/* need to register all events that you are interested in, @see {SDK\Event\EventTypes}
*/
$listenerProvider = new SimpleListenerProvider();
$listenerProvider->listen(EventType::ERROR, function (ErrorEvent $event) {
echo 'Custom handling of an error event: ' . $event->getError()->getMessage() . PHP_EOL;
}, -10); //runs before built-in handler
$listenerProvider->listen(EventType::ERROR, function (ErrorEvent $event) {
echo 'Another custom handling of an error event: ' . $event->getMessage() . PHP_EOL;
echo json_encode($event->getError()->getTrace()) . PHP_EOL;
echo 'Stopping event propagation...' . PHP_EOL;
$event->stopPropagation();
}, 5); //runs after build-in handler
$listenerProvider->listen(EventType::ERROR, function (ErrorEvent $event) {
echo 'This will not be executed, because a high priority handler stopped event propagation.' . PHP_EOL;
}, 10);

Dispatcher::setInstance(new SimpleDispatcher($listenerProvider));

//or, provide your own PSR-14 event dispatcher. This must be done before getting a tracer:
//$listenerProvider = new \Any\Psr14\ListenerProvider();
//$listenerProvider->listen(EventType::ERROR, function(ErrorEvent $event){...});
//DispatcherHolder::setInstance(\Any\Psr14\EventDispatcherInterface($listenerProvider));

$tracerProvider = new TracerProvider(
new SimpleSpanProcessor(
ZipkinExporter::fromConnectionString('http://invalid-host:9999', 'zipkin-exporter')
)
);
$tracer = $tracerProvider->getTracer();

$span = $tracer->spanBuilder('root')->startSpan();
$span->end();
7 changes: 3 additions & 4 deletions src/Contrib/Jaeger/JaegerTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@
use Jaeger\Thrift\Batch;
use Jaeger\Thrift\Process;
use Jaeger\Thrift\Span;
use OpenTelemetry\SDK\Behavior\LogsMessagesTrait;
use OpenTelemetry\SDK\Common\Event\Dispatcher;
use OpenTelemetry\SDK\Common\Event\Event\ErrorEvent;
use Thrift\Exception\TTransportException;
use Thrift\Protocol\TCompactProtocol;

final class JaegerTransport implements TransportInterface
{
use LogsMessagesTrait;

// DEFAULT_BUFFER_SIZE indicates the default maximum buffer size, or the size threshold
// at which the buffer will be flushed to the agent.
const DEFAULT_BUFFER_SIZE = 1;
Expand Down Expand Up @@ -93,7 +92,7 @@ public function flush($force = false): int
// reset the process tag
$this->process = null;
} catch (TTransportException $e) {
self::logError('jaeger: transport failure: ' . $e->getMessage());
Dispatcher::getInstance()->dispatch(new ErrorEvent('jaeger transport failure', $e));

return 0;
}
Expand Down
8 changes: 6 additions & 2 deletions src/Contrib/OtlpGrpc/Exporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace OpenTelemetry\Contrib\OtlpGrpc;

use Exception;
use grpc;
use Grpc\ChannelCredentials;
use OpenTelemetry\Contrib\Otlp\ExporterTrait;
Expand All @@ -12,6 +13,9 @@
use Opentelemetry\Proto\Collector\Trace\V1\TraceServiceClient;
use OpenTelemetry\SDK\Common\Environment\KnownValues as Values;
use OpenTelemetry\SDK\Common\Environment\Variables as Env;
use OpenTelemetry\SDK\Common\Event\Dispatcher;
use OpenTelemetry\SDK\Common\Event\Event\ErrorEvent;
use OpenTelemetry\SDK\Common\Event\Event\WarningEvent;
use OpenTelemetry\SDK\Trace\Behavior\SpanExporterTrait;
use OpenTelemetry\SDK\Trace\SpanExporterInterface;

Expand Down Expand Up @@ -140,12 +144,12 @@ protected function doExport(iterable $spans): int
\Grpc\STATUS_DATA_LOSS,
\Grpc\STATUS_UNAUTHENTICATED,
], true)) {
self::logWarning('Retryable error exporting grpc span', ['error' => $error]);
Dispatcher::getInstance()->dispatch(new WarningEvent('Retryable error exporting grpc span', new Exception($error['error'], $error['code'])));

return self::STATUS_FAILED_RETRYABLE;
}

self::logError('Error exporting grpc span', ['error' => $error]);
Dispatcher::getInstance()->dispatch(new ErrorEvent('Error exporting grpc span', new Exception($error['error'])));

return self::STATUS_FAILED_NOT_RETRYABLE;
}
Expand Down
39 changes: 39 additions & 0 deletions src/SDK/Common/Event/Dispatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Common\Event;

use OpenTelemetry\SDK\Common\Event\Handler\DebugEventHandler;
use OpenTelemetry\SDK\Common\Event\Handler\ErrorEventHandler;
use OpenTelemetry\SDK\Common\Event\Handler\WarningEventHandler;
use Psr\EventDispatcher\EventDispatcherInterface;

class Dispatcher
{
private static ?EventDispatcherInterface $instance = null;

public static function getInstance(): EventDispatcherInterface
{
if (self::$instance === null) {
$dispatcher = new SimpleDispatcher(new SimpleListenerProvider());
$dispatcher->listen(EventType::ERROR, new ErrorEventHandler());
$dispatcher->listen(EventType::WARNING, new WarningEventHandler());
$dispatcher->listen(EventType::DEBUG, new DebugEventHandler());

self::$instance = $dispatcher;
}

return self::$instance;
}

public static function setInstance(EventDispatcherInterface $dispatcher): void
{
self::$instance = $dispatcher;
}

public static function unset(): void
{
self::$instance = null;
}
}
27 changes: 27 additions & 0 deletions src/SDK/Common/Event/Event/DebugEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Common\Event\Event;

class DebugEvent
{
private string $message;
private array $extra;

public function __construct(string $message, array $extra = [])
{
$this->message = $message;
$this->extra = $extra;
}

public function getMessage(): string
{
return $this->message;
}

public function getExtra(): array
{
return $this->extra;
}
}
33 changes: 33 additions & 0 deletions src/SDK/Common/Event/Event/ErrorEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Common\Event\Event;

use OpenTelemetry\SDK\Common\Event\StoppableEventTrait;
use Psr\EventDispatcher\StoppableEventInterface;
use Throwable;

class ErrorEvent implements StoppableEventInterface
{
use StoppableEventTrait;

protected string $message;
protected Throwable $error;

public function __construct(string $message, Throwable $error)
{
$this->message = $message;
$this->error = $error;
}

public function getError(): Throwable
{
return $this->error;
}

public function getMessage(): string
{
return $this->message;
}
}
38 changes: 38 additions & 0 deletions src/SDK/Common/Event/Event/WarningEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Common\Event\Event;

use OpenTelemetry\SDK\Common\Event\StoppableEventTrait;
use Psr\EventDispatcher\StoppableEventInterface;
use Throwable;

class WarningEvent implements StoppableEventInterface
{
use StoppableEventTrait;

protected string $message;
protected ?Throwable $error = null;

public function __construct(string $message, ?Throwable $error = null)
{
$this->message = $message;
$this->error = $error;
}

public function getError(): ?Throwable
{
return $this->error;
}

public function hasError(): bool
{
return $this->error !== null;
}

public function getMessage(): string
{
return $this->message;
}
}
16 changes: 16 additions & 0 deletions src/SDK/Common/Event/EventType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Common\Event;

use OpenTelemetry\SDK\Common\Event\Event\DebugEvent;
use OpenTelemetry\SDK\Common\Event\Event\ErrorEvent;
use OpenTelemetry\SDK\Common\Event\Event\WarningEvent;

class EventType
{
public const ERROR = ErrorEvent::class;
public const WARNING = WarningEvent::class;
public const DEBUG = DebugEvent::class;
}
18 changes: 18 additions & 0 deletions src/SDK/Common/Event/Handler/DebugEventHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Common\Event\Handler;

use OpenTelemetry\SDK\Behavior\LogsMessagesTrait;
use OpenTelemetry\SDK\Common\Event\Event\DebugEvent;

class DebugEventHandler
{
use LogsMessagesTrait;

public function __invoke(DebugEvent $event): void
{
self::logDebug($event->getMessage(), $event->getExtra());
}
}
18 changes: 18 additions & 0 deletions src/SDK/Common/Event/Handler/ErrorEventHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Common\Event\Handler;

use OpenTelemetry\SDK\Behavior\LogsMessagesTrait;
use OpenTelemetry\SDK\Common\Event\Event\ErrorEvent;

class ErrorEventHandler
{
use LogsMessagesTrait;

public function __invoke(ErrorEvent $event): void
{
self::logError($event->getError()->getMessage(), ['error' => $event->getError()]);
brettmc marked this conversation as resolved.
Show resolved Hide resolved
}
}
18 changes: 18 additions & 0 deletions src/SDK/Common/Event/Handler/WarningEventHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Common\Event\Handler;

use OpenTelemetry\SDK\Behavior\LogsMessagesTrait;
use OpenTelemetry\SDK\Common\Event\Event\WarningEvent;

class WarningEventHandler
{
use LogsMessagesTrait;

public function __invoke(WarningEvent $event): void
{
self::logWarning($event->getMessage(), ['error' => $event->getError()]);
}
}
Loading