pymysql组装sql入库日志

pymysql模块的用法

采集这些指标(metirc)都是linux环境,会用到mysql,做为数据的存储,我用docker来启动

docker run  \
-p 3306:3306 \
-v /data/mysql:/var/lib/mysql \
-v /etc/localtime:/etc/localtime \
--name mysql5 \
--restart=always \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.6.23 \
--character-set-server=utf8 \
--collation-server=utf8_general_ci
create database mem;
create table mem_used(used int, time int) - 时间格式
>>> import time
>>> time.time()
1516860432.386474 - 入库后是整形,db自动处理, 也可以入库前int(time).
MySQL [mem]> select * from mem_used;
+-----------+------------+
| used | time |
+-----------+------------+
| 628658176 | 1516842082 |
| 628813824 | 1516842083 |
| 628936704 | 1516842084 |
| 628936704 | 1516842085 |
...

一个用法实例, 获取指标(通过psutil模块直接获取到已使用的内存) 也可以自己算出.

import psutil
import time
import pymysql as ms con = ms.connect(host='127.0.0.1', user='root', passwd='123456', db='mem')
con.autocommit(True)
cur = con.cursor() while True:
used = psutil.virtual_memory().used
sql = 'insert into mem_used values(%s,%s)' % (used / 1024 / 1024, int(time.time())) ## Mb
cur.execute(sql)
time.sleep(1) - %s可以接受任意类型的值

后面会将db操作封装成模块.

说下本次的任务

  • 本次的任务是将apache的access.log的(ip+状态)+出现次数, 入库到mysql里做分析. 分析网站访问的top10
  • 日志样式如下, 以空格分隔 arr[0] arr[8] 分别是ip和状态码
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
  • 通过pymysql模块操作mysql数据库

  • 将结果放在res={}一个大的字典里. 核心统计思路如下. 如果字典无key,则value初始化为1; 如果字典有key,则 以key对应的value +1

- 为何是这种格式,取决于echarts数据格式所需. 通常是找到一个合适的图,对图中数据胸有程序,即可以看到它需要一些什么数据,在看下echarts代码,看下需要什么格式的数据, 然后后端组装, json.dumps后返回.

res[(ip, status)] = res.get((ip, status), 0) + 1
  • 对字典的值排序
- 遍历日志文件,获取到一个大的字典如下
res = {('182.145.101.123', '200'): 302, ('139.205.220.131', '404'): 1, ('119.146.75.13', '304'): 10, ('59.39.103.85', '200'): 56, ... } - 对字典排序 转成 列表 后插入
print sorted(res.items(), key=lambda x: x[1], reverse=True) [(('123.174.51.164', '200'), 6930), (('117.63.146.40', '200'), 1457), (('118.112.143.148', '404'), 1336), (('111.85.34.165', '200'), 1325), ...] - 对排序后的列表(字典),字符串拼接sql,并插入数据库
for i in sorted(res.items(), key=iambda x: x[1], reverse=True):
sql = "insert into iog vaiues ('%s','%s','%s')" % (i[0][0], i[0][1], i[1])
db.execute(sql)

代码组织

入库代码

log2db.py

#!/usr/bin/env python
# coding=utf-8 from dbutils import DB # 连接mysql
db = DB(
host="127.0.0.1",
user="root",
passwd="",
db="logtest"
) # 日志处理成一个大的指定格式的字典(已统计好出现的次数) ((ip, status), count)
res = {}
with open('log.txt') as f:
for line in f:
if line == "\n":
continue
arr = line.split(" ")
ip = arr[0]
status = arr[8]
res[(ip, status)] = res.get((ip, status), 0) + 1 # 组合sql,执行sql入库日志
for l in sorted(res.items(), key=lambda x: x[1], reverse=True):
# {('192.168.1.1',404): 1000,('192.168.1.1',403): 3000,('192.168.1.1',200): 2000,}
sql = "insert into log values ('%s','%s','%s')" % (l[0][0], l[0][1], l[1])
db.execute(sql)

dbutils.py

#!/usr/bin/env python
# coding=utf-8 import pymysql as ms class DB:
def __init__(self, host, user, passwd, db):
self.host = host
self.user = user
self.passwd = passwd
self.db = db
self.connect() def connect(self):
self.conn = ms.connect(
host=self.host,
user=self.user,
passwd=self.passwd,
db=self.db
)
self.conn.autocommit(True)
self.cursor = self.conn.cursor() def execute(self, sql):
try:
self.cursor.execute(sql)
except Exception as e:
self.cursor.close()
self.conn.close()
self.connect()
return self.execute(sql)
else:
return self.cursor # def query(self,tables):
# sql = 'select * from users'
# self.cursor.execute(sql)
# return self.cursor.fetchall()

下载实例日志: https://github.com/lannyMa/flask_info/blob/master/demo5/log.txt

执行脚本入库

python log2db.py

结果查看

入库完成后, flask前端展示+echarts

将入库的日志通过flask前端展示

