Exception Handling
Exception Handling
Proper exception logging is critical for diagnosing production issues. The ByteHide Logs SDK captures exception details automatically and provides context to understand what went wrong.
Basic Exception Logging
Use the exception parameter to log exceptions with full details:
from bytehide_logs import Log
try:
file = open("/path/to/file.txt")
except FileNotFoundError as e:
Log.error("Configuration file not found", exception=e)from bytehide_logs import Log
try:
file = open("/path/to/file.txt")
except FileNotFoundError as e:
Log.error("Configuration file not found", exception=e)The SDK automatically captures:
- Exception type (
FileNotFoundError) - Error message
- Complete stack trace
- Exception chain (nested exceptions)
Exception Logging with Context
Combine exception information with contextual data:
try:
database.connect(host=db_host, port=db_port)
except ConnectionError as e:
Log.error(
"Database connection failed",
exception=e,
context={
"host": db_host,
"port": db_port,
"timeout_seconds": 30,
"environment": "production"
}
)try:
database.connect(host=db_host, port=db_port)
except ConnectionError as e:
Log.error(
"Database connection failed",
exception=e,
context={
"host": db_host,
"port": db_port,
"timeout_seconds": 30,
"environment": "production"
}
)Context is essential for understanding:
- What operation was being performed
- Which resources were involved
- Environmental factors that may have contributed
Try-Except Pattern with Logging
Handle exceptions while providing diagnostic information:
def fetch_user_data(user_id):
"""Fetch user from API with error handling and logging."""
try:
response = api.get(f"/users/{user_id}")
user_data = response.json()
Log.info(f"User data retrieved for user {user_id}")
return user_data
except requests.Timeout as e:
Log.warning(
f"API timeout retrieving user {user_id}",
exception=e,
context={"user_id": user_id, "timeout": "30s"}
)
return None
except requests.HTTPError as e:
if e.response.status_code == 404:
Log.warning(f"User not found: {user_id}", exception=e)
else:
Log.error(
f"API error retrieving user {user_id}",
exception=e,
context={"status_code": e.response.status_code}
)
return Nonedef fetch_user_data(user_id):
"""Fetch user from API with error handling and logging."""
try:
response = api.get(f"/users/{user_id}")
user_data = response.json()
Log.info(f"User data retrieved for user {user_id}")
return user_data
except requests.Timeout as e:
Log.warning(
f"API timeout retrieving user {user_id}",
exception=e,
context={"user_id": user_id, "timeout": "30s"}
)
return None
except requests.HTTPError as e:
if e.response.status_code == 404:
Log.warning(f"User not found: {user_id}", exception=e)
else:
Log.error(
f"API error retrieving user {user_id}",
exception=e,
context={"status_code": e.response.status_code}
)
return NoneException Chaining
Log exceptions that cause other exceptions:
try:
try:
result = risky_operation()
except OperationError as original_error:
raise ProcessingException("Failed to process data") from original_error
except ProcessingException as e:
Log.error(
"Processing failed with chained exceptions",
exception=e,
context={"operation": "data_processing"}
)try:
try:
result = risky_operation()
except OperationError as original_error:
raise ProcessingException("Failed to process data") from original_error
except ProcessingException as e:
Log.error(
"Processing failed with chained exceptions",
exception=e,
context={"operation": "data_processing"}
)The SDK preserves the full exception chain for debugging.
Selective Error Handling
Log different exception types with appropriate levels:
def process_payment(payment_data):
"""Process payment with appropriate error logging levels."""
try:
return payment_processor.charge(payment_data)
except CardDeclinedError as e:
# User error - warning level
Log.warning(
"Payment card declined",
exception=e,
context={
"card_last_four": payment_data.get("card_last_four"),
"amount": payment_data.get("amount")
}
)
except InvalidCardError as e:
# Validation error - warning level
Log.warning(
"Invalid card details provided",
exception=e,
context={"field": e.field}
)
except PaymentGatewayError as e:
# System error - error level
Log.error(
"Payment gateway error",
exception=e,
context={
"gateway": "stripe",
"retry_available": True
}
)def process_payment(payment_data):
"""Process payment with appropriate error logging levels."""
try:
return payment_processor.charge(payment_data)
except CardDeclinedError as e:
# User error - warning level
Log.warning(
"Payment card declined",
exception=e,
context={
"card_last_four": payment_data.get("card_last_four"),
"amount": payment_data.get("amount")
}
)
except InvalidCardError as e:
# Validation error - warning level
Log.warning(
"Invalid card details provided",
exception=e,
context={"field": e.field}
)
except PaymentGatewayError as e:
# System error - error level
Log.error(
"Payment gateway error",
exception=e,
context={
"gateway": "stripe",
"retry_available": True
}
)Traceback Information
Exception tracebacks are automatically included:
import traceback
try:
deeply_nested_function()
except Exception as e:
# Full traceback is captured automatically
Log.error("Unexpected error in nested function call", exception=e)
# No need to manually capture tracebackimport traceback
try:
deeply_nested_function()
except Exception as e:
# Full traceback is captured automatically
Log.error("Unexpected error in nested function call", exception=e)
# No need to manually capture tracebackThe SDK captures:
- Function names and line numbers
- Local variables (where applicable)
- File paths and code context
- Complete call stack
Best Practices
Always include context when logging exceptions:
# Good - includes context
try:
user = fetch_user(user_id)
except Exception as e:
Log.error("User fetch failed", exception=e, context={"user_id": user_id})
# Avoid - no context
try:
user = fetch_user(user_id)
except Exception as e:
Log.error("User fetch failed", exception=e)# Good - includes context
try:
user = fetch_user(user_id)
except Exception as e:
Log.error("User fetch failed", exception=e, context={"user_id": user_id})
# Avoid - no context
try:
user = fetch_user(user_id)
except Exception as e:
Log.error("User fetch failed", exception=e)Use appropriate log levels based on severity:
# Recoverable errors - use warning or info
Log.warning("Retrying failed request", exception=retry_error)
# Unexpected system errors - use error
Log.error("Unhandled database error", exception=db_error)
# Critical failures - use critical
Log.critical("Payment system offline", exception=critical_error)# Recoverable errors - use warning or info
Log.warning("Retrying failed request", exception=retry_error)
# Unexpected system errors - use error
Log.error("Unhandled database error", exception=db_error)
# Critical failures - use critical
Log.critical("Payment system offline", exception=critical_error)Log early and specifically rather than catching broadly:
# Good - specific exception with details
try:
database.execute(query)
except DatabaseError as e:
Log.error("Query execution failed", exception=e, context={"query_id": query.id})
raise
# Avoid - catching everything vaguely
try:
entire_request_handler()
except Exception as e:
Log.error("Something went wrong", exception=e)# Good - specific exception with details
try:
database.execute(query)
except DatabaseError as e:
Log.error("Query execution failed", exception=e, context={"query_id": query.id})
raise
# Avoid - catching everything vaguely
try:
entire_request_handler()
except Exception as e:
Log.error("Something went wrong", exception=e)Next Steps
- Learn about user identification to track which users experience errors
- Explore data masking to protect sensitive information in exception details
- Discover correlation IDs to trace errors across services