Skip to content

Commit 3e96f6e

Browse files
Copilotks6088ts
andcommitted
Implement comprehensive logging system with environment variable control
Co-authored-by: ks6088ts <[email protected]>
1 parent 66e6c23 commit 3e96f6e

File tree

8 files changed

+224
-17
lines changed

8 files changed

+224
-17
lines changed

.env.template

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@ MICROSOFT_GRAPH_TENANT_ID="<YOUR_TENANT_ID>"
3131
MICROSOFT_GRAPH_CLIENT_ID="<YOUR_CLIENT_ID>"
3232
MICROSOFT_GRAPH_CLIENT_SECRET="<YOUR_CLIENT_SECRET>"
3333
MICROSOFT_GRAPH_USER_SCOPES="User.Read Sites.Read.All" # scopes for Microsoft Graph API
34+
35+
# Logging Configuration
36+
LOG_LEVEL="INFO" # Valid levels: DEBUG, INFO, WARNING, ERROR, CRITICAL

template_fastapi/app.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,20 @@
1212
from opentelemetry.trace import Span
1313

1414
from template_fastapi.routers import agents, chats, demos, files, foodies, items, speeches
15+
from template_fastapi.settings.logging import configure_logging, get_logger
16+
17+
# Configure logging first
18+
configure_logging()
19+
logger = get_logger(__name__)
1520

1621
app = FastAPI()
1722

23+
logger.info("Starting FastAPI application")
24+
1825
# If APPLICATIONINSIGHTS_CONNECTION_STRING exists, configure Azure Monitor
1926
AZURE_CONNECTION_STRING = getenv("APPLICATIONINSIGHTS_CONNECTION_STRING")
2027
if AZURE_CONNECTION_STRING:
28+
logger.info("Configuring Azure Monitor telemetry")
2129

2230
def server_request_hook(span: Span, scope: dict):
2331
if span and span.is_recording():
@@ -26,14 +34,16 @@ def server_request_hook(span: Span, scope: dict):
2634
user_id = uuid.uuid4().hex # Replace with actual user ID retrieval logic
2735
span.set_attribute("enduser.id", user_id)
2836
except KeyError:
29-
pass
37+
logger.warning("Failed to set user ID attribute in telemetry")
3038

3139
configure_azure_monitor(
3240
connection_string=AZURE_CONNECTION_STRING,
3341
# enable_live_metrics=True,
3442
server_request_hook=server_request_hook,
3543
)
3644
FastAPIInstrumentor.instrument_app(app)
45+
else:
46+
logger.info("Azure Monitor telemetry not configured (APPLICATIONINSIGHTS_CONNECTION_STRING not set)")
3747

3848
# Include routers
3949
# Routers configuration list
@@ -77,6 +87,7 @@ def server_request_hook(span: Span, scope: dict):
7787

7888
# Include routers using a loop
7989
for routerConfig in routersConfig:
90+
logger.info(f"Including router: {routerConfig['prefix']} with tags: {routerConfig['tags']}")
8091
app.include_router(
8192
router=routerConfig["router"],
8293
prefix=routerConfig["prefix"],
@@ -87,3 +98,5 @@ def server_request_hook(span: Span, scope: dict):
8798
},
8899
},
89100
)
101+
102+
logger.info("FastAPI application configured successfully")

template_fastapi/core.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import logging
22

3-
logger = logging.getLogger(__name__)
3+
from template_fastapi.settings.logging import get_logger
4+
5+
# For backward compatibility with the original tests
6+
# We still need to support the original behavior
7+
logger = get_logger(__name__)
48

59

610
def hello_world(verbose: bool = False):
11+
"""Simple hello world function with optional verbose logging."""
712
if verbose:
13+
# Configure basic logging for verbose mode (for backward compatibility)
814
logging.basicConfig(level=logging.DEBUG)
15+
# Use module-level logging for test compatibility
916
logging.debug("Hello World")
1017

1118

