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言語による並行処理

page_footer_300rect




page_footer_300rect




-BackEnd
-,

執筆者:


comment

メールアドレスが公開されることはありません。

CAPTCHA


関連記事

RSpecの個人的Tips集〜その1〜

1 はじめに2 テストコードの実行をスキップする3 共通のテストコードを用意する4 外部APIの返却値をスタブにする5 さいごに はじめに みなさん、テストコードは書かれているでしょうか? 私も極力書 ...

Pythonで書かれたスクレイピングのコードをRubyで書いてみる

1 はじめに2 仕様3 ソースコード4 使用したモジュール、Gem5 対象ページを取得6 XPATHから目的のものを抜き出す7 次のページのリンクを取得する8 さいごに はじめに 以前、技術評論社さん ...

rails

RailsでERBからJavaScriptにhashを渡す方法

1 はじめに2 カスタムデータ属性とは3 実装例3.1 コントローラの実装3.2 ビューの実装3.3 実行結果4 さいごに はじめに 以前、選択したプルダウンメニューに応じて別のプルダウンメニューの内 ...

rails

deviseを使ってユーザ登録フォームを作る

1 はじめに1.1 前提条件2 deviseについて2.1 deviseとは2.2 deviseでできること3 登録処理の実装3.1 Gemのインストール3.2 deviseのインストール3.3 デフ ...

js

GoogleAppsScriptを使ってmBaaSの定期実行処理を実装する

1 はじめに1.1 簡単な状況説明1.2 定期実行を行う方法2 実装2.1 実装の流れ2.2 JavaScriptの実装2.3 スクリプトをアップロードする2.4 Google Apps Script ...

フォロー

follow us in feedly

AppLink

英語

page_side_300rect

2018年11月
« 10月  
 123
45678910
11121314151617
18192021222324
252627282930 

アプリ情報

目標を達成したい方を応援する、TODOアプリもリリースしております。 下記のアイコンから無料でダウンロードできます。

Web版MyCoach

私たちはより広い方にコーチングを知ってもらいたいと考えています。 下記のサイトにて、コーチの方々を紹介しておりますので、よろしければご覧ください。