Log Exception in tracing span when throwed. (#75)
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 @@
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 @@
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 @@
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 @@
.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 @@
} 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 @@
_: 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 @@
}
}
}
+
+ 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 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 @@
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))
}),
- Noop::noop(),
+ Box::new(move |_, span, _, _| {
+ let mut span = span.downcast::<Span>().unwrap();
+ log_exception(&mut span);
+ Ok(())
+ }),
)
}
@@ -119,7 +126,11 @@
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 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 @@
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))
}),
- Noop::noop(),
+ Box::new(move |_, span, _, _| {
+ let mut span = span.downcast::<Span>().unwrap();
+ log_exception(&mut span);
+ Ok(())
+ }),
)
}
@@ -191,12 +197,11 @@
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 @@
}
}
+ 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 @@
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 @@
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 dashmap::DashMap;
use once_cell::sync::Lazy;
use phper::{
- eg,
objects::ZObj,
sys,
values::{ExecuteData, ZVal},
@@ -360,24 +359,7 @@
) -> 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 @@
- { key: http.status_code, value: "200" }
- segmentId: "not null"
spans:
- - operationName: PDO->exec
+ - operationName: PDO->__construct
parentSpanId: 0
spanId: 1
spanLayer: Database
@@ -443,31 +443,27 @@
- { 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 }
+ - {
+ key: db.data_source,
value: "dbname=skywalking;host=127.0.0.1;port=3306",
}
- { key: db.statement, value: SELECT 1 }
- - operationName: PDO->prepare
- 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 }
- - {
- key: db.data_source,
- value: "dbname=skywalking;host=127.0.0.1:3306",
- }
- - {
- key: db.statement,
- value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
- }
- - operationName: PDOStatement->execute
+ - operationName: PDO->__construct
parentSpanId: 0
spanId: 3
spanLayer: Database
@@ -484,11 +480,7 @@
key: db.data_source,
value: "dbname=skywalking;host=127.0.0.1:3306",
}
- - {
- key: db.statement,
- value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
- }
- - operationName: PDOStatement->fetchAll
+ - operationName: PDO->prepare
parentSpanId: 0
spanId: 4
spanLayer: Database
@@ -509,7 +501,7 @@
key: db.statement,
value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
}
- - operationName: PDO->prepare
+ - operationName: PDOStatement->execute
parentSpanId: 0
spanId: 5
spanLayer: Database
@@ -523,6 +515,65 @@
tags:
- { key: db.type, value: mysql }
- {
+ key: db.data_source,
+ value: "dbname=skywalking;host=127.0.0.1:3306",
+ }
+ - {
+ key: db.statement,
+ value: "SELECT * FROM `mysql`.`user` WHERE `User` = :user",
+ }
+ - operationName: PDOStatement->fetchAll
+ parentSpanId: 0
+ spanId: 6
+ 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",
+ }
+ - {
+ 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: 8
+ 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;",
}
@@ -532,7 +583,7 @@
}
- operationName: PDOStatement->execute
parentSpanId: 0
- spanId: 6
+ spanId: 9
spanLayer: Database
startTime: gt 0
endTime: gt 0
@@ -553,7 +604,7 @@
}
- operationName: PDOStatement->fetchAll
parentSpanId: 0
- spanId: 7
+ spanId: 10
spanLayer: Database
startTime: gt 0
endTime: gt 0
@@ -572,9 +623,26 @@
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 @@
}
- operationName: PDOStatement->execute
parentSpanId: 0
- spanId: 9
+ spanId: 13
spanLayer: Database
startTime: gt 0
endTime: gt 0
@@ -715,7 +783,7 @@
- { key: http.status_code, value: "200" }
- segmentId: 'not null'
spans:
- - operationName: mysqli->query
+ - operationName: mysqli->__construct
parentSpanId: 0
spanId: 1
spanLayer: Database
@@ -727,14 +795,40 @@
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 @@
- { 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 @@
- { key: http.status_code, value: "200" }
- segmentId: "not null"
spans:
- - operationName: PDO->exec
+ - operationName: PDO->__construct
parentSpanId: 0
spanId: 1
spanLayer: Database
@@ -1317,6 +1409,23 @@
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 }
+ - {
+ key: db.data_source,
+ value: dbname=skywalking;host=127.0.0.1;port=3306,
+ }
- { key: db.statement, value: SELECT 1 }
- operationName: GET:/pdo
parentSpanId: -1
@@ -1335,7 +1444,7 @@
- { key: http.status_code, value: "200" }
- segmentId: "not null"
spans:
- - operationName: mysqli->query
+ - operationName: mysqli->__construct
parentSpanId: 0
spanId: 1
spanLayer: Database
@@ -1348,6 +1457,19 @@
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: GET:/mysqli
parentSpanId: -1