PHP中的可观测性:如何停止猜测并开始看透一切
\n\n当您的PHP应用程序开始变慢、出现500错误或行为异常时,第一反应是查看日志。但日志(即使结构完美)也只是拼图的一部分。现代开发需要更多:您需要知道每个请求执行了多长时间、哪些外部服务拖慢了系统,以及特定端点消耗了多少内存。
\n\n这一切都关乎可观测性。在本文中,我们将探讨PHP可观测性的三大支柱:追踪、指标和日志。您将了解如何通过代码示例设置真正的监控,并获得今天就可以实施的现成方案。
\n\n什么是可观测性,为什么PHP开发者需要它?
\n\n可观测性(O11y)是指通过系统的外部输出数据理解其内部状态的能力。在PHP世界中,这意味着您可以回答以下问题:
\n\n- \n
- 为什么某个特定的API请求花了5秒钟? \n
- 过去一小时内哪个SQL查询执行时间最长? \n
- 您的服务器现在每秒处理多少请求? \n
- 内存具体在哪里“崩溃”? \n
没有可观测性,您就是在盲目工作。有了它,您可以看到应用程序的每一个细节。是的,这不仅适用于Go或Java的微服务。PHP单体应用也可以并且应该被“观测”。
\n\nPHP中的追踪:使用OpenTelemetry进行分布式追踪
\n\n追踪是一个请求的历史记录。您可以看到请求如何通过您的应用程序、调用了哪些函数、调用了哪些外部API,以及每一步花费了多少时间。在PHP中,这使用的是OpenTelemetry标准。
\n\n为PHP安装OpenTelemetry
\n\n首先通过Composer安装必要的包:
\n\ncomposer require open-telemetry/opentelemetry\ncomposer require open-telemetry/transport-grpc\ncomposer require open-telemetry/exporter-otlp\n\n\n现在配置基本的追踪器。创建一个tracing.php文件:
// tracing.php\nuse OpenTelemetry\\API\\Globals;\nuse OpenTelemetry\\SDK\\Trace\\TracerProvider;\nuse OpenTelemetry\\SDK\\Trace\\SpanProcessor\\BatchSpanProcessor;\nuse OpenTelemetry\\SDK\\Trace\\Exporter\\ConsoleSpanExporter;\n\nrequire 'vendor/autoload.php';\n\n// 创建导出器(暂时输出到控制台)\n$exporter = new ConsoleSpanExporter();\n$processor = new BatchSpanProcessor($exporter);\n$tracerProvider = new TracerProvider($processor);\n\n// 注册全局追踪器\nGlobals::setTracerProvider($tracerProvider);\n\n// 获取以您的应用程序命名的追踪器\n$tracer = Globals::tracerProvider()->getTracer('my-php-app');\n\n\n在实际代码中创建跨度
\n\n假设您有一个处理订单的控制器。将关键操作包装成跨度:
\n\n// OrderController.php\nclass OrderController {\n public function createOrder(array $data) {\n $tracer = Globals::tracerProvider()->getTracer('order-service');\n \n // 主跨度——整个请求\n $span = $tracer->spanBuilder('POST /orders')->startSpan();\n $span->setAttribute('order.id', $data['id'] ?? 'unknown');\n \n try {\n // 用于验证用户的跨度\n $userSpan = $tracer->spanBuilder('validate.user')->startSpan();\n $user = $this->validateUser($data['user_id']);\n $userSpan->end();\n \n // 用于保存到数据库的跨度\n $dbSpan = $tracer->spanBuilder('save.order')->startSpan();\n $order = $this->saveOrder($data);\n $dbSpan->end();\n \n // 用于发送电子邮件的跨度\n $emailSpan = $tracer->spanBuilder('send.email')