Skip to content

Filtering ​

Filters control which log records reach each sink. Logly supports level filtering, prefix matching, callable filters, and mapping filters.

Level Filtering ​

The simplest filter is the level parameter on add():

python
from logly import logger

# Only WARNING and above
logger.add("warnings.log", level="WARNING")

# All levels (TRACE and above)
logger.add("debug.log", level="TRACE")

# Only ERROR and CRITICAL
logger.add("errors.log", level="ERROR")

Prefix Filter ​

Filter by logger name prefix:

python
from logly import logger

# Only records from loggers starting with "myapp."
logger.add("myapp.log", filter={"name": "myapp."})

# Records from "myapp." go to this sink
myapp = logger.bind(name="myapp.module")
myapp.info("This appears in myapp.log")

Callable Filter ​

Custom filter function that receives the record dict:

python
from logly import logger


# Filter by message content
def important_only(record: dict) -> bool:
    return "important" in record.get("message", "").lower()


logger.add("important.log", filter=important_only)


# Filter by level
def errors_and_critical(record: dict) -> bool:
    return record.get("level") in {"ERROR", "CRITICAL", "FAIL"}


logger.add("errors.log", filter=errors_and_critical)


# Filter by extra fields
def production_only(record: dict) -> bool:
    extra = record.get("extra", {})
    return extra.get("env") == "production"


logger.add("prod.log", filter=production_only)


# Combine conditions
def combined_filter(record: dict) -> bool:
    level = record.get("level", "")
    extra = record.get("extra", {})
    return level in {"ERROR", "CRITICAL"} and extra.get("service") == "api"

Mapping Filter ​

Filter by extra field values:

python
from logly import logger

# Single field
logger.add("audit.log", filter={"channel": "audit"})

# Multiple fields (AND logic)
logger.add(
    "filtered.log",
    filter={"env": "production", "service": "api"},
)

# Usage
logger.bind(channel="audit").info("Permission changed")
# This goes to audit.log

logger.bind(env="production", service="api").info("Request processed")
# This goes to filtered.log

Combining Filters ​

python
from logly import logger

# Level + callable filter
logger.add(
    "production-errors.log",
    level="ERROR",
    filter=lambda r: r.get("extra", {}).get("env") == "production",
)

# Level + mapping filter
logger.add(
    "api-warnings.log",
    level="WARNING",
    filter={"service": "api"},
)

# Multiple filters on different sinks
logger.add("all.log", level="DEBUG")
logger.add("errors.log", level="ERROR")
logger.add("api.log", filter={"service": "api"})
logger.add(
    "important.log", level="WARNING", filter=lambda r: "critical" in r.get("message", "").lower()
)

Filter Examples ​

python
from logly import logger

# Example 1: Route by logger name
logger.add("auth.log", filter={"name": "auth."})
logger.add("db.log", filter={"name": "database."})
logger.add("http.log", filter={"name": "http."})

# Example 2: Route by severity
logger.add("stdout", level="INFO")
logger.add("stderr", level="ERROR")
logger.add("debug.log", level="TRACE")

# Example 3: Route by context
logger.add("requests.log", filter={"type": "request"})
logger.add("background.log", filter={"type": "background"})


# Example 4: Complex filter
def complex_filter(record: dict) -> bool:
    level = record.get("level", "")
    extra = record.get("extra", {})
    message = record.get("message", "")

    # Allow all errors
    if level in {"ERROR", "CRITICAL", "FAIL"}:
        return True

    # Allow production warnings
    if level == "WARNING" and extra.get("env") == "production":
        return True

    # Allow messages containing "audit"
    if "audit" in message.lower():
        return True

    return False


logger.add("filtered.log", filter=complex_filter)

Released under the MIT License.