From 419ea63dd0f7f2a693dbf674e411782301e06dc6 Mon Sep 17 00:00:00 2001
From: fgsfds <4870330+fgsfds@users.noreply.github.com>
Date: Tue, 5 Aug 2025 15:10:14 +0500
Subject: [PATCH] Added filters to the xray logs viewer (#3314)
* added filters to xray logs viewer
* better freedom/blackhole tags handling
* better freedom/blackhole tags handling 2
* fix comments
* fix comments 2
---
web/controller/server.go | 43 ++++++++++++++++++++++++++++--
web/html/index.html | 27 +++++++++++++------
web/service/server.go | 56 +++++++++++++++++++++++++++++++++++-----
3 files changed, 110 insertions(+), 16 deletions(-)
diff --git a/web/controller/server.go b/web/controller/server.go
index dd001f5e..17b25cdc 100644
--- a/web/controller/server.go
+++ b/web/controller/server.go
@@ -17,7 +17,8 @@ var filenameRegex = regexp.MustCompile(`^[a-zA-Z0-9_\-.]+$`)
type ServerController struct {
BaseController
- serverService service.ServerService
+ serverService service.ServerService
+ settingService service.SettingService
lastStatus *service.Status
lastGetStatusTime time.Time
@@ -137,7 +138,45 @@ func (a *ServerController) getLogs(c *gin.Context) {
func (a *ServerController) getXrayLogs(c *gin.Context) {
count := c.Param("count")
- logs := a.serverService.GetXrayLogs(count)
+ filter := c.PostForm("filter")
+ showDirect := c.PostForm("showDirect")
+ showBlocked := c.PostForm("showBlocked")
+ showProxy := c.PostForm("showProxy")
+
+ var freedoms []string
+ var blackholes []string
+
+ //getting tags for freedom and blackhole outbounds
+ config, err := a.settingService.GetDefaultXrayConfig()
+ if err == nil && config != nil {
+ if cfgMap, ok := config.(map[string]interface{}); ok {
+ if outbounds, ok := cfgMap["outbounds"].([]interface{}); ok {
+ for _, outbound := range outbounds {
+ if obMap, ok := outbound.(map[string]interface{}); ok {
+ switch obMap["protocol"] {
+ case "freedom":
+ if tag, ok := obMap["tag"].(string); ok {
+ freedoms = append(freedoms, tag)
+ }
+ case "blackhole":
+ if tag, ok := obMap["tag"].(string); ok {
+ blackholes = append(blackholes, tag)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if len(freedoms) == 0 {
+ freedoms = []string{"direct"}
+ }
+ if len(blackholes) == 0 {
+ blackholes = []string{"blocked"}
+ }
+
+ logs := a.serverService.GetXrayLogs(count, filter, showDirect, showBlocked, showProxy, freedoms, blackholes)
jsonObj(c, logs, nil)
}
diff --git a/web/html/index.html b/web/html/index.html
index 4089e6a6..bc5483b0 100644
--- a/web/html/index.html
+++ b/web/html/index.html
@@ -457,6 +457,14 @@
+
+
+
+
+ Direct
+ Blocked
+ Proxy
+
@@ -651,6 +659,9 @@
visible: false,
logs: [],
rows: 20,
+ showDirect: true,
+ showBlocked: true,
+ showProxy: true,
loading: false,
show(logs) {
this.visible = true;
@@ -665,17 +676,17 @@
const parts = log.split(' ');
- if(parts.length === 9) {
+ if(parts.length === 10) {
const dateTime = `${parts[0]} ${parts[1]}`;
const from = `${parts[3]}`;
const to = `${parts[5].replace(/^\/+/, "")}`;
let outboundColor = '';
- if (parts[8].startsWith('blocked')) {
- outboundColor = ' style="color: #e04141;"';
+ if (parts[9] === "b") {
+ outboundColor = ' style="color: #e04141;"'; //red for blocked
}
- else if (!parts[8].startsWith('direct')) {
- outboundColor = ' style="color: #3c89e8;"';
+ else if (parts[9] === "p") {
+ outboundColor = ' style="color: #3c89e8;"'; //blue for proxies
}
formattedLogs += `
@@ -684,10 +695,10 @@ ${dateTime}
${from}
${parts[4]}
${to}
- ${parts.slice(6).join(' ')}
+ ${parts.slice(6, 9).join(' ')}
`;
} else {
- formattedLogs += `${parts.join(' ')}`;
+ formattedLogs += `${log}`;
}
});
@@ -817,7 +828,7 @@ ${dateTime}
},
async openXrayLogs(){
xraylogModal.loading = true;
- const msg = await HttpUtil.post('server/xraylogs/'+xraylogModal.rows);
+ const msg = await HttpUtil.post('server/xraylogs/'+xraylogModal.rows,{filter: xraylogModal.filter, showDirect: xraylogModal.showDirect, showBlocked: xraylogModal.showBlocked, showProxy: xraylogModal.showProxy});
if (!msg.success) {
return;
}
diff --git a/web/service/server.go b/web/service/server.go
index 2b174e8b..ee13b268 100644
--- a/web/service/server.go
+++ b/web/service/server.go
@@ -482,8 +482,16 @@ func (s *ServerService) GetLogs(count string, level string, syslog string) []str
return lines
}
-func (s *ServerService) GetXrayLogs(count string) []string {
- c, _ := strconv.Atoi(count)
+func (s *ServerService) GetXrayLogs(
+ count string,
+ filter string,
+ showDirect string,
+ showBlocked string,
+ showProxy string,
+ freedoms []string,
+ blackholes []string) []string {
+
+ countInt, _ := strconv.Atoi(count)
var lines []string
pathToAccessLog, err := xray.GetAccessLogPath()
@@ -498,21 +506,57 @@ func (s *ServerService) GetXrayLogs(count string) []string {
defer file.Close()
scanner := bufio.NewScanner(file)
+
for scanner.Scan() {
- line := scanner.Text()
- if strings.TrimSpace(line) == "" || strings.Contains(line, "api -> api") {
+ line := strings.TrimSpace(scanner.Text())
+
+ if line == "" || strings.Contains(line, "api -> api") {
+ //skipping empty lines and api calls
continue
}
+
+ if filter != "" && !strings.Contains(line, filter) {
+ //applying filter if it's not empty
+ continue
+ }
+
+ //adding suffixes to further distinguish entries by outbound
+ if hasSuffix(line, freedoms) {
+ if showDirect == "false" {
+ continue
+ }
+ line = line + " f"
+ } else if hasSuffix(line, blackholes) {
+ if showBlocked == "false" {
+ continue
+ }
+ line = line + " b"
+ } else {
+ if showProxy == "false" {
+ continue
+ }
+ line = line + " p"
+ }
+
lines = append(lines, line)
}
- if len(lines) > c {
- lines = lines[len(lines)-c:]
+ if len(lines) > countInt {
+ lines = lines[len(lines)-countInt:]
}
return lines
}
+func hasSuffix(line string, suffixes []string) bool {
+ for _, sfx := range suffixes {
+ if strings.HasSuffix(line, sfx+"]") {
+ return true
+ }
+ }
+ return false
+}
+
func (s *ServerService) GetConfigJson() (any, error) {
config, err := s.xrayService.GetXrayConfig()
if err != nil {