sun-chaochao 3 лет назад
Родитель
Сommit
b943396ad4

+ 52 - 0
src/pages/Log/Access/Detail/index.tsx

@@ -0,0 +1,52 @@
+import { Descriptions, Modal } from 'antd';
+import type { AccessLogItem } from '@/pages/Log/Access/typings';
+import { useEffect, useState } from 'react';
+import moment from 'moment';
+
+interface Props {
+  data: Partial<AccessLogItem>;
+  close: () => void;
+}
+
+const Detail = (props: Props) => {
+  const [data, setDada] = useState<Partial<AccessLogItem>>(props.data || {});
+
+  useEffect(() => {
+    setDada(props.data);
+  }, [props.data]);
+
+  return (
+    <Modal title={'详情'} visible onCancel={props.close} onOk={props.close} width={1000}>
+      <Descriptions bordered>
+        <Descriptions.Item label="URL">{data?.url}</Descriptions.Item>
+        <Descriptions.Item label="请求方法" span={2}>
+          {data?.httpMethod}
+        </Descriptions.Item>
+        <Descriptions.Item label="动作">{data?.action}</Descriptions.Item>
+        <Descriptions.Item label="类名" span={2}>
+          {data?.target}
+        </Descriptions.Item>
+        <Descriptions.Item label="方法名">{data?.method}</Descriptions.Item>
+        <Descriptions.Item label="IP" span={2}>
+          {data?.ip}
+        </Descriptions.Item>
+        <Descriptions.Item label="请求时间">
+          {moment(data?.requestTime).format('YYYY-MM-DD HH:mm:ss')}
+        </Descriptions.Item>
+        <Descriptions.Item label="请求耗时" span={2}>
+          {(data?.responseTime || 0) - (data?.requestTime || 0)}ms
+        </Descriptions.Item>
+        <Descriptions.Item label="请求头" span={3}>
+          {JSON.stringify(data?.httpHeaders)}
+        </Descriptions.Item>
+        <Descriptions.Item label="请求参数" span={3}>
+          {JSON.stringify(data?.parameters)}
+        </Descriptions.Item>
+        <Descriptions.Item label="异常信息" span={3}>
+          {data?.exception}
+        </Descriptions.Item>
+      </Descriptions>
+    </Modal>
+  );
+};
+export default Detail;

+ 136 - 0
src/pages/Log/Access/index.tsx

@@ -0,0 +1,136 @@
+import BaseService from '@/utils/BaseService';
+import { useRef, useState } from 'react';
+import type { ProColumns, ActionType } from '@jetlinks/pro-table';
+import type { AccessLogItem } from '@/pages/Log/Access/typings';
+import moment from 'moment';
+import { Tag, Tooltip } from 'antd';
+import { EyeOutlined } from '@ant-design/icons';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import ProTable from '@jetlinks/pro-table';
+import SearchComponent from '@/components/SearchComponent';
+import Detail from '@/pages/Log/Access/Detail';
+
+const service = new BaseService('logger/access');
+
+const Access = () => {
+  const actionRef = useRef<ActionType>();
+  const intl = useIntl();
+  const [param, setParam] = useState({});
+  const [visible, setVisible] = useState<boolean>(false);
+  const [current, setCurrent] = useState<Partial<AccessLogItem>>({});
+
+  const columns: ProColumns<AccessLogItem>[] = [
+    {
+      title: 'IP',
+      dataIndex: 'ip',
+      ellipsis: true,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.log.access.url',
+        defaultMessage: '请求路径',
+      }),
+      dataIndex: 'url',
+      ellipsis: true,
+    },
+    {
+      title: '请求方法',
+      dataIndex: 'httpMethod',
+      ellipsis: true,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.table.description',
+        defaultMessage: '说明',
+      }),
+      dataIndex: 'description',
+      ellipsis: true,
+      render: (text, record) => {
+        return `${record.action}-${record.describe}`;
+      },
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.log.access.requestTime',
+        defaultMessage: '请求时间',
+      }),
+      dataIndex: 'requestTime',
+      sorter: true,
+      valueType: 'dateTime',
+      defaultSortOrder: 'descend',
+      ellipsis: true,
+      width: 200,
+      renderText: (text: string) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.log.access.requestTimeConsuming',
+        defaultMessage: '请求耗时',
+      }),
+      renderText: (record: AccessLogItem) => (
+        <Tag color="purple">{record.responseTime - record.requestTime}ms</Tag>
+      ),
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.log.access.requestUser',
+        defaultMessage: '请求用户',
+      }),
+      dataIndex: 'context.username',
+      render: (text) => <Tag color="geekblue">{text}</Tag>,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.data.option',
+        defaultMessage: '操作',
+      }),
+      valueType: 'option',
+      align: 'center',
+      render: (text, record) => [
+        <a
+          key="editable"
+          onClick={() => {
+            setVisible(true);
+            setCurrent(record);
+          }}
+        >
+          <Tooltip title={'查看'}>
+            <EyeOutlined />
+          </Tooltip>
+        </a>,
+      ],
+    },
+  ];
+  return (
+    <>
+      <SearchComponent<AccessLogItem>
+        field={columns}
+        target="access-log"
+        onSearch={(data) => {
+          actionRef.current?.reset?.();
+          setParam(data);
+        }}
+      />
+      <ProTable<AccessLogItem>
+        columns={columns}
+        params={param}
+        request={async (params) =>
+          service.query({ ...params, sorts: [{ name: 'responseTime', order: 'desc' }] })
+        }
+        defaultParams={{ sorts: [{ responseTime: 'desc' }] }}
+        search={false}
+        actionRef={actionRef}
+      />
+      {visible && (
+        <Detail
+          data={current}
+          close={() => {
+            setVisible(false);
+            setCurrent({});
+          }}
+        />
+      )}
+    </>
+  );
+};
+export default Access;

