diff --git a/src/Instrumentation/WithSpanHandler.php b/src/Instrumentation/WithSpanHandler.php
new file mode 100644
index 0000000..a993660
--- /dev/null
+++ b/src/Instrumentation/WithSpanHandler.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\opentelemetry\Instrumentation;
+
+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 Throwable;
+
+/**
+ * Custom pre-hook and post-hook handlers for attribute-based auto instrumentation.
+ */
+class WithSpanHandler  {
+  /**
+   * @psalm-suppress ArgumentTypeCoercion
+   */
+  public static function pre(mixed $target, array $params, ?string $class, string $function, ?string $filename, ?int $lineno, ?array $span_args = [], ?array $attributes = []): void {
+
+    $name = $span_args['name'] ?? null;
+    if ($name === null) {
+      $name = empty($class)
+        ? $function
+        : sprintf('%s::%s', basename(str_replace('\\', '/', $class)), $function);
+    }
+
+    $kind = $span_args['span_kind'] ?? SpanKind::KIND_INTERNAL;
+
+    if (\Drupal::service('module_handler')->moduleExists('opentelemetry')) {
+      $tracer = \Drupal::service('opentelemetry')->getTracer();
+      $span = $tracer->spanBuilder($name)
+        ->setSpanKind($kind)
+        ->setAttribute('code.function', $function)
+        ->setAttribute('code.namespace', $class)
+        ->setAttribute('code.filepath', $filename)
+        ->setAttribute('code.lineno', $lineno)
+        ->setAttributes($attributes ?? [])
+        ->startSpan();
+      $context = $span->storeInContext(Context::getCurrent());
+      Context::storage()->attach($context);
+    }
+
+  }
+
+  public static function post(mixed $target, array $params, mixed $result, ?Throwable $exception): void {
+    $scope = Context::storage()->scope();
+    $scope?->detach();
+
+    if (!$scope || $scope->context() === Context::getCurrent()) {
+      return;
+    }
+
+    $span = Span::fromContext($scope->context());
+    if ($exception) {
+      $span->recordException($exception, ['exception.escaped' => true]);
+      $span->setStatus(StatusCode::STATUS_ERROR, $exception->getMessage());
+    }
+
+    $span->end();
+  }
+}
