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時点)

namestarlatest commit
zap5,4272018/08/15
go-kit11,6882018/11/01
zerolog1,4642018/11/01
glog2,0032016/01/26
logrus8,8052018/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


関連記事

laravel logo

Laravel Admin でCSVインポートを実装する

1 はじめに2 下準備2.1 Laravel Adminの導入2.2 Laravel Excelの導入3 Laravel Excelの実装3.1 Importクラスの作成3.2 モデルのfillabl ...

laravel logo

Laradock環境にPHPStormからXdebugする

1 はじめに2 Laradock側の設定変更2.1 xdebug.iniファイル2.2 .env2.3 コンテナの再構築3 PHPStormの設定3.1 Preferences3.1.1 Build, ...

rails

RailsでAjax処理で画面を更新する

1 はじめに2 View(遷移元)の設定3 Controllerの実装4 View(遷移先)の実装5 参考6 さいごに はじめに RailsでAjax処理で画面を更新する方法を簡単に紹介します。 Vi ...

Go言語

Go言語、ゴルーチン(goroutine)で並列処理を

1 はじめに2 ゴルーチン2.1 go文2.2 ゴルーチンの終了条件2.3 WaitGroup3 チャネル3.1 チャネルの型3.2 チャネルの生成3.3 チャネルの送受信3.4 チャネルとゴルーチン ...

rails

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

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

フォロー

follow us in feedly

blog-page_side_responsive

2018年11月
 123
45678910
11121314151617
18192021222324
252627282930 

アプリ情報

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