1. 运行环境的搭建

python的运行环境当然是只要去官网下载对应版本安装即可,注意,我们这边只讲3.5以上的版本,因此不要下载错了!安装好后要注意环境变量的配置,具体的可以参看官方说明.

1.1. Anaconda集成环境

更好的工具是使用Anaconda集成环境,这样就可以省去很多配置环境呀,配置依赖的问题,它也可以自动将你的python环境放入系统环境变量,省去了手工配置的麻烦.国内访问Anaconda会比较坑爹,好在有清华的镜像

Anaconda是一个全平台的常用于科学计算的python继承环境包.自带虚拟环境工具,python的版本管理和包管理.用它来安装python可以保证python的隔离性,并且它自带的包足够全面好用.如果嫌弃它太重,那么可以安装miniconda.依然是全平台支持,只是少了自带的包而已,清华提供的下载地址在https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/.

Anaconda的就是下载好后bash <anaconda.sh>(windows就是直接双击打开了)然后一路设置就好(完全可以全默认).

1.1.1. 换源

在墙内的我们最好将清华的源添加至默认,清华的源除了提供了anaconda自己维护的包源外还提供了几个常用的第三方源,包括Conda Forge,msys2,bioconda,menpo.

  • 使用命令行设置

清华源的帮助文档上已经写清楚了如何通过命令行添加源

conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/
conda config --set show_channel_urls yes
  • 使用配置文件设置

linux,mac用户编辑~/.condarc,windows用户编辑C:\Users\<你的用户名>\.condarc,输入如下内容即可.

channels: 
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ 
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/
- defaults

show_channel_urls: yes

这个设置文件windows下默认无法创建,所以必须借助第三方的文本编辑器,这边推荐atom,一些小技巧可以在我的相关文章中找到.

如何验证python是否安装成功呢?

打开terminal,window是cmd,之后输入python -V,就可以看到形如Python 3.6.0 :: Anaconda 4.3.1 (64-bit)这样的字样了,如果是Anaconda安装的,也可以在这条信息中看到Anaconda的版本.

1.2. *关于pypy

pypy是现今活下来的cpython外最好的python实现,它使用jit技术,因此比cpython快的不是一星半点.有测试pypy的io效率与node相当,而cpu密集型任务如果使用python自带的数据结构也比原生cpython快上2~3倍.现在对python3.5有个beta版本的支持,目前还不太完善.

抛开这些不说,pypy的c扩展能力很差,许多带c扩展的模块要么无法在其上使用,要么比在cpython上慢很多.因此可以关注,但并不推荐使用

1.3. *关于docker

docker有官方的python镜像,我们可以直接取来用,如何使用这个镜像创建python应用的镜像并运行可以看我的这篇文章,具体的docker怎么用,那是另一个故事了.

2. 虚拟环境

我们希望项目的环境依赖是独立隔离的,每个项目间各自不会影响其他的项目,最成熟传统的做法就是使用虚拟环境了.虚拟环境可以理解为node.js中npm工具的本地安装,他会把用到的包包括python的虚拟机都放到你指定的目录下,在一个terminal进程中只要你激活了那个虚拟环境,你用到的与python相关联的东西就都是虚拟环境中的了.

python3中自带了工具pyvenv(PEP 405)来构建虚拟环境,而如果希望统一的管理虚拟环境,则Anaconda提供的虚拟环境功能可能更加合适

2.1. pyvenv使用方法

  • pyvenv <venv path> 创建虚拟环境到指定目录

  • source <venv path>/bin/activate 使用虚拟环境,在windows下是 <venv path>/bin/activate.bat

    激活后会看到你的命令行每行前面多出一个(venv)字样,表示你在使用虚拟环境

  • deactivate 退出虚拟环境

2.2. Anaconda虚拟环境和多版本的管理

ananconda也有虚拟环境工具,而且可以通过虚拟环境实现多版本python的管理使用,也就是说Anaconda的虚拟环境工具除了创建虚拟环境,还是python的版本控制工具.

  • 创建虚拟环境
conda create -n <name> python=<python version> [collection]

输入以上命令我们就建立了一个以<name>为名字的虚拟环境,并且代码和虚拟机都将放在<AnacondaPath>/envs/<name>文件夹下.我们需要指定python的版本,如果想顺便把一些要用的包装了,可以在[collection]位置加上要的包.

  • 激活虚拟环境

Anaconda的虚拟环境激活不需要我们记住虚拟环境创建在哪里,只要记住名字就行

