Logging & observability
Ferron supports multiple observability outputs, so you can start with local log files and later move to centralized telemetry without changing your application stack.
This page focuses on common deployment patterns. For directive-level details, see Configuration: observability and logging.
For specific backend configurations:
Basic production logs to files
Use this when running Ferron directly on a VM or bare metal and collecting logs from disk:
example.com {
log "access.log"
root /var/www/html
}The text formatter uses the Combined Log Format (CLF) by default, the same format used by Apache and Nginx.
JSON-format access logs
Use this when you need structured logs for easier parsing by log aggregation tools (for example, ELK Stack, Splunk, or cloud-native log processors):
example.com {
log "access.log" {
format json
}
root /var/www/html
}Example output:
{"method":"GET","path":"/index.html","status":200,"duration_secs":0.012,"client_ip":"127.0.0.1","remote_ip":"127.0.0.1"}You can also select specific fields:
example.com {
log "access.log" {
format json
fields "method" "path" "status" "duration_secs" "client_ip"
}
root /var/www/html
}Custom text log patterns
You can customize the text log format using the access_pattern directive:
example.com {
log "access.log" {
format text
access_pattern "%client_ip - %auth_user [%{%d/%b/%Y:%H:%M:%S %z}t] \"%method %path_and_query %version\" %status %content_length \"%{Referer}i\" \"%{User-Agent}i\""
}
root /var/www/html
}Centralized observability with OTLP
Use this when shipping logs, metrics, and traces to an OpenTelemetry collector:
example.com {
observability {
provider otlp
logs "http://otel-collector.internal:4318/v1/Logs" {
protocol "http/protobuf"
}
metrics "http://otel-collector.internal:4318/v1/Metrics" {
protocol "http/protobuf"
}
traces "http://otel-collector.internal:4317" {
protocol "grpc"
}
service_name "ferron-prod"
}
root /var/www/html
}If you use gRPC OTLP endpoints, set protocol "grpc" and optionally an auth header:
example.com {
observability {
provider otlp
logs "https://otel.example.net/v1/logs" {
protocol "grpc"
authorization "Bearer YOUR_TOKEN"
}
metrics "https://otel.example.net/v1/metrics" {
protocol "grpc"
authorization "Bearer YOUR_TOKEN"
}
traces "https://otel.example.net/v1/traces" {
protocol "grpc"
authorization "Bearer YOUR_TOKEN"
}
service_name "ferron-prod"
}
}Prometheus metrics monitoring
Use this when you want to expose metrics for Prometheus scraping:
example.com {
observability {
provider prometheus
endpoint_listen "127.0.0.1:8889"
endpoint_format text
}
root /var/www/html
}This starts a metrics endpoint at http://localhost:8889/metrics that Prometheus can scrape.
Production Prometheus setup
example.com {
observability {
provider prometheus
endpoint_listen "0.0.0.0:8889"
endpoint_format text
}
root /var/www/html
}Multiple hosts with different metrics ports
# Main website
example.com {
observability {
provider prometheus
endpoint_listen "127.0.0.1:9001"
}
root /var/www/example
}
# API service
api.example.com {
observability {
provider prometheus
endpoint_listen "127.0.0.1:9002"
}
proxy http://backend:3000
}Hybrid setup: local fallback + OTLP
A practical migration strategy is to keep file logs for local troubleshooting while also exporting telemetry centrally:
example.com {
log "access.log" {
format json
}
observability {
provider otlp
logs "http://otel-collector.internal:4318/v1/Logs" {
protocol "http/protobuf"
}
metrics "http://otel-collector.internal:4318/v1/Metrics" {
protocol "http/protobuf"
}
traces "http://otel-collector.internal:4317" {
protocol "grpc"
}
service_name "ferron-prod"
}
root /var/www/html
}Hybrid setup: Prometheus + OTLP
You can combine both Prometheus and OTLP for maximum flexibility:
example.com {
# Local Prometheus metrics
observability {
provider prometheus
endpoint_listen "127.0.0.1:8889"
}
# Centralized OTLP export
observability {
provider otlp
service_name "ferron-prod"
logs "http://otel-collector.internal:4318/v1/Logs" {
protocol "http/protobuf"
}
metrics "http://otel-collector.internal:4318/v1/Metrics" {
protocol "http/protobuf"
}
traces "http://otel-collector.internal:4317" {
protocol "grpc"
}
}
root /var/www/html
}Notes and troubleshooting
- Start simple: text or JSON logs first, then add Prometheus metrics, then OTLP for full observability.
- Keep
no_verify falseunless you are in a controlled test environment. - If logs are missing, verify the formatter modules are loaded in your Ferron build and check endpoint/protocol pairing.
- All three signals (logs, metrics, traces) from the same HTTP request share the same
trace_id, enabling correlated queries. - If Ferron is behind a reverse proxy, configure
client_ip_from_headerso Ferron can see real client IPs. See HTTP host directives. - For available access log fields, see Configuration: observability and logging.
- For Prometheus metrics configuration, see Prometheus metrics.
- For OTLP configuration details, see OTLP observability.