use std::sync::{Arc, Weak};
use super::{LogLevel, LogStrategy};
use crate::log::{
BaseLogEntry, LogType,
loggable_fn::LoggableFn,
stdout_logger::{StdOutLogger, StdOutLoggerMode},
};
use uuid7::Uuid;
pub(crate) trait LogWriter {
fn log_any<T: Loggable>(&self, entry: T);
fn log_fn<F, R>(&self, log_fn: LoggableFn<F>)
where
R: Loggable,
F: Fn(BaseLogEntry) -> R;
}
impl LogWriter for Option<Arc<LogStrategy>> {
fn log_any<T: Loggable>(&self, entry: T) {
if let Some(logger) = self.as_ref() {
logger.log_any(entry);
}
}
fn log_fn<F, R>(&self, log_fn: LoggableFn<F>)
where
R: Loggable,
F: Fn(BaseLogEntry) -> R,
{
if let Some(logger) = self.as_ref() {
logger.log_fn(log_fn);
}
}
}
impl LogWriter for Option<&Arc<LogStrategy>> {
fn log_any<T: Loggable>(&self, entry: T) {
if let Some(logger) = self.as_ref() {
logger.log_any(entry);
}
}
fn log_fn<F, R>(&self, log_fn: LoggableFn<F>)
where
R: Loggable,
F: Fn(BaseLogEntry) -> R,
{
if let Some(logger) = self.as_ref() {
logger.log_fn(log_fn);
}
}
}
impl LogWriter for Option<Weak<LogStrategy>> {
fn log_any<T: Loggable>(&self, entry: T) {
if let Some(log_strategy) = self.as_ref().and_then(std::sync::Weak::upgrade) {
log_strategy.as_ref().log_any(entry);
return;
}
StdOutLogger::new(LogLevel::INFO, StdOutLoggerMode::Immediate).log_any(entry);
}
fn log_fn<F, R>(&self, log_fn: LoggableFn<F>)
where
R: Loggable,
F: Fn(BaseLogEntry) -> R,
{
if let Some(log_strategy) = self.as_ref().and_then(std::sync::Weak::upgrade) {
log_strategy.as_ref().log_fn(log_fn);
return;
}
let entry = log_fn.build();
StdOutLogger::new(LogLevel::INFO, StdOutLoggerMode::Immediate).log_any(entry);
}
}
const SEPARATOR: &str = "__";
pub(crate) fn composite_key(id: &str, tag: &str) -> String {
[id, tag].join(SEPARATOR)
}
pub(crate) trait Indexed {
fn get_id(&self) -> Uuid;
fn get_additional_ids(&self) -> Vec<Uuid>;
fn get_tags(&self) -> Vec<&str>;
fn get_index_keys(&self) -> Vec<String> {
let tags = self.get_tags();
let additional_ids = self
.get_additional_ids()
.into_iter()
.map(|v| v.to_string())
.collect::<Vec<String>>();
let additional_id_and_tag = additional_ids
.iter()
.flat_map(|id| tags.iter().map(move |tag| composite_key(id, tag)))
.collect::<Vec<String>>();
let tags_iter = tags
.into_iter()
.map(Into::<String>::into)
.collect::<Vec<String>>();
let mut result = Vec::with_capacity(
additional_ids.len() + additional_id_and_tag.len() + tags_iter.len(),
);
result.extend(additional_ids);
result.extend(additional_id_and_tag);
result.extend(tags_iter);
result
}
}
pub(crate) trait Loggable:
serde::Serialize + Indexed + Clone + Send + Sync + Sized + 'static
{
fn get_log_level(&self) -> Option<LogLevel>;
fn get_log_kind(&self) -> Option<LogType>;
fn can_log(&self, logger_level: LogLevel) -> bool {
can_log(self.get_log_level(), logger_level)
}
}
pub(super) fn can_log(entity_level: Option<LogLevel>, logger_level: LogLevel) -> bool {
if let Some(entry_log_level) = entity_level {
logger_level <= entry_log_level
} else {
true
}
}
pub trait LogStorage {
fn pop_logs(&self) -> Vec<serde_json::Value>;
fn get_log_by_id(&self, id: &str) -> Option<serde_json::Value>;
fn get_log_ids(&self) -> Vec<String>;
fn get_logs_by_tag(&self, tag: &str) -> Vec<serde_json::Value>;
fn get_logs_by_request_id(&self, request_id: &str) -> Vec<serde_json::Value>;
fn get_logs_by_request_id_and_tag(&self, request_id: &str, tag: &str)
-> Vec<serde_json::Value>;
}