- 村田靖拓
- 約 2,500 文字
- 200 View
こんにちは、TIGの村田です。
春の入門祭り連載2日目の本記事では、フューチャー製OSSであるreguerrに入門しようと思います。
入門の途中でエラーと遭遇したため、途中からエラーハンドリング編に突入しています。入門祭りということで、エラーハンドリングの一例として「こんな風に考えるんだなー」と思いつつ読んでいただければ幸いです。
また、最終的にはエラーハンドリングを元に、OSSへPRを投げています。そういったOSSとの向き合い方を感じて頂くきっかけになれば良いなと思っています。
では本編に入っていきます。
reguerrとは
reguerrはエラーハンドリング向けのソースコードを自動生成してくれるGo製のライブラリです。フューチャーの案件でも採用実績があり、体系的なエラー定義とそれに伴うハンドリングが重要となってくるエンタープライズシステムでの利用に足る機能をreguerrは有しています。
入門してみる
下準備
まずはフューチャーのGitHubリポジトリ上reguerrのreadmeに沿ってコマンドをインストールします。
go install github.com/future-architect/reguerr/cmd/reguerr |
以下のようにヘルプコマンドが実行できればインストール成功です。
reguerr -h |
自動生成してみる
generate コマンドのヘルプを見てみると、-fでインプットファイルを指定すれば良いことが分かります。
reguerr generate --help |
readmeに沿って、以下ファイルをexample.goとして作成します。
package example |
そしてexample.goをインプットファイルにして自動生成します。
reguerr generate -f example.go |
example_gen.goとexample_gen.mdの2つのファイルが作成されます。
goファイルの中身は以下のようになっています。
// generated by reguerr; DO NOT EDIT |
インプットファイルで定義されたエラーパターンをもとに、それぞれ以下2種の関数が作成されています。
- 引数で受け取ったエラーを、定義した任意のエラーへ変換して返してくれる関数
- 引数で受け取ったエラーが、定義したエラーとエラー内容が一致しているか判定してくれる関数
mdファイルは以下のような内容になっています。エラーが自動的に表形式で整理されるので、各種ドキュメンテーションの際に活躍してくれそうです。
# Error Code List |
自動生成の引数をいじってみる
generate コマンドの引数には --defaultStatusCode などの可変パラメータが存在していました。次はこちらをいじってみます。
reguerr generate -f example.go --defaultStatusCode 300 |
ステータスコードが指定通りに変更されていることが確認できました。
エラーハンドリングしてみる
--defaultErrorLevel をいじってデフォルトのエラーレベルを変更しようとしたのですが、エラーが出てしまいました。
reguerr generate -f example.go --defaultErrorLevel Info |
渡している文字列が悪いのか、渡し方が悪いのか、はたまた元のソースコードにバグが存在しているのか。末尾に出ている unknown error level がエラーログなので、ソースコードを追って原因を探ってみます。
リポジトリを漁ってみると、reguerr.goの47行目に該当のエラー文言がありました。
func NewLevel(s string) (Level, error) { |
コマンドの実行引数で渡している Info の文字列が NewLevel 関数の引数 s として渡っていくのだろうと思いますが、このswitch文の中でdefaultに突入、該当のエラーが発生しているだろうと推測されます。
この NewLevel 関数自体も呼び元がいるはずなので探ってみると、cmd配下のroot.go内71行目にて呼び出されていることが確認できました。
var opts []gen.Option |
ここで --defaultErrorLevel と --defaultStatusCode にて設定された値を処理しているようです。
期待する挙動は、先程のswitch文の中で strings.ToLower(s) の値が strings.ToLower(Info.String()) の値と一致することなのですが、そうなってないようなので何が起きているかもう少し探ってみます。
NewLevel のタイミングで各々の値が実際どうなっているのか確認できるようにログを仕込んでみました。
fmt.Printf("[USER]strings.ToLower(s)=%v\n", strings.ToLower(s)) |
これで再度generateを試してみます。
reguerr generate -f example.go --defaultErrorLevel Info |
ログが出ました。まず、コマンド引数として渡している部分は infolevel という文字列になっていました。たしかに NewLevel の呼び元で以下のように呼び出していましたね。
reguerr.NewLevel(errLevel + "Level") |
引数で渡されたエラーレベルの文言に Level という文字列を付け加え、それがlowercaseに変換されるのでプログラム上違和感はないです。
ただ、マッチ対象文字列は level という文字列を含まないのでこれが原因と考えられます。
試しに NewLevel の呼び出し方を以下のように変えてみます。
// level, err := reguerr.NewLevel(errLevel + "Level") |
以下コマンドで再度generateを実行。エラーなく終了しました。
reguerr generate -f example.go --defaultErrorLevel Info |
生成されたマークダウンファイルを覗いてみると、LOGLEVEL部が想定通り Info に変わっていることを確認できました。
cat example_gen.md |
また、goファイル側ではデフォルトのエラーレベルを変更するinit処理が追加されていることを確認できました。
func init() { |
OSSにPRを投げてみる
動作確認を元に以下の変更を加え、プルリクエストを作成しました。
OSSの挙動でおかしいと思われる点があった際に「このOSS使えねえ!」と騒ぐのではなくissueを起票するかPRをあげよとどこかのエラい人から教わったので、私も例に漏れずそのように行動したいと思います。このPRが少しでも世界平和に繋がることを祈っています。
などと言いつつ、執筆時点(2022.04)でこの修正が全体を鑑みた上でベストなのかどうかは判断しきれていないのが正直なところです。ただ、そこはコードオーナーのレビューに任せ、修正案のたたき台としてこのPRが機能するといいなという気持ちでPRをあげることにします。
まとめ
さて、今回はフューチャー製OSSであるreguerrに入門しつつ、エラーハンドリングしつつOSSへPRをあげるということに入門してみました。
私のPRがマージされた暁には、本記事で触れているエラーに直面することは無くなるのですが、エラーハンドリングの考え方やOSSとの向き合い方が皆さんの参考になればと思っています。
春の入門祭り連載、次回は戸田さんです。お楽しみに!