目次
とある日
Goの勉強を行うため、Programの問題を解いていた。
当たり前のようにfmt.Print
でデバッグをしていた。
そこで思った。
「めんどくさいな」
そう、fmt.Print
のデバッグだと色々とめんどくさい。
あ、Python
にはlogging
がある。
ってことで探したら、デフォルトであるみたい。
でも使い勝手が悪い、なので作ります。
Logging
Go
は勉強し始めて三ヶ月ほどなのでまだまだ至らないところがありますがご了承ください。
現在はinterface{}
という便利な型の勉強をしているので一部完成していない機能もあります。
ちなみに、Python3
のlogging
を参考にして作ってます。
概要
Python 用ロギング機能から拝借。
logging は、あるソフトウェアが実行されているときに起こったイベントを追跡するための手段です。ソフトウェアの開発者は、特定のイベントが発生したことを示す logging の呼び出しをコードに加えます。イベントは、メッセージで記述され、これに変数データ (すなわち、イベントが起こる度に異なるかもしれないデータ) を加えることもできます。イベントには、開発者がそのイベントに定めた重要性も含まれます。重要性は、レベル (level) や 重大度 (severity) とも呼ばれます。
- ロガーは、アプリケーションコードが直接使うインタフェースを公開します。
- ハンドラは、(ロガーによって生成された) ログ記録を適切な送信先に送ります。
- フィルタは、どのログ記録を出力するかを決定する、きめ細かい機能を提供します。
- フォーマッタは、ログ記録が最終的に出力されるレイアウトを指定します。
要約するとデバッグに役に立つ機能です。
ソースコード
全体を乗せると長くなるので、この記事では要所要所しか載せないです。
全体を閲覧したい方は、GitHubで公開していますのでご覧ください。
ドキュメントはないので、コードを読み解いて行ってもらうしかないです。
今後追加したいとは思っています。
例
色々と省略しています。
type Logging struct { format string formatTime bool formatFunName bool formatRunLine bool level int DEBUG int INFO int WARRING int ERROR int CRITICAL int levelList [5]string formatList map[string]string formatLenMax int printMode []string funcLog []string } func NewLogging() *Logging { logging := new(Logging) logging.InitializeLevel() logging.InitializeLevelList() logging.InitializeFormatList() logging.InitializeFormatMode() logging.GetMaxLenghtForFormat() logging.level = logging.DEBUG logging.format = "%#v\n" return logging } func main() { logging := NewLogging() logging.Debug(1) }
作成していく中で知って驚いたのが、Go
にはコンストラクターたるものが存在しないみたいです。
なので、初期値を設定するNewLogging関数
を作成しています。
main関数
のlogging.Debug(1)
で出力をしています。
余分な機能を除去して出力すると現在はこんな感じです。
func main() { logging := NewLogging() logging.Debug(1) } // >>1
機能
自分がこだわって作成している機能を3つに絞って解説したいと思います。
関数名出力
自分が過去にPython3
でlogging
を使用していたときの使い方として、特定の関数が実行されたときに処理順を記録するために関数名を出力してました。
出力部分は、logging
で行っていたのですが、関数名の抽出は独自で実装しました。
他にも、過去にimportしてある別ファイルのlogging
出力があり原因究明に時間がかかったことがあります。
という経験からデフォルトでそういったことを行ってくれる機能を実装しました。
使用例
l.formatFunName
で出力のオンオフを切り替えます。
現在はソースを直接操作して変更することにしています。
func (l *Logging) FormatConfig() { var formatTemplate = fmt.Sprintf("-%ds", l.formatLenMax+1) if l.formatFunName { pc, _, _, _ := runtime.Caller(3) f := runtime.FuncForPC(pc) var temp = fmt.Sprintf("%"+formatTemplate, l.formatList["formatFunName"]) fmt.Printf(temp+"=> %s\n", f.Name()) } } //Test temp func Test() { logging := NewLogging() logging.Debug(1) } func main() { Test() } //Function Names => main.Test //1
Function Names => main.Test
がこの機能の該当行になります。
Test関数
から実行されていることがわかるので、どこのなんのための出力なのかがわかる。
行番号出力
関数名出力機能と同じ構想のもと実装しました。
関数名だけでは、同じ関数内で複数回実行していた場合どこの出力なのか判別しづらい問題があると思うのでそれに対応するためプログラムソース上の行番号を出力する機能を実装しました。
使用例
l.formatRunLine
で出力のオンオフを切り替えます。
現在はソースを直接操作して変更することにしています。
func (l *Logging) FormatConfig() { var formatTemplate = fmt.Sprintf("-%ds", l.formatLenMax+1) if l.formatRunLine { var temp = fmt.Sprintf("%"+formatTemplate, l.formatList["formatRunLine"]) _, _, line, _ := runtime.Caller(3) fmt.Printf(temp+"=> %d\n", line) } } //Test temp func Test() { logging := NewLogging() logging.Debug(1) } func main() { Test() } //Run Line number => 186 //1
ソースコードをそのまま載せていないのでわからないと思いますが、
logging.Debug(1)
の行は、プログラムソース上だと186行目となっています。
指定レベル出力(未完成)
本家のlogging
では、指定レベル以上のものを出力する形式があるのですが、自分は特定のレベルだけの出力もしたい時がありました。
なので、それを実装する予定です。
構想案
func Test() { logging := NewLogging() logging.printMode = "Error" logging.Debug(0) logging.Info(1) logging.Warning(2) logging.Error(3) logging.Critical(4) } func main() { Test() } //>>3
本家の場合だとError
だけでなく、Critical
も出力されるのですがこの機能を使用すると特定のレベルだけの出力になる。
一応、本家の同様特定のレベル以上の出力も機能として実装しますがこの機能は別物として実装します。
特定レベルと特定レベル以上の出力優先度とかの設定もできたらいいなと思っています。
〆
基本的機能の実装は完成しました。
Goに慣れていないので、あまりきれいな記述方法や正しくはないと思いますが今後改変しなが実装しようと思っています。
今後追加したい機能などもあるので、Goに慣れつつ頑張ります。
今は他にも、Pythonのpprintを作成中です。
そこで、型について躓いているので作業難航中です。