目录

前文列表

OpenStack 实现技术分解 (1) 开发环境 — Devstack 部署案例详解

OpenStack 实现技术分解 (2) 虚拟机初始化工具 — Cloud-Init & metadata & userdata

OpenStack 实现技术分解 (3) 开发工具 — VIM & dotfiles

OpenStack 实现技术分解 (4) 通用技术 — TaskFlow

OpenStack 实现技术分解 (5) 应用开发 — 使用 OpenStackClients 进行二次开发

扩展阅读

Usage — oslo.log 3.21.1.dev1 documentation

oslo.log – Oslo Logging Library

日志级别

在记录日志时, 需要将日志消息关联一个级别, 系统默认提供了 6 个级别,它们分别是:

critical > error > warning > info > debug > notset

级别越高打印的日志越少,反之亦然:

  • DEBUG: 打印全级别日志( notset 等同于 debug)
  • INFO: 打印 info/warning/error/critical 级别日志
  • WARNING: 打印 warning/error/critical 级别日志
  • ERROR: 打印 error/critical 级别日志
  • CRITICAL: 打印 critical 级别

一般而言, 在程序的开发阶段会打印大量的日志, 此时的日志级别应为 DEBUG. 直到程序稳定后, 为了提高程序的执行效率, 需要打印日志应该相对减少. 根据实际情况, 可能会删减 DEBUG 级别的日志代码. 此时, 程序员更关心的是跟踪用户的动作, 例如: 用户对于核心数据的修改动作就非常有必要记录在案, 所以可以选择 INFO 及以上级别的日志, 其中最重要的信息就应该选择 CRITICAL 级别了. 需要注意的是, 异常捕获代码块(try-except)中都应该使用 ERROR/EXCEPTION(由 oslo_log 提供的异常栈级别) 级别的日志, 以便定位具体的错误原因.

oslo.log

(摘自官方文档)The oslo.log (logging) configuration library provides standardized configuration for all openstack projects. It also provides custom formatters, handlers and support for context specific logging (like resource id’s etc).

oslo.log(logging) 库为所有的 OpenStack 项目提供了标准的日志处理方式, 它还提供能自定义日志格式以及各种 handlers, 同时也支持指定上下文对象的日志, 例如: resource_id 的日志等等.

实际上 oslo_log 是基于 Python’s standard logging library 标准库的封装, 在整体使用的简易性上得到了提升.

初始化设置 DEMO

from oslo_config import cfg
from oslo_log import log as logging LOG = logging.getLogger(__name__)
CONF = cfg.CONF
DOMAIN = "demo" logging.register_options(CONF)
logging.setup(CONF, DOMAIN) # Oslo Logging uses INFO as default
LOG.info("Oslo Logging")
LOG.warning("Oslo Logging")
LOG.error("Oslo Logging")

显然的, 为了充分利用 oslo_conf 的跨文件作用域特性, oslo_log 一般会与 oslo_conf 结合使用, 由 oslo_conf 为 oslo_log 提供项目的日志配置项参数值. 对于 oslo_log 而言, 配置项引入是一个非常重要且具有实践价值的设计理念.

oslo.log 的相关配置项

各个配置项的含义在注释中也作了清晰的介绍:

