PHP code example of easyswoole / trace

1. Go to this page and download the library: Download easyswoole/trace library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

easyswoole / trace example snippets




asySwoole\Trace\Trace;
use EasySwoole\Trace\Utility;

$trace = new Trace('http-request', params: [
    'method' => 'GET',
    'uri' => '/users/10001',
]);

$controller = $trace->next('UserController::profile');
$service = $controller->next('UserService::profile', [
    'user_id' => 10001,
]);

$orders = $service->parallel('order-service');
$coupons = $service->parallel('coupon-service');

$orders->endCall();
$coupons->endCall();
$service->endCall();
$controller->endCall();
$trace->endCall();

echo Utility::toTree($trace), PHP_EOL;

$controller = $trace->next('controller');
$service = $controller->next('service');
$database = $service->next('database');

$trace->next('first');
$trace->next('second'); // LogicException

$serviceA = $point->parallel('service-a');
$serviceB = $point->parallel('service-b');
$serviceC = $point->parallel('service-c');

$next = $point->next('next-call');
$parallel1 = $point->parallel('parallel-1');
$parallel2 = $point->parallel('parallel-2');

use EasySwoole\Trace\TraceStatus;

TraceStatus::Running;
TraceStatus::Success;
TraceStatus::Exception;
TraceStatus::Timeout;
TraceStatus::Abandoned;

$point->endCall();
// 等价于:
$point->endCall(TraceStatus::Success);

try {
    callRemoteService();
    $point->endCall();
} catch (Throwable $error) {
    $point->fail($error);
    throw $error;
}

$point->timeout('deadline exceeded');

$parallel1 = $point->parallel('service-1');
$parallel2 = $point->parallel('service-2');

$parallel1->endCall();
// parallel2 没有正常结束。

$point->endCall(); // parallel2 自动变为 abandoned

$point = $trace->next('query-user', [
    'user_id' => 10001,
    'filters' => [
        'status' => 'active',
    ],
]);

$point->setParams([
    'user_id' => 10002,
]);

$params = $point->params();

$paramsJson = json_encode(
    $span->params,
    JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR,
);

$trace->traceId();
$point->spanId();
$point->parentSpanId();

$trace = new Trace(
    name: 'incoming-request',
    traceId: $incomingTraceId,
    parentSpanId: $incomingSpanId,
);

/**
 * @return null|array{trace_id: string, parent_span_id: string, flags: string}
 */
function parseTraceparent(?string $header): ?array
{
    if ($header === null) {
        return null;
    }

    $header = strtolower(trim($header));
    if (!preg_match(
        '/^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/',
        $header,
        $matches,
    )) {
        return null;
    }

    // W3C 不允许全零 Trace ID 或 Span ID。
    if (trim($matches[1], '0') === '' || trim($matches[2], '0') === '') {
        return null;
    }

    return [
        'trace_id' => $matches[1],
        'parent_span_id' => $matches[2],
        'flags' => $matches[3],
    ];
}

function buildTraceparent(
    string $traceId,
    string $parentSpanId,
    string $flags = '01',
): string {
    return sprintf('00-%s-%s-%s', $traceId, $parentSpanId, $flags);
}

use EasySwoole\Trace\Trace;

$incoming = parseTraceparent(
    $request->getHeaderLine('traceparent'),
);

if ($incoming === null) {
    $trace = new Trace('user-service.request', params: [
        'method' => $request->getMethod(),
        'route' => '/users/{id}',
    ]);
    $traceFlags = '01';
} else {
    $trace = new Trace(
        name: 'user-service.request',
        traceId: $incoming['trace_id'],
        parentSpanId: $incoming['parent_span_id'],
        params: [
            'method' => $request->getMethod(),
            'route' => '/users/{id}',
        ],
    );
    $traceFlags = $incoming['flags'];
}

$controller = $trace->next('UserController::profile');

$orderRequest = $controller->parallel('HTTP GET order-service', [
    'method' => 'GET',
    'host' => 'order-service',
    'route' => '/orders',
]);

