概要
今回はGoを使用したログの設定を私なりに作成してみたのでアウトプットとしてこちらに記載させていただきます!
環境
構築にはDockerを使用します。
Dockerfile
は下記を参照してください。
FROM golang:1.20-alpine3.16 AS dev
ENV ROOT /app
WORKDIR ${ROOT}
RUN apk update && apk add --no-cache git
COPY go.mod go.sum ./
RUN go mod download
COPY . .
EXPOSE 8080
CMD ["go", "run", "main.go"]
version: '3.8'
services:
db:
image: mysql:8.0
container_name: mysql
env_file:
- .env
environment:
MYSQL_ROOT_PASSWORD: $DB_ROOT_PASSWORD
MYSQL_DATABASE: $DB_NAME
MYSQL_USER: $DB_USER
MYSQL_PASSWORD: $DB_PASSWORD
volumes:
- ./mysql:/var/lib/mysql
ports:
- 3306:3306
api:
build:
context: .
target: dev
container_name: api
working_dir: /app
env_file:
- .env
environment:
APP_ENV: $APP_ENV
volumes:
- .:/app
ports:
- 8080:8080
depends_on:
- db
そもそもログがなぜ必要なのか
まず、そもそもなぜログが必要なのかについて簡単に解説します。
主な理由は下記です。
- システムに問題が発生した際に原因を特定しやすくするため
- システムの動きを追跡するため
- システムに何が入ってきて、何が出ていくのかを把握するため
もしシステムに問題が起きた際に、ログがまったくなければどこでシステムがエラーになっているのかを特定することはかなり難しくなります。
エラー原因の発見が不可能ではないにしても非常に効率が悪く原因を発見するのにかなりの時間がかかってしまいます。
またシステムが裏側でどのように稼働しているのか、どのようなデータが入ってきて出ていくのかなど様々なことに役に立つためログは非常に重要な役割を持っています。
ログの設定
それでは本題のログの設定を行なっていきます。
また今回はログを構造化できるlogrus
を使用していきます。
logrus
について詳しく知りたい方は下記を参照してください!
package logging
import (
"os"
log "github.com/sirupsen/logrus"
)
func SetupLogger() (*log.Logger, error) {
logger := log.New()
logger.SetFormatter(&log.JSONFormatter{})
logger.SetReportCaller(true)
if os.Getenv("APP_ENV") == "production" {
logger.SetLevel(log.InfoLevel)
} else {
logger.SetLevel(log.DebugLevel)
}
file, err := getLogFile()
if err != nil {
return nil, err
}
logger.SetOutput(file)
return logger, nil
}
func getLogFile() (*os.File, error) {
file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return nil, err
}
return file, nil
}
それではひとつひとつ解説していきます。
- まず今回はログの設定を汎用的に利用したいので、
log.New()
でインスタンスを作成してます。
logger := log.New()
- ロガーの設定を行う
logger.SetFormatter(&log.JSONFormatter{}) // ログをJSONフォーマットで出力する
logger.SetReportCaller(true) // ログ内に呼び出し元の情報を追加する(/app/main.go:10などの情報)
if os.Getenv("APP_ENV") == "production" { // 環境によってログレベルを設定
logger.SetLevel(log.InfoLevel)
} else {
logger.SetLevel(log.DebugLevel)
}
- 出力先のファイルを作成
file, err := getLogFile()
if err != nil {
return nil, err
}
// 省略
func getLogFile() (*os.File, error) {
file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return nil, err
}
return file, nil
}
app.log
というファイルがない場合作成し、ある場合は末尾に書き込みを行うように設定。
os
モジュールについてよくわからない場合は、下記を参照してみるといいかもしれません。
os.OpenFile
のオプションフラグ(os.O_CREATE
など)についても記載されています。
- ログの出力先を指定
logger.SetOutput(file)
先ほどのgetLogFile()
から取得したファイルオブジェクトを指定しています。
つまり、app.log
にログが出力するように指定しています。
これで設定は終わりです。
次は実際に出力させてみましょう。
ログの出力
トップのmain.goで呼び出して使ってみましょう。
package main
import (
"<モジュールのルートパス>/pkg/logging"
)
func main() {
logger, err := logging.SetupLogger()
if err != nil {
panic(err)
}
logger.Info("Hello world!")
}
実行するとapp.log
が作成され、ファイル内に下記のようなログが出力されます。
{"file":"/app/main.go:14","func":"main.main","level":"info","msg":"Hello World","time":"2023-02-18T09:25:05Z"}
もしタイムゾーンを変更したい場合、環境変数TZ
にAsia/Tokyo
を設定すれば変更できます。
まとめ
いかがだったでしょうか。
まだまだGoにおける今回のログの設定は改善の余地があると思いますが、学習を進めつつ改善できたらなと思っています。
もし至らない点等ありましたら、ご指摘いただけると幸いです。
最後まで読んでいただきありがとうございました!