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 2023/05/20 02:18:14 UTC

[skywalking-php] branch master updated: Log Exception in tracing span when throwed. (#75)

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 a4e6baa  Log Exception in tracing span when throwed. (#75)
a4e6baa is described below

commit a4e6baa037005e81cfcdc6e6c7700ee4b91fc852
Author: jmjoy <jm...@apache.org>
AuthorDate: Sat May 20 10:18:10 2023 +0800

    Log Exception in tracing span when throwed. (#75)
---
 src/plugin/mod.rs                |  28 +++++++
 src/plugin/plugin_amqplib.rs     |  10 ++-
 src/plugin/plugin_curl.rs        |   6 +-
 src/plugin/plugin_memcached.rs   |   6 +-
 src/plugin/plugin_mysqli.rs      |  23 ++++--
 src/plugin/plugin_pdo.rs         |  25 ++++---
 src/plugin/plugin_predis.rs      |   3 +
 src/plugin/plugin_redis.rs       |  22 +-----
 tests/data/expected_context.yaml | 158 ++++++++++++++++++++++++++++++++++-----
 9 files changed, 223 insertions(+), 58 deletions(-)

diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index 8826c2b..f564d2e 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -24,6 +24,8 @@ mod plugin_swoole;
 
 use crate::execute::{AfterExecuteHook, BeforeExecuteHook};
 use once_cell::sync::Lazy;
+use phper::{eg, objects::ZObj};
+use skywalking::trace::span::Span;
 
 // Register plugins here.
 static PLUGINS: Lazy<Vec<Box<DynPlugin>>> = Lazy::new(|| {
@@ -74,3 +76,29 @@ pub fn select_plugin(class_name: Option<&str>, function_name: &str) -> Option<&'
 
     selected_plugin.map(AsRef::as_ref)
 }
+
+fn log_exception(span: &mut Span) {
+    let ex = unsafe { ZObj::try_from_mut_ptr(eg!(exception)) };
+    if let Some(ex) = ex {
+        let mut span_object = span.span_object_mut();
+        span_object.is_error = true;
+
+        let mut logs = Vec::new();
+        if let Ok(class_name) = ex.get_class().get_name().to_str() {
+            logs.push(("error.kind", class_name.to_owned()));
+        }
+        if let Some(message) = ex.get_property("message").as_z_str() {
+            if let Ok(message) = message.to_str() {
+                logs.push(("message", message.to_owned()));
+            }
+        }
+        if let Ok(stack) = ex.call("getTraceAsString", []) {
+            if let Some(stack) = stack.as_z_str().and_then(|s| s.to_str().ok()) {
+                logs.push(("stack", stack.to_owned()));
+            }
+        }
+        if !logs.is_empty() {
+            span_object.add_log(logs);
+        }
+    }
+}
diff --git a/src/plugin/plugin_amqplib.rs b/src/plugin/plugin_amqplib.rs
index e6065a8..9016a72 100644
--- a/src/plugin/plugin_amqplib.rs
+++ b/src/plugin/plugin_amqplib.rs
@@ -13,11 +13,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use super::Plugin;
+use super::{log_exception, Plugin};
 use crate::{
     component::COMPONENT_AMQP_PRODUCER_ID,
     context::{RequestContext, SW_HEADER},
-    execute::{get_this_mut, validate_num_args, AfterExecuteHook, BeforeExecuteHook, Noop},
+    execute::{get_this_mut, validate_num_args, AfterExecuteHook, BeforeExecuteHook},
     tag::{TAG_MQ_BROKER, TAG_MQ_QUEUE, TAG_MQ_TOPIC},
 };
 use anyhow::Context;
@@ -99,7 +99,11 @@ impl AmqplibPlugin {
 
                 Ok(Box::new(span))
             }),
-            Noop::noop(),
+            Box::new(move |_, span, _, _| {
+                let mut span = span.downcast::<Span>().unwrap();
+                log_exception(&mut span);
+                Ok(())
+            }),
         )
     }
 
diff --git a/src/plugin/plugin_curl.rs b/src/plugin/plugin_curl.rs
index 7ca08f5..0f06a0c 100644
--- a/src/plugin/plugin_curl.rs
+++ b/src/plugin/plugin_curl.rs
@@ -13,7 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use super::Plugin;
+use super::{log_exception, Plugin};
 use crate::{
     component::COMPONENT_PHP_CURL_ID,
     context::{RequestContext, SW_HEADER},
@@ -442,6 +442,7 @@ impl CurlPlugin {
             .and_then(|code| code.as_long())
             .context("Call curl_getinfo, http_code is null")?;
         span.add_tag("status_code", &*http_code.to_string());
+
         if http_code == 0 {
             let result = call("curl_error", &mut [ch.clone()])?;
             let curl_error = result
@@ -456,6 +457,9 @@ impl CurlPlugin {
         } else {
             span.span_object_mut().is_error = false;
         }
+
+        log_exception(span);
+
         Ok(())
     }
 }
diff --git a/src/plugin/plugin_memcached.rs b/src/plugin/plugin_memcached.rs
index 48f4cf6..2744cfb 100644
--- a/src/plugin/plugin_memcached.rs
+++ b/src/plugin/plugin_memcached.rs
@@ -15,7 +15,7 @@
 
 use std::{any::Any, collections::HashMap};
 
-use super::Plugin;
+use super::{log_exception, Plugin};
 use crate::{
     component::COMPONENT_PHP_MEMCACHED_ID,
     context::RequestContext,
@@ -306,6 +306,7 @@ fn after_hook(
     _: Option<i64>, span: Box<dyn Any>, execute_data: &mut ExecuteData, return_value: &mut ZVal,
 ) -> crate::Result<()> {
     let mut span = span.downcast::<Span>().expect("Downcast to Span failed");
+
     if let Some(b) = return_value.as_bool() {
         if !b {
             span.span_object_mut().is_error = true;
@@ -330,6 +331,9 @@ fn after_hook(
             }
         }
     }
+
+    log_exception(&mut span);
+
     Ok(())
 }
 
diff --git a/src/plugin/plugin_mysqli.rs b/src/plugin/plugin_mysqli.rs
index b3f1dfc..40bf8bb 100644
--- a/src/plugin/plugin_mysqli.rs
+++ b/src/plugin/plugin_mysqli.rs
@@ -13,11 +13,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use super::Plugin;
+use super::{log_exception, Plugin};
 use crate::{
     component::COMPONENT_PHP_MYSQLI_ID,
     context::RequestContext,
-    execute::{get_this_mut, AfterExecuteHook, BeforeExecuteHook, Noop},
+    execute::{get_this_mut, AfterExecuteHook, BeforeExecuteHook},
 };
 use anyhow::Context;
 use dashmap::DashMap;
@@ -60,7 +60,7 @@ impl Plugin for MySQLImprovedPlugin {
 impl MySQLImprovedPlugin {
     fn hook_mysqli_construct(&self) -> (Box<BeforeExecuteHook>, Box<AfterExecuteHook>) {
         (
-            Box::new(|_, execute_data| {
+            Box::new(|request_id, execute_data| {
                 let this = get_this_mut(execute_data)?;
                 let handle = this.handle();
                 hack_dtor(this, Some(mysqli_dtor));
@@ -89,10 +89,17 @@ impl MySQLImprovedPlugin {
                     info.port = port
                 }
 
+                let span = create_mysqli_exit_span(request_id, "mysqli", "__construct", &info)?;
+
                 MYSQL_MAP.insert(handle, info);
-                Ok(Box::new(()))
+
+                Ok(Box::new(span))
+            }),
+            Box::new(move |_, span, _, _| {
+                let mut span = span.downcast::<Span>().unwrap();
+                log_exception(&mut span);
+                Ok(())
             }),
-            Noop::noop(),
         )
     }
 
@@ -119,7 +126,11 @@ impl MySQLImprovedPlugin {
 
                 Ok(Box::new(span) as _)
             }),
-            Noop::noop(),
+            Box::new(move |_, span, _, _| {
+                let mut span = span.downcast::<Span>().unwrap();
+                log_exception(&mut span);
+                Ok(())
+            }),
         )
     }
 }
diff --git a/src/plugin/plugin_pdo.rs b/src/plugin/plugin_pdo.rs
index bbedfa3..d1af7ba 100644
--- a/src/plugin/plugin_pdo.rs
+++ b/src/plugin/plugin_pdo.rs
@@ -13,11 +13,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use super::Plugin;
+use super::{log_exception, Plugin};
 use crate::{
     component::COMPONENT_PHP_PDO_ID,
     context::RequestContext,
-    execute::{get_this_mut, validate_num_args, AfterExecuteHook, BeforeExecuteHook, Noop},
+    execute::{get_this_mut, validate_num_args, AfterExecuteHook, BeforeExecuteHook},
     tag::{TAG_DB_STATEMENT, TAG_DB_TYPE},
 };
 use anyhow::Context;
@@ -83,7 +83,7 @@ impl Plugin for PdoPlugin {
 impl PdoPlugin {
     fn hook_pdo_construct(&self) -> (Box<BeforeExecuteHook>, Box<AfterExecuteHook>) {
         (
-            Box::new(|_, execute_data| {
+            Box::new(|request_id, execute_data| {
                 validate_num_args(execute_data, 1)?;
 
                 let this = get_this_mut(execute_data)?;
@@ -97,11 +97,17 @@ impl PdoPlugin {
                 let dsn: Dsn = dsn.parse()?;
                 debug!(?dsn, "parse PDO dsn");
 
+                let span = create_exit_span_with_dsn(request_id, "PDO", "__construct", &dsn)?;
+
                 DSN_MAP.insert(handle, dsn);
 
-                Ok(Box::new(()))
+                Ok(Box::new(span))
+            }),
+            Box::new(move |_, span, _, _| {
+                let mut span = span.downcast::<Span>().unwrap();
+                log_exception(&mut span);
+                Ok(())
             }),
-            Noop::noop(),
         )
     }
 
@@ -191,12 +197,11 @@ unsafe extern "C" fn dtor(object: *mut sys::zend_object) {
 fn after_hook(
     _: Option<i64>, span: Box<dyn Any>, execute_data: &mut ExecuteData, return_value: &mut ZVal,
 ) -> crate::Result<()> {
+    let mut span = span.downcast::<Span>().unwrap();
+
     if let Some(b) = return_value.as_bool() {
         if !b {
-            return after_hook_when_false(
-                get_this_mut(execute_data)?,
-                &mut span.downcast::<Span>().unwrap(),
-            );
+            return after_hook_when_false(get_this_mut(execute_data)?, &mut span);
         }
     } else if let Some(obj) = return_value.as_mut_z_obj() {
         let cls = obj.get_class();
@@ -209,6 +214,8 @@ fn after_hook(
         }
     }
 
+    log_exception(&mut span);
+
     Ok(())
 }
 
diff --git a/src/plugin/plugin_predis.rs b/src/plugin/plugin_predis.rs
index 0b643d6..b486ed5 100644
--- a/src/plugin/plugin_predis.rs
+++ b/src/plugin/plugin_predis.rs
@@ -18,6 +18,7 @@ use crate::{
     component::COMPONENT_PHP_PREDIS_ID,
     context::RequestContext,
     execute::{get_this_mut, validate_num_args, AfterExecuteHook, BeforeExecuteHook},
+    plugin::log_exception,
     tag::{TAG_CACHE_CMD, TAG_CACHE_KEY, TAG_CACHE_OP, TAG_CACHE_TYPE},
 };
 use once_cell::sync::Lazy;
@@ -238,6 +239,8 @@ impl PredisPlugin {
                     span.span_object_mut().is_error = true;
                 }
 
+                log_exception(&mut span);
+
                 Ok(())
             }),
         )
diff --git a/src/plugin/plugin_redis.rs b/src/plugin/plugin_redis.rs
index 3db2ddf..df22fe1 100644
--- a/src/plugin/plugin_redis.rs
+++ b/src/plugin/plugin_redis.rs
@@ -13,7 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use super::Plugin;
+use super::{log_exception, Plugin};
 use crate::{
     component::COMPONENT_PHP_REDIS_ID,
     context::RequestContext,
@@ -24,7 +24,6 @@ use anyhow::Context;
 use dashmap::DashMap;
 use once_cell::sync::Lazy;
 use phper::{
-    eg,
     objects::ZObj,
     sys,
     values::{ExecuteData, ZVal},
@@ -360,24 +359,7 @@ fn after_hook(
 ) -> crate::Result<()> {
     let mut span = span.downcast::<Span>().unwrap();
 
-    let ex = unsafe { ZObj::try_from_mut_ptr(eg!(exception)) };
-    if let Some(ex) = ex {
-        let mut span_object = span.span_object_mut();
-        span_object.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_object.add_log(logs);
-        }
-    }
+    log_exception(&mut span);
 
     Ok(())
 }
diff --git a/tests/data/expected_context.yaml b/tests/data/expected_context.yaml
index bc3470c..ea1bd78 100644
--- a/tests/data/expected_context.yaml
+++ b/tests/data/expected_context.yaml
@@ -428,7 +428,7 @@ segmentItems:
               - { key: http.status_code, value: "200" }
       - segmentId: "not null"
         spans:
-          - operationName: PDO->exec
+          - operationName: PDO->__construct
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -439,6 +439,23 @@ segmentItems:
             spanType: Exit
             peer: 127.0.0.1:3306
             skipAnalysis: false
+            tags:
+              - { key: db.type, value: mysql }
+              - {
+                  key: db.data_source,
+                  value: dbname=skywalking;host=127.0.0.1;port=3306,
+                }
+          - operationName: PDO->exec
+            parentSpanId: 0
+            spanId: 2
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8003
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:3306
+            skipAnalysis: false
             tags:
               - { key: db.type, value: mysql }
               - {
@@ -446,9 +463,26 @@ segmentItems:
                   value: "dbname=skywalking;host=127.0.0.1;port=3306",
                 }
               - { key: db.statement, value: SELECT 1 }
+          - operationName: PDO->__construct
+            parentSpanId: 0
+            spanId: 3
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8003
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:3306
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: mysql }
+              - {
+                  key: db.data_source,
+                  value: "dbname=skywalking;host=127.0.0.1:3306",
+                }
           - operationName: PDO->prepare
             parentSpanId: 0
-            spanId: 2
+            spanId: 4
             spanLayer: Database
             startTime: gt 0
             endTime: gt 0
@@ -469,7 +503,7 @@ segmentItems:
                 }
           - operationName: PDOStatement->execute
             parentSpanId: 0
-            spanId: 3
+            spanId: 5
             spanLayer: Database
             startTime: gt 0
             endTime: gt 0
@@ -490,7 +524,7 @@ segmentItems:
                 }
           - operationName: PDOStatement->fetchAll
             parentSpanId: 0
-            spanId: 4
+            spanId: 6
             spanLayer: Database
             startTime: gt 0
             endTime: gt 0
@@ -509,9 +543,26 @@ segmentItems:
                   key: db.statement,
                   value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
                 }
+          - operationName: PDO->__construct
+            parentSpanId: 0
+            spanId: 7
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8003
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:3306
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: mysql }
+              - {
+                  key: db.data_source,
+                  value: "dbname=skywalking;host=127.0.0.1:3306;",
+                }
           - operationName: PDO->prepare
             parentSpanId: 0
-            spanId: 5
+            spanId: 8
             spanLayer: Database
             startTime: gt 0
             endTime: gt 0
@@ -532,7 +583,7 @@ segmentItems:
               }
           - operationName: PDOStatement->execute
             parentSpanId: 0
-            spanId: 6
+            spanId: 9
             spanLayer: Database
             startTime: gt 0
             endTime: gt 0
@@ -553,7 +604,7 @@ segmentItems:
               }
           - operationName: PDOStatement->fetchAll
             parentSpanId: 0