################
# From oslo.log
################ # If set to true, the logging level will be set to DEBUG instead of the default
# INFO level. (boolean value)
# Note: This option can be changed without restarting.
debug = true # DEPRECATED: If set to false, the logging level will be set to WARNING instead
# of the default INFO level. (boolean value)
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
#verbose = true # The name of a logging configuration file. This file is appended to any
# existing logging configuration files. For details about logging configuration
# files, see the Python logging module documentation. Note that when logging
# configuration files are used then all logging configuration is set in the
# configuration file and other logging configuration options are ignored (for
# example, logging_context_format_string). (string value)
# Note: This option can be changed without restarting.
# Deprecated group/name - [DEFAULT]/log_config
#log_config_append = <None> # Defines the format string for %%(asctime)s in log records. Default:
# %(default)s . This option is ignored if log_config_append is set. (string
# value)
#log_date_format = %Y-%m-%d %H:%M:%S # (Optional) Name of log file to send logging output to. If no default is set,
# logging will go to stderr as defined by use_stderr. This option is ignored if
# log_config_append is set. (string value)
# Deprecated group/name - [DEFAULT]/logfile
#log_file = <None> # (Optional) The base directory used for relative log_file paths. This option
# is ignored if log_config_append is set. (string value)
# Deprecated group/name - [DEFAULT]/logdir
#log_dir = <None> # Uses logging handler designed to watch file system. When log file is moved or
# removed this handler will open a new log file with specified path
# instantaneously. It makes sense only if log_file option is specified and
# Linux platform is used. This option is ignored if log_config_append is set.
# (boolean value)
#watch_log_file = false # Use syslog for logging. Existing syslog format is DEPRECATED and will be
# changed later to honor RFC5424. This option is ignored if log_config_append
# is set. (boolean value)
#use_syslog = false # Syslog facility to receive log lines. This option is ignored if
# log_config_append is set. (string value)
#syslog_log_facility = LOG_USER # Log output to standard error. This option is ignored if log_config_append is
# set. (boolean value)
#use_stderr = true # Format string to use for log messages with context. (string value)
#logging_context_format_string = %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s # Format string to use for log messages when context is undefined. (string
# value)
#logging_default_format_string = %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s # Additional data to append to log message when logging level for the message
# is DEBUG. (string value)
#logging_debug_format_suffix = %(funcName)s %(pathname)s:%(lineno)d # Prefix each line of exception output with this format. (string value)
#logging_exception_prefix = %(asctime)s.%(msecs)03d %(process)d ERROR %(name)s %(instance)s # Defines the format string for %(user_identity)s that is used in
# logging_context_format_string. (string value)
#logging_user_identity_format = %(user)s %(tenant)s %(domain)s %(user_domain)s %(project_domain)s # List of package logging levels in logger=LEVEL pairs. This option is ignored
# if log_config_append is set. (list value)
#default_log_levels = amqp=WARN,amqplib=WARN,boto=WARN,qpid=WARN,sqlalchemy=WARN,suds=INFO,oslo.messaging=INFO,iso8601=WARN,requests.packages.urllib3.connectionpool=WARN,urllib3.connectionpool=WARN,websocket=WARN,requests.packages.urllib3.util.retry=WARN,urllib3.util.retry=WARN,keystonemiddleware=WARN,routes.middleware=WARN,stevedore=WARN,taskflow=WARN,keystoneauth=WARN,oslo.cache=INFO,dogpile.core.dogpile=INFO # Enables or disables publication of error events. (boolean value)
#publish_errors = false # The format for an instance that is passed with the log message. (string
# value)
#instance_format = "[instance: %(uuid)s] " # The format for an instance UUID that is passed with the log message. (string
# value)
#instance_uuid_format = "[instance: %(uuid)s] " # Enables or disables fatal status of deprecations. (boolean value)
#fatal_deprecations = false

对于有 Python logging 标准库使用经验的程序员而言, 应该很容易的就能体会到 oslo_log 对 logging 进行封装后带来的好处. 以往使用 logging 时需要在代码中显式定义的状态信息(e.g. 日志级别/打印格式/handler 等), 现在都能够通过 oslo_log 配置文件的配置项参数值来简化定义. 有效的避免了在程序中写出许多无谓的代码, 而且通过阅读配置文件能够更清晰的理解当前程序的日志状态.

一般来说我们需要关心的 oslo_log 配置项有以下几个:

  • debug = true: 是否将日志级别设定为 DEBUG
  • verbose = true: 将日志级别设定为 INFO(true) 还是 WARNING(false)
  • log_date_format = %Y-%m-%d %H:%M:%S: 日志的日期格式
  • log_file = <None>: 日志文件名称
  • log_dir = <None>: 日志文件路径
  • use_stderr = true: 是否让日志以 system standard error 管道输出
  • logging_context_format_string: 日志内容格式
  • default_log_levels: 相关程序的默认日志级别
  • instance_format: instance 的日志打印格式, 所谓的上下文对象日志