在linux或者mac上使用source activate <name>,在windows上使用activate <name>即可,需要注意的是windows下的powershell shell有一个bug,无法激活虚拟环境,要使用的话记得切换到cmd.

  • 退出虚拟环境

在linux或者mac上使用source deactivate,在windows上使用deactivate就可以退出当前的虚拟环境了

  • 查看有哪些虚拟环境

conda env list

  • 要删除一个虚拟环境

conda remove -n <name> --all

3. 包管理与模块

现代的编程场景早已从单打独斗的个人行为转向了多人合作集体行为.而现代编程语言也都有模块化支持以适应模块化的项目编程.python作为一门历史悠久的现代编程语言拥有让许多优秀的第三方包.现如今多数优质的第三方模块都注册在pip上可以很方便的下载安装.而github的兴起也促进了python社区的繁荣,每年都会有很多新的优秀的第三方模块进入pythoner的视野中.

历史悠久的同样意味着历史包袱.与javascript的npm相比,pip并不优秀,而由于python的版本割裂问题,也造成了许多第三方库无法向后兼容,但由于python语言尽量坚持一致性原则和实用至上的理念,python的第三方包相对整体质量更高也更易于定制.因此可以说python的包管理在现今看来依然是实用高效的.

python方便的模块引入语法简洁实用,配合第三方库可以让用户有着如同玩乐高积木一般的优秀体验.javascript的ES6语法中模块引入语法很大程度上借鉴了python的模块语法.

3.1. 包管理

python最常用的包管理工具就是官方的pip,当然Anaconda的conda命令也可以作为包管理工具使用,而且可能更加方便一些,但其实conda命令下载安装python的包也是用的pip.

3.1.1. pip

pip是python的官方第三方包管理工具(PEP 453),收录了大部分的第三方包。多数自带python的系统如mac osx, ubuntu都已经有现成的pip安装着了。如果确实没有pip可以去https://pip.pypa.io/en/latest/installing.html#python-os-support 下载get-pip.py文件,下载到本地后,cd到同一文件夹下使用python get-pip.py安装.基本上不会有人不装pip,因为如果不用它,python就少了很多便利性

pip基本使用:

pip命令可以单独作为脚本命令使用如pip list,也可以配合python解释器使用python -m pip list

后一种方式的好处是可以在不同的python环境使用pip,pip会自己把模块安装到指定python的第三方包文件夹下

  • 安装模块

    • pip install packageName 下载并安装最新的版本

    • pip install packageName==1.0.0下载并安装指定版本

    • pip install 'packageName>=1.0.0 下载并安装至少某个版本以上的版本的包

    • pip install url #从指定网址资源安装

    • pip install path #指定本地位置安装

    • pip install --find-links=url 从指定url下载安装

    • pip install --find-links=path 从指定path下载安装

    • pip install --upgrade packageName 更新一个已经安装过的过期模块

  • 从需求文件安装模块

    • pip freeze > requirements.txt 将当前pip管理的模块信息存储进文本文件

    • pip install -r requirements.txt 从文本文件安装依赖的模块

  • 卸载

    • pip uninstall <packageName>
  • 查找

    • pip search <name>
  • 查看模块信息

    • pip show <packageName>
  • 查看pip管理了哪些模块

    • pip list

    • pip list --outdated 查看过期的模块

3.1.2. 关于pip的国内源设置

感谢天朝的伟大电子长城,我们很多时候无法练到pypi的服务器,还好国内豆瓣有个一直在维护的镜像站可以提供源作为替代

如何设置呢?

在你的个人根目录下有一个.pip文件夹(没有就自己建个),在其中新建一个pip.conf文件作为配置文件,然后在其中填上如下内容:

[global]
index-url = http://pypi.douban.com/simple
trusted-host = pypi.douban.com

3.2. conda 包管理工具

Anaconda的定位是数据科学工具箱,它其实并不局限于python.

我们的pip和conda并不冲突,而conda实际上也是依赖于pip工具的,用conda的好处是:

  • 有些复杂的安装过程他会帮你省去,
  • 可以用它安装一些Anaconda公司的商业工具
  • 它对于包版本的追踪更加细致.
  • 可以用它安装一些不是python包的工具,尤其一些C/C++工具,比如windows下的minwg.

和pip一样,conda list是查看已安装包信息的工具

而查找包还是conda search <pkgname>

要安装也还是conda install,只是它可以加上参数--name <envname>来为特定环境跨环境安装包

而删除包就和pip有所不同了,它使用的是conda remove <pkgname>命令.

