You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by jm...@apache.org on 2022/11/10 07:08:22 UTC
[skywalking-php] branch master updated: Add plugin for phpredis. (#29)
This is an automated email from the ASF dual-hosted git repository.
jmjoy 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 c550167 Add plugin for phpredis. (#29)
c550167 is described below
commit c550167d9ca9b5654c1b78e9635f7a4f7311cf92
Author: jmjoy <jm...@apache.org>
AuthorDate: Thu Nov 10 15:08:17 2022 +0800
Add plugin for phpredis. (#29)
---
.github/workflows/rust.yml | 5 +-
Cargo.lock | 6 +-
Cargo.toml | 1 +
README.md | 2 +-
.../service-agent/php-agent/Supported-list.md | 1 +
src/component.rs | 1 +
src/lib.rs | 1 +
src/plugin/mod.rs | 2 +
src/plugin/plugin_redis.rs | 372 +++++++++++++++++++++
src/{component.rs => tag.rs} | 16 +-
src/util.rs | 72 +++-
tests/data/expected_context.yaml | 149 ++++++++-
tests/e2e.rs | 15 +
src/component.rs => tests/php/fpm/redis.fail.php | 25 +-
src/component.rs => tests/php/fpm/redis.succ.php | 27 +-
15 files changed, 660 insertions(+), 35 deletions(-)
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 0430487..b407ec1 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -85,8 +85,9 @@ jobs:
tools: php-config, composer:v2
extensions: >
bcmath, calendar, ctype, dom, exif, gettext, iconv, intl, json, mbstring,
- mysqli, mysqlnd, opcache, pdo, pdo_mysql, phar, posix, readline, memcached,
- swoole-${{ matrix.version.swoole }}, xml, xmlreader, xmlwriter, yaml, zip
+ mysqli, mysqlnd, opcache, pdo, pdo_mysql, phar, posix, readline, redis,
+ memcached, swoole-${{ matrix.version.swoole }}, xml, xmlreader, xmlwriter,
+ yaml, zip
- name: Setup php-fpm for Linux
if: matrix.os == 'ubuntu-20.04'
diff --git a/Cargo.lock b/Cargo.lock
index 1b08c5b..3000865 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1782,10 +1782,11 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.85"
+version = "1.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
+checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
dependencies = [
+ "indexmap",
"itoa",
"ryu",
"serde",
@@ -1885,6 +1886,7 @@ dependencies = [
"phper",
"prost",
"reqwest",
+ "serde_json",
"skywalking",
"systemstat",
"tempfile",
diff --git a/Cargo.toml b/Cargo.toml
index 79875cb..788c3e3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -46,6 +46,7 @@ libc = "0.2.132"
once_cell = "1.14.0"
phper = "0.5.1"
prost = "0.11.0"
+serde_json = { version = "1.0.87", features = ["preserve_order"] }
skywalking = "0.4.0"
systemstat = "0.2.0"
tempfile = "3.3.0"
diff --git a/README.md b/README.md
index cc757df..0d39aae 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ SkyWalking PHP Agent requires SkyWalking 8.4+ and PHP 7.2+
* [x] [PDO](https://www.php.net/manual/en/book.pdo.php)
* [x] [MySQL Improved](https://www.php.net/manual/en/book.mysqli.php)
* [x] [Memcached](https://www.php.net/manual/en/book.memcached.php)
- * [ ] [phpredis](https://github.com/phpredis/phpredis)
+ * [x] [phpredis](https://github.com/phpredis/phpredis)
* [ ] [php-amqp](https://github.com/php-amqp/php-amqp)
* [ ] [php-rdkafka](https://github.com/arnaud-lb/php-rdkafka)
* [x] [predis](https://github.com/predis/predis)
diff --git a/docs/en/setup/service-agent/php-agent/Supported-list.md b/docs/en/setup/service-agent/php-agent/Supported-list.md
index d4227e7..4e11b15 100644
--- a/docs/en/setup/service-agent/php-agent/Supported-list.md
+++ b/docs/en/setup/service-agent/php-agent/Supported-list.md
@@ -13,6 +13,7 @@ The following plugins provide the distributed tracing capability.
* [PDO](https://www.php.net/manual/en/book.pdo.php)
* [MySQL Improved](https://www.php.net/manual/en/book.mysqli.php)
* [Memcached](https://www.php.net/manual/en/book.memcached.php)
+* [phpredis](https://github.com/phpredis/phpredis)
## Support PHP library
diff --git a/src/component.rs b/src/component.rs
index cb6ab19..b6aaf08 100644
--- a/src/component.rs
+++ b/src/component.rs
@@ -23,3 +23,4 @@ pub const COMPONENT_PHP_PDO_ID: i32 = 8003;
pub const COMPONENT_PHP_MYSQLI_ID: i32 = 8004;
pub const COMPONENT_PHP_PREDIS_ID: i32 = 8006;
pub const COMPONENT_PHP_MEMCACHED_ID: i32 = 20;
+pub const COMPONENT_PHP_REDIS_ID: i32 = 7;
diff --git a/src/lib.rs b/src/lib.rs
index dd97960..6569149 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -25,6 +25,7 @@ mod execute;
mod module;
mod plugin;
mod request;
+mod tag;
mod util;
mod worker;
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index 5b16556..070185c 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -18,6 +18,7 @@ mod plugin_memcached;
mod plugin_mysqli;
mod plugin_pdo;
mod plugin_predis;
+mod plugin_redis;
mod plugin_swoole;
use crate::execute::{AfterExecuteHook, BeforeExecuteHook};
@@ -33,6 +34,7 @@ static PLUGINS: Lazy<Vec<Box<DynPlugin>>> = Lazy::new(|| {
Box::new(plugin_swoole::SwooleHttpResponsePlugin::default()),
Box::new(plugin_predis::PredisPlugin::default()),
Box::new(plugin_memcached::MemcachedPlugin::default()),
+ Box::new(plugin_redis::RedisPlugin::default()),
]
});
diff --git a/src/plugin/plugin_redis.rs b/src/plugin/plugin_redis.rs
new file mode 100644
index 0000000..e376d07
--- /dev/null
+++ b/src/plugin/plugin_redis.rs
@@ -0,0 +1,372 @@
+// 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 std::{any::Any, collections::HashSet};
+
+use super::Plugin;
+use crate::{
+ component::COMPONENT_PHP_REDIS_ID,
+ context::RequestContext,
+ execute::{get_this_mut, AfterExecuteHook, BeforeExecuteHook, Noop},
+ tag::{TAG_CACHE_CMD, TAG_CACHE_KEY, TAG_CACHE_OP, TAG_CACHE_TYPE},
+};
+use anyhow::Context;
+use dashmap::DashMap;
+use once_cell::sync::Lazy;
+use phper::{
+ eg,
+ objects::ZObj,
+ sys,
+ values::{ExecuteData, ZVal},
+};
+use skywalking::{skywalking_proto::v3::SpanLayer, trace::span::Span};
+use tracing::{debug, warn};
+
+static PEER_MAP: Lazy<DashMap<u32, Peer>> = Lazy::new(Default::default);
+
+static FREE_MAP: Lazy<DashMap<u32, sys::zend_object_free_obj_t>> = Lazy::new(Default::default);
+
+static REDIS_READ_COMMANDS: Lazy<HashSet<String>> = Lazy::new(|| {
+ [
+ "blPop",
+ "brPop",
+ "get",
+ "getBit",
+ "getKeys",
+ "getMultiple",
+ "getRange",
+ "hExists",
+ "hGet",
+ "hGetAll",
+ "hKeys",
+ "hLen",
+ "hMGet",
+ "hScan",
+ "hStrLen",
+ "hVals",
+ "keys",
+ "lGet",
+ "lGetRange",
+ "lLen",
+ "lRange",
+ "lSize",
+ "mGet",
+ "sContains",
+ "sGetMembers",
+ "sIsMember",
+ "sMembers",
+ "sScan",
+ "sSize",
+ "strLen",
+ "zCount",
+ "zRange",
+ "zRangeByLex",
+ "zRangeByScore",
+ "zScan",
+ "zSize",
+ ]
+ .into_iter()
+ .map(str::to_ascii_lowercase)
+ .collect()
+});
+
+static REDIS_WRITE_COMMANDS: Lazy<HashSet<String>> = Lazy::new(|| {
+ [
+ "append",
+ "bRPopLPush",
+ "decr",
+ "decrBy",
+ "del",
+ "delete",
+ "hDel",
+ "hIncrBy",
+ "hIncrByFloat",
+ "hMSet",
+ "hSet",
+ "hSetNx",
+ "incr",
+ "incrBy",
+ "incrByFloat",
+ "lInsert",
+ "lPush",
+ "lPushx",
+ "lRem",
+ "lRemove",
+ "lSet",
+ "lTrim",
+ "listTrim",
+ "mSet",
+ "mSetNX",
+ "pSetEx",
+ "rPopLPush",
+ "rPush",
+ "rPushX",
+ "randomKey",
+ "sAdd",
+ "sInter",
+ "sInterStore",
+ "sMove",
+ "sRandMember",
+ "sRem",
+ "sRemove",
+ "set",
+ "setBit",
+ "setEx",
+ "setNx",
+ "setRange",
+ "setTimeout",
+ "sort",
+ "unlink",
+ "zAdd",
+ "zDelete",
+ "zDeleteRangeByRank",
+ "zDeleteRangeByScore",
+ "zIncrBy",
+ "zRem",
+ "zRemRangeByRank",
+ "zRemRangeByScore",
+ "zRemove",
+ "zRemoveRangeByScore",
+ ]
+ .into_iter()
+ .map(str::to_ascii_lowercase)
+ .collect()
+});
+
+static REDIS_ALL_COMMANDS: Lazy<HashSet<String>> = Lazy::new(|| {
+ let mut commands = HashSet::new();
+ commands.extend(REDIS_READ_COMMANDS.iter().map(Clone::clone));
+ commands.extend(REDIS_WRITE_COMMANDS.iter().map(Clone::clone));
+ commands
+});
+
+#[derive(Default, Clone)]
+pub struct RedisPlugin;
+
+impl Plugin for RedisPlugin {
+ #[inline]
+ fn class_names(&self) -> Option<&'static [&'static str]> {
+ Some(&["Redis"])
+ }
+
+ #[inline]
+ fn function_name_prefix(&self) -> Option<&'static str> {
+ None
+ }
+
+ fn hook(
+ &self, class_name: Option<&str>, function_name: &str,
+ ) -> Option<(Box<BeforeExecuteHook>, Box<AfterExecuteHook>)> {
+ match (class_name, function_name) {
+ (Some("Redis"), "__construct") => Some(self.hook_redis_construct()),
+ (Some(class_name @ "Redis"), f)
+ if ["connect", "open", "pconnect", "popen"].contains(&f) =>
+ {
+ Some(self.hook_redis_connect(class_name, function_name))
+ }
+ (Some(class_name @ "Redis"), f)
+ if REDIS_ALL_COMMANDS.contains(&f.to_ascii_lowercase()) =>
+ {
+ Some(self.hook_redis_methods(class_name, function_name))
+ }
+ _ => None,
+ }
+ }
+}
+
+impl RedisPlugin {
+ /// TODO Support first optional argument as config for phpredis 6.0+.
+ /// <https://github.com/phpredis/phpredis/blob/cc2383f07666e6afefd7b58995fb607d9967d650/README.markdown#example-1>
+ fn hook_redis_construct(&self) -> (Box<BeforeExecuteHook>, Box<AfterExecuteHook>) {
+ (
+ Box::new(|_, execute_data| {
+ let this = get_this_mut(execute_data)?;
+ hack_free(this, Some(redis_dtor));
+
+ Ok(Box::new(()))
+ }),
+ Noop::noop(),
+ )
+ }
+
+ fn hook_redis_connect(
+ &self, class_name: &str, function_name: &str,
+ ) -> (Box<BeforeExecuteHook>, Box<AfterExecuteHook>) {
+ let class_name = class_name.to_owned();
+ let function_name = function_name.to_owned();
+ (
+ Box::new(move |request_id, execute_data| {
+ if execute_data.num_args() < 2 {
+ debug!("argument count less than 2, skipped.");
+ return Ok(Box::new(()));
+ }
+
+ let host = {
+ let mut f = || {
+ Ok::<_, anyhow::Error>(
+ execute_data
+ .get_parameter(0)
+ .as_z_str()
+ .context("isn't string")?
+ .to_str()?
+ .to_owned(),
+ )
+ };
+ match f() {
+ Ok(host) => host,
+ Err(err) => {
+ warn!(?err, "parse first argument to host failed, skipped.");
+ return Ok(Box::new(()));
+ }
+ }
+ };
+ let port = {
+ let mut f = || {
+ execute_data
+ .get_parameter(1)
+ .as_long()
+ .context("isn't long")
+ };
+ match f() {
+ Ok(port) => port,
+ Err(err) => {
+ warn!(?err, "parse second argument to port failed, skipped.");
+ return Ok(Box::new(()));
+ }
+ }
+ };
+
+ let this = get_this_mut(execute_data)?;
+ let addr = format!("{}:{}", host, port);
+ debug!(addr, "Get redis peer");
+ PEER_MAP.insert(this.handle(), Peer { addr: addr.clone() });
+
+ let mut span = RequestContext::try_with_global_ctx(request_id, |ctx| {
+ Ok(ctx.create_exit_span(&format!("{}->{}", class_name, function_name), &addr))
+ })?;
+
+ span.with_span_object_mut(|span| {
+ span.set_span_layer(SpanLayer::Cache);
+ span.component_id = COMPONENT_PHP_REDIS_ID;
+ span.add_tag(TAG_CACHE_TYPE, "redis");
+ });
+
+ Ok(Box::new(span))
+ }),
+ Box::new(after_hook),
+ )
+ }
+
+ fn hook_redis_methods(
+ &self, class_name: &str, function_name: &str,
+ ) -> (Box<BeforeExecuteHook>, Box<AfterExecuteHook>) {
+ let class_name = class_name.to_owned();
+ let function_name = function_name.to_owned();
+ (
+ Box::new(move |request_id, execute_data| {
+ let handle = get_this_mut(execute_data)?.handle();
+ debug!(handle, function_name, "call redis method");
+ let peer = PEER_MAP
+ .get(&handle)
+ .map(|r| r.value().addr.clone())
+ .unwrap_or_default();
+
+ let key = execute_data
+ .get_parameter(0)
+ .as_z_str()
+ .and_then(|s| s.to_str().ok());
+ let op = if REDIS_READ_COMMANDS.contains(&function_name.to_ascii_lowercase()) {
+ "read"
+ } else {
+ "write"
+ };
+
+ debug!(handle, function_name, key, op, "call redis command");
+
+ let mut span = RequestContext::try_with_global_ctx(request_id, |ctx| {
+ Ok(ctx.create_exit_span(&format!("{}->{}", class_name, function_name), &peer))
+ })?;
+
+ span.with_span_object_mut(|span| {
+ span.set_span_layer(SpanLayer::Cache);
+ span.component_id = COMPONENT_PHP_REDIS_ID;
+ span.add_tag(TAG_CACHE_TYPE, "redis");
+ span.add_tag(TAG_CACHE_CMD, function_name);
+ span.add_tag(TAG_CACHE_OP, op);
+ if let Some(key) = key {
+ span.add_tag(TAG_CACHE_KEY, key)
+ }
+ });
+
+ Ok(Box::new(span))
+ }),
+ Box::new(after_hook),
+ )
+ }
+}
+
+struct Peer {
+ addr: String,
+}
+
+fn hack_free(this: &mut ZObj, new_free: sys::zend_object_free_obj_t) {
+ let handle = this.handle();
+
+ unsafe {
+ let ori_free = (*(*this.as_mut_ptr()).handlers).free_obj;
+ FREE_MAP.insert(handle, ori_free);
+ (*((*this.as_mut_ptr()).handlers as *mut sys::zend_object_handlers)).free_obj = new_free;
+ }
+}
+
+unsafe extern "C" fn redis_dtor(object: *mut sys::zend_object) {
+ debug!("call Redis free");
+
+ let handle = ZObj::from_ptr(object).handle();
+
+ PEER_MAP.remove(&handle);
+ if let Some((_, Some(free))) = FREE_MAP.remove(&handle) {
+ free(object);
+ }
+}
+
+fn after_hook(
+ _request_id: Option<i64>, span: Box<dyn Any>, _execute_data: &mut ExecuteData,
+ _return_value: &mut ZVal,
+) -> anyhow::Result<()> {
+ let mut span = span.downcast::<Span>().unwrap();
+
+ let ex = unsafe { ZObj::try_from_mut_ptr(eg!(exception)) };
+ if let Some(ex) = ex {
+ span.with_span_object_mut(|span| {
+ span.is_error = true;
+
+ let mut logs = Vec::new();
+ if let Ok(class_name) = ex.get_class().get_name().to_str() {
+ logs.push(("Exception Class", class_name.to_owned()));
+ }
+ if let Some(message) = ex.get_property("message").as_z_str() {
+ if let Ok(message) = message.to_str() {
+ logs.push(("Exception Message", message.to_owned()));
+ }
+ }
+ if !logs.is_empty() {
+ span.add_log(logs);
+ }
+ });
+ }
+
+ Ok(())
+}
diff --git a/src/component.rs b/src/tag.rs
similarity index 64%
copy from src/component.rs
copy to src/tag.rs
index cb6ab19..69ca800 100644
--- a/src/component.rs
+++ b/src/tag.rs
@@ -13,13 +13,13 @@
// See the License for the specific language governing permissions and
// limitations under the License..
-//! Component ID
+//! Tags
//!
-//! <https://github.com/apache/skywalking/blob/014861535015745ae3f7b99acd7d14500b3b3927/oap-server/server-starter/src/main/resources/component-libraries.yml>
+//! Virtual Cache
+//!
+//! https://skywalking.apache.org/docs/main/next/en/setup/service-agent/virtual-cache/
-pub const COMPONENT_PHP_ID: i32 = 8001;
-pub const COMPONENT_PHP_CURL_ID: i32 = 8002;
-pub const COMPONENT_PHP_PDO_ID: i32 = 8003;
-pub const COMPONENT_PHP_MYSQLI_ID: i32 = 8004;
-pub const COMPONENT_PHP_PREDIS_ID: i32 = 8006;
-pub const COMPONENT_PHP_MEMCACHED_ID: i32 = 20;
+pub const TAG_CACHE_TYPE: &str = "cache.type";
+pub const TAG_CACHE_OP: &str = "cache.op";
+pub const TAG_CACHE_CMD: &str = "cache.cmd";
+pub const TAG_CACHE_KEY: &str = "cache.key";
diff --git a/src/util.rs b/src/util.rs
index 460c39f..1643780 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -16,7 +16,8 @@
use anyhow::bail;
use chrono::Local;
use once_cell::sync::Lazy;
-use phper::{sys, values::ZVal};
+use phper::{arrays::IterKey, sys, values::ZVal};
+use serde_json::{json, Number, Value};
use std::{
ffi::CStr,
panic::{catch_unwind, UnwindSafe},
@@ -112,3 +113,72 @@ pub fn catch_unwind_anyhow<F: FnOnce() -> anyhow::Result<R> + UnwindSafe, R>(
pub fn get_sapi_module_name() -> &'static CStr {
unsafe { CStr::from_ptr(sys::sapi_module.name) }
}
+
+/// Use for later scene.
+#[allow(dead_code)]
+pub fn json_encode_values(values: &[ZVal]) -> serde_json::Result<String> {
+ fn add(json_value: &mut Value, key: Option<String>, item: Value) {
+ match key {
+ Some(key) => {
+ json_value.as_object_mut().unwrap().insert(key, item);
+ }
+ None => {
+ json_value.as_array_mut().unwrap().push(item);
+ }
+ }
+ }
+
+ fn handle(json_value: &mut Value, key: Option<String>, val: &ZVal) {
+ let type_info = val.get_type_info();
+
+ if type_info.is_null() {
+ add(json_value, key, Value::Null);
+ } else if type_info.is_true() {
+ add(json_value, key, Value::Bool(true));
+ } else if type_info.is_false() {
+ add(json_value, key, Value::Bool(false));
+ } else if type_info.is_long() {
+ let i = val.as_long().unwrap();
+ add(json_value, key, Value::Number(i.into()));
+ } else if type_info.is_double() {
+ let d = val.as_double().unwrap();
+ let n = match Number::from_f64(d) {
+ Some(n) => Value::Number(n),
+ None => Value::String("<NaN>".to_owned()),
+ };
+ add(json_value, key, n);
+ } else if type_info.is_string() {
+ let s = val
+ .as_z_str()
+ .unwrap()
+ .to_str()
+ .map(ToOwned::to_owned)
+ .unwrap_or_default();
+ add(json_value, key, Value::String(s));
+ } else if type_info.is_array() {
+ let arr = val.as_z_arr().unwrap();
+ let is_arr = arr.iter().all(|(key, _)| matches!(key, IterKey::Index(_)));
+ let mut new_json_value = if is_arr { json!([]) } else { json!({}) };
+ for (key, new_val) in arr.iter() {
+ if is_arr {
+ handle(&mut new_json_value, None, new_val);
+ } else {
+ let key = match key {
+ IterKey::Index(i) => i.to_string(),
+ IterKey::ZStr(s) => s.to_str().map(ToOwned::to_owned).unwrap_or_default(),
+ };
+ handle(&mut new_json_value, Some(key), new_val);
+ }
+ }
+ add(json_value, key, new_json_value);
+ } else if type_info.is_object() {
+ add(json_value, key, Value::String("<Object>".to_owned()));
+ }
+ }
+
+ let mut json_value = json!([]);
+ for val in values {
+ handle(&mut json_value, None, val);
+ }
+ serde_json::to_string(&json_value)
+}
diff --git a/tests/data/expected_context.yaml b/tests/data/expected_context.yaml
index f5ca94a..f366211 100644
--- a/tests/data/expected_context.yaml
+++ b/tests/data/expected_context.yaml
@@ -15,7 +15,7 @@
segmentItems:
- serviceName: skywalking-agent-test-1
- segmentSize: 10
+ segmentSize: 12
segments:
- segmentId: "not null"
spans:
@@ -681,6 +681,153 @@ segmentItems:
- { key: url, value: /memcached.php }
- { key: http.method, value: GET }
- { key: http.status_code, value: "200" }
+ - segmentId: "not null"
+ spans:
+ - operationName: Redis->connect
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Cache
+ startTime: gt 0
+ endTime: gt 0
+ componentId: 7
+ isError: false
+ spanType: Exit
+ peer: "127.0.0.1:6379"
+ skipAnalysis: false
+ tags:
+ - key: cache.type
+ value: redis
+ - operationName: Redis->mset
+ parentSpanId: 0
+ spanId: 2
+ spanLayer: Cache
+ startTime: gt 0
+ endTime: gt 0
+ componentId: 7
+ isError: false
+ spanType: Exit
+ peer: 127.0.0.1:6379
+ skipAnalysis: false
+ tags:
+ - key: cache.type
+ value: redis
+ - key: cache.cmd
+ value: mset
+ - key: cache.op
+ value: write
+ - operationName: Redis->get
+ parentSpanId: 0
+ spanId: 3
+ spanLayer: Cache
+ startTime: gt 0
+ endTime: gt 0
+ componentId: 7
+ isError: false
+ spanType: Exit
+ peer: 127.0.0.1:6379
+ skipAnalysis: false
+ tags:
+ - key: cache.type
+ value: redis
+ - key: cache.cmd
+ value: get
+ - key: cache.op
+ value: read
+ - key: cache.key
+ value: key0
+ - operationName: Redis->get
+ parentSpanId: 0
+ spanId: 4
+ spanLayer: Cache
+ startTime: gt 0
+ endTime: gt 0
+ componentId: 7
+ isError: false
+ spanType: Exit
+ peer: 127.0.0.1:6379
+ skipAnalysis: false
+ tags:
+ - key: cache.type
+ value: redis
+ - key: cache.cmd
+ value: get
+ - key: cache.op
+ value: read
+ - key: cache.key
+ value: key1
+ - operationName: GET:/redis.succ.php
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: gt 0
+ endTime: gt 0
+ componentId: 8001
+ isError: false
+ spanType: Entry
+ peer: ""
+ skipAnalysis: false
+ tags:
+ - { key: url, value: /redis.succ.php }
+ - { key: http.method, value: GET }
+ - { key: http.status_code, value: "200" }
+ - segmentId: "not null"
+ spans:
+ - operationName: Redis->connect
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Cache
+ startTime: gt 0
+ endTime: gt 0
+ componentId: 7
+ isError: false
+ spanType: Exit
+ peer: "127.0.0.1:6379"
+ skipAnalysis: false
+ tags:
+ - key: cache.type
+ value: redis
+ - operationName: Redis->set
+ parentSpanId: 0
+ spanId: 2
+ spanLayer: Cache
+ startTime: gt 0
+ endTime: gt 0
+ componentId: 7
+ isError: true
+ spanType: Exit
+ peer: 127.0.0.1:6379
+ skipAnalysis: false
+ tags:
+ - key: cache.type
+ value: redis
+ - key: cache.cmd
+ value: set
+ - key: cache.op
+ value: write
+ - key: cache.key
+ value: foo
+ logs:
+ - logEvent:
+ - { key: Exception Class, value: RedisException }
+ - {
+ key: Exception Message,
+ value: NOAUTH Authentication required.,
+ }
+ - operationName: GET:/redis.fail.php
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: gt 0
+ endTime: gt 0
+ componentId: 8001
+ isError: false
+ spanType: Entry
+ peer: ""
+ skipAnalysis: false
+ tags:
+ - { key: url, value: /redis.fail.php }
+ - { key: http.method, value: GET }
+ - { key: http.status_code, value: "200" }
- serviceName: skywalking-agent-test-2
segmentSize: 1
segments:
diff --git a/tests/e2e.rs b/tests/e2e.rs
index a15671b..bc3f876 100644
--- a/tests/e2e.rs
+++ b/tests/e2e.rs
@@ -52,6 +52,7 @@ async fn run_e2e() {
request_fpm_predis().await;
request_fpm_mysqli().await;
request_fpm_memcached().await;
+ request_fpm_redis().await;
request_swoole_curl().await;
sleep(Duration::from_secs(3)).await;
request_collector_validate().await;
@@ -97,6 +98,20 @@ async fn request_fpm_memcached() {
.await;
}
+async fn request_fpm_redis() {
+ request_common(
+ HTTP_CLIENT.get(format!("http://{}/redis.succ.php", PROXY_SERVER_1_ADDRESS)),
+ "ok",
+ )
+ .await;
+
+ request_common(
+ HTTP_CLIENT.get(format!("http://{}/redis.fail.php", PROXY_SERVER_1_ADDRESS)),
+ "ok",
+ )
+ .await;
+}
+
async fn request_swoole_curl() {
request_common(
HTTP_CLIENT.get(format!("http://{}/curl", SWOOLE_SERVER_1_ADDRESS)),
diff --git a/src/component.rs b/tests/php/fpm/redis.fail.php
similarity index 61%
copy from src/component.rs
copy to tests/php/fpm/redis.fail.php
index cb6ab19..be6610f 100644
--- a/src/component.rs
+++ b/tests/php/fpm/redis.fail.php
@@ -1,3 +1,5 @@
+<?php
+
// 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.
@@ -11,15 +13,18 @@
// 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..
+// limitations under the License.
+
+
+use Webmozart\Assert\Assert;
+
+require_once dirname(__DIR__) . "/vendor/autoload.php";
-//! Component ID
-//!
-//! <https://github.com/apache/skywalking/blob/014861535015745ae3f7b99acd7d14500b3b3927/oap-server/server-starter/src/main/resources/component-libraries.yml>
+try {
+ $client = new Redis();
+ $client->connect("127.0.0.1", 6379);
+ $client->set('foo', 'bar');
+} catch (RedisException $e) {
+}
-pub const COMPONENT_PHP_ID: i32 = 8001;
-pub const COMPONENT_PHP_CURL_ID: i32 = 8002;
-pub const COMPONENT_PHP_PDO_ID: i32 = 8003;
-pub const COMPONENT_PHP_MYSQLI_ID: i32 = 8004;
-pub const COMPONENT_PHP_PREDIS_ID: i32 = 8006;
-pub const COMPONENT_PHP_MEMCACHED_ID: i32 = 20;
+echo "ok";
diff --git a/src/component.rs b/tests/php/fpm/redis.succ.php
similarity index 61%
copy from src/component.rs
copy to tests/php/fpm/redis.succ.php
index cb6ab19..1abc743 100644
--- a/src/component.rs
+++ b/tests/php/fpm/redis.succ.php
@@ -1,3 +1,5 @@
+<?php
+
// 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.
@@ -11,15 +13,20 @@
// 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..
+// limitations under the License.
+
+
+use Webmozart\Assert\Assert;
+
+require_once dirname(__DIR__) . "/vendor/autoload.php";
-//! Component ID
-//!
-//! <https://github.com/apache/skywalking/blob/014861535015745ae3f7b99acd7d14500b3b3927/oap-server/server-starter/src/main/resources/component-libraries.yml>
+{
+ $client = new Redis();
+ $client->connect("127.0.0.1", 6379);
+ $client->auth('password');
+ $client->mSet(['key0' => 'value0', 'key1' => 'value1']);
+ Assert::same($client->get('key0'), 'value0');
+ Assert::same($client->get('key1'), 'value1');
+}
-pub const COMPONENT_PHP_ID: i32 = 8001;
-pub const COMPONENT_PHP_CURL_ID: i32 = 8002;
-pub const COMPONENT_PHP_PDO_ID: i32 = 8003;
-pub const COMPONENT_PHP_MYSQLI_ID: i32 = 8004;
-pub const COMPONENT_PHP_PREDIS_ID: i32 = 8006;
-pub const COMPONENT_PHP_MEMCACHED_ID: i32 = 20;
+echo "ok";