This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/skywalking-php.git
The following commit(s) were added to refs/heads/master by this push: new c23e76a Inject skywalking context. (#107) c23e76a is described below commit c23e76a0028e09924daebfc3949dc05a60d0b609 Author: jmjoy <jm...@apache.org> AuthorDate: Wed Feb 21 10:20:55 2024 +0800 Inject skywalking context. (#107) --- docs/en/configuration/context-injection.md | 44 ++++++++++++++++++++++ docs/en/configuration/ini-settings.md | 1 + docs/menu.yml | 2 + src/lib.rs | 7 ++++ src/module.rs | 4 ++ src/request.rs | 60 ++++++++++++++++++++++++++++-- 6 files changed, 114 insertions(+), 4 deletions(-) diff --git a/docs/en/configuration/context-injection.md b/docs/en/configuration/context-injection.md new file mode 100644 index 0000000..3bb1eca --- /dev/null +++ b/docs/en/configuration/context-injection.md @@ -0,0 +1,44 @@ +# Context injection + +If you want to fetch the SkyWalking Context in your PHP code, which is super helpful for debugging and observability, +You can enable the configuration item `skywalking_agent.inject_context`. + +## Description + +`skywalking_agent.inject_context` + +Whether to enable automatic injection of skywalking context variables (such as `SW_TRACE_ID`). For `php-fpm` mode, it will be injected into the `$_SERVER` variable. For `swoole` mode, it will be injected into the `$request->server` variable. + +## Configuration + +```ini +[skywalking_agent] +extension = skywalking_agent.so +skywalking_agent.inject_context = On +``` + +## Usage + +For `php-fpm` mode: + +```php +<?php + +echo $_SERVER["SW_SERVICE_NAME"]; // get service name +echo $_SERVER["SW_INSTANCE_NAME"]; // get instance name +echo $_SERVER["SW_TRACE_ID"]; // get trace id +``` + +For `swoole` mode: + +```php +<?php + +$http = new Swoole\Http\Server('127.0.0.1', 9501); + +$http->on('request', function ($request, $response) { + echo $request->server["SW_SERVICE_NAME"]; // get service name + echo $request->server["SW_INSTANCE_NAME"]; // get instance name + echo $request->server["SW_TRACE_ID"]; // get trace id +}); +``` diff --git a/docs/en/configuration/ini-settings.md b/docs/en/configuration/ini-settings.md index b8a24fd..f437b6e 100644 --- a/docs/en/configuration/ini-settings.md +++ b/docs/en/configuration/ini-settings.md @@ -23,3 +23,4 @@ This is the configuration list supported in `php.ini`. | skywalking_agent.reporter_type | Reporter type, optional values are `grpc` and `kafka`. | grpc | | skywalking_agent.kafka_bootstrap_servers | A list of host/port pairs to use for connect to the Kafka cluster. Only available when `reporter_type` is `kafka`. | | | skywalking_agent.kafka_producer_config | Configure Kafka Producer configuration in JSON format `{"key": "value}`. Only available when `reporter_type` is `kafka`. | {} | +| skywalking_agent.inject_context | Whether to enable automatic injection of skywalking context variables (such as `SW_TRACE_ID`). For `php-fpm` mode, it will be injected into the `$_SERVER` variable. For `swoole` mode, it will be injected into the `$request->server` variable. | Off | diff --git a/docs/menu.yml b/docs/menu.yml index 723db61..f7f9e1a 100644 --- a/docs/menu.yml +++ b/docs/menu.yml @@ -28,6 +28,8 @@ catalog: path: "/en/configuration/ini-settings" - name: "Zend Observer" path: "/en/configuration/zend-observer" + - name: "Context injection" + path: "/en/configuration/context-injection" - name: "Reporter" catalog: - name: "Kafka Reporter" diff --git a/src/lib.rs b/src/lib.rs index 25a56a0..28ba16f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,6 +100,12 @@ const SKYWALKING_AGENT_KAFKA_BOOTSTRAP_SERVERS: &str = "skywalking_agent.kafka_b /// Only available when the reporter type is `kafka`. const SKYWALKING_AGENT_KAFKA_PRODUCER_CONFIG: &str = "skywalking_agent.kafka_producer_config"; +/// Whether to enable automatic injection of skywalking context variables (such +/// as `SW_TRACE_ID`). For `php-fpm` mode, it will be injected into the +/// `$_SERVER` variable. For `swoole` mode, it will be injected into the +/// `$request->server` variable. +const SKYWALKING_AGENT_INJECT_CONTEXT: &str = "skywalking_agent.inject_context"; + #[php_get_module] pub fn get_module() -> Module { let mut module = Module::new( @@ -180,6 +186,7 @@ pub fn get_module() -> Module { "{}".to_string(), Policy::System, ); + module.add_ini(SKYWALKING_AGENT_INJECT_CONTEXT, false, Policy::System); // Hooks. module.on_module_init(module::init); diff --git a/src/module.rs b/src/module.rs index 93f0cc6..6d5d865 100644 --- a/src/module.rs +++ b/src/module.rs @@ -129,6 +129,9 @@ pub static KAFKA_BOOTSTRAP_SERVERS: Lazy<String> = pub static KAFKA_PRODUCER_CONFIG: Lazy<String> = Lazy::new(|| get_str_ini_with_default(SKYWALKING_AGENT_KAFKA_PRODUCER_CONFIG)); +pub static INJECT_CONTEXT: Lazy<bool> = + Lazy::new(|| ini_get::<bool>(SKYWALKING_AGENT_INJECT_CONTEXT)); + /// For PHP 8.2+, zend observer api are now also called for internal functions. /// /// Refer to this commit: <https://github.com/php/php-src/commit/625f1649639c2b9a9d76e4d42f88c264ddb8447d> @@ -160,6 +163,7 @@ pub fn init() { Lazy::force(&REPORTER_TYPE); Lazy::force(&KAFKA_BOOTSTRAP_SERVERS); Lazy::force(&KAFKA_PRODUCER_CONFIG); + Lazy::force(&INJECT_CONTEXT); if let Err(err) = try_init_logger() { eprintln!("skywalking_agent: initialize logger failed: {}", err); diff --git a/src/request.rs b/src/request.rs index e09ab35..0bddbc9 100644 --- a/src/request.rs +++ b/src/request.rs @@ -16,7 +16,7 @@ use crate::{ component::COMPONENT_PHP_ID, context::RequestContext, - module::{is_enable, SKYWALKING_VERSION}, + module::{is_enable, INJECT_CONTEXT, SKYWALKING_VERSION}, util::{catch_unwind_result, get_sapi_module_name, z_val_to_string}, }; use anyhow::{anyhow, Context}; @@ -32,6 +32,10 @@ use std::{ use tracing::{error, instrument, trace, warn}; use url::Url; +const INJECT_CONTEXT_SERVICE_NAME: &str = "SW_SERVICE_NAME"; +const INJECT_CONTEXT_INSTANCE_NAME: &str = "SW_INSTANCE_NAME"; +const INJECT_CONTEXT_TRACE_ID: &str = "SW_TRACE_ID"; + #[instrument(skip_all)] pub fn init() { if !is_enable() { @@ -65,7 +69,9 @@ fn request_init_for_fpm() -> crate::Result<()> { let url = get_page_request_url(server)?; let method = get_page_request_method(server); - create_request_context(None, header.as_deref(), &method, &url) + create_request_context(None, header.as_deref(), &method, &url)?; + + inject_server_var_for_fpm() } fn request_shutdown_for_fpm() -> crate::Result<()> { @@ -74,6 +80,15 @@ fn request_shutdown_for_fpm() -> crate::Result<()> { finish_request_context(None, status_code) } +fn inject_server_var_for_fpm() -> crate::Result<()> { + if *INJECT_CONTEXT { + let server = get_mut_page_request_server()?; + inject_server_var(None, server)?; + } + + Ok(()) +} + #[allow(clippy::useless_conversion)] fn jit_initialization() { unsafe { @@ -145,6 +160,17 @@ fn get_page_request_server<'a>() -> anyhow::Result<&'a ZArr> { } } +fn get_mut_page_request_server<'a>() -> anyhow::Result<&'a mut ZArr> { + unsafe { + let symbol_table = ZArr::from_mut_ptr(&mut eg!(symbol_table)); + let carrier = symbol_table + .get_mut("_SERVER") + .and_then(|carrier| carrier.as_mut_z_arr()) + .context("$_SERVER is null")?; + Ok(carrier) + } +} + pub const HACK_SWOOLE_ON_REQUEST_FUNCTION_NAME: &str = "skywalking_hack_swoole_on_request_please_do_not_use"; @@ -193,7 +219,9 @@ pub fn skywalking_hack_swoole_on_request(args: &mut [ZVal]) -> phper::Result<ZVa } fn request_init_for_swoole(request: &mut ZVal) -> crate::Result<()> { - let request = request.as_z_obj().context("swoole request isn't object")?; + let request = request + .as_mut_z_obj() + .context("swoole request isn't object")?; let fd = request .get_property("fd") @@ -215,7 +243,14 @@ fn request_init_for_swoole(request: &mut ZVal) -> crate::Result<()> { let method = get_swoole_request_method(server); let url = get_swoole_request_url(server, headers)?; - create_request_context(Some(fd), header.as_deref(), &method, &url) + create_request_context(Some(fd), header.as_deref(), &method, &url)?; + + let server = request + .get_mut_property("server") + .as_mut_z_arr() + .context("swoole request server not exists")?; + + inject_server_var_for_swoole(Some(fd), server) } fn request_shutdown_for_swoole(response: &mut ZVal) -> crate::Result<()> { @@ -237,6 +272,14 @@ fn request_shutdown_for_swoole(response: &mut ZVal) -> crate::Result<()> { ) } +fn inject_server_var_for_swoole(request_id: Option<i64>, server: &mut ZArr) -> crate::Result<()> { + if *INJECT_CONTEXT { + inject_server_var(request_id, server)?; + } + + Ok(()) +} + fn get_swoole_request_header(header: &ZArr) -> Option<String> { if *SKYWALKING_VERSION >= 8 { header @@ -332,3 +375,12 @@ fn finish_request_context(request_id: Option<i64>, status_code: i32) -> crate::R Ok(()) } + +fn inject_server_var(request_id: Option<i64>, server: &mut ZArr) -> crate::Result<()> { + Ok(RequestContext::try_with_global_ctx(request_id, |ctx| { + server.insert(INJECT_CONTEXT_SERVICE_NAME, ctx.service()); + server.insert(INJECT_CONTEXT_INSTANCE_NAME, ctx.service_instance()); + server.insert(INJECT_CONTEXT_TRACE_ID, ctx.trace_id()); + Ok(()) + })?) +}