> For the complete documentation index, see [llms.txt](https://docs.xiwind-corp.com/tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.xiwind-corp.com/tech/python-library/logging-ri-zhi.md).

# logging 【日誌】

## Logging

***

Logging library module 允許開發人員將日誌功能整合到他們的應用程序中，從而在運行時記錄事件、錯誤和其他重要信息。

<figure><img src="/files/XtIeB1WdG7MNF666q6LW" alt="Logging library module"><figcaption><p>Logging library module</p></figcaption></figure>

`print()` 是沒有嚴重性概念的普通文本。而 Logging 具有 4 個級別的日誌訊息嚴重性或重要性，有助於從訊息忽略較低嚴重性的訊息。

<table><thead><tr><th width="344">Log 級別</th><th>說明</th></tr></thead><tbody><tr><td><code>info(msg, *args, **kwargs)</code></td><td>監視和理解應用程序行為。(例如: 報告流程、事件、顯示狀態)</td></tr><tr><td><code>debug(msg, *args, **kwargs)</code></td><td>用於開發和測試階段。(例如: 記錄變數值、路徑、內部狀態)</td></tr><tr><td><code>warning(msg, *args, **kwargs)</code></td><td>表示問題已經發生或即將發生，但應用程序仍可以繼續運行。</td></tr><tr><td><code>error(msg, *args, **kwargs)</code></td><td>表示特定操作或任務失敗或遇到異常。錯誤通常會阻止應用程序按預期運行或產生不正確的結果。</td></tr><tr><td><code>critical(msg, *args, **kwargs)</code></td><td>表示對應用程序的功能有重大的嚴重錯誤。當此類事件發生時，應用程序可能處於不穩定或不可恢復的狀態。</td></tr></tbody></table>

{% code title="PYTHON" %}

```python
import logging

# log
logging.info("[This is a info]") 
logging.warning("[This is a warning]")
logging.error("[This is a error]")
logging.critical("[This is a critical]")
```

{% endcode %}

**執行結果**

```TXT
WARNING:root:[This is a warning]
ERROR:root:[This is a error]
CRITICAL:root:[This is a critical]
```

### 最佳的紀錄工具

***

<table><thead><tr><th width="301">任務</th><th>工具</th></tr></thead><tbody><tr><td><strong>顯示在 Terminal</strong></td><td><code>print()</code></td></tr><tr><td><strong>監控或故障調查</strong></td><td><code>logging.info()</code>、 <code>logging.debug()</code></td></tr><tr><td><strong>發出事件的警告</strong></td><td><code>logging.warning()</code>(注意該事件)、<code>warnings.warn()</code>；(必須修正該事件)</td></tr><tr><td><strong>報告事件的錯誤</strong></td><td>引發 Exception。詳見: [[exception 【異常】]]</td></tr><tr><td><strong>抑制錯誤而不引發異常</strong><br>(例如: 伺服器中的 Threading)</td><td><code>logging.error()</code>, <code>logging.exception()</code>, <code>logging.critical()</code></td></tr></tbody></table>

## basicConfig

***

**快速設定多個屬性** (logger、FileHandler...)

{% code title="PYTHON" %}

```python
logging.basicConfig(**kwargs)
```

{% endcode %}

常用的參數：

<table><thead><tr><th width="171">輸出級別控制</th><th>說明</th></tr></thead><tbody><tr><td>filename</td><td>寫入日誌訊息的檔案名稱，否則輸出在 Terminal。</td></tr><tr><td>filemode</td><td>寫入模式，即 <code>open()</code>。如果指定 <code>filename</code>，則使用 <code>"a"</code>。</td></tr><tr><td>format</td><td>需要寫入的日誌訊息。詳見: <a href="https://docs.python.org/3/library/logging.html#logrecord-attributes">LogRecord attributes</a>。</td></tr><tr><td>datefmt</td><td>指定的日期/時間格式。與 <a href="https://docs.python.org/zh-tw/3/library/time.html#time.strftime">time.strftime()</a> 的格式相同。</td></tr><tr><td>style</td><td>日誌訊息的格式(<code>'%'</code>, <code>'{'</code>, <code>'$'</code>)，預設為<code>%</code>。</td></tr><tr><td>level</td><td>設置根記錄器級別，不會記錄較小的級別的日誌。級別大小為: <code>DEBUG</code> &#x3C; <code>INFO</code> &#x3C; <code>WARNING</code> &#x3C; <code>ERROR</code> &#x3C; <code>CRITICAL</code>。</td></tr><tr><td>encoding</td><td>編碼模式。常使用 <code>encoding="UTF-8"</code>。</td></tr></tbody></table>

### fileConfig

`fileConfig()` 允許使用 Configuration file 設定日誌記錄，而不是透過程式碼的方式定義。 支持的格式有：.ini、.yaml、.json。

透過使用 `logging.config.fileConfig()`，您可以將日誌記錄設定與您的程式碼分開，從而在不修改代碼本身的情況下更輕鬆地管理。此外，您可以為不同的環境或模塊使用不同的 Configuration file。

{% code title="PYTHON" %}

```python
logging.config.fileConfig(fname, 
                          defaults=None, 
                          disable_existing_loggers=True, 
                          encoding=None)
```

{% endcode %}

## Logger

***

建立 Logger ，可以過濾 log 資訊。

**設定 Logger。**

{% code title="PYTHON" %}

```python
logging.getLogger(name=None)
logging.getLogger(loggeyr_1)           # Ex1.
logging.getLogger(logger_1.logger_1_2) # Ex2. 可以使用`.` 代表 Logger 的階層。
```

{% endcode %}

{% hint style="success" %}
**建議使用 `logger.getLogger(__name__)。`**

因為它將返回當前的 package 命名空間。如果有多個.py 檔案時很有用。
{% endhint %}

## Handler

***

如何處理 Logger 的資訊。

**將 Logger 輸出到 File。**

{% code title="" %}

```python
logging.FileHandler(filename, 
                    mode='a', 
                    encoding=NoHne, 
                    delay=False, 
                    errors=None)
```

{% endcode %}

**將 Logger 輸出到 Terminal。**

```python
logging.StreamHandler(stream=None)
```

**將 Logger 輸出到 Web 。**

{% code title="PYTHON" %}

```python
handlers.HTTPHandler(host, 
                     url, 
                     method='GET', 
                     secure=False, 
                     credentials=None, 
                     context=None)
```

{% endcode %}

**設定 Handler 屬性。** 其他屬性詳見：[logging.Handler](https://docs.python.org/zh-tw/3/library/logging.html#logging.Handler)

{% code title="PYTHON" %}

```python
file_handler = logging.FileHandler('app.log')

# =========================================================
# level
file_handler.setLevel(logging.INFO)

# Formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
```

{% endcode %}

## Filter

***

Filter class 允許您自定義過濾器以根據特定條件有選擇地過濾掉日誌記錄。 它提供了一種微調日誌記錄系統中日誌記錄處理的方法。

Filter Filter class 是一個 base class， 它提供了一個 `filter(record)` 的方法，您需要在子 class 中覆蓋該方法。 `filter()` 方法將日誌記錄作為輸入，如果返回 True，則處理該日誌；則返回 True，如果返回 False，則跳過該日誌 。

{% hint style="success" %}
**過濾器與日誌級別將會一起運行，日誌級別具有較高的優先度。**&#x20;

例如：如果日誌級別設置為 WARNING，則只有 WARNING 或更高級別的日誌記錄才會被處理，而不管過濾器的設定。
{% endhint %}

## 範例

***

### basicConfig

{% code title="PYTHON" %}

```python
'''basicConfig'''

import logging

# 建立 logger 和 FileHandler
logging.basicConfig(filename = 'log.txt',
                    level = logging.DEBUG,
                    format = '{asctime}: {levelname}: {name}: {message}',
                    style='{')
                    #format = '%(asctime)s:%(levelname)s:%(name)s:%(message)s')

logging.debug('Debug message')
logging.info('Info message')
logging.warning('Warning message')
logging.error('Error message')
logging.critical('Critical message')
```

{% endcode %}

**執行結果：**

<figure><img src="/files/Wjof34HUmKhhbQFVz9Kj" alt="執行結果" width="375"><figcaption><p><strong>執行結果</strong></p></figcaption></figure>

{% code title="PYTHON" %}

```python
'''
basicConfig ->logger
            ->logger
'''

import logging

# 設定多個屬性
logging.basicConfig(filename='app.log', 
                    level=logging.INFO, 
                    format='%(asctime)s - %(levelname)s - %(message)s')

# =========================================================
# logger 1 
logger = logging.getLogger('my_logger_1')
logger.setLevel(logging.DEBUG)

# log
logger.debug('This is a debug message.')         # ✔️✔️
logger.info('This is an informational message.') # ✔️✔️
logger.warning('This is a warning message.')     # ✔️✔️

# =========================================================
# logger 2
logger_2 = logging.getLogger('my_logger_2') 
logger_2.setLevel(logging.WARNING)

# log
logger_2.debug('This is a debug message.')
logger_2.info('This is an informational message.')
logger_2.warning('This is a warning message.')   # ✔️✔️

```

{% endcode %}

**執行結果:**

{% code title="app.log" %}

```
2023-07-19 18:18:20,495 - DEBUG - This is a debug message.
2023-07-19 18:18:20,495 - INFO - This is an informational message.
2023-07-19 18:18:20,495 - WARNING - This is a warning message.
2023-07-19 18:18:20,495 - WARNING - This is a warning message.
```

{% endcode %}

### fileConfig

***

{% code title="INI" %}

```ini
[loggers]
keys=root

[handlers]
keys=fileHandler, consoleHandler

[formatters]
keys=defaultFormatter

[logger_root]
level=DEBUG
handlers=fileHandler, consoleHandler

[handler_fileHandler]     # 輸出到 檔案
class=FileHandler
level=DEBUG
formatter=defaultFormatter
args=('example.log', 'a')

[handler_consoleHandler]  # 輸出到 Terminal
class=StreamHandler
level=DEBUG
formatter=defaultFormatter
args=(sys.stdout,)

[formatter_defaultFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S
```

{% endcode %}

{% code title="YAML" %}

```yaml
version: 1
formatters:
  defaultFormatter:
    format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    datefmt: "%Y-%m-%d %H:%M:%S"
    
handlers:
  fileHandler:         # 輸出到 檔案
    class: logging.FileHandler
    level: DEBUG
    formatter: defaultFormatter
    filename: app.log
  consoleHandler:      # 輸出到 Terminal
    class: logging.StreamHandler
    level: DEBUG
    formatter: defaultFormatter
    stream: ext://sys.stdout
    
loggers:
    root:
        level: DEBUG
        handlers: [fileHandler, consoleHandler]
        propagate: yes
```

{% endcode %}

{% code title="PYTHON" %}

```python
import yaml
import logging
import logging.config

# 載入 configuration_file.conf，建立 logger
logging.config.fileConfig('logging.conf')
logger = logging.getLogger(__name__)

# 寫入 Log
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')

# =========================================================
# 載入 configuration_file.yaml，建立 logger
with open('logging.yaml', 'r') as f:
    config = yaml.safe_load(f.read())
    logging.config.dictConfig(config)
logger = logging.getLogger(__name__)

# 寫入 Log
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
```

{% endcode %}

### logger

***

{% code title="PYTHON" %}

```python
'''getLogger('my_logger') ->FileHandler'''

import logging

# 建立 logger
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

# 建立 FileHandler
file_handler = logging.FileHandler('app.log')
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)

# 加入 handler
logger.addHandler(file_handler)

# 寫入 log
logger.info('This is an informational message.')
logger.warning('This is a warning message.')

```

{% endcode %}

**執行結果:**

{% code title="app.log" %}

```
2023-07-19 17:58:12,393 - INFO - This is an informational message.
2023-07-19 17:58:12,393 - WARNING - This is a warning message.
```

{% endcode %}

{% code title="PYTHON" %}

```python
'''getLogger(__name__) ->FileHandler'''
import logging

# 建立 logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(name)s - %(asctime)s - %(levelname)s - %(message)s')

# 建立 FileHandler
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.WARNING)
file_handler.setFormatter(formatter)

# 加入 handler
logger.addHandler(file_handler)

# log
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')

```

{% endcode %}

**執行結果:**

{% code title="app.log" %}

```
__main__ - 2023-07-19 18:01:14,806 - WARNING - This is a warning message
__main__ - 2023-07-19 18:01:14,806 - ERROR - This is an error message
```

{% endcode %}

### Handler

***

{% code title="PYTHON" %}

```python
import logging

# 設定多個屬性
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

# =========================================================
# 建立 FileHandler
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)

# =========================================================
# 建立 StreamHandler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(formatter)

# =========================================================
# 加入 handler
logger.addHandler(file_handler)
logger.addHandler(console_handler)

# =========================================================
# 寫入 log
logger.debug('This is a debug message.')
logger.info('This is an informational message.')
logger.warning('This is a warning message.')
```

{% endcode %}

**執行結果:**

```
2023-07-19 18:04:38,094 - DEBUG - This is a debug message.
2023-07-19 18:04:38,094 - INFO - This is an informational message.
2023-07-19 18:04:38,094 - WARNING - This is a warning message.
```

{% code title="app.log" %}

```
2023-07-19 18:04:38,094 - INFO - This is an informational message.
2023-07-19 18:04:38,094 - WARNING - This is a warning message.
```

{% endcode %}

### Filter

***

{% code title="PYTHON" %}

```python
import logging

class LoggerFilter(logging.Filter):
    def filter(self, record):
        return record.levelno >= logging.WARNING


class HandlerFilter(logging.Filter):
    def filter(self, record):
        if record.msg.find('abc') == -1:
            return True
        return False
        
# 建立 Handler
logger = logging.getLogger('test')
logger.setLevel(logging.INFO)

# 建立 Handler
stream_handler = logging.StreamHandler()
logger.addHandler(stream_handler)

# =========================================================
logger.warning('this is warning-1')    # ✔️✔️
logger.info('this is info-1')          # ✔️✔️

# =========================================================
logger.addFilter(LoggerFilter())
logger.warning('this is warning-2')    # ✔️✔️
logger.info('this is info-2')          # Skip this log

# =========================================================
stream_handler.addFilter(HandlerFilter())
logger.warning('this is warning-3')    # ✔️✔️
logger.info('this is info-3')          # Skip this log
logger.warning('this is warning-abc')  # Skip this log
```

{% endcode %}

**執行結果:**

```
this is warning-1
this is info-1
this is warning-2
this is warning-3
```

### Logger Decorator

***

{% code title="PYTHON" %}

```python
import logging

def logger(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Running \"{func.__name__}\" with arguments {args} and kwargs {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"{func.__name__} returned {result}")
        return result
    return wrapper

logging.basicConfig(level=logging.INFO)

@logger
def adder(x, y):
    return x + y

print(adder(10, 5))

```

{% endcode %}

**執行結果:**

```
INFO:root:Running "adder" with arguments (10, 5) and kwargs {}
INFO:root:adder returned 15
15
```

{% code title="PYTHON" %}

```python
import logging

class LevelFilter(logging.Filter):
    """
    This is a custom filter class that filters out log messages below certain level.
    """
    def filter(self, record):
        return record.levelno >= logging.INFO

def setup_logger():
    logger = logging.getLogger('custom_logger')
    logger.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    # Create console handler with a higher log level
    ch = logging.StreamHandler()
    ch.setLevel(logging.INFO)
    ch.setFormatter(formatter)
    logger.addHandler(ch)

    # Create file handler which logs even debug messages
    fh = logging.FileHandler('log_file.log')
    fh.setLevel(logging.INFO)
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    
    # Add filter to the logger
    logger.addFilter(LevelFilter())
    return logger

logger = setup_logger()

def logger_decorator(func):
    def wrapper(*args, **kwargs):
        logger.info(f"Running \"{func.__name__}\" with arguments {args} and kwargs {kwargs}")
        result = func(*args, **kwargs)
        logger.info(f"{func.__name__} returned {result}")
        return result
    return wrapper

@logger_decorator
def adder(x, y):
    return x + y

print(adder(10, 5))
```

{% endcode %}

**執行結果:**

```
2023-07-19 18:40:51,589 - custom_logger - INFO - Running "adder" with arguments (10, 5) and kwargs {}
2023-07-19 18:40:51,589 - custom_logger - INFO - adder returned 15
15
```

## 參考資料

***

**Python Doc**

&#x20;[基礎 Logging 指南 — Python 3.11.3 說明文件](https://docs.python.org/zh-tw/3/howto/logging.html#logging-basic-tutorial)

[進階日誌教程 — Python 3.11.3 說明文件](https://docs.python.org/zh-tw/3/howto/logging.html#logging-advanced-tutorial)

[日誌操作手冊 — Python 3.11.3 說明檔](https://docs.python.org/zh-tw/3/howto/logging-cookbook.html#logging-cookbook)

[logging.basicConfig — Python 3.11.3 說明文件](https://docs.python.org/zh-tw/3/library/logging.html#logging.basicConfig)

[logging.getLogger — Python 3.11.3 說明文件](https://docs.python.org/zh-tw/3/library/logging.html#logging.getLogger)

[handlers — Python 3.11.3 文檔](https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers)

[config.fileConfig — Python 3.11.3 documentation](https://docs.python.org/3/library/logging.config.html#logging.config.fileConfig)

[filter objects](https://docs.python.org/3/library/logging.html#filter-objects)

**Blog**

&#x20;[Logging in Python - MachineLearningMastery.com](https://machinelearningmastery.com/logging-in-python/)

[Logging in Python ——真正的 Python](https://realpython.com/python-logging/)

[Python 日誌記錄基礎：方法教程、示例及更多 - Sematext](https://sematext.com/blog/python-logging/)

[在 Python 中登錄 - MachineLearningMastery.com](https://machinelearningmastery.com/logging-in-python/)

[如何使用logging.Filter？ | Python笔记](https://www.pynote.net/archives/2006)

[Python logging 模組之 logging.basicConfig 用法和參數詳解\_Sir靜堂的博客-CSDN博客](https://blog.csdn.net/colinlee19860724/article/details/90965100)

[Python - 日誌 (logging) 模組 | Titangene Blog](https://titangene.github.io/article/python-logging.html)
