前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >我的python学习--第十二天(二)

我的python学习--第十二天(二)

作者头像
py3study
发布2020-01-08 20:16:50
6720
发布2020-01-08 20:16:50
举报
文章被收录于专栏:python3python3

Python异常处理

  Python的异常处理能力是很强大的,可向用户准确反馈出错信息。在Python中,异常也是对象,可对它进行操作。

所有异常都是基类Exception的成员,所有异常都从基类Exception继承,而且都在exceptions模块中定义,

Python自动将所有异常名称放在内建命名空间中,所以程序不必导入exceptions模块即可使用异常。

一、格式

代码语言:javascript
复制
try:
    block                
except 异常类型:
    block               
finally:                                
    block

该种异常处理语法的规则是:

  • 执行try下的语句,如果引发异常,则执行过程会跳到第一个except语句。
  • 如果第一个except中定义的异常与引发的异常匹配,则执行该except中的语句。
  • 如果引发的异常不匹配第一个except,则会搜索第二个except,允许编写的except数量没有限制。
  • 如果所有的except都不匹配,则异常会传递到下一个调用本代码的最高层try代码中。
  • 不管上面执行的怎么样,都要执行finally下面的内容。

示例代码:

代码语言:javascript
复制
try:
    f = open(“file.txt”,”r”)
except IOError, e:      # 捕获到的IOError错误的详细原因会被放置在对象e中,然后运行该异常的except代码块
    print e

可以使用Exception来捕获所有的异常,所有的异常信息都收来了,简单省心

代码语言:javascript
复制
try:
    f = open(“file.txt”,”r”)
except Exception,e:    # Exception是所有异常类的基类,所有类型的错误信息都会输入到e中
    print e

常见异常类型

  • AttributeError     试图访问一个对象没有的树形,比如foo.x,但foo没有属性x
  • IOError         输入输出异常;基本是无法打开文件错误
  • ImportError      无法引入模块或者包;基本上是路径问题或者名称错误
  • IndentationError   语法错误;代码没有正确的对齐
  • IndexError:       下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
  • KeyError         试图访问字典里不存在的键              
  • NameError        使用一个还未赋值的变量
  • SyntaxError       代码非法,
  • TypeError        传入对象类型与要求的不符合
  • ValueError       传给函数的参数类型不正确,比如给int()函数传入字符串形

二、traceback获取详细的异常信息

1:传统方式的异常处理

代码语言:javascript
复制
In [1]: try:
   ...:     1/0
   ...: except Exception,e:
   ...:     print e
   ...:     
integer division or modulo by zero               # 只显示简单的错误信息

2:加入了traceback之后的异常处理

代码语言:javascript
复制
In [1]: import traceback

In [2]: try:
   ...:     1/0
   ...: except Exception:
   ...:     traceback.print_exc()                 # 打印出详细的错误信息
   ...:     
Traceback (most recent call last):
  File "<ipython-input-2-7989d926ba7a>", line 2, in <module>
    1/0
ZeroDivisionError: integer division or modulo by zero

3:traceback.print_exc() vs traceback.format_exc()

  format_exc():返回字符串,可以结合logging模块使用

    logging.getLogger().error("Get users list error: %s" % traceback.format_exc())

  print_exc():直接给打印出来。也可以接受file参数直接写入到一个文件

    traceback.print_exc()                       # 打印到屏幕

    traceback.print_exc(file=open('tb.txt','w+'))       # 错误信息重定向到文件

三、手动触发异常

  在Python中,除了程序自身错误引发的异常外,也可以根据自己需要手工引发异常,最简单的形式就是输入关键

字raise,后跟要引发的异常的名称。

  raise语法格式如下:

    raise[Exception[, args [, traceback]]]

  语句中Exception是异常的类型(例如,NameError)参数是一个异常参数值。该参数是可选的,如果不提供,异

常的参数是"None"。

定义一个异常:

代码语言:javascript
复制
In [1]: import traceback

In [2]: try:
   ...:     print 'hello world'
   ...:     raise Exception('just a test')      # 自己定义一个异常
   ...: except Exception:
   ...:     traceback.print_exc()
   ...:     
