Skip to main content

file_collector/
file_collector.rs

1//! Example usage of the data_hook crate with a file-based collector
2//!
3//! This example demonstrates how to implement a custom collector that writes
4//! data to files and how to use the data_hook! macro.
5
6use data_hook::{data_hook, set_collector, Collect, Record};
7use std::collections::HashMap;
8use std::fs::{File, OpenOptions};
9use std::io::{BufWriter, Write};
10use std::path::{Path, PathBuf};
11use std::sync::Mutex;
12use std::thread;
13use std::time::Duration;
14
15/// A file-based collector that writes data to files in a directory
16pub struct FileCollector {
17    output_dir: PathBuf,
18    writers: Mutex<HashMap<String, BufWriter<File>>>,
19}
20
21impl FileCollector {
22    /// Create a new file collector that writes to the specified directory
23    pub fn new<P: AsRef<Path>>(output_dir: P) -> std::io::Result<Self> {
24        let output_dir = output_dir.as_ref().to_path_buf();
25        
26        // Create the output directory if it doesn't exist
27        if !output_dir.exists() {
28            std::fs::create_dir_all(&output_dir)?;
29        }
30        
31        Ok(Self {
32            output_dir,
33            writers: Mutex::new(HashMap::new()),
34        })
35    }
36    
37    /// Get or create a writer for a specific category
38    fn get_or_create_writer(&self, category: &str) -> std::io::Result<()> {
39        let mut writers = self.writers.lock().unwrap();
40        
41        if !writers.contains_key(category) {
42            let file_path = self.output_dir.join(format!("{}.jsonl", category));
43            let file = OpenOptions::new()
44                .create(true)
45                .append(true)
46                .open(file_path)?;
47            let writer = BufWriter::new(file);
48            writers.insert(category.to_string(), writer);
49        }
50        
51        Ok(())
52    }
53}
54
55impl Collect for FileCollector {
56    fn collect(&self, record: &Record) {
57        if let Err(e) = self.get_or_create_writer(&record.category) {
58            eprintln!("Failed to create writer for category {}: {}", record.category, e);
59            return;
60        }
61
62        let mut writers = self.writers.lock().unwrap();
63        if let Some(writer) = writers.get_mut(&record.category) {
64            // Write as JSON Lines format
65            let json_line = format!(
66                r#"{{"category":"{}","data":"{}"}}"#,
67                record.category,
68                record.data.replace('"', r#"\""#).replace('\n', r#"\n"#)
69            );
70
71            if let Err(e) = writeln!(writer, "{}", json_line) {
72                eprintln!("Failed to write data record: {}", e);
73            }
74        }
75    }
76    
77    fn flush(&self) {
78        let mut writers = self.writers.lock().unwrap();
79        for writer in writers.values_mut() {
80            let _ = writer.flush();
81        }
82    }
83}
84
85impl Drop for FileCollector {
86    fn drop(&mut self) {
87        self.flush();
88    }
89}
90
91fn main() -> Result<(), Box<dyn std::error::Error>> {
92    println!("Data Hook Example with File Collector");
93    println!("=====================================");
94    
95    // Create and set up the file collector
96    let collector = FileCollector::new("data_output")?;
97    let collector: &'static FileCollector = Box::leak(Box::new(collector));
98    set_collector(collector);
99    
100    println!("Initialized file collector. Data will be saved to 'data_output' directory.");
101    
102    // Simulate some application events
103    simulate_user_actions();
104    simulate_performance_metrics();
105    simulate_error_events();
106    
107    // Flush any remaining data
108    collector.flush();
109    
110    println!("\nExample completed! Check the 'data_output' directory for collected data files:");
111    println!("- user_action.jsonl - User interaction events");
112    println!("- performance.jsonl - Performance metrics");
113    println!("- error.jsonl - Error events");
114    
115    Ok(())
116}
117
118fn simulate_user_actions() {
119    println!("\n--- Simulating User Actions ---");
120    
121    // Simple data collection
122    data_hook!("user_action", "User opened application");
123    
124    // Data collection with formatting
125    let user_id = 12345;
126    data_hook!("user_action", "User {} logged in", user_id);
127    
128    // Simulate button clicks
129    for i in 1..=3 {
130        data_hook!("user_action", "User clicked button: submit_{}", i);
131        thread::sleep(Duration::from_millis(100));
132    }
133    
134    // Navigation events
135    data_hook!("user_action", "User navigated to page: {}", "/dashboard");
136    data_hook!("user_action", "User navigated to page: {}", "/profile");
137    
138    println!("Collected {} user action events", 6);
139}
140
141fn simulate_performance_metrics() {
142    println!("\n--- Simulating Performance Metrics ---");
143    
144    // API response times
145    let response_times = [45, 123, 67, 234, 89];
146    for (i, time) in response_times.iter().enumerate() {
147        data_hook!("performance", "API request {} took {} ms", i + 1, time);
148        thread::sleep(Duration::from_millis(50));
149    }
150    
151    // Memory usage
152    data_hook!("performance", "Memory usage: {} MB", 256);
153    data_hook!("performance", "CPU usage: {}%", 15.7);
154    
155    // Database query performance
156    data_hook!("performance", "Database query executed in {} ms: {}", 89, "SELECT * FROM users");
157    
158    println!("Collected {} performance metrics", 8);
159}
160
161fn simulate_error_events() {
162    println!("\n--- Simulating Error Events ---");
163    
164    // Network errors
165    data_hook!("error", "Network timeout: Failed to connect to {}", "api.example.com");
166    
167    // Validation errors
168    data_hook!("error", "Validation failed: {} field is required", "email");
169    
170    // System errors
171    data_hook!("error", "File not found: {}", "/path/to/config.json");
172    
173    // Custom error with context
174    let error_code = 500;
175    let error_msg = "Internal server error";
176    data_hook!("error", "HTTP {} - {}: Request failed for endpoint /api/users", error_code, error_msg);
177    
178    println!("Collected {} error events", 4);
179}
180
181// Example of using data_hook in different modules
182mod user_service {
183    use data_hook::data_hook;
184    
185    pub fn create_user(username: &str) {
186        // Simulate user creation logic
187        data_hook!("user_action", "Creating user: {}", username);
188        
189        // Simulate some processing time
190        std::thread::sleep(std::time::Duration::from_millis(10));
191        
192        data_hook!("user_action", "User created successfully: {}", username);
193    }
194    
195    pub fn delete_user(user_id: u32) {
196        data_hook!("user_action", "Deleting user with ID: {}", user_id);
197        
198        // Simulate deletion
199        std::thread::sleep(std::time::Duration::from_millis(5));
200        
201        data_hook!("user_action", "User {} deleted", user_id);
202    }
203}