0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

本番でSQLログが出ないときはログレベル差分を疑う話

0
Posted at

はじめに

リリース直後の動作確認で、
「SQLログが1行も出ていない。処理が正常終了していないかも」
と一瞬パニックになった経験をまとめます。

ただ、よく考えるとエラーログは出ていなかったので、
致命的な失敗ではなく、処理自体はおおむね正常に流れていた可能性が高い状態でした。

結論はシンプルで、
本番だけログレベルが違っていただけでした。

同じように焦る人を減らすために、何が起きていたか整理します。

この記事で得られることは次の3点です。

  • 環境ごとのログレベル差分で起こる典型的なハマり方
  • GORM のログレベル挙動(通常SQLとスロークエリ)
  • 本番で SQL ログに頼りすぎないための、最低限のログ設計

何が起きたのか

本番環境では、アプリ側のログレベルが INFO から WARN に設定されていたため、
SQL のうちスロークエリだけが WARN レベルで出力され、
通常クエリ(SELECT / INSERT / UPDATE)が一切ログに出ていませんでした。

環境ごとの差分は次のとおりです。

  • ローカル: INFO(SQLが全て出る)
  • QA: INFO(SQLが全て出る)
  • 本番: WARN(スロークエリのみ出る)

つまり、アプリもDBも正常で、設定差分だけが原因でした。

なぜ焦ったのか

  • SQL が出ないと「何か動いていないのでは」と感じやすい
  • バッチやAPIの動作確認では SQL ログが安心材料になりがち
  • 本番だけ静かだと「壊したかもしれない」と錯覚する

特にスロークエリだけ出る構成だと、
「通常SQLは出ないのに、スロークエリだけ急に出る」
という挙動になり混乱しやすいです。

スロークエリだけ出た理由

GORM のログ出力は、イベント種別ごとにおおむね次のレベルで扱われます。

  • 通常の SQL は Info レベル
  • スロークエリは Warn レベル
  • エラーは Error レベル

としてログが出力されます。

そのため、ログレベルが Warn 以上になっていると、
通常の SQL が表示されず、スロークエリだけが見える状態になります。

ここでいうスロークエリは、実行時間がしきい値を超えたSQLのことです。
GORM では SlowThreshold でしきい値を決められ、たとえば 500ms に設定した場合は 500ms を超えたクエリが対象になります。

スロークエリログは、次のような原因の切り分けに使えます。

  • インデックス不足でフルスキャンになっている
  • N+1 のようにクエリ回数が過剰になっている
  • 一時的なDB負荷で応答が遅くなっている

本番で通常SQLを抑える構成でも、スロークエリだけは残す運用にしておくと、性能劣化の初期兆候を拾いやすくなります。

なぜ本番だけ WARN にしていたのか

本番だけ WARN にする判断自体は珍しくありません。理由は次のようなものです。

  • ログ量を抑えて保管コストを下げたい
  • I/Oを減らして性能影響を抑えたい
  • 監視ではまずスロークエリや異常系を優先して見たい

今回のポイントは、この設計判断自体よりも「副作用を把握していなかった」ことでした。

シンプルな対策

同じミスを避けるためのポイントです。

1. 環境ごとのログレベルを明文化する

  • config ファイル
  • README
  • インフラ側のパラメータ
    などに、環境とログレベルの対応表を書いておく

2. 重要ログだけは常に INFO で出す

  • アプリ起動
  • バッチ開始/終了
  • 処理件数
    これらを INFO で出しておくと、SQL が静かでも安心して確認できます

例えば、次のようなキーと値で出しておくと確認がしやすくなります。

  • batch=start job=aggregate_sales
  • batch=finish job=aggregate_sales processed=1234 errors=0 duration_ms=8421

3. ログレベル差分はチェックリスト化する

リリースチェック項目に
「本番のログレベル確認」
を1つ入れておくと事故が減ります。

Go + GORM のログ設定サンプル

package db

import (
	"log"
	"os"
	"time"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	gormlogger "gorm.io/gorm/logger"
)

func NewDB(dsn string) (*gorm.DB, error) {
	appEnv := os.Getenv("APP_ENV") // local / qa / prod

	logLevel := gormlogger.Info
	if appEnv == "prod" {
		logLevel = gormlogger.Warn
	}

	newLogger := gormlogger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags),
		gormlogger.Config{
			SlowThreshold:             500 * time.Millisecond,
			LogLevel:                  logLevel,
			IgnoreRecordNotFoundError: true,
			Colorful:                  false,
		},
	)

	return gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: newLogger,
	})
}

この例だと、APP_ENV=prod のときは通常SQLは出さず、スロークエリ中心の出力になります。
逆に localqa では Info で全SQLが見えるため、動作確認しやすくなります。
ドライバは MySQL を例にしていますが、ログレベルの考え方は PostgreSQL などでも同様です。

まとめ

本番で SQL ログが出なくても、
まずはログレベルを疑うのが近道です。

  • DBエラー
  • 接続不良
  • デプロイミス

…といった深刻な問題ではなく、
単なる設定差分であることはよくあります。

心臓に悪いですが、落ち着いてログレベルを確認しましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?