trojan_analytics/lib.rs
1//! Analytics module for trojan-rs.
2//!
3//! This module provides detailed connection event collection and export to ClickHouse
4//! for traffic analysis, billing, and auditing.
5//!
6//! # Feature Gating
7//!
8//! This crate should be used with the `analytics` feature in `trojan-server`:
9//!
10//! ```toml
11//! [features]
12//! analytics = ["trojan-analytics"]
13//! ```
14//!
15//! # Example
16//!
17//! ```ignore
18//! use trojan_analytics::{EventCollector, init};
19//! use trojan_config::AnalyticsConfig;
20//!
21//! // Initialize analytics
22//! let collector = init(config).await?;
23//!
24//! // Record connection events
25//! let event = collector.connection(conn_id, peer)
26//! .user("user123")
27//! .target("example.com", 443, TargetType::Domain)
28//! .protocol(Protocol::Tcp);
29//!
30//! // Event is automatically sent on drop
31//! ```
32
33mod collector;
34mod error;
35mod event;
36mod writer;
37
38pub use collector::{ConnectionEventBuilder, EventCollector};
39pub use error::AnalyticsError;
40pub use event::*;
41pub use trojan_config::{
42 AnalyticsBufferConfig, AnalyticsConfig, AnalyticsPrivacyConfig, AnalyticsSamplingConfig,
43 ClickHouseConfig,
44};
45
46use std::sync::Arc;
47use tokio::sync::mpsc;
48use tracing::info;
49
50/// Initialize the analytics module.
51///
52/// Returns an `EventCollector` that can be cloned and used across threads
53/// to record connection events.
54///
55/// # Errors
56///
57/// Returns an error if ClickHouse configuration is missing or connection fails.
58pub async fn init(config: AnalyticsConfig) -> Result<EventCollector, AnalyticsError> {
59 if !config.enabled {
60 return Err(AnalyticsError::Disabled);
61 }
62
63 let clickhouse_config = config.clickhouse.as_ref().ok_or(AnalyticsError::Config(
64 "clickhouse config is required".into(),
65 ))?;
66
67 // Create bounded channel for events
68 let buffer_size = config.buffer.size;
69 let (tx, rx) = mpsc::channel(buffer_size);
70
71 // Create ClickHouse client
72 let client = writer::clickhouse::create_client(clickhouse_config)?;
73
74 // Start background writer task
75 let writer_config = config.clone();
76 tokio::spawn(async move {
77 writer::run_writer(rx, client, writer_config).await;
78 });
79
80 info!(
81 buffer_size = buffer_size,
82 batch_size = config.buffer.batch_size,
83 flush_interval_secs = config.buffer.flush_interval_secs,
84 "analytics initialized"
85 );
86
87 Ok(EventCollector::new(tx, Arc::new(config)))
88}