3.3. 包分发

无论使用哪种方式管理python的第三方模块,如果想将自己的包分发出去与别人共享,都应该使用官方的pypi平台.和npm一样,作为开发者,你需要先注册才可以上传代到代码库.注册的时候注意,password必须大于16位,PGPkeyID可以不填. 表单提交好后登入邮箱验证即可注册完成.

3.3.1. 包管理文件(setup.py)

安装脚本setup.py就类似npm的package.json,它负责设定包的基本信息和依赖,以下是一个官方的例子的改版 setup.py:


# 一般用setuptools
from setuptools import setup, find_packages,Command
# 维持不同平台文件相同的编码
from codecs import open
import distutils
from os import path
import os
import subprocess


here = path.abspath(path.dirname(__file__))

# 用同文件夹下的README.rst文件定义长介绍
with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
    long_description = f.read()

# 用同文件夹下的requirements.txt文件定义运行依赖
with open(path.join(here, 'requirements.txt'), encoding='utf-8') as f:
    REQUIREMETS = f.readlines()

packages=find_packages(exclude=['contrib', 'docs', 'test'])

class CoverageCommand(Command):
    description = "覆盖率"
    user_options = [
    ("output=","o","选择报告的输出方式")
    ]
    def initialize_options(self):
        self.cwd = None
        self.output = ''
    def finalize_options(self):
        self.cwd = os.getcwd()
        if self.output and self.output not in ("report","html"):
            raise Exception("Parameter --output is missing")
    def run(self):
        assert os.getcwd() == self.cwd, 'Must be in package root: {self.cwd}'.format(self=self)
        command = ['/usr/bin/env', 'python', '-m', 'coverage']
        if self.output:
            command.append('{self.output}'.format(self=self))
        else:
            command.append('report')
        self.announce('Running command: {command}'.format(command = str(command)),
            level=distutils.log.INFO)
        subprocess.check_call(command)


class TestCommand(Command):
    description = "测试"
    user_options = []
    def initialize_options(self):
        self.cwd = None
    def finalize_options(self):
        self.cwd = os.getcwd()
    def run(self):
        assert os.getcwd() == self.cwd, 'Must be in package root: {self.cwd}'.format(self=self)
        command = ['/usr/bin/env', 'python', '-m',
        'coverage','run' ,'--source=score_card_model',
        '-m', 'unittest', 'discover', '-v', '-s', 'test']
        self.announce('Running command: {command}'.format(command = str(command)),
            level=distutils.log.INFO)
        subprocess.check_call(command)

setup(
    name='score_card_model',
    version='0.0.1',
    description='A sample Python project',
    long_description=long_description,

    # 项目地址
    url='https://github.com/pypa/sampleproject',

    # 作者信息
    author='The Python Packaging Authority',
    author_email='pypa-dev@googlegroups.com',
    # 维护者信息
    maintainer = "",
    maintainer_email = "",
    # 指定可用的平台,一般有c扩展的可能会用到
    platforms = ["any"],

    # 许可证信息
    license='MIT',

    # 分类信息,具体看 https://pypi.python.org/pypi?%3Aaction=list_classifiers
    classifiers=[
        # 发展时期,常见的如下
        #   3 - Alpha
        #   4 - Beta
        #   5 - Production/Stable
        'Development Status :: 3 - Alpha',

        # 开发的目标用户
        'Intended Audience :: Developers',
        # 属于什么类型
        'Topic :: Software Development :: Build Tools',

        # 许可证信息
        'License :: OSI Approved :: MIT License',

        # 目标python版本
        'Programming Language :: Python :: 2',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
    ],

    # 关键字
    keywords='sample setuptools development',

    # 指定用到的模块,find_packages会找到同文件夹下的模块,用`exclude`指定排除的模块
    packages=packages,

    # 运行时使用的依赖
    install_requires=REQUIREMETS,
    # 是否支持直接引用zip文件,这是setuptools的特有功能
    zip_safe=False,

    # 额外环境的依赖,一般不单独用文件指出
    # for example:
    # pip install -e .[dev,test]
    # extras_require={
    #     'dev': ['check-manifest'],
    #     'test': ['coverage'],
    # },
    # 指定可执行脚本,如果安装,脚本会被放到默认安装路径
    #scripts=["scripts/test.py"],

    # 模块如果有自带的数据文件,可以用package_data指定
    #package_data={
    #    'sample': ['package_data.dat'],
    #},

    # 指定模块自带数据所在的文件夹
    data_files=[('./', ['requirements.txt'])],
    # 定义自定义命令
    cmdclass = {
        'coverage':CoverageCommand,
        'test':TestCommand
    }
)