-            spanId: 7
+            spanId: 10
             spanLayer: Database
             startTime: gt 0
             endTime: gt 0
@@ -572,9 +623,26 @@ segmentItems:
                 key: db.statement,
                 value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
               }
+          - operationName: PDO->__construct
+            parentSpanId: 0
+            spanId: 11
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8003
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:3306
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: mysql }
+              - {
+                  key: db.data_source,
+                  value: dbname=skywalking;host=127.0.0.1;port=3306,
+                }
           - operationName: PDO->prepare
             parentSpanId: 0
-            spanId: 8
+            spanId: 12
             spanLayer: Database
             startTime: gt 0
             endTime: gt 0
@@ -595,7 +663,7 @@ segmentItems:
               }
           - operationName: PDOStatement->execute
             parentSpanId: 0
-            spanId: 9
+            spanId: 13
             spanLayer: Database
             startTime: gt 0
             endTime: gt 0
@@ -715,7 +783,7 @@ segmentItems:
               - { key: http.status_code, value: "200" }
       - segmentId: 'not null'
         spans:
-          - operationName: mysqli->query
+          - operationName: mysqli->__construct
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -726,15 +794,41 @@ segmentItems:
             spanType: Exit
             peer: 127.0.0.1:3306
             skipAnalysis: false
