use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ObservabilityConfig {
#[serde(default)]
pub metrics: MetricsConfig,
#[serde(default)]
pub logging: LoggingConfig,
#[serde(default)]
pub tracing: Option<TracingConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_metrics_address")]
pub address: String,
#[serde(default = "default_metrics_path")]
pub path: String,
#[serde(default)]
pub high_cardinality: bool,
}
impl Default for MetricsConfig {
fn default() -> Self {
Self {
enabled: default_true(),
address: default_metrics_address(),
path: default_metrics_path(),
high_cardinality: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
#[serde(default = "default_log_level")]
pub level: String,
#[serde(default = "default_log_format")]
pub format: String,
#[serde(default = "default_true")]
pub timestamps: bool,
pub file: Option<PathBuf>,
#[serde(default)]
pub access_log: Option<AccessLogConfig>,
#[serde(default)]
pub error_log: Option<ErrorLogConfig>,
#[serde(default)]
pub audit_log: Option<AuditLogConfig>,
}
impl Default for LoggingConfig {
fn default() -> Self {
Self {
level: default_log_level(),
format: default_log_format(),
timestamps: default_true(),
file: None,
access_log: None,
error_log: Some(ErrorLogConfig::default()),
audit_log: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccessLogFields {
#[serde(default = "default_true")]
pub timestamp: bool,
#[serde(default = "default_true")]
pub trace_id: bool,
#[serde(default = "default_true")]
pub method: bool,
#[serde(default = "default_true")]
pub path: bool,
#[serde(default = "default_true")]
pub query: bool,
#[serde(default = "default_true")]
pub status: bool,
#[serde(default = "default_true")]
pub latency_ms: bool,
#[serde(default = "default_true")]
pub body_bytes_sent: bool,
#[serde(default = "default_true")]
pub upstream_addr: bool,
#[serde(default = "default_true")]
pub connection_reused: bool,
#[serde(default = "default_true")]
pub rate_limit_hit: bool,
#[serde(default = "default_true")]
pub geo_country: bool,
#[serde(default = "default_true")]
pub user_agent: bool,
#[serde(default = "default_true")]
pub referer: bool,
#[serde(default = "default_true")]
pub client_ip: bool,
}
impl Default for AccessLogFields {
fn default() -> Self {
Self {
timestamp: true,
trace_id: true,
method: true,
path: true,
query: true,
status: true,
latency_ms: true,
body_bytes_sent: true,
upstream_addr: true,
connection_reused: true,
rate_limit_hit: true,
geo_country: true,
user_agent: true,
referer: true,
client_ip: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccessLogConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_access_log_file")]
pub file: PathBuf,
#[serde(default = "default_access_log_format")]
pub format: String,
#[serde(default = "default_buffer_size")]
pub buffer_size: usize,
#[serde(default = "default_true")]
pub include_trace_id: bool,
#[serde(default = "default_sample_rate")]
pub sample_rate: f64,
#[serde(default = "default_true")]
pub sample_errors_always: bool,
#[serde(default)]
pub fields: AccessLogFields,
}
impl Default for AccessLogConfig {
fn default() -> Self {
Self {
enabled: true,
file: default_access_log_file(),
format: default_access_log_format(),
buffer_size: default_buffer_size(),
include_trace_id: true,
sample_rate: default_sample_rate(),
sample_errors_always: true,
fields: AccessLogFields::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorLogConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_error_log_file")]
pub file: PathBuf,
#[serde(default = "default_error_log_level")]
pub level: String,
#[serde(default = "default_buffer_size")]
pub buffer_size: usize,
}
impl Default for ErrorLogConfig {
fn default() -> Self {
Self {
enabled: true,
file: default_error_log_file(),
level: default_error_log_level(),
buffer_size: default_buffer_size(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditLogConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_audit_log_file")]
pub file: PathBuf,
#[serde(default = "default_buffer_size")]
pub buffer_size: usize,
#[serde(default = "default_true")]
pub log_blocked: bool,
#[serde(default = "default_true")]
pub log_agent_decisions: bool,
#[serde(default = "default_true")]
pub log_waf_events: bool,
}
impl Default for AuditLogConfig {
fn default() -> Self {
Self {
enabled: true,
file: default_audit_log_file(),
buffer_size: default_buffer_size(),
log_blocked: true,
log_agent_decisions: true,
log_waf_events: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TracingConfig {
pub backend: TracingBackend,
#[serde(default = "default_sampling_rate")]
pub sampling_rate: f64,
#[serde(default = "default_service_name")]
pub service_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TracingBackend {
Jaeger { endpoint: String },
Zipkin { endpoint: String },
Otlp { endpoint: String },
}
fn default_true() -> bool {
true
}
fn default_metrics_address() -> String {
"0.0.0.0:9090".to_string()
}
fn default_metrics_path() -> String {
"/metrics".to_string()
}
fn default_log_level() -> String {
"info".to_string()
}
fn default_log_format() -> String {
"json".to_string()
}
fn default_access_log_format() -> String {
"json".to_string()
}
fn default_buffer_size() -> usize {
8192
}
fn default_access_log_file() -> PathBuf {
PathBuf::from("/var/log/zentinel/access.log")
}
fn default_error_log_file() -> PathBuf {
PathBuf::from("/var/log/zentinel/error.log")
}
fn default_error_log_level() -> String {
"warn".to_string()
}
fn default_audit_log_file() -> PathBuf {
PathBuf::from("/var/log/zentinel/audit.log")
}
fn default_sampling_rate() -> f64 {
0.01
}
fn default_sample_rate() -> f64 {
1.0 }
fn default_service_name() -> String {
"zentinel".to_string()
}