More than 1 year has passed since last update.

最近、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してしまいましょう。