3.3.2. 定义setup.py的子命令

类似package.json,setup.py也可以定义子命令,就是相比node.js的要稍微麻烦些.需要继承setuptools.Command, 需要重写其中的元素:

  • description 描述字符串
  • user_options 保存子参数设置的列表,每个元素为一个3元的元组,第一位为全称(--xxx),第二位为简称(-o),第三位为描述文字
[("output=","o","选择报告的输出方式")]

其中的方法

  • initialize_options(self) 初始化子参数
    self.output = ''
    
  • finalize_options(self) 判断命令行与设置的匹配与否和后续操作
    if self.output and self.output not in ("report","html"):
      raise Exception("Parameter --output is missing")
    
  • run(self) 运行逻辑
assert os.getcwd() == self.cwd, 'Must be in package root:

3.3.3. 安装模块

python setup.py install

3.3.4. 关于重复造轮子

python提倡任何事务总有一种最好的方式实现,并不鼓励重复造轮子(虽然事实上python重复的轮子相当多,官方与社区也总会有意见不统一的情况发生).自己写一些模块固然可以,但最好还是先看看有没有现成的实现.

3.3.5. *关于版本号

pep-440定义了符合python规范的版本号格式.当然了这不是强制要求,你要是喜欢也可以按npm的规范定义版本号,或者自己定义一套规范.最重要的是不能有歧义而且便于管理.当然了更加推荐按照pep-440的规范定义版本号.

3.3.6. *将包注册到pypi服务器

完成pypi上的注册,并定义好setup.py脚本后就可以将自己写的模块上传到pypi服务器上了.

  1. 注册包

    cd到 项目根目录

     python setup.py register
    

    用刚才注册的信息来注册本台电脑

    注意直接这样会有可能报错,因为和原来有个名字太接近了.

    我们应该先检查下名字

     pip search <pkgname>
    

    用来查看有哪些相关的包,我们得确定没有重名

  2. 上传

     Python setup.py sdist upload
    

3.3.7. *分发模块

setuptools支持的分发格式可以在python官网查看,打包方式都是使用

python setup.py <builtdist>

3.3.8. *使用wheel分发模块

本部分主要以wheel为例.

wheel是官方钦定的包分发格式,它本质上是一个zip包,使用.whl作为扩展名.wheel包的分发模式并不是为中心化的pypi设计的,而是为了方便点对点的传播,因此wheel模块更多的是本地安装

现有支持wheel的模块

现有支持wheel的模块已经不少,而且很多wheel是为了针对不同操作系统而额外打包的.pypi官方的wheel支持列表可以在http://pythonwheels.com/找到,而许多科学计算工具在windows下的wheel可以在http://www.lfd.uci.edu/~gohlke/pythonlibs/找到

将模块打包成wheel

wheel提供了一个bdist_wheel作为 setuptools 的扩展命令,这个命令可以用来生成wheel包。

python setup.py bdist_wheel

pip提供了对wheel的支持,setup.cfg 可以用来定义 wheel 打包时候的相关信息。

安装wheel打包过的模块

pip工具支持直接安装wheel打包过的模块,就和安装本地的模块一个方式

pip install somedir/xxxxxx.wheel

3.3.9. *工具安利

pmfp(python3.5以上可用)是我写的一个仿照npm的包管理统一平台,目前在测试阶段,但已经可用,它封装了pip,setuptools,pyvenv,zipapp等工具的操作,并设定了几个常用框架的模板,有兴趣的同学可以尝试使用,顺便帮我找找bug,这个工具我并没有写测试,而是打算用边用边测的形式逐步完善它.希望有同学一起帮我完成这个项目,不胜感激!

3.3.10. *本地架设pypi服务器

很多时候我们会有这样一种需求,我们希望我们的包私有或者在小范围内传播,这时候我们就可以架设本地的pypi服务器了

本地架设pypi服务器可以使用pypiserver或者localshop他们用法差不多,不同之处在于前者更轻些,而后者除了可以本地架设pypi服务器外还可以自动镜像pypi的包仓库.

3.4. 模块导入

python中模块和包是一个意思.一个.py文件就是一个模块,如果模块复杂,那么带有__init__.py的文件夹也是一个模块.模块中可以嵌套模块以构建为一个更为复杂的模块.

python的模块引入使用的是import语句.具体有3种形式:

import package
from package import object
from package import object as name

