log【Go】
ログの出力
実行時間とともに任意の内容を出力します。
log.Println("logging") // 2018/11/27 11:45:52 logging fmt.Println("print") // print log.Fatalln("error") // 2018/11/27 11:45:52 error fmt.Println("not executed") // (log.Fatalによりプログラムは終了するため、出力されない)
Fatalはエラーログを意味し、その後に記述された処理は実行されません。
deferをつけた処理であっても実行されません。
ログファイルの生成
func LoggingSettings(logFile string) { logfile, _ := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) // 読み書き両方が可能、第1引数のファイルがなかったら作成、第1引数のファイルが存在したら追記、全てのosユーザーに読み書きを許容。返り値のerrは破棄 multiLogFile := io.MultiWriter(os.Stdout, logfile) // 標準出力(画面出力)とlogfileへの書き込みを同時並行で行うと変数に定義 log.SetFlags(log.Ldate | log.Ltime | log.Llongfile) // logfileへの出力時に、日付、時間ファイル名(階層付)を出力 log.SetOutput(multiLogFile) // ログの出力に関する設定を確定 } func main() { LoggingSettings("test.log") // log出力よりも前に実行すること log.Println("logging") // LoggingSettingsの内容に沿って、logが出力される }
io.MultiWriterは、複数のio.Writerを受け取って、同時に処理する機能です。
io.WriterとはGoのインターフェースのことで、Wirte()メソッドの仕様を宣言しています。
インターフェースで宣言されているメソッドが定義されているデータ型については
そのインターフェースを使用する事ができます。
このデータ型に当てはまる、os.Stdoutとファイル出力を引数に指定しています。
os.Stdoutは、standard output、すなわち標準出力(画面出力)のことで、デバッグコンソールなど画面に出力します。
log.SetFlagsの log.Llongfileは階層付のファイル名のことで、
log.Lshortfileにすると、階層を表示しないファイル名になります。
インターフェース
インターフェースはあくまで、「何ができるか」を宣言しているだけです。
応用可能性が高い分、そのインターフェースを利用する機能は数多くありますし、
Writer以外にもReaderなどインターフェースの種類も多岐に渡ります。
しかし、どのインターフェースを使用しているか、という宣言は使用する機能側では記述されていません。
それを調べるにはgodocコマンドを使います。
godoc -http ":6060" -analysis type
localhost:6060にアクセスすると、公式goドキュメントのようなものがオフラインで閲覧できます。
Packages > io > 各インターフェースにアクセスすると、
公式ドキュメントにはないimplements(io以外にもあります)を確認できます。
ここに使用機能が記載されています。
注意点として、GOPATH以下にパッケージを多く格納している場合、
解析にかかる時間が膨らむため、GOPATHを一時的に変更するなどの対応をした方が良いようです。
パーミッション
os.OpenFileの第3引数はpermission modeで、何か特別な制限を加えたいなどがない限り、0666で大丈夫です。
これは、binary表記を8進数に縮めた場合の表記です。
例えば、8進数における6は、2進数で110となります。これはread write executeのon/offを意味します。
read=1、 write=1、execute=0となるので、読み書きが許容されるという意味です。
6が3桁並びますが、それぞれowner、ownerが所属するgroup、otherを表します。
3つ全てに6が並ぶので、全員に読み書きを許可する、となります。
先頭は常に0で意味を持ちませんが、システムの都合で省略できないため存在します。
os、io機能に関するおまけ
標準出力の方法としてos.Stdoutを紹介しましたが、
こんなこともできます。
f, err := os.Open("hello.go") // read用のos機能を使用して、ファイルを読み込む if err != nil { log.Fatalln(err) // エラーがある場合はここで終了 } defer f.Close() // 関数内の処理の最後にファイルを閉じる io.Copy(os.Stdout, f) // 第2引数の内容を第一引数にコピーする(ファイルの内容を画面にコピー出力)