Skip to main content
Logging is essential for debugging issues, monitoring bot behavior, and tracking errors in production. Pycord uses Python’s built-in logging module to provide detailed information about what’s happening in your bot.

Why Use Logging?

  • Debug issues faster - See exactly what’s happening internally
  • Monitor production - Track errors and performance
  • Audit actions - Keep records of bot activities
  • Diagnose connection issues - View WebSocket and API events

Quick Start

The simplest way to enable logging:
import discord
import logging

# Set up logging
logging.basicConfig(level=logging.INFO)

bot = discord.Bot()

@bot.event
async def on_ready():
    logging.info(f"Logged in as {bot.user}")

bot.run("TOKEN")
This will show INFO-level messages from Pycord and your bot.

Logging Levels

Python’s logging module has five standard levels:
LevelValueUsage
DEBUG10Detailed diagnostic information
INFO20General informational messages
WARNING30Warning messages for unexpected events
ERROR40Error messages for serious issues
CRITICAL50Critical errors that may crash the bot
Show general information and warnings:
import logging

logging.basicConfig(level=logging.INFO)
Output:
INFO:discord.client:Logging in using static token
INFO:discord.gateway:Shard ID None has connected to Gateway

Configuring Pycord Loggers

Pycord uses multiple loggers for different components. You can configure them individually:
import logging

# Get specific loggers
logger = logging.getLogger('discord')
logger.setLevel(logging.INFO)

# Configure the discord.gateway logger separately
gateway_logger = logging.getLogger('discord.gateway')
gateway_logger.setLevel(logging.WARNING)

# Configure the discord.http logger
http_logger = logging.getLogger('discord.http')
http_logger.setLevel(logging.INFO)

Available Loggers

  • discord - Main logger (parent of all others)
  • discord.client - Client events and connection status
  • discord.gateway - WebSocket gateway communication
  • discord.http - HTTP API requests
  • discord.state - Cache state management
  • discord.webhook - Webhook operations
  • discord.ext.commands - Command framework

Formatting Log Output

Customize how log messages appear:
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
Output:
2024-03-15 10:30:45,123 - discord.client - INFO - Logging in using static token

Logging to Files

Save logs to a file for later analysis:
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    filename='bot.log',
    filemode='a'  # 'a' for append, 'w' for overwrite
)

Custom Application Logging

Add logging to your own bot code:
import logging
import discord
from discord.ext import commands

# Create a logger for your bot
bot_logger = logging.getLogger('my_bot')
bot_logger.setLevel(logging.INFO)

bot = commands.Bot(command_prefix="!")

@bot.event
async def on_ready():
    bot_logger.info(f"Bot started with {len(bot.guilds)} guilds")

@bot.event
async def on_command(ctx):
    bot_logger.info(
        f"Command {ctx.command} invoked by {ctx.author} "
        f"in {ctx.guild.name if ctx.guild else 'DM'}"
    )

@bot.event
async def on_command_error(ctx, error):
    bot_logger.error(
        f"Error in command {ctx.command}: {error}",
        exc_info=True  # Include stack trace
    )

@bot.command()
async def ping(ctx):
    bot_logger.debug(f"Ping command invoked by {ctx.author}")
    await ctx.send("Pong!")
    bot_logger.debug("Ping response sent")

bot.run("TOKEN")

Complete Logging Setup

Here’s a production-ready logging configuration:
import logging
import logging.handlers
import discord
from discord.ext import commands
from pathlib import Path

