看日志希望带有彩色,希望从浏览器上看到,不用连到机器上看。

浏览系统的文件夹,scan + 系统文件夹的层级名字当做url路由,可以深层次看到机器上任何层级的文件夹,实现系统文件夹浏览下载。

如果是点击文件夹进入子目录。

如果是点击文件,尝试以文本格式读取文件,并以实时更新的方式显示到浏览器日志控制台并加彩。 主要是要做到不遗漏推送日志和不重复推送日志,采用的是python 操作文件的seek和tell。

浏览系统目录和下载文件的页面

查看实时日志更新的页面,提供了暂停功能和自动下拉功能。把日志根据级别加了彩色,更容易观察哪些是严重的,哪些是debug的。

实现代码。

# -*- coding: utf-8 -*-
# @Author : ydf
# @Time : 2019/6/14 17:33
import os
from pathlib import Path
from flask import Flask, send_from_directory, url_for, jsonify, request, render_template, current_app, abort, g, send_file
from flask_httpauth import HTTPBasicAuth
from flask_bootstrap import Bootstrap
from app.utils_ydf import LogManager, nb_print, time_util print(str((Path(__file__).parent / Path('ydf_dir')).absolute()))
app = Flask(__name__, template_folder=str((Path(__file__).parent / Path('ydf_dir')).absolute()))
app.config['JSON_AS_ASCII'] = False
app.config['REFRESH_MSEC'] = 1000
auth = HTTPBasicAuth()
LogManager(app.logger.name).get_logger_and_add_handlers()
bootstrap = Bootstrap(app) @app.route('/favicon.ico')
def favicon():
print(Path(__file__).parent / Path('ydf_dir/').absolute())
return send_from_directory(str(Path(__file__).parent / Path('ydf_dir/').absolute()),
'log_favicon.ico', mimetype='image/vnd.microsoft.icon') @app.route("/ajax0/<path:fullname>/")
def info0(fullname):
fullname = f'/{fullname}'
position = int(request.args.get('position'))
current_app.logger.debug(position)
# if os.path.isfile(full_name):
# fo = open(full_name,encoding='utf8')
# content = fo.read()
# return content
# else :
# return "There is no log file" with open(fullname, 'rb') as f:
try:
if position == 0:
f.seek(-50000, 2)
else:
f.seek(position, 0)
except Exception:
current_app.logger.exception('读取错误')
f.seek(0, 0)
content_text = f.read().decode()
# nb_print([content_text])
content_text = content_text.replace('\n', '<br>')
# nb_print(content_text)
position_new = f.tell()
current_app.logger.debug(position_new)
# nb_print(len(content_text)) return jsonify(content_text=content_text, position=position_new) @app.route("/ajax/<path:fullname>/")
def info(fullname):
fullname = f'/{fullname}'
position = int(request.args.get('position'))
current_app.logger.debug(position)
# if os.path.isfile(full_name):
# fo = open(full_name,encoding='utf8')
# content = fo.read()
# return content
# else :
# return "There is no log file" with open(fullname, 'rb') as f:
try:
if position == 0:
f.seek(-50000, 2)
else:
f.seek(position, 0)
except Exception:
current_app.logger.exception('读取错误')
f.seek(0, 0)
lines = f.readlines()
content_text = ''
for line in lines:
line = line.strip().decode()
if '- DEBUG -' in line:
color = '#00FF00'
elif '- INFO -' in line:
color = '#00FFFF'
elif '- WARNING -' in line:
color = 'yellow'
elif '- ERROR -' in line:
color = '#FF00FF'
elif '- CRITICAL -' in line:
color = '#FF0033'
else:
color = ''
content_text += f'<p style="color:{color}"> {line} </p>' # content_text = f.read().decode()
# # nb_print([content_text])
# content_text = content_text.replace('\n', '<br>')
# # nb_print(content_text)
position_new = f.tell()
current_app.logger.debug(position_new)
# nb_print(content_text) return jsonify(content_text=content_text, position=position_new) @app.route("/view/<path:fullname>")
def view(fullname):
view_html = '''
<html>
<head>
<title>查看 %s </title>
<script type="text/javascript" src="http://libs.baidu.com/jquery/2.1.1/jquery.min.js">
</script>
</head>
<body>
<div id="result"></div>
<hr>
<button onclick="toggle_scroll()"> 自动滚动浏览器滚动条 </button>
&nbsp;
<div style="display: inline" id="auto_scroll_stat">ON</div>
<button id= "runButton" style="margin-left:300px" onclick="startOrStop()"> 运行中 </button>
<button id= "runButton" style="margin-left:300px" > <a href="/%s/d" download="%s">下载 %s</a></button>
</body>
<script>
var autoscroll = "ON";
toggle_scroll = function(){
if(autoscroll == "ON") autoscroll = "OFF";
else autoscroll = "ON";
}
var position = 0;
function downloadFile(){ }
get_log = function(){
$.ajax({url: "/%s/a", data: {"position":position} ,success: function(result){
console.debug(4444);
var resultObj = result;
console.debug(6666);
//var html = document.getElementById("div_id").innerHTML;
var html = $("#result").html();
var htmlShort = html.substr(-40000);
console.debug(htmlShort);
document.getElementById("result").innerHTML = htmlShort || "";
console.debug($("#result").html());
$("#result").append( resultObj.content_text);
console.debug(resultObj.position);
position = resultObj.position;
if(autoscroll == "ON")
window.scrollTo(0,document.body.scrollHeight);
$("#auto_scroll_stat").text(autoscroll);
}});
}
iid = setInterval(get_log,%s);
status = 1;
function startRun(){
$("#runButton").text("运行中");
iid = setInterval(get_log,%s);
status = 1;
} function stopRun(){
$("#runButton").text("停止了");
clearInterval(iid);
status = 0;
}
function startOrStop(){
if(status == 1){
stopRun();}
else
{startRun();}
}
</script>
</html>
'''
# return view_html % (logfilename,logfilename,logfilename,logfilename,logfilename, REFRESH_MSEC, REFRESH_MSEC)
return render_template('/log_view_html.html', fullname=fullname) @app.route('/download/<path:fullname>', )
def download_file(fullname):
current_app.logger.debug(fullname)
return send_file(f'/{fullname}')
# return send_from_directory(f'/{logs_dir}',
# filename, as_attachment=True, ) @app.route('/scan/', )
@app.route('/scan/<path:logs_dir>', )
def index(logs_dir=''):
current_app.logger.debug(logs_dir)
file_ele_list = list()
dir_ele_list = list()
for f in (Path('/') / Path(logs_dir)).iterdir():
fullname = str(f).replace('\\', '/')
if f.is_file():
# current_app.logger.debug(str(f).replace('\\', '/')[1:])
# current_app.logger.debug((logs_dir, str(f).replace('\\','/')[1:]))
current_app.logger.debug(str(f))
current_app.logger.debug(url_for('download_file', fullname=fullname[0:]))
# current_app.logger.debug(url_for('download_file', logs_dir='', filename='windows_to_linux_syn_config.json'))
file_ele_list.append({'is_dir': 0, 'filesize': os.path.getsize(f) / 1000000,
'last_modify_time': time_util.DatetimeConverter(os.stat(str(fullname)).st_mtime).datetime_str,
'url': url_for('view', fullname=fullname[1:]), 'download_url': url_for('download_file', fullname=fullname[1:]), 'fullname': fullname})
if f.is_dir():
fullname = str(f).replace('\\', '/')
dir_ele_list.append({'is_dir': 1, 'filesize': 0,
'last_modify_time': time_util.DatetimeConverter(os.stat(str(f)).st_mtime).datetime_str, 'url': url_for('index', logs_dir=fullname[1:]), 'download_url': url_for('index', logs_dir=fullname[1:]), 'fullname': fullname}) return render_template('dir_view.html', ele_list=dir_ele_list + file_ele_list, logs_dir=logs_dir) @app.template_filter()
def file_filter(filefullname, file_name_part):
if file_name_part == 1:
return str(Path(filefullname).parent)
if file_name_part == 2:
return str(Path(filefullname).name) @app.context_processor
def dir_processor():
def format_logs_dir_to_multi(logs_dir):
parent_dir_list = list()
pa = Path(f'/{logs_dir}')
while True:
nb_print(pa.as_posix())
parent_dir_list.append({'url': url_for('index', logs_dir=pa.as_posix()[1:]), 'dir_name': pa.name[:]})
pa = pa.parent
if pa == Path('/'):
parent_dir_list.append({'url': url_for('index', logs_dir=''), 'dir_name': '根目录'})
break
nb_print(parent_dir_list)
return parent_dir_list return dict(format_logs_dir_to_multi=format_logs_dir_to_multi) @auth.verify_password
def verify_password(username, password):
if username == 'user' and password == 'mtfy123':
return True
return False @app.before_request
@auth.login_required
def before_request():
pass if __name__ == "__main__":
# main()
print(app.url_map) app.run(host="0.0.0.0", port=8888, threaded=True, )

