最近、RDB(Mysql)を操作するバッチ処理を実装することが多く、お勉強がてらgolangを使い始めました。
やることはいつも
- パラメーター判断
- トランザクション制御
- ログ出力
こんな感じなので、
golangの動作確認を兼ねて、コマンドアプリのひな型になりそうなサンプルを作りました。
package main
import (
"flag"
"fmt"
"time"
"os"
)
const VERSION = `0.0.1`
type ErrMessage struct {
What string
}
func (e ErrMessage) Error() string {
return fmt.Sprintf("%v: %v", time.Now(), e.What)
}
var (
version = flag.Bool("V", false, "display version information")
failure = flag.Bool("failure", false, "失敗させます")
)
func DoSomething(str string) (err error) {
defer func() {
// panicがおきたらRollback
if e := recover(); e != nil {
fmt.Fprintf(os.Stderr, "Rollback!!\n")
// エラーメッセージフォーマット変換
x, _ := e.(string)
err = ErrMessage{x}
}
}()
if *failure {
// Error発生
panic("DoSomething error...")
}
fmt.Fprintf(os.Stdout, "%s\n", str)
return nil
}
func main() {
flag.Parse()
if *version {
fmt.Printf("dosomething %s\n", VERSION)
os.Exit(0)
}
str := "do something & commit!!"
err := DoSomething(str)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
str = "and finish!!"
fmt.Fprintf(os.Stdout, "%s\n", str)
}
パラメーターの実装は、flagを使えば楽ですね。
トランザクション制御について、
異常時のRollBackを実行するには、deferとrecover()を使えば良さそうです。
どのような動きになるのか、パラメーターで動作を分けて試してみました。
また、
ログ出力時、メッセージフォーマットを自作するサンプルがあったので、採用してみました。
正常時のログはStdoutへ、異常時はStderrへ出力したいですから、そのあたりも実装してます。
fmt.Fprintf() に標準出力(os.Stdout)、標準エラー(os.Stderr)のファイルディスクリプタを渡します。
さて、実行してみましょう。
実行時パラメータ無しで、正常終了します。
$ (コマンド) 2>./stderr.log 1>./stdout.log
などとやれば、stderr.log には、何も出力されず、
stdout.log にログが出力されます。
failureパラメータをつけると、異常時の動作を確認できます。
(実際にはこんなパラメータは要らないですが...)
$ (コマンド) --failure 2>./stderr.log 1>./stdout.log
stderr.log で、deferに指定した処理が実行されたことがわかります。
stdout.logには出力されません。
何か異常があれば、すぐにpanic()を起こしてrollbackしてしまいましょう。