src/module.rs (225 lines of code) (raw):

// 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" }