而引入机制可以分为两种:

  • 相对引入
  • 完全引入

3.4.1. 相对引入

相对引入中一个点号来标识引入类库的精确位置。与linux的相对路径表示相似,一个点表示当前目录,每多一个点号则代表向上一层目录.

from .string import a
from ..string import a
from ...string import a

相对引入使用被引入文件的__name__ 属性来决定该文件在整个包结构的位置。那么如果文件的__name__没有包含任何包的信息,例如__name__被设置为了__main__,则认为其为top level script,而不管该文件的位置,这个时候相对引入就没有引入的参考物.

3.4.2. 完全引入

完全引入,非常类似于Java的引入进制,

完全引用是Python的默认的引入机制.它的使用方法如下:

from pkg import foo
from pkg.moduleA import foo

要注意的是,需要从包目录最顶层目录依次写下,而不能从中间开始.

两种引用方式各有利弊。绝对引用代码更加清晰明了,可以清楚的看到引入的包名和层次,但是,当包名修改的时候,我们需要手动修改所有的引用代码。相对引用则比较精简,不会被包名修改所影响,但是可读性较差,不如完全引用清晰。

3.4.3. PYTHONPATH

PYTHONPATH是Python搜索路径,默认我们import的模块都会从PYTHONPATH里面寻找。 使用下面的代码可以打印PYTHONPATH:

import os
print(os.sys.path)
['', 'C:\\Users\\87\\Anaconda3\\python36.zip', 'C:\\Users\\87\\Anaconda3\\DLLs', 'C:\\Users\\87\\Anaconda3\\lib', 'C:\\Users\\87\\Anaconda3', 'C:\\Users\\87\\Anaconda3\\lib\\site-packages', 'C:\\Users\\87\\Anaconda3\\lib\\site-packages\\win32', 'C:\\Users\\87\\Anaconda3\\lib\\site-packages\\win32\\lib', 'C:\\Users\\87\\Anaconda3\\lib\\site-packages\\Pythonwin', 'C:\\Users\\87\\Anaconda3\\lib\\site-packages\\setuptools-27.2.0-py3.6.egg', 'C:\\Users\\87\\Anaconda3\\lib\\site-packages\\IPython\\extensions', 'C:\\Users\\87\\.ipython']

我们的python解释器引入模块就是按这个list的顺序一个一个查找的.因此如果希望修改查找顺序或者将特定位置加入搜索路径,只要将它加入os.sys.path中即可

4. 部署

python有一套完善的项目部署方案,从打包到环境隔离,再到监控,一应俱全.如果没有特殊需求,完全可以跳过容器直接使用.当然了如果运维希望使用docker这类容器部署以限制各个项目的资源使用量时,得益于zipapppip,python也同样简单易于部署.

4.1. 打包与分发

python3.5提供了一种打包分发的方式--zipapp(PEP 441).它可以将写好的项目打包成.pyz文件,这样就可以简单的将项目四处分发了.注意,这种方式最好是打包纯python代码,这样不容易因为平台不同而出现无法使用的情况.如果有c扩展,那么最好单独抽出来写成模块利用pip单独安装.

.pyz文件并不能独立运行,依然依赖python环境.因此如果不用docker打包的话最好使用虚拟环境让项目在虚拟环境中运行.

zipapp的用法如下:

python -m zipapp myapp -m "myapp:main"

myapp是一个项目文件夹,并非模块,我们使用-m指定使用其中的哪个模块的哪个方法作为入口

同时也可以使用-p指定一个字符串作为Shebang

python -m zipapp myapp -m "myapp:main" -p "/user/bin/env python3"

zipapp本质是一个用zip打包项目的工具,它的定位其实是简化版jar.一个打包好的二进制文件远比文件夹好分发使用.这也是go语言的核心竞争力之一.现在python有了这样一个工具,虽然使用起来还是要配合虚拟机和pip包管理工具,但已经很够用了.

4.2. 使用虚拟环境部署

项目部署运行时不可能通过常规手段激活虚拟环境.而事实上也不需要,其实要使用虚拟环境只要指定好用虚拟环境的python解释器运行项目了.比如有个虚拟环境建在~/VENV文件夹.那么就可以直接使用这个文件夹下的python解释器直接使用.

~/VENV/bin/python myapp.pyz

4.3. *批量部署

python的运维神器fabric,用它可以实现对远程服务器的批量部署操作

一些使用方法和心得可以看我的博客

4.4. *服务监控

python的另一运维神器supervisor,配合cesi可以很好的监控管理项目进程.具体的可以看我的这篇博文

