From bcdd204fa0ff4ec7e7809fc76882b82ccb0fc01f Mon Sep 17 00:00:00 2001 From: houseme Date: Mon, 17 Mar 2025 21:37:47 +0800 Subject: [PATCH] improve log struct --- packages/obs/examples/config.toml | 2 +- packages/obs/src/entry/audit.rs | 382 +++++++++++++++++++++++++++-- packages/obs/src/entry/base.rs | 390 +++++++++++++++++++++++++++++- packages/obs/src/entry/mod.rs | 2 +- packages/obs/src/lib.rs | 2 + 5 files changed, 756 insertions(+), 22 deletions(-) diff --git a/packages/obs/examples/config.toml b/packages/obs/examples/config.toml index 2ceac439..ddf37635 100644 --- a/packages/obs/examples/config.toml +++ b/packages/obs/examples/config.toml @@ -1,6 +1,6 @@ [observability] endpoint = "http://localhost:4317" -use_stdout = true +use_stdout = false sample_ratio = 0.5 meter_interval = 30 service_name = "rustfs_obs_service" diff --git a/packages/obs/src/entry/audit.rs b/packages/obs/src/entry/audit.rs index 4e509985..8f8b1e34 100644 --- a/packages/obs/src/entry/audit.rs +++ b/packages/obs/src/entry/audit.rs @@ -1,19 +1,11 @@ +use crate::ObjectVersion; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; -/// ObjectVersion object version key/versionId -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ObjectVersion { - #[serde(rename = "objectName")] - pub object_name: String, - #[serde(rename = "versionId", skip_serializing_if = "Option::is_none")] - pub version_id: Option, -} - /// API details structure -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct ApiDetails { #[serde(rename = "name", skip_serializing_if = "Option::is_none")] pub name: Option, @@ -43,8 +35,107 @@ pub struct ApiDetails { pub time_to_response_in_ns: Option, } +impl ApiDetails { + /// Create a new `ApiDetails` with default values + pub fn new() -> Self { + ApiDetails { + name: None, + bucket: None, + object: None, + objects: Vec::new(), + status: None, + status_code: None, + input_bytes: 0, + output_bytes: 0, + header_bytes: None, + time_to_first_byte: None, + time_to_first_byte_in_ns: None, + time_to_response: None, + time_to_response_in_ns: None, + } + } + + /// Set the name + pub fn set_name(mut self, name: Option) -> Self { + self.name = name; + self + } + + /// Set the bucket + pub fn set_bucket(mut self, bucket: Option) -> Self { + self.bucket = bucket; + self + } + + /// Set the object + pub fn set_object(mut self, object: Option) -> Self { + self.object = object; + self + } + + /// Set the objects + pub fn set_objects(mut self, objects: Vec) -> Self { + self.objects = objects; + self + } + + /// Set the status + pub fn set_status(mut self, status: Option) -> Self { + self.status = status; + self + } + + /// Set the status code + pub fn set_status_code(mut self, status_code: Option) -> Self { + self.status_code = status_code; + self + } + + /// Set the input bytes + pub fn set_input_bytes(mut self, input_bytes: i64) -> Self { + self.input_bytes = input_bytes; + self + } + + /// Set the output bytes + pub fn set_output_bytes(mut self, output_bytes: i64) -> Self { + self.output_bytes = output_bytes; + self + } + + /// Set the header bytes + pub fn set_header_bytes(mut self, header_bytes: Option) -> Self { + self.header_bytes = header_bytes; + self + } + + /// Set the time to first byte + pub fn set_time_to_first_byte(mut self, time_to_first_byte: Option) -> Self { + self.time_to_first_byte = time_to_first_byte; + self + } + + /// Set the time to first byte in nanoseconds + pub fn set_time_to_first_byte_in_ns(mut self, time_to_first_byte_in_ns: Option) -> Self { + self.time_to_first_byte_in_ns = time_to_first_byte_in_ns; + self + } + + /// Set the time to response + pub fn set_time_to_response(mut self, time_to_response: Option) -> Self { + self.time_to_response = time_to_response; + self + } + + /// Set the time to response in nanoseconds + pub fn set_time_to_response_in_ns(mut self, time_to_response_in_ns: Option) -> Self { + self.time_to_response_in_ns = time_to_response_in_ns; + self + } +} + /// Entry - audit entry logs -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct AuditEntry { pub version: String, #[serde(rename = "deploymentid", skip_serializing_if = "Option::is_none")] @@ -55,9 +146,6 @@ pub struct AuditEntry { // Class of audit message - S3, admin ops, bucket management #[serde(rename = "type", skip_serializing_if = "Option::is_none")] pub entry_type: Option, - - // Deprecated, replaced by 'Event' - pub trigger: String, pub api: ApiDetails, #[serde(rename = "remotehost", skip_serializing_if = "Option::is_none")] pub remote_host: Option, @@ -86,3 +174,269 @@ pub struct AuditEntry { #[serde(rename = "error", skip_serializing_if = "Option::is_none")] pub error: Option, } + +impl AuditEntry { + /// Create a new `AuditEntry` with default values + pub fn new() -> Self { + AuditEntry { + version: String::new(), + deployment_id: None, + time: Utc::now(), + event: String::new(), + entry_type: None, + api: ApiDetails::new(), + remote_host: None, + request_id: None, + user_agent: None, + req_path: None, + req_host: None, + req_claims: None, + req_query: None, + req_header: None, + resp_header: None, + tags: None, + access_key: None, + parent_user: None, + error: None, + } + } + + /// Create a new `AuditEntry` with version and time event and api details + /// # Arguments + /// * `version` - Version of the audit entry + /// * `time` - Time of the audit entry + /// * `event` - Event of the audit entry + /// * `api` - API details of the audit entry + /// # Returns + /// * `AuditEntry` with the given values + /// # Example + /// ``` + /// use chrono::Utc; + /// use rustfs_obs::{ApiDetails, AuditEntry}; + /// let entry = AuditEntry::new_with_values( + /// "v1".to_string(), + /// Utc::now(), + /// "event".to_string(), + /// ApiDetails::new(), + /// ); + /// ``` + /// # Remarks + /// This is a convenience method to create an `AuditEntry` with the given values + /// without having to set each field individually + /// This is useful when you want to create an `AuditEntry` with the given values + /// without having to set each field individually + pub fn new_with_values(version: String, time: DateTime, event: String, api: ApiDetails) -> Self { + AuditEntry { + version, + deployment_id: None, + time, + event, + entry_type: None, + api, + remote_host: None, + request_id: None, + user_agent: None, + req_path: None, + req_host: None, + req_claims: None, + req_query: None, + req_header: None, + resp_header: None, + tags: None, + access_key: None, + parent_user: None, + error: None, + } + } + + /// Set the version + pub fn set_version(mut self, version: String) -> Self { + self.version = version; + self + } + + /// Set the deployment ID + pub fn set_deployment_id(mut self, deployment_id: Option) -> Self { + self.deployment_id = deployment_id; + self + } + + /// Set the time + pub fn set_time(mut self, time: DateTime) -> Self { + self.time = time; + self + } + + /// Set the event + pub fn set_event(mut self, event: String) -> Self { + self.event = event; + self + } + + /// Set the entry type + pub fn set_entry_type(mut self, entry_type: Option) -> Self { + self.entry_type = entry_type; + self + } + + /// Set the API details + pub fn set_api(mut self, api: ApiDetails) -> Self { + self.api = api; + self + } + + /// Set the remote host + pub fn set_remote_host(mut self, remote_host: Option) -> Self { + self.remote_host = remote_host; + self + } + + /// Set the request ID + pub fn set_request_id(mut self, request_id: Option) -> Self { + self.request_id = request_id; + self + } + + /// Set the user agent + pub fn set_user_agent(mut self, user_agent: Option) -> Self { + self.user_agent = user_agent; + self + } + + /// Set the request path + pub fn set_req_path(mut self, req_path: Option) -> Self { + self.req_path = req_path; + self + } + + /// Set the request host + pub fn set_req_host(mut self, req_host: Option) -> Self { + self.req_host = req_host; + self + } + + /// Set the request claims + pub fn set_req_claims(mut self, req_claims: Option>) -> Self { + self.req_claims = req_claims; + self + } + + /// Set the request query + pub fn set_req_query(mut self, req_query: Option>) -> Self { + self.req_query = req_query; + self + } + + /// Set the request header + pub fn set_req_header(mut self, req_header: Option>) -> Self { + self.req_header = req_header; + self + } + + /// Set the response header + pub fn set_resp_header(mut self, resp_header: Option>) -> Self { + self.resp_header = resp_header; + self + } + + /// Set the tags + pub fn set_tags(mut self, tags: Option>) -> Self { + self.tags = tags; + self + } + + /// Set the access key + pub fn set_access_key(mut self, access_key: Option) -> Self { + self.access_key = access_key; + self + } + + /// Set the parent user + pub fn set_parent_user(mut self, parent_user: Option) -> Self { + self.parent_user = parent_user; + self + } + + /// Set the error + pub fn set_error(mut self, error: Option) -> Self { + self.error = error; + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_audit_entry() { + let entry = AuditEntry::new() + .set_version("v1".to_string()) + .set_deployment_id(Some("12345".to_string())) + .set_time(Utc::now()) + .set_event("event".to_string()) + .set_entry_type(Some("type".to_string())) + .set_api(ApiDetails::new()) + .set_remote_host(Some("localhost".to_string())) + .set_request_id(Some("req-12345".to_string())) + .set_user_agent(Some("user-agent".to_string())) + .set_req_path(Some("/path".to_string())) + .set_req_host(Some("localhost".to_string())) + .set_req_claims(Some(HashMap::new())) + .set_req_query(Some(HashMap::new())) + .set_req_header(Some(HashMap::new())) + .set_resp_header(Some(HashMap::new())) + .set_tags(Some(HashMap::new())) + .set_access_key(Some("access-key".to_string())) + .set_parent_user(Some("parent-user".to_string())) + .set_error(Some("error".to_string())); + + assert_eq!(entry.version, "v1"); + assert_eq!(entry.deployment_id, Some("12345".to_string())); + assert_eq!(entry.event, "event"); + assert_eq!(entry.entry_type, Some("type".to_string())); + assert_eq!(entry.remote_host, Some("localhost".to_string())); + assert_eq!(entry.request_id, Some("req-12345".to_string())); + assert_eq!(entry.user_agent, Some("user-agent".to_string())); + assert_eq!(entry.req_path, Some("/path".to_string())); + assert_eq!(entry.req_host, Some("localhost".to_string())); + assert_eq!(entry.access_key, Some("access-key".to_string())); + assert_eq!(entry.parent_user, Some("parent-user".to_string())); + assert_eq!(entry.error, Some("error".to_string())); + } + + #[test] + fn test_api_details() { + let api = ApiDetails::new() + .set_name(Some("name".to_string())) + .set_bucket(Some("bucket".to_string())) + .set_object(Some("object".to_string())) + .set_objects(vec![ObjectVersion { + object_name: "object".to_string(), + version_id: Some("12345".to_string()), + }]) + .set_status(Some("status".to_string())) + .set_status_code(Some(200)) + .set_input_bytes(100) + .set_output_bytes(200) + .set_header_bytes(Some(300)) + .set_time_to_first_byte(Some("100ms".to_string())) + .set_time_to_first_byte_in_ns(Some("100ns".to_string())) + .set_time_to_response(Some("200ms".to_string())) + .set_time_to_response_in_ns(Some("200ns".to_string())); + + assert_eq!(api.name, Some("name".to_string())); + assert_eq!(api.bucket, Some("bucket".to_string())); + assert_eq!(api.object, Some("object".to_string())); + assert_eq!(api.objects.len(), 1); + assert_eq!(api.status, Some("status".to_string())); + assert_eq!(api.status_code, Some(200)); + assert_eq!(api.input_bytes, 100); + assert_eq!(api.output_bytes, 200); + assert_eq!(api.header_bytes, Some(300)); + assert_eq!(api.time_to_first_byte, Some("100ms".to_string())); + assert_eq!(api.time_to_first_byte_in_ns, Some("100ns".to_string())); + assert_eq!(api.time_to_response, Some("200ms".to_string())); + assert_eq!(api.time_to_response_in_ns, Some("200ns".to_string())); + } +} diff --git a/packages/obs/src/entry/base.rs b/packages/obs/src/entry/base.rs index aa9d22ab..c629c74d 100644 --- a/packages/obs/src/entry/base.rs +++ b/packages/obs/src/entry/base.rs @@ -4,7 +4,7 @@ use serde_json::Value; use std::collections::HashMap; /// ObjectVersion object version key/versionId -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)] pub struct ObjectVersion { #[serde(rename = "objectName")] pub object_name: String, @@ -12,8 +12,37 @@ pub struct ObjectVersion { pub version_id: Option, } +impl ObjectVersion { + /// Create a new ObjectVersion object + pub fn new() -> Self { + ObjectVersion { + object_name: String::new(), + version_id: None, + } + } + + /// Create a new ObjectVersion object with object name + pub fn new_with_object_name(object_name: String) -> Self { + ObjectVersion { + object_name, + version_id: None, + } + } + /// Set the object name + pub fn set_object_name(mut self, object_name: String) -> Self { + self.object_name = object_name; + self + } + + /// Set the version ID + pub fn set_version_id(mut self, version_id: Option) -> Self { + self.version_id = version_id; + self + } +} + /// Args - defines the arguments for the API -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct Args { #[serde(rename = "bucket", skip_serializing_if = "Option::is_none")] pub bucket: Option, @@ -27,8 +56,51 @@ pub struct Args { pub metadata: Option>, } +impl Args { + /// Create a new Args object + pub fn new() -> Self { + Args { + bucket: None, + object: None, + version_id: None, + objects: None, + metadata: None, + } + } + + /// Set the bucket + pub fn set_bucket(mut self, bucket: Option) -> Self { + self.bucket = bucket; + self + } + + /// Set the object + pub fn set_object(mut self, object: Option) -> Self { + self.object = object; + self + } + + /// Set the version ID + pub fn set_version_id(mut self, version_id: Option) -> Self { + self.version_id = version_id; + self + } + + /// Set the objects + pub fn set_objects(mut self, objects: Option>) -> Self { + self.objects = objects; + self + } + + /// Set the metadata + pub fn set_metadata(mut self, metadata: Option>) -> Self { + self.metadata = metadata; + self + } +} + /// Trace - defines the trace -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct Trace { #[serde(rename = "message", skip_serializing_if = "Option::is_none")] pub message: Option, @@ -38,8 +110,37 @@ pub struct Trace { pub variables: Option>, } +impl Trace { + /// Create a new Trace object + pub fn new() -> Self { + Trace { + message: None, + source: None, + variables: None, + } + } + + /// Set the message + pub fn set_message(mut self, message: Option) -> Self { + self.message = message; + self + } + + /// Set the source + pub fn set_source(mut self, source: Option>) -> Self { + self.source = source; + self + } + + /// Set the variables + pub fn set_variables(mut self, variables: Option>) -> Self { + self.variables = variables; + self + } +} + /// API - defines the api type and its args -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct API { #[serde(rename = "name", skip_serializing_if = "Option::is_none")] pub name: Option, @@ -47,8 +148,27 @@ pub struct API { pub args: Option, } +impl API { + /// Create a new API object + pub fn new() -> Self { + API { name: None, args: None } + } + + /// Set the name + pub fn set_name(mut self, name: Option) -> Self { + self.name = name; + self + } + + /// Set the args + pub fn set_args(mut self, args: Option) -> Self { + self.args = args; + self + } +} + /// Log kind/level enum -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum LogKind { #[serde(rename = "INFO")] Info, @@ -60,8 +180,14 @@ pub enum LogKind { Fatal, } +impl Default for LogKind { + fn default() -> Self { + LogKind::Info + } +} + /// Entry - defines fields and values of each log entry -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct Entry { #[serde(rename = "site", skip_serializing_if = "Option::is_none")] pub site: Option, @@ -98,6 +224,98 @@ pub struct Entry { pub trace: Option, } +impl Entry { + /// Create a new Entry object with default values + pub fn new() -> Self { + Entry { + site: None, + deployment_id: None, + level: LogKind::Info, + log_kind: None, + time: Utc::now(), + api: None, + remote_host: None, + host: None, + request_id: None, + user_agent: None, + message: None, + trace: None, + } + } + + /// Set the site + pub fn set_site(mut self, site: Option) -> Self { + self.site = site; + self + } + + /// Set the deployment ID + pub fn set_deployment_id(mut self, deployment_id: Option) -> Self { + self.deployment_id = deployment_id; + self + } + + /// Set the level + pub fn set_level(mut self, level: LogKind) -> Self { + self.level = level; + self + } + + /// Set the log kind + pub fn set_log_kind(mut self, log_kind: Option) -> Self { + self.log_kind = log_kind; + self + } + + /// Set the time + pub fn set_time(mut self, time: DateTime) -> Self { + self.time = time; + self + } + + /// Set the API + pub fn set_api(mut self, api: Option) -> Self { + self.api = api; + self + } + + /// Set the remote host + pub fn set_remote_host(mut self, remote_host: Option) -> Self { + self.remote_host = remote_host; + self + } + + /// Set the host + pub fn set_host(mut self, host: Option) -> Self { + self.host = host; + self + } + + /// Set the request ID + pub fn set_request_id(mut self, request_id: Option) -> Self { + self.request_id = request_id; + self + } + + /// Set the user agent + pub fn set_user_agent(mut self, user_agent: Option) -> Self { + self.user_agent = user_agent; + self + } + + /// Set the message + pub fn set_message(mut self, message: Option) -> Self { + self.message = message; + self + } + + /// Set the trace + pub fn set_trace(mut self, trace: Option) -> Self { + self.trace = trace; + self + } +} + /// Info holds console log messages #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Info { @@ -112,3 +330,163 @@ pub struct Info { #[serde(skip)] pub err: Option, } + +impl Info { + /// Create a new Info object with default values + pub fn new() -> Self { + Info { + entry: Entry::new(), + console_msg: String::new(), + node_name: String::new(), + err: None, + } + } + + /// Create a new Info object with console message and node name + pub fn new_with_console_msg(console_msg: String, node_name: String) -> Self { + Info { + entry: Entry::new(), + console_msg, + node_name, + err: None, + } + } + + /// Set the node name + pub fn set_node_name(&mut self, node_name: String) { + self.node_name = node_name; + } + + /// Set the entry + pub fn set_entry(&mut self, entry: Entry) { + self.entry = entry; + } + + /// Set the console message + pub fn set_console_msg(&mut self, console_msg: String) { + self.console_msg = console_msg; + } + + /// Set the error message + pub fn set_err(&mut self, err: Option) { + self.err = err; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_object_version() { + let mut object_version = ObjectVersion::new(); + object_version = object_version.clone().set_object_name("object".to_string()); + object_version = object_version.clone().set_version_id(Some("version".to_string())); + assert_eq!(object_version.object_name, "object".to_string()); + assert_eq!(object_version.version_id, Some("version".to_string())); + } + + #[test] + fn test_object_version_with_object_name() { + let object_version = ObjectVersion::new_with_object_name("object".to_string()); + assert_eq!(object_version.object_name, "object".to_string()); + assert_eq!(object_version.version_id, None); + } + + #[test] + fn test_args() { + let mut obj = ObjectVersion::new(); + obj.object_name = "object".to_string(); + obj.version_id = Some("version".to_string()); + let objs = vec![obj]; + let args = Args::new() + .set_bucket(Some("bucket".to_string())) + .set_object(Some("object".to_string())) + .set_version_id(Some("version".to_string())) + .set_objects(Some(objs.clone())) + .set_metadata(Some(HashMap::new())); + + assert_eq!(args.bucket, Some("bucket".to_string())); + assert_eq!(args.object, Some("object".to_string())); + assert_eq!(args.version_id, Some("version".to_string())); + assert_eq!(args.objects, Some(objs)); + assert_eq!(args.metadata, Some(HashMap::new())); + } + + #[test] + fn test_trace() { + let trace = Trace::new() + .set_message(Some("message".to_string())) + .set_source(Some(vec!["source".to_string()])) + .set_variables(Some(HashMap::new())); + + assert_eq!(trace.message, Some("message".to_string())); + assert_eq!(trace.source, Some(vec!["source".to_string()])); + assert_eq!(trace.variables, Some(HashMap::new())); + } + + #[test] + fn test_api() { + let api = API::new().set_name(Some("name".to_string())).set_args(Some(Args::new())); + + assert_eq!(api.name, Some("name".to_string())); + assert_eq!(api.args, Some(Args::new())); + } + + #[test] + fn test_log_kind() { + assert_eq!(LogKind::default(), LogKind::Info); + } + + #[test] + fn test_entry() { + let entry = Entry::new() + .set_site(Some("site".to_string())) + .set_deployment_id(Some("deployment_id".to_string())) + .set_level(LogKind::Info) + .set_log_kind(Some(LogKind::Info)) + .set_time(Utc::now()) + .set_api(Some(API::new())) + .set_remote_host(Some("remote_host".to_string())) + .set_host(Some("host".to_string())) + .set_request_id(Some("request_id".to_string())) + .set_user_agent(Some("user_agent".to_string())) + .set_message(Some("message".to_string())) + .set_trace(Some(Trace::new())); + + assert_eq!(entry.site, Some("site".to_string())); + assert_eq!(entry.deployment_id, Some("deployment_id".to_string())); + assert_eq!(entry.level, LogKind::Info); + assert_eq!(entry.log_kind, Some(LogKind::Info)); + assert_eq!(entry.api, Some(API::new())); + assert_eq!(entry.remote_host, Some("remote_host".to_string())); + assert_eq!(entry.host, Some("host".to_string())); + assert_eq!(entry.request_id, Some("request_id".to_string())); + assert_eq!(entry.user_agent, Some("user_agent".to_string())); + assert_eq!(entry.message, Some("message".to_string())); + assert_eq!(entry.trace, Some(Trace::new())); + } + + #[test] + fn test_info() { + let mut info = Info::new(); + info.set_node_name("node_name".to_string()); + info.set_entry(Entry::new()); + info.set_console_msg("console_msg".to_string()); + info.set_err(Some("err".to_string())); + + assert_eq!(info.node_name, "node_name".to_string()); + // assert_eq!(info.entry, Entry::new()); + assert_eq!(info.console_msg, "console_msg".to_string()); + assert_eq!(info.err, Some("err".to_string())); + } + + #[test] + fn test_info_with_console_msg() { + let info = Info::new_with_console_msg("console_msg".to_string(), "node_name".to_string()); + + assert_eq!(info.node_name, "node_name".to_string()); + assert_eq!(info.console_msg, "console_msg".to_string()); + assert_eq!(info.err, None); + } +} diff --git a/packages/obs/src/entry/mod.rs b/packages/obs/src/entry/mod.rs index efc697e0..51ba1584 100644 --- a/packages/obs/src/entry/mod.rs +++ b/packages/obs/src/entry/mod.rs @@ -1,3 +1,3 @@ pub(crate) mod audit; -mod base; +pub(crate) mod base; pub(crate) mod log; diff --git a/packages/obs/src/lib.rs b/packages/obs/src/lib.rs index 079d9abf..8d00c7b0 100644 --- a/packages/obs/src/lib.rs +++ b/packages/obs/src/lib.rs @@ -13,6 +13,8 @@ mod worker; pub use config::load_config; pub use config::{AppConfig, OtelConfig}; +pub use entry::audit::{ApiDetails, AuditEntry}; +pub use entry::base::{Args, Entry, Info, LogKind, ObjectVersion, Trace, API}; pub use entry::log::{LogEntry, SerializableLevel}; pub use logger::start_logger; pub use logger::{