[Golang]envconfigで環境変数を扱う


はじめに

Goで環境変数ってどうやって扱うのかを調べると、いくつかそれっぽいものを発見しました。

viperがスター数では最も多いのですが、今回はシンプルな用途で探していたのでenvconfigを試してみました。

標準パッケージだとこうなる

外部のライブラリを使用せず、標準パッケージであるosを使用すると、

main.go
package main
import (
    "fmt"
    "os"
)
func main() {
    val := os.Getenv("VALUE")
    fmt.Println("val is ", val)
}
$ VALUE="HOGE" go run main.go
val is HOGE

設定されている値をとってくるだけです。

もし指定したKeyが見つからなければvalには何も代入されません。

$ FAKE_VALUE="HOGE" go run main.go
val is

環境変数の必須化やデフォルト値の設定は、自前で処理を書く必要があリます。

envconfig使ってみる

準備

インストール

go get -u github.com/kelseyhightower/envconfig

今回使用する環境変数

  • TABLE_NAME
    • 必須とする
  • TIMEOUT
    • デフォルト値:10

実装

まず、全体像はこんな感じです。

main.go
package main

import (
    "fmt"
    "log"
    "github.com/kelseyhightower/envconfig"
)

type Config struct {
    TableName string `required:"true" split_words:"true"`
    Timeout   int    `default:"10"`
}

func main() {
    var config Config
    if err := envconfig.Process("", &config); err != nil {
        log.Fatalf("[ERROR] Failed to process env: %s", err.Error())
    }
    fmt.Println("Config.TableName:", config.TableName)
    fmt.Println("Config.Timeout:", config.Timeout)
}

以下で、パート別に紹介します。

structを定義する

環境変数とマッピングさせるためのstructを定義します。

type Config struct {
    TableName string `required:"true" split_words:"true"`
    Timeout   int    `default:"10"`
}
  • フィールドに、使用したい環境変数名と型を記載します。
  • フィールドに付随するタグを記載します。

タグ

主に使用するタグを紹介します。

  • required→必須かどうか。trueに設定すると、該当の環境変数が未設定の場合にエラーとなる
  • default→未設定の場合に適用されるデフォルト値
  • split_words→変数名をキャメルケースからスネークケースに変換した上で環境変数を探しに行く
    • 例えば、もしこれが設定されていない場合、TableNameTABLENAMEとして扱われる
    • フラグをtrueにすることでTABLE_NAMEとなる

その他を知りたい方は公式のドキュメントを参照してみてください。

envconfigを適用させる

var config Config
if err := envconfig.Process("", &config); err != nil {
    log.Fatalf("[ERROR] Failed to process env: %s", err.Error())
}

定義済のstruct(Config)のポインタを第2引数に渡して、環境変数とマッピングさせます。必須であるのに未設定の場合や、型が違う場合などはエラーとなるのでエラーハンドリングも必要です。

envconfig.Process()の第1引数について

envconfig.Process()の第1引数は、Prefixを設定したい場合に指定します。

例えば、envconfig.Process("app", &config)とすると、以下の環境変数を探しにいきます。

  • APP_TABLE_NAME
  • APP_TIMEOUT

基本的には""(空文字)で良いと思います。

実行

  • 正常系
$ TABLE_NAME=Test TIMEOUT=8 go run main.go 
Config.TableName: Test
Config.Timeout: 8

TIMEOUTのデフォルト値もちゃんと上書きされています。

  • 環境変数未設定
$ go run main.go                
2020/04/27 17:24:32 [ERROR] Failed to process env: required key TABLE_NAME missing value
exit status 1

TABLE_NAMEが必須にも関わらず、未設定なのでエラーとなりました。
エラーメッセージにも出力されているので分かりやすいですね。

  • 型違反
$ TABLE_NAME=Test TIMEOUT=8.1 go run main.go 
2020/04/27 17:26:07 [ERROR] Failed to process env: envconfig.Process: assigning TIMEOUT to Timeout: converting '8.1' to type int. details: strconv.ParseInt: parsing "8.1": invalid syntax
exit status 1

こちらは、intで定義しているTIMEOUTに少数を入れたので型違反でエラーとなりました。
こちらのエラーメッセージも分かりやすいです。

まとめ

標準パッケージを使用するよりは、柔軟な扱い方が可能だと思うのでこれからも使っていきたいです。

ただし、外出しした設定ファイルなどを読み込んでゴニョゴニョすることは出来ないので、そういったユースケースの時は冒頭でも触れたspf13/viperなどを使用していくべきなのかな?と思います。