Skip to content

Commit 68757ca

Browse files
feat: add shard information to all state & gw logging (#1551)
* feat: add shard information to all state & gw logging * feat: restore logging's heart
1 parent d69b4de commit 68757ca

File tree

2 files changed

+45
-23
lines changed

2 files changed

+45
-23
lines changed

interactions/api/gateway/gateway.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Outlines the interaction between interactions and Discord's Gateway API."""
22
import asyncio
3+
import logging
34
import sys
45
import time
56
import zlib
@@ -171,31 +172,32 @@ async def run(self) -> None:
171172
async def dispatch_opcode(self, data, op: OPCODE) -> None:
172173
match op:
173174
case OPCODE.HEARTBEAT:
174-
self.logger.debug("Received heartbeat request from gateway")
175+
self.state.wrapped_logger(logging.DEBUG, "❤ Received heartbeat request from gateway")
175176
return await self.send_heartbeat()
176177

177178
case OPCODE.HEARTBEAT_ACK:
178179
self._latency.append(time.perf_counter() - self._last_heartbeat)
179180

180181
if self._last_heartbeat != 0 and self._latency[-1] >= 15:
181-
self.logger.warning(
182-
f"High Latency! shard ID {self.shard[0]} heartbeat took {self._latency[-1]:.1f}s to be acknowledged!"
182+
self.state.wrapped_logger(
183+
logging.WARNING,
184+
f"❤ High Latency! shard ID {self.shard[0]} heartbeat took {self._latency[-1]:.1f}s to be acknowledged!",
183185
)
184186
else:
185-
self.logger.debug(f"❤ Heartbeat acknowledged after {self._latency[-1]:.5f} seconds")
187+
self.state.wrapped_logger(logging.DEBUG, "❤ Received heartbeat acknowledgement from gateway")
186188

187189
return self._acknowledged.set()
188190

189191
case OPCODE.RECONNECT:
190-
self.logger.debug("Gateway requested reconnect. Reconnecting...")
192+
self.state.wrapped_logger(logging.DEBUG, "Gateway requested reconnect. Reconnecting...")
191193
return await self.reconnect(resume=True, url=self.ws_resume_url)
192194

193195
case OPCODE.INVALIDATE_SESSION:
194-
self.logger.warning("Gateway has invalidated session! Reconnecting...")
196+
self.state.wrapped_logger(logging.WARNING, "Gateway invalidated session. Reconnecting...")
195197
return await self.reconnect()
196198

197199
case _:
198-
return self.logger.debug(f"Unhandled OPCODE: {op} = {OPCODE(op).name}")
200+
return self.state.wrapped_logger(logging.DEBUG, f"Unhandled OPCODE: {op} = {OPCODE(op).name}")
199201

200202
async def dispatch_event(self, data, seq, event) -> None:
201203
match event:
@@ -207,12 +209,14 @@ async def dispatch_event(self, data, seq, event) -> None:
207209
self.ws_resume_url = (
208210
f"{data['resume_gateway_url']}?encoding=json&v={__api_version__}&compress=zlib-stream"
209211
)
210-
self.logger.info(f"Shard {self.shard[0]} has connected to gateway!")
211-
self.logger.debug(f"Session ID: {self.session_id} Trace: {self._trace}")
212+
self.state.wrapped_logger(logging.INFO, "Gateway connection established")
213+
self.state.wrapped_logger(logging.DEBUG, f"Session ID: {self.session_id} Trace: {self._trace}")
212214
return self.state.client.dispatch(events.WebsocketReady(data))
213215

214216
case "RESUMED":
215-
self.logger.info(f"Successfully resumed connection! Session_ID: {self.session_id}")
217+
self.state.wrapped_logger(
218+
logging.INFO, f"Successfully resumed connection! Session_ID: {self.session_id}"
219+
)
216220
self.state.client.dispatch(events.Resume())
217221
return None
218222

@@ -228,9 +232,11 @@ async def dispatch_event(self, data, seq, event) -> None:
228232
processor(events.RawGatewayEvent(data.copy(), override_name=event_name))
229233
)
230234
except Exception as ex:
231-
self.logger.error(f"Failed to run event processor for {event_name}: {ex}")
235+
self.state.wrapped_logger(
236+
logging.ERROR, f"Failed to run event processor for {event_name}: {ex}"
237+
)
232238
else:
233-
self.logger.debug(f"No processor for `{event_name}`")
239+
self.state.wrapped_logger(logging.DEBUG, f"No processor for `{event_name}`")
234240

235241
self.state.client.dispatch(events.RawGatewayEvent(data.copy(), override_name="raw_gateway_event"))
236242
self.state.client.dispatch(events.RawGatewayEvent(data.copy(), override_name=f"raw_{event.lower()}"))
@@ -263,8 +269,8 @@ async def _identify(self) -> None:
263269
serialized = FastJson.dumps(payload)
264270
await self.ws.send_str(serialized)
265271

266-
self.logger.debug(
267-
f"Shard ID {self.shard[0]} has identified itself to Gateway, requesting intents: {self.state.intents}!"
272+
self.state.wrapped_logger(
273+
logging.DEBUG, f"Identification payload sent to gateway, requesting intents: {self.state.intents}"
268274
)
269275

270276
async def reconnect(self, *, resume: bool = False, code: int = 1012, url: str | None = None) -> None:
@@ -289,11 +295,11 @@ async def _resume_connection(self) -> None:
289295
serialized = FastJson.dumps(payload)
290296
await self.ws.send_str(serialized)
291297

292-
self.logger.debug(f"{self.shard[0]} is attempting to resume a connection")
298+
self.state.wrapped_logger(logging.DEBUG, f"Resume payload sent to gateway, session ID: {self.session_id}")
293299

294300
async def send_heartbeat(self) -> None:
295301
await self.send_json({"op": OPCODE.HEARTBEAT, "d": self.sequence}, bypass=True)
296-
self.logger.debug(f"❤ Shard {self.shard[0]} is sending a Heartbeat")
302+
self.state.wrapped_logger(logging.DEBUG, "❤ Gateway is sending a Heartbeat")
297303

298304
async def change_presence(self, activity=None, status: Status = Status.ONLINE, since=None) -> None:
299305
"""Update the bot's presence status."""

interactions/api/gateway/state.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import logging
23
import traceback
34
from datetime import datetime
45
from logging import Logger
@@ -72,7 +73,7 @@ async def start(self) -> None:
7273
"""Connect to the Discord Gateway."""
7374
self.gateway_url = await self.client.http.get_gateway()
7475

75-
self.logger.debug(f"Starting Shard ID {self.shard_id}")
76+
self.wrapped_logger(logging.INFO, "Starting Shard")
7677
self.start_time = datetime.now()
7778
self._shard_task = asyncio.create_task(self._ws_connect())
7879

@@ -84,7 +85,7 @@ async def start(self) -> None:
8485

8586
async def stop(self) -> None:
8687
"""Disconnect from the Discord Gateway."""
87-
self.logger.debug(f"Shutting down shard ID {self.shard_id}")
88+
self.wrapped_logger(logging.INFO, "Stopping Shard")
8889
if self.gateway is not None:
8990
self.gateway.close()
9091
self.gateway = None
@@ -102,7 +103,7 @@ def clear_ready(self) -> None:
102103

103104
async def _ws_connect(self) -> None:
104105
"""Connect to the Discord Gateway."""
105-
self.logger.info(f"Shard {self.shard_id} is attempting to connect to gateway...")
106+
self.wrapped_logger(logging.INFO, "Shard is attempting to connect to gateway...")
106107
try:
107108
async with GatewayClient(self, (self.shard_id, self.client.total_shards)) as self.gateway:
108109
try:
@@ -127,7 +128,18 @@ async def _ws_connect(self) -> None:
127128

128129
except Exception as e:
129130
self.client.dispatch(events.Disconnect())
130-
self.logger.error("".join(traceback.format_exception(type(e), e, e.__traceback__)))
131+
self.wrapped_logger("".join(traceback.format_exception(type(e), e, e.__traceback__)))
132+
133+
def wrapped_logger(self, level: int, message: str, **kwargs) -> None:
134+
"""
135+
A logging wrapper that adds shard information to the message.
136+
137+
Args:
138+
level: The logging level
139+
message: The message to log
140+
**kwargs: Any additional keyword arguments that Logger.log accepts
141+
"""
142+
self.logger.log(level, f"Shard ID {self.shard_id} | {message}", **kwargs)
131143

132144
async def change_presence(
133145
self,
@@ -157,15 +169,19 @@ async def change_presence(
157169

158170
if activity.type == ActivityType.STREAMING:
159171
if not activity.url:
160-
self.logger.warning("Streaming activity cannot be set without a valid URL attribute")
172+
self.wrapped_logger(
173+
logging.WARNING, "Streaming activity cannot be set without a valid URL attribute"
174+
)
161175
elif activity.type not in [
162176
ActivityType.GAME,
163177
ActivityType.STREAMING,
164178
ActivityType.LISTENING,
165179
ActivityType.WATCHING,
166180
ActivityType.COMPETING,
167181
]:
168-
self.logger.warning(f"Activity type `{ActivityType(activity.type).name}` may not be enabled for bots")
182+
self.wrapped_logger(
183+
logging.WARNING, f"Activity type `{ActivityType(activity.type).name}` may not be enabled for bots"
184+
)
169185
if status:
170186
if not isinstance(status, Status):
171187
try:
@@ -175,7 +191,7 @@ async def change_presence(
175191
elif self.client.status:
176192
status = self.client.status
177193
else:
178-
self.logger.warning("Status must be set to a valid status type, defaulting to online")
194+
self.wrapped_logger(logging.WARNING, "Status must be set to a valid status type, defaulting to online")
179195
status = Status.ONLINE
180196

181197
self.client._status = status

0 commit comments

Comments
 (0)