$headers = [
    'traceparent' => buildTraceparent(
        $trace->traceId(),
        $orderRequest->spanId(),
        $traceFlags,
    ),
];

try {
    $response = $httpClient->get(
        'http://order-service/orders?user_id=10001',
        headers: $headers,
    );

    if ($response->getStatusCode() >= 500) {
        $orderRequest->fail(
            new RuntimeException('order-service returned 5xx'),
        );
    } else {
        $orderRequest->endCall();
    }
} catch (Throwable $error) {
    $orderRequest->fail($error);
    throw $error;
}

$incoming = parseTraceparent(
    $request->getHeaderLine('traceparent'),
);

$trace = $incoming === null
    ? new Trace('order-service.request')
    : new Trace(
        name: 'order-service.request',
        traceId: $incoming['trace_id'],
        parentSpanId: $incoming['parent_span_id'],
    );

$controller = $trace->next('OrderController::list');
$database = $controller->next('query-orders', [
    'user_id' => 10001,
]);

try {
    $orders = queryOrders(10001);
    $database->endCall();
    $controller->endCall();
    $trace->endCall();
} catch (Throwable $error) {
    $database->fail($error);
    $controller->fail($error);
    $trace->fail($error);
    throw $error;
}

use EasySwoole\Trace\Bean\SpanBean;

$span = $point->toSpanBean();

/** @var SpanBean[] $spans */
$spans = $trace->spans();

$current = $point->spans(onlySelf: true);

$row = $span->toArray();

if (!$trace->isPersisted()) {
    $repository->insertMany($trace->spans());
    $trace->markPersisted();
}

use EasySwoole\Trace\TracePoint;

$trace->setAutoPersist(
    static function (TracePoint $point) use ($repository): void {
        $repository->insertMany(
            $point->spans(onlySelf: true),
        );
    },
);

// 在外层入库前先建立一个仍处于运行状态的协程范围。
$asyncScope = $trace->parallel('async-scope');

// 外层先批量写入当前已经存在的节点。
$repository->insertMany($trace->spans());
$trace->markPersisted();

// 内部协程之后创建的新节点默认是未持久化状态,
// 并且已经继承 auto-persist callback。
$latePoint = $asyncScope->next('late-coroutine');

$point->persistIfNeeded();

$didPersist = $point->persistIfNeeded();

use EasySwoole\Trace\Bean\SpanBean;
use EasySwoole\Trace\Utility;

$rows = $repository->findByTraceId(
    '4b0b2f5d54bb61e080ecce28ded4bc6a',
);

$beans = array_map(
    static fn (array $row): SpanBean => SpanBean::fromArray($row),
    $rows,
);

$trace = Utility::restoreFromBeans($beans);

echo Utility::toTree($trace), PHP_EOL;

$tree = $trace->toArray();

echo json_encode(
    $trace,
    JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR,
);

$point->next($name, $params);          // 创建唯一串行后继节点
$point->parallel($name, $params);      // 创建并行节点
$point->endCall();                     // 成功结束
$point->fail($throwable);              // 异常结束
$point->timeout($message);             // 超时结束
$point->setParams($params);            // 替换节点参数
$point->params();                      // 获取参数
$point->status();                      // 获取 TraceStatus
$point->children();                    // 获取直接子节点
$point->toSpanBean();                  // 转换当前节点
$point->spans();                       // 当前节点及所有后代 Bean
$point->spans(onlySelf: true);         // 仅当前节点 Bean
$point->toArray();                     // 树形数组
$point->setAutoPersist($callback);     // 设置自动补写回调
$point->persistIfNeeded();             // 主动执行补写
$point->isPersisted();                 // 是否已持久化
$point->markPersisted();               // 标记已持久化

Utility::restoreFromBeans($beans);     // 从 Bean 恢复调用树
Utility::toTree($trace);               // 输出文本调用树
text
traceparent: 00-4b0b2f5d54bb61e080ecce28ded4bc6a-00f067aa0ba902b7-01