feat:移除了弹窗,服务器添加sls

This commit is contained in:
AxTps committed 2026-09-08 22:39:45 +08:00
commit 6a295f9a7a
4082 files changed
+1322534

No files matched your search

@@ -0,0 +1,210 @@
import { type Ref, shallowRef, triggerRef } from 'vue'
import { detectLogLevel } from '../layouts/shared/console/composables/log-level'
import type { Log4jEvent, LogLevel, LogLine } from '../layouts/shared/console/types'
const ARCHIVE_CAPACITY = 20_000
const ARCHIVE_TEXT_CAPACITY = 4 * 1024 * 1024
const MAX_LOG_LINE_LENGTH = 64 * 1024
const BATCH_TIMEOUT_MS = 300
const INITIAL_BATCH_SIZE = 256
const ENTRY_START_RE = /^\[\d{2}:\d{2}:\d{2}\]/
const LOG_TRUNCATION_MARKER = ' … [log output truncated by Axolotl] … '
export interface ConsoleState {
output: Ref<LogLine[]>
addLog4jEvent: (event: Log4jEvent) => void
addLegacyLog: (message: string) => Promise<void>
clear: () => void
}
function groupContinuations(lines: LogLine[]): LogLine[] {
if (lines.length <= 1) return lines
const groups: LogLine[][] = []
for (const line of lines) {
if (ENTRY_START_RE.test(line.text)) {
groups.push([line])
} else if (groups.length > 0) {
let target = groups.length - 1
const lastEntry = groups[target][0]
if (lastEntry.level !== 'error' && lastEntry.level !== 'warn') {
if (line.level === 'error' || line.level === null) {
for (let i = groups.length - 2; i >= 0; i--) {
if (groups[i][0].level === 'error' || groups[i][0].level === 'warn') {
target = i
break
}
}
}
}
groups[target].push(line)
} else {
groups.push([line])
}
}
return groups.flat()
}
function mapLog4jLevel(level?: string): LogLevel | null {
if (!level) return null
switch (level.toUpperCase()) {
case 'FATAL':
case 'ERROR':
return 'error'
case 'WARN':
return 'warn'
case 'INFO':
return 'info'
case 'DEBUG':
return 'debug'
case 'TRACE':
return 'trace'
default:
return null
}
}
function formatTimestamp(millis?: number): string {
if (!millis) return ''
const date = new Date(millis)
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `[${hours}:${minutes}:${seconds}]`
}
function truncateLogText(text: string, maximumLength = MAX_LOG_LINE_LENGTH): string {
if (text.length <= maximumLength) return text
const retainedLength = Math.max(0, maximumLength - LOG_TRUNCATION_MARKER.length)
const prefixLength = Math.floor(retainedLength / 2)
const suffixLength = retainedLength - prefixLength
return `${text.slice(0, prefixLength)}${LOG_TRUNCATION_MARKER}${text.slice(-suffixLength)}`
}
function formatLog4jLines(event: Log4jEvent): LogLine[] {
const level = mapLog4jLevel(event.level)
const time = formatTimestamp(event.timestamp_millis)
const thread = event.thread_name ?? ''
const levelText = event.level ?? ''
const message = truncateLogText(event.message?.trim() ?? '')
const prefix = time ? `${time} [${thread}/${levelText}]: ` : `[${thread}/${levelText}]: `
const messageLines = message.split(/[\r\n]+/)
const lines: LogLine[] = [{ text: prefix + messageLines[0], level }]
for (let i = 1; i < messageLines.length; i++) {
if (!messageLines[i]) continue
lines.push({ text: messageLines[i], level })
}
if (event.throwable) {
for (const line of truncateLogText(event.throwable).split(/[\r\n]+/)) {
if (!line) continue
lines.push({ text: line, level: 'error' })
}
}
return lines
}
function textToLogLine(text: string): LogLine {
const truncated = truncateLogText(text)
return { text: truncated, level: detectLogLevel(truncated) }
}
export function createConsoleState(): ConsoleState {
const output = shallowRef<LogLine[]>([])
let lineBuffer: LogLine[] = []
let batchTimer: ReturnType<typeof setTimeout> | null = null
let outputTextLength = 0
function appendLines(lines: LogLine[]) {
for (const line of lines) {
const text = truncateLogText(line.text)
output.value.push(text === line.text ? line : { ...line, text })
outputTextLength += text.length
}
while (output.value.length > ARCHIVE_CAPACITY || outputTextLength > ARCHIVE_TEXT_CAPACITY) {
const removed = output.value.shift()
if (!removed) break
outputTextLength -= removed.text.length
}
}
function flushBuffer() {
if (lineBuffer.length === 0) return
const lines = groupContinuations(lineBuffer)
lineBuffer = []
batchTimer = null
appendLines(lines)
triggerRef(output)
}
function addLines(lines: LogLine[]) {
if (lines.length === 0) return
if (output.value.length === 0 && lines.length >= INITIAL_BATCH_SIZE) {
lineBuffer = lines
flushBuffer()
return
}
for (const line of lines) {
lineBuffer.push(line)
}
if (!batchTimer) {
batchTimer = setTimeout(flushBuffer, BATCH_TIMEOUT_MS)
}
}
function addLog4jEvent(event: Log4jEvent) {
addLines(formatLog4jLines(event))
}
// 历史日志/缓冲大文本一次进入数据(保持行序),渲染由高亮管线分帧消化,
// 避免分块写入把实时事件插到历史行之间造成顺序错乱
function addLegacyLog(message: string): Promise<void> {
const lines = message
.split(/[\r\n]+/)
.filter(Boolean)
.map(textToLogLine)
let parentLevel: LogLevel | null = null
for (const line of lines) {
if (ENTRY_START_RE.test(line.text)) {
parentLevel = line.level
} else if (line.level === null && parentLevel !== null) {
line.level = parentLevel
}
}
appendLines(lines)
triggerRef(output)
return Promise.resolve()
}
function clear() {
output.value = []
outputTextLength = 0
lineBuffer = []
if (batchTimer) {
clearTimeout(batchTimer)
batchTimer = null
}
}
return {
output,
addLog4jEvent,
addLegacyLog,
clear,
}
}