mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-09-26 11:24:50 +02:00
update to 6.8.37
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
#coding: utf-8
|
||||
import os,sys,time,json
|
||||
panelPath = os.getenv('BT_PANEL')
|
||||
if not panelPath:
|
||||
panelPath = "/www/server/panel"
|
||||
os.chdir(panelPath)
|
||||
if not panelPath + "/class/" in sys.path:
|
||||
sys.path.insert(0, panelPath + "/class/")
|
||||
import public,re
|
||||
|
||||
class logsBase:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
def find_line_str(self,_line,search):
|
||||
"""
|
||||
@name 查找字符串
|
||||
"""
|
||||
if search:
|
||||
if _line.lower().find(search.lower()) != -1:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def return_line_area(self,logs_list,ip_list):
|
||||
"""
|
||||
@name 日志行返回归属地
|
||||
"""
|
||||
if len(logs_list) <= 0: return logs_list
|
||||
n_data = '\r\n'.join(logs_list)
|
||||
res = public.get_ips_area(ip_list)
|
||||
for ip in ip_list:
|
||||
area = 'Unknown'
|
||||
if 'status' in res:
|
||||
area = '**** (Professional version exclusive)'
|
||||
elif ip in res:
|
||||
area = res[ip]['info']
|
||||
n_data = n_data.replace(ip,'{}({})'.format(ip,area))
|
||||
log_list = n_data.split('\r\n')
|
||||
return log_list
|
||||
|
||||
def GetNumLines(self,path, num, p=1,search = None):
|
||||
"""
|
||||
@name 取文件指定尾行数
|
||||
@param path 文件路径
|
||||
@param num 取尾行数
|
||||
@param p 当前页
|
||||
@param search 搜索关键字
|
||||
@return list
|
||||
"""
|
||||
pyVersion = sys.version_info[0]
|
||||
max_len = 1024 * 128 * 1024
|
||||
try:
|
||||
from html import escape
|
||||
if not os.path.exists(path): return ""
|
||||
start_line = (p - 1) * num
|
||||
count = start_line + num
|
||||
fp = open(path, 'rb')
|
||||
|
||||
buf = ""
|
||||
fp.seek(-1, 2)
|
||||
if fp.read(1) == "\n": fp.seek(-1, 2)
|
||||
data = []
|
||||
total_len = 0
|
||||
b = True
|
||||
n = 0
|
||||
|
||||
for i in range(count):
|
||||
while True:
|
||||
newline_pos = str.rfind(str(buf), "\n")
|
||||
|
||||
pos = fp.tell()
|
||||
if newline_pos != -1:
|
||||
if n >= start_line:
|
||||
line = buf[newline_pos + 1:]
|
||||
|
||||
is_res = True
|
||||
if search:
|
||||
is_res = False
|
||||
if line.find(search) >= 0 or re.search(search,line):
|
||||
is_res = True
|
||||
|
||||
if is_res:
|
||||
line_len = len(line)
|
||||
total_len += line_len
|
||||
sp_len = total_len - max_len
|
||||
if sp_len > 0:
|
||||
line = line[sp_len:]
|
||||
try:
|
||||
data.insert(0, escape(line))
|
||||
except:
|
||||
pass
|
||||
buf = buf[:newline_pos]
|
||||
n += 1
|
||||
break
|
||||
else:
|
||||
if pos == 0:
|
||||
b = False
|
||||
break
|
||||
to_read = min(4096, pos)
|
||||
fp.seek(-to_read, 1)
|
||||
t_buf = fp.read(to_read)
|
||||
if pyVersion == 3:
|
||||
try:
|
||||
if type(t_buf) == bytes: t_buf = t_buf.decode('utf-8',errors='ignore')
|
||||
except:
|
||||
try:
|
||||
if type(t_buf) == bytes: t_buf = t_buf.decode('gbk',errors='ignore')
|
||||
except:
|
||||
t_buf = str(t_buf)
|
||||
buf = t_buf + buf
|
||||
fp.seek(-to_read, 1)
|
||||
if pos - to_read == 0:
|
||||
buf = "\n" + buf
|
||||
if total_len >= max_len: break
|
||||
if not b: break
|
||||
fp.close()
|
||||
result = "\n".join(data)
|
||||
|
||||
if not result: raise Exception('null')
|
||||
except:
|
||||
result = ''
|
||||
if len(result) > max_len:
|
||||
result = result[-max_len:]
|
||||
|
||||
try:
|
||||
try:
|
||||
result = json.dumps(result)
|
||||
return json.loads(result).strip()
|
||||
except:
|
||||
if pyVersion == 2:
|
||||
result = result.decode('utf8', errors='ignore')
|
||||
else:
|
||||
result = result.encode('utf-8', errors='ignore').decode("utf-8", errors="ignore")
|
||||
return result.strip()
|
||||
except:
|
||||
return ""
|
||||
@@ -0,0 +1,469 @@
|
||||
#coding: utf-8
|
||||
# + -------------------------------------------------------------------
|
||||
# | 宝塔Linux面板
|
||||
# + -------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2016 宝塔软件(http:#bt.cn) All rights reserved.
|
||||
# + -------------------------------------------------------------------
|
||||
# | Author: hezhihong <272267659@@qq.cn>
|
||||
# + -------------------------------------------------------------------
|
||||
import public, os, time
|
||||
from logsModel.base import logsBase
|
||||
|
||||
try:
|
||||
from BTPanel import session
|
||||
except:
|
||||
pass
|
||||
#英文转月份缩写
|
||||
month_list = {
|
||||
"Jan": "1",
|
||||
"Feb": "2",
|
||||
"Mar": "3",
|
||||
"Apr": "4",
|
||||
"May": "5",
|
||||
"Jun": "6",
|
||||
"Jul": "7",
|
||||
"Aug": "8",
|
||||
"Sept": "9",
|
||||
"Sep": "9",
|
||||
"Oct": "10",
|
||||
"Nov": "11",
|
||||
"Dec": "12"
|
||||
}
|
||||
|
||||
|
||||
class main(logsBase):
|
||||
def __init__(self):
|
||||
self.__messages_file = "/var/log/"
|
||||
self.__ftp_backup_path = public.get_backup_path() + '/pure-ftpd/'
|
||||
if not os.path.isdir(self.__ftp_backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(self.__ftp_backup_path))
|
||||
self.__script_py = public.get_panel_path() + '/script/ftplogs_cut.py'
|
||||
|
||||
def get_file_list(self, path, is_bakcup=False):
|
||||
"""
|
||||
@name 取所有messages日志文件
|
||||
@param path: 日志文件路径
|
||||
@return: 返回日志文件列表
|
||||
"""
|
||||
files = os.listdir(path)
|
||||
if is_bakcup:
|
||||
file_name_list = [{
|
||||
"file": "/var/log/pure-ftpd.log",
|
||||
"time": int(time.time())
|
||||
}]
|
||||
else:
|
||||
file_name_list = []
|
||||
for i in files:
|
||||
tmp_dict = {}
|
||||
if not i: continue
|
||||
file_path = path + i
|
||||
tmp_dict['file'] = file_path
|
||||
if is_bakcup:
|
||||
if os.path.isfile(file_path) and i.find('pure-ftpd.log') != -1:
|
||||
tmp_dict['time'] = int(
|
||||
public.to_date(
|
||||
times=os.path.basename(file_path).split('_')[0] +
|
||||
' 00:00:00'))
|
||||
file_name_list.append(tmp_dict)
|
||||
else:
|
||||
if os.path.isfile(file_path) and i.find('messages') != -1:
|
||||
tmp_dict['time'] = int(
|
||||
public.to_date(
|
||||
times=os.path.basename(file_path).split('-')[1] +
|
||||
' 00:00:00'))
|
||||
file_name_list.append(tmp_dict)
|
||||
file_name_list = sorted(file_name_list,
|
||||
key=lambda x: x['time'],
|
||||
reverse=False)
|
||||
return file_name_list
|
||||
|
||||
def set_ftp_log(self, get):
|
||||
"""
|
||||
@name 开启、关闭、获取日志状态
|
||||
@author hezhihong
|
||||
@param get.exec_name 执行的动作
|
||||
"""
|
||||
if not hasattr(get, 'exec_name'):
|
||||
return public.returnMsg(False, 'The parameter is incorrect!')
|
||||
conf_path = '/etc/rsyslog.conf'
|
||||
conf = public.readFile(conf_path)
|
||||
import re
|
||||
search_str = r"ftp\.\*.*\t*.*\t*.*-/var/log/pure-ftpd.log"
|
||||
search_str_two = "ftp.none"
|
||||
rep_str = '\nftp.*\t\t-/var/log/pure-ftpd.log\n'
|
||||
result = re.search(search_str, conf)
|
||||
#获取日志状态
|
||||
if get.exec_name == 'getlog':
|
||||
if result:
|
||||
return_result = 'start'
|
||||
else:
|
||||
return_result = 'stop'
|
||||
return public.returnMsg(True, return_result)
|
||||
#开启日志审计
|
||||
elif get.exec_name == 'start':
|
||||
if result:
|
||||
conf = conf.replace(search_str, rep_str)
|
||||
else:
|
||||
conf += rep_str
|
||||
#禁止ftp日志写入/var/log/messages
|
||||
|
||||
d_conf = conf[conf.rfind('info;'):]
|
||||
d_conf = d_conf[:d_conf.find('/')]
|
||||
s_conf = d_conf.replace(',', ';')
|
||||
if s_conf.find(search_str_two) == -1:
|
||||
str_index = s_conf.rfind(';')
|
||||
s_conf = s_conf[:str_index +
|
||||
1] + search_str_two + s_conf[str_index + 1:]
|
||||
conf = conf.replace(d_conf, s_conf)
|
||||
self.add_crontab()
|
||||
#关闭日志审计
|
||||
elif get.exec_name == 'stop':
|
||||
if result:
|
||||
conf = re.sub(search_str, '', conf)
|
||||
#取消禁止ftp日志写入/var/log/messages
|
||||
if conf.find(search_str_two) != -1:
|
||||
conf = conf.replace(search_str_two, '')
|
||||
for i in [';;', ',,', ';,', ',;']:
|
||||
if conf.find(i) != -1: conf = conf.replace(i, '')
|
||||
self.del_crontab()
|
||||
public.writeFile(conf_path, conf)
|
||||
public.ExecShell('systemctl restart rsyslog')
|
||||
return public.returnMsg(True, 'successfully set')
|
||||
|
||||
def get_format_time(self, englist_time):
|
||||
"""
|
||||
@name 时间英文转换
|
||||
"""
|
||||
chinanese_time = ''
|
||||
try:
|
||||
for i in month_list.keys():
|
||||
if i in englist_time:
|
||||
tmp_time = englist_time.replace(i, month_list[i])
|
||||
tmp_time = tmp_time.split()
|
||||
chinanese_time = '{}-{} {}'.format(tmp_time[0], tmp_time[1],
|
||||
tmp_time[2])
|
||||
break
|
||||
return chinanese_time
|
||||
except:
|
||||
return chinanese_time
|
||||
|
||||
def get_login_log(self, get):
|
||||
"""
|
||||
@name 取登录日志
|
||||
@author hezhihong
|
||||
@param get.user_name ftp用户名
|
||||
return
|
||||
"""
|
||||
|
||||
search_str = 'pure-ftpd:'
|
||||
search_str2 = 'pure-ftpd['
|
||||
if not hasattr(get, 'user_name'):
|
||||
return public.returnMsg(False, 'The parameter is incorrect!')
|
||||
args = public.dict_obj()
|
||||
args.exec_name = 'getlog'
|
||||
file_name = self.__ftp_backup_path
|
||||
is_backup = True
|
||||
if self.set_ftp_log(get) == 'stop':
|
||||
file_name = self.__messages_file
|
||||
is_backup = False
|
||||
file_list = self.get_file_list(file_name, is_backup)
|
||||
data = []
|
||||
sortid = 0
|
||||
tmp_dict = {}
|
||||
login_all = []
|
||||
for file in file_list:
|
||||
|
||||
if not os.path.isfile(file['file']): continue
|
||||
conf = public.readFile(file['file'])
|
||||
lines = conf.split('\n')
|
||||
for line in lines:
|
||||
if not line: continue
|
||||
login_info = {}
|
||||
if search_str not in line and search_str2 not in line:
|
||||
continue
|
||||
tmp_value = ' is now logged in'
|
||||
info = line[:line.find(search_str)].strip()
|
||||
if not info:
|
||||
info = line[:line.find(search_str2)].strip()
|
||||
hostname = info.split()[-1]
|
||||
exec_time = info.split(hostname)[0].strip()
|
||||
exec_time = self.get_format_time(exec_time)
|
||||
ip = line[line.find('(') + 1:line.find(')')].split('@')[1]
|
||||
|
||||
#取登录成功日志
|
||||
if tmp_value in line:
|
||||
user = line.split(tmp_value)[0].strip().split()[-1]
|
||||
if user == '?' or user != get.user_name: continue
|
||||
dict_index = '{}__{}'.format(user, ip)
|
||||
if dict_index not in tmp_dict:
|
||||
tmp_dict[dict_index] = []
|
||||
tmp_dict[dict_index].append(exec_time)
|
||||
|
||||
#取登出日志
|
||||
tmp_value = '[INFO] Logout.'
|
||||
tmp_value_two = 'Timeout - try typing a little faster next time'
|
||||
if tmp_value in line or tmp_value_two in line:
|
||||
user = line[line.find('(') +
|
||||
1:line.find(')')].split('@')[0]
|
||||
if user == '?' or user != get.user_name: continue
|
||||
dict_index = '{}__{}'.format(user, ip)
|
||||
try:
|
||||
login_info['out_time'] = exec_time
|
||||
login_info['in_time'] = tmp_dict[dict_index][0]
|
||||
login_info['user'] = user
|
||||
login_info['ip'] = ip
|
||||
login_info['status'] = 'Success' #0为登录失败,1为登录成功
|
||||
login_info['sortid'] = sortid
|
||||
login_all.append(login_info)
|
||||
tmp_dict[dict_index] = []
|
||||
sortid += 1
|
||||
except:
|
||||
pass
|
||||
#取登录失败日志
|
||||
tmp_value = 'Authentication failed for user'
|
||||
if tmp_value in line:
|
||||
user = line.split(tmp_value)[-1].replace('[', '').replace(
|
||||
']', '').strip()
|
||||
if user == '?' or user != get.user_name: continue
|
||||
login_info['user'] = user
|
||||
login_info['ip'] = ip
|
||||
login_info['status'] = 'Failure' #0为登录失败,1为登录成功
|
||||
login_info['in_time'] = exec_time
|
||||
login_info['out_time'] = exec_time
|
||||
login_info['sortid'] = sortid
|
||||
login_all.append(login_info)
|
||||
sortid += 1
|
||||
|
||||
if tmp_dict:
|
||||
for item in tmp_dict.keys():
|
||||
if not tmp_dict[item]: continue
|
||||
info = {
|
||||
"status": "login successful",
|
||||
"in_time": tmp_dict[item][0],
|
||||
"out_time": "connecting",
|
||||
"user": item.split('__')[0],
|
||||
"ip": item.split('__')[1],
|
||||
"sortid": sortid
|
||||
}
|
||||
sortid += 1
|
||||
login_all.append(info)
|
||||
#搜索过滤
|
||||
if login_all and 'search' in get and get.search and get.search.strip():
|
||||
for info in login_all:
|
||||
try:
|
||||
search_str = str(get.search).strip().lower()
|
||||
# public.writeFile('/tmp/aa.aa', get.search)
|
||||
if info['ip'].find(search_str) != -1 or info['user'].lower(
|
||||
).find(search_str) != -1 or info['status'].find(
|
||||
search_str) != -1 or info['in_time'].find(
|
||||
search_str) != -1:
|
||||
data.append(info)
|
||||
elif info['out_time'] and info['out_time'].find(
|
||||
search_str) != -1:
|
||||
data.append(info)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
for info2 in login_all:
|
||||
data.append(info2)
|
||||
|
||||
data = sorted(data, key=lambda x: x['sortid'], reverse=True)
|
||||
return self.get_page(data, get)
|
||||
|
||||
def get_page(self, data, get):
|
||||
"""
|
||||
@name 取分页
|
||||
@author hezhihong
|
||||
@param data 需要分页的数据 list
|
||||
@param get.p 第几页
|
||||
@return 指定分页数据
|
||||
"""
|
||||
# 包含分页类
|
||||
import page
|
||||
# 实例化分页类
|
||||
page = page.Page()
|
||||
|
||||
info = {}
|
||||
info['count'] = len(data)
|
||||
info['row'] = 10
|
||||
info['p'] = 1
|
||||
if hasattr(get, 'p'):
|
||||
info['p'] = int(get['p'])
|
||||
info['uri'] = {}
|
||||
info['return_js'] = ''
|
||||
# 获取分页数据
|
||||
result = {}
|
||||
result['page'] = page.GetPage(info, limit='1,2,3,4,5,8')
|
||||
n = 0
|
||||
result['data'] = []
|
||||
for i in range(info['count']):
|
||||
if n >= page.ROW: break
|
||||
if i < page.SHIFT: continue
|
||||
n += 1
|
||||
result['data'].append(data[i])
|
||||
return result
|
||||
|
||||
def get_action_log(self, get):
|
||||
"""
|
||||
@name 取操作日志
|
||||
@author hezhihong
|
||||
@param get.user_name ftp用户名
|
||||
return {"upload":[],"download":[],"rename":[],"delete":[]}
|
||||
"""
|
||||
search_str = 'pure-ftpd:'
|
||||
args = public.dict_obj()
|
||||
args.exec_name = 'getlog'
|
||||
file_name = self.__ftp_backup_path
|
||||
is_backup = True
|
||||
if self.set_ftp_log(get) == 'stop':
|
||||
file_name = self.__messages_file
|
||||
is_backup = False
|
||||
file_list = self.get_file_list(file_name, is_backup)
|
||||
if not hasattr(get, 'user_name'):
|
||||
return public.returnMsg(False, 'The parameter is incorrect!')
|
||||
data = []
|
||||
tmp_data = []
|
||||
sortid = 0
|
||||
for file in file_list:
|
||||
if not os.path.isfile(file['file']): continue
|
||||
conf = public.readFile(file['file'])
|
||||
lines = conf.split('\n')
|
||||
for line in lines:
|
||||
if not line: continue
|
||||
action_info = {}
|
||||
if search_str not in line: continue
|
||||
|
||||
tmp_v = line.split(search_str)
|
||||
hostname = tmp_v[0].strip().split()[3].strip()
|
||||
action_time = tmp_v[0].replace(hostname, '').strip()
|
||||
action_info['time'] = self.get_format_time(action_time)
|
||||
|
||||
upload_value = ' uploaded '
|
||||
download_value = ' downloaded '
|
||||
rename_value = 'successfully renamed or moved:'
|
||||
delete_value = ' Deleted '
|
||||
ip = line[line.find('(') + 1:line.find(')')].split('@')[1]
|
||||
action_info['ip'] = ip
|
||||
action_info['type'] = ''
|
||||
#取操作用户
|
||||
user = ''
|
||||
if upload_value in line or download_value in line or rename_value in line or delete_value in line:
|
||||
user = line[line.find('(') +
|
||||
1:line.find(')')].split('@')[0]
|
||||
action_info['sortid'] = sortid
|
||||
sortid = sortid + 1
|
||||
if not user or user != get.user_name: continue
|
||||
#取上传日志
|
||||
if (get.type == 'all'
|
||||
or get.type == 'upload') and upload_value in line:
|
||||
line_list = line.split()
|
||||
upload_index = line_list.index('uploaded')
|
||||
# action_info['file'] = line_list[upload_index - 1].replace(
|
||||
# '//', '/')
|
||||
action_info['file'] = line[line.find(']') +
|
||||
1:line.rfind('(')].replace(
|
||||
'uploaded',
|
||||
'').replace('//',
|
||||
'/').strip()
|
||||
action_info['type'] = 'upload'
|
||||
tmp_data.append(action_info)
|
||||
#取下载日志
|
||||
if (get.type == 'all'
|
||||
or get.type == 'download') and download_value in line:
|
||||
line_list = line.split()
|
||||
upload_index = line_list.index('downloaded')
|
||||
action_info['file'] = line_list[upload_index - 1].replace(
|
||||
'//', '/')
|
||||
action_info['type'] = 'download'
|
||||
tmp_data.append(action_info)
|
||||
#取重命名日志
|
||||
if (get.type == 'all'
|
||||
or get.type == 'rename') and rename_value in line:
|
||||
action_info['file'] = line.split(rename_value)[1].replace(
|
||||
'->', 'Renamed to').strip().replace('//', '/')
|
||||
action_info['type'] = 'rename'
|
||||
tmp_data.append(action_info)
|
||||
#取删除日志
|
||||
if (get.type == 'all'
|
||||
or get.type == 'delete') and delete_value in line:
|
||||
action_info['file'] = line.split()[-1].strip().replace(
|
||||
'//', '/')
|
||||
action_info['type'] = 'delete'
|
||||
tmp_data.append(action_info)
|
||||
# f.close
|
||||
#搜索过滤
|
||||
if tmp_data and 'search' in get and get.search and get.search.strip():
|
||||
for info in tmp_data:
|
||||
search_str = str(get.search).strip().lower()
|
||||
if info['ip'].find(search_str) != -1 or info['file'].lower(
|
||||
).find(search_str) != -1 or info['type'].find(
|
||||
search_str) != -1 or info['time'].find(
|
||||
search_str) != -1 or get.user_name.lower().find(
|
||||
search_str) != -1:
|
||||
data.append(info)
|
||||
else:
|
||||
for info2 in tmp_data:
|
||||
data.append(info2)
|
||||
data = sorted(data, key=lambda x: x['sortid'], reverse=True)
|
||||
return self.get_page(data, get)
|
||||
|
||||
def del_crontab(self):
|
||||
"""
|
||||
@name 删除项目定时清理任务
|
||||
@auther hezhihong<2022-10-31>
|
||||
@return
|
||||
"""
|
||||
cron_name = '[Do not delete] FTP audit log cutting task'
|
||||
cron_path = public.GetConfigValue('setup_path') + '/cron/'
|
||||
cron_list = public.M('crontab').where("name=?", (cron_name, )).select()
|
||||
if cron_list:
|
||||
for i in cron_list:
|
||||
if not i: continue
|
||||
cron_echo = public.M('crontab').where(
|
||||
"id=?", (i['id'], )).getField('echo')
|
||||
args = {"id": i['id']}
|
||||
import crontab
|
||||
crontab.crontab().DelCrontab(args)
|
||||
del_cron_file = cron_path + cron_echo
|
||||
public.ExecShell(
|
||||
"crontab -u root -l| grep -v '{}'|crontab -u root -".
|
||||
format(del_cron_file))
|
||||
|
||||
def add_crontab(self):
|
||||
"""
|
||||
@name 构造日志切割任务
|
||||
"""
|
||||
python_path = ''
|
||||
try:
|
||||
python_path = public.ExecShell('which btpython')[0].strip("\n")
|
||||
except:
|
||||
try:
|
||||
python_path = public.ExecShell('which python')[0].strip("\n")
|
||||
except:
|
||||
pass
|
||||
if not python_path: return False
|
||||
if not public.M('crontab').where('name=?',
|
||||
('[Do not delete] FTP audit log cutting task', )).count():
|
||||
cmd = '{} {}'.format(python_path, self.__script_py)
|
||||
args = {
|
||||
"name": "[Do not delete] FTP audit log cutting task",
|
||||
"type": 'day',
|
||||
"where1": '',
|
||||
"hour": '0',
|
||||
"minute": '1',
|
||||
"sName": "",
|
||||
"sType": 'toShell',
|
||||
"notice": '0',
|
||||
"notice_channel": '',
|
||||
"save": '',
|
||||
"save_local": '1',
|
||||
"backupTo": '',
|
||||
"sBody": cmd,
|
||||
"urladdress": ''
|
||||
}
|
||||
import crontab
|
||||
res = crontab.crontab().AddCrontab(args)
|
||||
if res and "id" in res.keys():
|
||||
return True
|
||||
return False
|
||||
return True
|
||||
@@ -0,0 +1,321 @@
|
||||
#coding: utf-8
|
||||
#-------------------------------------------------------------------
|
||||
# 宝塔Linux面板
|
||||
#-------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||||
#-------------------------------------------------------------------
|
||||
# Author: cjxin <bt_ahong@qq.com>
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
#------------------------------
|
||||
# 面板日志类
|
||||
#------------------------------
|
||||
|
||||
import os,re,json,time
|
||||
from logsModel.base import logsBase
|
||||
import public,db
|
||||
from html import unescape,escape
|
||||
|
||||
class main(logsBase):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
def get_logs_info(self,args):
|
||||
'''
|
||||
@name 获取分类日志信息
|
||||
'''
|
||||
data = public.M('logs').query('''
|
||||
select type,count(id) as 'count' from logs
|
||||
group by type
|
||||
order by count(id) desc
|
||||
''')
|
||||
result = []
|
||||
for arrs in data:
|
||||
item = {}
|
||||
if not arrs: continue
|
||||
|
||||
item['count'] = arrs[1]
|
||||
item['type'] = arrs[0]
|
||||
result.append(item)
|
||||
public.set_module_logs('get_logs_info','get_logs_info')
|
||||
return result
|
||||
|
||||
def get_logs_bytype(self,args):
|
||||
"""
|
||||
@name 根据类型获取日志
|
||||
@param args.type 日志类型
|
||||
"""
|
||||
p,limit = 1,20
|
||||
if 'p' in args: p = int(args.p)
|
||||
if 'limit' in args: limit = int(args.limit)
|
||||
|
||||
stype = args.stype
|
||||
search = '[' + str(args.search) + ']'
|
||||
|
||||
where = "type=? and log like ? "
|
||||
|
||||
count = public.M('logs').where(where,(stype,'%'+search+'%')).count()
|
||||
data = public.get_page(count,p,limit)
|
||||
data['data'] = public.M('logs').where(where,(stype,'%'+search+'%')).limit('{},{}'.format(data['shift'], data['row'])).order('id desc').select()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def __get_panel_dirs(self):
|
||||
'''
|
||||
@name 获取面板日志目录
|
||||
'''
|
||||
dirs = []
|
||||
for filename in os.listdir('{}/logs/request'.format(public.get_panel_path())):
|
||||
if filename.find('.json') != -1:
|
||||
dirs.append(filename)
|
||||
|
||||
dirs = sorted(dirs,reverse=True)
|
||||
return dirs
|
||||
|
||||
|
||||
|
||||
def get_panel_log(self,get):
|
||||
"""
|
||||
@name 获取面板日志
|
||||
"""
|
||||
p,limit,search = 1,20,''
|
||||
if 'p' in get: p = int(get.p)
|
||||
if 'limit' in get: limit = int(get.limit)
|
||||
if 'search' in get: search = get.search
|
||||
|
||||
find_idx = 0
|
||||
log_list = []
|
||||
dirs = self.__get_panel_dirs()
|
||||
for filename in dirs:
|
||||
log_path = '{}/logs/request/{}'.format(public.get_panel_path(),filename)
|
||||
if not os.path.exists(log_path): #文件不存在
|
||||
continue
|
||||
|
||||
if len(log_list) >= limit:
|
||||
break
|
||||
|
||||
p_num = 0 #分页计数器
|
||||
next_file = False
|
||||
while not next_file:
|
||||
if len(log_list) >= limit:
|
||||
break
|
||||
p_num += 1
|
||||
result = self.GetNumLines(log_path,10001,p_num).split('\r\n')
|
||||
if len(result) < 10000:
|
||||
next_file = True
|
||||
result.reverse()
|
||||
for _line in result:
|
||||
if not _line: continue
|
||||
if len(log_list) >= limit:
|
||||
break
|
||||
|
||||
try:
|
||||
if self.find_line_str(_line,search):
|
||||
find_idx += 1
|
||||
|
||||
if find_idx > (p-1) * limit:
|
||||
|
||||
info = json.loads(unescape(_line))
|
||||
for key in info:
|
||||
if isinstance(info[key],str):
|
||||
info[key] = escape(info[key])
|
||||
|
||||
info['address'] = info['ip'].split(':')[0]
|
||||
log_list.append(info)
|
||||
except:pass
|
||||
|
||||
return public.return_area(log_list,'address')
|
||||
|
||||
def get_panel_error_logs(self,get):
|
||||
'''
|
||||
@name 获取面板错误日志
|
||||
'''
|
||||
search = ''
|
||||
if 'search' in get:
|
||||
search = get.search
|
||||
filename = '{}/logs/error.log'.format(public.get_panel_path())
|
||||
if not os.path.exists(filename):
|
||||
return public.returnMsg(False,'No error log')
|
||||
|
||||
res = {}
|
||||
res['data'] = public.xssdecode(self.GetNumLines(filename,2000,1,search))
|
||||
res['data'].reverse()
|
||||
return res
|
||||
|
||||
|
||||
def __get_ftp_log_files(self,path):
|
||||
"""
|
||||
@name 获取FTP日志文件列表
|
||||
@param path 日志文件路径
|
||||
@return list
|
||||
"""
|
||||
file_list = []
|
||||
if os.path.exists(path):
|
||||
for filename in os.listdir(path):
|
||||
if filename.find('.log') == -1: continue
|
||||
file_list.append('{}/{}'.format(path,filename))
|
||||
|
||||
file_list = sorted(file_list,reverse=True)
|
||||
return file_list
|
||||
|
||||
def get_ftp_logs(self,get):
|
||||
"""
|
||||
@name 获取ftp日志
|
||||
"""
|
||||
|
||||
p,limit,search,username = 1,500,'',''
|
||||
if 'p' in get: p = int(get.p)
|
||||
if 'limit' in get: limit = int(get.limit)
|
||||
if 'search' in get: search = get.search
|
||||
if 'username' in get: username = get.username
|
||||
|
||||
find_idx = 0
|
||||
ip_list = []
|
||||
log_list = []
|
||||
dirs = self.__get_ftp_log_files('{}/ftpServer/Logs'.format(public.get_soft_path()))
|
||||
for log_path in dirs:
|
||||
|
||||
if not os.path.exists(log_path): continue
|
||||
if len(log_list) >= limit: break
|
||||
|
||||
p_num = 0 #分页计数器
|
||||
next_file = False
|
||||
while not next_file:
|
||||
if len(log_list) >= limit:
|
||||
break
|
||||
p_num += 1
|
||||
result = self.GetNumLines(log_path,10001,p_num).split('\r\n')
|
||||
if len(result) < 10000:
|
||||
next_file = True
|
||||
result.reverse()
|
||||
for _line in result:
|
||||
if not _line.strip(): continue
|
||||
if len(log_list) >= limit:
|
||||
break
|
||||
try:
|
||||
if self.find_line_str(_line,search):
|
||||
#根据用户名查找
|
||||
if username and not re.search('-\s+({})\s+\('.format(username),_line):
|
||||
continue
|
||||
|
||||
find_idx += 1
|
||||
if find_idx > (p-1) * limit:
|
||||
#获取ip归属地
|
||||
for _ip in public.get_line_ips(_line):
|
||||
if not _ip in ip_list: ip_list.append(_ip)
|
||||
|
||||
info = escape(_line)
|
||||
log_list.append(info)
|
||||
except:pass
|
||||
|
||||
return self.return_line_area(log_list,ip_list)
|
||||
|
||||
|
||||
#取慢日志
|
||||
def get_slow_logs(self,get):
|
||||
'''
|
||||
@name 获取慢日志
|
||||
@get.search 搜索关键字
|
||||
'''
|
||||
search,p,limit = '',1,1000
|
||||
if 'search' in get: search = get.search
|
||||
if 'limit' in get: limit = get.limit
|
||||
|
||||
my_info = public.get_mysql_info()
|
||||
if not my_info['datadir']:
|
||||
return public.returnMsg(False,'MySQL is not installed!')
|
||||
|
||||
path = my_info['datadir'] + '/mysql-slow.log'
|
||||
if not os.path.exists(path):
|
||||
return public.returnMsg(False,'Log file does not exist!')
|
||||
# mysql慢日志有顺序问题,倒序显示不利于排查问题
|
||||
return public.returnMsg(True, public.xsssec(public.GetNumLines(path, limit)))
|
||||
|
||||
# find_idx = 0
|
||||
# p_num = 0 #分页计数器
|
||||
# next_file = False
|
||||
# log_list = []
|
||||
# while not next_file:
|
||||
# if len(log_list) >= limit:
|
||||
# break
|
||||
# p_num += 1
|
||||
# result = self.GetNumLines(path,10001,p_num).replace('\r\n','\n').split('\n')
|
||||
# if len(result) < 10000:
|
||||
# next_file = True
|
||||
# result.reverse()
|
||||
|
||||
# for _line in result:
|
||||
# if not _line: continue
|
||||
# if len(log_list) >= limit:
|
||||
# break
|
||||
|
||||
# try:
|
||||
# if self.find_line_str(_line,search):
|
||||
# find_idx += 1
|
||||
# if find_idx > (p-1) * limit:
|
||||
# info = escape(_line)
|
||||
# log_list.append(info)
|
||||
# except:pass
|
||||
# return log_list
|
||||
|
||||
def IP_geolocation(self, get):
|
||||
'''
|
||||
@name 列出所有IP及其归属地
|
||||
@return list {ip: {ip: ip_address, operation_num: 12 ,info: 归属地}, ...]
|
||||
'''
|
||||
|
||||
result = dict()
|
||||
|
||||
data = public.M('logs').query('''
|
||||
select * from logs
|
||||
''')
|
||||
for arrs in data:
|
||||
if not arrs: continue
|
||||
end = 0
|
||||
# 获得IP的尾后索引
|
||||
for ch in arrs[2]:
|
||||
if ch.isnumeric() or ch == '.':
|
||||
end += 1
|
||||
else:
|
||||
break
|
||||
|
||||
ip_addr = arrs[2][0:end]
|
||||
|
||||
if ip_addr:
|
||||
if result.get(ip_addr) != None:
|
||||
result[ip_addr]["operation_num"] = result[ip_addr]["operation_num"] + 1
|
||||
else:
|
||||
result[ip_addr] = {"ip":ip_addr,"operation_num":1, "info":None}
|
||||
|
||||
return_list = []
|
||||
|
||||
for k in result:
|
||||
info = public.get_free_ip_info(k)
|
||||
result[k]["info"] = info["info"]
|
||||
return_list.append(result[k])
|
||||
|
||||
return return_list
|
||||
|
||||
def get_error_logs_by_search(self, args):
|
||||
'''
|
||||
@name 根据搜索内容, 获取运行日志中的内容
|
||||
@args.search 匹配内容
|
||||
@return 匹配该内容的所有日志
|
||||
'''
|
||||
log_file_path = "{}/logs/error.log".format(public.get_panel_path())
|
||||
#return log_file_path
|
||||
data = public.readFile(log_file_path)
|
||||
if not data:
|
||||
return None
|
||||
data = data.split('\n')
|
||||
result = []
|
||||
for line in data:
|
||||
if args.search == None:
|
||||
result.append(line)
|
||||
elif args.search in line:
|
||||
result.append(line)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,110 @@
|
||||
#coding: utf-8
|
||||
#-------------------------------------------------------------------
|
||||
# 宝塔Linux面板
|
||||
#-------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||||
#-------------------------------------------------------------------
|
||||
# Author: cjxin <bt_ahong@qq.com>
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
#------------------------------
|
||||
# 面板日志类
|
||||
#------------------------------
|
||||
|
||||
import os,re,json,time
|
||||
from logsModel.base import logsBase
|
||||
import public,db
|
||||
from html import unescape,escape
|
||||
|
||||
class main(logsBase):
|
||||
|
||||
def __init__(self):
|
||||
self.serverType = public.get_webserver()
|
||||
|
||||
|
||||
def __get_iis_log_files(self,path):
|
||||
"""
|
||||
@name 获取IIS日志文件列表
|
||||
@param path 日志文件路径
|
||||
@return list
|
||||
"""
|
||||
file_list = []
|
||||
if os.path.exists(path):
|
||||
for filename in os.listdir(path):
|
||||
if filename.find('.log') == -1: continue
|
||||
file_list.append('{}/{}'.format(path,filename))
|
||||
|
||||
file_list = sorted(file_list,reverse=False)
|
||||
return file_list
|
||||
|
||||
def get_iis_logs(self,get):
|
||||
"""
|
||||
@name 获取IIS网站日志
|
||||
"""
|
||||
|
||||
p,limit,search = 1,2000,''
|
||||
if 'p' in get: limit = int(get.p)
|
||||
if 'limit' in get: limit = int(get.limit)
|
||||
if 'search' in get: search = get.search
|
||||
|
||||
import panelSite
|
||||
site_obj = panelSite.panelSite()
|
||||
data = site_obj.get_site_info(get.siteName)
|
||||
if not data:
|
||||
return public.returnMsg(False,'【{}】网站路径获取失败,请检查IIS是否存在此站点,如IIS不存在请通过面板删除此网站后重新创建.'.format(get.siteName))
|
||||
|
||||
log_path = '{}/wwwlogs/W3SVC{}'.format(public.get_soft_path(), data['id'])
|
||||
file_list = self.__get_iis_log_files(log_path)
|
||||
|
||||
find_idx = 0
|
||||
log_list = []
|
||||
for log_path in file_list:
|
||||
if not os.path.exists(log_path): continue
|
||||
if len(log_list) >= limit: break
|
||||
|
||||
p_num = 0 #分页计数器
|
||||
next_file = False
|
||||
while not next_file:
|
||||
if len(log_list) >= limit:
|
||||
break
|
||||
p_num += 1
|
||||
result = self.GetNumLines(log_path,10001,p_num).split('\r\n')
|
||||
if len(result) < 10000:
|
||||
next_file = True
|
||||
|
||||
for _line in result:
|
||||
if not _line: continue
|
||||
if len(log_list) >= limit:
|
||||
break
|
||||
|
||||
try:
|
||||
if self.find_line_str(_line,search):
|
||||
find_idx += 1
|
||||
if find_idx > (p-1) * limit:
|
||||
info = escape(_line)
|
||||
log_list.append(info)
|
||||
except:pass
|
||||
return log_list
|
||||
|
||||
# 取网站日志
|
||||
def get_site_logs(self, get):
|
||||
logPath = ''
|
||||
if self.serverType == 'iis':
|
||||
return self.get_iis_logs(get)
|
||||
|
||||
elif self.serverType == 'apache':
|
||||
logPath = self.setupPath + '/wwwlogs/' + get.siteName + '-access.log'
|
||||
else:
|
||||
logPath = self.setupPath + '/wwwlogs/' + get.siteName + '.log'
|
||||
|
||||
data = {}
|
||||
data['path'] = ''
|
||||
data['path'] = os.path.dirname(logPath)
|
||||
if os.path.exists(logPath):
|
||||
data['status'] = True
|
||||
data['msg'] = public.GetNumLines(logPath, 1000)
|
||||
|
||||
return data
|
||||
data['status'] = False
|
||||
data['msg'] = 'log is empty'
|
||||
return data
|
||||
Reference in New Issue
Block a user