+ 16 - 0
src/pages/Log/Access/typings.d.ts

@@ -0,0 +1,16 @@
+export type AccessLogItem = {
+  id: string;
+  context: any;
+  describe: string;
+  exception: string;
+  httpHeaders: any;
+  httpMethod: string;
+  ip: string;
+  method: string;
+  parameters: any;
+  requestTime: number;
+  responseTime: number;
+  target: string;
+  url: string;
+  action: string;
+};

+ 35 - 0
src/pages/Log/System/Detail/index.tsx

@@ -0,0 +1,35 @@
+import { Input, Modal, Space, Tag } from 'antd';
+import type { SystemLogItem } from '@/pages/Log/System/typings';
+import { useEffect, useState } from 'react';
+import moment from 'moment';
+
+interface Props {
+  data: Partial<SystemLogItem>;
+  close: () => void;
+}
+
+const Detail = (props: Props) => {
+  const [data, setDada] = useState<Partial<SystemLogItem>>(props.data || {});
+
+  useEffect(() => {
+    setDada(props.data);
+  }, [props.data]);
+
+  return (
+    <Modal title={'详情'} visible onCancel={props.close} onOk={props.close} width={1000}>
+      <Space>
+        <span>[{data?.threadName}]</span>
+        <span>{moment(data?.createTime).format('YYYY-MM-DD HH:mm:ss')}</span>
+        <span>{data?.className}</span>
+      </Space>
+      <p>
+        <Tag color={data?.level === 'ERROR' ? 'red' : 'orange'}>{data?.level}</Tag>
+        {data?.message}
+      </p>
+      <div>
+        <Input.TextArea rows={20} value={data?.exceptionStack} />
+      </div>
+    </Modal>
+  );
+};
+export default Detail;

+ 140 - 0
src/pages/Log/System/index.tsx

