python のlogの使い方　ファイルと画面に出力する/プログラム名を出力する

* pythonのログの機能の覚え書きです。
    * コードをそのまま実行するとログをファイルと画面に出力します。
    * 出力するログのレベル「info debug」などをコントロールするには「logger.setLevel(DEBUG)」を使います。
    * ログの関連情報「出力日時、ソースの場所等」の出力は「LogRecord 属性」を使い「setFormatter(Formatter」で行います。

* 「[結局どうする？ラクして無難にPython loggingを使うためのサンプルコードと使用例](https://ichiken-usa.blogspot.com/2021/06/python-logging.html)」を参考にしています。

## 参考
* [結局どうする？ラクして無難にPython loggingを使うためのサンプルコードと使用例](https://ichiken-usa.blogspot.com/2021/06/python-logging.html)
* [python用ロギング機能　LogRecord属性](https://docs.python.org/ja/3/library/logging.html#logrecord-attributes)  
* [Logging HOWTO](https://docs.python.org/ja/3/howto/logging.html#logging-basic-tutorial)
* [logging 基本的な使い方](https://www.python.ambitious-engineer.com/archives/693)

## logレベルの種類

|レベル|数値|いつ使うか|  
|:-|:-|:-|  
|DEBUG|10|おもに問題を診断するときにのみ関心があるような、詳細な情報。|  
|INFO|20|想定された通りのことが起こったことの確認。|
|WARNING|30|想定外のことが起こった、または問題が近く起こりそうである (例えば、'disk space low') ことの表示。
|ERROR|40|より重大な問題により、ソフトウェアがある機能を実行できないこと。|
|CRITICAL|50|プログラム自体が実行を続けられないことを表す、重大なエラー。|

## LogRecord 属性(setFormatter(Formatterで使う)
<table class="docutils align-default">
<colgroup>
<col style="width: 18%">
<col style="width: 28%">
<col style="width: 53%">
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>属性名</p></th>
<th class="head"><p>フォーマット</p></th>
<th class="head"><p>説明</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">args</span></code></p></td>
<td><p>このフォーマットを自分で使う必要はないでしょう。</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">msg</span></code> に組み合わせて <code class="docutils literal notranslate"><span class="pre">message</span></code> を生成するための引数のタプル、または、マージに用いられる辞書(引数が一つしかなく、かつそれが辞書の場合)。</p></td>
</tr>
<tr class="row-odd"><td><p>asctime</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(asctime)s</span></code></p></td>
<td><p><a class="reference internal" href="#logging.LogRecord" title="logging.LogRecord"><code class="xref py py-class docutils literal notranslate"><span class="pre">LogRecord</span></code></a> が生成された時刻を人間が読める書式で表したもの。デフォルトでは "2003-07-08 16:49:45,896" 形式 (コンマ以降の数字は時刻のミリ秒部分) です。</p></td>
</tr>
<tr class="row-even"><td><p>created</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(created)f</span></code></p></td>
<td><p><a class="reference internal" href="#logging.LogRecord" title="logging.LogRecord"><code class="xref py py-class docutils literal notranslate"><span class="pre">LogRecord</span></code></a> が生成された時刻 (<a class="reference internal" href="time.html#time.time" title="time.time"><code class="xref py py-func docutils literal notranslate"><span class="pre">time.time()</span></code></a> によって返される形式で)。</p></td>
</tr>
<tr class="row-odd"><td><p>exc_info</p></td>
<td><p>このフォーマットを自分で使う必要はないでしょう。</p></td>
<td><p>(<code class="docutils literal notranslate"><span class="pre">sys.exc_info</span></code> 風の) 例外タプルか、例外が起こっていない場合は <code class="docutils literal notranslate"><span class="pre">None</span></code>。</p></td>
</tr>
<tr class="row-even"><td><p>ファイル名</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(filename)s</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">pathname</span></code> のファイル名部分。</p></td>
</tr>
<tr class="row-odd"><td><p>funcName</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(funcName)s</span></code></p></td>
<td><p>ロギングの呼び出しを含む関数の名前。</p></td>
</tr>
<tr class="row-even"><td><p>levelname</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(levelname)s</span></code></p></td>
<td><p>メッセージのための文字のロギングレベル (<code class="docutils literal notranslate"><span class="pre">'DEBUG'</span></code>, <code class="docutils literal notranslate"><span class="pre">'INFO'</span></code>, <code class="docutils literal notranslate"><span class="pre">'WARNING'</span></code>, <code class="docutils literal notranslate"><span class="pre">'ERROR'</span></code>, <code class="docutils literal notranslate"><span class="pre">'CRITICAL'</span></code>)。</p></td>
</tr>
<tr class="row-odd"><td><p>levelno</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(levelno)s</span></code></p></td>
<td><p>メッセージのための数値のロギングレベル (<code class="xref py py-const docutils literal notranslate"><span class="pre">DEBUG</span></code>, <code class="xref py py-const docutils literal notranslate"><span class="pre">INFO</span></code>, <code class="xref py py-const docutils literal notranslate"><span class="pre">WARNING</span></code>, <code class="xref py py-const docutils literal notranslate"><span class="pre">ERROR</span></code>, <code class="xref py py-const docutils literal notranslate"><span class="pre">CRITICAL</span></code>)。</p></td>
</tr>
<tr class="row-even"><td><p>lineno</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(lineno)d</span></code></p></td>
<td><p>ロギングの呼び出しが発せられたソース行番号 (利用できる場合のみ)。</p></td>
</tr>
<tr class="row-odd"><td><p>message</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(message)s</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">msg</span> <span class="pre">%</span> <span class="pre">args</span></code> として求められた、ログメッセージ。 <a class="reference internal" href="#logging.Formatter.format" title="logging.Formatter.format"><code class="xref py py-meth docutils literal notranslate"><span class="pre">Formatter.format()</span></code></a> が呼び出されたときに設定されます。</p></td>
</tr>
<tr class="row-even"><td><p>module</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(module)s</span></code></p></td>
<td><p>モジュール (<code class="docutils literal notranslate"><span class="pre">filename</span></code> の名前部分)。</p></td>
</tr>
<tr class="row-odd"><td><p>msecs</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(msecs)d</span></code></p></td>
<td><p><a class="reference internal" href="#logging.LogRecord" title="logging.LogRecord"><code class="xref py py-class docutils literal notranslate"><span class="pre">LogRecord</span></code></a> が生成された時刻のミリ秒部分。</p></td>
</tr>
<tr class="row-even"><td><p>msg</p></td>
<td><p>このフォーマットを自分で使う必要はないでしょう。</p></td>
<td><p>元のロギングの呼び出しで渡されたフォーマット文字列。 <code class="docutils literal notranslate"><span class="pre">args</span></code> と合わせて、 <code class="docutils literal notranslate"><span class="pre">message</span></code> 、または任意のオブジェクトを生成します (<a class="reference internal" href="../howto/logging.html#arbitrary-object-messages"><span class="std std-ref">任意のオブジェクトをメッセージに使用する</span></a> 参照)。</p></td>
</tr>
<tr class="row-odd"><td><p>name</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(name)s</span></code></p></td>
<td><p>ロギングに使われたロガーの名前。</p></td>
</tr>
<tr class="row-even"><td><p>pathname</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(pathname)s</span></code></p></td>
<td><p>ロギングの呼び出しが発せられたファイルの完全なパス名 (利用できる場合のみ)。</p></td>
</tr>
<tr class="row-odd"><td><p>process</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(process)d</span></code></p></td>
<td><p>プロセス ID (利用可能な場合のみ)。</p></td>
</tr>
<tr class="row-even"><td><p>processName</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(processName)s</span></code></p></td>
<td><p>プロセス名 (利用可能な場合のみ)。</p></td>
</tr>
<tr class="row-odd"><td><p>relativeCreated</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(relativeCreated)d</span></code></p></td>
<td><p>logging モジュールが読み込まれた時刻に対する、LogRecord が生成された時刻を、ミリ秒で表したもの。</p></td>
</tr>
<tr class="row-even"><td><p>stack_info</p></td>
<td><p>このフォーマットを自分で使う必要はないでしょう。</p></td>
<td><p>現在のスレッドでのスタックの底からこのレコードの生成に帰着したログ呼び出しまでのスタックフレーム情報 (利用可能な場合)。</p></td>
</tr>
<tr class="row-odd"><td><p>thread</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(thread)d</span></code></p></td>
<td><p>スレッド ID (利用可能な場合のみ)。</p></td>
</tr>
<tr class="row-even"><td><p>threadName</p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">%(threadName)s</span></code></p></td>
<td><p>スレッド名 (利用可能な場合のみ)。</p></td>
</tr>
</tbody>
</table>








## 画面とファイルにログを出力するためのfunction(set_log_config)の定義
``` python
from logging import StreamHandler, DEBUG, INFO, Formatter
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
import os
 
def set_log_config(logger, dir, filename):
 
    # ---- Prepare log folder ----
    os.makedirs(dir, exist_ok=True) 
    filepath = dir + filename
 
    # ---- handler1: For terminal ----
    handler1 = StreamHandler()
    handler1.setLevel(DEBUG)
    handler1.setFormatter(Formatter("[%(asctime)s][%(levelname)8s][%(name)s][%(filename)s][%(funcName)s][line %(lineno)d] %(message)s"))
    # ---- handler2: For log file ----
    #handler2 = TimedRotatingFileHandler(filename = filepath, when='midnight', backupCount=30, encoding='utf-8') # If you want to rotate file by time, use this
    handler2 = RotatingFileHandler(filename = filepath, maxBytes = 1048576, backupCount = 10, encoding='utf-8', ) # 1MB, keep 10 files
    handler2.setLevel(INFO)
    handler2.setFormatter(Formatter("[%(asctime)s][%(levelname)8s][%(name)s][%(filename)s][%(funcName)s][line %(lineno)d] %(message)s"))
 
    # ---- Set log config ----
    logger.setLevel(INFO)
    logger.addHandler(handler1)
    logger.addHandler(handler2)
    logger.propagate = False
    
``` 
## logの初期化
``` python
#準備
from logging import getLogger
dir = 'Log/'
logger = getLogger('test')
set_log_config(logger, dir, 'main.log' )
```

## テスト１ 
logger.setLevelがそのままなので、set_log_config内で定義されるINFOがそのまま使われる。
そのため、loglevel info以上のログが出力される

``` python
def test_fnc1():
    #set_log_config内でのsetLevelは　INFO　
    logger.debug('DEBUG')
    logger.info('INFO')
    logger.warning('WARNING')
    logger.error('ERROR')
    logger.critical('CRITICAL')
    try:
        1/0
    except Exception as e:
        logger.exception('Test: exception')

test_fnc1()
```

## 出力
INFO以上（DEBUG以外）が出力される
```
ython3 log_test.py
[2022-05-31 19:29:14,659][    INFO][test][log_test.py][test_fnc1][line 41] INFO
[2022-05-31 19:29:14,659][ WARNING][test][log_test.py][test_fnc1][line 42] WARNING
[2022-05-31 19:29:14,659][   ERROR][test][log_test.py][test_fnc1][line 43] ERROR
[2022-05-31 19:29:14,659][CRITICAL][test][log_test.py][test_fnc1][line 44] CRITICAL
[2022-05-31 19:29:14,659][   ERROR][test][log_test.py][test_fnc1][line 48] Test: exception
Traceback (most recent call last):
  File "log_test.py", line 48, in test_fnc1
    1/0
ZeroDivisionError: division by zero
```


## テスト２ 
logger.setLevelをDEBUGにする。
そのため、loglevel DEBUG以上のログが出力される

``` python
def test_fnc2():
    #setLevelをDEBUGにする。
    logger.setLevel(DEBUG)

    logger.debug('DEBUG')
    logger.info('INFO')
    logger.warning('WARNING')
    logger.error('ERROR')
    logger.critical('CRITICAL')
    try:
        1/0
    except Exception as e:
        logger.exception('Test: exception')

test_fnc2()
```

## 出力
DEBUG以上（全て）が出力される
```
[2022-05-31 19:29:14,659][   DEBUG][test][log_test.py][test_fnc2][line 56] DEBUG
[2022-05-31 19:29:14,659][    INFO][test][log_test.py][test_fnc2][line 57] INFO
[2022-05-31 19:29:14,659][ WARNING][test][log_test.py][test_fnc2][line 58] WARNING
[2022-05-31 19:29:14,659][   ERROR][test][log_test.py][test_fnc2][line 59] ERROR
[2022-05-31 19:29:14,659][CRITICAL][test][log_test.py][test_fnc2][line 60] CRITICAL
[2022-05-31 19:29:14,660][   ERROR][test][log_test.py][test_fnc2][line 66] Test: exception
Traceback (most recent call last):
  File "log_test.py", line 67, in test_fnc2
    1/0
ZeroDivisionError: division by zero
```