Ir al contenido

Tutorial 4 — Structured Logging

Esta página aún no está disponible en tu idioma.

This tutorial demonstrates PyGo’s native logger package — structured logging with JSON/text formats and 5 levels.

  • Initialize logger in .pgo files
  • Log at different levels (debug, info, warn, error, fatal)
  • Structured logging with key-value pairs
  • JSON vs text formats
  • Integration with observability
from logger import Default, Config
# Create a configured logger instance
log = Default(
format="json", # "json" or "text"
level="info", # debug, info, warn, error, fatal
service="myapp",
env="production",
)
handler health:
health() -> Dict:
log.info("Health check requested", {
"path": "/health",
"user_agent": request.headers.get("user-agent", ""),
})
return {"status": "ok", "uptime": time.now()}

📝 Step 2: Log Levels & Structured Fields

Section titled “📝 Step 2: Log Levels & Structured Fields”
handler login:
login(email: Email, password: String) -> String:
log.debug("Login attempt", {"email": email})
user = User.find(email=email)
if not user:
log.warn("Login failed — user not found", {"email": email})
return "Invalid credentials"
# Authenticated successfully
log.info("User logged in", {
"user_id": str(user.id),
"email": email,
"ip": request.remote_addr,
})
return redirect("/dashboard")
handler api_users:
api_users() -> List[User]:
try:
users = User.all()
log.info("Users fetched", {"count": len(users)})
return users
except DatabaseError as e:
log.error("Database error fetching users", {
"error": str(e),
"query": "SELECT * FROM users",
})
raise # Re-raise or return error response
{"timestamp":"2024-01-15T10:30:00Z","level":"info","service":"auth-api","event":"User logged in","fields":{"user_id":"123-uuid","email":"user@example.com","ip":"10.0.0.1"}}
{"timestamp":"2024-01-15T10:30:05Z","level":"warn","service":"auth-api","event":"Login failed","fields":{"email":"bad@example.com"}}
2024-01-15T10:30:00Z INFO auth-api User logged in email=user@example.com ip=10.0.0.1
2024-01-15T10:30:05Z WARN auth-api Login failed email=notfound@example.com
pygo.toml
[logger]
format = "json" # json | text
level = "info" # debug | info | warn | error | fatal
service = "myapp" # service name for log field
include_request_id = true # auto-adds request_id to logs

Logs are compatible with OpenTelemetry:

# Logs automatically include trace_id and span_id
# when used with pygo-observability package
from observability import TraceContext
ctx = TraceContext()
log = Default(trace_context=ctx)
# All log entries include:
# {
# "trace_id": "abc123...",
# "span_id": "def456...",
# "request_id": "ghi789..."
# }
# Enable debug logging in tests
from logger import TestLogger
test_log = TestLogger(level="debug")
test_log.info("Test message", {"test": true})
# Captures all log entries for assertions
entries = test_log.entries()
assert entries[0].level == "info"
assert entries[0].message == "Test message"
  1. Always use structured fields — never interpolate values into log messages

    # ✅ Good
    log.info("User created", {"user_id": user.id, "email": user.email})
    # ❌ Bad
    log.info(f"User created: {user.email}")
  2. Use appropriate levels:

    • debug — detailed info, dev only
    • info — key business events (logins, payments)
    • warn — expected errors (404, rate limit)
    • error — unexpected errors requiring attention
    • fatal — critical errors that stop the app
  3. Include request context:

    # Auto-injected by framework middleware
    log.info("Processing request", {
    "method": request.method,
    "path": request.path,
    "request_id": request.id,
    })