瀏覽代碼

Feature #TASK_QT-18250 SQL ERROR

Carl 8 月之前
父節點
當前提交
e88a1655d8

+ 5 - 3
ebpftracer/ebpf/l7/l7.c

@@ -29,6 +29,7 @@
 #define METHOD_HTTP2_CLIENT_FRAMES  5
 #define METHOD_HTTP2_SERVER_FRAMES  6
 
+#define ERROR_MSG_PAYLOAD_SIZE 128
 #define MAX_PAYLOAD_SIZE 1024 // must be power of 2
 #define TRUNCATE_PAYLOAD_SIZE(size) ({                                  \
     size = MIN(size, MAX_PAYLOAD_SIZE-1);                               \
@@ -95,7 +96,8 @@ struct l7_event {
 	unsigned char app_id_from[APM_APP_ID_SIZE];
 	unsigned char span_id_from[APM_SPAN_ID_SIZE];
     unsigned char type_from[APM_TYPE_FROM_SIZE];
-
+    // 错误消息字段
+    unsigned char error_message[ERROR_MSG_PAYLOAD_SIZE];
 //    __u32 test_id;
     char payload[MAX_PAYLOAD_SIZE];
 } ;
@@ -1013,12 +1015,12 @@ int trace_exit_read_common(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long in
             char resp_combined_packet[5];
             bpf_probe_read(resp_combined_packet,MYSQL_PACKAGE_HEADER_LEN, resp_packet_header);
             bpf_probe_read(&resp_combined_packet[4],1, payload);
-            response = is_mysql_response(resp_combined_packet,sizeof(resp_combined_packet), req->request_type, &e->statement_id, &e->status);
+            response = is_mysql_response(resp_combined_packet,sizeof(resp_combined_packet), req->request_type, &e->statement_id, &e->status, e->error_message);
             if(response) {
                  bpf_map_delete_elem(&active_l7_requests_mysql_resp_header_ctx, &k);
             }
         } else {
-            response = is_mysql_response(payload, ret, req->request_type, &e->statement_id, &e->status);
+            response = is_mysql_response(payload, ret, req->request_type, &e->statement_id, &e->status, e->error_message);
         }
 		if (req->request_type == MYSQL_COM_STMT_PREPARE) {
 			e->method = METHOD_STATEMENT_PREPARE;

+ 26 - 6
ebpftracer/ebpf/l7/mysql.c

@@ -38,7 +38,7 @@ int is_mysql_query(char *buf, __u64 buf_size, __u8 *request_type) {
 }
 
 static __always_inline
-int is_mysql_response(char *buf, __u64 buf_size, __u8 request_type, __u32 *statement_id, __u32 *status) {
+int is_mysql_response(char *buf, __u64 buf_size, __u8 request_type, __u32 *statement_id, __u32 *status,unsigned char *error_message) {
     __u8 b[5];
     bpf_read(buf, b);
     if (b[3] < 1) { // sequence must be > 0
@@ -56,9 +56,29 @@ int is_mysql_response(char *buf, __u64 buf_size, __u8 request_type, __u32 *state
         *status = STATUS_OK;
         return 1;
     }
-    if (b[4] == MYSQL_RESPONSE_ERROR) {
-        *status = STATUS_FAILED;
-        return 1;
-    }
-    return 0;
+	if (b[4] == MYSQL_RESPONSE_ERROR) {
+		// MySQL错误包格式:
+		// packet_length(3) + packet_number(1) + 0xff(1) + error_code(2) + sql_state_marker(1) + sql_state(5) + error_message(variable)
+		__u16 error_code = 0;
+//		char error_message[128] = {};
+		__u8 error_message_len = 0;
+
+		// 读取错误代码(2字节,小端序)
+		if (buf_size >= 7) {
+			bpf_probe_read(&error_code, sizeof(error_code), buf + 5);
+			error_code = bpf_ntohs(error_code); // 转换为主机字节序
+		}
+
+		// 读取错误消息(从第13字节开始,跳过sql_state)
+		if (buf_size > 13) {
+			error_message_len = (__u8) (length - 9); // 总长度 - 头部(4) - 错误码(2) - sql_state(6)
+			bpf_probe_read(error_message, error_message_len, buf + 13);
+			error_message[error_message_len] = '\0'; // 确保字符串结束
+		}
+		// 输出错误信息到内核日志
+		bpf_printk("MySQL Error %d: %s", error_code, error_message);
+		*status = STATUS_FAILED;
+		return 1;
+	}
+	return 0;
 }

+ 2 - 1
ebpftracer/l7/l7.go

@@ -206,6 +206,7 @@ type RequestData struct {
 		InstanceIdFrom string
 		AppIdFrom      string
 		SpanIdFrom     string
-		TypeFrom	   string
+		TypeFrom       string
 	}
+	ErrorMsg string
 }

+ 7 - 5
ebpftracer/tracer.go

@@ -537,12 +537,12 @@ type l7Event struct {
 	EventCount          uint32
 	Sport               uint16
 	Dport               uint16
-	SAddr               [16]byte
-	DAddr               [16]byte
+	SAddr               HashByte16
+	DAddr               HashByte16
 	ComponentSport      uint16
 	ComponentDport      uint16
-	ComponentSAddr      [16]byte
-	ComponentDAddr      [16]byte
+	ComponentSAddr      HashByte16
+	ComponentDAddr      HashByte16
 	AssumedAppId        HashByte
 	SpanId              HashByte
 	TraceIdFrom         HashByte16
@@ -550,7 +550,8 @@ type l7Event struct {
 	InstanceIdFrom      HashByte
 	AppIdFrom           HashByte
 	SpanIdFrom          HashByte
-	TypeFrom          	[1]byte
+	TypeFrom            [1]byte
+	ErrorMsg            HashByte128
 }
 
 type SocketDataBufferddd struct {
@@ -779,6 +780,7 @@ func runEventsReader(name string, r *perf.Reader, ch chan<- Event, typ perfMapTy
 				EndAt:          v.EndtAt,
 				ComponentSAddr: ipPort(v.ComponentSAddr, v.ComponentSport),
 				ComponentDAddr: ipPort(v.ComponentDAddr, v.ComponentDport),
+				ErrorMsg:       strings.TrimRight(string(v.ErrorMsg[:]), "\x00"),
 			}
 			if req.Protocol == l7.ProtocolHTTP {
 				klog.Debugf("runEventsReader ComponentSAddr.String %s", req.ComponentSAddr.String())

+ 2 - 0
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/apm_exporter.go

@@ -900,6 +900,8 @@ func buildSQLMapEvent(mNode *MapInfoT, event tracesdk.Event) {
 			} else {
 				mNode.Exception = 0
 			}
+		case "sql.exception_msg":
+			mNode.ExceptionMsg = attr.Value.AsString()
 		case "sql.src_addr":
 			mNode.SrcAddr = attr.Value.AsString()
 		case "sql.destination_addr":

+ 5 - 0
tracing/apm_tracing.go

@@ -417,6 +417,11 @@ func (t *Trace) SQLTraceQueryEvent(l7Type l7.Protocol, semconvVal attribute.KeyV
 	attr = append(attr,
 		attribute.Bool("sql.exception", r.Status.Error()),
 	)
+	if r.Status.Error() {
+		attr = append(attr,
+			attribute.String("sql.exception_msg", r.ErrorMsg),
+		)
+	}
 	t.createTraceEvent(l7Type.String(), ebpftracer.EventTypeL7Request.Int(), l7Type.Int(), attr...)
 }
 

+ 5 - 2
utils/modelse/id.go

@@ -3,8 +3,9 @@ package modelse
 import "strconv"
 
 const (
-	HASH_SIZE_8  = 8
-	HASH_SIZE_16 = 16
+	HASH_SIZE_8   = 8
+	HASH_SIZE_16  = 16
+	HASH_SIZE_128 = 128
 )
 
 // apm_span_context
@@ -23,6 +24,8 @@ type HashByte [HASH_SIZE_8]byte
 
 type HashByte16 [HASH_SIZE_16]byte
 
+type HashByte128 [HASH_SIZE_128]byte
+
 type INT_HASH_ID struct {
 	IntVal   int64
 	HashtVal HashByte