From d0b3c1442159a9b720404d24e4295d515b3aaebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Tue, 13 Jan 2026 19:32:03 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/package.json | 1 + web/pnpm-lock.yaml | 3 + web/src/components/common/AppProvider.vue | 8 + web/src/components/common/RealtimeLog.vue | 7 +- .../components/common/RealtimeLogModal.vue | 7 +- web/src/utils/hljs/systemdlog.ts | 167 ++++++++++++++++++ web/src/views/website/EditView.vue | 4 +- 7 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 web/src/utils/hljs/systemdlog.ts diff --git a/web/package.json b/web/package.json index 039ec348..d2adae7d 100644 --- a/web/package.json +++ b/web/package.json @@ -39,6 +39,7 @@ "@xterm/xterm": "^6.0.0", "alova": "^3.3.4", "echarts": "^6.0.0", + "highlight.js": "^11.11.1", "install": "^0.13.0", "lodash-es": "^4.17.21", "luxon": "^3.7.2", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 4aa46711..4fe7a3a7 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: echarts: specifier: ^6.0.0 version: 6.0.0 + highlight.js: + specifier: ^11.11.1 + version: 11.11.1 install: specifier: ^0.13.0 version: 0.13.0 diff --git a/web/src/components/common/AppProvider.vue b/web/src/components/common/AppProvider.vue index d7f7b28c..5d704626 100644 --- a/web/src/components/common/AppProvider.vue +++ b/web/src/components/common/AppProvider.vue @@ -1,6 +1,13 @@ diff --git a/web/src/components/common/RealtimeLogModal.vue b/web/src/components/common/RealtimeLogModal.vue index db36d989..573663dd 100644 --- a/web/src/components/common/RealtimeLogModal.vue +++ b/web/src/components/common/RealtimeLogModal.vue @@ -9,6 +9,11 @@ const props = defineProps({ path: { type: String, required: true + }, + language: { + type: String, + required: false, + default: 'systemdlog' } }) @@ -72,7 +77,7 @@ defineExpose({ @close="handleClose" @mask-click="handleClose" > - + diff --git a/web/src/utils/hljs/systemdlog.ts b/web/src/utils/hljs/systemdlog.ts new file mode 100644 index 00000000..9e35f0a0 --- /dev/null +++ b/web/src/utils/hljs/systemdlog.ts @@ -0,0 +1,167 @@ +/* + Language: systemd Journal (journalctl) + Description: systemd/journald logs (journalctl -o short / short-iso) + Category: system, logs + */ + +/** @type {import('highlight.js').LanguageFn} */ +export default function systemdJournal(hljs: any) { + const regex = hljs.regex + + // Month names for "short" format + const MONTH = '(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)' + + // journalctl -o short: + // Jan 13 10:22:33 ... + const TS_SHORT = new RegExp(`^${MONTH}\\s+\\d{1,2}\\s+\\d{2}:\\d{2}:\\d{2}`) + + // journalctl -o short-iso: + // 2026-01-13T10:22:33+0800 ... + // 2026-01-13 10:22:33 ... + const TS_ISO = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?/ + + const HOST = /[A-Za-z0-9][A-Za-z0-9_.-]*/ + const IDENT = /[A-Za-z_][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_.-]+)*/ // e.g. systemd, sshd, foo/bar + const PID_OPT = /(?:\[\d+\])?/ + + const UNIT = /\b[\w.-]+?\.(?:service|socket|target|timer|mount|device|path|slice|scope)\b/ + + const PRIORITY_WORDS = [ + 'emerg', + 'alert', + 'crit', + 'critical', + 'err', + 'error', + 'warn', + 'warning', + 'notice', + 'info', + 'debug', + 'panic', + // systemd/journal 常见状态词 + 'failed', + 'failure', + 'timeout', + 'timed', + 'denied', + 'refused', + 'segfault' + ] + + const SYSTEMD_VERBS = [ + 'starting', + 'started', + 'stopping', + 'stopped', + 'reloading', + 'reloaded', + 'restarting', + 'restarted', + 'activating', + 'activated', + 'deactivating', + 'deactivated', + 'mounted', + 'mounting', + 'unmounted', + 'unmounting', + 'listening', + 'triggered', + 'queued', + 'succeeded', + 'success' + ] + + // Prefix: " [pid]: " + // Example: + // Jan 13 10:22:33 host systemd[1]: + // 2026-01-13T10:22:33+0800 host sshd[1234]: + const PREFIX = { + begin: [regex.either(TS_ISO, TS_SHORT), /\s+/, HOST, /\s+/, IDENT, PID_OPT], + beginScope: { + 1: 'meta', // timestamp + 3: 'title', // hostname + 5: 'symbol', // identifier + 6: 'number' // [pid] + }, + end: /: /, + endScope: 'punctuation', + relevance: 10 + } + + const SEVERITY = { + match: new RegExp(`\\b(?:${PRIORITY_WORDS.join('|')})\\b`, 'i'), + scope: 'keyword', + relevance: 2 + } + + const STATUS_VERB = { + match: new RegExp(`\\b(?:${SYSTEMD_VERBS.join('|')})\\b`, 'i'), + scope: 'built_in', + relevance: 1 + } + + // key=value(如:code=exited status=1/FAILURE UNIT=foo.service) + const KEY_VALUE = { + begin: /\b[\w.-]+=/, + scope: 'attr', + relevance: 0 + } + + // kernel/journal 常见的 monotonic timestamp: "[ 123.456]" + const MONOTONIC = { + match: /\[\s*\d+(?:\.\d+)?\]/, + scope: 'meta', + relevance: 0 + } + + const DQUOTE = { + scope: 'string', + begin: /"/, + end: /"/, + illegal: /\n/, + relevance: 0 + } + + const SQUOTE = { + scope: 'string', + begin: /'/, + end: /'/, + illegal: /\n/, + relevance: 0 + } + + const PATH = { + match: /(?:\/[^\s"'():\[\]]+)+/, + scope: 'string', + relevance: 0 + } + + return { + name: 'systemd Journal', + aliases: ['journalctl', 'journald', 'systemdlog', 'systemd-journal', 'systemd'], + case_insensitive: true, + contains: [ + PREFIX, + + // unit names in message body + { match: UNIT, scope: 'title', relevance: 3 }, + + // severity/status words + SEVERITY, + STATUS_VERB, + + MONOTONIC, + KEY_VALUE, + + // numbers (exit codes, pids, etc.) + hljs.NUMBER_MODE, + + // strings/paths + DQUOTE, + SQUOTE, + PATH + ] + } +} diff --git a/web/src/views/website/EditView.vue b/web/src/views/website/EditView.vue index 2346a00e..51ceb594 100644 --- a/web/src/views/website/EditView.vue +++ b/web/src/views/website/EditView.vue @@ -912,7 +912,7 @@ const updateTimeoutUnit = (proxy: any, unit: string) => { {{ $gettext('Are you sure you want to clear?') }} - + @@ -924,7 +924,7 @@ const updateTimeoutUnit = (proxy: any, unit: string) => { {{ $gettext('view') }}. - +