+            tags:
+              - { key: db.type, value: mysql }
+          - operationName: mysqli->query
+            parentSpanId: 0
+            spanId: 2
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8004
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:3306
+            skipAnalysis: false
             tags:
               - {key: db.type, value: mysql}
               - {
                   key: db.statement,
                   value: "SELECT 1",
                 }
+          - operationName: mysqli->__construct
+            parentSpanId: 0
+            spanId: 3
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8004
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:3306
+            skipAnalysis: false
+            tags:
+              - { key: db.type, value: mysql }
           - operationName: mysqli->query
             parentSpanId: 0
-            spanId: 2
+            spanId: 4
             spanLayer: Database
             startTime: gt 0
             endTime: gt 0
@@ -1005,11 +1099,9 @@ segmentItems:
               - { key: cache.key, value: foo }
             logs:
               - logEvent:
-                  - { key: Exception Class, value: RedisException }
-                  - {
-                      key: Exception Message,
-                      value: NOAUTH Authentication required.,
-                    }
+                  - { key: error.kind, value: RedisException }
+                  - { key: message, value: NOAUTH Authentication required. }
+                  - { key: stack, value: not null }
           - operationName: GET:/redis.fail.php
             parentSpanId: -1
             spanId: 0
@@ -1300,7 +1392,7 @@ segmentItems:
               - { key: http.status_code, value: "200" }
       - segmentId: "not null"
         spans:
