技術向上

プログラミングの学び、気になるテクノロジーやビジネストレンドを発信

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以外にもあります)を確認できます。
ここに使用機能が記載されています。
f:id:tech-up:20181127233815p:plain

注意点として、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引数の内容を第一引数にコピーする(ファイルの内容を画面にコピー出力)


ascii.jp

www.youtube.com

qa.geeksforgeeks.org