-
Notifications
You must be signed in to change notification settings - Fork 25
Logging
This guide explains how to enable logging in the mssql-python
driver to diagnose issues, monitor driver behavior, and troubleshoot database operations.
The mssql-python
driver includes a built-in logging system that captures detailed diagnostic information from both the Python layer and the underlying DDBC (Direct Database Connectivity) engine. Logging is disabled by default and can be easily enabled when needed.
- Comprehensive Diagnostics: Capture detailed information from both Python and C++ layers
- Simple Configuration: Just enable or disable - no complex log level settings to worry about
- Flexible Output: Log to file or console (stdout)
- Automatic File Management: Built-in log rotation prevents disk space issues
- Organized Storage: Logs stored in package directory with timestamped filenames
- Privacy & Security: Automatic sanitization of passwords and sensitive credentials
- Minimal Performance Impact: Disabled by default, minimal overhead when enabled
- Cross-Platform Support: Works consistently on Windows, macOS, and Linux
By default, logging writes to a file in the package installation directory:
import mssql_python
# Enable logging (writes to file only)
mssql_python.setup_logging()
# Your database operations
connection = mssql_python.connect("Server=localhost;Database=mydb;UID=user;PWD=password;...")
cursor = connection.cursor()
cursor.execute("SELECT * FROM users")
To see log output in real-time on your console:
import mssql_python
# Enable logging to console (stdout)
mssql_python.setup_logging('stdout')
# Your database operations
connection = mssql_python.connect("Server=localhost;Database=mydb;...")
The setup_logging()
function accepts a single parameter for output destination:
mssql_python.setup_logging(mode='file') # Default: log to file only
mssql_python.setup_logging('stdout') # Log to console
Mode | Description | Use Case |
---|---|---|
'file' |
Logs written to file only (default) | Production, persistent logging |
'stdout' |
Logs written to console | Development, real-time debugging, CI/CD |
Logs are written exclusively to a file in the package directory.
import mssql_python
# Explicitly specify file mode (this is the default)
mssql_python.setup_logging()
# or
mssql_python.setup_logging('file')
When to use:
- Production environments where you need persistent logs
- When analyzing issues after they occur
- When you want minimal console clutter
- Long-running applications that need historical logs
Logs are written to the console output for real-time monitoring.
import mssql_python
# Enable console output
mssql_python.setup_logging('stdout')
When to use:
- Development and debugging
- Real-time monitoring of driver behavior
- Interactive testing and troubleshooting
- CI/CD pipelines where console output is captured
- When you want immediate feedback
Log files are automatically stored in the logs/
subdirectory within the mssql_python
package installation:
<python-site-packages>/mssql_python/
├── __init__.py
├── connection.py
├── cursor.py
├── ...
└── logs/
├── mssql_python_trace_20251015_143022_12345.log
├── mssql_python_trace_20251015_143022_12345.log.1
└── mssql_python_trace_20251015_143022_12345.log.2
To find where logs are stored on your system:
import mssql_python
from mssql_python.logging_config import LoggingManager
import os
# Enable logging
mssql_python.setup_logging()
# Get the log directory location
manager = LoggingManager()
if manager.enabled:
log_dir = os.path.dirname(manager.log_file)
print(f"Logs are stored in: {log_dir}")
print(f"Current log file: {manager.log_file}")
Typical locations by platform:
-
Linux:
/usr/local/lib/python3.x/site-packages/mssql_python/logs/
-
macOS:
/Library/Frameworks/Python.framework/Versions/3.x/lib/python3.x/site-packages/mssql_python/logs/
-
Windows:
C:\Python3x\Lib\site-packages\mssql_python\logs\
-
Virtual Environment:
<venv>/lib/python3.x/site-packages/mssql_python/logs/
Log files use timestamped names for easy identification:
mssql_python_trace_YYYYMMDD_HHMMSS_PID.log
Components:
-
YYYYMMDD
: Date (e.g.,20251015
for October 15, 2025) -
HHMMSS
: Time (e.g.,143022
for 2:30:22 PM) -
PID
: Process ID (e.g.,12345
)
This ensures unique filenames for each execution and chronological sorting.
To prevent log files from consuming excessive disk space, the driver implements automatic log rotation:
Property | Value | Description |
---|---|---|
Maximum File Size | 512 MB | When a log file reaches 512 MB, it's rotated |
Backup Count | 5 | Keep up to 5 rotated backup files |
Total Maximum Size | ~3 GB | 512 MB × (1 active + 5 backups) |
How it works:
- Active log file:
mssql_python_trace_20251015_143022_12345.log
- When it reaches 512 MB, it's renamed to
.log.1
- A new active log file is created
- Older backups are shifted:
.log.1
→.log.2
,.log.2
→.log.3
, etc. - The oldest backup (
.log.5
) is deleted automatically
Each log entry follows this format:
YYYY-MM-DD HH:MM:SS,mmm - LEVEL - filename.py - [Layer] Message
Example:
2025-10-15 14:30:22,145 - INFO - connection.py - [Python Layer log] Connecting to server: localhost
2025-10-15 14:30:22,156 - DEBUG - connection.py - [DDBC Bindings log] Connection handle allocated
The driver distinguishes between two layers:
Messages from the Python code (connection management, query execution, error handling):
[Python Layer log] Executing query: SELECT * FROM users
[Python Layer log] Connection closed successfully
[Python Layer log] Cursor created for statement execution
Messages from the underlying C++ DDBC engine (low-level operations, TDS protocol, memory management):
[DDBC Bindings log] Allocating statement handle
[DDBC Bindings log] Binding parameter 1: type=SQL_VARCHAR, size=50
[DDBC Bindings log] SQLExecDirect completed, rows affected: 42
2025-10-15 14:30:22,145 - INFO - db_connection.py - [Python Layer log] Initializing connection
2025-10-15 14:30:22,147 - INFO - db_connection.py - [Python Layer log] Final connection string: Server=localhost;Database=mydb;UID=admin;PWD=***;
2025-10-15 14:30:22,150 - DEBUG - connection.py - [DDBC Bindings log] SQLAllocHandle(SQL_HANDLE_DBC) called
2025-10-15 14:30:22,152 - INFO - connection.py - [DDBC Bindings log] Connection established successfully
2025-10-15 14:30:22,155 - DEBUG - cursor.py - [Python Layer log] Creating cursor object
2025-10-15 14:30:22,157 - DEBUG - cursor.py - [DDBC Bindings log] SQLAllocHandle(SQL_HANDLE_STMT) called
2025-10-15 14:30:22,160 - INFO - cursor.py - [Python Layer log] Executing SQL: SELECT * FROM users WHERE active = 1
2025-10-15 14:30:22,175 - DEBUG - cursor.py - [DDBC Bindings log] SQLExecDirect completed, rows affected: 42
2025-10-15 14:30:22,180 - INFO - cursor.py - [Python Layer log] Query executed successfully
2025-10-15 14:30:22,185 - DEBUG - cursor.py - [Python Layer log] Fetching 42 rows
2025-10-15 14:30:22,190 - INFO - cursor.py - [Python Layer log] Fetch completed
The mssql-python
driver automatically sanitizes all sensitive information before writing to logs. You can safely enable logging without worrying about credential exposure.
The driver automatically removes or masks the following from logs:
Item | How It's Sanitized | Example |
---|---|---|
Passwords (PWD= ) |
Replaced with ***
|
PWD=secret123 → PWD=***
|
User IDs in auth contexts | Removed from sensitive operations | Authentication params filtered |
Access Tokens | Never logged | Token values are not written to logs |
Client Secrets | Never logged | Service principal secrets are not written |
Your Code:
import mssql_python
mssql_python.setup_logging()
connection_string = (
"Server=myserver.database.windows.net;"
"Database=mydb;"
"[email protected];"
"PWD=SuperSecret123!;"
"Encrypt=yes;"
)
conn = mssql_python.connect(connection_string)
What Gets Logged:
2025-10-15 14:30:22,147 - INFO - connection.py - [Python Layer log] Final connection string: Server=myserver.database.windows.net;Database=mydb;[email protected];PWD=***;Encrypt=yes;
Notice how PWD=SuperSecret123!
becomes PWD=***
automatically.
✅ Logs are safe to share with support teams - Passwords and secrets are automatically redacted
✅ No manual redaction required - The driver handles it for you
✅ SQL queries are logged - To help with troubleshooting (ensure queries don't contain secrets)
While the driver sanitizes credentials, follow these best practices:
- Review SQL queries: If your queries contain sensitive data (like SSNs or credit cards), be cautious when sharing logs
- Don't hardcode credentials: Use environment variables or secure credential stores
- Limit log sharing: Only share relevant portions of logs when reporting issues
import mssql_python
# Enable console logging to see connection diagnostics in real-time
mssql_python.setup_logging('stdout')
try:
connection = mssql_python.connect(
"Server=myserver.database.windows.net;"
"Database=mydb;"
"Authentication=ActiveDirectoryInteractive;"
"Encrypt=yes;"
)
print("Connection successful!")
except Exception as e:
print(f"Connection failed: {e}")
# Check the console output or log file for detailed diagnostic information
import mssql_python
# Production: Log to file for persistent diagnostics
mssql_python.setup_logging('file')
# Application logic
connection = mssql_python.connect(connection_string)
# ... your database operations ...
# Logs are automatically written to the package directory
import mssql_python
# Development: Use stdout for immediate feedback
mssql_python.setup_logging('stdout')
connection = mssql_python.connect(connection_string)
cursor = connection.cursor()
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
print(f"Fetched {len(rows)} rows")
# Watch the console for detailed operation logs
import mssql_python
import sys
# In CI/CD, log to stdout so it's captured in build logs
mssql_python.setup_logging('stdout')
try:
connection = mssql_python.connect(connection_string)
cursor = connection.cursor()
cursor.execute("SELECT 1")
print("✅ Database connectivity test: PASSED")
sys.exit(0)
except Exception as e:
print(f"❌ Database connectivity test: FAILED - {e}")
sys.exit(1)
import mssql_python
# Regular operations without logging
connection = mssql_python.connect("Server=localhost;...")
# Enable logging when you need to diagnose an issue
mssql_python.setup_logging('stdout')
# This operation will be logged
cursor = connection.cursor()
cursor.execute("SELECT * FROM problematic_table")
rows = cursor.fetchall()
# Note: Logging remains enabled for the rest of the session
-
Enable logging when developing or debugging
mssql_python.setup_logging('stdout')
-
Use file mode for production environments
mssql_python.setup_logging('file')
-
Call
setup_logging()
early in your applicationimport mssql_python # Call this before any database operations mssql_python.setup_logging() # Now proceed with your application
-
Enable logging when reporting issues
- Include relevant log excerpts in bug reports
- Logs are already sanitized, but review SQL queries if they contain sensitive data
-
Monitor disk space in long-running applications
- The automatic rotation handles this (max ~3GB)
- Be aware of the storage location in your package directory
-
Don't leave logging enabled in production unless needed
- Can generate large log files over time
- May have minor performance impact
- Enable only when troubleshooting
-
Don't ignore log rotation
- With 5 backups at 512MB each, you're using ~3GB in the package directory
- Plan disk space accordingly
-
Don't assume logs need manual redaction
- Passwords are automatically masked
- But review SQL queries if they contain sensitive business data
-
Don't call
setup_logging()
multiple times unnecessarily- The logger uses a singleton pattern
- Once enabled, it stays enabled for the session
-
Don't forget where logs are stored
- They're in the package installation directory, not your application directory
- Use the helper code above to find the exact location
Solution:
# Verify logging is enabled
from mssql_python.logging_config import LoggingManager
manager = LoggingManager()
print(f"Logging enabled: {manager.enabled}")
print(f"Log file: {manager.log_file}")
# If not enabled, call setup_logging()
import mssql_python
mssql_python.setup_logging()
Solution:
import mssql_python
from mssql_python.logging_config import LoggingManager
import os
# Enable logging first
mssql_python.setup_logging()
# Get the exact path
manager = LoggingManager()
log_dir = os.path.dirname(manager.log_file)
print(f"📁 Log directory: {log_dir}")
print(f"📄 Current log file: {manager.log_file}")
# List all log files
import glob
log_files = glob.glob(os.path.join(log_dir, "mssql_python_trace_*.log*"))
print(f"\n📋 Found {len(log_files)} log file(s):")
for f in sorted(log_files):
size_mb = os.path.getsize(f) / (1024 * 1024)
print(f" - {os.path.basename(f)} ({size_mb:.2f} MB)")
Possible Causes:
- Package installed in system directory without write permissions
- Virtual environment has restricted permissions
Solution:
# If using system Python, consider using a virtual environment
python -m venv myenv
source myenv/bin/activate # On Windows: myenv\Scripts\activate
pip install mssql-python
# Or install with user flag
pip install --user mssql-python
Solution:
import mssql_python
# Use stdout mode for real-time output
mssql_python.setup_logging('stdout')
# All operations will now print logs to console
Solution:
import mssql_python
from mssql_python.logging_config import LoggingManager
import shutil
import os
# Enable logging
mssql_python.setup_logging()
# Do your operations
connection = mssql_python.connect(connection_string)
# ... database operations ...
connection.close()
# Copy log file to desired location
manager = LoggingManager()
source_log = manager.log_file
destination = "/my/custom/path/app_logs/mssql_debug.log"
os.makedirs(os.path.dirname(destination), exist_ok=True)
shutil.copy2(source_log, destination)
print(f"Log copied to: {destination}")
Enabling logging has minimal performance impact for most applications:
Scenario | Performance Impact |
---|---|
Logging disabled | 0% (no overhead) |
Logging enabled | < 5% for typical workloads |
High-throughput applications | 5-10% in extreme cases |
-
Development: Always enable logging with
'stdout'
for immediate feedback -
Testing/Staging: Enable logging with
'file'
for diagnostics -
Production:
- Disable logging for normal operations
- Enable temporarily when investigating issues
- Use
'file'
mode if you need persistent production logging
- High-Performance Applications: Only enable logging when actively troubleshooting
A: No. Logging is disabled by default to minimize performance impact. You must explicitly call setup_logging()
to enable it.
A: The driver doesn't provide a disable method by design. Logging remains active once enabled for the session. The recommended approach is to enable it only when needed, or restart your application to disable it.
A: No. The driver automatically sanitizes passwords and sensitive credentials before writing to logs. Passwords are replaced with ***
and access tokens are never logged.
A: Yes! Log files are safe to share. Credentials are automatically redacted. Just review SQL queries if they contain sensitive business data (like SSNs, credit cards, etc.).
A:
-
'file'
: Logs written to file in package directory (silent console) -
'stdout'
: Logs written to console for real-time monitoring
A: Logs are stored in the logs/
subdirectory of the mssql_python
package installation. Use the code snippet in the "Finding Your Logs" section to get the exact path on your system.
A: Not directly. The driver stores logs in its package directory by design. However, you can copy or symlink the log files to another location after they're created.
A: No! The driver captures all relevant diagnostic information automatically when you enable logging. You don't need to configure log levels - just turn logging on or off.
A: You can parse the log file and filter by the prefix:
with open(log_file, 'r') as f:
for line in f:
if '[Python Layer log]' in line:
print(line) # Python layer only
# or
if '[DDBC Bindings log]' in line:
print(line) # DDBC layer only
A: The oldest backup (.log.5
) is automatically deleted, and newer files are shifted. This ensures you never exceed approximately 3GB of log storage in the package directory.
A: Yes! The logging system is thread-safe. Each thread's operations will be logged with proper synchronization.
If you encounter issues with logging or need assistance:
- Check the logs - Log files often contain detailed error information
- Consult the FAQ - See above for common questions
- GitHub Issues - Create an issue at github.com/microsoft/mssql-python/issues
- Email Support - Contact the mssql-python team
When reporting issues, please include:
- Log file excerpts (credentials are already sanitized!)
- Driver version:
pip show mssql-python
- Python version:
python --version
- Operating system and version
- Database server type (SQL Server, Azure SQL, etc.)
- Log file location on your system
The mssql-python
driver's logging system is designed for simplicity and security:
✅ Simple to use: Just call setup_logging()
or setup_logging('stdout')
✅ No complex configuration: No log levels to worry about
✅ Two output modes: File (persistent) or stdout (real-time)
✅ Automatic management: Log rotation prevents disk issues
✅ Privacy-first: Passwords and secrets automatically sanitized
✅ Package directory storage: Logs stored with the mssql_python package installation
✅ Minimal overhead: Negligible performance impact for most applications
✅ Developer-friendly: Detailed diagnostics for troubleshooting
Quick Start:
import mssql_python
# For development/debugging (real-time console output)
mssql_python.setup_logging('stdout')
# For production (persistent file logging)
mssql_python.setup_logging() # or setup_logging('file')