diff --git a/logger/logger.go b/logger/logger.go index 52b1977a..35c5c0ac 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "time" - "strings" "github.com/op/go-logging" ) @@ -127,35 +126,3 @@ func GetLogs(c int, level string) []string { } return output } - -func GetLogsSniffedDomains(c int) []string { - var output []string - logLevel, _ := logging.LogLevel("info") - - for i := len(logBuffer) - 1; i >= 0 && len(output) <= c; i-- { - if logBuffer[i].level <= logLevel && strings.Contains(logBuffer[i].log, "sniffed domain: ") { - index := strings.LastIndex(logBuffer[i].log, ": ") - if index != -1 { - domain := logBuffer[i].log[index+2:] - output = append(output, fmt.Sprintf("%s - %s", logBuffer[i].time, domain)) - } - } - } - return output -} - -func GetLogsBlockedDomains(c int) []string { - var output []string - logLevel, _ := logging.LogLevel("info") - - for i := len(logBuffer) - 1; i >= 0 && len(output) <= c; i-- { - if logBuffer[i].level <= logLevel && strings.Contains(logBuffer[i].log, "[blocked] for ") { - index := strings.LastIndex(logBuffer[i].log, "for [") - if index != -1 { - domain := strings.Replace(logBuffer[i].log[index+5:], "]", "", -1) - output = append(output, fmt.Sprintf("%s - %s", logBuffer[i].time, domain)) - } - } - } - return output -} diff --git a/web/controller/server.go b/web/controller/server.go index 15ccb258..35f4c688 100644 --- a/web/controller/server.go +++ b/web/controller/server.go @@ -45,8 +45,8 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) { g.POST("/restartXrayService", a.restartXrayService) g.POST("/installXray/:version", a.installXray) g.POST("/logs/:count", a.getLogs) - g.GET("/logs-sniffed/:count", a.getLogsSniffedDomains) - g.GET("/logs-blocked/:count", a.getLogsBlockedDomains) + g.POST("/access-log/:count", a.getAccessLog) + g.POST("/error-log/:count", a.getErrorLog) g.POST("/getConfigJson", a.getConfigJson) g.GET("/getDb", a.getDb) g.POST("/importDB", a.importDB) @@ -127,15 +127,17 @@ func (a *ServerController) getLogs(c *gin.Context) { jsonObj(c, logs, nil) } -func (a *ServerController) getLogsSniffedDomains(c *gin.Context) { +func (a *ServerController) getAccessLog(c *gin.Context) { count := c.Param("count") - logs := a.serverService.GetLogsSniffedDomains(count) + grep := c.PostForm("grep") + logs := a.serverService.GetAccessLog(count, grep) jsonObj(c, logs, nil) } -func (a *ServerController) getLogsBlockedDomains(c *gin.Context) { +func (a *ServerController) getErrorLog(c *gin.Context) { count := c.Param("count") - logs := a.serverService.GetLogsBlockedDomains(count) + grep := c.PostForm("grep") + logs := a.serverService.GetErrorLog(count, grep) jsonObj(c, logs, nil) } diff --git a/web/html/xui/index.html b/web/html/xui/index.html index 50902b8a..78aa1c39 100644 --- a/web/html/xui/index.html +++ b/web/html/xui/index.html @@ -125,7 +125,8 @@ {{ i18n "menu.link" }}: {{ i18n "pages.index.logs" }} - {{ i18n "pages.index.logDomains" }} + {{ i18n "pages.index.accessLog" }} + {{ i18n "pages.index.errorLog" }} {{ i18n "pages.index.config" }} {{ i18n "pages.index.backup" }} @@ -315,42 +316,87 @@
- - - Few - Medium - Many - - - Sniffed - Blocked + + 500 + 2500 + 7000 + + + + :href="'data:application/text;charset=utf-8,' + encodeURIComponent(accessLogModal.logs?.join('\n'))" download="xray-access.log"> -
+
+
+ + + + + + + 500 + 2500 + 7000 + + + + + + + + + + + +
0 ? this.formatLogs(this.logs, this.type) : "No Record..."; + this.formattedLogs = this.logs?.length > 0 ? this.formatLogs(this.logs) : "No Record..."; }, - formatLogs(logs, type) { + formatLogs(logs) { let formattedLogs = ''; + const levelColors = ["#3c89e8","#008771","#008771","#f37b24","#e04141","#bcbcbc"]; logs.forEach((log, index) => { - let [data, message] = log.split(" - ",2); - const parts = data.split(" "); - if(index>0) formattedLogs += '
'; + if (log.length <= 3) { + return; + } + let [date, time] = log.split(' ', 2); + let message = log.substr(date?.length !== undefined && time?.length !== undefined ? (date.length+time.length+2) : 0); + let messageColor = levelColors[5]; + if (message && message.indexOf('-> blocked') !== -1) { + messageColor = levelColors[4]; + } + + if (index > 0) formattedLogs += '
'; + formattedLogs += `${date} ${time} `; + formattedLogs += `- ${message}`; + }); - if (parts.length === 2) { - const d = parts[0]; - const t = parts[1]; - formattedLogs += `${d} ${t}`; + return formattedLogs; + }, + hide() { + this.visible = false; + }, + }; + + const errorLogModal = { + visible: false, + logs: [], + rows: 500, + grep: '', + loading: false, + show(logs) { + this.visible = true; + this.logs = logs; + this.formattedLogs = this.logs?.length > 0 ? this.formatLogs(this.logs) : "No Record..."; + }, + formatLogs(logs) { + let formattedLogs = ''; + const levels = ["DEBUG","INFO","NOTICE","WARNING","ERROR"]; + const levelsMap = {"[Debug]": levels[0], "[Info]": levels[1], "[Notice]": levels[2], "[Warning]": levels[3], "[Error]": levels[4]}; + const levelColors = ["#3c89e8","#008771","#008771","#f37b24","#e04141","#bcbcbc"]; + const idColors = ['#CADABF','#5F6F65','#FFDFD6','#BC9F8B','#C9DABF','#9CA986','#808D7C','#E7E8D8','#B5CFB7']; + let idColorIndex = 0; + let lastLogId = ''; + + logs.forEach((log, index) => { + if (log.length <= 3) { + return; + } + let [date, time, levelTag, id] = log.split(' ', 4); + if (date?.length === undefined || time?.length === undefined || levelTag?.length === undefined || id?.length === undefined) { + if (index > 0) formattedLogs += '
'; + formattedLogs += log; + } else { + let message = log.substr( + date?.length !== undefined && time?.length !== undefined && levelTag?.length !== undefined && id?.length !== undefined ? + (date.length + time.length + levelTag.length + id.length + 4) : 0 + ); + let level = levelsMap[levelTag]; + const levelIndex = levels.indexOf(level, levels) || 5; + + if (index > 0) formattedLogs += '
'; + formattedLogs += `${date} ${time} `; + formattedLogs += `${level} `; + formattedLogs += ' - '; + + if (id.substr(0, 1) === '[') { + let idColor = idColors[idColorIndex]; + if (lastLogId !== '' && lastLogId !== id) { + idColorIndex++; + } + if (idColorIndex >= idColors.length) { + idColorIndex = 0; + } + lastLogId = id; + formattedLogs += `${id} ${message}`; } else { - formattedLogs += `${data}`; + formattedLogs += `${id} ${message}`; } - - if (message) { - message = ""+(type === 'sniffed' ? 'Sniffed' : 'Blocked')+": " + message; - } - - formattedLogs += message ? ' - ' + message : ''; + } }); return formattedLogs; @@ -674,15 +781,25 @@ await PromiseUtil.sleep(500); logModal.loading = false; }, - async openLogDomains(){ - logDomainsModal.loading = true; - const msg = await HttpUtil.get('server/logs-'+(logDomainsModal.type==='blocked'?'blocked':'sniffed')+'/'+logDomainsModal.rows); + async openAccessLog() { + accessLogModal.loading = true; + const msg = await HttpUtil.post('server/access-log/'+accessLogModal.rows, { grep: accessLogModal.grep }); if (!msg.success) { return; } - logDomainsModal.show(msg.obj); + accessLogModal.show(msg.obj); await PromiseUtil.sleep(500); - logDomainsModal.loading = false; + accessLogModal.loading = false; + }, + async openErrorLog() { + errorLogModal.loading = true; + const msg = await HttpUtil.post('server/error-log/'+errorLogModal.rows, { grep: errorLogModal.grep }); + if (!msg.success) { + return; + } + errorLogModal.show(msg.obj); + await PromiseUtil.sleep(500); + errorLogModal.loading = false; }, async openConfig() { this.loading(true); diff --git a/web/service/server.go b/web/service/server.go index 21ab66f5..d8af59b3 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -448,22 +448,56 @@ func (s *ServerService) GetLogs(count string, level string, syslog string) []str return lines } -func (s *ServerService) GetLogsSniffedDomains(count string) []string { - c, _ := strconv.Atoi(count) - var lines []string +func (s *ServerService) GetAccessLog(count string, grep string) []string { + accessLogPath, err := xray.GetAccessLogPath() + if err != nil { + return []string{"Error in Access Log retrieval: " + err.Error()} + } - lines = logger.GetLogsSniffedDomains(c) - - return lines + if accessLogPath != "none" && accessLogPath != "" { + var cmdArgs []string + if grep != "" { + cmdArgs = []string{"bash", "-c", fmt.Sprintf("tail -n %s %s | grep '%s' | sort -r", count, accessLogPath, grep)} + } else { + cmdArgs = []string{"bash", "-c", fmt.Sprintf("tail -n %s %s | sort -r", count, accessLogPath)} + } + cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) + var out bytes.Buffer + cmd.Stdout = &out + err := cmd.Run() + if err != nil { + return []string{"Failed to run command: " + err.Error()} + } + return strings.Split(out.String(), "\n") + } else { + return []string{"Access Log disabled!"} + } } -func (s *ServerService) GetLogsBlockedDomains(count string) []string { - c, _ := strconv.Atoi(count) - var lines []string +func (s *ServerService) GetErrorLog(count string, grep string) []string { + errorLogPath, err := xray.GetErrorLogPath() + if err != nil { + return []string{"Error in Error Log retrieval: " + err.Error()} + } - lines = logger.GetLogsBlockedDomains(c) - - return lines + if errorLogPath != "none" && errorLogPath != "" { + var cmdArgs []string + if grep != "" { + cmdArgs = []string{"bash", "-c", fmt.Sprintf("tail -n %s %s | grep '%s' | sort -r", count, errorLogPath, grep)} + } else { + cmdArgs = []string{"bash", "-c", fmt.Sprintf("tail -n %s %s | sort -r", count, errorLogPath)} + } + cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) + var out bytes.Buffer + cmd.Stdout = &out + err := cmd.Run() + if err != nil { + return []string{"Failed to run command: " + err.Error()} + } + return strings.Split(out.String(), "\n") + } else { + return []string{"Error Log disabled!"} + } } func (s *ServerService) GetConfigJson() (interface{}, error) { diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 3fae7c5e..a6b661b2 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "Are you sure you want to change the Xray version to" "dontRefresh" = "Installation is in progress, please do not refresh this page" "logs" = "Logs" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "Config" "backup" = "Backup & Restore" "backupTitle" = "Database Backup & Restore" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index 170fedac..27928bcc 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "¿Estás seguro de que deseas cambiar la versión de Xray a" "dontRefresh" = "La instalación está en progreso, por favor no actualices esta página." "logs" = "Registros" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "Configuración" "backup" = "Copia de Seguridad y Restauración" "backupTitle" = "Copia de Seguridad y Restauración de la Base de Datos" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 09023f58..bf2bd972 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "آیا از تغییر نسخه‌ مطمئن هستید؟" "dontRefresh" = "در حال نصب، لطفا صفحه را رفرش نکنید" "logs" = "گزارش‌ها" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "پیکربندی" "backup" = "پشتیبان‌گیری" "backupTitle" = "پشتیبان‌گیری دیتابیس" diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml index f291a0d6..e635f586 100644 --- a/web/translation/translate.id_ID.toml +++ b/web/translation/translate.id_ID.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "Apakah Anda yakin ingin mengubah versi Xray menjadi" "dontRefresh" = "Instalasi sedang berlangsung, harap jangan menyegarkan halaman ini" "logs" = "Log" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "Konfigurasi" "backup" = "Cadangan & Pulihkan" "backupTitle" = "Cadangan & Pulihkan Database" diff --git a/web/translation/translate.ja_JP.toml b/web/translation/translate.ja_JP.toml index c374a769..17beb6c4 100644 --- a/web/translation/translate.ja_JP.toml +++ b/web/translation/translate.ja_JP.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "Xrayのバージョンを切り替えますか?" "dontRefresh" = "インストール中、このページをリロードしないでください" "logs" = "ログ" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "設定" "backup" = "バックアップと復元" "backupTitle" = "データベースのバックアップと復元" diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml index 6532cfa4..7718e700 100644 --- a/web/translation/translate.pt_BR.toml +++ b/web/translation/translate.pt_BR.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "Tem certeza de que deseja alterar a versão do Xray para" "dontRefresh" = "Instalação em andamento, por favor não atualize a página" "logs" = "Logs" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "Configuração" "backup" = "Backup e Restauração" "backupTitle" = "Backup e Restauração do Banco de Dados" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index f91a06e5..aad4cd29 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "Вы точно хотите сменить версию Xray?" "dontRefresh" = "Идёт установка. Пожалуйста, не обновляйте эту страницу" "logs" = "Логи" -"logDomains" = "Логи доменов" +"accessLog" = "Access Лог" +"errorLog" = "Error Лог" "config" = "Конфигурация" "backup" = "Бэкап и восстановление" "backupTitle" = "База данных бэкапа и восстановления" diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml index 35180f8e..b5a6c5ee 100644 --- a/web/translation/translate.tr_TR.toml +++ b/web/translation/translate.tr_TR.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "Xray sürümünü değiştirmek istediğinizden emin misiniz" "dontRefresh" = "Kurulum devam ediyor, lütfen bu sayfayı yenilemeyin" "logs" = "Günlükler" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "Yapılandırma" "backup" = "Yedekle & Geri Yükle" "backupTitle" = "Veritabanı Yedekleme & Geri Yükleme" diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml index 4398985a..875aa7cb 100644 --- a/web/translation/translate.uk_UA.toml +++ b/web/translation/translate.uk_UA.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "Ви впевнені, що бажаєте змінити версію Xray на" "dontRefresh" = "Інсталяція триває, будь ласка, не оновлюйте цю сторінку" "logs" = "Журнали" -"logDomains" = "Логи доменов" +"accessLog" = "Access Лог" +"errorLog" = "Error Лог" "config" = "Конфігурація" "backup" = "Резервне копіювання та відновлення" "backupTitle" = "Резервне копіювання та відновлення бази даних" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index cbaa2c3e..01a7c9d6 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "Bạn có chắc chắn muốn chuyển đổi phiên bản Xray sang" "dontRefresh" = "Đang tiến hành cài đặt, vui lòng không làm mới trang này." "logs" = "Nhật ký" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "Cấu hình" "backup" = "Sao lưu & Khôi phục" "backupTitle" = "Sao lưu & Khôi phục Cơ sở dữ liệu" diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml index 9be53d36..c8e4922a 100644 --- a/web/translation/translate.zh_CN.toml +++ b/web/translation/translate.zh_CN.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "是否切换 Xray 版本至" "dontRefresh" = "安装中,请勿刷新此页面" "logs" = "日志" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "配置" "backup" = "备份和恢复" "backupTitle" = "备份和恢复数据库" diff --git a/web/translation/translate.zh_TW.toml b/web/translation/translate.zh_TW.toml index 8d70a010..1f356803 100644 --- a/web/translation/translate.zh_TW.toml +++ b/web/translation/translate.zh_TW.toml @@ -105,7 +105,8 @@ "xraySwitchVersionDialogDesc" = "是否切換 Xray 版本至" "dontRefresh" = "安裝中,請勿重新整理此頁面" "logs" = "日誌" -"logDomains" = "Log Domains" +"accessLog" = "Access Log" +"errorLog" = "Error Log" "config" = "配置" "backup" = "備份和恢復" "backupTitle" = "備份和恢復資料庫" diff --git a/xray/process.go b/xray/process.go index b4947864..7e1d00d1 100644 --- a/xray/process.go +++ b/xray/process.go @@ -81,6 +81,30 @@ func GetAccessLogPath() (string, error) { return "", err } +func GetErrorLogPath() (string, error) { + config, err := os.ReadFile(GetConfigPath()) + if err != nil { + logger.Warningf("Failed to read configuration file: %s", err) + return "", err + } + + jsonConfig := map[string]interface{}{} + err = json.Unmarshal([]byte(config), &jsonConfig) + if err != nil { + logger.Warningf("Failed to parse JSON configuration: %s", err) + return "", err + } + + if jsonConfig["log"] != nil { + jsonLog := jsonConfig["log"].(map[string]interface{}) + if jsonLog["error"] != nil { + errorLogPath := jsonLog["error"].(string) + return errorLogPath, nil + } + } + return "", err +} + func stopProcess(p *Process) { p.Stop() }