Просмотр исходного кода

capturing Postgres queries at the eBPF level

Nikolay Sivko 3 лет назад
Родитель
Сommit
12d24bf447

+ 2 - 0
.dockerignore

@@ -0,0 +1,2 @@
+**/.git/
+**/.idea/

+ 61 - 40
containers/container.go

@@ -65,7 +65,7 @@ type ActiveConnection struct {
 	Fd         uint64
 }
 
-type HttpStats struct {
+type L7Stats struct {
 	Requests *prometheus.CounterVec
 	Latency  prometheus.Histogram
 }
@@ -90,8 +90,9 @@ type Container struct {
 	connectsFailed     map[netaddr.IPPort]int64     // dst -> count
 	connectLastAttempt map[netaddr.IPPort]time.Time // dst -> time
 	connectionsActive  map[AddrPair]ActiveConnection
-	retransmits        map[AddrPair]int64      // dst:actual_dst -> count
-	http               map[AddrPair]*HttpStats // dst:actual_dst -> stats
+	retransmits        map[AddrPair]int64 // dst:actual_dst -> count
+
+	l7Stats map[ebpftracer.L7Protocol]map[AddrPair]*L7Stats // protocol -> dst:actual_dst -> stats
 
 	oomKills int
 
@@ -120,7 +121,7 @@ func NewContainer(cg *cgroup.Cgroup, md *ContainerMetadata) *Container {
 		connectLastAttempt: map[netaddr.IPPort]time.Time{},
 		connectionsActive:  map[AddrPair]ActiveConnection{},
 		retransmits:        map[AddrPair]int64{},
-		http:               map[AddrPair]*HttpStats{},
+		l7Stats:            map[ebpftracer.L7Protocol]map[AddrPair]*L7Stats{},
 
 		mountIds: map[string]struct{}{},
 
@@ -162,9 +163,11 @@ func (c *Container) Describe(ch chan<- *prometheus.Desc) {
 	for _, m := range metricsList {
 		ch <- m
 	}
-	for _, s := range c.http {
-		s.Requests.Describe(ch)
-		s.Latency.Describe(ch)
+	for _, protoStats := range c.l7Stats {
+		for _, s := range protoStats {
+			s.Requests.Describe(ch)
+			s.Latency.Describe(ch)
+		}
 	}
 }
 
@@ -290,9 +293,11 @@ func (c *Container) Collect(ch chan<- prometheus.Metric) {
 		ch <- gauge(metrics.ApplicationType, 1, appType)
 	}
 
-	for _, stats := range c.http {
-		stats.Requests.Collect(ch)
-		stats.Latency.Collect(ch)
+	for _, protoStats := range c.l7Stats {
+		for _, s := range protoStats {
+			s.Requests.Collect(ch)
+			s.Latency.Collect(ch)
+		}
 	}
 
 	if !*flags.DisablePinger {
@@ -419,39 +424,53 @@ func (c *Container) onConnectionClose(srcDst AddrPair) bool {
 	return true
 }
 
-func (c *Container) onHttpRequest(pid uint32, fd uint64, r *ebpftracer.HttpRequest) {
+func (c *Container) onL7Request(pid uint32, fd uint64, r *ebpftracer.L7Request) {
 	for dest, conn := range c.connectionsActive {
 		if conn.Pid == pid && conn.Fd == fd {
 			key := AddrPair{src: dest.dst, dst: conn.ActualDest}
-			s := c.http[key]
+			stats := c.l7Stats[r.Protocol]
+			if stats == nil {
+				stats = map[AddrPair]*L7Stats{}
+				c.l7Stats[r.Protocol] = stats
+			}
+			s := stats[key]
 			if s == nil {
-				requests := prometheus.NewCounterVec(
-					prometheus.CounterOpts{
-						Name: "container_http_requests_total",
-						Help: "Total number of outbound HTTP requests",
-						ConstLabels: map[string]string{
-							"destination":        dest.dst.String(),
-							"actual_destination": conn.ActualDest.String(),
-						},
-					},
-					[]string{"status"},
-				)
-				latency := prometheus.NewHistogram(
-					prometheus.HistogramOpts{
-						Name: "container_http_request_duration_seconds_total",
-						Help: "Histogram of the response time for each outbound HTTP request",
-						ConstLabels: map[string]string{
-							"destination":        dest.dst.String(),
-							"actual_destination": conn.ActualDest.String(),
-						},
-					},
-				)
-				s = &HttpStats{Requests: requests, Latency: latency}
-				c.http[key] = s
+				constLabels := map[string]string{"destination": key.src.String(), "actual_destination": key.dst.String()}
+				cOpts, ok := L7Requests[r.Protocol]
+				if !ok {
+					klog.Warningln("cannot find metric description for L7 protocol: %s", r.Protocol.String())
+					return
+				}
+				hOpts, ok := L7Latency[r.Protocol]
+				if !ok {
+					klog.Warningln("cannot find metric description for L7 protocol: %s", r.Protocol.String())
+					return
+				}
+				s = &L7Stats{
+					Requests: prometheus.NewCounterVec(
+						prometheus.CounterOpts{Name: cOpts.Name, Help: cOpts.Help, ConstLabels: constLabels},
+						[]string{"status"},
+					),
+					Latency: prometheus.NewHistogram(
+						prometheus.HistogramOpts{Name: hOpts.Name, Help: hOpts.Help, ConstLabels: constLabels},
+					),
+				}
+				stats[key] = s
 			}
-			s.Requests.WithLabelValues(strconv.Itoa(r.Status)).Inc()
+			status := ""
+			switch r.Protocol {
+			case ebpftracer.L7ProtocolHTTP:
+				status = strconv.Itoa(r.Status)
+			case ebpftracer.L7ProtocolPostgres:
+				if r.Status == 500 {
+					status = "failed"
+				} else {
+					status = "ok"
+				}
+			}
+			s.Requests.WithLabelValues(status).Inc()
 			s.Latency.Observe(r.Duration.Seconds())
-			break
+			return
 		}
 	}
 }
@@ -732,9 +751,11 @@ func (c *Container) gc(now time.Time) {
 					delete(c.retransmits, d)
 				}
 			}
-			for d := range c.http {
-				if d.src == dst {
-					delete(c.http, d)
+			for _, protoStats := range c.l7Stats {
+				for d := range protoStats {
+					if d.src == dst {
+						delete(protoStats, d)
+					}
 				}
 			}
 		}

+ 12 - 0
containers/metrics.go

@@ -1,6 +1,7 @@
 package containers
 
 import (
+	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/prometheus/client_golang/prometheus"
 	"reflect"
 )
@@ -71,6 +72,17 @@ var metrics = struct {
 	ApplicationType: metric("container_application_type", "Type of the application running in the container (e.g. memcached, postgres, mysql)", "application_type"),
 }
 
+var (
+	L7Requests = map[ebpftracer.L7Protocol]prometheus.CounterOpts{
+		ebpftracer.L7ProtocolHTTP:     {Name: "container_http_requests_total", Help: "Total number of outbound HTTP requests"},
+		ebpftracer.L7ProtocolPostgres: {Name: "container_postgres_queries_total", Help: "Total number of outbound Postgres queries"},
+	}
+	L7Latency = map[ebpftracer.L7Protocol]prometheus.HistogramOpts{
+		ebpftracer.L7ProtocolHTTP:     {Name: "container_http_request_duration_seconds_total", Help: "Histogram of the response time for each outbound HTTP request"},
+		ebpftracer.L7ProtocolPostgres: {Name: "container_postgres_queries_duration_seconds_total", Help: "Histogram of the execution time for each outbound Postgres query"},
+	}
+)
+
 func metric(name, help string, labels ...string) *prometheus.Desc {
 	return prometheus.NewDesc(name, help, labels, nil)
 }

+ 4 - 3
containers/registry.go

@@ -202,12 +202,13 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 						break
 					}
 				}
-			case ebpftracer.EventTypeHTTPRequest:
-				if e.HttpRequest == nil {
+			case ebpftracer.EventTypeL7Request:
+				if e.L7Request == nil {
 					continue
 				}
+				//klog.Infof("L7 request proto:%s pid:%d fd:%d", e.L7Request.Protocol.String(), e.Pid, e.Fd)
 				if c := r.containersByPid[e.Pid]; c != nil {
-					c.onHttpRequest(e.Pid, e.Fd, e.HttpRequest)
+					c.onL7Request(e.Pid, e.Fd, e.L7Request)
 				}
 			}
 		}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
ebpftracer/ebpf.go


+ 1 - 0
ebpftracer/ebpf/ebpf.c

@@ -2,6 +2,7 @@
 #include <bpf/bpf_helpers.h>
 #include <bpf/bpf_core_read.h>
 #include <bpf/bpf_tracing.h>
+#include <bpf/bpf_endian.h>
 
 #define EVENT_TYPE_PROCESS_START	1
 #define EVENT_TYPE_PROCESS_EXIT		2

+ 60 - 0
ebpftracer/ebpf/http.c

@@ -0,0 +1,60 @@
+
+static __always_inline
+int is_http_request(char *buf) {
+    char b[16];
+    if (bpf_probe_read_str(&b, sizeof(b), (void *)buf) < 16) {
+        return 0;
+    }
+    if (b[0] == 'G' && b[1] == 'E' && b[2] == 'T') {
+        return 1;
+    }
+    if (b[0] == 'P' && b[1] == 'O' && b[2] == 'S' && b[3] == 'T') {
+        return 1;
+    }
+    if (b[0] == 'H' && b[1] == 'E' && b[2] == 'A' && b[3] == 'D') {
+        return 1;
+    }
+    if (b[0] == 'P' && b[1] == 'U' && b[2] == 'T') {
+        return 1;
+    }
+    if (b[0] == 'D' && b[1] == 'E' && b[2] == 'L' && b[3] == 'E' && b[4] == 'T' && b[5] == 'E') {
+        return 1;
+    }
+    if (b[0] == 'C' && b[1] == 'O' && b[2] == 'N' && b[3] == 'N' && b[4] == 'E' && b[5] == 'C' && b[6] == 'T') {
+        return 1;
+    }
+    if (b[0] == 'O' && b[1] == 'P' && b[2] == 'T' && b[3] == 'I' && b[4] == 'O' && b[5] == 'N' && b[6] == 'S') {
+        return 1;
+    }
+    if (b[0] == 'P' && b[1] == 'A' && b[2] == 'T' && b[3] == 'C' && b[4] == 'H') {
+        return 1;
+    }
+    return 0;
+}
+
+static __always_inline
+__u32 parse_http_status(char *buf) {
+    char b[16];
+    if (bpf_probe_read_str(&b, sizeof(b), (void *)buf) < 16) {
+        return 0;
+    }
+    if (b[0] != 'H' || b[1] != 'T' || b[2] != 'T' || b[3] != 'P' || b[4] != '/') {
+        return 0;
+    }
+    if (b[5] < '0' || b[5] > '9') {
+        return 0;
+    }
+    if (b[6] != '.') {
+        return 0;
+    }
+    if (b[7] < '0' || b[7] > '9') {
+        return 0;
+    }
+    if (b[8] != ' ') {
+        return 0;
+    }
+    if (b[9] < '0' || b[9] > '9' || b[10] < '0' || b[10] > '9' || b[11] < '0' || b[11] > '9') {
+        return 0;
+    }
+    return (b[9]-'0')*100 + (b[10]-'0')*10 + (b[11]-'0');
+}

+ 64 - 94
ebpftracer/ebpf/l7.c

@@ -1,6 +1,28 @@
+#include "http.c"
+#include "postgres.c"
+
+#define PROTOCOL_UNKNOWN    0
+#define PROTOCOL_HTTP	    1
+#define PROTOCOL_POSTGRES	2
+
+struct l7_event {
+	__u64 fd;
+	__u32 pid;
+    __u32 status;
+    __u64 duration;
+    __u8 protocol;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+	__uint(key_size, sizeof(int));
+	__uint(value_size, sizeof(int));
+} l7_events SEC(".maps");
+
+
 struct rw_args_t {
     __u64 fd;
-    const char* buf;
+    char* buf;
 };
 
 struct {
@@ -15,18 +37,24 @@ struct socket_key {
     __u32 pid;
 };
 
+struct l7_request {
+    __u64 ns;
+    __u8 protocol;
+};
+
 struct {
 	__uint(type, BPF_MAP_TYPE_HASH);
 	__uint(key_size, sizeof(struct socket_key));
-	__uint(value_size, sizeof(__u64));
+	__uint(value_size, sizeof(struct l7_request));
 	__uint(max_entries, 10240);
-} active_http_requests SEC(".maps");
+} active_l7_requests SEC(".maps");
 
 struct trace_event_raw_sys_enter_rw__stub {
 	__u64 unused;
 	long int id;
 	__u64 fd;
-	const char* buf;
+	char* buf;
+	__u64 size;
 };
 
 struct trace_event_raw_sys_exit_rw__stub {
@@ -35,87 +63,29 @@ struct trace_event_raw_sys_exit_rw__stub {
 	long int ret;
 };
 
-struct http_event {
-	__u64 fd;
-	__u32 pid;
-    __u32 status;
-    __u64 duration;
-};
-
-struct {
-	__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
-	__uint(key_size, sizeof(int));
-	__uint(value_size, sizeof(int));
-} http_events SEC(".maps");
-
-
-static __always_inline
-int is_http_request(char b[16]) {
-    if (b[0] == 'G' && b[1] == 'E' && b[2] == 'T') {
-        return 1;
-    }
-    if (b[0] == 'P' && b[1] == 'O' && b[2] == 'S' && b[3] == 'T') {
-        return 1;
-    }
-    if (b[0] == 'H' && b[1] == 'E' && b[2] == 'A' && b[3] == 'D') {
-        return 1;
-    }
-    if (b[0] == 'P' && b[1] == 'U' && b[2] == 'T') {
-        return 1;
-    }
-    if (b[0] == 'D' && b[1] == 'E' && b[2] == 'L' && b[3] == 'E' && b[4] == 'T' && b[5] == 'E') {
-        return 1;
-    }
-    if (b[0] == 'C' && b[1] == 'O' && b[2] == 'N' && b[3] == 'N' && b[4] == 'E' && b[5] == 'C' && b[6] == 'T') {
-        return 1;
-    }
-    if (b[0] == 'O' && b[1] == 'P' && b[2] == 'T' && b[3] == 'I' && b[4] == 'O' && b[5] == 'N' && b[6] == 'S') {
-        return 1;
-    }
-    if (b[0] == 'P' && b[1] == 'A' && b[2] == 'T' && b[3] == 'C' && b[4] == 'H') {
-        return 1;
-    }
-    return 0;
-}
-static __always_inline
-__u32 parse_http_status(char b[16]) {
-    if (b[0] != 'H' || b[1] != 'T' || b[2] != 'T' || b[3] != 'P' || b[4] != '/') {
-        return 0;
-    }
-    if (b[5] < '0' || b[5] > '9') {
-        return 0;
-    }
-    if (b[6] != '.') {
-        return 0;
-    }
-    if (b[7] < '0' || b[7] > '9') {
-        return 0;
-    }
-    if (b[8] != ' ') {
-        return 0;
-    }
-    if (b[9] < '0' || b[9] > '9' || b[10] < '0' || b[10] > '9' || b[11] < '0' || b[11] > '9') {
-        return 0;
-    }
-    return (b[9]-'0')*100 + (b[10]-'0')*10 + (b[11]-'0');
-}
+#define bpf_printk(fmt, ...)                            \
+({                                                      \
+        char ____fmt[] = fmt;                           \
+        bpf_trace_printk(____fmt, sizeof(____fmt),      \
+                         ##__VA_ARGS__);                \
+})
 
 static inline __attribute__((__always_inline__))
-int trace_http_request(__u64 fd, void *buf) {
+int trace_enter_write(__u64 fd, char *buf, __u64 size) {
     __u32 pid = bpf_get_current_pid_tgid() >> 32;
-    char p[16];
-    long ret = bpf_probe_read_str(&p, sizeof(p), buf);
-    if (ret < 16) {
-        return 0;
-    }
-    if (!is_http_request(p)) {
+    struct l7_request req = {};
+    if (is_http_request(buf)) {
+        req.protocol = PROTOCOL_HTTP;
+    } else if (is_postgres_query(buf, size)) {
+        req.protocol = PROTOCOL_POSTGRES;
+    } else {
         return 0;
     }
+    req.ns = bpf_ktime_get_ns();
     struct socket_key k = {};
     k.pid = pid;
     k.fd = fd;
-    __u64 ns = bpf_ktime_get_ns();
-    bpf_map_update_elem(&active_http_requests, &k, &ns, BPF_ANY);
+    bpf_map_update_elem(&active_l7_requests, &k, &req, BPF_ANY);
     return 0;
 }
 
@@ -144,27 +114,27 @@ int trace_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
     struct socket_key k = {};
     k.pid = id >> 32;
     k.fd = args->fd;
-    __u64 *req_start = bpf_map_lookup_elem(&active_http_requests, &k);
-    if (!req_start) {
-        return 0;
-    }
-    bpf_map_delete_elem(&active_http_requests, &k);
-    char p[16];
-    long ret = bpf_probe_read_str(&p, sizeof(p), (void *)args->buf);
-    if (ret < 16) {
+
+    struct l7_request *req = bpf_map_lookup_elem(&active_l7_requests, &k);
+    if (!req) {
         return 0;
     }
-    __u32 status = parse_http_status(p);
+    bpf_map_delete_elem(&active_l7_requests, &k);
 
-    if (status <= 0) {
+    struct l7_event e = {};
+    if (req->protocol == PROTOCOL_HTTP) {
+        e.status = parse_http_status(args->buf);
+    } else if (req->protocol == PROTOCOL_POSTGRES) {
+        e.status = parse_postgres_status(args->buf, ctx->ret);
+    }
+    if (e.status == 0) {
         return 0;
     }
-    struct http_event e = {};
+    e.protocol = req->protocol;
     e.fd = k.fd;
     e.pid = k.pid;
-    e.status = status;
-    e.duration = bpf_ktime_get_ns() - *req_start;
-    bpf_perf_event_output(ctx, &http_events, BPF_F_CURRENT_CPU, &e, sizeof(e));
+    e.duration = bpf_ktime_get_ns() - req->ns;
+    bpf_perf_event_output(ctx, &l7_events, BPF_F_CURRENT_CPU, &e, sizeof(e));
     return 0;
 }
 
@@ -174,17 +144,17 @@ int sys_enter_writev(struct trace_event_raw_sys_enter_rw__stub* ctx) {
     if (bpf_probe_read(&vec, sizeof(void*), (void *)ctx->buf) < 0) {
         return 0;
     }
-    return trace_http_request(ctx->fd, vec);
+    return trace_enter_write(ctx->fd, vec, 0);
 }
 
 SEC("tracepoint/syscalls/sys_enter_write")
 int sys_enter_write(struct trace_event_raw_sys_enter_rw__stub* ctx) {
-    return trace_http_request(ctx->fd, (void *)ctx->buf);
+    return trace_enter_write(ctx->fd, ctx->buf, ctx->size);
 }
 
 SEC("tracepoint/syscalls/sys_enter_sendto")
 int sys_enter_sendto(struct trace_event_raw_sys_enter_rw__stub* ctx) {
-    return trace_http_request(ctx->fd, (void *)ctx->buf);
+    return trace_enter_write(ctx->fd, ctx->buf, ctx->size);
 }
 
 SEC("tracepoint/syscalls/sys_enter_read")

+ 62 - 0
ebpftracer/ebpf/postgres.c

@@ -0,0 +1,62 @@
+// Postgres wire protocol
+// https://www.pgcon.org/2014/schedule/attachments/330_postgres-for-the-wire.pdf
+// https://www.postgresql.org/docs/current/protocol-message-formats.html
+
+static __always_inline
+int is_postgres_query(char *buf, int buf_size) {
+    if (buf_size < 1) {
+        return 0;
+    }
+    char f_cmd;
+    int f_length;
+    if (bpf_probe_read(&f_cmd, sizeof(f_cmd), (void *)((char *)buf)) < 0) {
+        return 0;
+    }
+    if (bpf_probe_read(&f_length, sizeof(f_length), (void *)((char *)buf+1)) < 0) {
+        return 0;
+    }
+    f_length = bpf_ntohl(f_length);
+    if (f_cmd == 'Q' && f_length+1 == buf_size) {
+        return 1;
+    }
+    char sync[5];
+    if (bpf_probe_read(&sync, sizeof(sync), (void *)((char *)buf+buf_size-5)) < 0) {
+        return 0;
+    }
+    if (sync[0] == 'S' && sync[1] == 0 && sync[2] == 0 && sync[3] == 0 && sync[4] == 4) {
+        return 1;
+    }
+    return 0;
+}
+
+static __always_inline
+__u32 parse_postgres_status(char *buf, int buf_size) {
+    char cmd;
+    int length;
+    if (bpf_probe_read(&cmd, sizeof(cmd), (void *)((char *)buf)) < 0) {
+        return 0;
+    }
+    if (bpf_probe_read(&length, sizeof(length), (void *)((char *)buf+1)) < 0) {
+        return 0;
+    }
+    length = bpf_ntohl(length);
+
+    if (length+1 > buf_size) {
+        return 0;
+    }
+    if (cmd == '2' && length == 4 && buf_size >= 10) {
+        if (bpf_probe_read(&cmd, sizeof(cmd), (void *)((char *)buf+5)) < 0) {
+            return 0;
+        }
+        if (bpf_probe_read(&length, sizeof(length), (void *)((char *)buf+5+1)) < 0) {
+            return 0;
+        }
+    }
+    if (cmd == 'E') {
+        return 500;
+    }
+    if (cmd == 'T' || cmd == 'D' || cmd == 'C') {
+        return 200;
+    }
+    return 0;
+}

+ 35 - 15
ebpftracer/tracer.go

@@ -33,25 +33,43 @@ const (
 	EventTypeListenClose     EventType = 7
 	EventTypeFileOpen        EventType = 8
 	EventTypeTCPRetransmit   EventType = 9
-	EventTypeHTTPRequest     EventType = 10
+	EventTypeL7Request       EventType = 10
 
 	EventReasonNone    EventReason = 0
 	EventReasonOOMKill EventReason = 1
 )
 
-type HttpRequest struct {
+type L7Protocol uint8
+
+const (
+	L7ProtocolHTTP     L7Protocol = 1
+	L7ProtocolPostgres L7Protocol = 2
+)
+
+func (p L7Protocol) String() string {
+	switch p {
+	case L7ProtocolHTTP:
+		return "HTTP"
+	case L7ProtocolPostgres:
+		return "Postgres"
+	}
+	return "UNKNOWN:" + strconv.Itoa(int(p))
+}
+
+type L7Request struct {
+	Protocol L7Protocol
 	Status   int
 	Duration time.Duration
 }
 
 type Event struct {
-	Type        EventType
-	Reason      EventReason
-	Pid         uint32
-	SrcAddr     netaddr.IPPort
-	DstAddr     netaddr.IPPort
-	Fd          uint64
-	HttpRequest *HttpRequest
+	Type      EventType
+	Reason    EventReason
+	Pid       uint32
+	SrcAddr   netaddr.IPPort
+	DstAddr   netaddr.IPPort
+	Fd        uint64
+	L7Request *L7Request
 }
 
 type Tracer struct {
@@ -159,7 +177,7 @@ func (t *Tracer) ebpf(ch chan<- Event, kernelVersion string, disableL7Tracing bo
 		"file_events":           &fileEvent{},
 	}
 	if !disableL7Tracing {
-		events["http_events"] = &httpEvent{}
+		events["l7_events"] = &l7Event{}
 	}
 
 	for name, typ := range events {
@@ -226,8 +244,8 @@ func (t EventType) String() string {
 		return "file-open"
 	case EventTypeTCPRetransmit:
 		return "tcp-retransmit"
-	case EventTypeHTTPRequest:
-		return "http-request"
+	case EventTypeL7Request:
+		return "l7-request"
 	}
 	return "unknown: " + strconv.Itoa(int(t))
 }
@@ -280,15 +298,17 @@ func (e fileEvent) Event() Event {
 	return Event{Type: EventType(e.Type), Pid: e.Pid, Fd: e.Fd}
 }
 
-type httpEvent struct {
+type l7Event struct {
 	Fd       uint64
 	Pid      uint32
 	Status   uint32
 	Duration uint64
+	Protocol uint8
 }
 
-func (e httpEvent) Event() Event {
-	return Event{Type: EventTypeHTTPRequest, Pid: e.Pid, Fd: e.Fd, HttpRequest: &HttpRequest{
+func (e l7Event) Event() Event {
+	return Event{Type: EventTypeL7Request, Pid: e.Pid, Fd: e.Fd, L7Request: &L7Request{
+		Protocol: L7Protocol(e.Protocol),
 		Status:   int(e.Status),
 		Duration: time.Duration(e.Duration),
 	}}

Некоторые файлы не были показаны из-за большого количества измененных файлов