template_fastapi/routers/agents/azure_ai_foundry.py

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
ThreadResponse,
1212
)
1313
from template_fastapi.repositories.agents import AgentRepository
14+
from template_fastapi.settings.logging import get_logger
1415

16+
logger = get_logger(__name__)
1517
router = APIRouter()
1618
agent_repo = AgentRepository()
1719

@@ -25,9 +27,13 @@ async def create_agent(request: AgentRequest) -> AgentResponse:
2527
"""
2628
新しいエージェントを作成する
2729
"""
30+
logger.info(f"Creating new agent with model: {request.model}")
2831
try:
29-
return agent_repo.create_agent(request)
32+
result = agent_repo.create_agent(request)
33+
logger.info(f"Successfully created agent with ID: {result.id}")
34+
return result
3035
except Exception as e:
36+
logger.error(f"Failed to create agent: {str(e)}", exc_info=True)
3137
raise HTTPException(status_code=500, detail=f"エージェントの作成に失敗しました: {str(e)}")
3238

3339

@@ -40,9 +46,13 @@ async def get_agent(agent_id: str) -> AgentResponse:
4046
"""
4147
エージェントの情報を取得する
4248
"""
49+
logger.info(f"Getting agent: {agent_id}")
4350
try:
44-
return agent_repo.get_agent(agent_id)
51+
result = agent_repo.get_agent(agent_id)
52+
logger.debug(f"Successfully retrieved agent: {agent_id}")
53+
return result
4554
except Exception as e:
55+
logger.warning(f"Agent not found: {agent_id} - {str(e)}")
4656
raise HTTPException(status_code=404, detail=f"エージェントが見つかりません: {str(e)}")
4757

4858

