From 26bd2d08be89f3f4c2b4dae5b51a6cfb11db6b32 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Mon, 27 Jul 2026 11:25:26 +0800 Subject: [PATCH] fix: improve host system log filtering compatibility (#13373) --- agent/app/api/v2/logs.go | 16 +++++ agent/app/dto/logs.go | 7 +++ agent/app/service/logs.go | 64 +++++++++++++++++++- agent/i18n/lang/en.yaml | 1 + agent/i18n/lang/es-ES.yaml | 1 + agent/i18n/lang/fa.yaml | 1 + agent/i18n/lang/ja.yaml | 1 + agent/i18n/lang/ko.yaml | 1 + agent/i18n/lang/lo.yaml | 1 + agent/i18n/lang/ms.yaml | 1 + agent/i18n/lang/pt-BR.yaml | 1 + agent/i18n/lang/ru.yaml | 1 + agent/i18n/lang/tr.yaml | 1 + agent/i18n/lang/zh-Hant.yaml | 1 + agent/i18n/lang/zh.yaml | 1 + agent/router/ro_log.go | 1 + core/cmd/server/docs/docs.go | 44 ++++++++++++++ core/cmd/server/docs/swagger.json | 44 ++++++++++++++ frontend/src/api/interface/log.ts | 7 +++ frontend/src/api/modules/log.ts | 5 ++ frontend/src/views/log/host-system/index.vue | 46 +++++++++++++- 21 files changed, 242 insertions(+), 4 deletions(-) diff --git a/agent/app/api/v2/logs.go b/agent/app/api/v2/logs.go index 37dcf6481..97fc9d8cd 100644 --- a/agent/app/api/v2/logs.go +++ b/agent/app/api/v2/logs.go @@ -22,6 +22,22 @@ func (b *BaseApi) GetSystemFiles(c *gin.Context) { helper.SuccessWithData(c, data) } +// @Tags Logs +// @Summary Get host system log status +// @Produce json +// @Success 200 {object} dto.SystemLogStatus +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /logs/system/status [get] +func (b *BaseApi) GetSystemLogStatus(c *gin.Context) { + data, err := logService.GetSystemLogStatus() + if err != nil { + helper.InternalServer(c, err) + return + } + helper.SuccessWithData(c, data) +} + // @Tags Logs // @Summary Read host logs // @Accept json diff --git a/agent/app/dto/logs.go b/agent/app/dto/logs.go index 8ea011fda..83f084139 100644 --- a/agent/app/dto/logs.go +++ b/agent/app/dto/logs.go @@ -34,6 +34,13 @@ type SystemLogRes struct { NextCursor string `json:"nextCursor"` } +type SystemLogStatus struct { + Source string `json:"source"` + Version string `json:"version"` + KeywordFilterSupported bool `json:"keywordFilterSupported"` + Message string `json:"message"` +} + type SystemLogItem struct { Timestamp int64 `json:"-"` Cursor string `json:"-"` diff --git a/agent/app/service/logs.go b/agent/app/service/logs.go index b6cd2daae..28cba8555 100644 --- a/agent/app/service/logs.go +++ b/agent/app/service/logs.go @@ -13,7 +13,9 @@ import ( "time" "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/i18n" "github.com/1Panel-dev/1Panel/agent/utils/re" ) @@ -40,6 +42,7 @@ type systemLogCursor struct { type ILogService interface { ListSystemLogFile() ([]string, error) + GetSystemLogStatus() (dto.SystemLogStatus, error) ReadSystemLog(req dto.SystemLogReq) (dto.SystemLogRes, error) ListRunningServices() ([]string, error) } @@ -74,6 +77,9 @@ func (u *LogService) ReadSystemLog(req dto.SystemLogReq) (dto.SystemLogRes, erro output, err := exec.CommandContext(ctx, journalctl, queryArgs...).CombinedOutput() cancel() if err != nil { + if journalctlGrepUnsupported(req.Keyword, string(output)) { + return dto.SystemLogRes{}, buserr.New("ErrSystemLogKeywordFilterUnsupported") + } return dto.SystemLogRes{}, fmt.Errorf("read host system logs failed: %s", strings.TrimSpace(string(output))) } content := strings.TrimSpace(string(output)) @@ -90,6 +96,62 @@ func (u *LogService) ReadSystemLog(req dto.SystemLogReq) (dto.SystemLogRes, erro return buildSystemLogResponse("journalctl", items, pageSize, cursor) } +func (u *LogService) GetSystemLogStatus() (dto.SystemLogStatus, error) { + journalctl, err := exec.LookPath("journalctl") + if err != nil { + return dto.SystemLogStatus{ + Source: "file", + KeywordFilterSupported: true, + }, nil + } + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + versionOutput, err := exec.CommandContext(ctx, journalctl, "--version").CombinedOutput() + cancel() + if err != nil { + return dto.SystemLogStatus{}, fmt.Errorf("read journalctl version failed: %s", strings.TrimSpace(string(versionOutput))) + } + + ctx, cancel = context.WithTimeout(context.Background(), 3*time.Second) + helpOutput, err := exec.CommandContext(ctx, journalctl, "--help").CombinedOutput() + cancel() + if err != nil { + return dto.SystemLogStatus{}, fmt.Errorf("read journalctl capabilities failed: %s", strings.TrimSpace(string(helpOutput))) + } + supported := journalctlHelpSupportsGrep(string(helpOutput)) + status := dto.SystemLogStatus{ + Source: "journalctl", + Version: firstOutputLine(string(versionOutput)), + KeywordFilterSupported: supported, + } + if !supported { + status.Message = i18n.Get("ErrSystemLogKeywordFilterUnsupported") + } + return status, nil +} + +func journalctlHelpSupportsGrep(help string) bool { + return strings.Contains(help, "--grep=") +} + +func journalctlGrepUnsupported(keyword, output string) bool { + if strings.TrimSpace(keyword) == "" { + return false + } + output = strings.ToLower(output) + return strings.Contains(output, "grep") && + (strings.Contains(output, "unrecognized option") || strings.Contains(output, "unknown option")) +} + +func firstOutputLine(output string) string { + for _, line := range strings.Split(output, "\n") { + if line = strings.TrimSpace(line); line != "" { + return line + } + } + return "" +} + func buildJournalQueryArgs(req dto.SystemLogReq, startTime, endTime time.Time, pageSize int, cursor *systemLogCursor) []string { args := []string{ "--no-pager", "--reverse", "--output=json", @@ -104,7 +166,7 @@ func buildJournalQueryArgs(req dto.SystemLogReq, startTime, endTime time.Time, p args = append(args, "-u", service) } if priority := strings.TrimSpace(req.Priority); priority != "" { - args = append(args, "--priority", priority) + args = append(args, "--priority", priority+".."+priority) } if keyword := strings.TrimSpace(req.Keyword); keyword != "" { args = append(args, "--grep", keyword) diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index f25fe05ec..671bf4c44 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -12,6 +12,7 @@ MonitorLogExport: 'Website monitoring log export' LogExportNoData: 'No logs match the selected filters' LogExportDiskSpace: 'Less than 1 GiB of disk space is available; the export was stopped' LogExportRetentionInvalid: 'Export record retention must be between 1 and 30 days' +ErrSystemLogKeywordFilterUnsupported: 'The current system version does not support system log keyword filtering (requires systemd 237 or later)' ErrStructTransform: 'Type conversion failed: {{ .err }}' ErrNotSupportType: 'Unsupported type: {{ .name }}' diff --git a/agent/i18n/lang/es-ES.yaml b/agent/i18n/lang/es-ES.yaml index 59a2d1fb0..837cf872d 100644 --- a/agent/i18n/lang/es-ES.yaml +++ b/agent/i18n/lang/es-ES.yaml @@ -12,6 +12,7 @@ MonitorLogExport: 'Exportación de registros de monitorización web' LogExportNoData: 'Ningún registro coincide con los filtros seleccionados' LogExportDiskSpace: 'Hay menos de 1 GiB disponible; la exportación se detuvo' LogExportRetentionInvalid: 'La retención del historial de exportación debe ser de 1 a 30 días' +ErrSystemLogKeywordFilterUnsupported: 'La versión actual del sistema no admite el filtrado por palabras clave de los registros del sistema (requiere systemd 237 o posterior)' ErrStructTransform: 'Error de conversión: {{ .err }}' ErrNotSupportType: 'Tipo no soportado: {{ .name }}' diff --git a/agent/i18n/lang/fa.yaml b/agent/i18n/lang/fa.yaml index d2d3cd89f..1d3d1ed28 100644 --- a/agent/i18n/lang/fa.yaml +++ b/agent/i18n/lang/fa.yaml @@ -12,6 +12,7 @@ MonitorLogExport: 'خروجی لاگ پایش وب‌سایت' LogExportNoData: 'هیچ لاگی با فیلترهای انتخاب‌شده مطابقت ندارد' LogExportDiskSpace: 'فضای آزاد کمتر از ۱ گیگابایت است؛ خروجی متوقف شد' LogExportRetentionInvalid: 'مدت نگهداری سوابق خروجی باید بین ۱ تا ۳۰ روز باشد' +ErrSystemLogKeywordFilterUnsupported: 'نسخه فعلی سیستم از فیلتر کلیدواژه در گزارش‌های سیستم پشتیبانی نمی‌کند (به systemd 237 یا جدیدتر نیاز است)' ErrStructTransform: 'تبدیل نوع ناموفق بود: {{ .err }}' ErrNotSupportType: 'نوع پشتیبانی نمی‌شود: {{ .name }}' diff --git a/agent/i18n/lang/ja.yaml b/agent/i18n/lang/ja.yaml index b3a6c3866..44f5d1ae5 100644 --- a/agent/i18n/lang/ja.yaml +++ b/agent/i18n/lang/ja.yaml @@ -12,6 +12,7 @@ MonitorLogExport: 'Web サイト監視ログのエクスポート' LogExportNoData: '条件に一致するログがありません' LogExportDiskSpace: '空き容量が 1 GiB 未満のため、エクスポートを停止しました' LogExportRetentionInvalid: 'エクスポート履歴の保持期間は 1~30 日にしてください' +ErrSystemLogKeywordFilterUnsupported: '現在のシステムバージョンはシステムログのキーワードフィルタリングに対応していません(systemd 237 以降が必要です)' ErrStructTransform: '型変換エラー: {{ .err }}' ErrNotSupportType: '未対応のタイプ: {{ .name }}' diff --git a/agent/i18n/lang/ko.yaml b/agent/i18n/lang/ko.yaml index 49202678f..32f26b10b 100644 --- a/agent/i18n/lang/ko.yaml +++ b/agent/i18n/lang/ko.yaml @@ -12,6 +12,7 @@ MonitorLogExport: '웹사이트 모니터링 로그 내보내기' LogExportNoData: '선택한 조건과 일치하는 로그가 없습니다' LogExportDiskSpace: '사용 가능한 공간이 1 GiB 미만이어서 내보내기를 중지했습니다' LogExportRetentionInvalid: '내보내기 기록 보관 기간은 1일에서 30일 사이여야 합니다' +ErrSystemLogKeywordFilterUnsupported: '현재 시스템 버전은 시스템 로그 키워드 필터링을 지원하지 않습니다(systemd 237 이상 필요)' ErrStructTransform: '형 변환 실패: {{ .err }}' ErrNotSupportType: '지원하지 않는 타입: {{ .name }}' diff --git a/agent/i18n/lang/lo.yaml b/agent/i18n/lang/lo.yaml index e9cc8b90b..643038829 100644 --- a/agent/i18n/lang/lo.yaml +++ b/agent/i18n/lang/lo.yaml @@ -17,6 +17,7 @@ MonitorLogExport: 'ສົ່ງອອກລັອກການຕິດຕາມ LogExportNoData: 'ບໍ່ມີລັອກທີ່ກົງກັບເງື່ອນໄຂການກັ່ນຕອງ' LogExportDiskSpace: 'ພື້ນທີ່ດິສກ໌ທີ່ໃຊ້ໄດ້ໜ້ອຍກວ່າ 1 GiB; ການສົ່ງອອກຖືກຢຸດແລ້ວ' LogExportRetentionInvalid: 'ໄລຍະເກັບບັນທຶກການສົ່ງອອກຕ້ອງຢູ່ລະຫວ່າງ 1 ຫາ 30 ມື້' +ErrSystemLogKeywordFilterUnsupported: 'ເວີຊັນລະບົບປັດຈຸບັນບໍ່ຮອງຮັບການກັ່ນຕອງຄຳສຳຄັນຂອງບັນທຶກລະບົບ (ຕ້ອງການ systemd 237 ຫຼືໃໝ່ກວ່າ)' ErrStructTransform: 'ການແປງປະເພດຂໍ້ມູນລົ້ມເຫຼວ: {{ .err }}' ErrNotLogin: 'ຍັງບໍ່ໄດ້ເຂົ້າສູ່ລະບົບ: {{ .detail }}' ErrPasswordExpired: 'ລະຫັດຜ່ານໝົດອາຍຸ: {{ .detail }}' diff --git a/agent/i18n/lang/ms.yaml b/agent/i18n/lang/ms.yaml index e070901ff..d172d03c3 100644 --- a/agent/i18n/lang/ms.yaml +++ b/agent/i18n/lang/ms.yaml @@ -12,6 +12,7 @@ MonitorLogExport: 'Eksport log pemantauan laman web' LogExportNoData: 'Tiada log yang sepadan dengan penapis dipilih' LogExportDiskSpace: 'Ruang tersedia kurang daripada 1 GiB; eksport dihentikan' LogExportRetentionInvalid: 'Tempoh simpanan rekod eksport mestilah antara 1 hingga 30 hari' +ErrSystemLogKeywordFilterUnsupported: 'Versi sistem semasa tidak menyokong penapisan kata kunci log sistem (memerlukan systemd 237 atau lebih baharu)' ErrStructTransform: 'Ralat penukaran: {{ .err }}' ErrNotSupportType: 'Jenis tidak disokong: {{ .name }}' diff --git a/agent/i18n/lang/pt-BR.yaml b/agent/i18n/lang/pt-BR.yaml index 8a881ac00..40d6f8889 100644 --- a/agent/i18n/lang/pt-BR.yaml +++ b/agent/i18n/lang/pt-BR.yaml @@ -12,6 +12,7 @@ MonitorLogExport: 'Exportação de logs de monitoramento de sites' LogExportNoData: 'Nenhum log corresponde aos filtros selecionados' LogExportDiskSpace: 'Há menos de 1 GiB disponível; a exportação foi interrompida' LogExportRetentionInvalid: 'A retenção dos registros de exportação deve ser de 1 a 30 dias' +ErrSystemLogKeywordFilterUnsupported: 'A versão atual do sistema não oferece suporte à filtragem de logs do sistema por palavra-chave (requer systemd 237 ou posterior)' ErrStructTransform: 'Erro de conversão: {{ .err }}' ErrNotSupportType: 'Tipo não suportado: {{ .name }}' diff --git a/agent/i18n/lang/ru.yaml b/agent/i18n/lang/ru.yaml index 7a618c156..6758ea2ed 100644 --- a/agent/i18n/lang/ru.yaml +++ b/agent/i18n/lang/ru.yaml @@ -12,6 +12,7 @@ MonitorLogExport: 'Экспорт журналов мониторинга веб LogExportNoData: 'Нет журналов, соответствующих выбранным фильтрам' LogExportDiskSpace: 'Доступно менее 1 GiB; экспорт остановлен' LogExportRetentionInvalid: 'Срок хранения истории экспорта должен быть от 1 до 30 дней' +ErrSystemLogKeywordFilterUnsupported: 'Текущая версия системы не поддерживает фильтрацию системных журналов по ключевым словам (требуется systemd 237 или новее)' ErrStructTransform: 'Ошибка преобразования: {{ .err }}' ErrNotSupportType: 'Тип не поддерживается: {{ .name }}' diff --git a/agent/i18n/lang/tr.yaml b/agent/i18n/lang/tr.yaml index d33c197d7..9f50b83ab 100644 --- a/agent/i18n/lang/tr.yaml +++ b/agent/i18n/lang/tr.yaml @@ -12,6 +12,7 @@ MonitorLogExport: 'Web sitesi izleme günlüğü dışa aktarma' LogExportNoData: 'Seçilen filtrelerle eşleşen günlük yok' LogExportDiskSpace: 'Kullanılabilir alan 1 GiB altında; dışa aktarma durduruldu' LogExportRetentionInvalid: 'Dışa aktarma kayıtları 1 ile 30 gün arasında saklanmalıdır' +ErrSystemLogKeywordFilterUnsupported: 'Mevcut sistem sürümü sistem günlüklerinde anahtar kelime filtrelemeyi desteklemiyor (systemd 237 veya üzeri gerekir)' ErrStructTransform: 'Dönüşüm hatası: {{ .err }}' ErrNotSupportType: 'Bu tür desteklenmiyor: {{ .name }}' diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 950b50fa2..a0cd160ba 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -12,6 +12,7 @@ MonitorLogExport: '網站監控日誌匯出' LogExportNoData: '沒有符合條件的日誌' LogExportDiskSpace: '可用磁碟空間不足 1 GiB,匯出任務已停止' LogExportRetentionInvalid: '匯出記錄保留天數必須介於 1 到 30 天之間' +ErrSystemLogKeywordFilterUnsupported: '目前系統版本不支援系統日誌關鍵字篩選(需要 systemd 237 或更高版本)' ErrStructTransform: '型別轉換失敗: {{ .err }}' ErrNotSupportType: '系統暫不支援目前類型: {{ .name }}' diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 6b2590c34..877579054 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -12,6 +12,7 @@ MonitorLogExport: "网站监控日志导出" LogExportNoData: "没有符合条件的日志" LogExportDiskSpace: "可用磁盘空间不足 1 GiB,导出任务已停止" LogExportRetentionInvalid: "导出记录保留天数必须在 1 到 30 天之间" +ErrSystemLogKeywordFilterUnsupported: "当前系统版本不支持系统日志关键词过滤(需要 systemd 237 或更高版本)" ErrStructTransform: "类型转换失败: {{ .err }}" ErrNotSupportType: "不支持当前类型: {{ .name }}" diff --git a/agent/router/ro_log.go b/agent/router/ro_log.go index af4feb8bd..08c13b9d2 100644 --- a/agent/router/ro_log.go +++ b/agent/router/ro_log.go @@ -12,6 +12,7 @@ func (s *LogRouter) InitRouter(Router *gin.RouterGroup) { baseApi := v2.ApiGroupApp.BaseApi { operationRouter.GET("/system/files", baseApi.GetSystemFiles) + operationRouter.GET("/system/status", baseApi.GetSystemLogStatus) operationRouter.POST("/system/read", baseApi.ReadSystemLog) operationRouter.GET("/system/services", baseApi.ListRunningServices) operationRouter.POST("/tasks/search", baseApi.PageTasks) diff --git a/core/cmd/server/docs/docs.go b/core/cmd/server/docs/docs.go index b2a337e07..df385ab77 100644 --- a/core/cmd/server/docs/docs.go +++ b/core/cmd/server/docs/docs.go @@ -20388,6 +20388,33 @@ const docTemplate = `{ ] } }, + "/logs/system/status": { + "get": { + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.SystemLogStatus" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Get host system log status", + "tags": [ + "Logs" + ] + } + }, "/logs/tasks/executing/count": { "get": { "responses": { @@ -38946,6 +38973,23 @@ const docTemplate = `{ }, "type": "object" }, + "dto.SystemLogStatus": { + "properties": { + "keywordFilterSupported": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object" + }, "dto.Tag": { "properties": { "key": { diff --git a/core/cmd/server/docs/swagger.json b/core/cmd/server/docs/swagger.json index 725b4c049..e0879a090 100644 --- a/core/cmd/server/docs/swagger.json +++ b/core/cmd/server/docs/swagger.json @@ -20384,6 +20384,33 @@ ] } }, + "/logs/system/status": { + "get": { + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dto.SystemLogStatus" + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "Timestamp": [] + } + ], + "summary": "Get host system log status", + "tags": [ + "Logs" + ] + } + }, "/logs/tasks/executing/count": { "get": { "responses": { @@ -38942,6 +38969,23 @@ }, "type": "object" }, + "dto.SystemLogStatus": { + "properties": { + "keywordFilterSupported": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object" + }, "dto.Tag": { "properties": { "key": { diff --git a/frontend/src/api/interface/log.ts b/frontend/src/api/interface/log.ts index 7778b1fa2..6b566af2d 100644 --- a/frontend/src/api/interface/log.ts +++ b/frontend/src/api/interface/log.ts @@ -50,6 +50,13 @@ export namespace Log { nextCursor: string; } + export interface SystemLogStatus { + source: 'journalctl' | 'file'; + version: string; + keywordFilterSupported: boolean; + message: string; + } + export interface SystemLogSearch { pageSize: number; cursor?: string; diff --git a/frontend/src/api/modules/log.ts b/frontend/src/api/modules/log.ts index ec767d3f7..c177b843c 100644 --- a/frontend/src/api/modules/log.ts +++ b/frontend/src/api/modules/log.ts @@ -21,6 +21,11 @@ export const getSystemFiles = (node?: string) => { return http.get>(`/logs/system/files${params}`); }; +export const getSystemLogStatus = (node?: string) => { + const query = node ? `?operateNode=${node}` : ''; + return http.get(`/logs/system/status${query}`); +}; + export const readSystemLogs = (params: Log.SystemLogSearch, node?: string) => { const query = node ? `?operateNode=${node}` : ''; return http.post(`/logs/system/read${query}`, params); diff --git a/frontend/src/views/log/host-system/index.vue b/frontend/src/views/log/host-system/index.vue index ac37afac3..37497c23d 100644 --- a/frontend/src/views/log/host-system/index.vue +++ b/frontend/src/views/log/host-system/index.vue @@ -1,7 +1,33 @@