hello world
Traceback (most recent call last):
  File "<ipython-input-2-32f7ee25cfcc>", line 3, in <module>
    raise Exception('just a test')
Exception: just a test

生产中自定义异常的方式:直接return 错误错误编号和信息

代码语言:javascript
复制
try:
    ... ...
    if role != 0:
        return json.dumps({'code':1,'errmsg':'you are not admin'})
    ... ...
except:
    logging.getLogger().error("select  Cabinet list error: %s" % traceback.format_exc())
    return json.dumps({'code': 1, 'errmsg': 'select  Cabinet list error'})

logging模块

一、概述

  在实际项目中,需要对一些数据进行日志记录,并将日志记录到不同的存储单元中,例如数据库,文本,或者推送到图形化界面中,当需要时发现自己实现一个日志库其实是要很大的代价,因此,第三方的日志库上进行定制化处理 正文内容是对logging的理解和使用方式,非常方便

1:四个主要类,使用官方文档中的概括:

  • logger       提供了应用程序可以直接使用的接口;
  • handler      将(logger创建的)日志记录发送到合适的目的输出;
  • filter       提供了细度设备来决定输出哪条日志记录;用处不太大
  • formatter     决定日志记录的最终输出格式

2:模块级函数

  • logging.getLogger([name])       # 返回一个logger对象,如果没有指定名字将返回root logger,最常用
  • logging.basicConfig():         # 给logger对象的配置管理函数,不常用   
  • logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical(): # logger的日志级别

二、logging工作流演示

代码语言:javascript
复制
#coding:utf-8
import logging

# 创建一个logger命名为mylogger(可以是任意字符串), %(name)s可调用这个名字
logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)

# 创建一个handler,用于写入日志文件,只输出debug级别以上的日志
fh = logging.FileHandler('test.log')
fh.setLevel(logging.DEBUG)

# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(filename)s- %(levelname)s - %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# 给logger添加handler
logger.addHandler(fh)
logger.addHandler(ch)

# 记录两条日志
logger.info('foorbar')  
logger.debug('just a test ')

运行结果:

代码语言:javascript
复制
[root@yaoliang day_12]# python test.py 
2016-10-17 17:26:10,111 - mylogger - test.py- INFO - foorbar
2016-10-17 17:26:10,113 - mylogger - test.py- DEBUG - just a test

三、logging模块的api

1:logging.getLogger([name])

  返回一个logger实例,如果没有指定name,返回root logger。只要name相同,返回的logger实例都是同一个而且只有一个,即name和logger实例是一一对应的。这意味着,无需把logger实例在各个模块中传递。只要知道name,就能得到同一个logger实例

2:logger.setLevel(lvl):设置logger记录日志的级别

level有以下几个级别:

  NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICA

  如果把logger的级别设置为INFO,那么小于INFO级别的日志都不输出,大于等于INFO级别的日志都输出。也就意味着同一个logger实例,如果多个地方调用,会出现很多重复的日志

3:logger.addHandler(hd):logger雇佣handler来帮它处理日志

  handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler

handler主要有以下几种:

(常用)

  • logging.StreamHandler:              # 日志输出到流即控制台,可以是sys.stderr、sys.stdout
  • logging.FileHandler:                # 日志输出到文件
  • logging.handlers.RotatingFileHandler:    # 日志输出到文件,并按照设定的日志文件大小切割
  • logging.handlers.TimedRotatingFileHandler  # 日志输出到文件,并按设定的时间切割日志文件

(不常用) 

  • logging.handlers.SocketHandler:         # 远程输出日志到TCP/IP sockets
  • logging.handlers.DatagramHandler:       # 远程输出日志到UDP sockets
  • logging.handlers.SMTPHandler:          # 远程输出日志到邮件地址
  • logging.handlers.SysLogHandler:         # 日志输出到syslog
  • logging.handlers.NTEventLogHandler:      # 远程输出日志到Windows NT/2000/XP的事件日志
  • logging.handlers.MemoryHandler:         # 日志输出到内存中的制定buffer

  由于StreamHandler和FileHandler是常用的日志处理方式,所以直接包含在logging模块中,而其他方式则包含在logging.handlers模块中,

