Frappe Logging & Error Tracking
Three Logging Mechanisms
| Mechanism |
Storage |
Use For |
frappe.logger() |
File (rotating) |
Application logging, debug info, audit trails |
frappe.log_error() |
Database (Error Log DocType) |
Errors visible in admin UI, persistent tracking |
frappe.log() / frappe.errprint() |
stderr / request-scoped |
Quick debugging only (NOT for production) |
Decision Tree
Need to log something?
│
├─ Application logging (info, debug, warnings)?
│ └─ frappe.logger("my_module").info("message")
│ → Writes to sites/{site}/logs/my_module.log
│
├─ Error that admins should see in Desk UI?
│ └─ frappe.log_error(title="Short desc", message=traceback)
│ → Creates Error Log document (queryable, auto-cleanup)
│
├─ Quick debug during development?
│ └─ frappe.errprint(variable) — shows in console
│ → NEVER leave in production code
│
├─ Track all HTTP requests?
│ └─ Set enable_frappe_logger: true in site_config.json
│ → Logs to frappe.web.log
│
├─ Performance monitoring?
│ └─ Set monitor: true in site_config.json
│ → Logs to monitor.json.log (JSON, per-request metrics)
│
└─ External error tracking (Sentry)?
└─ Set FRAPPE_SENTRY_DSN environment variable
→ Auto-captures unhandled exceptions
Quick Reference: frappe.logger()
# Get a logger for your module (ALWAYS specify module name)
logger = frappe.logger("my_app")
# Standard Python logging levels
logger.debug("Detailed diagnostic info")
logger.info("Normal operations: processed 50 records")
logger.warning("Something unexpected but recoverable")
logger.error("Operation failed", exc_info=True)
logger.critical("System-level failure")
# Full signature
frappe.logger(
module=None, # Logger name + log filename
with_more_info=False, # Auto-log request form_dict
allow_site=True, # Log under site's logs/ directory
filter=None, # Custom logging.Filter
max_size=100_000, # Max bytes per log file (100KB default)
file_count=20 # Rotated files retained (20 default)
)
Log location: sites/{site}/logs/{module}.log
Rotation: RotatingFileHandler — 100KB per file, 20 backups (~2MB total per logger)
Default Log Levels
| Mode |
Level |
Effect |
Development (_dev_server) |
WARNING |
Debug/info suppressed |
| Production |
ERROR |
Only errors and above |
# Change level dynamically
frappe.utils.logger.set_log_level("DEBUG")
Quick Reference: frappe.log_error()
# ALWAYS use keyword arguments (title/message can swap otherwise)
frappe.log_error(
title="Payment gateway timeout", # Short description (140 chars max)
message=frappe.get_traceback(), # Full error details
reference_doctype="Payment Entry", # Related DocType
reference_name="PE-00001" # Related document
)
# Minimal — auto-captures current traceback
try:
risky_operation()
except Exception:
frappe.log_error(title="Operation failed")
Error Log cleanup: Auto-deletes after 30 days. Manual: frappe.whitelist: clear_error_logs()
Auto-Captured Exceptions
Unhandled exceptions (HTTP 500+) are automatically logged to Error Log.
Excluded from auto-capture:
frappe.AuthenticationError
frappe.CSRFTokenError
frappe.SecurityException
frappe.InReadOnlyMode
Production Configuration
site_config.json Keys
| Key |
Value |
Effect |
enable_frappe_logger |
true |
HTTP request logging → frappe.web.log |
logging |
2 |
Log all SQL queries (debug only!) |
monitor |
true |
Request/job metrics → monitor.json.log |
disable_error_snapshot |
true |
Disable auto-capture of exceptions |
Environment Variables
| Variable |
Effect |
FRAPPE_STREAM_LOGGING=1 |
Log to stderr instead of files |
FRAPPE_SENTRY_DSN=<dsn> |
Enable Sentry error tracking |
ENABLE_SENTRY_DB_MONITORING |
Track SQL queries in Sentry |
SENTRY_TRACING_SAMPLE_RATE |
Performance tracing rate (0.0-1.0) |
Production Log Files
| File |
Content |
logs/web.error.log |
HTTP errors (supervisor) |
logs/web.log |
Gunicorn stdout |
logs/worker.error.log |
Background job errors |
logs/frappe.log |
Default frappe logger |
logs/frappe.web.log |
HTTP request metadata |
logs/monitor.json.log |
Performance metrics (JSON) |
sites/{site}/logs/*.log |
Per-site application logs |
Anti-Patterns
| NEVER |
ALWAYS |
Why |
print("debug info") |
frappe.logger("mod").info(...) |
print() disappears in production |
frappe.log_error("info msg") |
frappe.logger().info(...) |
log_error creates Error Log docs, clutters admin UI |
frappe.logger() (no module) |
frappe.logger("my_module") |
No-module mixes with framework logs |
frappe.log_error(title, msg) positional |
frappe.log_error(title=t, message=m) |
Positional args can swap (known quirk) |
| Log passwords/tokens |
Mask sensitive data |
SiteContextFilter only masks form_dict |
frappe.log() in production |
frappe.logger() |
frappe.log() is debug-only, request-scoped |
Leave logging=2 in prod |
Only during debugging |
Logs ALL SQL queries, massive I/O |
Version Differences
| Feature |
v14 |
v15+ |
frappe.logger() |
Yes |
Yes |
frappe.log_error() |
Yes |
+ defer_insert kwarg |
| Error Log trace_id |
-- |
Added |
| Error Log metadata |
-- |
JSON request/job context |
| Error snapshots |
File-based + scheduled collection |
Direct DB insert |
| Sentry integration |
Basic |
Enhanced (DB monitoring, profiling) |
guess_exception_source() |
-- |
Identifies which app caused error |
FRAPPE_STREAM_LOGGING |
Yes |
Yes |
Reference Files
- Logger API & Patterns — frappe.logger() advanced usage
- Error Tracking — Error Log, Sentry, monitoring
1---2name: frappe-core-logging3description: Use when implementing logging, error tracking, or monitoring in Frappe v14-v16. Covers frappe.logger() for file-based logging, frappe.log_error() for Error Log DocType entries, request logging, Sentry integration, and production logging patterns. Prevents common mistakes with print(), swapped log_error arguments, and sensitive data. Keywords: frappe.logger, log_error, Error Log, logging, Sentry,, where are the logs, how to log errors, error tracking, print not showing, production logs. monitor, request logging, error tracking, debug, production.4license: MIT5---67# Frappe Logging & Error Tracking89## Three Logging Mechanisms1011| Mechanism | Storage | Use For |12|-----------|---------|---------|13| `frappe.logger()` | File (rotating) | Application logging, debug info, audit trails |14| `frappe.log_error()` | Database (Error Log DocType) | Errors visible in admin UI, persistent tracking |15| `frappe.log()` / `frappe.errprint()` | stderr / request-scoped | Quick debugging only (NOT for production) |1617---1819## Decision Tree2021```22Need to log something?23│24├─ Application logging (info, debug, warnings)?25│ └─ frappe.logger("my_module").info("message")26│ → Writes to sites/{site}/logs/my_module.log27│28├─ Error that admins should see in Desk UI?29│ └─ frappe.log_error(title="Short desc", message=traceback)30│ → Creates Error Log document (queryable, auto-cleanup)31│32├─ Quick debug during development?33│ └─ frappe.errprint(variable) — shows in console34│ → NEVER leave in production code35│36├─ Track all HTTP requests?37│ └─ Set enable_frappe_logger: true in site_config.json38│ → Logs to frappe.web.log39│40├─ Performance monitoring?41│ └─ Set monitor: true in site_config.json42│ → Logs to monitor.json.log (JSON, per-request metrics)43│44└─ External error tracking (Sentry)?45 └─ Set FRAPPE_SENTRY_DSN environment variable46 → Auto-captures unhandled exceptions47```4849---5051## Quick Reference: frappe.logger()5253```python54# Get a logger for your module (ALWAYS specify module name)55logger = frappe.logger("my_app")5657# Standard Python logging levels58logger.debug("Detailed diagnostic info")59logger.info("Normal operations: processed 50 records")60logger.warning("Something unexpected but recoverable")61logger.error("Operation failed", exc_info=True)62logger.critical("System-level failure")6364# Full signature65frappe.logger(66 module=None, # Logger name + log filename67 with_more_info=False, # Auto-log request form_dict68 allow_site=True, # Log under site's logs/ directory69 filter=None, # Custom logging.Filter70 max_size=100_000, # Max bytes per log file (100KB default)71 file_count=20 # Rotated files retained (20 default)72)73```7475**Log location:** `sites/{site}/logs/{module}.log`76**Rotation:** RotatingFileHandler — 100KB per file, 20 backups (~2MB total per logger)7778### Default Log Levels7980| Mode | Level | Effect |81|------|-------|--------|82| Development (`_dev_server`) | WARNING | Debug/info suppressed |83| Production | ERROR | Only errors and above |8485```python86# Change level dynamically87frappe.utils.logger.set_log_level("DEBUG")88```8990---9192## Quick Reference: frappe.log_error()9394```python95# ALWAYS use keyword arguments (title/message can swap otherwise)96frappe.log_error(97 title="Payment gateway timeout", # Short description (140 chars max)98 message=frappe.get_traceback(), # Full error details99 reference_doctype="Payment Entry", # Related DocType100 reference_name="PE-00001" # Related document101)102103# Minimal — auto-captures current traceback104try:105 risky_operation()106except Exception:107 frappe.log_error(title="Operation failed")108```109110**Error Log cleanup:** Auto-deletes after 30 days. Manual: `frappe.whitelist: clear_error_logs()`111112### Auto-Captured Exceptions113114Unhandled exceptions (HTTP 500+) are automatically logged to Error Log.115116**Excluded from auto-capture:**117- `frappe.AuthenticationError`118- `frappe.CSRFTokenError`119- `frappe.SecurityException`120- `frappe.InReadOnlyMode`121122---123124## Production Configuration125126### site_config.json Keys127128| Key | Value | Effect |129|-----|-------|--------|130| `enable_frappe_logger` | `true` | HTTP request logging → `frappe.web.log` |131| `logging` | `2` | Log all SQL queries (debug only!) |132| `monitor` | `true` | Request/job metrics → `monitor.json.log` |133| `disable_error_snapshot` | `true` | Disable auto-capture of exceptions |134135### Environment Variables136137| Variable | Effect |138|----------|--------|139| `FRAPPE_STREAM_LOGGING=1` | Log to stderr instead of files |140| `FRAPPE_SENTRY_DSN=<dsn>` | Enable Sentry error tracking |141| `ENABLE_SENTRY_DB_MONITORING` | Track SQL queries in Sentry |142| `SENTRY_TRACING_SAMPLE_RATE` | Performance tracing rate (0.0-1.0) |143144### Production Log Files145146| File | Content |147|------|---------|148| `logs/web.error.log` | HTTP errors (supervisor) |149| `logs/web.log` | Gunicorn stdout |150| `logs/worker.error.log` | Background job errors |151| `logs/frappe.log` | Default frappe logger |152| `logs/frappe.web.log` | HTTP request metadata |153| `logs/monitor.json.log` | Performance metrics (JSON) |154| `sites/{site}/logs/*.log` | Per-site application logs |155156---157158## Anti-Patterns159160| NEVER | ALWAYS | Why |161|-------|--------|-----|162| `print("debug info")` | `frappe.logger("mod").info(...)` | print() disappears in production |163| `frappe.log_error("info msg")` | `frappe.logger().info(...)` | log_error creates Error Log docs, clutters admin UI |164| `frappe.logger()` (no module) | `frappe.logger("my_module")` | No-module mixes with framework logs |165| `frappe.log_error(title, msg)` positional | `frappe.log_error(title=t, message=m)` | Positional args can swap (known quirk) |166| Log passwords/tokens | Mask sensitive data | SiteContextFilter only masks form_dict |167| `frappe.log()` in production | `frappe.logger()` | frappe.log() is debug-only, request-scoped |168| Leave `logging=2` in prod | Only during debugging | Logs ALL SQL queries, massive I/O |169170---171172## Version Differences173174| Feature | v14 | v15+ |175|---------|:---:|:----:|176| `frappe.logger()` | Yes | Yes |177| `frappe.log_error()` | Yes | + `defer_insert` kwarg |178| Error Log trace_id | -- | Added |179| Error Log metadata | -- | JSON request/job context |180| Error snapshots | File-based + scheduled collection | Direct DB insert |181| Sentry integration | Basic | Enhanced (DB monitoring, profiling) |182| `guess_exception_source()` | -- | Identifies which app caused error |183| `FRAPPE_STREAM_LOGGING` | Yes | Yes |184185---186187## Reference Files188189- [Logger API & Patterns](references/logger-patterns.md) — frappe.logger() advanced usage190- [Error Tracking](references/error-tracking.md) — Error Log, Sentry, monitoring