# 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)


---

# 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://docs.xiwind-corp.com/tech/python-library/logging-ri-zhi.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.
