BackEnd

Go言語のエラーハンドリングとログローテーション

投稿日:2018年11月5日 更新日:

はじめに

こんにちは、今回はGo言語でのエラーハンドリングとログ関連についての記事となります。

例外のないGo言語でどのようにエラーを伝搬させていくのかについて一つの方法を紹介させていただき、その後、そのエラーをファイルに出力する方法を紹介したいと思います。
なお、開発環境の構築がまだの方は、ぜひこちらで環境構築をしてからお読みいただければと思います。

エラーハンドリング

エラーハンドリングについてですが、まずはGo言語でerrorとは一体何ものなのかについて触れたいと思います。その後、pkg/errorsパッケージでerrorをwrapをする方法を記載し、次の節でそれを活用した独自のerrorsパッケージを作成したいと思います。

error インターフェース

Go言語では例外の概念がないため(Go2では例外が組み込まれるかもしれませんが)、ファイルの処理などで下記のようにerrorを返却するコードをよく目にすると思います。

この os.Open関数で返却されるerrorとは何かと言うと、error インタフェースを実装した構造体となります。下記にerrorインタフェースの定義を記載しておきます。

そして、errors.New関数で生成され、返却されるものは、上記を実装した構造体の一つになります。標準パッケージのerrors.goの内容も掲載しておきます。

つまり、errorインタフェースさえ実装すれば、何でもerrorとして扱えるということになり、本ブログでもその特性を利用して、独自のerrorsパッケージの作成を後半で行います。

pkg/errors パッケージ

error関連のパッケージで、pkg/errorsパッケージを少しだけ紹介したいと思います。これは、errorを呼び出し元に返却する際に元のerrorをwrapすることで、errorをstackさせていけるようにするためのシンプルなパッケージです。

例えば、先ほどos.Openのエラーハンドリングを記載しましたが、pkg/errorsを利用すると下記のようにerrorをstackさせることができます。

それでは、このpkg/errorsパッケージを利用して、独自のerrorsパッケージを作成してみたいと思います。

独自のエラータイプ付き errorsパッケージを作成

先ほどの節で、errorインタフェースを実装した構造体であれば、errorとして扱えるということがわかったと思います。ただ、文字列しか表現できないerrorでは何かと扱いにくい場合もあると思います。そこで、今回は一例としてエラータイプというものを持たせたerrorをpkg/errorsパッケージを利用して、作成したいと思います。早速コードの全量になります。

それほどコード量がないのでみていただければ、だいたい理解できると思いますが、このerrorsパッケージの特徴を簡単に箇条書きで記載しておきます。

  • errorインタフェースを実装したtypedError構造体を定義し、そこにエラーそのものとエラータイプを保持させた
  • typedError構造体をパッケージスコープにして、外部から直接生成できないようにした(エラータイプはGetType関数で取得)
  • ErrorTypeを定義して、特定のエラータイプのerrorを生成する関数郡を用意した
  • pkg/errorsを使い、errorをstackできるようにした

なお、このerrorsパッケージの利用例については、最後の節でエラーの内容をログファイルに出力させるサンプルで紹介します。

log パッケージ

この節では、上記のエラーをファイル出力や標準出力するためのログについて記載していきます。標準でもlogパッケージが提供されていますが、ログレベルの調整などを行う場合には手間がかかるため、githubで公開されているパッケージを利用しようと思います。

log パッケージのGitHubスター数

スター数が1,000を超えている有名どころのlog関連のパッケージのいくつかを下記に紹介します。(※2018/11/02時点)

name star latest commit
zap 5,427 2018/08/15
go-kit 11,688 2018/11/01
zerolog 1,464 2018/11/01
glog 2,003 2016/01/26
logrus 8,805 2018/11/02

この中では、zapが高速であることをおしており、私も実際にベンチマークをしたところ、logrusと比べ数倍は早い結果になりました。ただ、今回はそれほどログを大量に出力することは考慮せず、もっとも使い勝手がよく開発も活発なlogrusで独自のloggersパッケージを作ることにしました。

logrus パッケージ

このパッケージについては、ほとんど説明することなるすぐに利用ができます。こちらのREADMEにある通り、「ログのフォーマット(SetFormatter)」「ログの出力先(SetOutput)」「ログの出力レベル(SetLevel)」を設定して、あとはInfoやErrorなどの関数で出力を行うだけです。実際のサンプル例はログローテーションと合わせて後の節で記載します。

ログローテーション

先ほど紹介した全てのlogパッケージは、ログをファイルに出力してもログローテーションまではしてくれません。つまり、一つのファイルに永遠と出力を続け、放っておくと巨大なサイズのファイルができあがります。

