Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,10 @@ jobs:
- name: Setup php-fpm for Linux
if: matrix.os == 'ubuntu-24.04'
run: |
sudo apt-get update
sudo apt-get update
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:ondrej/php
sudo apt-get update
sudo apt-get install -y php${{ matrix.flag.php_version }}-fpm
sudo ln -sf /usr/sbin/php-fpm${{ matrix.flag.php_version }} /usr/sbin/php-fpm
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ members = [
]

[workspace.package]
version = "1.1.0"
version = "1.2.0"
authors = ["Apache Software Foundation", "jmjoy <jmjoy@apache.org>", "Yanlong He <heyanlong@apache.org>"]
edition = "2024"
rust-version = "1.85"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

<img src="http://skywalking.apache.org/assets/logo.svg" alt="Sky Walking logo" height="90px" align="right" />

**SkyWalking PHP** The PHP Agent for Apache SkyWalking, which provides the native tracing abilities for PHP project.
**SkyWalking PHP** The PHP Agent for Apache SkyWalking, which provides native tracing and PHP Health Metrics (PHM)
runtime reporting for PHP projects.

**SkyWalking** an APM(application performance monitor) system, especially designed for
microservices, cloud native and container-based (Docker, Kubernetes, Mesos) architectures.
Expand Down
2 changes: 2 additions & 0 deletions docs/en/configuration/ini-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ This is the configuration list supported in `php.ini`.
| skywalking_agent.instance_name | Instance name. You can set `${HOSTNAME}`, refer to [Example #1](https://www.php.net/manual/en/install.fpm.configuration.php) | |
| skywalking_agent.standalone_socket_path | Unix domain socket file path of standalone skywalking php worker. Only available when `reporter_type` is `standalone`. | |
| skywalking_agent.psr_logging_level | The log level reported to SkyWalking, based on PSR-3, one of `Off`, `Debug`, `Info`, Notice`, Warning`, Error`, Critical`, Alert`, Emergency`. | Off |
| skywalking_agent.metrics_enable | Enable PHP Health Metrics (PHM) meter reporting via native MeterReportService. **Linux only** (requires `/proc`). Default **On** on Linux when the agent is active; default **Off** on macOS/Windows. Set to `Off` to disable on Linux. Reports six process meters: CPU utilization, memory used/peak, virtual memory, thread count, and open FD count. See [PHP agent README](../setup/service-agent/php-agent/README.md#php-health-metrics-phm). | On (Linux); Off (other) |
| skywalking_agent.metrics_report_period | PHM meter collection interval in seconds. Process meters are collected periodically by the reporter worker via `/proc`. **Linux only.** | 30 |
38 changes: 38 additions & 0 deletions docs/en/setup/service-agent/php-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,44 @@ Refer to the Configuration section for more configuration items.
> Enabling it by default will cause extra meaningless consumption when skywalking agent is not
> needed (such as simply executing a php script).

### PHP Health Metrics (PHM)

> **Platform:** PHM process meters are **Linux only**. The reporter worker reads the parent
> PHP-FPM process via `/proc` (`/proc/{pid}/status`, `stat`, and `fd`). They are not available
> on macOS or Windows. Trace and other agent features are unchanged.

PHM reports PHP runtime meters through the native Meter protocol (same transport as trace reporting).
Process CPU, memory, virtual memory, thread count, and open file descriptors are collected
periodically by the reporter worker via `/proc`, without requiring HTTP traffic,
similar to Python PVM and Ruby runtime meters.
**PHM is enabled by default on Linux** when the agent is active (`skywalking_agent.enable = On`).
To disable it or tune the interval, use `php.ini`:

```ini
; Disable PHM if not needed (default is On on Linux).
; skywalking_agent.metrics_enable = Off

; Report interval in seconds (default 30).
skywalking_agent.metrics_report_period = 30
```

PHM reports six process meters (aligned with OAP `php-runtime.yaml` and Horizon UI widgets):

| Agent meter name | OAP / UI expression | Source |
| --- | --- | --- |
| `instance_php_process_cpu_utilization` | `meter_instance_php_process_cpu_utilization` | `/proc/{pid}/stat` utime+stime delta |
| `instance_php_memory_used_mb` | `meter_instance_php_memory_used_mb` | `/proc/{pid}/status` VmRSS |
| `instance_php_memory_peak_mb` | `meter_instance_php_memory_peak_mb` | `/proc/{pid}/status` VmHWM |
| `instance_php_virtual_memory_mb` | `meter_instance_php_virtual_memory_mb` | `/proc/{pid}/status` VmSize |
| `instance_php_thread_count` | `meter_instance_php_thread_count` | `/proc/{pid}/status` Threads |
| `instance_php_open_fd_count` | `meter_instance_php_open_fd_count` | `/proc/{pid}/fd` count |

On the OAP side, activate the `php-runtime` entry in
`agent-analyzer.default.meterAnalyzerActiveFiles`. Horizon UI shows the widgets on the **General
Service → Instance** dashboard when data is available.

See [INI Settings](../../../configuration/ini-settings.md) for all PHM options.

## Run

Start `php-fpm` server:
Expand Down
17 changes: 17 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ const SKYWALKING_AGENT_STANDALONE_SOCKET_PATH: &str = "skywalking_agent.standalo
/// `Info`, Notice`, Warning`, Error`, Critical`, Alert`, Emergency`.
const SKYWALKING_AGENT_PSR_LOGGING_LEVEL: &str = "skywalking_agent.psr_logging_level";

/// Whether to report PHP Health Metrics (PHM) via native meter protocol.
/// Default is **On on Linux** when the agent extension is active (`/proc`
/// sampling only); **Off** on other platforms.
const SKYWALKING_AGENT_METRICS_ENABLE: &str = "skywalking_agent.metrics_enable";

/// PHM report period in seconds. Meters are sampled at most once per period.
const SKYWALKING_AGENT_METRICS_REPORT_PERIOD: &str = "skywalking_agent.metrics_report_period";

#[php_get_module]
pub fn get_module() -> Module {
let mut module = Module::new(
Expand Down Expand Up @@ -214,6 +222,15 @@ pub fn get_module() -> Module {
"".to_string(),
Policy::System,
);
#[cfg(target_os = "linux")]
module.add_ini(SKYWALKING_AGENT_METRICS_ENABLE, true, Policy::System);
#[cfg(not(target_os = "linux"))]
module.add_ini(SKYWALKING_AGENT_METRICS_ENABLE, false, Policy::System);
module.add_ini(
SKYWALKING_AGENT_METRICS_REPORT_PERIOD,
30i64,
Policy::System,
);

// Hooks.
module.on_module_init(module::init);
Expand Down
14 changes: 13 additions & 1 deletion src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ pub static PSR_LOGGING_LEVEL: Lazy<PsrLogLevel> = Lazy::new(|| {
.into()
});

pub static METRICS_ENABLE: Lazy<bool> =
Lazy::new(|| ini_get::<bool>(SKYWALKING_AGENT_METRICS_ENABLE));

pub static METRICS_REPORT_PERIOD: Lazy<i64> =
Lazy::new(|| ini_get::<i64>(SKYWALKING_AGENT_METRICS_REPORT_PERIOD));

pub fn init() {
if !is_enable() {
return;
Expand All @@ -193,6 +199,8 @@ pub fn init() {
Lazy::force(&KAFKA_PRODUCER_CONFIG);
Lazy::force(&INJECT_CONTEXT);
Lazy::force(&PSR_LOGGING_LEVEL);
Lazy::force(&METRICS_ENABLE);
Lazy::force(&METRICS_REPORT_PERIOD);

if let Err(err) = try_init_logger() {
eprintln!("skywalking_agent: initialize logger failed: {}", err);
Expand Down Expand Up @@ -239,7 +247,11 @@ pub fn init() {
reporter.clone(),
));

logger::set_global_logger(Logger::new(&*SERVICE_NAME, &*SERVICE_INSTANCE, reporter));
logger::set_global_logger(Logger::new(
&*SERVICE_NAME,
&*SERVICE_INSTANCE,
reporter.clone(),
));

// Hook functions.
register_execute_functions();
Expand Down
27 changes: 24 additions & 3 deletions src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@
// limitations under the License.

use crate::module::{
AUTHENTICATION, ENABLE_TLS, HEARTBEAT_PERIOD, PROPERTIES_REPORT_PERIOD_FACTOR, REPORTER_TYPE,
SERVER_ADDR, SERVICE_INSTANCE, SERVICE_NAME, SOCKET_FILE_PATH, SSL_CERT_CHAIN_PATH,
SSL_KEY_PATH, SSL_TRUSTED_CA_PATH, WORKER_THREADS, is_standalone_reporter_type,
AUTHENTICATION, ENABLE_TLS, HEARTBEAT_PERIOD, METRICS_ENABLE, METRICS_REPORT_PERIOD,
PROPERTIES_REPORT_PERIOD_FACTOR, REPORTER_TYPE, SERVER_ADDR, SERVICE_INSTANCE, SERVICE_NAME,
SOCKET_FILE_PATH, SSL_CERT_CHAIN_PATH, SSL_KEY_PATH, SSL_TRUSTED_CA_PATH, WORKER_THREADS,
is_standalone_reporter_type,
};
#[cfg(feature = "kafka-reporter")]
use crate::module::{KAFKA_BOOTSTRAP_SERVERS, KAFKA_PRODUCER_CONFIG};
#[cfg(feature = "kafka-reporter")]
use skywalking_php_worker::reporter::KafkaReporterConfiguration;
use skywalking_php_worker::{
HeartBeatConfiguration, WorkerConfiguration, new_tokio_runtime,
phm::PhmConfiguration,
reporter::{GrpcReporterConfiguration, ReporterConfiguration},
start_worker,
};
Expand Down Expand Up @@ -79,6 +81,7 @@ pub fn init_worker() {
properties_report_period_factor: *PROPERTIES_REPORT_PERIOD_FACTOR,
}),
reporter_config,
phm: phm_configuration(),
};

// Run the worker in subprocess.
Expand Down Expand Up @@ -106,3 +109,21 @@ fn worker_threads() -> usize {
worker_threads as usize
}
}

#[cfg(target_os = "linux")]
fn phm_configuration() -> Option<PhmConfiguration> {
if !*METRICS_ENABLE {
return None;
}
Some(PhmConfiguration {
service_name: SERVICE_NAME.clone(),
service_instance: SERVICE_INSTANCE.clone(),
report_period_secs: *METRICS_REPORT_PERIOD,
php_process_pid: unsafe { libc::getppid() as i32 },
})
}

#[cfg(not(target_os = "linux"))]
fn phm_configuration() -> Option<PhmConfiguration> {
None
}
11 changes: 10 additions & 1 deletion tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,17 @@ fn setup_php_fpm(index: usize, fpm_addr: &str) -> Child {
"-d",
"skywalking_agent.psr_logging_level=Warning",
];
let mut args: Vec<String> = args.iter().map(|s| (*s).to_string()).collect();
if index == 1 {
args.extend([
"-d".to_owned(),
"skywalking_agent.metrics_enable=On".to_owned(),
"-d".to_owned(),
"skywalking_agent.metrics_report_period=5".to_owned(),
]);
}
info!(cmd = args.join(" "), "start command");
let child = Command::new(args[0])
let child = Command::new(&args[0])
.args(&args[1..])
.stdin(Stdio::null())
.stdout(File::create("/tmp/fpm-skywalking-stdout.log").unwrap())
Expand Down
28 changes: 28 additions & 0 deletions tests/data/expected_context.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2059,3 +2059,31 @@ logItems:
- {key: bar, value: 'false'}
- {key: baz, value: test}
layer: ''
meterItems:
- serviceName: skywalking-agent-test-1
meterSize: ge 6
meters:
- meterId:
name: instance_php_process_cpu_utilization
tags: []
singleValue: ge 0
- meterId:
name: instance_php_memory_used_mb
tags: []
singleValue: ge 0
- meterId:
name: instance_php_memory_peak_mb
tags: []
singleValue: ge 0
- meterId:
name: instance_php_virtual_memory_mb
tags: []
singleValue: ge 0
- meterId:
name: instance_php_thread_count
tags: []
singleValue: ge 1
- meterId:
name: instance_php_open_fd_count
tags: []
singleValue: ge 1
3 changes: 2 additions & 1 deletion tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ async fn run_e2e() {
request_swoole_2_predis().await;
request_swoole_2_mongodb().await;
request_swoole_2_memcache().await;
sleep(Duration::from_secs(3)).await;
// Wait for PHM meter report (metrics_report_period=5s on FPM test-1).
sleep(Duration::from_secs(8)).await;
request_collector_validate().await;
}

Expand Down
8 changes: 7 additions & 1 deletion worker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// limitations under the License.

pub mod channel;
pub mod phm;
pub mod reporter;

use crate::{
Expand Down Expand Up @@ -45,6 +46,7 @@ pub struct WorkerConfiguration {
pub socket_file_path: PathBuf,
pub heart_beat: Option<HeartBeatConfiguration>,
pub reporter_config: ReporterConfiguration,
pub phm: Option<phm::PhmConfiguration>,
}

pub struct HeartBeatConfiguration {
Expand Down Expand Up @@ -126,7 +128,11 @@ pub async fn start_worker(config: WorkerConfiguration) -> anyhow::Result<()> {
});

if let Some(heart_beat_config) = config.heart_beat {
report_properties_and_keep_alive(heart_beat_config, TxReporter(tx_));
report_properties_and_keep_alive(heart_beat_config, TxReporter(tx_.clone()));
}

if let Some(phm_config) = config.phm {
phm::run_phm_collector(phm_config, TxReporter(tx_));
}

// Run reporter with blocking.
Expand Down
Loading
Loading