4.5. 日志工具

代码检查,debug,调优都只能让代码确保当时是可靠的,一些复杂的关联错误,也可能让这些测试呀debug呀失准,,而只有日志才能长期的帮助我们监控项目的健壮性.这种时候就可以使用标准库logging为程序的运行做记录,在试运行之后通过分析logging记录的方式来debug.

在logging框架下首先我们需要初始化一个logger来处理log,之后通过添加handler,Formatter和config子属性来自定义我们的logger.

一个简单的例子

import logging
import sys
#日志的名字,会在每行的一开始写
logger = logging.getLogger("endlesscode")
#格式化
formatter = logging.Formatter('%(name)-12s %(asctime)s %(levelname)-8s %(message)s', '%a, %d %b %Y %H:%M:%S',)
#设定输出文件
file_handler = logging.FileHandler("src/test.log")
#为handler设置输出格式
file_handler.setFormatter(formatter)
#流控制,将信息输出到标准流输出
stream_handler = logging.StreamHandler(sys.stderr)
#为logger设置handler
logger.addHandler(file_handler)
#发送信息到流
logger.addHandler(stream_handler)
#设置报错等级
#logger.setLevel(logging.ERROR)
#报错
logger.error("w")
#移除handler
logger.removeHandler(stream_handler)
#报错
logger.error("f")
w

其中

  • level: 设置日志级别,默认为logging.WARNING

  • stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略

4.5.1. 输出文本的格式化

元素 格式化字符串 描述
args 不用格式化 参数会是一个元组
asctime %(asctime)s 可读的时间
created %(created)f 记录的创建时间
filename %(filename)s 文件名
funcName %(funcName)s 函数名
levelname %(levelname)s 错误,警报等的名字
levelno %(levelno)s 错误,警报等,是预警等级
lineno %(lineno)d 报错行数
module %(module)s 报错模块
msecs %(msecs)d 毫秒级的出错时间
message %(message)s 错误信息
name %(name)s log的名字
pathname %(pathname)s 报错文件所在path
process %(process)d 进程id
processName %(processName)s 进程名
relativeCreated %(relativeCreated)d 微秒级的报错时间
thread %(thread)d 线程id
threadName %(threadName)s 线程名

4.5.2. 日志回滚

日志也不是一直记录就好,也要考录时效性和存储空间的限制,回滚机制便是解决这个问题的

from logging.handlers import RotatingFileHandler
#定义一个RotatingFileHandler,最多备份5个日志文件,每个日志文件最大10M
Rthandler = RotatingFileHandler('src/myapp.log', maxBytes=10*1024*1024,backupCount=5)
Rthandler.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
Rthandler.setFormatter(formatter)
logging.getLogger('').addHandler(Rthandler)

4.5.3. 几种handler

  • StreamHandler(stream=None) 流输出

  • FileHandler(filename, mode='a', encoding=None, delay=False) 写入文件

  • WatchedFileHandler(filename[, mode[, encoding[, delay]]]) 监控log文件

  • RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0) 轮替日志,根据日志文件的大小来循环

  • TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None) 轮替日志,根据时间来循环,interval参数可选的值有:

    • "S"-Seconds
    • 'M'-Minutes
    • 'H'-Hours
    • 'D'-Days
    • 'W0'~'W6'-Weekday (0=Monday)
    • 'midnight'-半夜循环
  • SocketHandler(host, port) 把log送到网上的socket

  • DatagramHandler(host, port) 把log送到网上的UDP sockets

  • SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM) log送到unix系统log

  • SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0) log送到电子邮箱

  • MemoryHandler(capacity, flushLevel=ERROR, target=None) log存入内存

  • HTTPHandler(host, url, method='GET', secure=False, credentials=None, context=None) log通过http网络送到服务器

4.5.4. 使用设置文件设置logging行为

当然可以在程序中设置log了,但为了改起来方便也可以写在别的文件中然后用config.fileConfig(path)来设置,配置文件的形式是这样:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=%a, %d %b %Y %H:%M:%S

要注意的是如果用这种方式那么,使用rotation file handler时,不要同时声明file handler,否则rotation发生时,doRollover 函数的os.rename 会报错(「另一个程序正在使用此文件,进程无法访问).当然,可以写另一个py文件专门用来初始化,要用的时候import进来就好了.

Copyright © hsz 2017 all right reserved,powered by Gitbook该文件修订时间: 2018-03-03 15:10:41

results matching ""

    No results matching ""