ログローテーションのやり方としては、Linuxなどの環境にインストールされているlogrotateパッケージをデプロイ先のサーバで設定するというのがあります。ただ、これだとGo言語で開発しているプロジェクト内でコントロールできないので、今回はlumberjackを使って、ソースコード内で設定をしようと思います。

lumberjack パッケージ

このパッケージも利用方法はとてもシンプルで、こちらのREADMEを読むだけですぐに設定できます。
設定項目としては、「ファイル名(Filename)」「ローテーションするファイルサイズ(MaxSize)」「古いログを保持する日数(MaxAge)」「保持する古いログの最大ファイル数(MaxBackups)」「バックアップファイルの時刻フォーマットをサーバローカル時間にするかどうか(LocalTime)」「ローテーションされたファイルをgzip圧縮するかどうか(Compress)」があります。こちらも実際のサンプル例は次の節でlogrusと合わせて紹介します。

独自のloggersパッケージを作成

それでは、logrus と lumberjackの利用して、独自のloggersパッケージを作成してみたいと思います。とてもシンプルなので、いきなり全量のコードをはります。

上記の解説を箇条書きで記載します。

  • init関数の中でlogrusの初期化処理をlumberjackも使い、全て完了させた
  • 後の関数はlogrusをラップしただけ。(他のコードでlogrusを直接意識して触らせないためです)
  • logrusのファイル出力はJSON形式とし、ログレベルとログの出力先は環境変数によって分けた
  • lumberjackは、ファイル名を環境変数によって決め、1年間ログを保持し、古いファイルは圧縮させた

独自のerrors と loggers でエラーログ出力

それでは最後に、上記で紹介した独自のerrorsパッケージとloggersパッケージを利用するサンプルコードと実行結果を紹介します。

まず、下記が 上記の2パッケージを利用するmain.goファイルになります。mysqlサーバからuserレコードを取得する際にエラーが発生したということを擬似的に表現しています。

実行結果と出力されたログファイルの中身を記載します。

特筆すべきところとしては、switch文でログタイプにより処理が分岐できている点と、ログファイルのmsgの中身がerrorのstackを出力できている点です。これで、どのようにエラーが伝搬して最終的にログファイルに書き込まれたのかがわかるようになっています。

さいごに

いかがでしたでしょうか。エラーハンドリングから、そのエラー内容をログに出力させ、そしてログファイルをローテーションさせるまで一気に説明させていただきました。これをベースにもう少し使い勝手をよくしたり、また必要なログ情報を付け加えたりしていこうと思います。また、logrus から zapに切り替えるなどもやってみたいと思います。

なお、Go言語に関してはこちらでも多くの記事を紹介していますので、ぜひご覧ください。

おすすめ書籍

スターティングGo言語 (CodeZine BOOKS) Goプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る impress top gearシリーズ みんなのGo言語[現場で使える実践テクニック] Go言語による並行処理

blog-page_footer_336




blog-page_footer_336




-BackEnd
-,

執筆者:

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

Stripe Connectを使って継続課金を実装

1 はじめに2 商品・価格の登録2.1 マイグレーション2.2 製品・価格登録処理の実装2.3 Stripe管理画面での確認3 サブスクリプション登録3.1 事前準備3.2 課金処理の実装3.3 St ...

Go言語

[Go初心者]Interfaceについて学習

1 はじめに2 interfaceとは3 interfaceのメリット3.1 インターフェースを使うとコードの共通化ができる3.2 インターフェースを使うと実装を隠蔽することができる3.3 インターフ ...

rails

Rails Developer Meetup に参加してきました【1日目】

1 はじめに2 発表について2.1 安全かつ高速に進めるマイクロサービス化2.2 Rails in Microservices2.3 MySQL/InnoDB の裏側2.4 H2O/mruby でつく ...

Rust入門してみた その3 Enum / match / Option編

1 はじめに2 Enum2.1 Enumの定義2.2 パターンマッチ2.3 Enumへのメソッド実装3 よく使う標準Enum3.1 Option3.2 Result4 おすすめ書籍 はじめに 前回に引 ...

軽量なAlpine Linuxイメージでgitbookのローカル環境を構築する

1 はじめに2 Alpine Linuxとは3 Docker本体のインストール4 サンプルリポジトリのダウンロード5 dockerイメージ作成6 Gitbook初期化&実行7 Dockerの ...

フォロー

blog-page_side_responsive

2018年11月
 123
45678910
11121314151617
18192021222324
252627282930  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。