handle常见调用

  • Handler.setLevel(lel)               # 指定被处理的信息级别,低于lel级别的信息将被忽略
  • Handler.setFormatter()              # 给这个handler选择一个格式
  • Handler.addFilter(filter)            # 新增或删除一个filter对象
  • Handler.removeFilter(filter)          # 新增或删除一个filter对象

logging生产环境的使用方法:将其封装为函数

代码语言:javascript
复制
#/usr/bin/env python
#coding:utf-8
import logging,logging.handlers

def WriteLog(log_name):
    log_filename = "/tmp/test.log"
    log_level = logging.DEBUG         # 日志级别
    format = logging.Formatter('%(asctime)s %(filename)s [line:%(lineno)2d]-%(funcName)s  %(levelname)s %(message)s')       # 日志格式
    handler = logging.handlers.RotatingFileHandler(log_filename, mode='a', maxBytes=10*1024*1024, backupCount=5)        # 日志输出到文件,文件最大10M,最多5个
    handler.setFormatter(format)

    logger = logging.getLogger(log_name)
    logger.setLevel(log_level)
    
    if not logger.handlers:        # 每调用一次就会添加一个logger.handler,每次就额外多打印一次日志,if判断使其只调用一次
        logger.addHandler(handler)
        
    return logger         # 函数最终将实例化的logger对象返回,后面直接调用即可

if __name__ == "__main__":
    WriteLog('api').info('123')         # 模块内部直接调用函数。等价下面两行
    # 下面的方法不推荐
    # writelog = WriteLog('api')
    # writelog.info('123')

4、logging.basicConfig([**kwargs]):加载logger的各项配置参数,不好用

代码语言:javascript
复制
# coding:utf-8
import logging
logging.basicConfig(level=logging.DEBUG,   # 输出debug及其级别更高级别的日志
           format='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s',
           datefmt='%d %b %Y %H:%M:%S',
           filename='myapp.log',           # 日志文件输出的文件地址,不写默认打印到桌面
           filemode='w')

logging.debug("this is debug message")
logging.info("this is info message")
logging.warning("this is warning message")

结果

代码语言:javascript
复制
[root@yaoliang day_12]# tail myapp.log 
17 Oct 2016 17:42:48 test2.py [line:9] DEBUG this is debug message
17 Oct 2016 17:42:48 test2.py [line:10] INFO this is info message
17 Oct 2016 17:42:48 test2.py [line:11] WARNING this is warning message

关于logging.basicConfig函数的常用配置:

filename:                # 指定日志文件名

filemode:                # 和file函数意义相同,指定日志文件的打开模式,'w'或'a'

datefmt:                # 指定时间格式,同time.strftime()

level:                  # 设置日志级别,默认为logging.WARNING,即warning及级别更高日志才输出

stream                  # 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,

                       默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略

format                  # 指定输出的格式和内容,format可以输出很多有用信息

  • %(name)s:        # 打印logger名,默认为root
  • %(levelno)s:       # 打印日志级别的数值
  • %(levelname)s:     # 打印日志级别名称
  • %(pathname)s:      # 打印当前执行程序的路径,其实就是sys.argv[0]
  • %(filename)s:      # 打印当前执行程序名
  • %(funcName)s:      # 打印日志的当前函数
  • %(lineno)d:       # 打印日志的当前行号
  • %(asctime)s:      # 打印日志的时间
  • %(message)s:      # 打印日志信息
  • %(thread)d:       # 打印线程ID
  • %(threadName)s:    # 打印线程名称
  • %(process)d:      # 打印进程ID

5、logging.config模块通过配置文件的方式,加载logger的参数,最好用的方式

