Stage 2 プログラムの土台を作る
2-1 ロガーの設定をする
まずはコードの config.py というファイルをご覧ください。
#! /usr/bin/env python3
# config.py
# programmed by Saito-Saito-Saito
# explained in https://Saito-Saito-Saito.github.io/reversi
# last update: 2/7/2020
import sys
from logging import getLogger, StreamHandler, FileHandler, Formatter, DEBUG, INFO, WARNING, ERROR, CRITICAL
### LOG SETTINGS
DEFAULT_LOG_ADDRESS = 'log.txt'
DEFAULT_LOG_FORMAT = Formatter('%(asctime)s - %(levelname)s - logger:%(name)s - %(filename)s - L%(lineno)d - %(funcName)s - %(message)s')
# set up function
def setLogger(name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_ADDRESS, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=CRITICAL, streamformat=DEFAULT_LOG_FORMAT):
logger = getLogger(name)
logger.setLevel(level)
# file handler
fhandler = fhandler or FileHandler(filename, mode=filemode)
fhandler.setLevel(fhandler_level)
fhandler.setFormatter(fileformat)
logger.addHandler(fhandler)
# stream handler
shandler = shandler or StreamHandler()
shandler.setLevel(shandler_level)
shandler.setFormatter(streamformat)
logger.addHandler(shandler)
return logger
logger = setLogger(__name__)
# board is 8 * 8
SIZE = 8
if int(SIZE / 2) != SIZE / 2:
logger.error('SIZE VALUE HAS TO BE EVEN')
print('SYSTEM ERROR')
sys.exit()
# rows & columns index
ROW = 0
COL = 1
# piece values
EMPTY = 0
B = BLACK = 1
W = WHITE = -1
# game proceeding/set
GAME_PRC = 0
GAME_SET = 1
# for return
SUCCEEDED = True
FAILED = False
# direction is represented as follows: [toROW - frROW, toCOL - frCOL]
WHOLE_DIRECTION = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1]
]
# whether the index is in the board
def InBoard(subject):
if 0 <= subject < SIZE:
return True
else:
return False
クソなげーなおい
冒頭でいろいろインポートしていますね。
import sys
from logging import getLogger, StreamHandler, FileHandler, Formatter, DEBUG, INFO, WARNING, ERROR, CRITICAL
下段の logging からのインポートについては、logger と logging の違いを明確にするために、logging という文字を書かなくていいようにしています。その意義についてはこちらの記事で大いに力説されていますので、是非ご覧ください。
~~~~~~~~~~~~~~~
まずはログの設定をします。
### LOG SETTINGS
DEFAULT_LOG_ADDRESS = 'log.txt'
DEFAULT_LOG_FORMAT = Formatter('%(asctime)s - %(levelname)s - logger:%(name)s - %(filename)s - L%(lineno)d - %(funcName)s - %(message)s')
# set up function
def setLogger(name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_ADDRESS, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=CRITICAL, streamformat=DEFAULT_LOG_FORMAT):
logger = getLogger(name)
logger.setLevel(level)
# file handler
fhandler = fhandler or FileHandler(filename, mode=filemode)
fhandler.setLevel(fhandler_level)
fhandler.setFormatter(fileformat)
logger.addHandler(fhandler)
# stream handler
shandler = shandler or StreamHandler()
shandler.setLevel(shandler_level)
shandler.setFormatter(streamformat)
logger.addHandler(shandler)
return logger
こんなに長いの何やるんだよ
大したことないですって。見てけばわかりますから。
~~~~~~~~~~~~~~~
はじめにログを出力するデフォルトのファイルの名前、フォーマットを設定しておきます。
### LOG SETTINGS
DEFAULT_LOG_ADDRESS = 'log.txt'
DEFAULT_LOG_FORMAT = Formatter('%(asctime)s - %(levelname)s - logger:%(name)s - %(filename)s - L%(lineno)d - %(funcName)s - %(message)s')
今後ロガーは基本的にこのデフォルトに従って出力します。
下に行くと関数がありますよね。
# set up function
def setLogger(name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_ADDRESS, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=CRITICAL, streamformat=DEFAULT_LOG_FORMAT):
logger = getLogger(name)
logger.setLevel(level)
# file handler
fhandler = fhandler or FileHandler(filename, mode=filemode)
fhandler.setLevel(fhandler_level)
fhandler.setFormatter(fileformat)
logger.addHandler(fhandler)
# stream handler
shandler = shandler or StreamHandler()
shandler.setLevel(shandler_level)
shandler.setFormatter(streamformat)
logger.addHandler(shandler)
return logger
なんでわざわざ関数なんか作んなあかんの?
今後各ファイルでロガーを作ることになるわけですが、いちいち「ロガーを宣言して、ハンドラーを宣言して、ロガーに加えて、...」っていうのはあまりにも面倒。そこで「いっそのことロガーを作る機能を全部まとめちゃおうじゃないか」という魂胆です。楽になりますよ。
~~~~~~~~~~~~~~~
デフォルトの引数を順に見ていきましょう。
内容 | デフォルト | |
---|---|---|
name | 新設するロガーの名前 | 'default' |
level | ロガーのレベル | DEBUG |
fhandler | ロガーのファイルハンドラー | None |
fhandler_level | ファイルハンドラーに記載する最低レベル | DEBUG |
filename | ログを出力するファイルの名前 | DEFAULT_LOG_FILE_NAME |
filemode | ファイルの書き込みモード(新規書き込み 'w' / 上書き 'a') | 'w' |
fileformat | ファイルに記録するフォーマット | DEFAULT_LOG_FORMAT |
shandler | ロガーのストリームハンドラー | None |
shandler_level | ストリームハンドラーで記録する最低レベル | WARNING |
streamformat | ストリームハンドラーで記録するフォーマット | DEFAULT_LOG_FORMAT |
さっぱり意味わかんねぇ...
所々にリンクを隠していますので、わからない言葉に当たったら都度解説をご覧ください。なんならここを飛ばして 2-2 に行ってしまうのもアリです。
引数の途中にアスタリスク * が一人でいるのは、後続の引数をキーワード引数にするためです。
def setLogger(name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_ADDRESS, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=CRITICAL, streamformat=DEFAULT_LOG_FORMAT):
実際に引数を入れるときは setLogger(filemode='a')
のように、キーワードと一緒に書く必要があります。
~~~~~~~~~~~~~~~
関数の中身に入ります。まずは getLogger でロガーを取得。getLogger の引数はロガーにつける名前ですから、setLogger の引数にした name を入れるんですね。
def setLogger(name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_ADDRESS, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=CRITICAL, streamformat=DEFAULT_LOG_FORMAT):
logger = getLogger(name)
logger.setLevel(level)
直下で logger に setLevel を使ってレベルを設定しています。
今度はファイルハンドラーの設定です。
# file handler
fhandler = fhandler or FileHandler(filename, mode=filemode)
fhandler.setLevel(fhandler_level)
fhandler.setFormatter(fileformat)
logger.addHandler(fhandler)
もし引数の時点で使うファイルハンドラーが指定されていれば、それをそのまま fhandler に代入します。一方引数に何も入れられていなければ fhandler は None の状態ですから、FileHandler メソッドでファイルハンドラーを新設するという仕組みになっています。
その下でフォーマットを設定します。
fhandler.setLevel(fhandler_level)
fhandler.setFormatter(fileformat)
logger.addHandler(fhandler)
設定が終われば addHandler で logger に適用しましょう。
~~~~~~~~~~~~~~~
ラストはストリームハンドラーを設定しますよ。
ターミナルとかに出力されるやつやろ
よく勉強してらっしゃるじゃないですか。その通りです。
イヤミかクソ
# stream handler
shandler = shandler or StreamHandler()
shandler.setLevel(shandler_level)
shandler.setFormatter(streamformat)
logger.addHandler(shandler)
やることはファイルハンドラーでやったのと同じです。引数でストリームハンドラーが指定されていればそれを使いますし、指定されていなければ shandler は None ですから、StreamHandler メソッドで新設します。
レベルとフォーマットを設定したら、やはり logger に適用しましょう。
shandler.setFormatter(streamformat)
logger.addHandler(shandler)
~~~~~~~~~~~~~~~
これで logger は出来上がりました。この setLogger メソッドを使ってロガーを作りたいわけですから、リターンするのはロガーがいいですね。
logger = setLogger(...) みたいにしたいってことやろ?
その通りです。ということで、メソッドの最後には
return logger
を入れておきます。