难点是找图形的数据模型,即json数据格式. 后端返回. 所以当一幅图出来后,要对其上面有啥数据,啥数据是后端返回的,要胸有成竹, 通过代码+浏览器f12conson.log打印出来.

echarts官网上去看最简单实例的教程,用flask展示出来

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts</title>
<!-- 引入 echarts.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/4.0.2/echarts.min.js"></script>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main')); // 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
}; // 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>

echarts.min.js路径替换下

flask启动展示

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
return render_template('demo.html') if __name__ == '__main__':
app.run(debug=True)

先找一个合适的echarts图,观察它所需的接口数据格式,后设计接口

计划使用echarts这个饼图展示

它的源码如下

option = {
title : {
text: '某站点用户访问来源',
subtext: '纯属虚构',
x:'center'
},
tooltip : {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
orient: 'vertical',
left: 'left',
data: ['直接访问','邮件营销','联盟广告','视频广告','搜索引擎']
},
series : [
{
name: '访问来源',
type: 'pie',
radius : '55%',
center: ['50%', '60%'],
data:[
{value:335, name:'直接访问'},
{value:310, name:'邮件营销'},
{value:234, name:'联盟广告'},
{value:135, name:'视频广告'},
{value:1548, name:'搜索引擎'}
],
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};

下载后,修改echarts-pie.html源码

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts</title> <!-- 引入 echarts.js -->
<script src="/static/jquery.min.js"></script>
<script src="/static/echarts.min.js"></script>
</head>
<body> <!-- 为ECharts准备一个具备大小(宽高)的Dom画布 -->
<div id="main" style="width: 600px;height:400px;"></div> <script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main')); // 指定图表的配置项和数据
var option = {
title: {
text: '某站点用户访问来源',
{# subtext: '纯属虚构',#}
x: 'center'
},
tooltip: {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
toolbox: {
feature: {
saveAsImage: {
show: true
}
}
},
legend: {
orient: 'vertical',
left: 'left',
{# data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎']#}
},
series: [
{
name: '访问来源',
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
{# data: [#}
{# {value: 335, name: '直接访问'},#}
{# {value: 310, name: '邮件营销'},#}
{# {value: 234, name: '联盟广告'},#}
{# {value: 135, name: '视频广告'},#}
{# {value: 1548, name: '搜索引擎'}#}
{# ],#}
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}; //使用jq去访问api获取得到数据.
$.getJSON('/piedata', function (res) {
option.legend.data = res.legend
option.series[0].data = res.data // 使用刚指定的配置项和数据显示图表-绑定数据
myChart.setOption(option);
})
</script>
</body>
</html>

我们知道了前端js需要的数据所需要的数据格式后,就从后端构造这样的api,供前端调用

- api需返回数据的格式
{"data": [{"name": 200, "value": 49691}, {"name": 206, "value": 32}, {"name": 301, "value": 2}, {"name": 304, "value": 7584}, {"name": 403, "value": 1}, {"name": 404, "value": 3858}], "legend": [200, 206, 301, 304, 403, 404]} - js访问接口方法
$.getJSON('/piedata', function (res) {
option.legend.data = res.legend
option.series[0].data = res.data
myChart.setOption(option); //获取后直接绑定
})

设计top10状态码的api

  • 目标是

即访问
http://127.0.0.1:5000/piedata 返回json数据: {"data": [{"name": 200, "value": 49691}, {"name": 206, "value": 32}, {"name": 301, "value": 2}, {"name": 304, "value": 7584}, {"name": 403, "value": 1}, {"name": 404, "value": 3858}], "legend": [200, 206, 301, 304, 403, 404]}
  • 查库后得到的结果
- 用sql语句做数据汇聚,查出status 和 对应的总数, 按照状态码总数来排序
select status,sum(count) from log group by status;

查询结果如下

  • flask写api

- cur.fetchall返回的结果,需对其遍历,重组一定格式的数据
((200, Decimal('49691')), (206, Decimal('32')), (301, Decimal('2')), (304, Decimal('7584')), (403, Decimal('1')), (404, Decimal('3858'))) @app.route("/piedata")
def piedata():
sql = "select status,sum(count) from log group by status";
cur = db.execute(sql)
# 构造前端所需的数据结构
res = {
'legend': [],
'data': []
}
for c in cur.fetchall():
code = c[0]
count = int(c[1])
res['legend'].append(code)
res['data'].append({
'name': code,
'value': count
})
print(res)
return json.dumps(res) - 重组后数据格式如下
{'data': [{'name': 200, 'value': 49691}, {'name': 206, 'value': 32}, {'name': 301, 'value': 2}, {'name': 304, 'value': 7584}, {'name': 403, 'value': 1}, {'name': 404, 'value': 3858}], 'legend': [200, 206, 301, 304, 403, 404]} - 通过json模块处理后返回给将这批数据返回给前端

最终访问

实现前端html展示

@app.route("/")
def index():
return render_template("echarts-pie.html")

最终效果:

小结

[py]access日志入mysql-通过flask前端展示的更多相关文章

  1. 日志分析平台ELK之前端展示kibana

    之前的博客一直在聊ELK集群中的存储.日志收集相关的组件的配置,但通常我们给用户使用不应该是一个黑黑的shell界面,通过接口去查询搜索:今天我们来了ELK中的前端可视化组件kibana:kibana ...

  2. ELK之收集日志到mysql数据库

    写入数据库的目的是持久化保存重要数据,比如状态码.客户端浏览器版本等,用于后期按月做数据统计等. 环境准备 linux-elk1:10.0.0.22,Kibana ES Logstash Nginx ...

  3. 实时统计每天pv,uv的sparkStreaming结合redis结果存入mysql供前端展示

    最近有个需求,实时统计pv,uv,结果按照date,hour,pv,uv来展示,按天统计,第二天重新统计,当然了实际还需要按照类型字段分类统计pv,uv,比如按照date,hour,pv,uv,typ ...

  4. elk收集分析nginx access日志

    elk收集分析nginx access日志 首先elk的搭建按照这篇文章使用elk+redis搭建nginx日志分析平台说的,使用redis的push和pop做队列,然后有个logstash_inde ...

  5. legend2---开发日志6(后端和前端如何相互配合(比如php,js,元素状态和数据改变))

    legend2---开发日志6(后端和前端如何相互配合(比如php,js,元素状态和数据改变)) 一.总结 一句话总结:php给元素初始状态,js根据这个状态做初始化和后续变化,使用vue真的很方便( ...

  6. spring boot(13)-logback和access日志

    logback logback出自log4j的作者,性能和功能相比log4j作出了一些改进,而配置方法和log4j类似,是spring boot的默认日志组件.在application.propert ...

  7. nginx access 日志位置

    nginx access 日志位置 /var/log/nginx tail -f access.log

  8. Spring Boot (16) logback和access日志

    Spring Boot 内部采用的是Commons Logging进行日志记录,但是在底层为Java Util Logging.Log4J2.Logback等日志框架提供了默认配置. logback ...

  9. [PHP] PHP-FPM的access日志error日志和slow日志

    PHP-FPM的错误日志建议打开,这样可以看到PHP的错误信息:一般是这个配置路径 /etc/php/7.3/fpm/pool.d/www.conf,日志目录如果需要自己建立PHP目录,一定要把权限赋 ...

随机推荐

  1. 用图形数据库Neo4j 设计权限模块

    已经 SpringSecurity 在几个项目中 实现权限模块,对于数据库,也是思考了不少,从Mysql 到 mongodb 都不是特别满意, 在Mysql中,如果权限相对简单,那么还能接受,如果稍微 ...

  2. 【github】添加 ssh 秘钥

    1 生成秘钥 打开shell 备注: 123@example.com 为邮箱地址 ssh-keygen -t rsa -C "123@example.com" 此处选Y ,其他都是 ...

  3. 【Python3】 PyV8的安装与使用

    centos7  python3.6.4 1 安装boost  :  PyV8 依赖于Boost yum install boost yum install boost-devel yum insta ...

  4. java高级---->Thread之单例模式的使用

    这里我们介绍一下在多线程中如何安全正确的编写单例模式的代码.不知为何,恰如其分的话总是姗姗来迟,错过最恰当的时机. 多线程中的单例模式 这里面通过代码来体会一下在多线程中如何正确的编写单例模式的代码. ...

  5. JVM常用工具使用之jmap

    一.参考链接 https://www.cnblogs.com/yjd_hycf_space/p/7753847.html 二.个人的总结 一般我习惯使用 jmap -dump:live,format= ...

  6. 数据导入报错:Got a packet bigger than‘max_allowed_packet’bytes的问题

    数据导入报错:Got a packet bigger than‘max_allowed_packet’bytes的问题 2个解决方法: 1.临时修改:mysql>set global max_a ...

  7. ASP.NET MVC View使用Conditional compilation symbols

    由于View(.cshtml)的运行时编译关系,在项目级别中定义的symbols是无法被直接使用的.需要在Web.config中添加compilerOptions(在View目录下的Web.confi ...

  8. cadence allegro 封装焊盘编号修改 (引脚编号修改)

    1. 打开dra文件在find里面 off all  然后只点击text 2.点击需要更改的焊盘 3.菜单栏edit - text 4.弹出窗口修改即可 注意: 按照网上的其他操作并没有执行步骤1操作 ...

  9. vue--非父子组件之间的传值

    一个项目都有一个根组件 App.vue 一个根组件下面可能会有多个自组件,例如:Hello.vue 和 Header.vue Hello.vue 和 Header.vue 就是兄弟组件,那么这两个兄弟 ...

  10. html 自动弹出框

    1.点击div外部隐藏, //*代表tip_box所包含的子元素 $('body').click(function(e) { var target = $(e.target); if(!target. ...