网站统计 x11

pull/139/head
midoks 3 years ago
parent 1f1ac45719
commit a9bf908e0f
  1. 2
      class/core/db.py
  2. 50
      plugins/webstats/index.html
  3. 95
      plugins/webstats/index.py
  4. 355
      plugins/webstats/js/stats.js

@ -128,7 +128,7 @@ class Sql():
try: try:
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
# print(sql) # print(sql, self.__OPT_PARAM)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
data = result.fetchall() data = result.fetchall()
# 构造字曲系列 # 构造字曲系列

@ -36,6 +36,55 @@
border-radius: 0px; border-radius: 0px;
} }
.overview_list{
background-color: #FAFAFA;
border: #ddd 1px solid;
width: 100%;
font-size: 0;
color: #78797D;
}
.overview_list .overview_box{
width: 14.2%;
display: inline-block;
font-size: 12px;
padding: 15px 0 15px 15px;
vertical-align: top;
}
.overview_list .ov_num{
font-size: 20px;
margin-top: 7px;
color: #333;
}
.indicators {
margin-top: 15px;
position: relative;
}
.indicators-container {
padding: 5px;
}
.indicators-container>span {
color: #787a7d;
margin-right: 10px;
}
.indicators-label {
display: inline-block;
width: 95px;
height: 26px;
line-height: 26px;
cursor: pointer;
}
.indicators-label input {
height: 13px;
margin: 0 5px 0 0;
vertical-align: -2px;
}
.ws_tips { .ws_tips {
color: #777; color: #777;
margin-left: -10px; margin-left: -10px;
@ -130,6 +179,7 @@
<div class="bt-w-menu"> <div class="bt-w-menu">
<p class="bgw" onclick="pluginService('webstats');">服务</p> <p class="bgw" onclick="pluginService('webstats');">服务</p>
<p onclick="wsOverview();">概览</p> <p onclick="wsOverview();">概览</p>
<p onclick="wsSitesList();">网站列表</p>
<p onclick="wsSpiderStat();">蜘蛛统计</p> <p onclick="wsSpiderStat();">蜘蛛统计</p>
<p onclick="wsClientStat();">客服端统计</p> <p onclick="wsClientStat();">客服端统计</p>
<p onclick="wsSitesErrorLog();">错误日志</p> <p onclick="wsSitesErrorLog();">错误日志</p>

@ -313,6 +313,78 @@ def setDefaultSite(name):
return mw.returnJson(True, 'OK') return mw.returnJson(True, 'OK')
def toSumField(sql):
l = sql.split(",")
field = ""
for x in l:
field += "sum(" + x + ") as " + x + ","
field = field.strip(',')
return field
def getOverviewList():
args = getArgs()
check = checkArgs(args, ['site', 'query_date', 'order'])
if not check[0]:
return check[1]
domain = args['site']
query_date = args['query_date']
order = args['order']
setDefaultSite(domain)
conn = pSqliteDb('request_stat', domain)
conn = conn.where("1=1", ())
field = 'time,req,pv,uv,ip,length'
field_sum = toSumField(field.replace("time,", ""))
time_field = "substr(time,1,8),"
if order == "hour":
time_field = "substr(time,9,10),"
field_sum = time_field + field_sum
conn = conn.field(field_sum)
if query_date == "today":
todayTime = time.strftime(
'%Y%m%d00', time.localtime(time.time() - 0 * 86400))
conn.andWhere("time >= ?", (todayTime,))
elif query_date == "yesterday":
startTime = time.strftime(
'%Y%m%d00', time.localtime(time.time() - 1 * 86400))
endTime = time.strftime(
'%Y%m%d00', time.localtime(time.time()))
conn.andWhere("time>=? and time<=?", (startTime, endTime))
elif query_date == "l7":
todayTime = time.strftime(
'%Y%m%d00', time.localtime(time.time() - 7 * 86400))
conn.andWhere("time >= ?", (todayTime,))
elif query_date == "l30":
todayTime = time.strftime(
'%Y%m%d00', time.localtime(time.time() - 30 * 86400))
conn.andWhere("time >= ?", (todayTime,))
else:
exlist = query_date.split("-")
start = time.strftime(
'%Y%m%d00', time.localtime(int(exlist[0])))
end = time.strftime(
'%Y%m%d23', time.localtime(int(exlist[1])))
conn.andWhere("time >= ? and time <= ? ", (start, end,))
# 统计总数
stat_list = conn.inquiry(field)
del(stat_list[0]['time'])
# 分组统计
dlist = conn.group(time_field.strip(",")).inquiry(field)
data = {}
data['data'] = dlist
data['stat_list'] = stat_list[0]
return mw.returnJson(True, 'ok', data)
def getLogsList(): def getLogsList():
args = getArgs() args = getArgs()
check = checkArgs(args, ['page', 'page_size', check = checkArgs(args, ['page', 'page_size',
@ -452,15 +524,6 @@ def getLogsErrorList():
return mw.returnJson(True, 'ok', data) return mw.returnJson(True, 'ok', data)
def toSumField(sql):
l = sql.split(",")
field = ""
for x in l:
field += "sum(" + x + ") as " + x + ","
field = field.strip(',')
return field
def getClientStatList(): def getClientStatList():
args = getArgs() args = getArgs()
check = checkArgs(args, ['page', 'page_size', check = checkArgs(args, ['page', 'page_size',
@ -491,9 +554,11 @@ def getClientStatList():
'%Y%m%d00', time.localtime(time.time() - 0 * 86400)) '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
stat.where("time >= ?", (todayTime,)) stat.where("time >= ?", (todayTime,))
elif query_date == "yesterday": elif query_date == "yesterday":
todayTime = time.strftime( startTime = time.strftime(
'%Y%m%d00', time.localtime(time.time() - 1 * 86400)) '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
stat.where("time >= ?", (todayTime,)) endTime = time.strftime(
'%Y%m%d00', time.localtime(time.time()))
stat.where("time>=? and time<=?", (startTime, endTime))
elif query_date == "l7": elif query_date == "l7":
todayTime = time.strftime( todayTime = time.strftime(
'%Y%m%d00', time.localtime(time.time() - 7 * 86400)) '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
@ -632,9 +697,11 @@ def getSpiderStatList():
'%Y%m%d00', time.localtime(time.time() - 0 * 86400)) '%Y%m%d00', time.localtime(time.time() - 0 * 86400))
stat.where("time >= ?", (todayTime,)) stat.where("time >= ?", (todayTime,))
elif query_date == "yesterday": elif query_date == "yesterday":
todayTime = time.strftime( startTime = time.strftime(
'%Y%m%d00', time.localtime(time.time() - 1 * 86400)) '%Y%m%d00', time.localtime(time.time() - 1 * 86400))
stat.where("time >= ?", (todayTime,)) endTime = time.strftime(
'%Y%m%d00', time.localtime(time.time()))
stat.where("time>=? and time<=?", (startTime, endTime))
elif query_date == "l7": elif query_date == "l7":
todayTime = time.strftime( todayTime = time.strftime(
'%Y%m%d00', time.localtime(time.time() - 7 * 86400)) '%Y%m%d00', time.localtime(time.time() - 7 * 86400))
@ -719,6 +786,8 @@ if __name__ == "__main__":
print(setGlobalConf()) print(setGlobalConf())
elif func == 'get_default_site': elif func == 'get_default_site':
print(getDefaultSite()) print(getDefaultSite())
elif func == 'get_overview_list':
print(getOverviewList())
elif func == 'get_logs_list': elif func == 'get_logs_list':
print(getLogsList()) print(getLogsList())
elif func == 'get_logs_error_list': elif func == 'get_logs_error_list':

@ -64,105 +64,308 @@ function wsPostCallbak(method, version, args,callback){
} }
function wsOverview(){ function wsOverviewRequest(page){
var args = {}; var args = {};
args['page'] = 1;
args['page_size'] = 10; args['site'] = $('select[name="site"]').val();
args['site'] = 'unset';
args['tojs'] = 'wsOverview'; var query_date = 'today';
wsPost('get_logs_list', '' ,args, function(rdata){ if ($('#time_choose').attr("data-name") != ''){
query_date = $('#time_choose').attr("data-name");
} else {
query_date = $('#search_time button.cur').attr("data-name");
}
args['query_date'] = query_date;
args['order'] = $('#time_order button.cur').attr('data-name');
var select_option = $('.indicators-container input:checked').parent().attr('data-name');
console.log(select_option);
wsPost('get_overview_list', '' ,args, function(rdata){
var rdata = $.parseJSON(rdata.data); var rdata = $.parseJSON(rdata.data);
console.log(rdata);
var list = ''; var list = '';
var data = rdata.data.data; var data = rdata.data.data;
for(i in data){ var statData = rdata.data.stat_list;
list += '<tr>';
list += '<td>' + data[i]['time']+'</td>'; console.log(statData, data);
list += '<td>' + data[i]['domain'] +'</td>';
list += '<td>' + data[i]['ip'] +'</td>'; $('.overview_list .overview_box:eq(0) .ov_num').text(statData['pv']);
list += '<td>' + data[i]['body_length'] +'</td>'; $('.overview_list .overview_box:eq(1) .ov_num').text(statData['uv']);
list += '<td>' + data[i]['request_time'] +'ms</td>'; $('.overview_list .overview_box:eq(2) .ov_num').text(statData['ip']);
list += '<td>' + data[i]['uri'] +'</td>'; $('.overview_list .overview_box:eq(3) .ov_num').text(toSize(statData['length']));
list += '<td>' + data[i]['status_code']+'/' + data[i]['method'] +'</td>'; $('.overview_list .overview_box:eq(4) .ov_num').text(statData['req']);
list += '<td><a href="javascript:;" class="btlink" onclick="openPhpmyadmin()" title="详情">详情</a></td>';
list += '</tr>'; var list = [];
for (var i = 0; i < data.length; i++) {
list.push(data[i][select_option]);
} }
var table = '<div class="divtable mtb10">\
<div class="tablescroll">\ console.log("list",list);
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
<thead><tr>\ var chat = {};
<th>时间</th>\
<th>域名</th>\
<th>IP</th>\ var chatSeriesVal = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14','15', '16', '17', '18', '19', '20', '21', '22', '23'];
<th>响应</th>\ chat['seriesData'] = {
<th>耗时</th>\ data : chatSeriesVal,
<th>URL</th>\ type: 'line',
<th>状态/类型</th>\ areaStyle: {}
<th style="text-align:right;">操作</th></tr></thead>\ }
<tbody>\
'+ list +'\
</tbody></table>\
</div>\ var statEc = echarts.init(document.getElementById('total_num_echart'));
<div id="wsPage" class="dataTables_paginate paging_bootstrap page"></div>\ var option = {
</div>'; backgroundColor:'#fff',
legend:{
data:chat['xAxisData'],
left:'center',
top:'94%',
},
grid: {
bottom: '9%',
containLabel: true,
x: 20,
y: 20,
x2: 20,
y2: 20
},
xAxis:{
type: 'category',
boundaryGap: false,
axisTick: {
alignWithLabel: true
},
axisLabel: {
interval: 1,
},
data: [],
},
yAxis: [],
graphic:[{
type: 'group',
right: 420,
top: 50,
z: 100,
children: [{
type: 'text',
left: 'center',
top: 'center',
z: 100,
style: {
fill: '#ccc',
text: args['site'],
font: '16px Arial'
}
}]
}],
series:chat['seriesData'],
}
statEc.setOption(option);
});
}
function wsOverview(){
////////////////////////////////////////////////////////////////////////////////////////////////////////
var randstr = getRandomString(10);
var html = '<div>\ var html = '<div>\
<div style="padding-bottom:10px;">\ <div style="padding-bottom:10px;">\
<span>网站: </span>\ <span>网站: </span>\
<select class="bt-input-text" name="" style="margin-left:4px">\ <select class="bt-input-text" name="site" style="margin-left:4px;width:100px;">\
<option value="0">请选择</option>\ <option value="unset">未设置</option>\
<option value="1">1-2GB</option>\
</select>\ </select>\
<span style="margin-left:10px">时间: </span>\ <span style="margin-left:10px">时间: </span>\
<div class="input-group" style="width:510px;float:right;">\ <div class="input-group" style="margin-left:10px;width:300px;display: inline-table;vertical-align: top;">\
<div class="input-group-btn btn-group-sm">\ <div id="search_time" class="input-group-btn btn-group-sm">\
<button type="button" class="btn btn-default gt">今日</button>\ <button data-name="today" type="button" class="btn btn-default">今日</button>\
<button type="button" class="btn btn-default gt">昨日</button>\ <button data-name="yesterday" type="button" class="btn btn-default">昨日</button>\
<button type="button" class="btn btn-default gt">近7天</button>\ <button data-name="l7" type="button" class="btn btn-default">近7天</button>\
<button type="button" class="btn btn-default gt">近30天</button>\ <button data-name="l30" type="button" class="btn btn-default">近30天</button>\
</div>\ </div>\
<input type="text" class="form-control btn-group-sm" autocomplete="off" placeholder="自定义时间" style="font-size: 12px;padding: 0 10px;height:30px;width: 150px; background-position: 10px center;">\ <span class="last-span"><input data-name="" type="text" id="time_choose" lay-key="1000001_'+randstr+'" class="form-control btn-group-sm" autocomplete="off" placeholder="自定义时间" style="display: inline-block;font-size: 12px;padding: 0 10px;height:30px;width: 155px;"></span>\
</div>\ </div>\
<span style="margin-left:10px">时间: </span>\
<div class="input-group" style="width:100px;margin-left:10px;display: inline-table;vertical-align: top;">\
<div id="time_order" class="input-group-btn btn-group-sm">\
<button data-name="hour" type="button" class="btn btn-default">按时</button>\
<button data-name="day" type="button" class="btn btn-default">按天</button>\
</div>\ </div>\
<div style="padding-bottom:10px;">\ </div>\
<span>请求类型: </span>\ <div class="input-group" style="width:30px;margin-left:10px;display: inline-table;vertical-align: top;">\
<select class="bt-input-text" name="req_type" style="margin-left:4px">\
<option value="0">所有</option>\
<option value="GET">GET</option>\
<option value="POST">POST</option>\
<option value="HEAD">HEAD</option>\
<option value="PUT">PUT</option>\
<option value="DELETE">DELETE</option>\
</select>\
<span style="margin-left:10px;">状态码: </span>\
<select class="bt-input-text" name="code_type" style="margin-left:4px">\
<option value="0">所有</option>\
<option value="500">500</option>\
<option value="502">502</option>\
<option value="503">503</option>\
<option value="404">404</option>\
<option value="200">200</option>\
</select>\
<span style="margin-left:10px;">蜘蛛过滤: </span>\
<select class="bt-input-text" name="spider_type" style="margin-left:4px">\
<option value="0">不过滤</option>\
<option value="baidu">百度</option>\
</select>\
<span style="margin-left:10px;">URL过滤: </span>\
<div class="input-group" style="width:210px;float:right;">\
<input type="text" class="form-control btn-group-sm" autocomplete="off" placeholder="URI搜索" style="font-size: 12px;padding: 0 10px;height:30px;">\
<div class="input-group-btn btn-group-sm">\ <div class="input-group-btn btn-group-sm">\
<button type="button" class="btn btn-default">搜索</button>\ <button id="ov_refresh" data-name="refresh" type="button" class="btn btn-default">刷新</button>\
</div>\
</div>\
</div>\
<!-- stat --->\
<div class="overview_list" style="padding-top:10px;">\
<div class="overview_box">\
<p class="ov_title">浏览量(PV)<i class="tips" data-toggle="tooltip" data-placement="top" title="用户每次打开网站页面被记录1次。用户多次打开同一页面,访问量值累计多次。此指标衡量网站访问量情况。">?</i></p>\
<p class="ov_num">0</p>\
</div>\
<div class="overview_box">\
<p class="ov_title">访客量(UV)<i class="tips" data-toggle="tooltip" data-placement="top" title="访问您网站的上网电脑数量(以cookie为依据),此指标衡量独立访客数量情况。">?</i></p>\
<p class="ov_num">0</p>\
</div>\
<div class="overview_box">\
<p class="ov_title">IP数<i class="tips" data-toggle="tooltip" data-placement="top" title="当前时间段内您网站的独立访问ip数。">?</i></p>\
<p class="ov_num">0</p>\
</div>\
<div class="overview_box">\
<p class="ov_title">流量<i class="tips" data-toggle="tooltip" data-placement="top" title="当前时间段内您网站的总响应流量大小。包括已排除的请求。">?</i></p>\
<p class="ov_num">0</p>\
</div>\
<div class="overview_box">\
<p class="ov_title">请求<i class="tips" data-toggle="tooltip" data-placement="top" title="当前时间段内您网站的总请求数量。包括已排除的请求。">?</i></p>\
<p class="ov_num">0</p>\
</div>\
<div class="overview_box">\
<p class="ov_title">实时流量<i class="tips" data-toggle="tooltip" data-placement="top" title="当前10秒内您网站的实时流量大小。包括已排除的请求。">?</i></p>\
<p class="ov_num">0</p>\
</div>\
<div class="overview_box">\
<p class="ov_title">每秒请求<i class="tips" data-toggle="tooltip" data-placement="top" title="当前10秒内您网站的实时请求数量。包括已排除的请求。">?</i></p>\
<p class="ov_num">0</p>\
</div>\ </div>\
</div>\ </div>\
<div class="indicators">\
<div class="indicators-container">\
<span>趋势指标: </span>\
<div class="indicators-label" bt-event-click="indicatorsType" data-name="pv">\
<input type="radio" id="check_pv" name="check_pv" checked="">\
<span class="check_pv" style="font-weight:normal">浏览量(PV)</span>\
</div>\ </div>\
'+table+'\ <div class="indicators-label" bt-event-click="indicatorsType" data-name="uv">\
<input type="radio" id="check_uv" name="check_uv">\
<span class="check_uv" style="font-weight:normal">访客量(UV)</span>\
</div>\
<div class="indicators-label" bt-event-click="indicatorsType" data-name="ip">\
<input type="radio" id="check_ip" name="check_ip">\
<span class="check_ip" style="font-weight:normal">IP数</span>\
</div>\
<div class="indicators-label" bt-event-click="indicatorsType" data-name="length">\
<input type="radio" id="check_length" name="check_length">\
<span class="check_length" style="font-weight:normal">流量</span>\
</div>\
<div class="indicators-label" bt-event-click="indicatorsType" data-name="req">\
<input type="radio" id="check_req" name="check_req">\
<span class="check_req" style="font-weight:normal">请求</span>\
</div>\
<div class="indicators-label" bt-event-click="indicatorsType" data-name="realtime_traffic">\
<input type="radio" id="check_realtime_traffic" name="check_realtime_traffic"> \
<span class="check_realtime_traffic" style="font-weight:normal">实时流量</span>\
</div>\
<div class="indicators-label" bt-event-click="indicatorsType" data-name="realtime_request">\
<input type="radio" id="check_realtime_request" name="check_realtime_request">\
<span class="check_realtime_request" style="font-weight:normal">每秒请求</span>\
</div>\
</div>\
</div>\
<div class="total_num_echart" id="total_num_echart" style="height:330px;"></div>\
</div>'; </div>';
$(".soft-man-con").html(html); $(".soft-man-con").html(html);
$('#wsPage').html(rdata.data.page); $('[data-toggle="tooltip"]').tooltip();
//日期范围
laydate.render({
elem: '#time_choose',
value:'',
range:true,
done:function(value, startDate, endDate){
if(!value){
return false;
}
$('#search_time button').each(function(){
$(this).removeClass('cur');
});
var timeA = value.split('-')
var start = $.trim(timeA[0]+'-'+timeA[1]+'-'+timeA[2])
var end = $.trim(timeA[3]+'-'+timeA[4]+'-'+timeA[5])
query_txt = toUnixTime(start + " 00:00:00") + "-"+ toUnixTime(end + " 00:00:00")
$('#time_choose').attr("data-name",query_txt);
$('#time_choose').addClass("cur");
wsOverviewRequest(1);
},
});
$('#ov_refresh').click(function(){
wsOverviewRequest(1);
});
$('#time_order button:eq(0)').addClass('cur');
$('#time_order button').click(function(){
$('#time_order button').each(function(){
if ($(this).hasClass('cur')){
$(this).removeClass('cur');
}
});
$(this).addClass('cur');
wsOverviewRequest(1);
});
$('#search_time button:eq(0)').addClass('cur');
$('#search_time button').click(function(){
$('#search_time button').each(function(){
if ($(this).hasClass('cur')){
$(this).removeClass('cur');
}
});
$('#time_choose').attr("data-name",'');
$('#time_choose').removeClass("cur");
$(this).addClass('cur');
wsOverviewRequest(1);
});
$('.indicators-container input').click(function(){
$('.indicators-container input').each(function(){
$(this).removeAttr('checked');
});
$(this).prop({'checked':true});
wsOverviewRequest(1);
}); });
wsPost('get_default_site','',{},function(rdata){
$('select[name="site"]').html('');
var rdata = $.parseJSON(rdata.data);
var rdata = rdata.data;
var default_site = rdata["default"];
var select = '';
for (var i = 0; i < rdata["list"].length; i++) {
if (default_site == rdata["list"][i]){
select += '<option value="'+rdata["list"][i]+'" selected>'+rdata["list"][i]+'</option>';
} else{
select += '<option value="'+rdata["list"][i]+'">'+rdata["list"][i]+'</option>';
} }
}
$('select[name="site"]').html(select);
wsOverviewRequest(1);
$('select[name="site"]').change(function(){
wsOverviewRequest(1);
});
});
////////////////////////////////////////////////////////////////////////////////////////////////////////
}
function wsSitesList(){
}
function wsSpiderStatLogRequest(page){ function wsSpiderStatLogRequest(page){

Loading…
Cancel
Save