【文法系】【go】基本15:ロギングとエラーハンドリング

1.log(ロギング)

以下のように、該当するファイルがない場合、エラー文を表示して、終了する際などエラーハンドリングに使用。

主な使用場面
package main

import (
    "io"
    "log"
    "os"
)

func loggingSettings(logFile string){
    logfile, _ := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    multiLogFile := io.MultiWriter(os.Stdout, logfile)
    log.SetFlags(log.Ldate | log.Ltime | log.Llongfile)
    log.SetOutput(multiLogFile)
}

func main() {
    loggingSettings("test.log") // test.logに以下のエラーハンドリングの結果を出力
    _, err := os.Open("xxxxxx")
    if err != nil{
        log.Fatalln("Exit", err)
    }
}
▼ 出力
2020/02/24 06:53:21 /home/vagrant/workspace/src/myapp/lesson.go:20: Exit open xxxxxx: no such file or directory
exit status 1

<test.log>

2020/02/24 06:53:21 /home/vagrant/workspace/src/myapp/lesson.go:20: Exit open xxxxxx: no such file or directory
⑴ 基本的な書き方
例①
package main

import (
    "log"
    "fmt"
)

func main() {
    log.Println("logging!")
    log.Printf("%T %v", "test", "test")

    log.Fatalln("error!")

    fmt.Println("OK!") // 実行されない
}
▼ 実行
$ go run lesson.go

2020/02/24 06:27:29 logging!
2020/02/24 06:27:29 string test
2020/02/24 06:27:29 error!
exit status 1

《解説》
「log.Fatalln()」と同様に、「log.Fatalf()」もそこでコードは終了する。

func main() {
    log.Println("logging!")
    log.Printf("%T %v", "test", "test")

    log.Fatalf("%T %v", "test2", "test2")

    fmt.Println("OK!") // 実行されない
}
▼ 出力
2020/02/24 06:38:26 logging!
2020/02/24 06:38:26 string test
2020/02/24 06:38:26 string test2
exit status 1

2.エラーハンドリング

説明

⑴ 基本的な書き方
例①
package main

import (
    "fmt"
    "log"
    "os"
)

func main() {
    file, err := os.Open("./lesson.go")
    if err != nil{
        log.Fatalln("Error!")
    }

    defer file.Close()
    data := make([]byte, 100)
    count, err := file.Read(data)
    if err != nil{
        log.Fatalln("Error")
    }
    fmt.Println(count, string(data))

    err = os.Chdir("test") // Chdirはchangeディレクトリの意
    if err != nil{
        log.Fatalln("Error")
    }
}
▼ 実行
$ go run lesson.go

100 package main

import (
    "fmt"
    "log"
    "os"
)

func main() {
    file, err := os.Open("./les

2020/02/24 07:52:42 Error
exit status 1

《解説》
:=(イニシャライズのショートデクレアレーションは)が最低一つイニシャライズする変数があれば良いため、今回のケースでも使用できる。 つまり、「count, err := file.Read(data)」のcountはイニシャライズされ、errはすでに定義されているため、override(上書き)される。
なお、os.Chdir()については、エラーかどうかのみ返すため、変数は一つとなり、errはすでに定義済みのため、=でoverrideする。

2.panicとrecover

panic:自分で例外を投げられるため、そこでプログラムは強制終了される。

⑴ panic
例①
package main

import "fmt"

func thirdPartyConnectDB(){
    panic("Unable to connect database!")
}

func save(){
    thirdPartyConnectDB()
}

func main() {
    save()
    fmt.Println("OK?")
}
▼ 実行
$ go run lesson.go

panic: Unable to connect database!

goroutine 1 [running]:
main.thirdPartyConnectDB()
    /home/vagrant/workspace/src/myapp/lesson.go:6 +0x39
main.save()
    /home/vagrant/workspace/src/myapp/lesson.go:10 +0x20
main.main()
    /home/vagrant/workspace/src/myapp/lesson.go:14 +0x22
exit status 2

《解説》
save関数でthirdPartyConnectDB関数を呼び出し、panicが実行されるため、そこでプログラムが終了し、main関数にあるfmt.Println("OK?")は実行されない。

⑵ recover
例①
package main

import "fmt"

func thirdPartyConnectDB(){
    panic("Unable to connect database!")
}

func save(){
    defer func(){
        s := recover()
        fmt.Println(s)
    }()
    thirdPartyConnectDB()
}

func main() {
    save()
    fmt.Println("OK?")
}
▼ 実行
$ go run lesson.go

Unable to connect database!
OK?

《解説》
save関数でthirdPartyConnectDB関数を呼び出し、panicが実行されるが、save関数では、thirdPartyConnectDB関数よりも先にdefer func()が記述されているため、panicで発生した例外の内容のみrecover()が受け取り、Printlnで出力する(プログラムの終了はさせない)。プログラムは終了しないため、main関数のfmt.Println("OK?")も実行される。

【補足】
以下のように、panicを呼び出すthirdPartyConnectDB関数をdefer func()よりも先に書くと、panicでプログラムが終了してしまうため注意。

func save(){
    thirdPartyConnectDB()
    defer func(){
        s := recover()
        fmt.Println(s)
    }()
}
▼ 出力
panic: Unable to connect database!

goroutine 1 [running]:
main.thirdPartyConnectDB()
    /home/vagrant/workspace/src/myapp/lesson.go:6 +0x39
main.save()
    /home/vagrant/workspace/src/myapp/lesson.go:10 +0x22
main.main()
    /home/vagrant/workspace/src/myapp/lesson.go:18 +0x22
exit status 2