こんにちは!フリーランスエンジニアのこたろうです。
今回は、Docker環境で動作するGoバックエンドのデバッグ方法について、実践的な知見を共有します。
1. docker logsの基本
リアルタイムでログを確認
# コンテナのログをリアルタイムで表示
docker logs -f backend-container
# 直近100行のログを表示
docker logs --tail 100 backend-container
# タイムスタンプ付きでログを表示
docker logs -f --timestamps backend-container
Goコードでのログ出力
// handlers/message.go
func (h *MessageHandler) Create(c *gin.Context) {
// リクエストの開始をログ出力
fmt.Printf("Message creation started at: %v\n", time.Now())
var message Message
if err := c.ShouldBindJSON(&message); err != nil {
fmt.Printf("Error binding JSON: %v\n", err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 処理の結果をログ出力
fmt.Printf("Message created: %+v\n", message)
}
2. grepを使ったログのフィルタリング
特定のAPIリクエストの抽出
# /api/messages/createへのリクエストのみ表示
docker logs -f backend-container | grep "/api/messages/create"
# エラーを含むログの抽出
docker logs backend-container | grep "Error"
# 特定の時間帯のログを抽出
docker logs backend-container | grep "2024-02-06T15"
パイプを活用した複雑なフィルタリング
# エラーを含み、特定のAPIに関連するログを抽出
docker logs backend-container | grep "Error" | grep "/api/messages"
# JSONデータの内容を確認
docker logs backend-container | grep "Message created" | jq .
3. CORSデバッグのテクニック
OPTIONSリクエストの確認
// main.go
func setupCORS(r *gin.Engine) {
fmt.Println("Setting up CORS...")
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:3000"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
// OPTIONSリクエストのログ
r.OPTIONS("/*path", func(c *gin.Context) {
fmt.Printf("OPTIONS request received: %s\n", c.Request.URL.Path)
c.Status(http.StatusNoContent)
})
}
CORSエラーのデバッグ
# OPTIONSリクエストのログを確認
docker logs backend-container | grep "OPTIONS request received"
# CORSヘッダーの確認
docker logs backend-container | grep "Access-Control-Allow"
4. WebSocketデバッグのポイント
コネクション確立のログ
func (h *WebSocketHandler) HandleWebSocket(c *gin.Context) {
fmt.Printf("WebSocket connection attempt from: %s\n", c.Request.RemoteAddr)
conn, err := h.upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
fmt.Printf("WebSocket upgrade error: %v\n", err)
return
}
defer conn.Close()
fmt.Printf("WebSocket connection established with: %s\n", c.Request.RemoteAddr)
}
WebSocketメッセージのログ
# WebSocketの接続ログを確認
docker logs backend-container | grep "WebSocket connection"
# エラーを含むWebSocketログを確認
docker logs backend-container | grep "WebSocket" | grep "error"
5. 実践的なトラブルシューティング例
エラーの原因特定
# エラーの発生時刻を特定
docker logs --timestamps backend-container | grep "Error"
# エラー前後のログを確認
docker logs --timestamps backend-container | grep -A 5 -B 5 "Error"
# 特定のリクエストの一連の流れを確認
docker logs backend-container | grep -E "(Started|Completed|Error)"
デバッグ用ログの実装
type DebugLogger struct {
Enabled bool
}
func (l *DebugLogger) Printf(format string, v ...interface{}) {
if l.Enabled {
fmt.Printf("[DEBUG] "+format+"\n", v...)
}
}
// 使用例
logger := &DebugLogger{Enabled: true}
logger.Printf("Processing request: %s", requestID)
まとめ
効果的なデバッグのポイント:
- 適切なログレベルの設定
- 必要な情報の選択的な出力
- grepを活用したログの絞り込み
- エラー発生時の文脈の把握
- システマティックな原因特定