@@ -0,0 +1,140 @@
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { useRef, useState } from 'react';
+import type { ProColumns, ActionType } from '@jetlinks/pro-table';
+import type { SystemLogItem } from '@/pages/Log/System/typings';
+import { Tag, Tooltip } from 'antd';
+import moment from 'moment';
+import BaseService from '@/utils/BaseService';
+import { EyeOutlined } from '@ant-design/icons';
+import ProTable from '@jetlinks/pro-table';
+import SearchComponent from '@/components/SearchComponent';
+import Detail from '@/pages/Log/System/Detail';
+
+const service = new BaseService<SystemLogItem>('logger/system');
+const System = () => {
+  const intl = useIntl();
+  const actionRef = useRef<ActionType>();
+  const [param, setParam] = useState({});
+  const [visible, setVisible] = useState<boolean>(false);
+  const [current, setCurrent] = useState<Partial<SystemLogItem>>({});
+
+  const columns: ProColumns<SystemLogItem>[] = [
+    {
+      title: intl.formatMessage({
+        id: 'pages.table.name',
+        defaultMessage: '名称',
+      }),
+      dataIndex: 'name',
+      ellipsis: true,
+    },
+    {
+      title: '日志级别',
+      dataIndex: 'level',
+      width: 80,
+      render: (text) => <Tag color={text === 'ERROR' ? 'red' : 'orange'}>{text}</Tag>,
+      valueType: 'select',
+      valueEnum: {
+        ERROR: {
+          text: 'ERROR',
+          status: 'ERROR',
+        },
+        INFO: {
+          text: 'INFO',
+          status: 'INFO',
+        },
+        DEBUG: {
+          text: 'DEBUG',
+          status: 'DEBUG',
+        },
+        WARN: {
+          text: 'WARN',
+          status: 'WARN',
+        },
+      },
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.log.system.logContent',
+        defaultMessage: '日志内容',
+      }),
+      dataIndex: 'exceptionStack',
+      ellipsis: true,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.log.system.serviceName',
+        defaultMessage: '服务名',
+      }),
+      dataIndex: 'context.server',
+      width: 150,
+      ellipsis: true,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.log.system.creationTime',
+        defaultMessage: '创建时间',
+      }),
+      dataIndex: 'createTime',
+      width: 200,
+      sorter: true,
+      ellipsis: true,
+      valueType: 'dateTime',
+      defaultSortOrder: 'descend',
+      renderText: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.data.option',
+        defaultMessage: '操作',
+      }),
+      valueType: 'option',
+      align: 'center',
+      width: 200,
+      render: (text, record) => [
+        <a
+          key="editable"
+          onClick={() => {
+            setVisible(true);
+            setCurrent(record);
+          }}
+        >
+          <Tooltip title="查看">
+            <EyeOutlined />
+          </Tooltip>
+        </a>,
+      ],
+    },
+  ];
+  return (
+    <>
+      <SearchComponent<SystemLogItem>
+        field={columns}
+        target="system-log"
+        onSearch={(data) => {
+          actionRef.current?.reset?.();
+          setParam(data);
+        }}
+      />
+      <ProTable<SystemLogItem>
+        columns={columns}
+        params={param}
+        request={async (params) =>
+          service.query({ ...params, sorts: [{ name: 'createTime', order: 'desc' }] })
+        }
+        defaultParams={{ sorts: [{ createTime: 'desc' }] }}
+        search={false}
+        actionRef={actionRef}
+      />
+      {visible && (
+        <Detail
+          data={current}
+          close={() => {
+            setVisible(false);
+            setCurrent({});
+          }}
+        />
+      )}
+    </>
+  );
+};
+export default System;

+ 14 - 0
src/pages/Log/System/typings.d.ts

@@ -0,0 +1,14 @@
+export type SystemLogItem = {
+  id: string;
+  className: string;
+  context: any;
+  createTime: number;
+  exceptionStack: string;
+  level: string;
+  lineNumber: number;
+  message: string;
+  methodName: string;
+  name: string;
+  threadId: string;
+  threadName: string;
+};

+ 28 - 0
src/pages/Log/index.tsx

@@ -0,0 +1,28 @@
+import { PageContainer } from '@ant-design/pro-layout';
+import { useState } from 'react';
+import Access from '@/pages/Log/Access';
+import System from '@/pages/Log/System';
+
+const Log = () => {
+  const [tab, setTab] = useState<string>('access');
+  const list = [
+    {
+      key: 'access',
+      tab: '访问日志',
+      component: <Access />,
+    },
+    {
+      key: 'system',
+      tab: '系统日志',
+      component: <System />,
+    },
+  ];
+
+  return (
+    <PageContainer onTabChange={setTab} tabList={list} tabActiveKey={tab}>
+      {list.find((k) => k.key === tab)?.component}
+    </PageContainer>
+  );
+};
+
+export default Log;

+ 0 - 0
src/pages/log/index.md