feat: 新增日志组件
This commit is contained in:
parent
71b28e2a9b
commit
8f81a65b6e
10486
pnpm-lock.yaml
10486
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
195
src/components/common/log-drawer.vue
Normal file
195
src/components/common/log-drawer.vue
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { NCollapse, NCollapseItem } from 'naive-ui';
|
||||||
|
import { defineComponent, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'LogDrawer'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title?: string;
|
||||||
|
show?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
title: '日志详情',
|
||||||
|
show: false
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:show', show: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
default: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const messageList = ref([
|
||||||
|
{
|
||||||
|
time_stamp: '1712021845601',
|
||||||
|
level: 'ERROR',
|
||||||
|
port: '8018',
|
||||||
|
throwable:
|
||||||
|
'java.lang.ArithmeticException: / by zero\n\tat com.example.easy.retry.service.impl.RemoteRetryServiceImpl.remoteSync(RemoteRetryServiceImpl.java:46)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:568)\n\tat org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n\tat org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756)\n\tat com.aizuda.easy.retry.client.core.intercepter.EasyRetryInterceptor.invoke(EasyRetryInterceptor.java:92)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)\n\tat org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756)\n\tat org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)\n\tat com.example.easy.retry.service.impl.RemoteRetryServiceImpl$$SpringCGLIB$$0.remoteSync(<generated>)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:568)\n\tat org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:281)\n\tat com.aizuda.easy.retry.client.core.strategy.ExecutorAnnotationMethod.doExecute(ExecutorAnnotationMethod.java:28)\n\tat com.aizuda.easy.retry.client.core.executor.AbstractRetryExecutor.doExecute(AbstractRetryExecutor.java:32)\n\tat com.aizuda.easy.retry.client.core.executor.AbstractRetryExecutor.execute(AbstractRetryExecutor.java:23)\n\tat com.aizuda.easy.retry.client.core.strategy.RemoteRetryStrategies.lambda$doGetCallable$2(RemoteRetryStrategies.java:89)\n\tat com.github.rholder.retry.AttemptTimeLimiters$NoAttemptTimeLimit.call(AttemptTimeLimiters.java:78)\n\tat com.github.rholder.retry.Retryer.call(Retryer.java:160)\n\tat com.aizuda.easy.retry.client.core.executor.GuavaRetryExecutor.call(GuavaRetryExecutor.java:56)\n\tat com.aizuda.easy.retry.client.core.strategy.AbstractRetryStrategies.openRetry(AbstractRetryStrategies.java:88)\n\tat com.aizuda.easy.retry.client.core.client.RetryEndPoint.dispatch(RetryEndPoint.java:99)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
location: 'com.aizuda.easy.retry.client.core.client.RetryEndPoint.dispatch(RetryEndPoint.java:124)',
|
||||||
|
thread: 'http-nio-8018-exec-5',
|
||||||
|
message: 'remote retry complete. count:[1] '
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time_stamp: '1712021875512',
|
||||||
|
level: 'INFO',
|
||||||
|
port: '8018',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
location: 'com.aizuda.easy.retry.client.core.client.RetryEndPoint.dispatch(RetryEndPoint.java:117)',
|
||||||
|
thread: 'http-nio-8018-exec-2',
|
||||||
|
message: 'remote retry complete. count:[4] result:[null]'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time_stamp: '1712021875512',
|
||||||
|
level: 'ERROR',
|
||||||
|
port: '8018',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
location: 'com.aizuda.easy.retry.client.core.client.RetryEndPoint.dispatch(RetryEndPoint.java:124)',
|
||||||
|
thread: 'http-nio-8018-exec-2',
|
||||||
|
message: 'remote retry complete. count:[4] '
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const ThrowableComponent = defineComponent({
|
||||||
|
props: {
|
||||||
|
throwable: String
|
||||||
|
},
|
||||||
|
setup(thProps) {
|
||||||
|
return () => {
|
||||||
|
if (!thProps.throwable) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
const firstLine = thProps.throwable.match(/^.+/m);
|
||||||
|
if (!firstLine) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
const restOfText = thProps.throwable.replace(/^.+(\n|$)/m, '');
|
||||||
|
return (
|
||||||
|
<NCollapse>
|
||||||
|
<NCollapseItem title={firstLine[0]} name="1">
|
||||||
|
{`${restOfText}`}
|
||||||
|
</NCollapseItem>
|
||||||
|
</NCollapse>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.show,
|
||||||
|
val => {
|
||||||
|
visible.value = val;
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const onUpdateShow = (value: boolean) => {
|
||||||
|
emit('update:show', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
function timestampToDate(timestamp: string): string {
|
||||||
|
const date = new Date(Number.parseInt(timestamp.toString(), 10));
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month =
|
||||||
|
(date.getMonth() + 1).toString().length === 1 ? `0${date.getMonth() + 1}` : (date.getMonth() + 1).toString();
|
||||||
|
const day = date.getDate().toString().length === 1 ? `0${date.getDate()}` : date.getDate().toString();
|
||||||
|
const hours = date.getHours().toString().length === 1 ? `0${date.getHours()}` : date.getHours().toString();
|
||||||
|
const minutes = date.getMinutes().toString().length === 1 ? `0${date.getMinutes()}` : date.getMinutes().toString();
|
||||||
|
const seconds = date.getSeconds().toString().length === 1 ? `0${date.getSeconds()}` : date.getSeconds().toString();
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${date.getMilliseconds()}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDrawer v-model:show="visible" width="100%" display-directive="if" @update:show="onUpdateShow">
|
||||||
|
<NDrawerContent title="日志详情" closable>
|
||||||
|
<div class="snail-log">
|
||||||
|
<div class="snail-log-scrollbar">
|
||||||
|
<code>
|
||||||
|
<pre
|
||||||
|
v-for="(message, index) in messageList"
|
||||||
|
:key="index"
|
||||||
|
><NDivider v-if="index !== 0" /><span class="log-hljs-time">{{timestampToDate(message.time_stamp)}}</span><span :class="`log-hljs-level-${message.level}`">{{`\t${message.level}\t`}}</span><span class="log-hljs-thread">{{`[${message.thread}]\t`}}</span><span class="log-hljs-location">{{`${message.location}: \n`}}</span> -<span class="pl-6px">{{`${message.message}\n`}}</span><ThrowableComponent :throwable="message.throwable" /></pre>
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NDrawerContent>
|
||||||
|
</NDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.snail-log {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&-scrollbar {
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
@include scrollbar();
|
||||||
|
|
||||||
|
.n-divider:not(.n-divider--vertical) {
|
||||||
|
margin-top: 6px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333639;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
.snail-log {
|
||||||
|
background-color: #1e1f22;
|
||||||
|
|
||||||
|
pre {
|
||||||
|
color: #ffffffe6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-hljs {
|
||||||
|
&-time {
|
||||||
|
color: #2db7f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-level {
|
||||||
|
&-DEBUG {
|
||||||
|
color: #2647cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-INFO {
|
||||||
|
color: #5c962c;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-WARN {
|
||||||
|
color: #da9816;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-ERROR {
|
||||||
|
color: #dc3f41;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-thread {
|
||||||
|
color: #00a3a3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-location {
|
||||||
|
color: #a771bf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -30,8 +30,8 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
"function_toggle-auth": () => import("@/views/function/toggle-auth/index.vue"),
|
"function_toggle-auth": () => import("@/views/function/toggle-auth/index.vue"),
|
||||||
group: () => import("@/views/group/index.vue"),
|
group: () => import("@/views/group/index.vue"),
|
||||||
home: () => import("@/views/home/index.vue"),
|
home: () => import("@/views/home/index.vue"),
|
||||||
job_task: () => import("@/views/job/task/index.vue"),
|
|
||||||
job_batch: () => import("@/views/job/batch/index.vue"),
|
job_batch: () => import("@/views/job/batch/index.vue"),
|
||||||
|
job_task: () => import("@/views/job/task/index.vue"),
|
||||||
manage_menu: () => import("@/views/manage/menu/index.vue"),
|
manage_menu: () => import("@/views/manage/menu/index.vue"),
|
||||||
manage_role: () => import("@/views/manage/role/index.vue"),
|
manage_role: () => import("@/views/manage/role/index.vue"),
|
||||||
"manage_user-detail": () => import("@/views/manage/user-detail/[id].vue"),
|
"manage_user-detail": () => import("@/views/manage/user-detail/[id].vue"),
|
||||||
|
@ -202,16 +202,6 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
icon: 'eos-icons:cronjob'
|
icon: 'eos-icons:cronjob'
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
name: 'job_task',
|
|
||||||
path: '/job/task',
|
|
||||||
component: 'view.job_task',
|
|
||||||
meta: {
|
|
||||||
title: 'job_task',
|
|
||||||
i18nKey: 'route.job_task',
|
|
||||||
icon: 'octicon:tasklist'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'job_batch',
|
name: 'job_batch',
|
||||||
path: '/job/batch',
|
path: '/job/batch',
|
||||||
@ -221,6 +211,16 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
i18nKey: 'route.job_batch',
|
i18nKey: 'route.job_batch',
|
||||||
icon: 'carbon:batch-job'
|
icon: 'carbon:batch-job'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'job_task',
|
||||||
|
path: '/job/task',
|
||||||
|
component: 'view.job_task',
|
||||||
|
meta: {
|
||||||
|
title: 'job_task',
|
||||||
|
i18nKey: 'route.job_task',
|
||||||
|
icon: 'octicon:tasklist'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -114,14 +114,14 @@ function transformElegantRouteToVueRoute(
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add redirect to child
|
// add redirect to child
|
||||||
if (children?.length && !vueRoute.redirect) {
|
if (children?.length && !vueRoute.redirect) {
|
||||||
vueRoute.redirect = {
|
vueRoute.redirect = {
|
||||||
name: children[0].name
|
name: children[0].name
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (children?.length) {
|
if (children?.length) {
|
||||||
const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views));
|
const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views));
|
||||||
|
|
||||||
@ -164,8 +164,8 @@ const routeMap: RouteMap = {
|
|||||||
"group": "/group",
|
"group": "/group",
|
||||||
"home": "/home",
|
"home": "/home",
|
||||||
"job": "/job",
|
"job": "/job",
|
||||||
"job_task": "/job/task",
|
|
||||||
"job_batch": "/job/batch",
|
"job_batch": "/job/batch",
|
||||||
|
"job_task": "/job/task",
|
||||||
"login": "/login/:module(pwd-login)?",
|
"login": "/login/:module(pwd-login)?",
|
||||||
"manage": "/manage",
|
"manage": "/manage",
|
||||||
"manage_menu": "/manage/menu",
|
"manage_menu": "/manage/menu",
|
||||||
|
Loading…
Reference in New Issue
Block a user