def setup_logging():
    # Create logs directory
    log_dir = Path("logs")
    log_dir.mkdir(exist_ok=True)
    
    # Create formatter
    log_format = logging.Formatter(
        '[{asctime}] [{levelname:<8}] {name}: {message}',
        datefmt='%Y-%m-%d %H:%M:%S',
        style='{'
    )
    
    # Set up root logger
    root_logger = logging.getLogger()
    root_logger.setLevel(logging.INFO)
    
    # Console handler
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(log_format)
    root_logger.addHandler(console_handler)
    
    # File handler (with rotation)
    file_handler = logging.handlers.RotatingFileHandler(
        log_dir / 'bot.log',
        maxBytes=10485760,  # 10MB
        backupCount=5,
        encoding='utf-8'
    )
    file_handler.setLevel(logging.INFO)
    file_handler.setFormatter(log_format)
    root_logger.addHandler(file_handler)
    
    # Error file handler
    error_handler = logging.handlers.RotatingFileHandler(
        log_dir / 'error.log',
        maxBytes=10485760,
        backupCount=5,
        encoding='utf-8'
    )
    error_handler.setLevel(logging.ERROR)
    error_handler.setFormatter(log_format)
    root_logger.addHandler(error_handler)
    
    # Set discord.py logging level
    discord_logger = logging.getLogger('discord')
    discord_logger.setLevel(logging.INFO)
    
    # Reduce http noise
    logging.getLogger('discord.http').setLevel(logging.WARNING)
    logging.getLogger('discord.gateway').setLevel(logging.INFO)

# Set up logging
setup_logging()
logger = logging.getLogger('bot')

# Create bot
intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(command_prefix="!", intents=intents)

@bot.event
async def on_ready():
    logger.info(f"Logged in as {bot.user} (ID: {bot.user.id})")
    logger.info(f"Connected to {len(bot.guilds)} guilds")

@bot.event
async def on_command(ctx):
    logger.info(
        f"[{ctx.guild}] {ctx.author} executed: {ctx.message.content}"
    )

@bot.event
async def on_command_error(ctx, error):
    logger.error(
        f"Error in command {ctx.command}: {error}",
        exc_info=error
    )

try:
    bot.run("TOKEN")
except Exception as e:
    logger.critical("Failed to start bot", exc_info=e)

Debugging Specific Issues

Enable detailed gateway logging:
import logging

logging.getLogger('discord.gateway').setLevel(logging.DEBUG)
logging.getLogger('discord.client').setLevel(logging.DEBUG)
This shows:
  • WebSocket connection attempts
  • HEARTBEAT/HEARTBEAT_ACK exchanges
  • IDENTIFY and RESUME operations
  • Disconnection reasons
Monitor HTTP requests and rate limits:
import logging

logging.getLogger('discord.http').setLevel(logging.DEBUG)
This shows:
  • All API requests
  • Rate limit headers
  • 429 responses
  • Retry attempts
See which events are being dispatched:
import logging

# Enable debug logging
logging.getLogger('discord.client').setLevel(logging.DEBUG)
Look for lines like:
DEBUG:discord.client:Dispatching event message
DEBUG:discord.client:Dispatching event member_join
Debug command framework:
import logging

logging.getLogger('discord.ext.commands').setLevel(logging.DEBUG)

Best Practices

  1. Use appropriate levels - Don’t log everything at DEBUG in production
  2. Rotate log files - Prevent logs from consuming all disk space
  3. Log errors with stack traces - Use exc_info=True for errors
  4. Include context - Log user IDs, guild names, command names, etc.
  5. Separate error logs - Keep errors in a separate file for easy review
  6. Don’t log tokens - Be careful not to log sensitive information
  7. Use structured logging - Consider JSON logging for easier parsing
  8. Monitor log size - Large log files can indicate issues

Common Issues

Problem: Logging statements don’t show upSolution: Make sure you’ve called logging.basicConfig() or set up handlers before creating the bot:
import logging

# Set up logging FIRST
logging.basicConfig(level=logging.INFO)

# Then create bot
bot = discord.Bot()
Problem: Logs are overwhelmingSolution: Reduce logging level or filter specific loggers:
import logging

# Set discord.http to WARNING to reduce noise
logging.getLogger('discord.http').setLevel(logging.WARNING)

# Keep main logger at INFO
logging.getLogger('discord').setLevel(logging.INFO)
Problem: File handler not writing logsSolution: Ensure the directory exists and permissions are correct:
from pathlib import Path

# Create directory if it doesn't exist
log_dir = Path("logs")
log_dir.mkdir(exist_ok=True)

# Then set up handler
handler = logging.FileHandler(log_dir / 'bot.log')

Build docs developers (and LLMs) love