oslo_log 支持通过调用 logging 的 register_options() method 来实现配置项的注册:

CONF = cfg.CONF

def prepare():

    # Required step to register common, logging and generic configuration
# variables
logging.register_options(CONF)

register_options method 会帮助我们将上述配置文件中的配置项以默认值注册到 CONF 对象中:

logging.register_options(CONF)
>>> CONF.items()
[('default_log_levels', ['amqp=WARN', 'amqplib=WARN', 'boto=WARN', 'qpid=WARN', 'sqlalchemy=WARN', 'suds=INFO', 'oslo.messaging=INFO', 'iso8601=WARN', 'requests.packages.urllib3.connectionpool=WARN', 'urllib3.connectionpool=WARN', 'websocket=WARN', 'requests.packages.urllib3.util.retry=WARN', 'urllib3.util.retry=WARN', 'keystonemiddleware=WARN', 'routes.middleware=WARN', 'stevedore=WARN', 'taskflow=WARN', 'keystoneauth=WARN', 'oslo.cache=INFO', 'dogpile.core.dogpile=INFO']), ('verbose', True), ('watch_log_file', False), ('logging_default_format_string', '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s'), ('use_stderr', False), ('log_date_format', '%Y-%m-%d %H:%M:%S'), ('rate_limit_burst', 0), ('logging_context_format_string', '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s'), ('instance_format', '[instance: %(uuid)s] '), ('use_syslog', False), ('log_dir', None), ('publish_errors', False), ('logging_debug_format_suffix', '%(funcName)s %(pathname)s:%(lineno)d'), ('logging_exception_prefix', '%(asctime)s.%(msecs)03d %(process)d ERROR %(name)s %(instance)s'), ('syslog_log_facility', 'LOG_USER'), ('instance_uuid_format', '[instance: %(uuid)s] '), ('log_config_append', None), ('rate_limit_except_level', 'CRITICAL'), ('rate_limit_interval', 0), ('debug', False), ('log_file', None), ('logging_user_identity_format', '%(user)s %(tenant)s %(domain)s %(user_domain)s %(project_domain)s')]

所以我们在使用 oslo_log 时无需在程序代码中重复定义这些 options. 能够非常方便的通过修改配置文件中配置项的值, 来实现对 oslo_log 的管理.

NOTE: 如果在执行了 logging.register_options(CONF) 之后, 又在程序中重复定义了这些重名配置项的话, 会触发异常:

slo_config.cfg.DuplicateOptError: duplicate option: debug

在注册了配置项之后, 还可以通过调用 logging 的 setup() method 来指定该 logger 的作用域, 支持在一个程序中存在多个不同 domain 的 logger:

DOMAIN = 'demo'

def prepare():

    # Required setup based on configuration and domain
logging.setup(CONF, DOMAIN)

同时, oslo_log 也支持使用 logging 的 set_defaults method 来动态修改日志级别:

    # Optional step to set new defaults if necessary for
# * logging_context_format_string
# * default_log_levels extra_log_level_defaults = [
'dogpile=INFO',
'routes=INFO'
] logging.set_defaults(
default_log_levels=logging.get_default_log_levels() +
extra_log_level_defaults)

上述的 3 个 method 让程序中日志的使用变得更加灵活.

oslo.log 的日志级别

oslo.log 常用的 5 个日志级及其对应的 function:

from oslo_log import log as logging

LOG = logging.getLogger(__name__)

LOG.debug("Some key is %s", "key")

LOG.info("Create something %s start", "test")

LOG.exception("Failed to create something %(something)s as error %(err)s", {"something": "something", "err": "err"})

LOG.error("Failed to create something")

LOG.cretical("Fatal error as %s", "fatal")

oslo.log 的使用技巧

一般而言, 我会建议程序中的日志宜多不宜少, 但在上文也有提到, 大量的日志会对程序运行效率造成影响, 所以理解如何精准的在程序中的不同场景使用不同级别的日志就显得很有必要了.

推荐使用 LOG.debug 的地方

