日志上下文浏览 - 自定义 Drawer 和 Scroll 实现

1. 前言

    运维系统接入日志查询,开发日志上下文功能方便查看日志。
    参照阿里云日志服务控制台上下文浏览。

图1-1.日志上下文浏览图

2. 效果

    查看日志列表时,选中某条日志查看其上下文,查询指定日志前后紧邻时间的记录。效果是抽屉加滚动列表;element-ui 组件支持InfiniteScroll(无限滚动组件)Drawer(抽屉组件)实现。此处使用原生HTML 标签及CSS 样式自定义实现Drawer 效果。

图2-1.日志查询列表图
    选中某行日志记录,展开日志可选择查询上下文,上下文查询支持查询紧邻的更早时间记录,更新可查看当前日志时间点之后紧邻的记录。

图2-2.日志查询上下文图

图2-3.日志查询上下文更早日志图

图2-4.日志查询上下文更新日志图

3. 实现

    日志查询模块文件结构如图

图3-1.日志查询文件结构图

    NSTableAnalysisLog.js 和NSTableAnalysisLog.vue 是图2-1.日志查询日志列表图中的日志表单列表效果页。日志上下文效果在index.vue 文件实现。
    此处贴出index.vue 和index.js 文件代码。

3.1 index.vue

<template>
  <div class="page-operate-analysis-log">
    <ns-table-analysis-log/>
    <!-- <el-drawer
      title="我是标题"
      :visible.sync="drawer"
      direction="rtl">
      <span>我来啦!</span>
    </el-drawer> -->
    <div class="ns-drawer">
      <div :class="drawer?'mask':''"/>
      <div :class="drawer?'open':'close'">
        <ns-button @click="closeDrawer()" class="ns-drawer-close" icon="el-icon-close" circle></ns-button>
        <div class="ns-drawer-header">
          日志上下文浏览
        </div>
        <br/>
        <ns-button class="ns-button" type="info" @click="handleTopMore">更早</ns-button>
        <div class="ns-loading">
          <span v-if="topLoading">加载中...</span>
        </div>
        <div class="ns-data-log" v-for="(dataLog, index) in dataLogList" :key="index">
          <pre>
          <font v-if="dataLog.orderNumber > 0" color=red>【{{dataLog.orderNumber}}】</font><font v-if="dataLog.orderNumber < 0" color=blue>【{{dataLog.orderNumber}}】</font><font v-if="dataLog.orderNumber === 0" color=black>【{{dataLog.orderNumber}}】</font><b>【{{dataLog.time}}】{{dataLog.level}}:{{dataLog.location}}</b>
          <b>日志信息</b>:{{dataLog.message}}
          <b>日志详情</b>:{{dataLog.log}}
          </pre>
        </div>
        <div class="ns-loading">
          <span v-if="buttomLoading">加载中...</span>
          <span v-if="!topLoading && !buttomLoading && dataLogList.length === 0">暂无数据</span>
        </div>
        <ns-button class="ns-button" type="info" @click="handleButtomMore">更新</ns-button>
      </div>
    </div>
  </div>
</template>

<script>
import NsTableAnalysisLog from './NsTableAnalysisLog'
import index from './src'
index.components = {
  NsTableAnalysisLog
}
export default index
</script>
<style>
  pre {
    display: block;
    font-family: -moz-fixed;
    margin: 1em 0;
    white-space: pre-wrap;
    word-wrap: break-word;
  }
  .ns-button {
    width: 100%;
  }
  .ns-data-log {
    background: #FAFAFA;
    margin: 5px;
  }
  .ns-loading {
     text-align: center;
     font-weight: 700;
     margin: 10px;;
  }
  .ns-drawer {
    .mask {
      position: fixed;
      z-index: 10;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, .1);
    }
    .close {
      position: absolute;
      z-index: 10;
      left: -800px;
      top: 0;
      width: 800px;
      height: 100%;
      background: #fff;
      transition: left linear 1s;
    }
    .open {
      z-index: 10;
      right: 0;
      top: 0;
      width: 70%;
      height: 100%;
      padding-left: 1%;
      padding-right: 1%;
      float: right;
      overflow-y: auto;
      position: fixed;
      background: #fff;
      transition: left linear 1s;
      .ns-drawer-header {
        margin-top: 10%;
        font-size: 14px;
        font-weight: 700;
        float: right;
      }
      .ns-drawer-close {
        margin-top: 5%;
        position: fixed;
      }
    }
  }
</style>

3.2 index.js

