CLI & Structured Logging
Deep dive into ZCore's cascading server runner, introspection-based environment scaffolding, and unified structlog observability pipeline.
ZCore provides native tooling to streamline developer ergonomics and guarantee production observability through introspection-driven CLI commands, cascading development servers, and a centralized, context-aware logging pipeline.
1. Dynamic Environment Introspection (zc genenv)
Out-of-sync environment templates (.env.example) are a frequent source of deployment failures. ZCore eliminates manual .env synchronization through runtime class introspection.
How zc genenv Works
- Imports your application package context (
main.py). - Evaluates all registered subclasses of
SettingsviaSettings.__subclasses__()and selects the active schema configuration. - Iterates over Pydantic's
model_fields, formats default values, and writes a clean.env.examplefile.
# Generate .env.example based on your active AppSettings class
zc genenv
# Overwrite an existing configuration template
zc genenv --force -o .env.production.example2. Cascading Server Runner (zc run)
ZCore provides an intelligent development and production server runner that resolves configuration hierarchically:
CLI Arguments $\longrightarrow$ .env File Variables $\longrightarrow$ Built-in Defaults
Architecture & Precedence Resolution
When executing zc run [app], the CLI runner executes the following lifecycle:
- Environment File Parsing: Reads key-value pairs safely from
.env(or custom file via--env-file), resolvingAPP_MODULE,UVICORN_APP,HOST,PORT,RELOAD,DEBUG,WORKERS, andLOG_LEVEL. - CLI Override Enforcement: Explicit flags (e.g.
--host 0.0.0.0 --port 8080) take immediate precedence over.envvariables and defaults (127.0.0.1:8000). - Smart Multi-Worker Protection: When
--workersis set to greater than1(or specified in.env), live code reloading (--reload) is automatically disabled to prevent worker race conditions. - Transparent Uvicorn Passthrough: The CLI uses
parse_known_args()to capture any arbitrary Uvicorn flags (such as--proxy-headers,--ssl-keyfile, or--forwarded-allow-ips) and forwards them directly to the underlying server process.
# Example: Production multi-worker execution with arbitrary Uvicorn flags
zc run --workers 4 --log-level warning --proxy-headers --forwarded-allow-ips="*"3. Structured Logging Pipeline (structlog)
Standard FastAPI, Uvicorn, and SQLAlchemy logging often outputs unformatted, duplicated text streams that are difficult to parse in log aggregators.
ZCore's setup_logging() intercepts and unifies this pipeline through ProcessorFormatter:
- Granular Logger Routing (Tri-Split Loggers): ZCore categorizes loggers into three distinct groups in
LoggingSettings:muted_loggers(default:["sqlalchemy.engine"]): Clears handlers and setspropagate = Falseto prevent noisy low-level logs.passthrough_loggers(default:["uvicorn", "uvicorn.access", "uvicorn.error"]): Preserves native, colorized Uvicorn console formatting in development mode (propagate = False).intercept_loggers(default:["uvicorn", "uvicorn.access", "uvicorn.error"]): Clears handlers and channels Uvicorn output intostructlogas structured JSON records when running in production mode (propagate = True).
- Contextual Distributed Tracing:
RequestLogMiddlewareextracts or generates anx-request-idheader (validated against^[a-zA-Z0-9\-\.\_\:]{8,64}$), binds it viastructlog.contextvars.bind_contextvars(request_id=...), and appends the correlation ID to response headers. Every log message emitted across repositories, services, or events automatically includes the uniquerequest_id, while HTTP requests logstatus_code,client_ip, andduration_ms. - Adaptive Output Rendering:
- Development (
DEBUG = Trueorjson_format = False): Colorized, human-readable console rendering with formatted tracebacks viarich.traceback. - Production (
DEBUG = Falseorjson_format = True): Serialized, high-throughput JSON records containing ISO timestamps, log levels, and contextual metadata, ready for ingestion by Grafana Loki, ELK, or Datadog.
- Development (
Declarative Configuration (LoggingSettings)
Logging can be configured declaratively through Settings.LOGGING using the LoggingSettings schema or overridden directly in setup_logging():
from zcore.config import LoggingSettings, Settings
class AppSettings(Settings):
LOGGING: LoggingSettings = LoggingSettings(
level="INFO",
json_format=None, # None: Console in Debug, JSON in Production
log_sql_queries=False, # Defaults to False in rc.2 to avoid noise
slow_query_threshold_ms=200.0, # Log only queries exceeding 200ms (works independently!)
file_path="./logs/app.log", # Enables RotatingFileHandler
max_bytes=10 * 1024 * 1024, # Rotate when file reaches 10MB (Default: 10MB)
backup_count=5, # Retain 5 rotated log backups (Default: 5)
muted_loggers=["sqlalchemy.engine"],
passthrough_loggers=["uvicorn", "uvicorn.access", "uvicorn.error"],
custom_processors=[]
)Advanced setup_logging Overrides
setup_logging() supports programmatic configuration, file rotation, custom processors, extra standard library handlers, and complete dictConfig overrides:
from zcore.logging import setup_logging, LoggingSettings
import logging
# 1. Zero-code initialization (reads from settings.LOGGING)
setup_logging()
# 2. Programmatic LoggingSettings or dict config
setup_logging(
config=LoggingSettings(
level="DEBUG",
file_path="./logs/app.log",
max_bytes=20 * 1024 * 1024,
backup_count=10,
slow_query_threshold_ms=100.0
)
)
# 3. Custom processors and extra handlers
setup_logging(
custom_processors=[my_structlog_processor],
extra_handlers=[logging.StreamHandler()],
log_level="DEBUG"
)
# 4. Full logging.config.dictConfig override
setup_logging(
dict_config={
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"console": {"class": "logging.StreamHandler", "level": "INFO"}
},
"root": {"handlers": ["console"], "level": "INFO"}
}
)SQL Query Diagnostics:
DatabaseManager hooks directly into SQLAlchemy cursor lifecycle events (before_cursor_execute and after_cursor_execute). When log_sql_queries is enabled, it logs clean, sanitized SQL statements along with execution durations in milliseconds (duration_ms), while automatically filtering out internal schema catalog noise. If slow_query_threshold_ms is configured, slow queries are tracked and logged independently, even if general query logging is turned off.
File Storage Security Architecture
Deep dive into how ZCore prevents path traversal, DoS through large files, arbitrary file deletions, and executable MIME spoofing.
BaseRepository
Comprehensive API reference for the BaseRepository class, query scoping, bulk operations, soft-delete lifecycles, and restoration methods.