docserver/src/defs/tracedata.rs

291 lines
10 KiB
Rust
Raw Normal View History

2023-07-19 04:00:41 +01:00
use serde::{Deserialize,Serialize};
use std::{
io::Write,
// sync::Arc,
fmt::Debug,
fs::{self,File},
path::{Path, PathBuf},
io::{Error, ErrorKind, BufRead, BufReader},
collections::HashMap,
};
use log::error;
use crate::{
defs::{
FILE_SCHEME,
SID_TRACE_FILE,
Config,
},
tools::str_date_from_timestamp,
};
fn default_empty() -> String {
"/".to_string()
}
fn default_tracedata_items() -> Vec<TraceContent> {
Vec::new()
}
fn default_tracecontent() -> TraceContent {
TraceContent::default()
}
fn default_tracecontent_req() -> HashMap<String,String> {
HashMap::new()
}
#[derive(Default,Deserialize,Serialize,Debug,Clone)]
pub struct RequestData {
#[serde(default = "default_empty")]
pub agent: String,
}
#[derive(Default,Deserialize,Serialize,Debug,Clone)]
pub struct TraceContent {
#[serde(default = "default_empty")]
pub when: String,
#[serde(default = "default_empty")]
pub sid: String,
#[serde(default = "default_empty")]
pub origin: String,
#[serde(default = "default_empty")]
pub trigger: String,
#[serde(default = "default_empty")]
pub id: String,
#[serde(default = "default_empty")]
pub info: String,
#[serde(default = "default_empty")]
pub context: String,
#[serde(default = "default_empty")]
pub role: String,
#[serde(default = "default_tracecontent_req")]
pub req: HashMap<String,String>,
}
impl TraceContent {
pub fn to_json(&self) -> String {
serde_json::to_string(self).unwrap_or_else(|e|{
println!("Error to convert TraceContent to json: {}",e);
String::from("")
})
}
}
#[derive(Default,Deserialize,Serialize,Debug,Clone)]
pub struct TraceLogContent {
#[serde(default = "default_empty")]
pub line_id: String,
#[serde(default = "default_tracecontent")]
pub content: TraceContent,
}
#[derive(Default,Deserialize,Serialize,Debug,Clone)]
pub struct TraceData {
#[serde(default = "default_empty")]
pub user_id: String,
#[serde(default = "default_empty")]
pub timestamp: String,
#[serde(default = "default_tracedata_items")]
pub contents: Vec<TraceContent>,
}
impl TraceData {
fn id_path(&self, file: &str, config: &Config) -> String {
if self.user_id.is_empty() {
format!("{}/{}",
config.trace_store_uri.replace(FILE_SCHEME, ""),
file)
} else {
format!("{}/{}/{}",
config.trace_store_uri.replace(FILE_SCHEME, ""),
self.user_id,file)
}
}
fn write_data(&self, file_path: &str, data: &str, overwrite: bool, verbose: u8 ) -> std::io::Result<()> {
let check_path = |path: &Path| -> std::io::Result<()> {
if ! Path::new(&path).exists() {
if let Err(e) = std::fs::create_dir(&path) {
return Err(Error::new( ErrorKind::InvalidInput,
format!("Error create path {}: {}",&path.display(), e)
));
}
}
Ok(())
};
if file_path.is_empty() || data.is_empty() {
return Err(Error::new(
ErrorKind::InvalidInput,
format!("Error save {}",&file_path)
));
}
if ! Path::new(&file_path).exists() {
let path = PathBuf::from(&file_path);
if let Some(dir_path) = path.parent() {
if ! Path::new(&dir_path).exists() {
if let Some(parent_dir_path) = dir_path.parent() {
if ! Path::new(&parent_dir_path).exists() {
let res = check_path(&parent_dir_path);
if res.is_err() { return res; }
}
}
let res = check_path(&dir_path);
if res.is_err() { return res; }
}
}
}
if overwrite || ! Path::new(&file_path).exists() {
fs::write(&file_path, data)?;
if verbose > 2 { println!("Overwrite: {}",&file_path); }
} else {
let sid_settings_file = fs::OpenOptions::new()
.write(true)
.append(true) // This is needed to append to file
.open(&file_path);
if let Ok(mut file) = sid_settings_file {
file.write_all(data.as_bytes())?;
}
if verbose > 2 { println!("write: {}",&file_path); }
}
Ok(())
}
pub fn contents_to_json(&self) -> Vec<String> {
self.contents.clone().into_iter().map(|item| item.to_json()).collect()
}
pub fn save(&self, config: &Config, overwrite: bool) -> std::io::Result<()> {
let file_trace_path = self.id_path(&format!("{}",SID_TRACE_FILE), config);
let contents = self.contents_to_json();
let mut result = Ok(());
let cnt_lines = contents.len();
let lines = if cnt_lines > 0 {
cnt_lines - 1
} else {
cnt_lines
};
let mut write_overwrite = overwrite;
for (idx, line) in contents.iter().enumerate() {
let prfx = if idx == 0 && Path::new(&file_trace_path).exists() {
",\n"
} else {
""
};
let sfx = if idx == lines {
""
} else {
",\n"
};
result = self.write_data(
&file_trace_path,
format!("{}{}{}",&prfx,&line,&sfx).as_str(),
write_overwrite,
config.verbose
);
let _ = result.as_ref().unwrap_or_else(|e|{
println!("Error save trace contets: {} line {}",&e, &idx);
&()
});
if write_overwrite { write_overwrite = false}
if result.is_err() { break; }
}
result
}
pub fn get_reader(self, config: &Config) -> std::io::Result<BufReader<File>> {
let file_path = self.id_path(&format!("{}",SID_TRACE_FILE), config);
if ! Path::new(&file_path).exists() {
error!("Error file path not exist: {}",&file_path);
return Err(Error::new(
ErrorKind::InvalidInput,
format!("Error file path not exist: {}",file_path)
));
}
let file = File::open(&file_path)?;
let reader = BufReader::new(file);
Ok(reader)
}
pub fn load(&self, config: &Config, human: bool, reverse: bool) -> Vec<TraceLogContent> {
let mut log: Vec<TraceLogContent> = Vec::new();
match self.clone().get_reader(config) {
Ok(reader) => {
let mut line_pos = 0;
for line in reader.lines() {
line_pos +=1;
let line = match line {
Ok(res) => if res.ends_with(",") {
res[0..res.len() - 1].to_owned()
} else {
res
},
Err(_) => continue,
};
if ! line.is_empty() {
let mut log_line: TraceContent = serde_json::from_str(&line).unwrap_or_else(|e| {
println!("Error parse load line {}: {}", &line_pos,e);
TraceContent::default()
});
if ! log_line.when.is_empty() {
let line_id = log_line.when.to_owned();
if human {
log_line.when = str_date_from_timestamp(&log_line.when);
}
log.push(TraceLogContent { line_id, content: log_line })
}
}
}
},
Err(e) => {
error!("Error on load log: {}",e);
}
}
if reverse { log.reverse(); }
log
}
pub fn clean(&self, config: &Config, line_id: &str) -> std::io::Result<()> {
let file_path = self.id_path(&format!("{}",SID_TRACE_FILE), config);
if ! Path::new(&file_path).exists() {
error!("Error file path not exist: {}",&file_path);
return Err(Error::new(
ErrorKind::InvalidInput,
format!("Error file path not exist: {}",file_path)
));
}
if line_id == "ALL" {
std::fs::remove_file(&file_path)
} else {
let mut log: Vec<TraceContent> = Vec::new();
match self.clone().get_reader(config) {
Ok(reader) => {
let mut line_pos = 0;
for line in reader.lines() {
line_pos +=1;
let line = match line {
Ok(res) => if res.ends_with(",") {
res[0..res.len() - 1].to_owned()
} else {
res
},
Err(_) => continue,
};
if ! line.is_empty() {
let log_line: TraceContent = serde_json::from_str(&line).unwrap_or_else(|e| {
println!("Error parse load line {}: {}", &line_pos,e);
TraceContent::default()
});
if ! log_line.when.is_empty() && log_line.when != line_id {
log.push(log_line)
}
}
}
},
Err(e) => {
error!("Error on load log: {}",e);
}
}
if log.len() == 0 { return std::fs::remove_file(&file_path) }
let trace_data = TraceData {
user_id: self.user_id.to_owned(),
timestamp: String::from(""),
contents: log
};
trace_data.save(config, true).unwrap_or_else(|e|{
println!("Error save filter {} to path {}: {}",line_id, &file_path,&e);
error!("Error save filter {} to path {}: {}",&line_id, &file_path,e);
});
Ok(())
}
}
}