dir_view.html

{% extends 'bootstrap/base.html' %}

{% block title %} {{ logs_dir }} {% endblock %}
{% block scripts %}
{{ super() }}
<script>
(function ($) {
//插件
$.extend($, {
//命名空间
sortTable: {
sort: function (tableId, Idx) {
var table = document.getElementById(tableId);
var tbody = table.tBodies[0];
var tr = tbody.rows; var trValue = new Array();
for (var i = 0; i < tr.length; i++) {
trValue[i] = tr[i]; //将表格中各行的信息存储在新建的数组中
} if (tbody.sortCol == Idx) {
trValue.reverse(); //如果该列已经进行排序过了,则直接对其反序排列
} else {
//trValue.sort(compareTrs(Idx)); //进行排序
trValue.sort(function (tr1, tr2) {
var value1 = tr1.cells[Idx].innerHTML;
var value2 = tr2.cells[Idx].innerHTML;
return value1.localeCompare(value2);
});
} var fragment = document.createDocumentFragment(); //新建一个代码片段,用于保存排序后的结果
for (var i = 0; i < trValue.length; i++) {
fragment.appendChild(trValue[i]);
} tbody.appendChild(fragment); //将排序的结果替换掉之前的值
tbody.sortCol = Idx;
}
}
});
})(jQuery);
</script>
{% endblock %} {% block content %}
  
