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