代码语言:javascript
复制
[root@yaoliang day_12]# cat logger.conf
# 定义logger模块,root是父类,必需存在的,其它的是自定义。
# logging.getLogger(NAME)就相当于向logging模块注册了实例化了
# name 中用 . 表示 log 的继承关系
[loggers]   
keys=root,example01,example02
# [logger_xxxx] logger_模块名称
# level     级别,级别有DEBUG、INFO、WARNING、ERROR、CRITICAL
# handlers  处理类,可以有多个,用逗号分开
# qualname  logger名称,应用程序通过 logging.getLogger获取。对于不能获取的名称,则记录到root模块。
# propagate 是否继承父类的log信息,0:否 1:是
[logger_root]
level=DEBUG
handlers=hand01,hand02
[logger_example01]
handlers=hand01,hand02
qualname=example01
propagate=0
[logger_example02]
handlers=hand01,hand03
qualname=example02
propagate=0
# [handler_xxxx]
# class handler类名
# level 日志级别
# formatter,上面定义的formatter
# args handler初始化函数参数
[handlers]
keys=hand01,hand02,hand03

[handler_hand01]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stderr,)

[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form01
args=('myapp.log', 'a')
[handler_hand03]
class=handlers.RotatingFileHandler
level=INFO
formatter=form02
args=('myapp.log', 'a', 10*1024*1024, 5)
# 日志格式
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
[formatter_form02]
format=%(asctime)s%(name)-12s: %(levelname)-8s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S

调用

代码语言:javascript
复制
import logging
import logging.config

logging.config.fileConfig("logger.conf")
logger = logging.getLogger("example01")
logger.debug('This is debug message')
logger.info('This is info message')
logger.warning('This is warning message')

生产环境中的调用方法:通过函数

代码语言:javascript
复制
import logging,
import  logging.config

def write_log(loggername):
    work_dir = os.path.dirname(os.path.realpath(__file__))
    log_conf= os.path.join(work_dir, 'conf/logger.conf')
    logging.config.fileConfig(log_conf)
    logger = logging.getLogger(loggername)
    return logger

四、关于root logger以及logger的父子关系

如何得到root logger?

  root logger是默认的logger如果不创建logger实例, 直接调用logging.debug()、logging.info()logging.warning(),logging.error()、logging.critical()这些函数,

那么使用的logger就是 root logger, 它可以自动创建,也是单实例的。

root logger的日志级别?

  root logger默认的level是logging.WARNING

如何表示父子关系?

  logger的name的命名方式可以表示logger之间的父子关系. 比如:

parent_logger = logging.getLogger('foo')

child_logger = logging.getLogger('foo.bar')

什么是effective level?

  logger有一个概念,叫effective level。 如果一个logger没有显示地设置level,那么它就

用父亲的level。如果父亲也没有显示地设置level, 就用父亲的父亲的level,以此推....

最后到达root logger,一定设置过level。默认为logging.WARNING

child loggers得到消息后,既把消息分发给它的handler处理,也会传递给所有祖先logger处理,

示例:

代码语言:javascript
复制
# coding:utf-8
import logging

# 设置root logger,祖先
r = logging.getLogger()
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
r.addHandler(ch)

# 创建一个logger作为父亲
p = logging.getLogger('foo')
p.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(message)s')
ch.setFormatter(formatter)
p.addHandler(ch)

# 创建一个孩子logger
c = logging.getLogger('foo.bar')
c.debug('foo')

输出结果:

代码语言:javascript
复制
[root@yaoliang day_12]# python test3.py
2016-10-17 17:56:01,375 - foo
2016-10-17 17:56:01,375 - DEBUG - foo

可见,孩子logger没有任何handler,所以对消息不做处理。但是它把消息转发给了它的父亲以及root logger。最后输出两条日志。

这也就出现了一个问题,同一条日志会重复输出

解决方案

1、每个logger实例都给一个独立的名字,输出之间互不影响,

2、logging.conf中定义不继承

nginx + gunicorn + supervisor + flask

1、安装gunicorn和supervisor

代码语言:javascript
复制
[root@yaoliang day_12]# pip install gunicorn supervisor

2、启动gunicorn

代码语言:javascript
复制
[root@yaoliang homework_11]# ls
app  run.py
[root@yaoliang homework_11]# gunicorn -w4 -b0.0.0.0:9999 app:app -D
[root@yaoliang homework_11]# ps aux | grep gunicorn
root      43387  0.0  1.2 220196 12040 ?        S    17:42   0:00 gunicorn: master [app:app]
root      43392  0.1  1.9 324784 19844 ?        S    17:42   0:00 gunicorn: worker [app:app]
root      43393  0.1  1.9 324792 19848 ?        S    17:42   0:00 gunicorn: worker [app:app]
root      43394  0.1  1.9 324800 19856 ?        S    17:42   0:00 gunicorn: worker [app:app]
root      43397  0.1  1.9 324812 19864 ?        S    17:42   0:00 gunicorn: worker [app:app]
root      43474  0.0  0.0 112648   976 pts/0    R+   17:43   0:00 grep --color=auto gunicorn

此时可以通过9999端口进行访问

  • -w:表示启动多少个进程
  • -b:表示监听的ip和端口
  • 第一个app:表示包含Flask(__name__)对象的模块或包
  • 第二个app:表示实例化Flask(__name__)对象
  • -D:表示以守护进程运行

3、通过supervisor,一个专门用来管理进程的工具来管理系统的进程。

  3.1、先生成配置文件

代码语言:javascript
复制
[root@yaoliang day_12]# echo_supervisord_conf > /etc/supervisor.conf

  3.2、修改配置文件,开启web管理界面,并在/etc/supervisor.conf底部添加新配置

代码语言:javascript
复制
[inet_http_server]         ; inet (TCP) server disabled by default                       port=*:9001                ; (ip_address:port specifier, *:port for all iface)
username=user              ; (default is no username (open server))
password=123               ; (default is no password (open server))

[program:myapp]
command=/usr/bin/gunicorn -w4 -b0.0.0.0:9999 app:app                ; supervisor启动命令
directory=/data/python/homework_11
startsecs=0                                                         ; 启动时间
stopwaitsecs=0                                                      ; 终止等待时间
autostart=false                                                     ; 是否自动启动
autorestart=false                                                   ; 是否自动重启
stdout_logfile=/tmp/gunicorn.log                                    ; 日常输出日志
stderr_logfile=/tmp/gunicorn.err                                    ; 错误日志

  3.3、supervisor的基本使用方法

代码语言:javascript
复制
supervisord -c /etc/supervisor.conf                        # 通过配置文件启动supervisor
supervisorctl -c /etc/supervisor.conf status                    # 察看supervisor的状态
supervisorctl -c /etc/supervisor.conf reload                    # 重新载入 配置文件
supervisorctl -c /etc/supervisor.conf start [all]|[appname]     # 启动指定/所有 supervisor管理的程序进程
supervisorctl -c /etc/supervisor.conf stop [all]|[appname]      # 关闭指定/所有 supervisor管理的程序进程

  3.4、启动supervisor

代码语言:javascript
复制
[root@yaoliang day_12]# supervisord -c /etc/supervisor.conf 
[root@yaoliang day_12]# ps aux | grep supervisor
root      44393  0.0  1.1 224528 11308 ?        Ss   17:59   0:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisor.conf
root      44399  0.0  0.0 112648   980 pts/0    R+   17:59   0:00 grep --color=auto supervisor
[root@yaoliang day_12]# supervisorctl -c /etc/supervisor.conf status
myapp                            STOPPED   Not started
[root@yaoliang day_12]# supervisorctl -c /etc/supervisor.conf start myapp
myapp: started
[root@yaoliang day_12]# supervisorctl -c /etc/supervisor.conf status
myapp                            RUNNING   pid 44417, uptime 0:00:04

  3.5、通过nginx配置supervisor的web管理界面,并启动

代码语言:javascript
复制
[root@yaoliang day_12]# vim /etc/nginx/nginx.conf
    server {
        listen       80; 
        server_name  localhost;

        location / { 
            proxy_pass http://127.0.0.1:9001;
        }
    } 
[root@yaoliang day_12]# systemctl start nginx

  3.6、访问nginx

wKioL1gFihyC8FWxAABIzsgy8-k695.png
wKioL1gFihyC8FWxAABIzsgy8-k695.png
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
日志服务
日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档