Skip to content

Commit

Permalink
Add standalone reporter type and standalone skywalking worker (#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmjoy authored Aug 16, 2024
1 parent f433815 commit 3187ca1
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 15 deletions.
10 changes: 5 additions & 5 deletions docs/en/configuration/ini-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This is the configuration list supported in `php.ini`.

| Configuration Item | Description | Default Value |
| ------------------------------------------------ |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ------------------------- |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| skywalking_agent.enable | Enable skywalking_agent extension or not. | Off |
| skywalking_agent.log_file | Log file path. | /tmp/skywalking-agent.log |
| skywalking_agent.log_level | Log level: one of `OFF`, `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`. | INFO |
Expand All @@ -20,9 +20,9 @@ This is the configuration list supported in `php.ini`.
| skywalking_agent.heartbeat_period | Agent heartbeat report period. Unit, second. | 30 |
| skywalking_agent.properties_report_period_factor | The agent sends the instance properties to the backend every heartbeat_period * properties_report_period_factor seconds. | 10 |
| skywalking_agent.enable_zend_observer | Whether to use `zend observer` instead of `zend_execute_ex` to hook the functions, this feature is only available for PHP8+. | Off |
| skywalking_agent.reporter_type | Reporter type, optional values are `grpc` and `kafka`. | grpc |
| skywalking_agent.reporter_type | Reporter type, optional values are `grpc`, `kafka` and `standalone`. | 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 |
| 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.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 |
| 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`. | |
42 changes: 42 additions & 0 deletions docs/en/reporter/standalone-reporter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Standalone reporter

When the reporter type is `grpc` or `kafka`, the `skywalking_agent` extension forks a child process during
the extension initialization phase to act as a worker process for sending data to the SkyWalking OAP server
or Kafka.

However, this approach has some limitations, such as:

1. It cannot be used with the `php-fpm` daemon mode.
2. Multiple worker processes can be redundant when there are several `php-fpm` processes on the instance.

To address these issues, `skywalking_agent` introduces a new reporter type: `standalone`.

With the `standalone` reporter type, the `skywalking_agent` extension no longer forks a child process.
Instead, the user needs to manually start an independent worker process.

## Steps

1. Compile the standalone `skywalking-php-worker` binary:

```shell
cargo build -p skywalking-php-worker --bin skywalking-php-worker --all-features --release
```

2. Run `skywalking-php-worker`:

Assuming the socket file path is `/tmp/skywalking-php-worker.sock` and the SkyWalking OAP server address is `127.0.0.1:11800`, the command is:

```shell
./target/release/skywalking-php-worker -s /tmp/skywalking-php-worker.sock grpc --server-addr 127.0.0.1:11800
```

For additional parameters, refer to `./target/release/skywalking-php-worker --help`.

3. Configure `php.ini`:

```ini
[skywalking_agent]
extension = skywalking_agent.so
skywalking_agent.reporter_type = standalone
skywalking_agent.standalone_socket_path = /tmp/skywalking-php-worker.sock
```
2 changes: 2 additions & 0 deletions docs/menu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ catalog:
catalog:
- name: "Kafka Reporter"
path: "/en/reporter/kafka-reporter"
- name: "Standalone Reporter"
path: "/en/reporter/standalone-reporter"
- name: "Contribution"
catalog:
- name: "Compiling Guidance"
Expand Down
12 changes: 11 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ const SKYWALKING_AGENT_PROPERTIES_REPORT_PERIOD_FACTOR: &str =
/// PHP8's jit.
const SKYWALKING_AGENT_ENABLE_ZEND_OBSERVER: &str = "skywalking_agent.enable_zend_observer";

/// Reporter type, optional values are `grpc` and `kafka`, default is `grpc`.
/// Reporter type, optional values are `grpc`, `kafka` and `standalone`, default
/// is `grpc`.
const SKYWALKING_AGENT_REPORTER_TYPE: &str = "skywalking_agent.reporter_type";

/// A list of host/port pairs to use for establishing the initial connection to
Expand All @@ -108,6 +109,10 @@ const SKYWALKING_AGENT_KAFKA_PRODUCER_CONFIG: &str = "skywalking_agent.kafka_pro
/// `$request->server` variable.
const SKYWALKING_AGENT_INJECT_CONTEXT: &str = "skywalking_agent.inject_context";

/// Unix domain socket file path of standalone skywalking php worker. Only
/// available when `reporter_type` is `standalone`.
const SKYWALKING_AGENT_STANDALONE_SOCKET_PATH: &str = "skywalking_agent.standalone_socket_path";

#[php_get_module]
pub fn get_module() -> Module {
let mut module = Module::new(
Expand Down Expand Up @@ -194,6 +199,11 @@ pub fn get_module() -> Module {
Policy::System,
);
module.add_ini(SKYWALKING_AGENT_INJECT_CONTEXT, false, Policy::System);
module.add_ini(
SKYWALKING_AGENT_STANDALONE_SOCKET_PATH,
"".to_string(),
Policy::System,
);

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

pub static SOCKET_FILE_PATH: Lazy<PathBuf> = Lazy::new(|| {
if is_standalone_reporter_type() {
return PathBuf::from(get_str_ini_with_default(
SKYWALKING_AGENT_STANDALONE_SOCKET_PATH,
));
}

let mut dir = RUNTIME_DIR.clone();

let dur = SystemTime::now()
Expand Down Expand Up @@ -263,7 +269,9 @@ fn try_init_logger() -> anyhow::Result<()> {

let file = open_options.open(path)?;

let filter = EnvFilter::new(format!("info,skywalking_agent={}", log_level));
let filter = EnvFilter::new(format!(
"info,skywalking_agent={log_level},skywalking_php_worker={log_level}"
));

let subscriber = FmtSubscriber::builder()
.with_env_filter(filter)
Expand All @@ -285,3 +293,8 @@ fn get_module_registry() -> &'static ZArr {
pub fn is_enable() -> bool {
*IS_ENABLE
}

#[inline]
pub fn is_standalone_reporter_type() -> bool {
REPORTER_TYPE.as_str() == "standalone"
}
10 changes: 7 additions & 3 deletions src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
// 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,
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,
};
#[cfg(feature = "kafka-reporter")]
use crate::module::{KAFKA_BOOTSTRAP_SERVERS, KAFKA_PRODUCER_CONFIG};
Expand All @@ -31,6 +31,10 @@ use std::{cmp::Ordering, num::NonZeroUsize, process::exit, thread::available_par
use tracing::error;

pub fn init_worker() {
if is_standalone_reporter_type() {
return;
}

unsafe {
// TODO Shutdown previous worker before fork if there is a PHP-FPM reload
// operation.
Expand Down
8 changes: 4 additions & 4 deletions worker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ skywalking = { version = "0.8.0", features = ["management"] }
tokio = { version = "1.29.1", features = ["full"] }
tokio-stream = "0.1.14"
tonic = { version = "0.8.3", features = ["tls", "tls-roots"] }
tracing = { version = "0.1.37", features = ["attributes"] }
tracing = { version = "0.1.37", features = ["attributes", "log"] }
tracing-subscriber = { version = "0.3.17", features = ["env-filter"], optional = true }

# [[bin]]
# name = "skywalking-php-worker"
# required-features = ["standalone", "kafka-reporter"]
[[bin]]
name = "skywalking-php-worker"
required-features = ["standalone", "kafka-reporter"]
156 changes: 156 additions & 0 deletions worker/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use clap::Parser;
use skywalking_php_worker::{
new_tokio_runtime,
reporter::{GrpcReporterConfiguration, KafkaReporterConfiguration, ReporterConfiguration},
start_worker, WorkerConfiguration,
};
use std::{num::NonZeroUsize, path::PathBuf, thread::available_parallelism};
use tracing::log::LevelFilter;
use tracing_subscriber::{EnvFilter, FmtSubscriber};

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Path of socket file to listening
#[arg(short, long)]
socket_file_path: PathBuf,

/// Count of worker threads, default is `nproc`
#[arg(long)]
worker_threads: Option<usize>,

/// Log level, will be overwritten by env `RUST_LOG`
#[arg(short, long, default_value = "INFO")]
log_level: LevelFilter,

/// Select reporter
#[command(subcommand)]
reporter: ReporterArgs,
}

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
enum ReporterArgs {
/// Report to Skywalking OAP via grpc protocol
Grpc {
/// skywalking server address
#[arg(long)]
server_addr: String,

/// Skywalking agent authentication token
#[arg(long)]
authentication: Option<String>,

/// Wether to enable tls for gPRC
#[arg(long)]
enable_tls: bool,

/// The gRPC SSL trusted ca file
#[arg(long, required_if_eq("enable_tls", "true"))]
ssl_cert_chain_path: Option<String>,

/// The private key file. Enable mTLS when ssl_key_path and
/// ssl_cert_chain_path exist
#[arg(long)]
ssl_key_path: Option<String>,

/// The certificate file. Enable mTLS when ssl_key_path and
/// ssl_cert_chain_path exist
#[arg(long)]
ssl_trusted_ca_path: Option<String>,
},
/// Report to kafka
Kafka {
/// A list of host/port pairs to use for establishing the initial
/// connection to the Kafka cluster. Only available when the
/// reporter type is `kafka`
#[arg(long)]
kafka_bootstrap_servers: String,

/// Configure Kafka Producer configuration in JSON format.
/// Only available when the reporter type is `kafka`
#[arg(long)]
kafka_producer_config: Option<String>,
},
}

impl From<ReporterArgs> for ReporterConfiguration {
fn from(args: ReporterArgs) -> Self {
match args {
ReporterArgs::Grpc {
server_addr,
authentication,
enable_tls,
ssl_cert_chain_path,
ssl_key_path,
ssl_trusted_ca_path,
} => ReporterConfiguration::Grpc(GrpcReporterConfiguration {
server_addr,
authentication: authentication.unwrap_or_default(),
enable_tls,
ssl_cert_chain_path: ssl_cert_chain_path.unwrap_or_default(),
ssl_key_path: ssl_key_path.unwrap_or_default(),
ssl_trusted_ca_path: ssl_trusted_ca_path.unwrap_or_default(),
}),
ReporterArgs::Kafka {
kafka_bootstrap_servers,
kafka_producer_config,
} => ReporterConfiguration::Kafka(KafkaReporterConfiguration {
kafka_bootstrap_servers,
kafka_producer_config: kafka_producer_config.unwrap_or_default(),
}),
}
}
}

fn init_logger(log_level: &LevelFilter) -> anyhow::Result<()> {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::new(format!(
"info,skywalking_agent={log_level},skywalking_php_worker={log_level}"
))
});

let subscriber = FmtSubscriber::builder()
.with_env_filter(filter)
.with_ansi(false)
.finish();

tracing::subscriber::set_global_default(subscriber)?;

Ok(())
}

fn worker_threads(worker_threads: Option<usize>) -> usize {
worker_threads.unwrap_or_else(|| available_parallelism().map(NonZeroUsize::get).unwrap_or(1))
}

fn main() -> anyhow::Result<()> {
let args = Args::parse();

init_logger(&args.log_level)?;

let rt = new_tokio_runtime(worker_threads(args.worker_threads));

rt.block_on(start_worker(WorkerConfiguration {
socket_file_path: args.socket_file_path,
heart_beat: None,
reporter_config: args.reporter.into(),
}))?;

Ok(())
}
2 changes: 1 addition & 1 deletion worker/src/reporter/reporter_grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ use tonic::transport::{Certificate, Channel, ClientTlsConfig, Endpoint, Identity
use tracing::{debug, info, warn};

pub struct GrpcReporterConfiguration {
pub server_addr: String,
pub authentication: String,
pub enable_tls: bool,
pub server_addr: String,
pub ssl_cert_chain_path: String,
pub ssl_key_path: String,
pub ssl_trusted_ca_path: String,
Expand Down

0 comments on commit 3187ca1

Please sign in to comment.