Skip to content

Commit

Permalink
feat: add opensearch, twig, and shopware kernel instrumentations (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gaitholabi authored Aug 13, 2024
1 parent 7ad4a0d commit 5eb46ad
Show file tree
Hide file tree
Showing 13 changed files with 575 additions and 117 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_SERVICE_NAME=shopware # or any other name
```

This extension can be disabled via:
```bash
OTEL_PHP_DISABLED_INSTRUMENTATIONS=shopware
```

You will need to configure the exporter to send the data to a collector.

Here is an example with OTLP over gRPC:
Expand Down
10 changes: 9 additions & 1 deletion _register.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
use Shopware\OpenTelemetry\Instrumentation\ConnectionInstrumentation;
use Shopware\OpenTelemetry\Instrumentation\DALInstrumentation;
use Shopware\OpenTelemetry\Instrumentation\HttpClientInstrumentation;
use Shopware\OpenTelemetry\Instrumentation\OpenSearchInstrumentation;
use Shopware\OpenTelemetry\Instrumentation\ShopwareInstrumentation;
use Shopware\OpenTelemetry\Instrumentation\SymfonyInstrumentation;
use OpenTelemetry\SDK\Sdk;
use Shopware\OpenTelemetry\Instrumentation\TwigInstrumentation;

if (class_exists(Sdk::class) && Sdk::isInstrumentationDisabled(SymfonyInstrumentation::NAME) === true) {
if (class_exists(Sdk::class) && Sdk::isInstrumentationDisabled(ShopwareInstrumentation::NAME) === true) {
return;
}

Expand All @@ -21,8 +24,13 @@
return;
}

ShopwareInstrumentation::register();
SymfonyInstrumentation::register();
HttpClientInstrumentation::register();
DALInstrumentation::register();
ConnectionInstrumentation::register();
CommandInstrumentation::register();
TwigInstrumentation::register();
if (class_exists('OpenSearch\Client')) {
OpenSearchInstrumentation::register();
}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"ext-opentelemetry": "*",
"monolog/monolog": "2.* || 3.*",
"shopware/core": "*",
"open-telemetry/api": "^1.0.0beta10",
"open-telemetry/api": "^1.0.3",
"open-telemetry/sem-conv": "^1.22",
"symfony/http-kernel": "*",
"symfony/http-client-contracts": "*"
Expand Down
15 changes: 8 additions & 7 deletions src/Instrumentation/CommandInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@

use OpenTelemetry\API\Instrumentation\CachedInstrumentation;
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\API\Trace\StatusCode;
use OpenTelemetry\Context\Context;
use Opentelemetry\Proto\Trace\V1\Span\SpanKind;
use Opentelemetry\Proto\Trace\V1\Status\StatusCode;
use OpenTelemetry\SemConv\TraceAttributes;
use Symfony\Component\Console\Application;
use Throwable;

use function OpenTelemetry\Instrumentation\hook;

class CommandInstrumentation
{
public static function register()
public static function register(): void
{
hook(
class: Application::class,
Expand All @@ -30,7 +31,7 @@ function: 'doRunCommand',
$builder = (new CachedInstrumentation('io.opentelemetry.contrib.php.shopware'))
->tracer()
->spanBuilder(sprintf('bin/console %s', $params[0]->getName()))
->setSpanKind(SpanKind::SPAN_KIND_INTERNAL)
->setSpanKind(SpanKind::KIND_INTERNAL)
->setAttribute(TraceAttributes::CODE_FUNCTION, $function)
->setAttribute(TraceAttributes::CODE_NAMESPACE, $class)
->setAttribute(TraceAttributes::CODE_FILEPATH, $filename)
Expand All @@ -48,7 +49,7 @@ function: 'doRunCommand',
Application $application,
array $params,
mixed $ret,
?Throwable $exception
?Throwable $exception,
) {
$scope = Context::storage()->scope();
if (null === $scope) {
Expand All @@ -57,10 +58,10 @@ function: 'doRunCommand',
$scope->detach();
$span = Span::fromContext($scope->context());
if ($exception !== null) {
$span->setStatus(StatusCode::STATUS_CODE_ERROR, $exception->getMessage());
$span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage());
}
$span->end();
}
},
);
}
}
19 changes: 11 additions & 8 deletions src/Instrumentation/ConnectionInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,18 @@ public static function register(): void

if ($part === 'SELECT' && preg_match('/FROM\s*`?(\w*)`?/m', $query, $matches)) {
$spanTitle .= '.' . $matches[1];
} else if ($part === 'INSERT' && preg_match('/INTO\s*`?(\w*)`?/m', $query, $matches)) {
} elseif ($part === 'INSERT' && preg_match('/INTO\s*`?(\w*)`?/m', $query, $matches)) {
$spanTitle .= '.' . $matches[1];
} else if ($part === 'UPDATE' && preg_match('/UPDATE\s*`?(\w*)`?/m', $query, $matches)) {
} elseif ($part === 'UPDATE' && preg_match('/UPDATE\s*`?(\w*)`?/m', $query, $matches)) {
$spanTitle .= '.' . $matches[1];
} else if ($part === 'DELETE' && preg_match('/FROM\s*`?(\w*)`?/m', $query, $matches)) {
} elseif ($part === 'DELETE' && preg_match('/FROM\s*`?(\w*)`?/m', $query, $matches)) {
$spanTitle .= '.' . $matches[1];
}

$builder = (new CachedInstrumentation('io.opentelemetry.contrib.php.shopware'))
->tracer()
->spanBuilder($spanTitle)
->setSpanKind(SpanKind::KIND_SERVER)
->setSpanKind(SpanKind::KIND_CLIENT)
->setAttribute(TraceAttributes::DB_STATEMENT, $query)
->setAttribute(TraceAttributes::CODE_FUNCTION, $function)
->setAttribute(TraceAttributes::CODE_NAMESPACE, $class)
Expand All @@ -86,7 +86,7 @@ public static function register(): void
Statement $repository,
array $params,
mixed $ret,
?Throwable $exception
?Throwable $exception,
) {
$scope = Context::storage()->scope();
if (null === $scope) {
Expand All @@ -100,20 +100,23 @@ public static function register(): void
}

$span->end();
}
},
);
}

/**
* @return array<string, mixed>|null
*/
private static function getBacktrace(): ?array
{
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);

foreach ($backtrace as $trace) {
if (isset($trace['class']) && (
str_starts_with($trace['class'], 'Doctrine\\DBAL') ||
str_starts_with($trace['class'], 'Doctrine\\DBAL') ||
str_starts_with($trace['class'], 'Shopware\\OpenTelemetry\\') ||
str_starts_with($trace['class'], 'Shopware\Core\Framework\DataAbstractionLayer')
)) {
)) {
continue;
}

Expand Down
157 changes: 70 additions & 87 deletions src/Instrumentation/DALInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,102 +18,85 @@ final class DALInstrumentation
{
public static function register(): void
{
hook(
EntityRepository::class,
$methods = [
'search',
pre: static function (
EntityRepository $repository,
array $params,
string $class,
string $function,
?string $filename,
?int $lineno,
) {
$builder = (new CachedInstrumentation('io.opentelemetry.contrib.php.shopware'))
->tracer()
->spanBuilder($repository->getDefinition()->getEntityName() . '::search')
->setSpanKind(SpanKind::KIND_SERVER)
->setAttribute(TraceAttributes::CODE_FUNCTION, $function)
->setAttribute(TraceAttributes::CODE_NAMESPACE, $class)
->setAttribute(TraceAttributes::CODE_FILEPATH, $filename)
->setAttribute(TraceAttributes::CODE_LINENO, $lineno);

$parent = Context::getCurrent();

$span = $builder
->setParent($parent)
->startSpan();

Context::storage()->attach($span->storeInContext($parent));
},
post: static function (
EntityRepository $repository,
array $params,
?EntitySearchResult $response,
?\Throwable $exception
) {
$scope = Context::storage()->scope();
if (null === $scope) {
return;
}
$scope->detach();
$span = Span::fromContext($scope->context());
'aggregate',
'searchIds',
'update',
'upsert',
'create',
'delete',
'createVersion',
'merge',
'clone',
];

if ($exception) {
$span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage());
}
foreach ($methods as $method) {
hook(
EntityRepository::class,
$method,
pre: self::pre(),
post: self::post(),
);
}
}

$span->end();
/**
* @return \Closure
*/
public static function post(): \Closure
{
return static function (
EntityRepository $repository,
array $params,
mixed $return,
?\Throwable $exception,
) {
$scope = Context::storage()->scope();
if (null === $scope) {
return;
}
);
$scope->detach();
$span = Span::fromContext($scope->context());

hook(
EntityRepository::class,
'aggregate',
pre: static function (
EntityRepository $repository,
array $params,
string $class,
string $function,
?string $filename,
?int $lineno,
) {
$builder = (new CachedInstrumentation('io.opentelemetry.contrib.php.shopware'))
->tracer()
->spanBuilder($repository->getDefinition()->getEntityName() . '::aggregate')
->setSpanKind(SpanKind::KIND_SERVER)
->setAttribute(TraceAttributes::CODE_FUNCTION, $function)
->setAttribute(TraceAttributes::CODE_NAMESPACE, $class)
->setAttribute(TraceAttributes::CODE_FILEPATH, $filename)
->setAttribute(TraceAttributes::CODE_LINENO, $lineno);
if ($exception !== null) {
$span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage());
$span->recordException($exception);
}

$parent = Context::getCurrent();
$span->end();
};
}

$span = $builder
->setParent($parent)
->startSpan();
/**
* @return \Closure
*/
public static function pre(): \Closure
{
return static function (
EntityRepository $repository,
array $params,
string $class,
string $function,
?string $filename,
?int $lineno,
) {
$builder = (new CachedInstrumentation('io.opentelemetry.contrib.php.shopware.dal'))
->tracer()
->spanBuilder(sprintf('%s::%s', $repository->getDefinition()->getEntityName(), $function))
->setSpanKind(SpanKind::KIND_INTERNAL)
->setAttribute(TraceAttributes::CODE_FUNCTION, $function)
->setAttribute(TraceAttributes::CODE_NAMESPACE, $class)
->setAttribute(TraceAttributes::CODE_FILEPATH, $filename)
->setAttribute(TraceAttributes::CODE_LINENO, $lineno);

Context::storage()->attach($span->storeInContext($parent));
},
post: static function (
EntityRepository $repository,
array $params,
?AggregationResultCollection $response,
?\Throwable $exception
) {
$scope = Context::storage()->scope();
if (null === $scope) {
return;
}
$scope->detach();
$span = Span::fromContext($scope->context());
$parent = Context::getCurrent();

if ($exception) {
$span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage());
}
$span = $builder
->setParent($parent)
->startSpan();

$span->end();
}
);
Context::storage()->attach($span->storeInContext($parent));
};
}
}
6 changes: 4 additions & 2 deletions src/Instrumentation/HttpClientInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ public static function register(): void
?string $filename,
?int $lineno,
): array {
$method = is_string($params[0]) && strlen($params[0]) > 0 ? $params[0] : 'UNKNOWN_HTTP_METHOD';

$builder = (new CachedInstrumentation('io.opentelemetry.contrib.php.symfony_http'))
->tracer()
->spanBuilder(\sprintf('%s', $params[0]))
->spanBuilder($method)
->setSpanKind(SpanKind::KIND_CLIENT)
->setAttribute(TraceAttributes::URL_FULL, (string) $params[1])
->setAttribute(TraceAttributes::HTTP_REQUEST_METHOD, $params[0])
Expand Down Expand Up @@ -93,7 +95,7 @@ public static function register(): void
HttpClientInterface $client,
array $params,
?ResponseInterface $response,
?\Throwable $exception
?\Throwable $exception,
): void {
$scope = Context::storage()->scope();
if (null === $scope) {
Expand Down
Loading

0 comments on commit 5eb46ad

Please sign in to comment.