背景
- 最近在单核1G的乞丐版服务器上部署了个自己的后台应用玩玩,想顺便部署个SpringBoot-admin来查查日志啥的结果一部署上去服务器资源就不够用了,那咋整?不会每次想看日志都的SSH过去然后追到日志目录然后再去tail?想想就觉得麻烦!于是花了点时间做了个简单快捷查日志的小工具,顺便加了关键字高亮,这样日志看起来漂亮多了!
效果
实时查询日志, 服务器新产生的日志会立即打印到该窗口
集成简单的查询和字符操作
源码
https://github.com/shuoGG1239/LogViewer
使用
- 仅需配置下userinfo.json即可. name,host,password为服务器SSH登录三元组
- 然后把cmd下的
/home/server/admin.log
换成服务器日志的路径, 然后直接跑就行了
{
"password": "122222",
"host": "39.108.226.222",
"name": "root",
"cmd": "tail -f -n 500 /home/server/admin.log"
}
分析
其实看到
userinfo
下的字段就知道这其实只是一个超级简化版的SSH客户端了吧, 然后cmd就是自动执行的命令, 最后把执行的命令显示在输出框…确实就是这么简单! 其实本来是对cmd进行一层封装的, 不过后来觉得不自由, 还是改成在cmd填命令直接跑吧, 反正是给自己用的:P其中SSH连接模块是用了
paramiko
, 其实python的SSH工具包很多, 什么fabric
,pexpect
都用过, 但是在windows+Py3
的环境下感觉不是很友好, 踩过一些坑就不多说了, 反正windows下就无脑选paramiko
吧代码高亮部分, 我这边由于日志用的是log4j, 所以着色也是根据log4j的格式来适配,
prettify_text
方法是着色+过滤主体, 可以根据自己服务器日志的格式修改此方法即可
# LogViewer.py
@pyqtSlot(str)
def __slot_redirect(self, text):
raw_text = text.strip()
if raw_text != '':
# 着色+过滤
pretty_text = self.prettify_text(raw_text)
if pretty_text != '':
self.ui.textBrowser.append(pretty_text)
def prettify_text(self, text):
"""
对日志进行渲染美化, 包括着色过滤等
:param text:
:return:
"""
# 含有这些字段的报文都过滤掉
ex_list = ('PushUtil', 'pushUtil', 'Jdbc', 'jdbc', 'HostServiceImpl')
# 着色
pretty_text = log4j_type.colorize(text)
pretty_text = log4j_type.frame_pack('39.108.226.252', pretty_text)
pretty_text = log4j_type.exclude(ex_list, pretty_text)
return pretty_text
- 着色和过滤的具体实现在
log4j_type.py
# log4j_type.py
def colorize(frame):
"""
对单行进行着色
:param frame:
:return:
"""
frame = re.sub(r'(admin\d?\.log)', color_util.bold(color_util.colorize('\\1', 'orange')), frame)
frame = re.sub(r'\s:\s(.+)', color_util.colorize(' : \\1', 'blue'), frame)
frame = frame.replace('INFO', color_util.colorize('INFO', color_util.LIGHT_BLUE))
frame = frame.replace('ERROR', color_util.colorize('ERROR', 'red'))
frame = frame.replace('WARN', color_util.colorize('WARN', 'orange'))
frame = re.sub(r'(\w+Exception)', color_util.colorize('\\1', 'red'), frame)
frame = re.sub(r'(\([\w_]+\.java:\d+\))', color_util.colorize(color_util.underline('\\1'), 'red'), frame)
return frame
- 搜索框的实现: 直接用Qt的东西, 没啥好说的
# SearchForm.py
class SearchForm(QWidget):
signal_key = pyqtSignal(int)
def __init__(self):
QWidget.__init__(self)
self.ui = Ui_SearchForm()
self.ui.setupUi(self)
def on_pushButtonClose_clicked(self):
self.hide()
def on_pushButtonForward_clicked(self):
pass
def on_pushButtonBackward_clicked(self):
pass
def keyPressEvent(self, e):
if (e.modifiers() == Qt.NoModifier) and (e.key() == Qt.Key_Return):
self.signal_key.emit(Qt.Key_Return)
elif (e.modifiers() == Qt.ShiftModifier) and (e.key() == Qt.Key_Return):
self.signal_key.emit(Qt.Key_Return + Qt.ShiftModifier)
elif (e.modifiers() == Qt.ControlModifier) and (e.key() == Qt.Key_F):
self.ui.pushButtonClose.click()
elif (e.modifiers() == Qt.NoModifier) and (e.key() == Qt.Key_Escape):
self.ui.pushButtonClose.click()
# LogViewer.py
# -------search From Start-------
def __init_searchForm(self):
self.searchForm = SearchForm()
self.searchForm.setParent(self)
self.searchForm.hide()
self.searchForm.ui.pushButtonForward.clicked.connect(self.__slot_find_forward)
self.searchForm.ui.pushButtonBackward.clicked.connect(self.__slot_find_backward)
self.searchForm.ui.pushButtonClose.clicked.connect(self.__slot_searchForm_close)
self.searchForm.signal_key.connect(self.__slot_searchForm_keyboard)
@pyqtSlot(int)
def __slot_searchForm_keyboard(self, key):
if key == Qt.Key_Return:
self.ui.textBrowser.find(self.searchForm.ui.lineEdit.text())
elif key == (Qt.Key_Return + Qt.ShiftModifier):
self.ui.textBrowser.find(self.searchForm.ui.lineEdit.text(), QTextDocument.FindBackward)
@pyqtSlot()
def __slot_searchForm_close(self):
self.ui.textBrowser.setFocus()
@pyqtSlot()
def __slot_find_forward(self):
self.ui.textBrowser.find(self.searchForm.ui.lineEdit.text())
@pyqtSlot()
def __slot_find_backward(self):
self.ui.textBrowser.find(self.searchForm.ui.lineEdit.text(), QTextDocument.FindBackward)
def resizeEvent(self, e):
self.searchForm.setGeometry(e.size().width() - self.searchForm.width(), 0,
self.searchForm.width(), self.searchForm.height())
# --------search From End-------