fix(ops): bound database-backed system logs

This commit is contained in:
cyh
2026-08-31 19:04:58 +08:00
parent 52374af940
commit 163eb7ffae
18 changed files with 418 additions and 103 deletions
+1
View File
@@ -814,6 +814,7 @@ export interface OpsAggregationSettings {
export interface OpsRuntimeLogConfig {
level: 'debug' | 'info' | 'warn' | 'error'
persist_access_logs: boolean
enable_sampling: boolean
sampling_initial: number
sampling_thereafter: number
@@ -42,8 +42,11 @@ export default {
samplingInitial: 'Sampling initial',
samplingThereafter: 'Sampling thereafter',
retentionDays: 'Retention days',
retentionDaysHint: 'Applied by the scheduled data-cleanup job.',
caller: 'caller',
sampling: 'sampling',
persistAccessLogs: 'Store access logs in database',
persistAccessLogsHint: 'Disabled by default because access logs add one indexed database row per request. Warning, error, and audit logs are always stored.',
saveAndApply: 'Save and apply',
resetDefaults: 'Reset defaults',
latestWriteError: 'Latest write error:',
@@ -42,8 +42,11 @@ export default {
samplingInitial: '采样初始条数',
samplingThereafter: '后续采样间隔',
retentionDays: '保留天数',
retentionDaysHint: '由定时数据清理任务执行。',
caller: '调用方',
sampling: '采样',
persistAccessLogs: '将访问日志写入数据库',
persistAccessLogsHint: '默认关闭,因为访问日志会为每个请求新增一条带索引的数据库记录。警告、错误和审计日志始终会保留。',
saveAndApply: '保存并应用',
resetDefaults: '重置默认值',
latestWriteError: '最近写入错误:',
@@ -41,6 +41,7 @@ const runtimeLoading = ref(false)
const runtimeSaving = ref(false)
const runtimeConfig = reactive<OpsRuntimeLogConfig>({
level: 'info',
persist_access_logs: false,
enable_sampling: false,
sampling_initial: 100,
sampling_thereafter: 100,
@@ -231,6 +232,7 @@ const loadRuntimeConfig = async () => {
try {
const cfg = await opsAPI.getRuntimeLogConfig()
runtimeConfig.level = cfg.level
runtimeConfig.persist_access_logs = cfg.persist_access_logs
runtimeConfig.enable_sampling = cfg.enable_sampling
runtimeConfig.sampling_initial = cfg.sampling_initial
runtimeConfig.sampling_thereafter = cfg.sampling_thereafter
@@ -249,6 +251,7 @@ const saveRuntimeConfig = async () => {
try {
const saved = await opsAPI.updateRuntimeLogConfig({ ...runtimeConfig })
runtimeConfig.level = saved.level
runtimeConfig.persist_access_logs = saved.persist_access_logs
runtimeConfig.enable_sampling = saved.enable_sampling
runtimeConfig.sampling_initial = saved.sampling_initial
runtimeConfig.sampling_thereafter = saved.sampling_thereafter
@@ -272,6 +275,7 @@ const resetRuntimeConfig = async () => {
try {
const saved = await opsAPI.resetRuntimeLogConfig()
runtimeConfig.level = saved.level
runtimeConfig.persist_access_logs = saved.persist_access_logs
runtimeConfig.enable_sampling = saved.enable_sampling
runtimeConfig.sampling_initial = saved.sampling_initial
runtimeConfig.sampling_thereafter = saved.sampling_thereafter
@@ -419,6 +423,7 @@ onMounted(async () => {
<label class="text-xs text-gray-600 dark:text-gray-300">
{{ t('admin.ops.systemLogs.retentionDays') }}
<input v-model.number="runtimeConfig.retention_days" type="number" min="1" max="3650" class="input mt-1" />
<span class="mt-1 block text-[11px] text-gray-500 dark:text-gray-400">{{ t('admin.ops.systemLogs.retentionDaysHint') }}</span>
</label>
<div class="md:col-span-2 xl:col-span-6">
<div class="grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-end">
@@ -431,6 +436,10 @@ onMounted(async () => {
<input v-model="runtimeConfig.enable_sampling" type="checkbox" />
{{ t('admin.ops.systemLogs.sampling') }}
</label>
<label class="inline-flex items-center gap-2 text-xs text-gray-600 dark:text-gray-300">
<input v-model="runtimeConfig.persist_access_logs" type="checkbox" />
{{ t('admin.ops.systemLogs.persistAccessLogs') }}
</label>
</div>
<div class="flex flex-wrap items-center gap-2 lg:justify-end">
<button type="button" class="btn btn-primary btn-sm" :disabled="runtimeSaving" @click="saveRuntimeConfig">
@@ -443,6 +452,7 @@ onMounted(async () => {
</div>
</div>
</div>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">{{ t('admin.ops.systemLogs.persistAccessLogsHint') }}</p>
<p v-if="health.last_error" class="mt-2 text-xs text-red-600 dark:text-red-400">{{ t('admin.ops.systemLogs.latestWriteError') }} {{ health.last_error }}</p>
</div>
@@ -53,6 +53,7 @@ const PaginationStub = defineComponent({
const runtimeConfig = {
level: 'info',
persist_access_logs: false,
enable_sampling: false,
sampling_initial: 100,
sampling_thereafter: 100,
@@ -126,6 +127,22 @@ describe('OpsSystemLogTable host support', () => {
expect(mockCleanupSystemLogs).toHaveBeenCalledWith(expect.objectContaining({ host: 'api-node-2' }))
})
it('keeps database access-log persistence opt-in', async () => {
const wrapper = mount(OpsSystemLogTable, {
global: {
stubs: {
Select: SelectStub,
Pagination: PaginationStub,
},
},
})
await flushPromises()
const label = wrapper.findAll('label').find((item) => item.text().includes('admin.ops.systemLogs.persistAccessLogs'))
expect(label).toBeDefined()
expect((label!.find('input').element as HTMLInputElement).checked).toBe(false)
})
it.each([
['zh', zhLocale],
['en', enLocale],