@@ -57,9 +67,13 @@ async def list_agents(
5767
"""
5868
エージェントの一覧を取得する
5969
"""
70+
logger.info(f"Listing agents with limit: {limit}")
6071
try:
61-
return agent_repo.list_agents(limit=limit)
72+
result = agent_repo.list_agents(limit=limit)
73+
logger.info(f"Retrieved {len(result.agents)} agents")
74+
return result
6275
except Exception as e:
76+
logger.error(f"Failed to list agents: {str(e)}", exc_info=True)
6377
raise HTTPException(status_code=500, detail=f"エージェント一覧の取得に失敗しました: {str(e)}")
6478

6579

@@ -71,13 +85,17 @@ async def delete_agent(agent_id: str) -> dict:
7185
"""
7286
エージェントを削除する
7387
"""
88+
logger.info(f"Deleting agent: {agent_id}")
7489
try:
7590
success = agent_repo.delete_agent(agent_id)
7691
if success:
92+
logger.info(f"Successfully deleted agent: {agent_id}")
7793
return {"message": "エージェントが正常に削除されました"}
7894
else:
95+
logger.error(f"Failed to delete agent (no error): {agent_id}")
7996
raise HTTPException(status_code=500, detail="エージェントの削除に失敗しました")
8097
except Exception as e:
98+
logger.error(f"Failed to delete agent {agent_id}: {str(e)}", exc_info=True)
8199
raise HTTPException(status_code=500, detail=f"エージェントの削除に失敗しました: {str(e)}")
82100

83101

@@ -90,9 +108,14 @@ async def chat_with_agent(agent_id: str, request: ChatRequest) -> ChatResponse:
90108
"""
91109
エージェントとチャットする
92110
"""
111+
logger.info(f"Starting chat with agent {agent_id} on thread {request.thread_id}")
112+
logger.debug(f"Chat message: {request.message[:100]}...") # Log first 100 chars
93113
try:
94-
return agent_repo.chat_with_agent(agent_id, request)
114+
result = agent_repo.chat_with_agent(agent_id, request)
115+
logger.info(f"Chat completed successfully with agent {agent_id}")
116+
return result
95117
except Exception as e:
118+
logger.error(f"Failed to chat with agent {agent_id}: {str(e)}", exc_info=True)
96119
raise HTTPException(status_code=500, detail=f"エージェントとのチャットに失敗しました: {str(e)}")
97120

98121

@@ -105,9 +128,13 @@ async def create_thread(request: ThreadRequest) -> ThreadResponse:
105128
"""
106129
新しいスレッドを作成する
107130
"""
131+
logger.info("Creating new thread")
108132
try:
109-
return agent_repo.create_thread(request)
133+
result = agent_repo.create_thread(request)
134+
logger.info(f"Successfully created thread with ID: {result.id}")
135+
return result
110136
except Exception as e:
137+
logger.error(f"Failed to create thread: {str(e)}", exc_info=True)
111138
raise HTTPException(status_code=500, detail=f"スレッドの作成に失敗しました: {str(e)}")
112139

113140

@@ -120,9 +147,13 @@ async def get_thread(thread_id: str) -> ThreadResponse:
120147
"""
121148
スレッドの情報を取得する
122149
"""
150+
logger.info(f"Getting thread: {thread_id}")
123151
try:
124-
return agent_repo.get_thread(thread_id)
152+
result = agent_repo.get_thread(thread_id)
153+
logger.debug(f"Successfully retrieved thread: {thread_id}")
154+
return result
125155
except Exception as e:
156+
logger.warning(f"Thread not found: {thread_id} - {str(e)}")
126157
raise HTTPException(status_code=404, detail=f"スレッドが見つかりません: {str(e)}")
127158

128159

@@ -134,13 +165,17 @@ async def delete_thread(thread_id: str) -> dict:
134165
"""
135166
スレッドを削除する
136167
"""
168+
logger.info(f"Deleting thread: {thread_id}")
137169
try:
138170
success = agent_repo.delete_thread(thread_id)
139171
if success:
172+
logger.info(f"Successfully deleted thread: {thread_id}")
140173
return {"message": "スレッドが正常に削除されました"}
141174
else:
175+
logger.error(f"Failed to delete thread (no error): {thread_id}")
142176
raise HTTPException(status_code=500, detail="スレッドの削除に失敗しました")
143177
except Exception as e:
178+
logger.error(f"Failed to delete thread {thread_id}: {str(e)}", exc_info=True)
144179
raise HTTPException(status_code=500, detail=f"スレッドの削除に失敗しました: {str(e)}")
145180

146181

@@ -155,7 +190,11 @@ async def list_threads(
155190
"""
156191
エージェントのスレッド一覧を取得する
157192
"""
193+
logger.info(f"Listing threads with limit: {limit}")
158194
try:
159-
return agent_repo.list_threads(limit=limit)
195+
result = agent_repo.list_threads(limit=limit)
196+
logger.info(f"Retrieved {len(result.threads)} threads")
197+
return result
160198
except Exception as e:
199+
logger.error(f"Failed to list threads: {str(e)}", exc_info=True)
161200
raise HTTPException(status_code=500, detail=f"スレッド一覧の取得に失敗しました: {str(e)}")

template_fastapi/routers/chats.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
from template_fastapi.repositories.chats import ChatRepository
55
from template_fastapi.settings.chats import get_chats_settings
6+
from template_fastapi.settings.logging import get_logger
67

8+
logger = get_logger(__name__)
79
router = APIRouter()
810
templates = Jinja2Templates(directory="template_fastapi/templates")
911
chat_repository = ChatRepository()
@@ -14,7 +16,9 @@
1416
)
1517
async def get(request: Request):
1618
"""Get the chat page with configurable WebSocket URL."""
19+
logger.info("Serving chat page")
1720
settings = get_chats_settings()
21+
logger.debug(f"Using WebSocket URL: {settings.chats_websocket_url}")
1822
return templates.TemplateResponse(
1923
"chats.html",
2024
{
@@ -29,11 +33,20 @@ async def get(request: Request):
2933
)
3034
async def websocket_endpoint(websocket: WebSocket, client_id: int):
3135
"""WebSocket endpoint for chat functionality."""
32-
await chat_repository.manager.connect(websocket)
36+
logger.info(f"WebSocket connection attempt for client {client_id}")
3337
try:
38+
await chat_repository.manager.connect(websocket)
39+
logger.info(f"WebSocket connected successfully for client {client_id}")
40+
3441
while True:
3542
data = await websocket.receive_text()
43+
logger.debug(f"Received message from client {client_id}: {data[:100]}...") # Log first 100 chars
3644
await chat_repository.handle_client_message(data, websocket, client_id)
45+
3746
except WebSocketDisconnect:
47+
logger.info(f"WebSocket disconnected for client {client_id}")
3848
chat_repository.manager.disconnect(websocket)
3949
await chat_repository.handle_client_disconnect(client_id)
50+
except Exception as e:
51+
logger.error(f"WebSocket error for client {client_id}: {str(e)}", exc_info=True)
52+
chat_repository.manager.disconnect(websocket)

template_fastapi/routers/demos.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from fastapi import APIRouter, HTTPException
55

66
from template_fastapi.opentelemetry import get_meter, get_tracer
7+
from template_fastapi.settings.logging import get_logger
78

9+
logger = get_logger(__name__)
810
tracer = get_tracer(__name__)
911
meter = get_meter(__name__)
1012
router = APIRouter()
@@ -16,6 +18,7 @@ async def flaky_exception():
1618
"""
1719
A flaky endpoint that always raises an exception.
1820
"""
21+
logger.warning("Flaky exception endpoint called - simulating error")
1922
raise HTTPException(
2023
status_code=500,
2124
detail="Simulated exception",
@@ -29,18 +32,24 @@ async def flaky(failure_rate: int):
2932
3033
The failure rate is a percentage (0-100) that determines the likelihood of failure.
3134
"""
35+
logger.debug(f"Flaky endpoint called with failure rate: {failure_rate}")
36+
3237
if not (0 <= failure_rate <= 100):
38+
logger.error(f"Invalid failure rate: {failure_rate}")
3339
raise HTTPException(
3440
status_code=400,
3541
detail="Failure rate must be between 0 and 100",
3642
)
3743

38-
if random.randint(0, 100) < failure_rate:
44+
random_value = random.randint(0, 100)
45+
if random_value < failure_rate:
46+
logger.warning(f"Simulating failure (random: {random_value}, threshold: {failure_rate})")
3947
raise HTTPException(
4048
status_code=500,
4149
detail="Simulated failure",
4250
)
4351

52+
logger.info(f"Request succeeded (random: {random_value}, threshold: {failure_rate})")
4453
return {
4554
"message": "Request succeeded",
4655
}
@@ -53,17 +62,22 @@ async def heavy_sync_with_sleep(sleep_ms: int):
5362
5463
This simulates a long-running synchronous operation.
5564
"""
65+
logger.info(f"Heavy sync endpoint called with sleep time: {sleep_ms}ms")
66+
5667
if sleep_ms < 0:
68+
logger.error(f"Invalid sleep time: {sleep_ms}")
5769
raise HTTPException(
5870
status_code=400,
5971
detail="Sleep time must be a non-negative integer",
6072
)
6173

6274
with tracer.start_as_current_span("parent"):
63-
print(f"Sleeping for {sleep_ms} milliseconds")
75+
logger.debug(f"Starting sleep for {sleep_ms} milliseconds")
6476
time.sleep(sleep_ms / 1000.0)
6577
with tracer.start_as_current_span("child"):
66-
print("Child span")
78+
logger.debug("Child span execution")
79+
80+
logger.info(f"Completed sleep operation for {sleep_ms}ms")
6781
return {
6882
"message": f"Slept for {sleep_ms} milliseconds",
6983
}
@@ -82,5 +96,6 @@ async def roll_dice():
8296
"""
8397
with tracer.start_as_current_span("roll_dice"):
8498
roll = random.randint(1, 6)
99+
logger.info(f"Dice rolled: {roll}")
85100
roll_counter.add(1, {"roll.value": str(roll)})
86101
return roll

0 commit comments

Comments
 (0)