# python日志

> 简化和实用的处理日志请参考[Python日志处理基础](https://github.com/huataihuang/cloud-atlas-draft/tree/6d95ec0946a276d6dafb4bc70b6dd3402f069a64/develop/python/log/python_logging_basic/README.md)

Python非常容易实现日志功能，因为提供了`logging`模块方便开发者在代码事件中添加日志调用。事件是通过可选的包含变量的描述信息所组成。事件还有一个重要的特性是可以设置日志级别。

## 什么时候使用`logging`

`logging`提供一系列简化的日志功能，提供级别有`debug()`, `info()`, `warning()`, `error()` 和 `critical()`。

## 简单案例

```python
import logging
logging.warning('Watch out!')  # will print a message to the console
logging.info('I told you so')  # will not print anything
```

上述没有指定输出到文件，则默认在终端输出。

## 日志记录到文件

```python
import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
```

注意：在日志文件中会同时记录日志级别，所以默认显示如下：

```
DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
```

如果希望通过命令行参数来设置日志级别，例如`--log=INFO`，则可以使用如下方法获取参数：

```python
getattr(logging, loglevel.upper())
```

以下是一个案例：

```python
# assuming loglevel is bound to the string value obtained from the
# command line argument. Convert to upper case to allow the user to
# specify --log=DEBUG or --log=debug
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
    raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=numeric_level, ...)
```

> 这里调用了`basicConfig()`来设置日志级别

## 从多个模块进行日志

如果程序包含多个模块，以下是组织`logging`的一个案例：

```python
# myapp.py
import logging
import mylib

def main():
    logging.basicConfig(filename='myapp.log', level=logging.INFO)
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()
```

```python
# mylib.py
import logging

def do_something():
    logging.info('Doing something')
```

此时运行`myapp.py`将在日志文件`myapp.log`看到如下内容：

```
INFO:root:Started
INFO:root:Doing something
INFO:root:Finished
```

## 日志变量值

对于比阿亮数据，可以将变量作为参数传递：

```
import logging
logging.warning('%s before you %s', 'Look', 'leap!')
```

## 日志格式

```python
import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('This message should appear on the console')
logging.info('So should this')
logging.warning('And this, too')
```

## 在日志中显示时间

```python
import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')
```

则日志如下：

```
2010-12-12 11:41:42,612 is when this event was logged.
```

默认时间格式是ISO8601，即类似如上。如果需要控制日期格式，可以使用`basicconfig`:

```python
import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')
```

则日志内容如下：

```
12/12/2010 11:46:36 AM is when this event was logged.
```

## 模块话日志

`logging`通过调用`Logger`累的方法来实现，所以创建`logger`对象：

```python
logger = logging.getLogger(__name__)
```

## 配置Logging

```python
import logging

# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
```

运行以上案例输入：

```bash
$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message
```

以下哪里创建`logger`和`handler`以及`formatter`

```python
import logging
import logging.config

logging.config.fileConfig('logging.conf')

# create logger
logger = logging.getLogger('simpleExample')

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
```

以下是`logging.conf`配置文件：

```
[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=
```

则日志输出如下

```
$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message
2005-03-19 15:38:55,979 - simpleExample - INFO - info message
2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message
2005-03-19 15:38:56,055 - simpleExample - ERROR - error message
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message
```

## 简单的日志

* 简单记录日志

```python
import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
```

* 日志中增加时间戳

```python
import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')
```

* 设置日志级别：

```python
import logging
logger = logging.getLogger('myapp')
hdlr = logging.FileHandler('/var/tmp/myapp.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.WARNING)
```

然后通过如下方法记录日志

```python
logger.error('We have a problem')
logger.info('While this is just chatty')
```

> 参考 [Basic example - log to a file](https://docs.python.org/2.3/lib/node304.html)

## 多线程日志记录

以下案例是主线程（initlal）和其他线程同时记录日志的方法

```
import logging
import threading
import time

def worker(arg):
    while not arg['stop']:
```

> 参考 [Logging Cookbook](https://docs.python.org/2/howto/logging-cookbook.html)

## 日志文件中的空行

```python
g_tsar_log="/tmp/tsar.logging"
logging.basicConfig(filename=g_tsar_log,format='%(asctime)s %(message)s')
logging.warning(res)
```

但是发现日志中每行记录后面都有一个空白行，所以记录日志中增加`strip()`去除多余回行

```python
logging.warning(res.strip())
```

## 参考

* [Logging HOWTO](https://docs.python.org/2/howto/logging.html)
* [Logging Cookbook](https://docs.python.org/3/howto/logging-cookbook.html)
* [Logging HOWTO:Basic Logging Tutorial](https://docs.python.org/2/howto/logging.html)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://huataihuang.gitbook.io/cloud-atlas-draft/develop/python/startup/logging.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