<div class="container" style="width: 80%">
<span class="label label-default">常用文件夹</span>
<a class="label label-primary" href="{{url_for('index',logs_dir='pythonlogs/')}}" target="_blank">pythonlogs/</a>
<a class="label label-primary" href="{{url_for('index',logs_dir='data/supervisorout')}}" target="_blank">data/supervisorout/</a>
<table class="table" id="table1">
{# <caption> / {{ logs_dir }} 的文件浏览</caption> #}
<caption> {% for dir_item in format_logs_dir_to_multi(logs_dir)|reverse %}
/<a href="{{ dir_item.url }}" > {{ dir_item.dir_name }}</a>
{% endfor %}
&nbsp;的文件浏览
</caption>
<thead>
<tr>
<th onclick="$.sortTable.sort('table1',0)">
<button>名称</button>
</th>
<th onclick="$.sortTable.sort('table1',1)">
<button>最后修改时间</button>
</th>
<th onclick="$.sortTable.sort('table1',2)">
<button>文件大小</button>
</th>
<th>下载文件</th>
</tr>
</thead>
<tbody>
{% for ele in ele_list %}
{% if ele.is_dir %}
<tr class="warning">
{% else %}
<tr class="success">
{% endif %} <td><a href="{{ ele.url }}" >{{ ele.fullname | file_filter(2) }}</a></td>
<td>{{ ele.last_modify_time }}</td>
<td>{{ ele.filesize }} M</td>
<td><a href="{{ ele.download_url }}" download={{ ele.fullname | file_filter(2) }}>下载 {{ ele.fullname | file_filter(2) }}</a></td>
</tr>
{% endfor %} </tbody>
</table> </div>
{% endblock %}

log_view.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>查看 {{ fullname| file_filter(2) }} </title>
<script type="text/javascript" src="http://libs.baidu.com/jquery/2.1.1/jquery.min.js">
</script>
</head>
<style>
.page {
background-color: #000000;
color: #FFFFFF;
}
</style>
<body class="page"> <div id="result"></div>
<hr>
<button onclick="toggle_scroll()"> 自动滚动浏览器滚动条</button>
&nbsp;
<div style="display: inline" id="auto_scroll_stat">ON</div>
<button id="runButton" style="margin-left:300px" onclick="startOrStop()"> 运行中</button>
<button style="margin-left:300px"><a href="{{ url_for('download_file',fullname=fullname) }}" download={{ fullname| file_filter(2) }}>下载 {{ fullname| file_filter(2) }} </a></button>
<script>
var autoscroll = "ON";
toggle_scroll = function () {
if (autoscroll === "ON") autoscroll = "OFF";
else autoscroll = "ON";
};
var position = 0; get_log = function () {
$.ajax({
url: "{{ url_for('info',fullname=fullname) }}", data: {"position": position}, success: function (result) {
console.debug(4444);
var resultObj = result;
console.debug(6666);
//var html = document.getElementById("div_id").innerHTML;
var resultEle = $("#result");
var html = resultEle.html();
var htmlShort = html.substr(-50000);
console.debug(htmlShort);
document.getElementById("result").innerHTML = htmlShort;
console.debug(resultEle.html());
resultEle.append(resultObj.content_text);
console.debug(resultObj.position);
position = resultObj.position;
if (autoscroll === "ON") {
window.scrollTo(0, document.body.scrollHeight);
}
$("#auto_scroll_stat").text(autoscroll);
}
});
};
iid = setInterval(get_log, {{ config.REFRESH_MSEC }});
runStatus = 1; function startRun() {
$("#runButton").text("运行中");
iid = setInterval(get_log, {{ config.REFRESH_MSEC }});
runStatus = 1;
} function stopRun() {
$("#runButton").text("暂停了");
clearInterval(iid);
runStatus = 0;
} function startOrStop() {
if (runStatus === 1) {
stopRun();
} else {
startRun();
}
}
</script> </body>
</html>

使用py 和flask 实现的服务器系统目录浏览,日志文件实时显示到网页的功能的更多相关文章

  1. SNF开发平台WinForm之十三-单独从服务器上获取PDF文件进行显示-SNF快速开发平台3.3-Spring.Net.Framework

    1运行效果: 2开发实现: 如果需要单独显示PDF文件时用下面代码去实现,指定url地址. 地址: . 获取附件管理的实体对象: List<KeyValuePair<string, obj ...

  2. python实现websocket服务器,可以在web实时显示远程服务器日志

    一.开始的话 使用python简单的实现websocket服务器,可以在浏览器上实时显示远程服务器的日志信息. 之前做了一个web版的发布系统,但没实现在线看日志,每次发布版本后,都需要登录到服务器上 ...

  3. lnmp vps服务器删除mysql日志文件三种方法

    我在上一篇文章介绍了著名的LNMP主机一键安装工具,对比了军哥lnmp和AMH主机的差别,由于AMH拥有用户后台界面,易于新手操作,值得推荐. 但是,上周末我网站宕机,收到DNSPOD发来了宕机提醒, ...

  4. python2.7实现websocket服务器,可以在web实时显示远程服务器日志

    一.开始的话 使用python实现websocket服务器,可以在浏览器上实时显示远程服务器的日志. 之前写了一个发布系统,每次发布版本后,为了了解发布情况(进度.是否有错误)都会登录到服务器上查看日 ...

  5. windows服务器自动删除日志文件

    https://blog.csdn.net/u010050174/article/details/72510367 步骤: 1.新建 一个bat脚本 2.添加到window执行计划中,进行每日执行. ...

  6. 将线上服务器生成的日志信息实时导入kafka,采用agent和collector分层传输,app的数据通过thrift传给agent,agent通过avro sink将数据发给collector,collector将数据汇集后,发送给kafka

    记flume部署过程中遇到的问题以及解决方法(持续更新) - CSDN博客 https://blog.csdn.net/lijinqi1987/article/details/77449889 现将调 ...

  7. python websocket网页实时显示远程服务器日志信息

    功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息 一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看.你还在用 ...

  8. django搭建一个小型的服务器运维网站-查看服务器中的日志与前端的datatable的利用

    目录 项目介绍和源码: 拿来即用的bootstrap模板: 服务器SSH服务配置与python中paramiko的使用: 用户登陆与session; 最简单的实践之修改服务器时间: 查看和修改服务器配 ...

  9. 【实操日记】使用 PyQt5 设计下载远程服务器日志文件程序

    最近通过 PyQt5 设计了一个下载服务器指定日期日志文件的程序,里面有些有意思的技术点,现在做一些分享. PyQt5 是一套 Python 绑定 Digia Qt5 应用的框架,是最强大的 GUI ...

随机推荐

  1. 好用的 curl 抓取 页面的封装函数

    由于经常使用php curl 抓取页面的内容,在此mark 平时自己封装的 curl函数,(其实 现在也开始用 Python 来爬了~ ^-^) /** * 封装curl方法 * @author Fr ...

  2. dos与unix文件格式之间的转换

    1. VI编辑器中转换 在VI中使用命令 set ff?(fileformat)可以查看文件的格式,使用set ff=dos(unix)可以设置文件的格式 2.使用sed处理(来至http://www ...

  3. spring AOP简单实现代码存放

    @Before:使用Before增强处理只能在目标方法执行之前织入增强,如果Before增强处理没有特殊处理,目标方法总会自动执行,如果Before处需要阻止目标方法的执行,可通过抛出一个异常来实现. ...

  4. STM32F4XX高效驱动篇1-UART

    之前一直在做驱动方面的整理工作,对驱动的高效性有一些自己的理解这里和大家分享一下.并奉驱动程序,本程序覆盖uart1-8. 串口驱动,这是在每个单片机中可以说是必备接口.可以说大部分产品中都会使用,更 ...

  5. poj 1144 Network 【求一个网络的割点的个数 矩阵建图+模板应用】

    题目地址:http://poj.org/problem?id=1144 题目:输入一个n,代表有n个节点(如果n==0就结束程序运行). 在当下n的这一组数据,可能会有若干行数据,每行先输入一个节点a ...

  6. JSP嵌入ueditor、umeditor富文本编辑器

    一.下载: 1.什么是富文本编辑器?就是: 或者是这个: 其中第一个功能比较详尽,其主要用来编写文章,名字叫做udeitor. 第二个就相对精简,是第一个的MINI版,其主要用来编辑即时聊天或者发帖, ...

  7. LINQ 学习路程 -- 查询操作 Skip SkipWhile Take TakeWhile

    Method Description Skip 跳过序列中指定数量元素,然后返回剩余序列 SkipWhile 只要满足条件,就跳过序列中的元素,然后返回剩余函数 Take 从序列的开头返回指定数量的连 ...

  8. Spring MVC工作原理(好用版)

    Spring MVC工作原理 参考: SpringMVC工作原理 - 平凡希 - 博客园https://www.cnblogs.com/xiaoxi/p/6164383.html SpringMVC的 ...

  9. Mybatis中collection与association的区别

    association是多对一的关系 collection是一个一对多的关系

  10. Oracle--存储过程和自定义函数

    一.相关概念 1.存储过程和存储函数 ~指存储在数据库中供所有用户程序调用的子程序 ~存储过程和存储函数的相同点:完成特定功能的程序 ~存储过程和存储函数区别:是否用return语句返回值 2.创建和 ...