| // 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 crate::{ |
| channel::Reporter, |
| execute::{register_execute_functions, register_observer_handlers}, |
| log::PsrLogLevel, |
| util::{IPS, get_sapi_module_name, get_str_ini_with_default}, |
| worker::init_worker, |
| *, |
| }; |
| use anyhow::bail; |
| use once_cell::sync::Lazy; |
| use phper::{arrays::ZArr, ini::ini_get, sys}; |
| use skywalking::{ |
| common::random_generator::RandomGenerator, |
| logging::logger::{self, Logger}, |
| trace::tracer::{self, Tracer}, |
| }; |
| use std::{ |
| ffi::{CStr, OsStr}, |
| fs::{self, OpenOptions}, |
| os::unix::prelude::OsStrExt, |
| path::{Path, PathBuf}, |
| str::FromStr, |
| sync::Arc, |
| time::SystemTime, |
| }; |
| use time::format_description::well_known::Rfc3339; |
| use tracing::{debug, error, info, metadata::LevelFilter}; |
| use tracing_subscriber::{EnvFilter, FmtSubscriber, fmt::time::OffsetTime}; |
| |
| static IS_ENABLE: Lazy<bool> = Lazy::new(|| { |
| if !ini_get::<bool>(SKYWALKING_AGENT_ENABLE) { |
| return false; |
| } |
| |
| let sapi = get_sapi_module_name().to_bytes(); |
| |
| if sapi == b"fpm-fcgi" { |
| return true; |
| } |
| |
| if sapi == b"cli" && get_module_registry().exists("swoole") { |
| return true; |
| } |
| |
| false |
| }); |
| |
| pub static SERVER_ADDR: Lazy<String> = |
| Lazy::new(|| get_str_ini_with_default(SKYWALKING_AGENT_SERVER_ADDR)); |
| |
| pub static SERVICE_NAME: Lazy<String> = |
| Lazy::new(|| get_str_ini_with_default(SKYWALKING_AGENT_SERVICE_NAME)); |
| |
| pub static SERVICE_INSTANCE: Lazy<String> = Lazy::new(|| { |
| let rnd_hostname = RandomGenerator::generate() + "@" + &IPS[0]; |
| let mut service_instance = rnd_hostname.as_str(); |
| |
| let defined_instance_name = ini_get::<Option<&CStr>>(SKYWALKING_AGENT_INSTANCE_NAME) |
| .and_then(|s| s.to_str().ok()) |
| .unwrap_or_default(); |
| let defined_instance_name = defined_instance_name.trim(); |
| |
| if !defined_instance_name.is_empty() { |
| service_instance = defined_instance_name; |
| } |
| service_instance.to_string() |
| }); |
| |
| pub static SKYWALKING_VERSION: Lazy<i64> = |
| Lazy::new(|| ini_get::<i64>(SKYWALKING_AGENT_SKYWALKING_VERSION)); |
| |
| pub static RUNTIME_DIR: Lazy<PathBuf> = Lazy::new(|| { |
| let mut path = PathBuf::new(); |
| if let Some(dir) = ini_get::<Option<&CStr>>(SKYWALKING_AGENT_RUNTIME_DIR) { |
| let dir = dir.to_bytes(); |
| if !dir.is_empty() { |
| path.push(OsStr::from_bytes(dir)); |
| } |
| } |
| path |
| }); |
| |
| 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() |
| .duration_since(SystemTime::UNIX_EPOCH) |
| .expect("Get timestamp failed") |
| .as_micros(); |
| |
| dir.push(format!("{:x}.sock", dur)); |
| |
| dir |
| }); |
| |
| pub static AUTHENTICATION: Lazy<String> = |
| Lazy::new(|| get_str_ini_with_default(SKYWALKING_AGENT_AUTHENTICATION)); |
| |
| pub static ENABLE_TLS: Lazy<bool> = Lazy::new(|| ini_get::<bool>(SKYWALKING_AGENT_ENABLE_TLS)); |
| |
| pub static SSL_TRUSTED_CA_PATH: Lazy<String> = |
| Lazy::new(|| get_str_ini_with_default(SKYWALKING_AGENT_SSL_TRUSTED_CA_PATH)); |
| |
| pub static SSL_KEY_PATH: Lazy<String> = |
| Lazy::new(|| get_str_ini_with_default(SKYWALKING_AGENT_SSL_KEY_PATH)); |
| |
| pub static SSL_CERT_CHAIN_PATH: Lazy<String> = |
| Lazy::new(|| get_str_ini_with_default(SKYWALKING_AGENT_SSL_CERT_CHAIN_PATH)); |
| |
| pub static HEARTBEAT_PERIOD: Lazy<i64> = |
| Lazy::new(|| ini_get::<i64>(SKYWALKING_AGENT_HEARTBEAT_PERIOD)); |
| |
| pub static PROPERTIES_REPORT_PERIOD_FACTOR: Lazy<i64> = |
| Lazy::new(|| ini_get::<i64>(SKYWALKING_AGENT_PROPERTIES_REPORT_PERIOD_FACTOR)); |
| |
| /// Zend observer is only support in PHP8+. |
| pub static ENABLE_ZEND_OBSERVER: Lazy<bool> = Lazy::new(|| { |
| sys::PHP_MAJOR_VERSION >= 8 && ini_get::<bool>(SKYWALKING_AGENT_ENABLE_ZEND_OBSERVER) |
| }); |
| |
| pub static WORKER_THREADS: Lazy<i64> = |
| Lazy::new(|| ini_get::<i64>(SKYWALKING_AGENT_WORKER_THREADS)); |
| |
| pub static REPORTER_TYPE: Lazy<String> = |
| Lazy::new(|| get_str_ini_with_default(SKYWALKING_AGENT_REPORTER_TYPE)); |
| |
| pub static KAFKA_BOOTSTRAP_SERVERS: Lazy<String> = |
| Lazy::new(|| get_str_ini_with_default(SKYWALKING_AGENT_KAFKA_BOOTSTRAP_SERVERS)); |
| |
| 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> |
| #[allow(clippy::absurd_extreme_comparisons)] |
| pub const IS_ZEND_OBSERVER_CALLED_FOR_INTERNAL: bool = |
| sys::PHP_MAJOR_VERSION > 8 || (sys::PHP_MAJOR_VERSION == 8 && sys::PHP_MINOR_VERSION >= 2); |
| |
| pub static PSR_LOGGING_LEVEL: Lazy<PsrLogLevel> = Lazy::new(|| { |
| get_str_ini_with_default(SKYWALKING_AGENT_PSR_LOGGING_LEVEL) |
| .as_str() |
| .into() |
| }); |
| |
| pub fn init() { |
| if !is_enable() { |
| return; |
| } |
| |
| // Initialize configuration properties. |
| Lazy::force(&SERVER_ADDR); |
| Lazy::force(&SERVICE_NAME); |
| Lazy::force(&SERVICE_INSTANCE); |
| Lazy::force(&SKYWALKING_VERSION); |
| Lazy::force(&RUNTIME_DIR); |
| Lazy::force(&SOCKET_FILE_PATH); |
| Lazy::force(&AUTHENTICATION); |
| Lazy::force(&ENABLE_TLS); |
| Lazy::force(&SSL_TRUSTED_CA_PATH); |
| Lazy::force(&SSL_KEY_PATH); |
| Lazy::force(&SSL_CERT_CHAIN_PATH); |
| Lazy::force(&HEARTBEAT_PERIOD); |
| Lazy::force(&PROPERTIES_REPORT_PERIOD_FACTOR); |
| Lazy::force(&ENABLE_ZEND_OBSERVER); |
| Lazy::force(&WORKER_THREADS); |
| Lazy::force(&REPORTER_TYPE); |
| Lazy::force(&KAFKA_BOOTSTRAP_SERVERS); |
| Lazy::force(&KAFKA_PRODUCER_CONFIG); |
| Lazy::force(&INJECT_CONTEXT); |
| Lazy::force(&PSR_LOGGING_LEVEL); |
| |
| if let Err(err) = try_init_logger() { |
| eprintln!("skywalking_agent: initialize logger failed: {}", err); |
| } |
| |
| // Skywalking agent info. |
| info!( |
| service_name = &*SERVICE_NAME, |
| service_instance = &*SERVICE_INSTANCE, |
| skywalking_version = &*SKYWALKING_VERSION, |
| heartbeat_period = &*HEARTBEAT_PERIOD, |
| properties_report_period_factor = &*PROPERTIES_REPORT_PERIOD_FACTOR, |
| "Starting skywalking agent" |
| ); |
| |
| // Skywalking version check. |
| let skywalking_version = *SKYWALKING_VERSION; |
| if skywalking_version < 8 { |
| error!( |
| skywalking_version, |
| "The skywalking agent only supports versions after skywalking 8" |
| ); |
| return; |
| } |
| |
| // Initialize runtime directory. |
| if RUNTIME_DIR.as_os_str().is_empty() { |
| error!("The skywalking agent runtime directory must not be empty"); |
| return; |
| } |
| if let Err(err) = fs::create_dir_all(&*RUNTIME_DIR) { |
| error!(?err, "Create runtime directory failed"); |
| return; |
| } |
| |
| // Initialize Agent worker. |
| init_worker(); |
| |
| let reporter = Arc::new(Reporter::new(&*SOCKET_FILE_PATH)); |
| |
| tracer::set_global_tracer(Tracer::new( |
| &*SERVICE_NAME, |
| &*SERVICE_INSTANCE, |
| reporter.clone(), |
| )); |
| |
| logger::set_global_logger(Logger::new(&*SERVICE_NAME, &*SERVICE_INSTANCE, reporter)); |
| |
| // Hook functions. |
| register_execute_functions(); |
| register_observer_handlers(); |
| } |
| |
| pub fn shutdown() { |
| if !is_enable() { |
| return; |
| } |
| |
| debug!("skywalking agent shutdown hook called"); |
| } |
| |
| fn try_init_logger() -> anyhow::Result<()> { |
| let log_level = ini_get::<Option<&CStr>>(SKYWALKING_AGENT_LOG_LEVEL) |
| .and_then(|s| s.to_str().ok()) |
| .unwrap_or("OFF"); |
| let log_level = log_level.trim(); |
| |
| let log_level = LevelFilter::from_str(log_level)?; |
| if log_level == LevelFilter::OFF { |
| return Ok(()); |
| } |
| |
| let log_file = ini_get::<Option<&CStr>>(SKYWALKING_AGENT_LOG_FILE) |
| .and_then(|s| s.to_str().ok()) |
| .unwrap_or_default(); |
| let log_file = log_file.trim(); |
| if log_file.is_empty() { |
| bail!("log file can't be empty when log enabled"); |
| } |
| |
| let path = Path::new(log_file); |
| |
| if let Some(parent) = path.parent() { |
| fs::create_dir_all(parent)?; |
| } |
| |
| let mut open_options = OpenOptions::new(); |
| open_options.append(true).create(true); |
| |
| let file = open_options.open(path)?; |
| |
| let filter = EnvFilter::new(format!( |
| "info,skywalking_agent={log_level},skywalking_php_worker={log_level}" |
| )); |
| |
| let subscriber = FmtSubscriber::builder() |
| .with_env_filter(filter) |
| .with_ansi(false) |
| .with_writer(file) |
| .with_timer( |
| OffsetTime::local_rfc_3339() |
| .unwrap_or_else(|_| OffsetTime::new(time::UtcOffset::UTC, Rfc3339)), |
| ) |
| .finish(); |
| |
| tracing::subscriber::set_global_default(subscriber)?; |
| |
| Ok(()) |
| } |
| |
| #[inline] |
| fn get_module_registry() -> &'static ZArr { |
| unsafe { ZArr::from_ptr(&raw const sys::module_registry) } |
| } |
| |
| #[inline] |
| pub fn is_enable() -> bool { |
| *IS_ENABLE |
| } |
| |
| #[inline] |
| pub fn is_standalone_reporter_type() -> bool { |
| REPORTER_TYPE.as_str() == "standalone" |
| } |