修改查看日志的展示方式
This commit is contained in:
parent
c1b54b4b4e
commit
9e640ee7a8
49
spider-flow-api/src/main/java/org/spiderflow/io/Line.java
Normal file
49
spider-flow-api/src/main/java/org/spiderflow/io/Line.java
Normal file
@ -0,0 +1,49 @@
|
||||
package org.spiderflow.io;
|
||||
|
||||
public class Line {
|
||||
|
||||
private long from;
|
||||
|
||||
private String text;
|
||||
|
||||
private long to;
|
||||
|
||||
public Line(long from, String text, long to) {
|
||||
this.from = from;
|
||||
this.text = text;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public long getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public void setFrom(long from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public long getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public void setTo(long to) {
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Line{" +
|
||||
"from=" + from +
|
||||
", text='" + text + '\'' +
|
||||
", to=" + to +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
package org.spiderflow.io;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RandomAccessFileReader implements Closeable {
|
||||
|
||||
private RandomAccessFile raf;
|
||||
|
||||
/**
|
||||
* 从index位置开始读取
|
||||
*/
|
||||
private long index;
|
||||
|
||||
/**
|
||||
* 读取顺序,默认倒叙
|
||||
*/
|
||||
private boolean reversed;
|
||||
|
||||
/**
|
||||
* 缓冲区大小
|
||||
*/
|
||||
private int bufSize;
|
||||
|
||||
public RandomAccessFileReader(RandomAccessFile raf, long index, boolean reversed) throws IOException {
|
||||
this(raf, index, 1024, reversed);
|
||||
}
|
||||
|
||||
public RandomAccessFileReader(RandomAccessFile raf, long index, int bufSize, boolean reversed) throws IOException {
|
||||
if (raf == null) {
|
||||
throw new NullPointerException("file is null");
|
||||
}
|
||||
this.raf = raf;
|
||||
this.reversed = reversed;
|
||||
this.bufSize = bufSize;
|
||||
this.index = index;
|
||||
this.init();
|
||||
}
|
||||
|
||||
private void init() throws IOException {
|
||||
if (reversed) {
|
||||
this.index = this.index == -1 ? this.raf.length() : Math.min(this.index, this.raf.length());
|
||||
} else {
|
||||
this.index = Math.min(Math.max(this.index, 0), this.raf.length());
|
||||
}
|
||||
if (this.index > 0) {
|
||||
this.raf.seek(this.index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取n行
|
||||
*
|
||||
* @param n 要读取的行数
|
||||
* @param keywords 搜索的关键词
|
||||
* @param matchcase 是否区分大小写
|
||||
* @param regx 是否是正则搜索
|
||||
* @return 返回Line对象,包含行的起始位置与终止位置
|
||||
*/
|
||||
public List<Line> readLine(int n, String keywords, boolean matchcase, boolean regx) throws IOException {
|
||||
List<Line> lines = new ArrayList<>(n);
|
||||
long lastCRLFIndex = reversed ? this.index : (this.index > 0 ? this.index + 1 : -1);
|
||||
boolean find = keywords == null || keywords.isEmpty();
|
||||
Pattern pattern = regx && !find ? Pattern.compile(keywords) : null;
|
||||
while (n > 0) {
|
||||
byte[] buf = reversed ? new byte[(int) Math.min(this.bufSize, this.index)] : new byte[this.bufSize];
|
||||
if (this.reversed) {
|
||||
if (this.index == 0) {
|
||||
break;
|
||||
}
|
||||
this.raf.seek(this.index -= buf.length);
|
||||
}
|
||||
int len = this.raf.read(buf, 0, buf.length);
|
||||
if (len == -1) { //已读完
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < len && n > 0; i++) {
|
||||
int readIndex = reversed ? len - i - 1 : i;
|
||||
if (isCRLF(buf[readIndex])) { //如果读取到\r或\n
|
||||
if (Math.abs(this.index + readIndex - lastCRLFIndex) > 1) { //两行之间的间距,当=1时则代表有\r\n,\n\r,\r\r,\n\n四种情况之一
|
||||
long fromIndex = reversed ? this.index + readIndex : lastCRLFIndex; //计算起止位置
|
||||
long endIndex = reversed ? lastCRLFIndex : this.index + readIndex; //计算终止位置
|
||||
Line line = readLine(fromIndex + 1, endIndex); //取出文本
|
||||
if (find || (find = (pattern == null ? find(line.getText(), keywords, matchcase) : find(line.getText(), pattern)))) { //定位查找,使被查找的行始终在第一行
|
||||
if (reversed) {
|
||||
lines.add(0, line); //反向查找时,插入到List头部
|
||||
} else {
|
||||
lines.add(line);
|
||||
}
|
||||
n--;
|
||||
}
|
||||
}
|
||||
lastCRLFIndex = this.index + readIndex; //记录上次读取到的\r或\n位置
|
||||
}
|
||||
}
|
||||
if (!reversed) {
|
||||
this.index += buf.length;
|
||||
}
|
||||
}
|
||||
if (reversed && n > 0 && lastCRLFIndex > 1 && (find || lines.size() > 0)) {
|
||||
lines.add(0, readLine(0, lastCRLFIndex));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
private boolean find(String text, String keywords, boolean matchcase) {
|
||||
return matchcase ? text.contains(keywords) : text.toLowerCase().contains(keywords.toLowerCase());
|
||||
}
|
||||
|
||||
private boolean find(String text, Pattern pattern) {
|
||||
return pattern.matcher(text).find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定位置读取一行
|
||||
*
|
||||
* @param fromIndex 开始位置
|
||||
* @param endIndex 结束位置
|
||||
* @return 返回Line对象
|
||||
* @throws IOException
|
||||
*/
|
||||
private Line readLine(long fromIndex, long endIndex) throws IOException {
|
||||
long index = this.raf.getFilePointer();
|
||||
this.raf.seek(fromIndex);
|
||||
byte[] buf = new byte[(int) (endIndex - fromIndex)];
|
||||
this.raf.read(buf, 0, buf.length);
|
||||
Line line = new Line(fromIndex, new String(buf), endIndex);
|
||||
this.raf.seek(index);
|
||||
return line;
|
||||
}
|
||||
|
||||
private boolean isCRLF(byte b) {
|
||||
return b == 13 || b == 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (this.raf != null) {
|
||||
this.raf.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,8 @@
|
||||
package org.spiderflow.controller;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -22,6 +14,8 @@ import org.spiderflow.executor.FunctionExecutor;
|
||||
import org.spiderflow.executor.FunctionExtension;
|
||||
import org.spiderflow.executor.PluginConfig;
|
||||
import org.spiderflow.executor.ShapeExecutor;
|
||||
import org.spiderflow.io.Line;
|
||||
import org.spiderflow.io.RandomAccessFileReader;
|
||||
import org.spiderflow.model.Grammer;
|
||||
import org.spiderflow.model.JsonBean;
|
||||
import org.spiderflow.model.Plugin;
|
||||
@ -33,9 +27,15 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 爬虫Controller
|
||||
@ -155,16 +155,14 @@ public class SpiderFlowController {
|
||||
}
|
||||
|
||||
@RequestMapping("/log")
|
||||
public String log(String id){
|
||||
SpiderFlow flow = spiderFlowService.getById(id);
|
||||
if(flow == null){
|
||||
return "未找到此爬虫";
|
||||
}
|
||||
try {
|
||||
return FileUtils.readFileToString(new File(spiderLogPath,id + ".log"), "UTF-8");
|
||||
public JsonBean<List<Line>> log(String id, String keywords, Long index, Integer count, Boolean reversed,Boolean matchcase,Boolean regx){
|
||||
try (RandomAccessFileReader reader = new RandomAccessFileReader(new RandomAccessFile(new File(spiderLogPath,id + ".log"),"r"), index == null ? -1 : index, reversed == null || reversed)){
|
||||
return new JsonBean<>(reader.readLine(count == null ? 10 : count,keywords,matchcase != null && matchcase,regx != null && regx));
|
||||
} catch(FileNotFoundException e){
|
||||
return new JsonBean<>(0,"日志文件不存在");
|
||||
} catch (IOException e) {
|
||||
logger.error("读取日志文件出错",e);
|
||||
return "读取日志文件出错," + e.getMessage();
|
||||
return new JsonBean<>(-1,"读取日志文件出错");
|
||||
}
|
||||
}
|
||||
|
||||
|
166
spider-flow-web/src/main/resources/static/js/log-viewer.js
Normal file
166
spider-flow-web/src/main/resources/static/js/log-viewer.js
Normal file
@ -0,0 +1,166 @@
|
||||
function LogViewer(options){
|
||||
options = options || {};
|
||||
this.element = options.element;
|
||||
this.maxLines = options.maxLines || 10;
|
||||
this.onSearchFinish = options.onSearchFinish || function(){};
|
||||
this.bufferSize = this.maxLines * 10;
|
||||
this.logId = options.logId;
|
||||
this.url = options.url;
|
||||
this.buffer = [];
|
||||
this.displayIndex = -1;
|
||||
this.index = -1;
|
||||
this.loading = false;
|
||||
this.reversed = true;
|
||||
this.matchcase = true;
|
||||
this.initEvent();
|
||||
this.init(options.onLoad);
|
||||
}
|
||||
LogViewer.prototype.init = function(callback){
|
||||
var _this = this;
|
||||
_this.index = -1;
|
||||
this.autoLoad(callback);
|
||||
}
|
||||
LogViewer.prototype.autoLoad = function(callback){
|
||||
var _this = this;
|
||||
this.loadLines(this.maxLines,function(hasData){
|
||||
if(_this.reversed){
|
||||
_this.displayIndex = _this.buffer.length - _this.maxLines;
|
||||
}else{
|
||||
_this.displayIndex = 0;
|
||||
}
|
||||
_this.render(_this.buffer.slice(_this.displayIndex,_this.displayIndex + _this.maxLines));
|
||||
callback&&callback(hasData);
|
||||
},false);
|
||||
}
|
||||
LogViewer.prototype.render = function(lines){
|
||||
if(lines.length == 0){
|
||||
return;
|
||||
}
|
||||
this.firstFrom = lines[0].from;
|
||||
this.firstTo = lines[0].to;
|
||||
this.lastFrom = lines[lines.length - 1].from;
|
||||
this.lastTo = lines[lines.length - 1].to;
|
||||
var html = [];
|
||||
if(this.reversed){
|
||||
lines = lines.reverse();
|
||||
}
|
||||
var find = this.keywords === undefined || this.keywords === '';
|
||||
var regx = new RegExp('(' + this.keywords + ')',this.matchcase ? "ig" : "g");
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var text = lines[i].text;
|
||||
if(find == false && (find = text.match(regx))){
|
||||
text = text.replace(regx,'b4430885ba83495_$1_88d1220d37eac831d');
|
||||
}
|
||||
//转义html
|
||||
text = text.replace(/</g,'<');
|
||||
//搜索关键词高亮
|
||||
text = text.replace(/b4430885ba83495_(.*?)_88d1220d37eac831d/g,'<em class="search-finded">$1</em>');
|
||||
html.push('<div class="log-row">' + text + '</div>');
|
||||
}
|
||||
if(this.reversed){
|
||||
html = html.reverse();
|
||||
}
|
||||
this.element.html(html.join(''));
|
||||
}
|
||||
LogViewer.prototype.search = function(reversed){
|
||||
if(reversed === undefined){
|
||||
reversed = this.reversed;
|
||||
}
|
||||
this.index = reversed ? this.lastFrom : this.firstTo;
|
||||
var _this = this;
|
||||
this.autoLoad(function(hasData){
|
||||
_this.onSearchFinish(hasData);
|
||||
});
|
||||
}
|
||||
LogViewer.prototype.initEvent = function(){
|
||||
var _this = this;
|
||||
function eventFunc(e){
|
||||
e.stopPropagation();
|
||||
_this.scroll((e.wheelDelta||e.detail) > 0,3);
|
||||
return false;
|
||||
}
|
||||
document.addEventListener('DOMMouseScroll',eventFunc,false);
|
||||
window.onmousewheel = document.onmousewheel = eventFunc;
|
||||
document.addEventListener('keydown', function (e) {
|
||||
e = e || event;
|
||||
var currKey = e.keyCode || e.which || e.charCode;
|
||||
if (currKey === 38 || currKey === 40) {
|
||||
if(_this.keywords){
|
||||
_this.search(currKey === 38);
|
||||
}else{
|
||||
_this.scroll(currKey === 38, 1);
|
||||
}
|
||||
}
|
||||
if (currKey === 33 || currKey === 34) {
|
||||
_this.scroll(currKey === 33, _this.maxLines);
|
||||
}
|
||||
if (currKey === 36 || currKey ===35){
|
||||
_this.reversed = currKey === 35;
|
||||
_this.init();
|
||||
}
|
||||
});
|
||||
}
|
||||
LogViewer.prototype.setOptions = function(key,value){
|
||||
var _this = this;
|
||||
_this[key] = value;
|
||||
}
|
||||
LogViewer.prototype.scroll = function(reversed,count){
|
||||
var _this = this;
|
||||
_this.reversed = reversed;
|
||||
var ignore = false;
|
||||
if(reversed){
|
||||
if(this.displayIndex == 0){
|
||||
this.index = this.buffer[0].from;
|
||||
this.loadLines(this.bufferSize,function(hasData){
|
||||
if(hasData){
|
||||
_this.displayIndex = Math.max(_this.buffer.length - _this.maxLines,0);
|
||||
}
|
||||
},false);
|
||||
}else{
|
||||
_this.displayIndex-=count;
|
||||
}
|
||||
}else{
|
||||
if(this.displayIndex + this.maxLines >= this.buffer.length){
|
||||
this.index = this.buffer[this.buffer.length - 1].to;
|
||||
this.loadLines(this.bufferSize,function(hasData){
|
||||
if(hasData){
|
||||
_this.displayIndex = 0;
|
||||
}
|
||||
},false);
|
||||
}else{
|
||||
_this.displayIndex+=count;
|
||||
}
|
||||
}
|
||||
this.render(this.buffer.slice(this.displayIndex,this.displayIndex + this.maxLines));
|
||||
|
||||
}
|
||||
LogViewer.prototype.loadLines = function(count,callback,async){
|
||||
if(this.loading){
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
var _this = this;
|
||||
$.ajax({
|
||||
url : this.url,
|
||||
async : async,
|
||||
type : 'post',
|
||||
data : {
|
||||
reversed : this.reversed,
|
||||
count : this.bufferSize,
|
||||
id : this.logId,
|
||||
index : _this.index,
|
||||
keywords : this.keywords,
|
||||
matchcase : this.matchcase,
|
||||
regx : this.regx
|
||||
},
|
||||
dataType : 'json',
|
||||
success : function(json){
|
||||
var hasData = json&&json.data&&json.data.length > 0;
|
||||
if(hasData){
|
||||
_this.buffer = json.data;
|
||||
}
|
||||
callback && callback(hasData);
|
||||
_this.loading = false;
|
||||
}
|
||||
})
|
||||
}
|
@ -5,28 +5,227 @@
|
||||
<title>SpiderFlow</title>
|
||||
<script type="text/javascript" src="js/layui/layui.all.js" ></script>
|
||||
<script type="text/javascript" src="js/common.js" ></script>
|
||||
<script>$ = layui.$;</script>
|
||||
<script type="text/javascript" src="js/log-viewer.js" ></script>
|
||||
<style type="text/css">
|
||||
html,body,.log-container{
|
||||
*{
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
html,body{
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
font-weight: bold;
|
||||
font-family: Consolas;
|
||||
overflow: hidden;
|
||||
}
|
||||
html,body{
|
||||
overflow: hidden;
|
||||
}
|
||||
.log-container{
|
||||
width : 100%;
|
||||
position: absolute;
|
||||
top : 0px;
|
||||
bottom : 40px;
|
||||
overflow: auto;
|
||||
background-color: #000;
|
||||
}
|
||||
.toolbox-container{
|
||||
width : 100%;
|
||||
position: absolute;
|
||||
height : 24px;
|
||||
padding:8px 0;
|
||||
bottom : 0px;
|
||||
background: #3c3f41;
|
||||
}
|
||||
.toolbox-container .input-text{
|
||||
outline: 0;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
margin-left: 7px;
|
||||
background: #45494a;
|
||||
border: 1px solid #646464;
|
||||
width: 300px;
|
||||
color: #ddd;
|
||||
padding-left: 5px;
|
||||
font-size: 14px;
|
||||
float : left;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.toolbox-container .input-text.search-finish{
|
||||
background: #743a3a;
|
||||
}
|
||||
.toolbox-container .input-checkbox{
|
||||
visibility: hidden;
|
||||
}
|
||||
.toolbox-container .input-checkbox +label{
|
||||
color : #c9c9c9;
|
||||
float : left;
|
||||
font-size:12px;
|
||||
height:24px;
|
||||
line-height: 24px;
|
||||
margin-left: 25px;
|
||||
margin-right:5px;
|
||||
user-select: none;
|
||||
}
|
||||
.toolbox-container .input-checkbox +label::before{
|
||||
display: inline-block;
|
||||
background: #43494a;
|
||||
border:1px solid #6b6b6b;
|
||||
content : '';
|
||||
width : 16px;
|
||||
height : 16px;
|
||||
line-height: 16px;
|
||||
position : absolute;
|
||||
top : 12px;
|
||||
margin-left:-22px;
|
||||
}
|
||||
.toolbox-container .input-checkbox:checked +label::before{
|
||||
display : inline-block;
|
||||
content : "\2714";
|
||||
text-align: center;
|
||||
font-size:12px;
|
||||
color:#fff;
|
||||
}
|
||||
.toolbox-container .btn{
|
||||
display: inline-block;
|
||||
width : 24px;
|
||||
height : 24px;
|
||||
border-radius: 2px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAoklEQVQ4T63SwQnCQBCF4fdIN9qEZxEMgqnAGqzDDgSPCZgYgwcvVmGKEeLIGFZwHEWW7HX34x+WISIPIx2GhVV1Ggm7HYgb78kqTaetneyjqAjscgHG+pjAFZJkFr9Bi0LFwy9okYBZX5TcKz+hhxbzWaF3+0Oz9DB/oTCqh1nWxwsEE32k44WS/UWLWdbNVqEA62/IlDcgz8MuwD9rGF18AERoXOuD03ayAAAAAElFTkSuQmCC");
|
||||
float : left;
|
||||
}
|
||||
.btn.btn-prev{
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAoklEQVQ4T63SwQnCQBCF4fdIN9qEZxEMgqnAGqzDDgSPCZgYgwcvVmGKEeLIGFZwHEWW7HX34x+WISIPIx2GhVV1Ggm7HYgb78kqTaetneyjqAjscgHG+pjAFZJkFr9Bi0LFwy9okYBZX5TcKz+hhxbzWaF3+0Oz9DB/oTCqh1nWxwsEE32k44WS/UWLWdbNVqEA62/IlDcgz8MuwD9rGF18AERoXOuD03ayAAAAAElFTkSuQmCC");
|
||||
}
|
||||
.btn.btn-next{
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAqElEQVQ4T73SzQ2CQBAF4PdCN9iEZ2MiMZEKrME67MDEIySASDx4sQotxgTHjLBIcOCwB/e6+2Xf/BCeh54O/jAvqwMEcwF269UynUqQnaoNIXuQV+bl+aZQgYDxGG5Rou8I3FkUlxCsEwFmY3iIIEH8qXEKWyiKFo+uORZuYkkXT39S1Mb9tmOI3Y3W1Ec/0IptIRM6LKyPIJ58BVsXrz8q/wX4+8q9AR2wXOs7tERxAAAAAElFTkSuQmCC");
|
||||
}
|
||||
.btn.btn-page-prev{
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAABIElEQVQ4T92SP0sDQRDF31vSieA3sLMQrLQVvE7kktsVIljYWAgGsbDQwtZGGwsRNKUgQoTs3h9t4xew1MLSL6EW3kjucppcEu3danfe/N4Ms0OMOM65OajKogAfU5MT157nvZfTWA60w3CZoi5ATHc1QjqQdEdr/dyfOwC2w2STkEsAlZLhqwIbQeDfFfFv0EXJoYgcZYLgBcTM0D2VXWNqZ3knAKyLzkE2cje5QcomFDrZM4UHJVsA1zOAPNU1f482jFoA670WDkxQPbE2XuoHjak+2DDeB3DcM2/SRvEVBBsCrq0G/m3WwQiwG2+HSZ2QlgK2h6b6Gzh2qoUwruJ/Ap27nxV+PuX/yAVj/Mc/d/VnQMk8qd60XhnY0UL/AhYefjeyfy3+AAAAAElFTkSuQmCC");
|
||||
}
|
||||
.btn.btn-page-next{
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAABGklEQVQ4T+VSsUoDQRSctwGxzCcICnZWfoBiIcjldlc/QQhWgihYGlsVC0VQmxSWajbvPIhoEX/AQgQ7wX+w9Ngn2VM4k80X+Kp9szMLOzOEMcN8PyPiJ4xZeYtRKAYy9+Y8ipdw52ne2uR5mBcVOne3AIV+KcSitY2nfyl0nG8DcqRAWuskG5gwzhzmPPUQBuSaOt3smIi2goHe769Z3YoJbx23lFJ7pbtyGeLoZPkhiewECHDkcVKNQxQ2CbBBQ3Rq02Swl+M4bwJyUW70Dsj06Bm7VjcOAqMa7E03X67Bn4No6m/g9AXy6zZNr37xkeYw92Y9FWcQLP3850MgzVWtH6uPRSvXbvcn6/XPDShVIykejDGvw5X7BuachnZstBghAAAAAElFTkSuQmCC");
|
||||
}
|
||||
.btn.btn-page-home{
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAA/klEQVQ4T9WRsUoDURBF7107O/0Av8DCP1CwsNnNMinW3kJELAzYpom9WKXTSmxEXzazlmJrY+cX+A2iguSN7BPDZvMghZVTDMPMPXNhhgDgxvoIw1ZdLwxCpZN1+CdwoUtEEBzboarLE2PPzFYkz05imjnQufsNLPlzGDYDQF58fb4fF0Xx1lwwA96OdDdJOITZalNksCf6yZGIPP/2p2Cp1cB76/8MeAjYsFV/ANyXPL0KijrdjcZnJHsAXuGxJ5I9uFKtnkme0TndRoJLAGsGFt08vQlgWeqBJ3YsYb+bpi/hRQ0wLK+qdXo7NeP1FIxdrQ22NdF3xBz/MfgNtRhyDx//lbEAAAAASUVORK5CYII=");
|
||||
}
|
||||
.btn.btn-page-end{
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAA/0lEQVQ4T91SsU4CQRSct8QEjd/AF9DR0BJL73EsxoZYSUdl7LWhNcHCGGNBzxVsjruGggT/wMIv4AOws/PGcKgceqeJpVtt3pvZ93ZmBAXHhRFXLeur5EFyiyvgfyOOw/hYhB0auWh73lPeH8dxXJWEfUNMfV/vUnFSIhgAWCDBqbU6y4rjXHQAgyGACslBu9U8/1TVhfEJwHsAu4D0AN6ubdjcjZG+r95lWs165Jyr0ZRuBFLf8k5kmSTsHbV09FH/5mMQBPs75b1rkN31QDzg1ZxZe/iYfeynAFyJyHNJOFDVl6/pKSQWRXFrVTeJJiD0N/D76nPb1EY68S/EN0z7aw9vab4+AAAAAElFTkSuQmCC");
|
||||
}
|
||||
.toolbox-container .btn:hover{
|
||||
background-color: #4c5052;
|
||||
}
|
||||
::-webkit-input-placeholder {
|
||||
color: #eee;
|
||||
}
|
||||
.log-row{
|
||||
font-family: Consolas,serif;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
height : 14px;
|
||||
white-space: pre;
|
||||
color: #e5e5e5;
|
||||
background: #000;
|
||||
}
|
||||
.log-row::selection{
|
||||
background: rgba(255,255,255,.998);
|
||||
color : #000;
|
||||
}
|
||||
.log-row em.search-finded{
|
||||
font-style: inherit;
|
||||
background :#ff0 !important;
|
||||
color : #000;
|
||||
}
|
||||
::-webkit-scrollbar{
|
||||
width : 8px;
|
||||
height : 8px;
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-track{
|
||||
border-radius: 2px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb{
|
||||
border-radius: 2px;
|
||||
background: #999;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<div class="log-container"></div>
|
||||
<div class="toolbox-container">
|
||||
<input type="text" id="keywords" placeholder="请输入关键词搜索定位日志" class="input-text"/>
|
||||
<input type="checkbox" id="reversed" checked class="input-checkbox"/>
|
||||
<label for="reversed">反向搜索</label>
|
||||
<input type="checkbox" id="matchcase" checked class="input-checkbox"/>
|
||||
<label for="matchcase">区分大小写</label>
|
||||
<input type="checkbox" id="regx" class="input-checkbox"/>
|
||||
<label for="regx">正则搜索</label>
|
||||
<span class="btn btn-prev" title="上一个/上一行(↑)"></span>
|
||||
<span class="btn btn-next" title="下一个/下一行(↓)"></span>
|
||||
<span class="btn btn-page-prev" title="上一页(Page Up)"></span>
|
||||
<span class="btn btn-page-next" title="下一页(Page Down)"></span>
|
||||
<span class="btn btn-page-home" title="第一页/首页(Home)"></span>
|
||||
<span class="btn btn-page-end" title="最后一页/尾页(End)"></span>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var id = getQueryString('id');
|
||||
layui.$.ajax({
|
||||
$(function(){
|
||||
var viewer = new LogViewer({
|
||||
url: 'spider/log',
|
||||
data : {
|
||||
id : id
|
||||
maxLines : parseInt(($('.log-container').height() - 8) / 14),
|
||||
logId : getQueryString('id'),
|
||||
element : $('.log-container'),
|
||||
onSearchFinish : function(hasData){
|
||||
if(hasData){
|
||||
$('.input-text').removeClass('search-finish');
|
||||
}else{
|
||||
$('.input-text').addClass('search-finish').focus();
|
||||
}
|
||||
},
|
||||
dataType : 'text',
|
||||
success : function(content){
|
||||
layui.$('.log-container').html(content.replace(/\n/g,'<br>').replace(/ /g,' ').replace(/\t/g,' '));
|
||||
onLoad : function(hasData){
|
||||
if(!hasData){
|
||||
layui.layer.alert('日志文件不存在');
|
||||
}
|
||||
}
|
||||
})
|
||||
var setOptions = function(){
|
||||
viewer.setOptions('keywords',$('.toolbox-container .input-text').val());
|
||||
viewer.setOptions('matchcase',$('#matchcase').is(':checked'));
|
||||
viewer.setOptions('regx',$('#regx').is(':checked'));
|
||||
viewer.setOptions('reversed',$('#reversed').is(':checked'));
|
||||
}
|
||||
$('.toolbox-container').on('keydown','.input-text',function(e){
|
||||
setOptions();
|
||||
if(e.keyCode === 13){
|
||||
viewer.search();
|
||||
}
|
||||
if(this.value === ''){
|
||||
$(this).removeClass('search-finish');
|
||||
}
|
||||
}).on('change','.input-checkbox',function(){
|
||||
setOptions();
|
||||
}).on('click','.btn-prev',function(){
|
||||
if(viewer.keywords){
|
||||
viewer.search(true);
|
||||
}else{
|
||||
viewer.scroll(true,1);
|
||||
}
|
||||
}).on('click','.btn-next',function(){
|
||||
if(viewer.keywords){
|
||||
viewer.search(false);
|
||||
}else{
|
||||
viewer.scroll(false,1);
|
||||
}
|
||||
}).on('click','.btn-page-prev',function(){
|
||||
viewer.scroll(true,viewer.maxLines);
|
||||
}).on('click','.btn-page-next',function(){
|
||||
viewer.scroll(false,viewer.maxLines);
|
||||
}).on('click','.btn-page-home',function(){
|
||||
viewer.setOptions('reversed',false);
|
||||
viewer.init();
|
||||
}).on('click','.btn-page-end',function(){
|
||||
viewer.setOptions('reversed',true);
|
||||
viewer.init();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user