LOG.debug 一般用于对实参数据体精确度较为敏感的 function/method 语句块的首行, 让开发者能够快速的判断是否得到了预期实参. 所以这里需要打印出 module_name & function_name/method_name & 实参值.

EXAMPLE 1: REST API controller method 中接收到的 request info:

def index(self, req):
LOG.debug("Get server list with parameter %s,", req.GET())
...

EXAMPLE 2: 一些通过逻辑比较复杂的流程而得到的参数

...
# 执行一次复杂的排序操作
sort_result = _complex_sort(arr)
LOG.debug("Front list %(fro)s change to %(now)s after complex_sort.", {'fro': arr, 'now': sort_result})
...

推荐使用 LOG.info 的地方

LOG.info 一般用于标记业务逻辑步骤, 让运维/开发人员都能够快速判断程序流到了哪一个步, 所以这里需要打印 module_name & step_description & 业务对象的唯一标示

EXAMPLE: 当执行一个所需步骤大于等于二的程序流程时, 需要打印程序流开始到结束的过程

...
LOG.info("Create server %s start.", server.id)
...
LOG.info("Prepare image for server %s.", server.id)
...
LOG.info("Prepare network for server %s", server.id)
...
LOG.info("Create server %s successful", server.id)
...

推荐使用 LOG.exception 的地方

LOG.exception/LOG.error 一般用于 try-except 异常捕获语句块, 如果捕获到的是某一个准确的异常可以使用 LOG.error, 如果捕获的是某一类异常或全部异常时可以使用 LOG.exception 来打印出异常栈.

EXAMPLE: 在暴露的 RPC API 或者 HTTP API 方法中加入 exception 级别日志

def index(self, req):
...
try:
self.server_api.get_all_servers()
except Exception as err:
LOG.exception("Failed to get servers as error %s", six.text_type(err))
raise exc.HTTPInnevalError()
...

推荐使用 LOG.error 的地方

EXAMPLE: 同上, 但如果程序流没有再上一层的调用时, 也可以使用LOG.error, 因为已经不需要打印异常栈了.

try:
# do something
...
except Exception as err:
LOG.error("Failed to do something as error %s", six.text_type(err))
raise

推荐使用 LOG.cretical 的地方

LOG.cretical 一般用于当错误可能会导致程序进程崩溃等极其严重的逻辑块中

try:
# 执行一些危险操作
...
except Exception as err:
LOG.cretical("Failed to do something as error %s", six.text_type(err))
raise

