Skip to content

Commit bcd362f

Browse files
authored
プルリクエスト #75 のマージ
#59 server log
2 parents 7fd7bf4 + 3aa275b commit bcd362f

File tree

8 files changed

+101
-22
lines changed

8 files changed

+101
-22
lines changed

backend/app.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
# lib
12
import os
23
from datetime import timedelta
34

5+
from config.logging_setup import setup_logger
46
from dotenv import load_dotenv
7+
8+
# flask lib
59
from flask import Flask, request
610
from flask_cors import CORS
711
from flask_jwt_extended import JWTManager
12+
13+
# blueprint
814
from routes.auth import auth_bp
915
from routes.dream.mine import my_dream_bp
1016
from routes.dream.public import public_dream_bp
@@ -13,7 +19,8 @@
1319
load_dotenv()
1420

1521
app = Flask(__name__)
16-
22+
# ログの出力設定
23+
setup_logger(app)
1724
# CORSのセットアップ
1825
CORS(
1926
app,
@@ -48,6 +55,7 @@ def test(): # 生存確認API
4855
if request.method == "HEAD":
4956
return "", 200
5057
elif request.method == "GET":
58+
app.logger.info("GET request")
5159
return "Hello", 200
5260

5361

backend/config/logging_setup.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import logging
2+
3+
from flask import Flask, has_request_context, request
4+
from rich.logging import RichHandler
5+
6+
7+
# ログの出力にメソッドとパスを追記する
8+
class RequestPathFilter(logging.Filter):
9+
def filter(self, record: logging.LogRecord) -> bool:
10+
if has_request_context():
11+
record.request_path = f"{request.method} {request.path}"
12+
else:
13+
record.request_path = "-"
14+
15+
return True
16+
17+
18+
# ロガーのセットアップ
19+
def setup_logger(app: Flask):
20+
# 既存ロガーの削除
21+
for h in app.logger.handlers:
22+
app.logger.removeHandler(h)
23+
24+
# 本番用ロガー
25+
handler = RichHandler(
26+
markup=True,
27+
rich_tracebacks=True,
28+
show_path=app.debug,
29+
)
30+
31+
formatter = logging.Formatter("%(request_path)s - %(message)s")
32+
handler.setFormatter(formatter)
33+
34+
handler.addFilter(RequestPathFilter())
35+
# INFOレベル以上のみ
36+
app.logger.setLevel(logging.INFO)
37+
app.logger.addHandler(handler)
38+
39+
# 本番環境では標準ログを無効化
40+
if not app.debug:
41+
logging.getLogger("werkzeug").disabled = True

backend/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ Flask-JWT-Extended~=4.7.1
66
supabase==2.13.0
77
gunicorn==23.0.0
88
pydantic==2.10.6
9+
rich==13.9.4

backend/routes/auth.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from flask import Blueprint, jsonify, make_response, request
1+
from flask import Blueprint, jsonify, make_response, request, current_app
22
from flask_jwt_extended import create_access_token
33
from models.db import get_supabase_client
44
from supabase import Client
@@ -22,6 +22,7 @@ def login():
2222
user = response.user
2323
# userを取得できなかった場合
2424
if user is None:
25+
current_app.logger.error("Credentials are incorrect")
2526
return "Credentials are incorrect", 401
2627

2728
# ユーザーID から JWT を生成
@@ -42,6 +43,7 @@ def login_with_oauth():
4243
credentials = request.get_json()
4344
user_id = credentials["userId"]
4445
if user_id is None:
46+
current_app.logger.error("User ID is required")
4547
return "User ID is required", 401
4648

4749
response = make_response("", 200)

backend/routes/dream/mine.py

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from flask import Blueprint, jsonify, request
1+
from flask import Blueprint, jsonify, request, current_app
22
from flask_jwt_extended import get_jwt_identity, jwt_required
33
from models.dream import Dream
44
from pydantic import ValidationError
@@ -7,46 +7,51 @@
77
my_dream_bp = Blueprint("my_dream", __name__)
88

99

10-
# 自分が作成した夢を取得
1110
@my_dream_bp.route("/dreams/mine", methods=["GET"])
1211
@jwt_required()
1312
def get_my_dreams():
1413
user_id = get_jwt_identity()
1514

1615
try:
1716
params = GetMyDreamsParams(**request.args)
18-
except ValidationError:
17+
except ValidationError as e:
18+
current_app.logger.error("Invalid query parameters", exc_info=e)
1919
return "Invalid query parameter", 400
2020

2121
dreams = Dream.get_all_by_user(user_id, params.sort_by)
2222

2323
try:
2424
dreams = [MyDreamResponse(**dream.__dict__) for dream in dreams]
25-
except ValidationError:
25+
except ValidationError as e:
26+
current_app.logger.error("Response validation failed", exc_info=e)
2627
return "Response Validation Error", 500
2728

2829
return jsonify([dream.model_dump() for dream in dreams]), 200
2930

3031

31-
# 夢を新規作成
3232
@my_dream_bp.route("/dreams/mine", methods=["POST"])
3333
@jwt_required()
3434
def create_my_dream():
35+
3536
body = request.get_json()
37+
3638
try:
3739
body = CreateMyDreamRequest(**body)
38-
except ValidationError:
40+
except ValidationError as e:
41+
current_app.logger.error("Invalid request body", exc_info=e)
3942
return "Invalid request body", 400
4043

41-
# JWTのヘッダからユーザーIDを取得
4244
user_id = get_jwt_identity()
43-
44-
# 夢とハッシュタグのテーブルにデータを追加
45-
created_dream = Dream.create_with_hashtags(user_id, body)
45+
try:
46+
created_dream = Dream.create_with_hashtags(user_id, body)
47+
except Exception as e:
48+
current_app.logger.exception("Failed to create dream")
49+
return "Internal server error", 500
4650

4751
try:
4852
created_dream = MyDreamResponse(**created_dream.__dict__)
49-
except ValidationError:
53+
except ValidationError as e:
54+
current_app.logger.error("Response validation error", exc_info=e)
5055
return "Response Validation Error", 500
5156

5257
return jsonify(created_dream.model_dump()), 201
@@ -55,23 +60,35 @@ def create_my_dream():
5560
@my_dream_bp.route("/dreams/mine/<int:dream_id>", methods=["PATCH"])
5661
@jwt_required()
5762
def toggle_visibility(dream_id: int):
58-
updated_dream = Dream.toggle_visibility(dream_id)
63+
try:
64+
updated_dream = Dream.toggle_visibility(dream_id)
65+
except Exception as e:
66+
current_app.logger.error.exception(f"Failed to toggle visibility for dream_id={dream_id}")
67+
return "Internal Server Error", 500
68+
5969
if updated_dream is None:
70+
current_app.logger.warning(f"Dream with id {dream_id} not found for visibility toggle")
6071
return "", 404
6172

6273
try:
6374
updated_dream = MyDreamResponse(**updated_dream.__dict__)
64-
except ValidationError:
75+
except ValidationError as e:
76+
current_app.logger.error("Response validation error", exc_info=e)
6577
return "Response Validation Error", 500
6678

6779
return jsonify(updated_dream.model_dump()), 200
6880

6981

70-
# 夢を削除
7182
@my_dream_bp.route("/dreams/mine/<int:dream_id>", methods=["DELETE"])
7283
@jwt_required()
7384
def delete_my_dream(dream_id: int):
74-
if Dream.delete(dream_id):
75-
return "", 204 # 削除成功
76-
else:
77-
return "", 404 # 夢が存在しない
85+
try:
86+
if Dream.delete(dream_id):
87+
current_app.logger.info(f"Deleted dream {dream_id}")
88+
return "", 204
89+
else:
90+
current_app.logger.warning(f"Attempted to delete non-existing dream {dream_id}")
91+
return "", 404
92+
except Exception as e:
93+
current_app.logger.exception("Failed to delete dream")
94+
return "Internal Server Error", 500

backend/routes/dream/public.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from flask import Blueprint, jsonify
1+
from flask import Blueprint, jsonify, current_app
22
from models.dream import Dream
33
from pydantic import ValidationError
44
from schemas.dream.public import PublicDreamResponse
@@ -15,6 +15,7 @@ def get_public_dreams():
1515
PublicDreamResponse(**dream.__dict__) for dream in public_dreams
1616
]
1717
except ValidationError:
18+
current_app.logger.exception("Validation error")
1819
return "Response Validation Error", 500
1920

