2018-10-31 更新Logging日志记录以及异常捕获
感谢廖大教程。Python实战php
直接在闲置的服务器上开发。阿里云Centos 6.8 64位
。css
Python 环境是Python 3.4
, 在装aiohttp的时候报错,以前用pip3和系统自己的pip(Python 2.7
)也有安装问题,索性下最新版Python 3.6.4
,并安装virtualenv独立运行。html
python3.3之后自带venv模块支持轻量级虚拟环境,virtualenv模块仍然支持,可安装。 1.建立虚拟环境 virtualenv --no-site-packages myvenv 等价于 virtualenv myvenv (目前新版默认不使用系统环境包) python3自带venv python -m venv myvenv 也是默认全新干净的环境,相反可选的参数 python -m venv --system-site-packages myvenv 使虚拟环境指向系统环境包目录(非复制),在系统环境pip新安装包,在虚拟环境就可使用。 2.激活虚拟环境 Platform Shell Command to activate virtual environment Posix bash/zsh $ source <venv>/bin/activate fish $ . <venv>/bin/activate.fish csh/tcsh $ source <venv>/bin/activate.csh Windows cmd.exe C:> <venv>\Scripts\activate.bat PowerShell PS C:> <venv>\Scripts\Activate.ps1 3.关闭虚拟环境 deactivate 4.删除虚拟环境 删除目录便可 -by 林er爱喝果汁Q https://www.liaoxuefeng.com/discuss/001409195742008d822b26cf3de46aea14f2b7378a1ba91000/00150035599472221b683bc9ae245c4a08097bd0cc7866c000
因此直接运行:前端
> python3.6 myvenv > source myvenv/bin/activate
以后就能够直接用Python命令而非Python3.6来指定使用版本了。vue
原文中监听127.0.0.1:9000,本地测试直接打开便可,但是服务器怎么看页面呢。安装有nginx,可是配置太麻烦。想要快速查看页面。python
阿里ECS管理页面中有公网和私网IPmysql
在代码中从新监听私有IP的9000端口,而后访问公网IP:9000,无效。jquery
将端口加入到安全组nginx
以后,再次访问公网IP:9000。就成功了,不过是下载的形式。加上Content-Type便可:laravel
return web.Response(body=b'Awesome', headers={'content-type':'text/html'})
用Ctrl+Z
结束正在运行的Python进程后,再次python app.py
出错
error while attempting to bind on address ('172.*.*.*', 9000): address already in use
在ubuntu下,这个问题一般因为按ctrl+z结束程序形成。使用fg命令以后,按ctrl+c从新结束任务便可。 CTRL-Z和CTRL-C都是中断命令,可是他们的做用却不同. CTRL-C是强制中断程序的执行, 而CTRL-Z的是将任务中断,可是此任务并无结束,他仍然在进程中他只是维持挂起的状态,用户可使用fg/bg操做继续前台或后台的任务,fg命令从新启动前台被中断的任务,bg命令把被中断的任务放在后台执行. 例如: 当你vi一个文件是,若是须要用shell执行别的操做,可是你又不打算关闭vi,由于你得 存盘推出,你能够简单的按下CTRL-Z,shell会将vi进程挂起~,当你结束了那个shell操做以后,你能够用fg命令继续vi你的文件. -by http://blog.csdn.net/helinbin/article/details/56015572
用了fg
以后,确实又启动能够访问了:),用Ctrl+C
结束后再次运行就没有地址占用错误提醒了。
##### 弃坑 3节ORM 和 5节的Web框架。都比较难。并且对比GIT中的多个不明因此的函数后面的继续不下去了。虽然很想粗略的过一遍。可是算了,认可本身比较弱也是和本身的一种妥协。协程和装饰器仍是理解的慢。之后再来补,一个月后再来挑战。 2018-3-1 17:48:24 ##### 再次开始 2018年9月17日 服务器再次清零(试过好多东西,内部git、laravel、练手的php框架),从新安装Python,只看成Python工具服务器用,只用Python实现,好比,想作一个距离懂你课程的倒计时来用退款鼓励本身。 `腾讯云Centos 7.5 64位` 总之,如今作什么事情,就是要快! 网上各类资料一查,就必需要尽快搞定。 我也老是目标不明确,原本作A,中途遇到问题牵扯到的其余问题就都看了,其实目标只是A。A才是主线,其余的都不重要。
安装MariaDB、链接以及系统一些问题花费了小半天,遇到问题很多,若是刚开始就从手册走的话,就会清晰便捷不少。MariaDB使用MySQL链接器。
#! /usr/bin/python import pymysql as DB import datetime from hashlib import md5 class Sql(object): def __init__(self): pass def sqlInit(self, dbInfo, mode='tuple'): """ sql初始化 """ try: dbHost = dbInfo.get('host') dbUser = dbInfo.get('user') dbPasswd = dbInfo.get('passwd') dbName = dbInfo.get('db') conn = DB.connect(host=dbHost, user=dbUser, passwd=dbPasswd, db=dbName) if mode == 'tuple': cur = conn.cursor() else: cur = conn.cursor(DB.cursors.DictCursor) return conn, cur except Exception as e: self.log(e) print("connect failed.") exit() @classmethod def query(cls, sql, mode='tuple', **dbInfo): """ 查询 """ conn, cur = cls().sqlInit(dbInfo, mode) try: cur.execute(sql) data = cur.fetchall() except Exception as e: Sql.log(e) Sql.log("error sql: " + str(sql)) data = None conn.close() return data @classmethod def insert(cls, sql, param, **dbInfo): """ 插入 """ conn, cur = cls().sqlInit(dbInfo) try: cur.executemany(sql, param) conn.commit() result = True except Exception as e: conn.rollback() Sql.log(e) Sql.log("error sql: " + str(sql)) result = False conn.close() return result @classmethod def delete(cls, sql, param, **dbInfo): """ 删除 """ conn, cur = cls().sqlInit(dbInfo) try: cur.execute(sql, param) conn.commit() result = True except Exception as e: Sql.log(e) Sql.log('error sql: ' + str(sql)) conn.rollback() result = False conn.close() return result @classmethod def update(cls, sql, param, **dbInfo): """ 更新 """ conn, cur = cls().sqlInit(dbInfo) try: cur.execute(sql, param) conn.commit() result = True except Exception as e: Sql.log(e) Sql.log('error sql: ' + str(sql)) conn.rollback() result = False conn.close() return result @classmethod def run(cls, sql, **dbInfo): """ 执行其余语句 """ conn, cur = cls().sqlInit(dbInfo) try: cur.execute(sql) result = True except Exception as e: Sql.log(e) Sql.log('error sql: ' + str(sql)) result = False conn.close() return result @staticmethod def log(msg): """ 记录日志 """ nowStr = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') fileName = datetime.datetime.now().strftime('%Y%m%d') + '.log' with open('./' + fileName, 'a') as f: f.write(str(nowStr) + '\n') if isinstance(msg, Exception): f.write("\tFile: %s, Line: %s.\n" % (msg.__traceback__.tb_frame.f_globals['__file__'], msg.__traceback__.tb_lineno)) f.write('\t' + str(msg) + '\n') dbInfo = { 'host': '127.0.0.1', 'user': 'root', 'passwd': '123456', 'db': 'test_xxxx' } try: print("1. 建表") if not Sql.run("DROP TABLE IF EXISTS `user`", **dbInfo): raise Exception('drop error') sql = ''' CREATE TABLE `user`( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `password` varchar(64) NOT NULL, `created` varchar(64) NULL, `updated` varchar(64) NULL, `deleted` varchar(64) NULL, `status` int(1) NOT NULL DEFAULT 1, PRIMARY KEY (`id`) ) ENGINE = InnoDB; ''' if not Sql.run(sql, **dbInfo): raise Exception('建表失败!') print('2. 插入') sql = "INSERT INTO `user`(name, password, created, status) VALUES (%s, %s, %s, %s)" param = [['admin', md5(b'123456').hexdigest(), datetime.datetime.now(), 1]] Sql.insert(sql, param, **dbInfo) print('3. 查询') data = Sql.query("select * from user", 'tuple', **dbInfo) print(data) print('4. 更新') Sql.update("UPDATE `user` set name = %s, updated = %s where id = %s", ('admin2', datetime.datetime.now(), 1), **dbInfo) data = Sql.query("select * from user", 'dict', **dbInfo) print(data) print('5. 删除') Sql.delete("DELETE FROM `user` where id = %s", 1, **dbInfo) data = Sql.query("select * from user", 'tuple', **dbInfo) print(data) except Exception as e: print('error: '+str(e))
执行结果
1. 建表 2. 插入 3. 查询 ((1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', '2018-10-04 21:52:13.543246', No ne, None, 1),) 4. 更新 [{'id': 1, 'name': 'admin2', 'password': 'e10adc3949ba59abbe56e057f20f883e', 'crea ted': '2018-10-04 21:52:13.543246', 'updated': '2018-10-04 21:52:13.553830', 'dele ted': None, 'status': 1}] 5. 删除 ()
用了两种查询方法tuple dict.
def cursor(self, cursor=None): """ Create a new cursor to execute queries with. :param cursor: The type of cursor to create; one of :py:class:`Cursor`, :py:class:`SSCursor`, :py:class:`DictCursor`, or :py:class:`SSDictCursor`. None means use Cursor. """ if cursor: return cursor(self) return self.cursorclass(self)
有一个简单日志记录错误。总体很简单粗糙,不能称之为ORM,只是简单的增删改查操做。
照着廖大的抄了一遍,再看了justoneliu的ORM注释理解了一下没懂的地方。
还有须要修改的地方,好比Logging.info输出如何输出log文件,输出中args丢失,更新数据未指定列所有成了默认值。不影响主进度日志待定,先完善一下输出。
Model小节也简单过了一遍,应该有个生成脚本直接经过各表Model去生成sql。
这里感受也好难理解呢。python之aiohttp源码解析——add_route和middleware的工做方式 ,aiohttp的middleware为啥有两个参数,倒序的包含handle又是怎么回事?仍是没有理解。官方文档aiohttp middlewares中都用了@web.middleware
修饰了,大概看一下流程,之后再来搞懂吧。真头大,每次看到这些很难懂的都感受本身很笨。
配置我直接复制重命名.bak了一份,开发时本身去掉就行了。
但愿能够顺利启动哈哈。orm webframe app 都很紧张呢。
这里终于要建handlers.py
了,以前app.py运行会报错
INFO:root:Model into metaclass ,yeeeeeeeeee DEBUG:asyncio:Using selector: EpollSelector INFO:root:create database connection pool... ./app.py:107: DeprecationWarning: loop argument is deprecated logger_factory, response_factory INFO:root:init jinja2... INFO:root:set jinja2 template path: /data/webapp/templates Traceback (most recent call last): File "./app.py", line 118, in <module> loop.run_until_complete(init(loop)) File "/usr/local/lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete return future.result() File "./app.py", line 111, in init add_routes(app, 'handlers') File "/data/webapp/webFrame.py", line 169, in add_routes mod = __import__(module_name, globals(), locals()) ModuleNotFoundError: No module named 'handlers'
Ok
blogs = [ Blog(id='1', name='Test Blog', summary=summary, created_at=time.time()-120), Blog(id='2', name='Something New', summary=summary, created_at=time.time()-3600), Blog(id='3', name='Learn Swift', summary=summary, created_at=time.time()-7200) ]
这块没理解,调了几回发现只是由于Blog(dict)继承自dict, 就直接转为dict了。Ohhhhhhh
本节没有按照原文用uikit css框架,直接写一个最简单的html,用一个jquery cdn就够用了。相似这个很像文档的文章,我想要的博客就是这样的,简单不作做,有干货,虽然我还差好多 :)
这一节很简单
缺乏vue 引入vue.js, 以及awesome.js 中定义的方法。又碰到了CryptoJS
,确定再引入sha1.min.js。
测试POST时,报错405: Method Not Allowed
, 由于已经存在一个同名的get请求(90%)。
一个__init__参数错误
卡了好久,
INFO:root:server started at http://127.0.0.1:9000 INFO:root:Request:POST /api/users INFO:root:Response handler... Unsupported Content-Type: application/octet-stream ERROR:aiohttp.server:Error handling request Traceback (most recent call last): File "/usr/local/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 390, in start resp = await self._request_handler(request) File "/usr/local/lib/python3.7/site-packages/aiohttp/web_app.py", line 366, in _handle resp = await handler(request) File "/usr/local/lib/python3.7/site-packages/aiohttp/web_middlewares.py", line 106, in impl return await handler(request) File "./app.py", line 43, in logger return (await handler(request)) File "./app.py", line 61, in response r = await handler(request) File "/usr/local/lib/python3.7/site-packages/aiohttp/web_urldispatcher.py", line 120, in handler_wrapper result = await result File "/data/webapp/webFrame.py", line 114, in __call__ return web.HTTPBadRequest('Unsupported Content-Type: {0}'.format(request.content_type)) TypeError: __init__() takes 1 positional argument but 2 were given
须要一个参数,给了两个。检查再三,觉得抄代码时候哪里遗漏了,对比再对比,发现都同样的啊。调试一下
发现确实是这句的错,去掉参数后就行了。 归根溯源,看看真身,
class HTTPBadRequest(HTTPClientError) | HTTPBadRequest(*, headers=None, reason=None, body=None, text=None, content_type=None)
原来只有命名关键字参数的啊。再次验证一下命名关键字参数前的*
是不能接受参数么,来试一下:
原来如此。
那为啥大伙的HTTPBadRequest中加str的参数能够呢。无论了,修改成
return web.HTTPBadRequest(reason = 'Unsupported Content-Type: {0}'.format(request.content_type), content_type = request.content_type)
Okay.
如今,问题来了,个人为啥会抛错。怎么就获得了这个错,content-type是application/octet-stream
。 在app.py
的response_factory
中响应到字节流的时候会带这个type
if isinstance(r, bytes): resp = web.Response(body = r) resp.content_type = 'application/octet-stream' return resp
用浏览器提交则正常,我是经过postman模拟的就错误,设置header的Content-Type:application/x-www-form-urlencoded
即好。
另:
以后其实用postman模拟参数name, email, passed
也提交不了,由于passwd
在客户端就用sha1
加密了。像以前那个tapd登陆同样,也得模拟一下sha1加密才可提交成功。这里sha1加密直接查看源代码就能够看到,很简单的用sha1加密,passwd: CryptoJS.SHA1(email + ':' + this.password1).toString()
,而那次逆向学习到的是加AES的CBC加密。有时间了能够好好研究一下。
又搞了两个小时,修复了错误,加了cookie和登陆,时间过得真快。
```
解析失败,换了mistune, 添加了markdown样式。Python下Markdnown解析踩的小坑logging文件
记录日志并滚动2018-10-31专门开一块,这里放不懂和学习到的,防止思绪飞扬
1. 复习多进程
import os print('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork() if pid == 0: print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())) else: print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
这段代码,输出中os.getppid()
为1,和预期不符
[root@centos py]# ./process.py process 21289 start I (21289) just created a child process (21290). [root@centos py]# I am child process (21290) and my parent is 1.
参考getpid,getppid中说:“getppid返回父进程的pid,可是若是父进程已经结束了,那个子进程获得的getppid永远是1。”“阻止父进程消亡,加上sleep”
#! /usr/bin/python import os import time print("process %s start"% os.getpid()) pid = os.fork() if pid==0: print("I am child process (%s) and my parent is %s." % (os.getpid(), os.getppid() ) ) else: print("I (%s) just created a child process (%s)."% (os.getpid(), pid)) time.sleep(1)
[root@centos py]# ./process.py process 21754 start I (21754) just created a child process (21755). I am child process (21755) and my parent is 21754.
2. vim 进入默认为 REPLACE
ssh
登陆主机后,使用vim
打开文件默认为REPLACE
模式,好烦人。
搜索一阵怀疑是ConEmu
的问题,在下面启用MINGW
登陆ssh
启动vim则是正常的。发现win下的ssh
和 powershell
都会如此。
3. 加解密
AES五种加密模式(CBC、ECB、CTR、OCF、CFB)
还搜到一个好玩的N1CTF 2018:RSA_Padding,关于m^3>n
,我原本胡乱猜测的是
(a+b)^3
二项式展开后第一项a^3==n
的话,取模就会为0,balabalabala胡扯,以后忽然发现这个公式
(a+b)^p ≡ a^p+b^p(modp)
这不就是答案么,第一项a^p<=n
的话,取模则为a自身或0。可能加密没用了吧。
这里有从费马小定理的公式推导,又扯太远了,何况我如今又看不懂。
4. 日志
加了日志记录,终于摆脱了logging.basicConfig(level=logging.DEBUG)
的简单记录。放到配置文件去读取
logging.config.fileConfig('log.conf') logger = logging.getLogger('root')
文件中配置了日志滚动,设置以后直接使用logger.info()/warn()/debug()
感受很方便。
配置文件参考:
[loggers] keys=root [handlers] keys=hand01,hand02 [formatters] keys=form01,form02 [logger_root] level=NOTSET handlers=hand01 ################################### [handler_hand01] class=handlers.TimedRotatingFileHandler level=DEBUG formatter=form01 args=('/srv/web/log/run.log','h', 2, 12*10) # 2h一次滚动,共备份十天的 [handler_hand02] class=StreamHandler level=DEBUG formatter=form02 #args=(sys.stdout,) args=tuple() ################################### [formatter_form01] format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s datefmt=[%Y-%m-%d %H:%M:%S] [formatter_form02] format=%(asctime)s %(name)s %(levelname)s %(message)s class=logging.Formatter
hand02
为屏幕输出,方便调试时用。
有缺点是只能获取到本身手动打的日志,未捕获的异常则没记录很不方便。
用sys.excepthook
进行异常捕获。
def my_exception_hook(exctype, value, traceback): """ 捕获异常 """ logger.warn(' !! exception hook !!') logger.warn("type:{},value:{}\n traceback:{}".format(exctype, value, traceback)) def exception_set(flag): """ 设置捕获异常 """ if flag: sys.excepthook = my_exception_hook
在其余文件使用1/0
测试触发异常。
在app.py
中运行exception_set(True)
则开启异常捕获。
图:上部分未开启捕获,异常报错输出,下部分开启捕获信息写入日志。
日志信息为:
[2018-10-31 12:06:19] functions.py[line:82] WARNING !! exception hook !! [2018-10-31 12:06:19] functions.py[line:83] WARNING type:<class 'ZeroDivisionError'>,value:division by zero traceback:<traceback object at 0x7f6dbe43b448>
traceback只输出了对象信息。
最终修改成:
import traceback ... ... 81 def my_exception_hook(exctype, value, tb): 82 """ 捕获异常 """ 83 err_msg = ' '.join(traceback.format_exception(exctype, value, tb)) 84 logger.warn(' !! exception hook !!') 85 #logger.warn("type:{},value:{}\n traceback:{}".format(exctype, value, traceback.print_exc())) 86 logger.warn(err_msg) 87 88 def exception_set(flag): 89 """ 设置捕获异常 """ 90 if flag: 91 sys.excepthook = my_exception_hook 92 else: 93 sys.excepthook = sys.__excepthook__ 94
日志查看
4 [2018-10-31 12:33:18] functions.py[line:84] WARNING !! exception hook !! 5 [2018-10-31 12:33:18] functions.py[line:86] WARNING Traceback (most recent call last): 6 File "app.py", line 148, in <module> 7 loop.run_until_complete(init(loop)) 8 File "/usr/local/lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete 9 return future.result() 10 File "app.py", line 138, in init 11 add_routes(app, 'handlers') 12 File "/srv/web/www/webFrame.py", line 174, in add_routes 13 mod = __import__(module_name, globals(), locals()) 14 File "/srv/web/www/handlers.py", line 20, in <module> 15 1/0 16 ZeroDivisionError: division by zero
虽然不是那么完美,够用了。
Centos7中没有Mysql, 因此mysql-server mysql-devel都不存在,用yum install mysql安装一次后再次调用会有 # yum install mysql Package 1:mariadb-5.5.60-1.el7_5.x86_64 already installed and latest version 证实了默认安装mysql则为`mariadb`, 故安装`mariadb-server` `mariadb-devel` 便可。
import pymysql
便可的PyMySQL模块有以下要求: Python解释器(知足下列条件之一): Cpython解释器 >= 2.6 或 >= 3.3 PyPy >= 4.0 IronPython = 2.7 MySQL服务(知足下列条件之一): MySQL >= 4.1 MariaDB >= 5.1