OpenStack 实现技术分解 (6) 通用库 — oslo_log的更多相关文章

  1. OpenStack 实现技术分解 (7) 通用库 — oslo_config

    目录 目录 前文列表 扩展阅读 osloconfig argparse cfgpy class Opt class ConfigOpts CONF 对象的单例模式 前文列表 OpenStack 实现技 ...

  2. Openstack 实现技术分解 (4) 通用技术 — TaskFlow

    目录 目录 前文列表 扩展阅读 简介 基本概念 实现样例 最后 前文列表 Openstack 实现技术分解 (1) 开发环境 - Devstack 部署案例详解 Openstack 实现技术分解 (2 ...

  3. OpenStack 实现技术分解 (5) 应用开发 — 使用 OpenStackClients 进行二次开发

    文件夹 文件夹 前文列表 參考阅读 前言 OpenStackClients 使用 OpenStackClients 获取 project_client object 的 demo 调用 project ...

  4. Openstack 实现技术分解 (3) 开发工具 — VIM & dotfiles

    目录 目录 前文列表 扩展阅读 前言 插件管理 Vundle 主题 Solarized 浏览项目目录结构 Nerdtree Symbol 窗口 Tagbar 文件模糊查询 CtrlP 代码补全 You ...

  5. Openstack 实现技术分解 (1) 开发环境 — Devstack 部署案例详解

    目录 目录 前言 系统环境 Devstack 下载源码 配置文件 local.conf & localrc 简易的环境脚本 openrc 部署 Devstack 自动化部署流程 部署案例 单节 ...

  6. Openstack 实现技术分解 (2) 虚拟机初始化工具 — Cloud-Init & metadata & userdata

    目录 目录 前文列表 扩展阅读 系统环境 前言 Cloud-init Cloud-init 的配置文件 metadata userdata metadata 和 userdata 的区别 metada ...

  7. Google之Chromium浏览器源码学习——base公共通用库(一)

    Google的优秀C++开源项目繁多,其中的Chromium浏览器项目可以说是很具有代表性的,此外还包括其第三开发开源库或是自己的优秀开源库,可以根据需要抽取自己感兴趣的部分.在研究.学习该项目前的时 ...

  8. Google之Chromium浏览器源码学习——base公共通用库(三)

    本节将介绍base公共通用库中的containers,其包含堆栈.列表.集合.以及Most Recently Used cache(最近使用缓存模板). linked_list.h:一个简单的列表类型 ...

  9. Google之Chromium浏览器源码学习——base公共通用库(二)

    上次提到Chromium浏览器中base公共通用库中的内存分配器allocator,其中用到了三方库tcmalloc.jemalloc:对于这两个内存分配器,个人建议,对于内存,最好是自己维护内存池: ...

随机推荐

  1. handlebars杂记

      1.{{{caption}}}三个花括号,可以解析 空格 变成 ‘空格’.   2.数据是posts:[{  }]数组时候,可以用{{posts.length}}取得其数组长度   3.handl ...

  2. 万兴神剪手 Wondershare Filmora v9.2.11.6 简体中文版

    目录 1. 介绍 2. 简体中文9.2.1.10汉化版下载 3. 安装和激活说明 1. 介绍 万兴神剪手 Filmora 是一款界面简洁时尚.功能强大的视频编辑软件,它是深圳万兴科技公司近年来的代表作 ...

  3. Python3零基础入门学习视频+源码+课件+习题-小甲鱼

    目录 1. 介绍 2. 目录 3. 下载地址 1. 介绍 适用人群 完全零基础入门,不需要任何前置知识. 课程概述 本系列教程面向零基础的同学,是一个深入浅出,通俗易懂的Python3视频教程. 前半 ...

  4. mariadb索引、视图、关联查询、备份恢复、外键

    连接查询(两张表关联查询) 在sql语句中,- - 代表注释 内关联查询(查询两张表的交集) select * from 表1 inner join 表2 on 表1.id=表2.id(此处id是表1 ...

  5. Uva 10054 欧拉回路 打印路径

    看是否有欧拉回路 有的话打印路径 欧拉回路存在的条件: 如果是有向图的话 1.底图必须是连通图 2.最多有两个点的入度不等于出度 且一个点的入度=出度+1 一个点的入度=出度-1 如果是无向图的话 1 ...

  6. app 进入后台进行模糊处理

    金融类app防止信息在后台中被一些恶意截屏软件进行截屏,对进入后台的app做模糊处理 - (void)applicationWillResignActive:(UIApplication *)appl ...

  7. 关于Python你不得不知道的Python语言特点

    首先什么是语言?什么是编程? 准确来说是:定义计算机程序的语言,用来向计算机发送指令 个人理解:   语言:是一种交流的工具或者方式.比如我们的汉语普通话.各地的方言.外语中的英语.俄语.日语等.我们 ...

  8. 微信小程序-饮食日志_开发记录01

    今天主要了解微信小程序的框架结构以及环境部署等. 小程序的框架主要分为: js.json.wxss.wxml等 和java web的内容相似,主要了解内部代码的使用情况和语言方式. 主要写了页面的框架 ...

  9. 【NOIP2016提高A组五校联考2】running

    题目 小胡同学是个热爱运动的好孩子. 每天晚上,小胡都会去操场上跑步,学校的操场可以看成一个由n个格子排成的一个环形,格子按照顺时针顺序从0 到n- 1 标号. 小胡观察到有m 个同学在跑步,最开始每 ...

  10. C# 获得对象的命名空间 ?.

    A a = new A(); var t = a?.ToString(); //t = WebApplication1.Controllers.A //获得命名空间和类名 var t1 = (A)nu ...