-          - operationName: PDO->exec
+          - operationName: PDO->__construct
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -1311,6 +1403,23 @@ segmentItems:
             spanType: Exit
             peer: 127.0.0.1:3306
             skipAnalysis: false
+            tags:
+              - { key: db.type, value: mysql }
+              - {
+                  key: db.data_source,
+                  value: dbname=skywalking;host=127.0.0.1;port=3306,
+                }
+          - operationName: PDO->exec
+            parentSpanId: 0
+            spanId: 2
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8003
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:3306
+            skipAnalysis: false
             tags:
               - { key: db.type, value: mysql }
               - {
@@ -1335,7 +1444,7 @@ segmentItems:
               - { key: http.status_code, value: "200" }
       - segmentId: "not null"
         spans:
-          - operationName: mysqli->query
+          - operationName: mysqli->__construct
             parentSpanId: 0
             spanId: 1
             spanLayer: Database
@@ -1346,6 +1455,19 @@ segmentItems:
             spanType: Exit
             peer: 127.0.0.1:3306
             skipAnalysis: false
+            tags:
+              - { key: db.type, value: mysql }
+          - operationName: mysqli->query
+            parentSpanId: 0
+            spanId: 2
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 8004
+            isError: false
+            spanType: Exit
+            peer: 127.0.0.1:3306
+            skipAnalysis: false
             tags:
               - { key: db.type, value: mysql }
               - { key: db.statement, value: SELECT 1 }