Logging

Keywords: application logs, logging levels, debug logging, log rotation, error logging

Asok provides a centralized, configuration-aware logging system out of the box.

1. Application Logger

The recommended way to log messages is via the app.logger instance. This logger is automatically configured to respect your application’s settings (LOG_LEVEL, LOG_FILE, etc.).

from asok import Asok

app = Asok()

# Standard logging
app.logger.info("Application initialized")
app.logger.warning("Low disk space")

# Convenient shortcuts
app.log_info("Hello Asok!")
app.log_error("Something went wrong")

Usage in a page

In your src/pages/page.py, you can access the app logger via the request environment:

# src/pages/page.py
def render(request):
    app = request.environ.get("asok.app")
    if app:
        app.log_info("User visited the home page")
    return "Welcome to Asok!"

2. Request Logger Middleware

To log every incoming HTTP request, create src/middlewares/logger.py:

from asok import RequestLogger

# Uses app.logger settings automatically
log = RequestLogger()

def handle(request, next):
    return log(request, next)

Output example:

[2026-05-14 12:00:01] INFO asok.request: GET / 200 OK 3.2ms
[2026-05-14 12:00:02] INFO asok.request: POST /contact 302 Found 12.5ms

3. Configuration

You can configure logging via your .env file or directly in app.config.

| Key | Default | Description | |

5. Built-in Observability & Prometheus Metrics

Asok includes a zero-dependency, thread-safe Prometheus metrics registry and health check endpoints out of the box:

Health Probe Endpoints (/healthz, /health)

Returns JSON status (200 OK or 503 Service Unavailable) for Kubernetes and container orchestration:

{
  "status": "healthy",
  "version": "0.8.0",
  "uptime_seconds": 3612.4,
  "checks": {
    "database": "ok",
    "cache": "ok"
  }
}

Prometheus Metrics Endpoint (/metrics)

Exports metrics in standard Prometheus text format:

  • asok_http_requests_total{method="GET",status="200",path="/"} 42

  • asok_http_request_duration_seconds_count 42

  • asok_http_request_duration_seconds_sum 1.4502

  • asok_db_queries_total{engine="sqlite"} 128

  • asok_db_query_duration_seconds_sum 0.3214

  • asok_db_slow_queries_total 1

  • asok_websocket_active_connections 8

  • asok_uptime_seconds 3612.4

—|—|—| | LOG_LEVEL | DEBUG | Logging level (DEBUG, INFO, WARNING, ERROR). | | LOG_FILE | None | Optional path to a file for persistent logging. | | LOG_FORMAT| text | Format of logs: text or json. |

Example .env:

LOG_LEVEL=INFO
LOG_FILE=logs/app.log
LOG_FORMAT=json

4. Standalone Logger

For logging in modules where app is not available, you can still use get_logger():

from asok import get_logger

logger = get_logger("my_module")
logger.info("Standalone log message")

5. Distributed Tracing (W3C TraceContext & OpenTelemetry)

Asok includes a pure-Python, zero-dependency distributed tracing engine fully compliant with the W3C TraceContext specification.

Enabling the Middleware

from asok import Asok, TracingMiddleware

app = Asok()
app.use(TracingMiddleware())

Response Headers

Every HTTP response processed by TracingMiddleware includes standard tracing headers:

  • traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

  • X-Trace-ID: 4bf92f3577b34da6a3ce929d0e0e4736

  • X-Span-ID: 00f067aa0ba902b7

Programmatic Spans in Views

Use the contextual tracer to record child spans within your application logic:

from asok import tracer

def process_order(request):
    with tracer.start_span("validate_cart", attributes={"cart.items": 3}) as span:
        span.add_event("cart_validated")

    with tracer.start_span("charge_payment") as span:
        span.set_attribute("payment.provider", "stripe")

    return request.json({"status": "success"})

Each span automatically measures its duration in milliseconds (duration_ms), records exceptions, and can be exported as an OpenTelemetry-compatible dictionary via span.to_dict().