mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-30 16:23:31 +00:00
fix(ops): bound database-backed system logs
This commit is contained in:
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user