路由支持正则、编码、增删改操作、增加log日志
mini_frame.py
import re
# url编码相关
import urllib.parse
import logging
from pymysql import connect
"""
URL_FUNC_DICT = {
"/index.html": index,
"/center.html": center
}
"""
URL_FUNC_DICT = dict()
def route(url):
def set_func(func):
# URL_FUNC_DICT["/index.py"] = index
URL_FUNC_DICT[url] = func
def call_func(*args, **kwargs):
return func(*args, **kwargs)
return call_func
return set_func
@route(r"/index.html")
def index(ret):
with open("./templates/index.html") as f:
content = f.read()
# my_stock_info = "哈哈哈哈 这是你的本月名称....."
# content = re.sub(r"\{%content%\}", my_stock_info, content)
# 创建Connection连接
conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
# 获得Cursor对象
cs = conn.cursor()
cs.execute("select * from info;")
stock_infos = cs.fetchall()
cs.close()
conn.close()
tr_template = """<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>
<input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="%s">
</td>
</tr>
"""
html = ""
for line_info in stock_infos:
html += tr_template % (line_info[0],line_info[1],line_info[2],line_info[3],line_info[4],line_info[5],line_info[6],line_info[7], line_info[1])
# content = re.sub(r"\{%content%\}", str(stock_infos), content)
content = re.sub(r"\{%content%\}", html, content)
return content
@route(r"/center.html")
def center(ret):
with open("./templates/center.html") as f:
content = f.read()
# my_stock_info = "这里是从mysql查询出来的数据。。。"
# content = re.sub(r"\{%content%\}", my_stock_info, content)
# 创建Connection连接
conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
# 获得Cursor对象
cs = conn.cursor()
cs.execute("select i.code,i.short,i.chg,i.turnover,i.price,i.highs,f.note_info from info as i inner join focus as f on i.id=f.info_id;")
stock_infos = cs.fetchall()
cs.close()
conn.close()
tr_template = """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>
<a type="button" class="btn btn-default btn-xs" href="/update/%s.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a>
</td>
<td>
<input type="button" value="删除" id="toDel" name="toDel" systemidvaule="%s">
</td>
</tr>
"""
html = ""
for line_info in stock_infos:
html += tr_template % (line_info[0],line_info[1],line_info[2],line_info[3],line_info[4],line_info[5],line_info[6], line_info[0], line_info[0])
# content = re.sub(r"\{%content%\}", str(stock_infos), content)
content = re.sub(r"\{%content%\}", html, content)
return content
# 给路由添加正则表达式的原因:在实际开发时,url中往往会带有很多参数,例如/add/000007.html中000007就是参数,
# 如果没有正则的话,那么就需要编写N次@route来进行添加 url对应的函数 到字典中,此时字典中的键值对有N个,浪费空间
# 而采用了正则的话,那么只要编写1次@route就可以完成多个 url例如/add/00007.html /add/000036.html等对应同一个函数,此时字典中的键值对个数会少很多
@route(r"/add/(\d+)\.html")
def add_focus(ret):
# 1. 获取股票代码
stock_code = ret.group(1)
# 2. 判断试下是否有这个股票代码
conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
cs = conn.cursor()
sql = """select * from info where code=%s;"""
cs.execute(sql, (stock_code,))
# 如果要是没有这个股票代码,那么就认为是非法的请求
if not cs.fetchone():
cs.close()
conn.close()
return "没有这支股票,大哥 ,我们是创业公司,请手下留情..."
# 3. 判断以下是否已经关注过
sql = """ select * from info as i inner join focus as f on i.id=f.info_id where i.code=%s;"""
cs.execute(sql, (stock_code,))
# 如果查出来了,那么表示已经关注过
if cs.fetchone():
cs.close()
conn.close()
return "已经关注过了,请勿重复关注..."
# 4. 添加关注
sql = """insert into focus (info_id) select id from info where code=%s;"""
cs.execute(sql, (stock_code,))
conn.commit()
cs.close()
conn.close()
return "关注成功...."
# 删除关注
@route(r"/del/(\d+)\.html")
def del_focus(ret):
# 1. 获取股票代码
stock_code = ret.group(1)
# 2. 判断试下是否有这个股票代码
conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
cs = conn.cursor()
sql = """select * from info where code=%s;"""
cs.execute(sql, (stock_code,))
# 如果要是没有这个股票代码,那么就认为是非法的请求
if not cs.fetchone():
cs.close()
conn.close()
return "没有这支股票,大哥 ,我们是创业公司,请手下留情..."
# 3. 判断以下是否已经关注过
sql = """ select * from info as i inner join focus as f on i.id=f.info_id where i.code=%s;"""
cs.execute(sql, (stock_code,))
# 如果没有关注过,那么表示非法的请求
if not cs.fetchone():
cs.close()
conn.close()
return "%s 之前未关注,请勿取消关注..." % stock_code
# 4. 取消关注
# sql = """insert into focus (info_id) select id from info where code=%s;"""
sql = """delete from focus where info_id = (select id from info where code=%s);"""
cs.execute(sql, (stock_code,))
conn.commit()
cs.close()
conn.close()
return "取消关注成功...."
# 修改页面
@route(r"/update/(\d+)\.html")
def show_update_page(ret):
"""显示修改的那个页面"""
# 1. 获取股票代码
stock_code = ret.group(1)
# 2. 打开模板
with open("./templates/update.html") as f:
content = f.read()
# 3. 根据股票代码查询相关的备注信息
conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
cs = conn.cursor()
sql = """select f.note_info from focus as f inner join info as i on i.id=f.info_id where i.code=%s;"""
cs.execute(sql, (stock_code,))
stock_infos = cs.fetchone()
note_info = stock_infos[0] # 获取这个股票对应的备注信息
cs.close()
conn.close()
content = re.sub(r"\{%note_info%\}", note_info, content)
content = re.sub(r"\{%code%\}", stock_code, content)
return content
# 保存修改信息
@route(r"/update/(\d+)/(.*)\.html")
def save_update_page(ret):
""""保存修改的信息"""
stock_code = ret.group(1)
comment = ret.group(2)
# 浏览器传给服务器的URL是经过编码的 服务器再传给框架需要进行解码
comment = urllib.parse.unquote(comment)
conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
cs = conn.cursor()
sql = """update focus set note_info=%s where info_id = (select id from info where code=%s);"""
cs.execute(sql, (comment, stock_code))
conn.commit()
cs.close()
conn.close()
return "修改成功..."
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')])
file_name = env['PATH_INFO']
# file_name = "/index.py"
"""
if file_name == "/index.py":
return index()
elif file_name == "/center.py":
return center()
else:
return 'Hello World! 我爱你中国....'
"""
logging.basicConfig(level=logging.INFO,
filename='./log.txt',
filemode='a',
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
logging.info("访问的是,%s" % file_name)
try:
# func = URL_FUNC_DICT[file_name]
# return func()
# return URL_FUNC_DICT[file_name]()
for url, func in URL_FUNC_DICT.items():
# {
# r"/index.html":index,
# r"/center.html":center,
# r"/add/\d+\.html":add_focus
# }
ret = re.match(url, file_name)
if ret:
return func(ret)
else:
logging.warning("没有对应的函数....")
return "请求的url(%s)没有对应的函数...." % file_name
except Exception as ret:
return "产生了异常:%s" % str(ret)
开发过程中出现bug是必不可免的,你会怎样debug?从第1行代码开始看么?还是有个文件里面记录着哪里错了更方便呢!!!log日志
Python中有个logging模块可以完成相关信息的记录,在debug时用它往往事半功倍
- 日志级别
日志一共分成5个等级,从低到高分别是:
-
DEBUG
-
INFO
-
WARNING
-
ERROR
-
CRITICAL
说明: -
DEBUG:详细的信息,通常只出现在诊断问题上
-
INFO:确认一切按预期运行
-
WARNING:一个迹象表明,一些意想不到的事情发生了,或表明一些问题在不久的将来(例如。磁盘空间低”)。这个软件还能按预期工作。
-
ERROR:更严重的问题,软件没能执行一些功能
-
CRITICAL:一个严重的错误,这表明程序本身可能无法继续运行
这5个等级,也分别对应5种打日志的方法: debug 、info 、warning 、error 、critical。默认的是WARNING,当在WARNING或之上时才被跟踪。
日志输出
有两种方式记录跟踪,一种输出控制台,另一种是记录到文件中,如日志文件。
将日志输出到控制台
比如,log1.py 如下:
import logging
logging.basicConfig(level=logging.WARNING,
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
# 开始使用log功能
logging.info('这是 loggging info message')
logging.debug('这是 loggging debug message')
logging.warning('这是 loggging a warning message')
logging.error('这是 an loggging error message')
logging.critical('这是 loggging critical message')
运行结果
2017-11-06 23:07:35,725 - log1.py[line:9] - WARNING: 这是 loggging a warning message
2017-11-06 23:07:35,725 - log1.py[line:10] - ERROR: 这是 an loggging error message
2017-11-06 23:07:35,725 - log1.py[line:11] - CRITICAL: 这是 loggging critical message
说明
通过logging.basicConfig函数对日志的输出格式及方式做相关配置,上面代码设置日志的输出等级是WARNING级别,意思是WARNING级别以上的日志才会输出。另外还制定了日志输出的格式。
注意,只要用过一次log功能再次设置格式时将失效,实际开发中格式肯定不会经常变化,所以刚开始时需要设定好格式
将日志输出到文件
我们还可以将日志输出到文件,只需要在logging.basicConfig函数中设置好输出文件的文件名和写文件的模式。
log2.py 如下:
import logging
logging.basicConfig(level=logging.WARNING,
filename='./log.txt',
filemode='w',
format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
# use logging
logging.info('这是 loggging info message')
logging.debug('这是 loggging debug message')
logging.warning('这是 loggging a warning message')
logging.error('这是 an loggging error message')
logging.critical('这是 loggging critical message')
运行效果
python@ubuntu: cat log.txt
2017-11-06 23:10:44,549 - log2.py[line:10] - WARNING: 这是 loggging a warning message
2017-11-06 23:10:44,549 - log2.py[line:11] - ERROR: 这是 an loggging error message
2017-11-06 23:10:44,549 - log2.py[line:12] - CRITICAL: 这是 loggging critical message
既要把日志输出到控制台, 还要写入日志文件
这就需要一个叫作Logger 的对象来帮忙,下面将对他进行详细介绍,现在这里先学习怎么实现把日志既要输出到控制台又要输出到文件的功能。
import logging
# 第一步,创建一个logger
logger = logging.getLogger()
logger.setLevel(logging.INFO) # Log等级总开关
# 第二步,创建一个handler,用于写入日志文件
logfile = './log.txt'
fh = logging.FileHandler(logfile, mode='a') # open的打开模式这里可以进行参考
fh.setLevel(logging.DEBUG) # 输出到file的log等级的开关
# 第三步,再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.WARNING) # 输出到console的log等级的开关
# 第四步,定义handler的输出格式
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 第五步,将logger添加到handler里面
logger.addHandler(fh)
logger.addHandler(ch)
# 日志
logger.debug('这是 logger debug message')
logger.info('这是 logger info message')
logger.warning('这是 logger warning message')
logger.error('这是 logger error message')
logger.critical('这是 logger critical message')
运行时终端的输出结果:
2017-11-06 23:14:04,731 - log3.py[line:28] - WARNING: 这是 logger warning message
2017-11-06 23:14:04,731 - log3.py[line:29] - ERROR: 这是 logger error message
2017-11-06 23:14:04,731 - log3.py[line:30] - CRITICAL: 这是 logger critical message
在log.txt中,有如下数据:
2017-11-06 23:14:04,731 - log3.py[line:27] - INFO: 这是 logger info message
2017-11-06 23:14:04,731 - log3.py[line:28] - WARNING: 这是 logger warning message
2017-11-06 23:14:04,731 - log3.py[line:29] - ERROR: 这是 logger error message
2017-11-06 23:14:04,731 - log3.py[line:30] - CRITICAL: 这是 logger critical message
日志格式说明
logging.basicConfig函数中,可以指定日志的输出格式format,这个参数可以输出很多有用的信息,如下:
-
%(levelno)s: 打印日志级别的数值
-
%(levelname)s: 打印日志级别名称
-
%(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
-
%(filename)s: 打印当前执行程序名
-
%(funcName)s: 打印日志的当前函数
-
%(lineno)d: 打印日志的当前行号
-
%(asctime)s: 打印日志的时间
-
%(thread)d: 打印线程ID
-
%(threadName)s: 打印线程名称
-
%(process)d: 打印进程ID
-
%(message)s: 打印日志信息
在工作中给的常用格式如下:format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'
这个格式可以输出日志的打印时间,是哪个模块输出的,输出的日志级别是什么,以及输入的日志内容。