import NsTableAnalysisLog from '../NsTableAnalysisLog'
// import InfiniteScroll from 'vue-infinite-scroll'
// import drawer from 'drawer'
export default {
  name: 'OperateAnalysisLog',
  components: {
    NsTableAnalysisLog
  },
  data () {
    return {
      // 记录日志库
      platformSLSType: '',
      startTime: '',
      endTime: '',
      dataLogList: [],
      // 加载中
      topLoading: false,
      topNoMore: false,
      buttomLoading: false,
      buttomNoMore: false,
      // 抽屉
      drawer: false
    }
  },
  methods: {
    showDrawer (row, platformSLSType, date) {
      this.buttomLoading = true
      this.platformSLSType = platformSLSType
      this.startTime = date[0]
      this.endTime = date[1]
      this.drawer = true
      let type = 0
      this.handleCursorLogContext(row, type)
    },
    closeDrawer () {
      this.buttomLoading = false
      this.topLoading = false
      this.drawer = false
      this.dataLogList = []
    },
    handleButtomMore () {
      this.buttomLoading = true
      // 查询之后的日志
      let type = 0
      let size = this.dataLogList.length - 1
      let row = this.dataLogList[size]
      this.handleCursorLogContext(row, type)
    },
    handleTopMore () {
      this.topLoading = true
      // 查询更早的日志
      let type = 1
      let row = this.dataLogList[0]
      this.handleCursorLogContext(row, type)
    },
    // 上下文查询,type = 0 更新,type = 1 更早
    handleCursorLogContext (row, type) {
      if (row === undefined || row === null) {
        this.$notify.warning('当前日志上下文暂无数据!')
        this.buttomLoading = false
        this.topLoading = false
        return
      }
      if (this.dataLogList.length > 300) {
        this.$notify.warning('当前日志上下文已超出300条记录!')
        this.buttomLoading = false
        this.topLoading = false
        return
      }
      let params = {
        startTime: this.startTime,
        endTime: this.endTime,
        fromTime: row.time,
        logStore: this.platformSLSType,
        type: type
      }
      this.$http.fetch(this.$api.operate.analysisLog.findCursorLogList, params)
        .then((resp) => {
          let resultLength = resp.result.length
          let dataLogListLength = this.dataLogList.length
          if (type === 0) {
            let resultData = []
            for (var index = 0; index < resultLength; index++) {
              let resultIndex = resp.result[index]
              let indexOfFlag = false
              for (let dataLog of this.dataLogList) {
                if (dataLog.level === resultIndex.level && dataLog.location === resultIndex.location && dataLog.log === resultIndex.log && dataLog.message === resultIndex.message && dataLog.time === resultIndex.time) {
                  indexOfFlag = true
                }
              }
              if (indexOfFlag) {
                continue
              }
              if (index === 0 && this.dataLogList.length === 0) {
                resultIndex.orderNumber = 0
              } else if (index === 0 && this.dataLogList.length > 0) {
                resultIndex.orderNumber = this.dataLogList[this.dataLogList.length - 1].orderNumber + 1
              } else if (index > 0) {
                resultIndex.orderNumber = resp.result[index - 1].orderNumber + 1
              }
              resultData.push(resultIndex)
            }
            this.dataLogList = this.dataLogList.concat(resultData)
          } else if (type === 1) {
            for (var i = resultLength - 1; i > 0; i--) {
              if (this.dataLogList.indexOf(resp.result[i]) !== -1) {
                continue
              }
              // 清空临时数据
              let tempData = []
              if (i === resultLength - 1 && this.dataLogList.length === 0) {
                resp.result[i].orderNumber = 0
              } else if (i === resultLength - 1 && this.dataLogList.length > 0) {
                resp.result[i].orderNumber = this.dataLogList[0].orderNumber - 1
                tempData.push(resp.result[i])
                this.dataLogList = tempData.concat(this.dataLogList)
              } else if (i > 0) {
                resp.result[i].orderNumber = resp.result[i + 1].orderNumber - 1
                tempData.push(resp.result[i])
                this.dataLogList = tempData.concat(this.dataLogList)
              }
            }
          }
          if (dataLogListLength === this.dataLogList.length) {
            this.$notify.warning('暂无更多数据!')
          }
        }).catch((resp) => {
          this.$notify.warning(resp.msg)
        }).finally(() => {
          this.buttomLoading = false
          this.topLoading = false
        })
    }
  }
}

3.3 附件:日志记录实体类SLSMessageVo.java

import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
public class SLSMessageVo {

    @ApiModelProperty(value = "日志级别")
    private String level;
    @ApiModelProperty(value = "location")
    private String location;
    @ApiModelProperty(value = "日志信息")
    private String message;
    @ApiModelProperty(value = "日志时间")
    private String time;
    @ApiModelProperty(value = "日志详情")
    private String log;
    @ApiModelProperty(value = "序号")
    private Integer orderNumber;
}

The End

©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页