2021
return jsonify([dream.model_dump() for dream in public_dreams]), 200
@@ -29,11 +30,13 @@ def increment_like_count(dream_id: int):
2930
likes_count = dream.likes + 1
3031
updated_dream = Dream.update_likes(dream_id=dream_id, likes=likes_count)
3132
if updated_dream is None:
33+
current_app.logger.exception("Failed to increment like count, no dreams")
3234
return "", 404
3335

3436
try:
3537
updated_dream = PublicDreamResponse(**updated_dream.__dict__)
3638
except ValidationError:
39+
current_app.logger.exception("Validation error")
3740
return "Response Validation Error", 500
3841

3942
return jsonify(updated_dream.model_dump()), 200

backend/routes/user.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from flask import Blueprint, jsonify, request
1+
from flask import Blueprint, jsonify, request, current_app
22
from models.user import User
33
from pydantic import ValidationError
44
from schemas.user import CreateUserBody, UserResponse
@@ -12,19 +12,23 @@ def create_user():
1212
try:
1313
body = CreateUserBody(**body)
1414
except ValidationError:
15+
current_app.logger.error("Validation Error of request body")
1516
return "Invalid request body", 400
1617

1718
user = User.get_user_by_email(body.email)
1819
if user is not None:
20+
current_app.logger.error("Email is already taken")
1921
return "Email is already taken", 409
2022

2123
new_user = User.create_user(body.email, body.password)
2224
if new_user is None:
25+
current_app.logger.error("Failed to create user")
2326
return "Failed to create user", 500
2427

2528
try:
2629
new_user = UserResponse(**new_user.__dict__)
2730
except ValidationError:
31+
current_app.logger.error("Response Validation Error")
2832
return "Response Validation Error", 500
2933

3034
return jsonify(new_user.model_dump()), 201

backend/schemas/dream/mine.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from datetime import datetime
22
from typing import Optional
3+
from flask import current_app
34

45
from pydantic import (
56
BaseModel,
@@ -48,6 +49,8 @@ class CreateMyDreamRequest(BaseModel):
4849
@field_validator("content")
4950
def validate_content(cls, content: str) -> str:
5051
if len(content) == 0:
52+
# 空白エラー
53+
current_app.logger.error("Content is required")
5154
raise ValidationError("Content is required")
5255

5356
return content

0 commit comments

Comments
 (0)