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, request_logging_middleware, grpc_log_request, grpc_log_response,
43 health_check, root,
44};
45#[cfg(feature = "kafka")]
46pub use messaging::{
47 kafka::{KafkaConsumer, KafkaProducer},
48 Message, MessageConsumer, MessageConsumerType, MessageProducer, MessageProducerType,
49};
50#[cfg(feature = "consumer")]
51pub use messaging::{KafkaMessageHandler, KafkaMessageRouter};
52#[cfg(feature = "nacos")]
53pub use nacos::{
54 deregister_service, get_config, get_config_client, get_naming_client, get_service_instances,
55 get_subscribed_configs, get_subscribed_services, init_nacos, register_service,
56 subscribe_configs, subscribe_services,
57};
58pub use server::{Server, ServerBuilder};
59pub use state::AppState;
60pub use utils::get_uid_from_headers;
61
62pub(crate) fn init_logging(config: &Config) -> Result<(), anyhow::Error> {
69 use time::UtcOffset;
70 use tracing_subscriber::{
71 fmt::time::OffsetTime, layer::SubscriberExt, util::SubscriberInitExt,
72 };
73
74 let tz_hour = config.log.timezone;
76 let offset = UtcOffset::from_hms(tz_hour as i8, 0, 0)
77 .map_err(|e| {
78 if tz_hour >= 0 {
79 anyhow::anyhow!("无效的时区偏移 UTC+{}: {}", tz_hour, e)
80 } else {
81 anyhow::anyhow!("无效的时区偏移 UTC{}: {}", tz_hour, e)
82 }
83 })?;
84
85 eprintln!(
86 "ℹ 日志时区设置: UTC{}",
87 if tz_hour >= 0 {
88 format!("+{}", tz_hour)
89 } else {
90 tz_hour.to_string()
91 }
92 );
93
94 let timer = OffsetTime::new(
95 offset,
96 time::format_description::parse(
97 "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]",
98 )
99 .map_err(|e| anyhow::anyhow!("时间格式解析失败: {}", e))?,
100 );
101
102 let env_filter = tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
104 config.log.level.as_str().into()
105 });
106
107 let stdout_json = if config.log.json {
109 Some(
110 tracing_subscriber::fmt::layer()
111 .json()
112 .with_timer(timer.clone())
113 .with_line_number(true),
114 )
115 } else {
116 None
117 };
118 let stdout_plain = if !config.log.json {
119 Some(
120 tracing_subscriber::fmt::layer()
121 .with_timer(timer.clone())
122 .with_line_number(true),
123 )
124 } else {
125 None
126 };
127
128 let (file_json, file_plain) = if let Some(ref file_config) = config.log.file {
130 std::fs::create_dir_all(&file_config.directory)
132 .map_err(|e| anyhow::anyhow!("无法创建日志目录 {}: {}", file_config.directory, e))?;
133
134 if file_config.count_limit > 0 {
136 crate::logging::cleanup_old_logs(
137 &file_config.directory,
138 &file_config.filename,
139 file_config.count_limit,
140 ).ok();
141 }
142
143 let file_appender = match file_config.rotation.as_str() {
145 "size" => {
146 eprintln!(
147 "ℹ 日志配置: 按大小滚动 (限制: {}MB, 保留: {} 个文件)",
148 if file_config.size_limit_mb == 0 {
149 "无限制".to_string()
150 } else {
151 file_config.size_limit_mb.to_string()
152 },
153 if file_config.count_limit == 0 {
154 "无限制".to_string()
155 } else {
156 file_config.count_limit.to_string()
157 }
158 );
159 tracing_appender::rolling::daily(
160 &file_config.directory,
161 &file_config.filename,
162 )
163 }
164 _ => {
165 tracing_appender::rolling::daily(
166 &file_config.directory,
167 &file_config.filename,
168 )
169 }
170 };
171
172 let is_file_json = file_config.format.as_str() == "json";
173 let fj = if is_file_json {
174 Some(
175 tracing_subscriber::fmt::layer()
176 .json()
177 .with_writer(file_appender)
178 .with_timer(timer.clone())
179 .with_line_number(true)
180 .with_ansi(false),
181 )
182 } else {
183 None
184 };
185 let fp = if !is_file_json {
187 let file_appender2 = tracing_appender::rolling::daily(
188 &file_config.directory,
189 &file_config.filename,
190 );
191 Some(
192 tracing_subscriber::fmt::layer()
193 .with_writer(file_appender2)
194 .with_timer(timer)
195 .with_line_number(true)
196 .with_ansi(false),
197 )
198 } else {
199 None
200 };
201 (fj, fp)
202 } else {
203 (None, None)
204 };
205
206 tracing_subscriber::registry()
208 .with(env_filter)
209 .with(stdout_json)
210 .with(stdout_plain)
211 .with(file_json)
212 .with(file_plain)
213 .try_init()
214 .map_err(|e| anyhow::anyhow!("日志系统初始化失败: {}", e))?;
215
216 Ok(())
217}
218
219
220#[cfg(feature = "balance")]
221pub use balance::{
222 create_grpc_channel, create_grpc_client, get_load_balancer, get_service_endpoints,
223 grpc_call, get_instance_circuit_breaker, get_existing_instance_circuit_breaker,
224 CircuitBreaker, CircuitBreakerConfig, CircuitState, GrpcClientBuilder,
225 HealthCheckConfig, HealthStatus, LoadBalancer, ResilientGrpcClient, RetryConfig,
226 RoundRobinLoadBalancer, ServiceEndpoint, start_health_checker,
227};
228