use super::LoggerManager;
use crate::InklogError;
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
use crate::integrations::Database;
use crate::integrations::{Cache, Config};
use crate::{ConsoleSinkConfig, FileSinkConfig, InklogConfig};
use std::sync::Arc;
#[derive(Default)]
pub struct LoggerDependencies {
pub cache: Option<Arc<dyn Cache>>,
pub config: Option<Arc<dyn Config>>,
pub custom_sinks: Vec<Arc<dyn crate::LogSink>>,
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub database: Option<Arc<dyn Database>>,
}
impl std::fmt::Debug for LoggerDependencies {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut builder = f.debug_struct("LoggerDependencies");
builder
.field("cache", &self.cache.as_ref().map(|_| "Arc<dyn Cache>"))
.field("config", &self.config.as_ref().map(|_| "Arc<dyn Config>"))
.field("custom_sinks", &self.custom_sinks.len());
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
builder.field(
"database",
&self.database.as_ref().map(|_| "Arc<dyn Database>"),
);
builder.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_valid_level() {
let builder = LoggerBuilder::new().level("debug");
assert!(builder.validation_errors.is_empty());
assert_eq!(builder.config.global.level, "debug");
}
#[test]
fn test_builder_invalid_level() {
let builder = LoggerBuilder::new().level("invalid_level");
assert_eq!(builder.validation_errors.len(), 1);
assert!(builder.validation_errors[0].contains("Invalid log level"));
}
#[test]
fn test_builder_case_insensitive_level() {
let builder = LoggerBuilder::new().level("DEBUG");
assert!(builder.validation_errors.is_empty());
}
#[test]
fn test_builder_multiple_errors() {
let builder = LoggerBuilder::new().level("bad1").level("bad2");
assert_eq!(builder.validation_errors.len(), 2);
}
#[test]
fn test_builder_invalid_level_keeps_default_value() {
let builder = LoggerBuilder::new().level("not-a-level");
assert_eq!(builder.validation_errors.len(), 1);
assert_eq!(builder.config.global.level, "info");
}
#[test]
fn test_builder_invalid_level_keeps_previous_value() {
let builder = LoggerBuilder::new().level("debug").level("not-a-level");
assert_eq!(builder.validation_errors.len(), 1);
assert_eq!(builder.config.global.level, "debug");
}
#[cfg(feature = "http")]
#[test]
fn test_builder_http_port_zero_does_not_write_config() {
let builder = LoggerBuilder::new().http_port(0);
assert_eq!(builder.validation_errors.len(), 1);
assert!(builder.config.http_server.is_none());
}
#[cfg(feature = "http")]
#[test]
fn test_builder_http_port_zero_does_not_overwrite_existing() {
let builder = LoggerBuilder::new()
.enable_http_server(true)
.http_port(8080)
.http_port(0);
assert_eq!(builder.validation_errors.len(), 1);
assert_eq!(builder.config.http_server.as_ref().unwrap().port, 8080);
}
#[cfg(feature = "http")]
#[test]
fn test_builder_http_port_zero() {
let builder = LoggerBuilder::new().http_port(0);
assert_eq!(builder.validation_errors.len(), 1);
assert!(builder.validation_errors[0].contains("HTTP port"));
}
#[cfg(feature = "http")]
#[test]
fn test_builder_http_port_valid() {
let builder = LoggerBuilder::new().http_port(8080);
assert!(builder.validation_errors.is_empty());
}
#[test]
fn test_builder_add_sink_accumulates_and_upcasts() {
use crate::InklogError;
use crate::LogRecord;
use async_trait::async_trait;
struct NoopSink;
#[async_trait]
impl crate::support::io::LogSink for NoopSink {
async fn write(&self, _record: &LogRecord) -> Result<(), InklogError> {
Ok(())
}
async fn flush(&self) -> Result<(), InklogError> {
Ok(())
}
async fn shutdown(&self) -> Result<(), InklogError> {
Ok(())
}
}
let sink: std::sync::Arc<dyn crate::support::io::sink::AsyncSink> =
std::sync::Arc::new(NoopSink);
let builder = LoggerBuilder::new().add_sink(sink);
assert_eq!(builder.deps.custom_sinks.len(), 1);
let as_log_sink: std::sync::Arc<dyn crate::support::io::LogSink> =
builder.deps.custom_sinks[0].clone();
let _ = as_log_sink;
}
#[tokio::test]
async fn test_builder_build_fails_with_validation_errors() {
let result = LoggerBuilder::new().level("invalid").build().await;
assert!(result.is_err());
match result {
Err(e) => {
let msg = e.to_string();
assert!(
msg.contains("Builder validation failed") || msg.contains("构建器验证失败")
);
}
Ok(_) => panic!("Expected build to fail with validation errors"),
}
}
#[test]
fn test_builder_file_when_no_file_sink() {
let mut builder = LoggerBuilder::new();
builder.config.file_sink = None;
builder = builder.file("/tmp/test.log");
assert!(builder.config.file_sink.is_some());
let file_cfg = builder.config.file_sink.as_ref().unwrap();
assert!(file_cfg.enabled);
assert_eq!(file_cfg.path, std::path::PathBuf::from("/tmp/test.log"));
}
#[test]
fn test_builder_file_when_file_sink_exists() {
let mut builder = LoggerBuilder::new();
builder.config.file_sink = Some(FileSinkConfig::default());
builder = builder.file("/tmp/updated.log");
let file_cfg = builder.config.file_sink.as_ref().unwrap();
assert!(file_cfg.enabled);
assert_eq!(file_cfg.path, std::path::PathBuf::from("/tmp/updated.log"));
}
#[cfg(feature = "http")]
#[test]
fn test_builder_http_error_mode_unknown() {
let builder = LoggerBuilder::new().http_error_mode("invalid_mode");
assert_eq!(builder.validation_errors.len(), 1);
assert!(builder.validation_errors[0].contains("Unknown HTTP error mode"));
}
#[cfg(feature = "http")]
#[test]
fn test_builder_http_error_mode_warn() {
let builder = LoggerBuilder::new().http_error_mode("warn");
assert!(builder.validation_errors.is_empty());
assert!(matches!(
builder.config.http_server.as_ref().unwrap().error_mode,
crate::HttpErrorMode::Warn
));
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_with_pool_size_sets_config() {
let builder = LoggerBuilder::new()
.database("postgres://localhost/logs")
.with_pool_size(5);
let db = builder.config.database_sink.as_ref().unwrap();
assert_eq!(db.pool_size, 5);
assert!(db.enabled);
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_with_pool_size_creates_config_when_absent() {
let builder = LoggerBuilder::new().with_pool_size(3);
let db = builder.config.database_sink.as_ref().unwrap();
assert_eq!(db.pool_size, 3);
assert!(!db.enabled);
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_with_batch_size_sets_config() {
let builder = LoggerBuilder::new()
.database("postgres://localhost/logs")
.with_batch_size(50);
assert_eq!(
builder.config.database_sink.as_ref().unwrap().batch_size,
50
);
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_with_flush_interval_ms_sets_config() {
let builder = LoggerBuilder::new()
.database("postgres://localhost/logs")
.with_flush_interval_ms(250);
assert_eq!(
builder
.config
.database_sink
.as_ref()
.unwrap()
.flush_interval_ms,
250
);
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_with_table_name_sets_config() {
let builder = LoggerBuilder::new()
.database("postgres://localhost/logs")
.with_table_name("app_logs");
assert_eq!(
builder.config.database_sink.as_ref().unwrap().table_name,
"app_logs"
);
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_with_admin_role_sets_config() {
let builder = LoggerBuilder::new()
.database("postgres://localhost/logs")
.with_admin_role("log_admin");
assert_eq!(
builder.config.database_sink.as_ref().unwrap().admin_role,
"log_admin"
);
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_with_driver_sets_config() {
let builder = LoggerBuilder::new()
.database("sqlite::memory:")
.with_driver(crate::DatabaseDriver::MySQL);
let db = builder.config.database_sink.as_ref().unwrap();
assert!(matches!(db.driver, crate::DatabaseDriver::MySQL));
assert!(builder.db_driver_explicit);
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_database_infers_postgres_driver() {
let builder = LoggerBuilder::new().database("postgres://localhost/logs");
let db = builder.config.database_sink.as_ref().unwrap();
assert!(matches!(db.driver, crate::DatabaseDriver::PostgreSQL));
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_database_infers_mysql_driver() {
let builder = LoggerBuilder::new().database("mysql://localhost/logs");
assert!(matches!(
builder.config.database_sink.as_ref().unwrap().driver,
crate::DatabaseDriver::MySQL
));
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_database_infers_sqlite_memory_driver() {
let builder = LoggerBuilder::new().database("sqlite::memory:");
assert!(matches!(
builder.config.database_sink.as_ref().unwrap().driver,
crate::DatabaseDriver::SQLite
));
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_database_unknown_scheme_keeps_default_driver() {
let builder = LoggerBuilder::new().database("custom-backend://localhost");
let db = builder.config.database_sink.as_ref().unwrap();
assert!(matches!(db.driver, crate::DatabaseDriver::SQLite));
assert!(builder.validation_errors.is_empty());
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_infer_driver_common_schemes() {
use crate::DatabaseDriver;
assert_eq!(
LoggerBuilder::infer_driver("postgres://h/db"),
Some(DatabaseDriver::PostgreSQL)
);
assert_eq!(
LoggerBuilder::infer_driver("postgresql://h/db"),
Some(DatabaseDriver::PostgreSQL)
);
assert_eq!(
LoggerBuilder::infer_driver("mysql://h/db"),
Some(DatabaseDriver::MySQL)
);
assert_eq!(
LoggerBuilder::infer_driver("sqlite://f.db"),
Some(DatabaseDriver::SQLite)
);
assert_eq!(
LoggerBuilder::infer_driver("sqlite3://f.db"),
Some(DatabaseDriver::SQLite)
);
assert_eq!(
LoggerBuilder::infer_driver("sqlite::memory:"),
Some(DatabaseDriver::SQLite)
);
assert_eq!(
LoggerBuilder::infer_driver("duckdb://f.db"),
Some(DatabaseDriver::DuckDB)
);
assert_eq!(
LoggerBuilder::infer_driver("POSTGRES://h/db"),
Some(DatabaseDriver::PostgreSQL)
);
assert_eq!(LoggerBuilder::infer_driver("weird://x"), None);
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_with_driver_conflicting_url_records_error() {
let builder = LoggerBuilder::new()
.database("postgres://localhost/logs")
.with_driver(crate::DatabaseDriver::SQLite);
assert_eq!(builder.validation_errors.len(), 1);
assert!(builder.validation_errors[0].contains("conflicts with"));
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
#[test]
fn test_builder_database_conflicting_explicit_driver_records_error() {
let builder = LoggerBuilder::new()
.with_driver(crate::DatabaseDriver::MySQL)
.database("postgres://localhost/logs");
assert_eq!(builder.validation_errors.len(), 1);
assert!(builder.validation_errors[0].contains("conflicts with"));
assert!(matches!(
builder.config.database_sink.as_ref().unwrap().driver,
crate::DatabaseDriver::PostgreSQL
));
}
}
#[derive(Default)]
pub struct LoggerBuilder {
pub(crate) config: InklogConfig,
pub(crate) deps: LoggerDependencies,
pub(crate) validation_errors: Vec<String>,
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub(crate) db_driver_explicit: bool,
}
impl LoggerBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn level(mut self, level: impl Into<String>) -> Self {
let level_str = level.into();
if !crate::LogLevel::is_valid_level(&level_str) {
self.validation_errors.push(format!(
"Invalid log level '{}'. Valid levels: {}",
level_str,
crate::LogLevel::VALID_LEVEL_STRINGS.join(", ")
));
return self;
}
self.config.global.level = level_str;
self
}
pub fn format(mut self, format: impl Into<String>) -> Self {
self.config.global.format = format.into();
self
}
pub fn console(mut self, enabled: bool) -> Self {
if let Some(ref mut console) = self.config.console_sink {
console.enabled = enabled;
} else if enabled {
self.config.console_sink = Some(ConsoleSinkConfig::default());
}
self
}
pub fn file(mut self, path: impl Into<std::path::PathBuf>) -> Self {
if let Some(ref mut file) = self.config.file_sink {
file.enabled = true;
file.path = path.into();
} else {
let path_buf = path.into();
self.config.file_sink = Some(FileSinkConfig {
enabled: true,
path: path_buf,
..Default::default()
});
}
self
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub fn database(mut self, url: impl Into<String>) -> Self {
let url_str = url.into();
let inferred = Self::infer_driver(&url_str);
if self.db_driver_explicit
&& let Some(ref db) = self.config.database_sink
&& let Some(ref target) = inferred
&& db.driver != *target
{
self.validation_errors.push(format!(
"Database driver '{}' (explicitly set) conflicts with driver '{}' inferred from URL '{}'. The inferred driver takes precedence.",
db.driver, target, url_str
));
}
if let Some(ref mut db) = self.config.database_sink {
db.enabled = true;
db.url = url_str;
if let Some(target) = inferred {
db.driver = target;
}
} else {
self.config.database_sink = Some(crate::DatabaseSinkConfig {
enabled: true,
url: url_str,
driver: inferred.unwrap_or_default(),
..Default::default()
});
}
self
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
fn infer_driver(url: &str) -> Option<crate::DatabaseDriver> {
let scheme = url.split(':').next()?;
match scheme.to_ascii_lowercase().as_str() {
"postgres" | "postgresql" => Some(crate::DatabaseDriver::PostgreSQL),
"mysql" => Some(crate::DatabaseDriver::MySQL),
"sqlite" | "sqlite3" => Some(crate::DatabaseDriver::SQLite),
"duckdb" => Some(crate::DatabaseDriver::DuckDB),
_ => None,
}
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub fn with_driver(mut self, driver: crate::DatabaseDriver) -> Self {
if let Some(ref db) = self.config.database_sink
&& let Some(target) = Self::infer_driver(&db.url)
&& target != driver
{
self.validation_errors.push(format!(
"Database driver '{}' conflicts with driver '{}' inferred from URL '{}'.",
driver, target, db.url
));
}
self.db_driver_explicit = true;
if let Some(ref mut db) = self.config.database_sink {
db.driver = driver;
} else {
self.config.database_sink = Some(crate::DatabaseSinkConfig {
driver,
..Default::default()
});
}
self
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub fn with_pool_size(mut self, pool_size: u32) -> Self {
if let Some(ref mut db) = self.config.database_sink {
db.pool_size = pool_size;
} else {
self.config.database_sink = Some(crate::DatabaseSinkConfig {
pool_size,
..Default::default()
});
}
self
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub fn with_batch_size(mut self, batch_size: usize) -> Self {
if let Some(ref mut db) = self.config.database_sink {
db.batch_size = batch_size;
} else {
self.config.database_sink = Some(crate::DatabaseSinkConfig {
batch_size,
..Default::default()
});
}
self
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub fn with_flush_interval_ms(mut self, flush_interval_ms: u64) -> Self {
if let Some(ref mut db) = self.config.database_sink {
db.flush_interval_ms = flush_interval_ms;
} else {
self.config.database_sink = Some(crate::DatabaseSinkConfig {
flush_interval_ms,
..Default::default()
});
}
self
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub fn with_table_name(mut self, table_name: impl Into<String>) -> Self {
let table_name = table_name.into();
if let Some(ref mut db) = self.config.database_sink {
db.table_name = table_name;
} else {
self.config.database_sink = Some(crate::DatabaseSinkConfig {
table_name,
..Default::default()
});
}
self
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub fn with_admin_role(mut self, admin_role: impl Into<String>) -> Self {
let admin_role = admin_role.into();
if let Some(ref mut db) = self.config.database_sink {
db.admin_role = admin_role;
} else {
self.config.database_sink = Some(crate::DatabaseSinkConfig {
admin_role,
..Default::default()
});
}
self
}
pub fn channel_capacity(mut self, capacity: usize) -> Self {
self.config.performance.channel_capacity = capacity;
self
}
pub fn worker_threads(mut self, threads: usize) -> Self {
self.config.performance.worker_threads = threads;
self
}
pub fn console_colored(mut self, colored: bool) -> Self {
if let Some(ref mut console) = self.config.console_sink {
console.colored = colored;
} else if colored {
self.config.console_sink = Some(ConsoleSinkConfig {
colored,
..Default::default()
});
}
self
}
pub fn console_stderr_levels(mut self, levels: &[&str]) -> Self {
if let Some(ref mut console) = self.config.console_sink {
console.stderr_levels = levels.iter().map(|s| (*s).to_string()).collect();
} else {
self.config.console_sink = Some(ConsoleSinkConfig {
stderr_levels: levels.iter().map(|s| (*s).to_string()).collect(),
..Default::default()
});
}
self
}
pub fn file_max_size(mut self, max_size: impl Into<String>) -> Self {
if let Some(ref mut file) = self.config.file_sink {
file.max_size = max_size.into();
} else {
self.config.file_sink = Some(FileSinkConfig {
max_size: max_size.into(),
..Default::default()
});
}
self
}
pub fn file_compress(mut self, compress: bool) -> Self {
if let Some(ref mut file) = self.config.file_sink {
file.compress = compress;
} else {
self.config.file_sink = Some(FileSinkConfig {
compress,
..Default::default()
});
}
self
}
pub fn file_rotation_time(mut self, rotation: impl Into<String>) -> Self {
if let Some(ref mut file) = self.config.file_sink {
file.rotation_time = rotation.into();
} else {
self.config.file_sink = Some(FileSinkConfig {
rotation_time: rotation.into(),
..Default::default()
});
}
self
}
pub fn file_keep_files(mut self, keep: u32) -> Self {
if let Some(ref mut file) = self.config.file_sink {
file.keep_files = keep;
} else {
self.config.file_sink = Some(FileSinkConfig {
keep_files: keep,
..Default::default()
});
}
self
}
#[cfg(feature = "http")]
pub fn enable_http_server(mut self, enabled: bool) -> Self {
if let Some(ref mut http) = self.config.http_server {
http.enabled = enabled;
} else if enabled {
self.config.http_server = Some(crate::HttpServerConfig {
enabled: true,
..Default::default()
});
}
self
}
#[cfg(feature = "http")]
pub fn http_host(mut self, host: impl Into<String>) -> Self {
if let Some(ref mut http) = self.config.http_server {
http.host = host.into();
} else {
self.config.http_server = Some(crate::HttpServerConfig {
host: host.into(),
..Default::default()
});
}
self
}
#[cfg(feature = "http")]
pub fn http_port(mut self, port: u16) -> Self {
if port == 0 {
self.validation_errors
.push("HTTP port must be between 1 and 65535".to_string());
return self;
}
if let Some(ref mut http) = self.config.http_server {
http.port = port;
} else {
self.config.http_server = Some(crate::HttpServerConfig {
port,
..Default::default()
});
}
self
}
#[cfg(feature = "http")]
pub fn http_metrics_path(mut self, path: impl Into<String>) -> Self {
if let Some(ref mut http) = self.config.http_server {
http.metrics_path = path.into();
} else {
self.config.http_server = Some(crate::HttpServerConfig {
metrics_path: path.into(),
..Default::default()
});
}
self
}
#[cfg(feature = "http")]
pub fn http_health_path(mut self, path: impl Into<String>) -> Self {
if let Some(ref mut http) = self.config.http_server {
http.health_path = path.into();
} else {
self.config.http_server = Some(crate::HttpServerConfig {
health_path: path.into(),
..Default::default()
});
}
self
}
#[cfg(feature = "http")]
pub fn http_error_mode(mut self, mode: impl Into<String>) -> Self {
let mode_str = mode.into();
let error_mode = match mode_str.to_lowercase().as_str() {
"warn" => crate::HttpErrorMode::Warn,
"strict" => crate::HttpErrorMode::Strict,
_ => {
self.validation_errors.push(format!(
"Unknown HTTP error mode '{}'. Valid modes: warn, strict. Using default 'strict'.",
mode_str
));
crate::HttpErrorMode::default()
}
};
if let Some(ref mut http) = self.config.http_server {
http.error_mode = error_mode;
} else {
self.config.http_server = Some(crate::HttpServerConfig {
error_mode,
..Default::default()
});
}
self
}
pub fn cache(mut self, cache: Arc<dyn Cache>) -> Self {
self.deps.cache = Some(cache);
self
}
pub fn config(mut self, config: Arc<dyn Config>) -> Self {
self.deps.config = Some(config);
self
}
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
pub fn with_database(mut self, database: Arc<dyn Database>) -> Self {
self.deps.database = Some(database);
self
}
pub fn add_sink(mut self, sink: Arc<dyn crate::support::io::sink::AsyncSink>) -> Self {
let sink: Arc<dyn crate::LogSink> = sink;
self.deps.custom_sinks.push(sink);
self
}
pub async fn build(self) -> Result<LoggerManager, InklogError> {
if !self.validation_errors.is_empty() {
let mut args = fluent_bundle::FluentArgs::new();
args.set("count", self.validation_errors.len());
return Err(InklogError::ConfigError(crate::i18n::tr_args(
"config-builder_validation_failed",
args,
)));
}
let has_deps = self.deps.cache.is_some()
|| self.deps.config.is_some()
|| !self.deps.custom_sinks.is_empty()
|| {
#[cfg(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
))]
{
self.deps.database.is_some()
}
#[cfg(not(any(
feature = "sqlite",
feature = "postgres",
feature = "mysql",
feature = "duckdb"
)))]
{
false
}
};
if has_deps {
if self.deps.config.is_none() {
let mut deps = self.deps;
deps.config = Some(Arc::new(
crate::integrations::infra::InklogConfigAdapter::from_config(
self.config.clone(),
),
));
return LoggerManager::build_with_deps(deps).await;
}
LoggerManager::build_with_deps(self.deps).await
} else {
LoggerManager::with_config(self.config).await
}
}
}