Hướng dẫn Loguru Python Logging: Thiết lập không cần cấu hình
Học Loguru, framework logging dễ nhất của Python. Thành thạo xoay vòng file, logging có cấu trúc, xử lý ngoại lệ, và custom sinks mà không cần cấu hình.
Bạn đã debug bằng các câu lệnh print() suốt cả cuộc đời lập trình của mình. Có thể bạn đã thử module logging của Python vài lần, nhưng rồi quay lại dùng print vì chúng đơn giản hơn.
Thực ra có một thứ tốt hơn mà vẫn dễ dùng như vậy.
Câu lệnh print không thể lưu vào file hay bật/tắt dễ dàng. Logging tích hợp sẵn của Python làm được, nhưng bạn phải viết phần thiết lập còn dài hơn cả chương trình chính.
Loguru đi theo hướng khác: logging nên dễ như print, nhưng hữu ích hơn rất nhiều. Bạn có màu sắc trong terminal, dấu thời gian tự động, và khả năng lưu mọi thứ vào file mà không cần viết một dòng cấu hình nào.
Trong hướng dẫn này, tôi sẽ trình bày các ví dụ thực tế để logging trở nên tự nhiên thay vì nặng nề. Bạn sẽ thấy những tính năng có lẽ chưa biết đến, sử dụng các mẫu code giúp việc debug dễ dàng hơn.
Nếu bạn mới tìm hiểu debug trong Python hoặc muốn củng cố kỹ năng nền tảng,xây dựng nền tảng Python vững chắc sẽ giúp bạn hiểu khi nào và vì sao logging là cần thiết.
Với module logging thông thường, bạn sẽ phải thiết lập handlers, formatters, và cấu hình đủ thứ trước khi thấy được một dòng log.
Để so sánh sâu hơn về các cách logging tích hợp sẵn trong Python, hãy xemhướng dẫn logging toàn diện bao quát các phương pháp truyền thống.
Với Loguru, bạn chỉ cần import và bắt đầu log:
from loguru import logger
# Instead of print("Processing user data...")
logger.info("Processing user data...")# Instead of print(f"Found {count} records")
logger.info("Found {} records", count)Được hỗ trợ bởi
Kết quả xuất hiện trong terminal với màu sắc, dấu thời gian và cấp độ log tự động. Không cần thiết lập.
Giả sử bạn đang xây dựng một scraper web đơn giản. Cách debug của bạn sẽ thay đổi như sau:
import requests
from loguru import logger
defscrape_website(url):
logger.info("Starting scrape for {}", url)
response = requests.get(url)
logger.debug("Response status: {}", response.status_code)if response.status_code ==200:
logger.success("Successfully scraped {} characters",len(response.text))return response.text
else:
logger.error("Failed to scrape {}: status {}", url, response.status_code)returnNoneĐược hỗ trợ bởi
Lưu ý các cấp độ log khác nhau (info, debug, success, error) và kiểu định dạng ngoặc nhọn. Mọi thứ hoạt động ngay lập tức mà không cần file cấu hình hay mã thiết lập.
Ghi log ra file và xoay vòng đơn giản
Khi debug bằng output trên console, bạn mất sạch mọi thứ ngay khi đóng terminal. Bạn không thể tra cứu những gì xảy ra một giờ trước, và nếu script sập qua đêm, sẽ không còn dấu vết nào cho thấy lỗi ở đâu. Bạn cũng khó chia sẻ output debug với đồng đội khi cần trợ giúp.
Ghi log ra file giải quyết điều này bằng cách lưu mọi thứ một cách lâu dài.
Logging truyền thống buộc bạn viết mã cấu hình để thiết lập file handler. Bạn phải chỉ định đường dẫn, tạo thư mục, và xử lý quyền. Với Loguru, thêm ghi log ra file thực sự chỉ một dòng:
from loguru import logger
# Add file logging alongside console output
logger.add("app.log")
logger.info("This appears in both console and file")
logger.error("So does this error message")Được hỗ trợ bởi
Giờ đây mọi thứ được lưu vào app.log đồng thời vẫn hiển thị trên terminal.
Nhưng log file sẽ phình to theo thời gian. Một ứng dụng web chạy vài ngày có thể sinh ra hàng GB log và cuối cùng đầy ổ đĩa. Xoay vòng file giải quyết việc này bằng cách tự động tạo file log mới khi đạt điều kiện nhất định. File cũ được đổi tên kèm dấu thời gian, và một file mới bắt đầu:
# When app.log reaches 10 MB, it becomes app_2024-07-15_14-30-25.log# and a new empty app.log starts
logger.add("app.log", rotation="10 MB")# Or start a new file every day at midnight
logger.add("app.log", rotation="00:00")Được hỗ trợ bởi
Dấu thời gian mặc định hoạt động ổn, nhưng thường ở UTC hoặc khó đọc. Loguru cho phép bạn tùy chỉnh định dạng datetime theo nhu cầu:
# Custom timestamp format that's easier to read
logger.add("app.log",format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}",
rotation="1 day")Được hỗ trợ bởi
Với hệ thống production, bạn cần kiểm soát tốt hơn các file cũ. Logging trong production đòi hỏi tuân theo các thực hành tốt của Python để log có thể bảo trì và an toàn. Nếu không quản lý, log file sẽ chất đống và chiếm dụng lưu trữ:
# Keep only the last 10 rotated files, compress the rest
logger.add("production.log",
rotation="50 MB",# New file every 50MB
retention=10,# Keep only 10 old files
compression="zip")# Compress rotated filesĐược hỗ trợ bởi
Hãy xem trong một kịch bản thực tế. Bạn đang xây dựng pipeline dữ liệu xử lý nhiều file CSV mỗi đêm, làm sạch và kiểm tra dữ liệu khách hàng trước khi nạp vào cơ sở dữ liệu. Bạn cần theo dõi file nào xử lý thành công và bắt các vấn đề chất lượng dữ liệu:
import pandas as pd
from loguru import logger
# Production-ready file logging setup
logger.add("data_processing_{time:YYYY-MM-DD}.log",
rotation="00:00",# New file daily
retention="1 week",# Keep 1 week of logsformat="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}")defprocess_csv_files(file_list):
logger.info("Starting batch processing of {} files",len(file_list))for filename in file_list:try:
df = pd.read_csv(filename)
logger.info("Loaded {} with {} rows", filename,len(df))# Your processing logic here
processed_df = df.dropna()
logger.success("Processed {} - kept {} rows", filename,len(processed_df))except Exception as e:
logger.error("Failed to process {}: {}", filename,str(e))
logger.info("Batch processing complete")Được hỗ trợ bởi
Log của bạn được lưu với dấu thời gian dễ đọc, file tự xoay vòng, và log cũ tự dọn dẹp. Bạn có thể xem lại chuyện gì đã xảy ra sau vài ngày hoặc gửi file log cho đồng nghiệp khi cần khắc phục sự cố.
Logging có cấu trúc cho ứng dụng hiện đại
Khi chạy ứng dụng web, log nhanh chóng trở thành một mớ hỗn độn. Hành vi người dùng, truy vấn cơ sở dữ liệu, lời gọi API và lỗi lẫn lộn trong một bức tường chữ. Bạn khó lọc theo người dùng cụ thể, truy vết từng request, hay tự động phân tích log cho hệ thống giám sát.
Ứng dụng hướng dữ liệu đặc biệt hưởng lợi từ logging có cấu trúc. Nếu bạn làm việc với pipeline dữ liệu hoặc phân tích, phát triển kỹ năng data analyst sẽ giúp bạn hiểu nên ghi lại thông tin gì trong log.
Logging dạng chuỗi truyền thống làm mọi thứ tệ hơn. Mỗi thông điệp log chỉ là văn bản, nên việc trích xuất dữ liệu có ý nghĩa đòi hỏi regex phức tạp hoặc tìm kiếm thủ công.
Logging có cấu trúc thay đổi điều này bằng cách coi mỗi mục log như một đối tượng dữ liệu thay vì chỉ là văn bản. Mỗi mục log trở thành một bản ghi có cấu trúc với các trường có thể tìm kiếm, giúp truy vấn và phân tích dễ dàng bằng chương trình.
Logging JSON có cấu trúc với serialize=True
Loguru khiến logging có cấu trúc trở nên đơn giản với tùy chọn serialize=True:
Tham số serialize=True báo cho Loguru xuất JSON thay vì văn bản thuần. Thay vì câu chữ dễ đọc, file log giờ chứa dữ liệu có cấu trúc:
{"text":"User login","record":{"time":"2024-07-15T14:30:25","level":{"name":"INFO"},"extra":{"user_id":123,"ip_address":"192.168.1.1"}}}Được hỗ trợ bởi
Định dạng này hoạt động hoàn hảo với các công cụ phân tích log có thể tự động tìm kiếm và lọc dữ liệu JSON.
Gắn ngữ cảnh (context binding)
Nhưng ứng dụng web cần nhiều hơn dữ liệu có cấu trúc. Bạn cần ngữ cảnh kết nối các mục log liên quan. Khi xử lý một request người dùng, bạn muốn mọi log liên quan đều chia sẻ thông tin chung như user ID hoặc session ID.
Gắn ngữ cảnh giải quyết điều này bằng cách đính kèm thông tin thường trực vào logger của bạn bằng logger.bind():
from loguru import logger
defprocess_user_order(user_id, order_data):# Create a logger with attached context
order_logger = logger.bind(user_id=user_id, order_id=order_data['id'])
order_logger.info("Starting order processing")
order_logger.debug("Validating payment method", method=order_data['payment'])
order_logger.info("Order processing complete", total=order_data['total'])Được hỗ trợ bởi
Giờ đây mọi log từ order_logger tự động bao gồm user_id và order_id, giúp dễ dàng truy vết toàn bộ log liên quan đến một đơn hàng cụ thể.
Nhiều sink cho các đầu ra khác nhau
Ứng dụng hiện đại cũng cần các đầu ra log khác nhau cho các mục đích khác nhau. Bạn có thể muốn log dễ đọc trên console khi phát triển, nhưng log JSON cho giám sát production. Trong Loguru, mỗi nơi nhận log được gọi là “sink” — hãy coi đó là nơi thông điệp log chảy tới.
Loguru cho phép bạn thêm nhiều sink với định dạng và quy tắc khác nhau:
from loguru import logger
import sys
# Remove the default console output
logger.remove()# Sink 1: Human-readable console for development
logger.add(sys.stdout,
level="DEBUG",format="<green>{time:HH:mm:ss}</green> | {level} | {message}")# Sink 2: JSON file for production monitoring
logger.add("app.log",
serialize=True,
level="INFO")# Sink 3: Error-only file for alerts
logger.add("errors.log",
level="ERROR")Được hỗ trợ bởi
Lệnh logger.remove() loại bỏ output console mặc định của Loguru để bạn tự thiết lập. Mỗi logger.add() tạo một sink mới với quy tắc riêng về những gì được log và cách định dạng.
Giờ đây cùng một câu lệnh log sẽ cho ra các đầu ra khác nhau:
logger.debug("Processing started")# Only appears in console
logger.info("User data loaded")# Appears in console and app.log
logger.error("Database connection failed")# Appears in all three placesĐược hỗ trợ bởi
Ví dụ hoàn chỉnh Loguru: Hệ thống đăng ký người dùng
Hãy xem một ví dụ đơn giản và thực tế hơn về việc dùng Loguru — một hệ thống đăng ký người dùng cơ bản:
from loguru import logger
import sys
# Set up multiple outputs
logger.remove()
logger.add(sys.stdout, level="INFO",format="{time:HH:mm:ss} | {message}")
logger.add("user_registration.log", serialize=True, level="INFO")
logger.add("errors.log", level="ERROR")defregister_user(email, password):# Create logger with user context
user_logger = logger.bind(email=email)
user_logger.info("Starting user registration")# Validate emailif"@"notin email:
user_logger.error("Invalid email format")returnFalse# Check if user exists
user_logger.debug("Checking if user already exists")# Simulate password hashing
user_logger.debug("Hashing password")# Save to database
user_logger.info("Saving user to database")
user_logger.success("User registration completed")returnTrue# Usage
register_user("[email protected]","secure123")Được hỗ trợ bởi
Thiết lập này mang đến output console dễ đọc khi phát triển, log JSON có cấu trúc cho giám sát tự động, và log lỗi riêng cho cảnh báo. Phương thức bind() đảm bảo mọi log cho mỗi lượt đăng ký đều bao gồm địa chỉ email, giúp debug dễ dàng hơn nhiều.
Xử lý ngoại lệ với Loguru
Khi code bị crash, bạn thường nhận một stack trace cơ bản cho biết lỗi xảy ra ở đâu, nhưng không nhiều về lý do. Bạn mất đi giá trị biến tại thời điểm crash, khiến khó hiểu chuyện gì đã sai. Xử lý ngoại lệ truyền thống cũng yêu cầu bạn bao bọc mọi thứ trong khối try-catch và tự log từng ngoại lệ.
Điều này đặc biệt đau đầu khi debug sự cố production. Người dùng báo lỗi, nhưng bạn không tái hiện được vì thiếu ngữ cảnh tồn tại khi lỗi xảy ra.
Loguru giải quyết bằng việc tự động bắt ngoại lệ và cung cấp traceback nâng cao cho bạn thấy chính xác chuyện gì đang diễn ra trong code khi mọi thứ trục trặc.
Tự động bắt ngoại lệ với @logger.catch
Decorator @logger.catch tự động bắt mọi ngoại lệ xảy ra trong một hàm và log lại với đầy đủ ngữ cảnh. Các decorator như @logger.catch là mộtkhái niệm Python trung cấp có thể thay đổi cách bạn xử lý lỗi trên toàn bộ codebase:
from loguru import logger
@logger.catchdefdivide_numbers(a, b):
result = a / b
return result
# This automatically logs the exception with full traceback
result = divide_numbers(10,0)Được hỗ trợ bởi
Thay vì chương trình sập với thông báo lỗi cơ bản, Loguru bắt ngoại lệ và log lại với thông tin chi tiết, trong khi chương trình của bạn tiếp tục chạy. Decorator hoạt động với mọi hàm — method của class, hàm async, hoặc hàm thông thường.
Traceback nâng cao với kiểm tra biến
Nhưng Loguru còn đi xa hơn với traceback nâng cao hiển thị giá trị biến ở mỗi mức của call stack. Tính năng này gọi là “diagnose” và giống như có một trình debug tích hợp ngay trong log của bạn:
from loguru import logger
# Enable enhanced tracebacks
logger.add("debug.log", diagnose=True)@logger.catchdefprocess_user_data(users):for user in users:
age = user['age']if age >0:
years_to_retirement =65- age
print(f"User {user['name']} has {years_to_retirement} years to retirement")# When this crashes, you'll see the exact values of 'users', 'user', and 'age'
users_data =[{"name":"Alice","age":30},{"name":"Bob","age":"invalid"},# This will cause an error]
process_user_data(users_data)Được hỗ trợ bởi
Với diagnose=True, khi hàm gặp lỗi do cố trừ số với chuỗi, Loguru hiển thị chính xác những gì trong từng biến:
TypeError: unsupported operand type(s) for -: 'int' and 'str'
Variables values:
users = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": "invalid"}]
user = {"name": "Bob", "age": "invalid"}
age = "invalid"Được hỗ trợ bởi
Mức độ chi tiết này giúp debug nhanh hơn vì bạn thấy chính xác dữ liệu nào gây ra vấn đề.
Xử lý ngoại lệ theo môi trường
Tuy nhiên, traceback nâng cao có thể lộ thông tin nhạy cảm như mật khẩu hoặc API key trong log production. Loguru cho phép bạn kiểm soát hành vi này theo từng môi trường:
from loguru import logger
import os
# Development: Full diagnostic informationif os.getenv("ENVIRONMENT")=="development":
logger.add("app.log", diagnose=True, level="DEBUG")else:# Production: Basic logging without variable inspection
logger.add("app.log", diagnose=False, level="INFO")@logger.catchdefauthenticate_user(username, password, api_key):# In development, you'll see all variable values if this crashes# In production, sensitive data stays hiddenifnot username ornot password:raise ValueError("Missing credentials")Được hỗ trợ bởi
Đánh giá lười (lazy evaluation) để tối ưu hiệu năng
Một tính năng mạnh khác là đánh giá lười, giúp cải thiện hiệu năng khi log các thao tác tốn kém. Đôi khi bạn muốn log thông tin phức tạp, nhưng chỉ khi thật sự có lỗi xảy ra:
from loguru import logger
@logger.catchdefprocess_large_dataset(data):try:# Some complex processing that might fail
result = complex_calculation(data)return result
except Exception as e:# This expensive operation only runs if there's an error
logger.error("Processing failed with data: {data}",
data=lambda: expensive_data_summary(data))raisedefexpensive_data_summary(data):# This function takes time to run, summarizing huge datasetsreturn{"row_count":len(data),"columns":list(data.keys()),"memory_usage": data.memory_usage()}Được hỗ trợ bởi
Cú pháp lambda: tạo một hàm chỉ được gọi khi Loguru thực sự ghi thông điệp log. Nếu không có lỗi, expensive_data_summary() sẽ không chạy, tiết kiệm thời gian xử lý.
Chiến lược khác nhau cho các loại lỗi khác nhau
Bạn cũng có thể dùng các chiến lược xử lý ngoại lệ khác nhau cho từng loại lỗi:
from loguru import logger
# Configure multiple outputs for different error severities
logger.add("warnings.log", level="WARNING",filter=lambda record: record["level"].name =="WARNING")
logger.add("critical_errors.log", level="ERROR")@logger.catch(level="ERROR")defcritical_database_operation(query):# Database errors get logged as ERROR level
execute_database_query(query)@logger.catch(level="WARNING", reraise=False)defoptional_feature(user_id):# Non-critical features log as WARNING and continue execution
send_optional_notification(user_id)defprocess_user_request(user_id, query):# Critical operation - if this fails, the whole request fails
critical_database_operation(query)# Optional operation - if this fails, request continues
optional_feature(user_id)Được hỗ trợ bởi
Tham số reraise=False báo Loguru log ngoại lệ nhưng không ném lại, cho phép chương trình tiếp tục. Điều này hữu ích với các tính năng tùy chọn không nên làm gián đoạn luồng chính.
Ví dụ hoàn chỉnh: xử lý file với xử lý lỗi
Hãy xem ví dụ hoàn chỉnh với hệ thống xử lý file ứng xử khéo léo với các loại lỗi khác nhau:
from loguru import logger
import os
# Set up environment-aware loggingif os.getenv("DEBUG"):
logger.add("file_processor.log", diagnose=True, level="DEBUG")else:
logger.add("file_processor.log", diagnose=False, level="INFO")@logger.catch(reraise=False)defvalidate_file_format(filename):ifnot filename.endswith(('.csv','.json','.xml')):raise ValueError(f"Unsupported file format: {filename}")@logger.catchdefload_file_data(filename):
logger.info("Loading file: {}", filename)# Expensive operation only logged on errors
logger.debug("File stats: {stats}",
stats=lambda:{"size": os.path.getsize(filename),"modified": os.path.getmtime(filename)})withopen(filename,'r')as f:return f.read()defprocess_files(file_list):
results =[]for filename in file_list:# Validation errors are logged but don't stop processing
validate_file_format(filename)try:# Loading errors are logged and do stop processing this file
data = load_file_data(filename)
results.append(data)
logger.success("Successfully processed {}", filename)except Exception:
logger.error("Skipping file {} due to loading error", filename)continuereturn resultsĐược hỗ trợ bởi
Thiết lập này mang đến khả năng tự động bắt ngoại lệ, ngữ cảnh lỗi chi tiết khi cần, và các chiến lược xử lý khác nhau cho mỗi loại lỗi. Log của bạn hữu ích hơn rất nhiều cho việc debug, trong khi code vẫn gọn gàng và dễ đọc.
Cấu hình và tùy biến Loguru nâng cao
Khi ứng dụng phát triển, bạn sẽ gặp những tình huống mà mặc định của Loguru chưa phù hợp. Có thể bạn cần một cấp độ log riêng cho số đo nghiệp vụ, muốn lọc thông tin nhạy cảm khỏi log, hoặc cần gửi cảnh báo lên Slack khi có lỗi.
Tin tốt là Loguru được xây dựng để tùy biến mà không đánh mất sự đơn giản. Bạn có thể mở rộng theo cách mà với hệ thống logging truyền thống sẽ đòi hỏi cấu hình phức tạp, trong khi code vẫn sạch và dễ đọc.
Hãy xem những tùy biến hữu ích nhất giải quyết các vấn đề thực tế bạn sẽ gặp trong ứng dụng production.
Cấp độ log và màu sắc tuỳ chỉnh
Loguru có sẵn các cấp độ chuẩn như INFO, DEBUG và ERROR. Nhưng đôi khi bạn cần thứ gì đó đặc thù cho ứng dụng. Có thể bạn muốn một cấp độ riêng cho số liệu hiệu năng hoặc sự kiện nghiệp vụ.
Tạo cấp độ log tuỳ chỉnh rất đơn giản:
from loguru import logger
# Add a custom level for business metrics
logger.level("METRIC", no=38, color="<yellow>", icon="📊")# Add a custom level for security events
logger.level("SECURITY", no=45, color="<red><bold>", icon="🔒")# Now you can use these levels
logger.log("METRIC","Order completed in {} seconds",2.5)
logger.log("SECURITY","Failed login attempt from IP: {}","192.168.1.100")Được hỗ trợ bởi
Tham số no đặt mức độ ưu tiên của level (số càng lớn = ưu tiên càng cao). Các level chuẩn: DEBUG=10, INFO=20, WARNING=30, ERROR=40, CRITICAL=50.
Hãy xem trong bối cảnh huấn luyện machine learning:
from loguru import logger
# Set up custom levels for ML tracking
logger.level("EPOCH", no=35, color="<cyan><bold>")
logger.level("CHECKPOINT", no=38, color="<green><bold>")# Configure output
logger.remove()
logger.add("training_{time:YYYY-MM-DD}.log", level="INFO")deftrain_model(model, data_loader, epochs=10):
logger.info("Starting training with {} epochs", epochs)for epoch inrange(epochs):# Log epoch progress with custom level
logger.log("EPOCH","Epoch {}/{} started", epoch +1, epochs)# Training logic here
loss =0.523-(epoch *0.05)# Simulated improving loss
accuracy =0.72+(epoch *0.03)# Simulated improving accuracy
logger.info("Loss: {:.4f}, Accuracy: {:.2%}", loss, accuracy)# Save checkpoint every 5 epochsif(epoch +1)%5==0:
logger.log("CHECKPOINT","Model saved at epoch {}", epoch +1)
logger.success("Training completed!")Được hỗ trợ bởi
Chặn và sửa đổi thông điệp log
Đôi khi bạn cần sửa đổi thông điệp log trước khi ghi. Trường hợp phổ biến gồm loại bỏ dữ liệu nhạy cảm, thêm request ID, hoặc định dạng lại thông điệp cho công cụ cụ thể.
Loguru cho phép chặn thông điệp bằng tham số filter:
from loguru import logger
import re
defhide_sensitive_data(record):# Hide credit card numbers
record["message"]= re.sub(r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b','XXXX-XXXX-XXXX-XXXX',
record["message"])# Hide email addresses
record["message"]= re.sub(r'\b[\w.-]+@[\w.-]+\.\w+\b','***@***.***',
record["message"])# Continue processing the logreturnTrue# Add the filter to your logger
logger.add("secure.log",filter=hide_sensitive_data)# Test it
logger.info("User paid with card 4532-1234-5678-9012")
logger.info("Contact email: [email protected]")Được hỗ trợ bởi
Bạn cũng có thể dùng filter để thêm thông tin ngữ cảnh:
from loguru import logger
import threading
defadd_thread_info(record):# Add thread name to every log message
record["extra"]["thread_name"]= threading.current_thread().name
returnTruedefadd_environment_info(record):# Add environment to production logs
record["extra"]["env"]="production"
record["extra"]["region"]="us-west-2"returnTrue# Apply multiple filters
logger.add("app.log",filter=lambda record: add_thread_info(record)and add_environment_info(record),format="{time} | {extra[env]} | {extra[thread_name]} | {message}")Được hỗ trợ bởi
Sink tuỳ chỉnh cho dịch vụ bên ngoài
Sink là nơi log của bạn chảy tới. Dù file và console là phổ biến, bạn thường cần gửi log tới các dịch vụ bên ngoài như Slack, email, hoặc hệ thống giám sát.
Đây là cách tạo một sink tuỳ chỉnh gửi lỗi lên Slack:
from loguru import logger
import requests
defsend_to_slack(message):"""Custom sink that sends messages to Slack"""# Parse the log record
record = message.record
# Only send errors and aboveif record["level"].no <40:# ERROR level is 40return# Format message for Slack
slack_message ={"text":f"🚨 {record['level'].name}: {record['message']}","attachments":[{"color":"danger","fields":[{"title":"Time","value":str(record["time"]),"short":True},{"title":"Function","value": record["function"],"short":True},{"title":"File","value":f"{record['file'].name}:{record['line']}","short":False}]}]}# Send to Slack webhook
webhook_url ="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
requests.post(webhook_url, json=slack_message)# Add the custom sink
logger.add(send_to_slack, level="ERROR")# Test it
logger.error("Database connection lost!")Được hỗ trợ bởi
Đây là một ví dụ thực tế khác — một sink tuỳ chỉnh thu thập số đo hiệu năng trong suốt quá trình chạy và lưu lại để phân tích:
from loguru import logger
from collections import defaultdict
import json
classMetricsCollector:def__init__(self):
self.metrics = defaultdict(list)def__call__(self, message):"""This method is called for every log message"""
record = message.record
# Only process METRIC level logsif record["level"].name !="METRIC":return# Extract metric data from the 'extra' fields
extra = record["extra"]if"metric_name"in extra and"value"in extra:
self.metrics[extra["metric_name"]].append({"value": extra["value"],"timestamp": record["time"].timestamp()})defsave_metrics(self):"""Save all collected metrics to a JSON file"""withopen("metrics.json","w")as f:
json.dump(dict(self.metrics), f, indent=2)# Create the collector and add it as a sink
collector = MetricsCollector()
logger.add(collector)# Create the METRIC level
logger.level("METRIC", no=38)# Now when you log metrics, they're automatically collected# The bind() method attaches extra data to the log entry
logger.bind(metric_name="response_time", value=0.234).log("METRIC","API response")
logger.bind(metric_name="response_time", value=0.189).log("METRIC","API response")
logger.bind(metric_name="active_users", value=1523).log("METRIC","User count")# Later, save all metrics to file
collector.save_metrics()# The metrics.json file will contain:# {# "response_time": [# {"value": 0.234, "timestamp": 1627...},# {"value": 0.189, "timestamp": 1627...}# ],# "active_users": [# {"value": 1523, "timestamp": 1627...}# ]# }Được hỗ trợ bởi
Điểm tuyệt vời của cách tiếp cận này là code ứng dụng của bạn vẫn sạch — bạn chỉ cần log số đo như bình thường, và sink collector sẽ tự xử lý lưu trữ. Bạn có thể dễ dàng thay thế bằng sink gửi số đo tới Prometheus, DataDog, hoặc bất kỳ dịch vụ giám sát nào.
Những tùy biến này cho bạn sự linh hoạt để điều chỉnh Loguru theo nhu cầu cụ thể trong khi vẫn giữ được sự đơn giản khiến nó trở nên tuyệt vời. Dù bạn xây dựng script nhỏ hay ứng dụng lớn, bạn có thể mở rộng Loguru để đáp ứng yêu cầu logging độc đáo mà không cần file cấu hình phức tạp hay mã lặp khuôn.
Kết luận
Loguru khiến logging trong Python đơn giản như vốn dĩ nó nên thế. Không còn file cấu hình, không còn thiết lập handler, không còn mã lặp khuôn. Chỉ cần import và bắt đầu log với màu sắc, dấu thời gian, và xoay vòng file tự động.
Bạn đã thấy cách nó xử lý các điểm đau thường gặp: lưu log ra file, tự động xoay vòng, bắt ngoại lệ với đầy đủ ngữ cảnh, và gửi log tới dịch vụ bên ngoài. Tính năng logging có cấu trúc khiến nó sẵn sàng cho production, trong khi các cấp độ và sink tuỳ chỉnh cho phép bạn thích nghi với mọi trường hợp sử dụng.
Hãy dùng Loguru khi:
Bạn đã mệt mỏi với việc debug bằng print
Bạn cần logging nhanh trong script hoặc notebook
Bạn đang xây dựng ứng dụng web cần log có cấu trúc
Bạn muốn theo dõi ngoại lệ tốt hơn mà không cần thiết lập phức tạp
Bắt đầu nhỏ. Hãy thay một câu lệnh print bằng logger.info(). Bạn sẽ không muốn quay lại nữa.
Câu hỏi thường gặp về Loguru
Loguru so với các thư viện logging khác của Python như thế nào?⌄
Loguru nổi bật nhờ không cần cấu hình — bạn có thể bắt đầu log ngay với from loguru import logger. Khác với module logging tích hợp của Python cần handlers, formatters và thiết lập phức tạp, Loguru cung cấp màu sắc, dấu thời gian và xoay vòng file sẵn có. Nó cũng có traceback nâng cao với kiểm tra biến mà các thư viện khác không có.
Những lợi ích chính khi dùng Loguru thay cho module logging tích hợp của Python là gì?⌄
Loguru loại bỏ sự phức tạp trong cấu hình, cung cấp xoay vòng và lưu giữ file tự động, hỗ trợ logging JSON có cấu trúc với serialize=True, bao gồm traceback ngoại lệ nâng cao với giá trị biến, và hỗ trợ các sink tuỳ chỉnh cho dịch vụ bên ngoài. Bạn có được tính năng logging sẵn sàng cho production mà không cần viết mã thiết lập.
Loguru có xử lý hiệu quả ứng dụng đa luồng không?⌄
Có. Loguru mặc định an toàn với đa luồng và xử lý tốt ứng dụng đa luồng. Bạn có thể thêm thông tin thread vào log bằng filters, và phương thức logger.bind() hoạt động xuyên suốt các thread để duy trì ngữ cảnh. Hiệu năng của Loguru được tối ưu cho logging đồng thời mà không chặn thread.
Xoay vòng và lưu giữ log tự động của Loguru hoạt động như thế nào?⌄
Loguru cung cấp xoay vòng linh hoạt dựa trên kích thước file (rotation="10 MB"), khoảng thời gian (rotation="00:00" cho hằng ngày), hoặc điều kiện tùy chỉnh. Retention tự động xóa file cũ (retention=10 giữ 10 file), và nén giúp tiết kiệm dung lượng (compression="zip"). Tất cả diễn ra tự động, không cần quản lý file thủ công.
Một số ví dụ thực tế về việc dùng tính năng custom sink của Loguru là gì?⌄
Các sink tuỳ chỉnh cho phép bạn gửi log tới dịch vụ bên ngoài như Slack để cảnh báo lỗi, thu thập số đo cho hệ thống giám sát, hoặc định dạng log cho công cụ cụ thể. Bạn có thể tạo sink lọc theo cấp độ log, thêm thông tin ngữ cảnh, hoặc tích hợp với các dịch vụ như DataDog, Prometheus, hay email thông báo cho lỗi nghiêm trọng.
Tôi là người sáng tạo nội dung về khoa học dữ liệu với hơn 2 năm kinh nghiệm và là một trong những tài khoản có lượng người theo dõi lớn nhất trên Medium. Tôi thích viết các bài chuyên sâu về AI và ML với chút giọng điệu mỉa mai, vì bạn cũng phải làm gì đó để chúng bớt khô khan. Tôi đã xuất bản hơn 130 bài viết và một khóa học trên DataCamp, cùng với một khóa nữa đang được thực hiện. Nội dung của tôi đã được hơn 5 triệu lượt xem, trong đó 20 nghìn người đã trở thành người theo dõi trên cả Medium và LinkedIn.
Nâng cao kỹ năng về dữ liệu, khám phá cách thao tác và trực quan hóa dữ liệu, đồng thời áp dụng phân tích nâng cao để đưa ra quyết định dựa trên dữ liệu.
Học Python để phát triển phần mềm, từ việc viết hàm đến định nghĩa lớp. Hãy trang bị những kỹ năng cần thiết để bắt đầu sự nghiệp phát triển phần mềm của bạn!
Tiếp tục phát triển kỹ năng Khoa học Dữ liệu hiện đại của bạn bằng cách tìm hiểu về các cấu trúc lặp (iterators) và biểu thức danh sách (list comprehensions).
Bạn đã debug bằng các câu lệnh
print()suốt cả cuộc đời lập trình của mình. Có thể bạn đã thử module logging của Python vài lần, nhưng rồi quay lại dùng print vì chúng đơn giản hơn.Thực ra có một thứ tốt hơn mà vẫn dễ dùng như vậy.
Câu lệnh print không thể lưu vào file hay bật/tắt dễ dàng. Logging tích hợp sẵn của Python làm được, nhưng bạn phải viết phần thiết lập còn dài hơn cả chương trình chính.
Loguru đi theo hướng khác: logging nên dễ như print, nhưng hữu ích hơn rất nhiều. Bạn có màu sắc trong terminal, dấu thời gian tự động, và khả năng lưu mọi thứ vào file mà không cần viết một dòng cấu hình nào.
Trong hướng dẫn này, tôi sẽ trình bày các ví dụ thực tế để logging trở nên tự nhiên thay vì nặng nề. Bạn sẽ thấy những tính năng có lẽ chưa biết đến, sử dụng các mẫu code giúp việc debug dễ dàng hơn.
Nếu bạn mới tìm hiểu debug trong Python hoặc muốn củng cố kỹ năng nền tảng, xây dựng nền tảng Python vững chắc sẽ giúp bạn hiểu khi nào và vì sao logging là cần thiết.
Bắt đầu với Loguru: “Phép màu” không cần cấu hình
Cài đặt Loguru chỉ với một lệnh:
Với module
loggingthông thường, bạn sẽ phải thiết lập handlers, formatters, và cấu hình đủ thứ trước khi thấy được một dòng log.Để so sánh sâu hơn về các cách logging tích hợp sẵn trong Python, hãy xem hướng dẫn logging toàn diện bao quát các phương pháp truyền thống.
Với Loguru, bạn chỉ cần import và bắt đầu log:
Kết quả xuất hiện trong terminal với màu sắc, dấu thời gian và cấp độ log tự động. Không cần thiết lập.
Giả sử bạn đang xây dựng một scraper web đơn giản. Cách debug của bạn sẽ thay đổi như sau:
Lưu ý các cấp độ log khác nhau (
info,debug,success,error) và kiểu định dạng ngoặc nhọn. Mọi thứ hoạt động ngay lập tức mà không cần file cấu hình hay mã thiết lập.Ghi log ra file và xoay vòng đơn giản
Khi debug bằng output trên console, bạn mất sạch mọi thứ ngay khi đóng terminal. Bạn không thể tra cứu những gì xảy ra một giờ trước, và nếu script sập qua đêm, sẽ không còn dấu vết nào cho thấy lỗi ở đâu. Bạn cũng khó chia sẻ output debug với đồng đội khi cần trợ giúp.
Ghi log ra file giải quyết điều này bằng cách lưu mọi thứ một cách lâu dài.
Logging truyền thống buộc bạn viết mã cấu hình để thiết lập file handler. Bạn phải chỉ định đường dẫn, tạo thư mục, và xử lý quyền. Với Loguru, thêm ghi log ra file thực sự chỉ một dòng:
Giờ đây mọi thứ được lưu vào
app.logđồng thời vẫn hiển thị trên terminal.Nhưng log file sẽ phình to theo thời gian. Một ứng dụng web chạy vài ngày có thể sinh ra hàng GB log và cuối cùng đầy ổ đĩa. Xoay vòng file giải quyết việc này bằng cách tự động tạo file log mới khi đạt điều kiện nhất định. File cũ được đổi tên kèm dấu thời gian, và một file mới bắt đầu:
Dấu thời gian mặc định hoạt động ổn, nhưng thường ở UTC hoặc khó đọc. Loguru cho phép bạn tùy chỉnh định dạng datetime theo nhu cầu:
Với hệ thống production, bạn cần kiểm soát tốt hơn các file cũ. Logging trong production đòi hỏi tuân theo các thực hành tốt của Python để log có thể bảo trì và an toàn. Nếu không quản lý, log file sẽ chất đống và chiếm dụng lưu trữ:
Hãy xem trong một kịch bản thực tế. Bạn đang xây dựng pipeline dữ liệu xử lý nhiều file CSV mỗi đêm, làm sạch và kiểm tra dữ liệu khách hàng trước khi nạp vào cơ sở dữ liệu. Bạn cần theo dõi file nào xử lý thành công và bắt các vấn đề chất lượng dữ liệu:
Log của bạn được lưu với dấu thời gian dễ đọc, file tự xoay vòng, và log cũ tự dọn dẹp. Bạn có thể xem lại chuyện gì đã xảy ra sau vài ngày hoặc gửi file log cho đồng nghiệp khi cần khắc phục sự cố.
Logging có cấu trúc cho ứng dụng hiện đại
Khi chạy ứng dụng web, log nhanh chóng trở thành một mớ hỗn độn. Hành vi người dùng, truy vấn cơ sở dữ liệu, lời gọi API và lỗi lẫn lộn trong một bức tường chữ. Bạn khó lọc theo người dùng cụ thể, truy vết từng request, hay tự động phân tích log cho hệ thống giám sát.
Ứng dụng hướng dữ liệu đặc biệt hưởng lợi từ logging có cấu trúc. Nếu bạn làm việc với pipeline dữ liệu hoặc phân tích, phát triển kỹ năng data analyst sẽ giúp bạn hiểu nên ghi lại thông tin gì trong log.
Logging dạng chuỗi truyền thống làm mọi thứ tệ hơn. Mỗi thông điệp log chỉ là văn bản, nên việc trích xuất dữ liệu có ý nghĩa đòi hỏi regex phức tạp hoặc tìm kiếm thủ công.
Logging có cấu trúc thay đổi điều này bằng cách coi mỗi mục log như một đối tượng dữ liệu thay vì chỉ là văn bản. Mỗi mục log trở thành một bản ghi có cấu trúc với các trường có thể tìm kiếm, giúp truy vấn và phân tích dễ dàng bằng chương trình.
Logging JSON có cấu trúc với serialize=True
Loguru khiến logging có cấu trúc trở nên đơn giản với tùy chọn
serialize=True:Tham số
serialize=Truebáo cho Loguru xuất JSON thay vì văn bản thuần. Thay vì câu chữ dễ đọc, file log giờ chứa dữ liệu có cấu trúc:Định dạng này hoạt động hoàn hảo với các công cụ phân tích log có thể tự động tìm kiếm và lọc dữ liệu JSON.
Gắn ngữ cảnh (context binding)
Nhưng ứng dụng web cần nhiều hơn dữ liệu có cấu trúc. Bạn cần ngữ cảnh kết nối các mục log liên quan. Khi xử lý một request người dùng, bạn muốn mọi log liên quan đều chia sẻ thông tin chung như user ID hoặc session ID.
Gắn ngữ cảnh giải quyết điều này bằng cách đính kèm thông tin thường trực vào logger của bạn bằng
logger.bind():Giờ đây mọi log từ
order_loggertự động bao gồmuser_idvàorder_id, giúp dễ dàng truy vết toàn bộ log liên quan đến một đơn hàng cụ thể.Nhiều sink cho các đầu ra khác nhau
Ứng dụng hiện đại cũng cần các đầu ra log khác nhau cho các mục đích khác nhau. Bạn có thể muốn log dễ đọc trên console khi phát triển, nhưng log JSON cho giám sát production. Trong Loguru, mỗi nơi nhận log được gọi là “sink” — hãy coi đó là nơi thông điệp log chảy tới.
Loguru cho phép bạn thêm nhiều sink với định dạng và quy tắc khác nhau:
Lệnh
logger.remove()loại bỏ output console mặc định của Loguru để bạn tự thiết lập. Mỗilogger.add()tạo một sink mới với quy tắc riêng về những gì được log và cách định dạng.Giờ đây cùng một câu lệnh log sẽ cho ra các đầu ra khác nhau:
Ví dụ hoàn chỉnh Loguru: Hệ thống đăng ký người dùng
Hãy xem một ví dụ đơn giản và thực tế hơn về việc dùng Loguru — một hệ thống đăng ký người dùng cơ bản:
Thiết lập này mang đến output console dễ đọc khi phát triển, log JSON có cấu trúc cho giám sát tự động, và log lỗi riêng cho cảnh báo. Phương thức
bind()đảm bảo mọi log cho mỗi lượt đăng ký đều bao gồm địa chỉ email, giúp debug dễ dàng hơn nhiều.Xử lý ngoại lệ với Loguru
Khi code bị crash, bạn thường nhận một stack trace cơ bản cho biết lỗi xảy ra ở đâu, nhưng không nhiều về lý do. Bạn mất đi giá trị biến tại thời điểm crash, khiến khó hiểu chuyện gì đã sai. Xử lý ngoại lệ truyền thống cũng yêu cầu bạn bao bọc mọi thứ trong khối try-catch và tự log từng ngoại lệ.
Điều này đặc biệt đau đầu khi debug sự cố production. Người dùng báo lỗi, nhưng bạn không tái hiện được vì thiếu ngữ cảnh tồn tại khi lỗi xảy ra.
Loguru giải quyết bằng việc tự động bắt ngoại lệ và cung cấp traceback nâng cao cho bạn thấy chính xác chuyện gì đang diễn ra trong code khi mọi thứ trục trặc.
Tự động bắt ngoại lệ với @logger.catch
Decorator
@logger.catchtự động bắt mọi ngoại lệ xảy ra trong một hàm và log lại với đầy đủ ngữ cảnh. Các decorator như@logger.catchlà một khái niệm Python trung cấp có thể thay đổi cách bạn xử lý lỗi trên toàn bộ codebase:Thay vì chương trình sập với thông báo lỗi cơ bản, Loguru bắt ngoại lệ và log lại với thông tin chi tiết, trong khi chương trình của bạn tiếp tục chạy. Decorator hoạt động với mọi hàm — method của class, hàm async, hoặc hàm thông thường.
Traceback nâng cao với kiểm tra biến
Nhưng Loguru còn đi xa hơn với traceback nâng cao hiển thị giá trị biến ở mỗi mức của call stack. Tính năng này gọi là “diagnose” và giống như có một trình debug tích hợp ngay trong log của bạn:
Với
diagnose=True, khi hàm gặp lỗi do cố trừ số với chuỗi, Loguru hiển thị chính xác những gì trong từng biến:Mức độ chi tiết này giúp debug nhanh hơn vì bạn thấy chính xác dữ liệu nào gây ra vấn đề.
Xử lý ngoại lệ theo môi trường
Tuy nhiên, traceback nâng cao có thể lộ thông tin nhạy cảm như mật khẩu hoặc API key trong log production. Loguru cho phép bạn kiểm soát hành vi này theo từng môi trường:
Đánh giá lười (lazy evaluation) để tối ưu hiệu năng
Một tính năng mạnh khác là đánh giá lười, giúp cải thiện hiệu năng khi log các thao tác tốn kém. Đôi khi bạn muốn log thông tin phức tạp, nhưng chỉ khi thật sự có lỗi xảy ra:
Cú pháp
lambda:tạo một hàm chỉ được gọi khi Loguru thực sự ghi thông điệp log. Nếu không có lỗi,expensive_data_summary()sẽ không chạy, tiết kiệm thời gian xử lý.Chiến lược khác nhau cho các loại lỗi khác nhau
Bạn cũng có thể dùng các chiến lược xử lý ngoại lệ khác nhau cho từng loại lỗi:
Tham số
reraise=Falsebáo Loguru log ngoại lệ nhưng không ném lại, cho phép chương trình tiếp tục. Điều này hữu ích với các tính năng tùy chọn không nên làm gián đoạn luồng chính.Ví dụ hoàn chỉnh: xử lý file với xử lý lỗi
Hãy xem ví dụ hoàn chỉnh với hệ thống xử lý file ứng xử khéo léo với các loại lỗi khác nhau:
Thiết lập này mang đến khả năng tự động bắt ngoại lệ, ngữ cảnh lỗi chi tiết khi cần, và các chiến lược xử lý khác nhau cho mỗi loại lỗi. Log của bạn hữu ích hơn rất nhiều cho việc debug, trong khi code vẫn gọn gàng và dễ đọc.
Cấu hình và tùy biến Loguru nâng cao
Khi ứng dụng phát triển, bạn sẽ gặp những tình huống mà mặc định của Loguru chưa phù hợp. Có thể bạn cần một cấp độ log riêng cho số đo nghiệp vụ, muốn lọc thông tin nhạy cảm khỏi log, hoặc cần gửi cảnh báo lên Slack khi có lỗi.
Tin tốt là Loguru được xây dựng để tùy biến mà không đánh mất sự đơn giản. Bạn có thể mở rộng theo cách mà với hệ thống logging truyền thống sẽ đòi hỏi cấu hình phức tạp, trong khi code vẫn sạch và dễ đọc.
Hãy xem những tùy biến hữu ích nhất giải quyết các vấn đề thực tế bạn sẽ gặp trong ứng dụng production.
Cấp độ log và màu sắc tuỳ chỉnh
Loguru có sẵn các cấp độ chuẩn như INFO, DEBUG và ERROR. Nhưng đôi khi bạn cần thứ gì đó đặc thù cho ứng dụng. Có thể bạn muốn một cấp độ riêng cho số liệu hiệu năng hoặc sự kiện nghiệp vụ.
Tạo cấp độ log tuỳ chỉnh rất đơn giản:
Tham số
nođặt mức độ ưu tiên của level (số càng lớn = ưu tiên càng cao). Các level chuẩn: DEBUG=10, INFO=20, WARNING=30, ERROR=40, CRITICAL=50.Hãy xem trong bối cảnh huấn luyện machine learning:
Chặn và sửa đổi thông điệp log
Đôi khi bạn cần sửa đổi thông điệp log trước khi ghi. Trường hợp phổ biến gồm loại bỏ dữ liệu nhạy cảm, thêm request ID, hoặc định dạng lại thông điệp cho công cụ cụ thể.
Loguru cho phép chặn thông điệp bằng tham số
filter:Bạn cũng có thể dùng filter để thêm thông tin ngữ cảnh:
Sink tuỳ chỉnh cho dịch vụ bên ngoài
Sink là nơi log của bạn chảy tới. Dù file và console là phổ biến, bạn thường cần gửi log tới các dịch vụ bên ngoài như Slack, email, hoặc hệ thống giám sát.
Đây là cách tạo một sink tuỳ chỉnh gửi lỗi lên Slack:
Đây là một ví dụ thực tế khác — một sink tuỳ chỉnh thu thập số đo hiệu năng trong suốt quá trình chạy và lưu lại để phân tích:
Điểm tuyệt vời của cách tiếp cận này là code ứng dụng của bạn vẫn sạch — bạn chỉ cần log số đo như bình thường, và sink collector sẽ tự xử lý lưu trữ. Bạn có thể dễ dàng thay thế bằng sink gửi số đo tới Prometheus, DataDog, hoặc bất kỳ dịch vụ giám sát nào.
Những tùy biến này cho bạn sự linh hoạt để điều chỉnh Loguru theo nhu cầu cụ thể trong khi vẫn giữ được sự đơn giản khiến nó trở nên tuyệt vời. Dù bạn xây dựng script nhỏ hay ứng dụng lớn, bạn có thể mở rộng Loguru để đáp ứng yêu cầu logging độc đáo mà không cần file cấu hình phức tạp hay mã lặp khuôn.
Kết luận
Loguru khiến logging trong Python đơn giản như vốn dĩ nó nên thế. Không còn file cấu hình, không còn thiết lập handler, không còn mã lặp khuôn. Chỉ cần import và bắt đầu log với màu sắc, dấu thời gian, và xoay vòng file tự động.
Bạn đã thấy cách nó xử lý các điểm đau thường gặp: lưu log ra file, tự động xoay vòng, bắt ngoại lệ với đầy đủ ngữ cảnh, và gửi log tới dịch vụ bên ngoài. Tính năng logging có cấu trúc khiến nó sẵn sàng cho production, trong khi các cấp độ và sink tuỳ chỉnh cho phép bạn thích nghi với mọi trường hợp sử dụng.
Hãy dùng Loguru khi:
Để học sâu hơn, hãy khám phá tài liệu chính thức cùng với hướng dẫn logging này để có cái nhìn toàn diện về các chiến lược logging trong Python.
Bắt đầu nhỏ. Hãy thay một câu lệnh print bằng
logger.info(). Bạn sẽ không muốn quay lại nữa.