Телеметрія
Телеметрія Concept Core — збір метрик і відміток життєвого циклу
(bootstrap, HTTP, шаблони, SQL) для debug і профілювання. Це не шина доменних подій:
ядро не надає EventDispatcherInterface для бізнес-подій на кшталт
UserRegistered — таку логіку додають у власному service provider (PSR-14, Symfony тощо).
Див. також: Service providers → TelemetryTrait, життєвий цикл запиту, конфігурація.
Увімкнення
Ключ telemetry.enabled (ConfigKey::TELEMETRY_ENABLED):
// config/telemetry.php
return [
'telemetry' => [
'enabled' => false,
'db_queries' => false,
'logs' => false,
],
];
// config/dev/telemetry.php — override для APP_ENV=dev
return [
'telemetry' => [
'enabled' => true,
'db_queries' => true,
'logs' => false,
],
];
За замовчуванням у skeleton — false; у dev-override — true.
Поки прапорець вимкнено, TelemetryTrait::telemetry() повертає null
і ядро не записує події (окрім місць, де колектор передають явно — див. нижче).
Компоненти телеметрії
| Клас | Роль |
|---|---|
TelemetryServiceProvider |
Реєструє shared TelemetryCollector у контейнері |
TelemetryCollector |
Зберігання подій: start(), finish(), record(), mark(), items() |
TelemetryTrait |
Доступ до колектора з урахуванням telemetry.enabled (у провайдерах, RouteStrategy) |
TelemetryEvent |
Константи імен подій (рядкові ідентифікатори) |
TelemetryItem |
Один запис: контекст, started_at, finished_at, duration |
TelemetryLogHandler |
Monolog handler: дзеркалить записи логів у LOG_RECORDED за telemetry.logs |
TelemetryServiceProvider стоїть рано в bootstrap/providers/app.php
(після ConfigServiceProvider і LocaleServiceProvider), щоб інші
провайдери могли писати телеметрію під час register().
Вбудовані записи
Ідентифікатори в Concept\Core\Services\Telemetry\TelemetryEvent:
| Константа | Значення | Коли записується |
|---|---|---|
FRAMEWORK_SERVICE_AWAKENING |
framework.service_awakening |
Перше створення сервісу в провайдері (mark з ім’ям класу) |
FRAMEWORK_COMPONENT_REGISTERED |
framework.component_registered |
ComponentsServiceProvider::boot() — кожен активний компонент |
FRAMEWORK_ROUTES_REGISTERED |
framework.routes_registered |
HttpServiceProvider після завантаження route-файлів з routes.list |
HTTP_ROUTE_INTERCEPTOR_EXECUTED |
http.route_interceptor_executed |
Кожен route interceptor у RouteStrategy::runInterceptors() |
HTTP_ROUTE_CALLABLE_INVOKED |
http.route_callable_invoked |
Виклик handler у RouteStrategy (start/finish) |
HTTP_REQUEST_HANDLED |
http.request_handled |
Кінець App::run() — method, path, memory, загальна тривалість |
TPL_RENDERED |
tpl.rendered |
Рендер Twig у TwigView::render() (ім’я шаблону, тривалість) |
DB_QUERY_EXECUTED |
db.query_executed |
SQL listener у DatabaseServiceProvider (якщо telemetry.db_queries) |
LOG_RECORDED |
log.recorded |
Запис Monolog через TelemetryLogHandler (якщо telemetry.logs) |
HTTP_FORM_REQUEST_VALIDATED |
http.form_request_validated |
Зарезервовано в API; наразі ядро не емітує цей запис |
API колектора
mark() — миттєва відмітка
Для точкових відміток без вимірювання тривалості (пробудження сервісу):
$this->telemetry()?->mark(
TelemetryEvent::FRAMEWORK_SERVICE_AWAKENING,
SessionInterface::class
);
start() / finish() — span
$id = $this->telemetry()?->start(TelemetryEvent::HTTP_ROUTE_CALLABLE_INVOKED, [
'route' => $route,
'handler' => 'UserController::index',
]);
try {
// ...
} finally {
$this->telemetry()?->finish(TelemetryEvent::HTTP_ROUTE_CALLABLE_INVOKED, (string) $id);
}
record() — миттєвий span
Поєднує start() і finish() в одному виклику (SQL, routes, підсумок HTTP):
$this->telemetry()?->record(
TelemetryEvent::DB_QUERY_EXECUTED,
[TelemetryKey::SQL => $query->sql],
$query->time / 1000
);
Читання даних
/** @var TelemetryCollector $telemetry */
$telemetry = $container->get(TelemetryCollector::class);
$services = $telemetry->items(TelemetryEvent::FRAMEWORK_SERVICE_AWAKENING);
$all = $telemetry->toArray();
Колектор — singleton на запит. Після завершення HTTP-запиту дані не зберігаються між запитами (якщо застосунок не експортує їх сам, наприклад у DebugBar).
TelemetryTrait у провайдерах
Провайдери ядра (HttpServiceProvider, SessionServiceProvider,
DatabaseServiceProvider тощо) використовують TelemetryTrait:
private function telemetry(): ?TelemetryCollector
{
// 1. Є TelemetryCollector у контейнері?
// 2. telemetry.enabled === true?
// 3. Інакше — null (запис пропускається)
}
У власному bootable-провайдері можна підключити той самий trait, якщо клас має
getContainer() (як у AbstractServiceProvider).
Приклад: DebugBar
Компонент DebugBar читає TelemetryCollector і показує віджети
«Services», handler маршруту, останній відрендерений шаблон:
// ServicesDataCollector
$items = $this->telemetryCollector->items(TelemetryEvent::FRAMEWORK_SERVICE_AWAKENING);
foreach ($items as $item) {
$className = $item->getContext()['name'] ?? 'Unknown';
// ...
}
Увімкніть telemetry.enabled і компонент DebugBarComponent
у config/dev/components.php.
Власні записи
У сервісі або middleware застосунку:
public function __construct(
private readonly TelemetryCollector $telemetry,
private readonly ConfigInterface $config,
) {}
public function heavyOperation(): void
{
if (!$this->config->getBool('telemetry.enabled')) {
return;
}
$id = $this->telemetry->start('app.heavy_operation', ['step' => 'import']);
try {
// ...
} finally {
$this->telemetry->finish('app.heavy_operation', $id);
}
}
Префікс імені (app.) — домовленість застосунку; ядро не валідує довільні рядки.
Для констант у своєму коді створіть клас на кшталт AppTelemetryEvent.
Чого немає в ядрі
- Доменні події — реєстрація listener/subscriber для бізнес-подій; підключайте PSR-14 або Symfony EventDispatcher у власному провайдері
-
EventServiceProvider— такого класу в concept-core немає (див. життєвий цикл) -
Логування SQL vs телеметрія —
log.db_queriesпише уquery.log;telemetry.db_queriesнаповнюєTelemetryCollector. Прапорці незалежні.
Телеметрія призначена для debug і профілювання, не для бізнес-логіки. Не покладайтеся на неї в production-коді як на механізм інтеграції між модулями.