1pub mod base;
2pub mod config;
3pub mod constants;
4pub mod entity;
5pub mod error;
6pub mod http;
7pub mod logging;
8pub mod server;
9pub mod state;
10pub mod utils;
11
12#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
13pub mod database;
14
15pub mod cache;
16
17#[cfg(feature = "balance")]
18pub mod balance;
19
20#[cfg(feature = "nacos")]
21pub mod nacos;
22
23#[cfg(feature = "kafka")]
24pub mod messaging;
25
26pub use base::{CursorPageBaseResp, R};
27pub use config::Config;
28#[cfg(feature = "kafka")]
29pub use config::{
30 KafkaConfig, KafkaConsumerConfig as ConfigKafkaConsumerConfig,
31 KafkaProducerConfig as ConfigKafkaProducerConfig,
32};
33pub use constants::{
34 CREATE_BY, CREATE_BY_FIELD, CREATE_ORG_ID_FIELD, CREATE_TIME, CREATE_TIME_FIELD, DELETE_FIELD,
35 ID_FIELD, LABEL, PARENT_ID, PARENT_ID_FIELD, SORT_VALUE, SORT_VALUE_FIELD, TENANT_ID,
36 UPDATE_BY, UPDATE_BY_FIELD, UPDATE_TIME, UPDATE_TIME_FIELD,
37};
38
39pub use entity::*;
40pub use error::{AppError, AppResult};
41pub use http::{
42 create_cors_layer, create_trace_layer, health_check, request_logger_middleware, root,
43};
44#[cfg(feature = "kafka")]
45pub use messaging::{
46 kafka::{KafkaConsumer, KafkaProducer},
47 Message, MessageConsumer, MessageConsumerType, MessageProducer, MessageProducerType,
48};
49#[cfg(feature = "consumer")]
50pub use messaging::{KafkaMessageHandler, KafkaMessageRouter};
51#[cfg(feature = "nacos")]
52pub use nacos::{
53 deregister_service, get_config, get_config_client, get_naming_client, get_service_instances,
54 get_subscribed_configs, get_subscribed_services, init_nacos, register_service,
55 subscribe_configs, subscribe_services,
56};
57pub use server::{Server, ServerBuilder};
58pub use state::AppState;
59pub use utils::get_uid_from_headers;
60
61pub(crate) fn init_logging(config: &Config) -> Result<(), anyhow::Error> {
68 use time::UtcOffset;
69 use tracing_subscriber::{
70 fmt::time::OffsetTime, layer::SubscriberExt, util::SubscriberInitExt,
71 };
72
73 let tz_hour = config.log.timezone;
75 let offset = UtcOffset::from_hms(tz_hour as i8, 0, 0)
76 .map_err(|e| {
77 if tz_hour >= 0 {
78 anyhow::anyhow!("无效的时区偏移 UTC+{}: {}", tz_hour, e)
79 } else {
80 anyhow::anyhow!("无效的时区偏移 UTC{}: {}", tz_hour, e)
81 }
82 })?;
83
84 eprintln!(
85 "ℹ 日志时区设置: UTC{}",
86 if tz_hour >= 0 {
87 format!("+{}", tz_hour)
88 } else {
89 tz_hour.to_string()
90 }
91 );
92
93 let timer = OffsetTime::new(
94 offset,
95 time::format_description::parse(
96 "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]",
97 )
98 .map_err(|e| anyhow::anyhow!("时间格式解析失败: {}", e))?,
99 );
100
101 let env_filter = tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
103 config.log.level.as_str().into()
104 });
105
106 let stdout_json = if config.log.json {
108 Some(
109 tracing_subscriber::fmt::layer()
110 .json()
111 .with_timer(timer.clone())
112 .with_line_number(true),
113 )
114 } else {
115 None
116 };
117 let stdout_plain = if !config.log.json {
118 Some(
119 tracing_subscriber::fmt::layer()
120 .with_timer(timer.clone())
121 .with_line_number(true),
122 )
123 } else {
124 None
125 };
126
127 let (file_json, file_plain) = if let Some(ref file_config) = config.log.file {
129 std::fs::create_dir_all(&file_config.directory)
131 .map_err(|e| anyhow::anyhow!("无法创建日志目录 {}: {}", file_config.directory, e))?;
132
133 if file_config.count_limit > 0 {
135 crate::logging::cleanup_old_logs(
136 &file_config.directory,
137 &file_config.filename,
138 file_config.count_limit,
139 ).ok();
140 }
141
142 let file_appender = match file_config.rotation.as_str() {
144 "size" => {
145 eprintln!(
146 "ℹ 日志配置: 按大小滚动 (限制: {}MB, 保留: {} 个文件)",
147 if file_config.size_limit_mb == 0 {
148 "无限制".to_string()
149 } else {
150 file_config.size_limit_mb.to_string()
151 },
152 if file_config.count_limit == 0 {
153 "无限制".to_string()
154 } else {
155 file_config.count_limit.to_string()
156 }
157 );
158 tracing_appender::rolling::daily(
159 &file_config.directory,
160 &file_config.filename,
161 )
162 }
163 _ => {
164 tracing_appender::rolling::daily(
165 &file_config.directory,
166 &file_config.filename,
167 )
168 }
169 };
170
171 let is_file_json = file_config.format.as_str() == "json";
172 let fj = if is_file_json {
173 Some(
174 tracing_subscriber::fmt::layer()
175 .json()
176 .with_writer(file_appender)
177 .with_timer(timer.clone())
178 .with_line_number(true)
179 .with_ansi(false),
180 )
181 } else {
182 None
183 };
184 let fp = if !is_file_json {
186 let file_appender2 = tracing_appender::rolling::daily(
187 &file_config.directory,
188 &file_config.filename,
189 );
190 Some(
191 tracing_subscriber::fmt::layer()
192 .with_writer(file_appender2)
193 .with_timer(timer)
194 .with_line_number(true)
195 .with_ansi(false),
196 )
197 } else {
198 None
199 };
200 (fj, fp)
201 } else {
202 (None, None)
203 };
204
205 tracing_subscriber::registry()
207 .with(env_filter)
208 .with(stdout_json)
209 .with(stdout_plain)
210 .with(file_json)
211 .with(file_plain)
212 .try_init()
213 .map_err(|e| anyhow::anyhow!("日志系统初始化失败: {}", e))?;
214
215 Ok(())
216}
217
218
219#[cfg(feature = "balance")]
220pub use balance::{
221 create_grpc_channel, create_grpc_client, get_load_balancer, get_service_endpoints,
222 grpc_call, get_instance_circuit_breaker, get_existing_instance_circuit_breaker,
223 CircuitBreaker, CircuitBreakerConfig, CircuitState, GrpcClientBuilder,
224 HealthCheckConfig, HealthStatus, LoadBalancer, ResilientGrpcClient, RetryConfig,
225 RoundRobinLoadBalancer, ServiceEndpoint, start_health_checker,
226};
227