mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-28 09:08:58 +00:00
feat(logging): add Kafka and Webhook audit targets
- Implemented `KafkaAuditTarget` to send audit entries to a Kafka topic. - Implemented `WebhookAuditTarget` to send audit entries to a specified webhook URL. - Updated `AuditLogger` to support multiple audit targets including Kafka and Webhook. - Added examples and documentation for the new audit targets.
This commit is contained in:
@@ -9,6 +9,11 @@ version.workspace = true
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
audit-kafka = ["dep:rdkafka", "dep:serde_json"]
|
||||
audit-webhook = ["dep:reqwest"]
|
||||
|
||||
[dependencies]
|
||||
opentelemetry = { workspace = true }
|
||||
opentelemetry_sdk = { workspace = true, features = ["rt-tokio"] }
|
||||
@@ -20,6 +25,9 @@ tracing = { workspace = true }
|
||||
tracing-opentelemetry = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, features = ["fmt", "env-filter", "tracing-log", "time", "local-time", "json"] }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
rdkafka = { workspace = true, features = ["tokio"], optional = true }
|
||||
reqwest = { workspace = true, features = ["json"], optional = true }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
#[cfg(feature = "audit-kafka")]
|
||||
use rdkafka::{
|
||||
producer::{FutureProducer, FutureRecord},
|
||||
ClientConfig,
|
||||
};
|
||||
|
||||
#[cfg(feature = "audit-webhook")]
|
||||
use reqwest::Client;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@@ -72,6 +81,80 @@ impl AuditTarget for FileAuditTarget {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "audit-webhook")]
|
||||
/// Webhook audit objectives
|
||||
pub struct WebhookAuditTarget {
|
||||
client: Client,
|
||||
url: String,
|
||||
}
|
||||
|
||||
#[cfg(feature = "audit-webhook")]
|
||||
impl WebhookAuditTarget {
|
||||
pub fn new(url: &str) -> Self {
|
||||
Self {
|
||||
client: Client::new(),
|
||||
url: url.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "audit-webhook")]
|
||||
impl AuditTarget for WebhookAuditTarget {
|
||||
fn send(&self, entry: AuditEntry) {
|
||||
let client = self.client.clone();
|
||||
let url = self.url.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = client.post(&url).json(&entry).send().await {
|
||||
eprintln!("Failed to send to Webhook: {:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "audit-kafka")]
|
||||
/// Kafka audit objectives
|
||||
pub struct KafkaAuditTarget {
|
||||
producer: FutureProducer,
|
||||
topic: String,
|
||||
}
|
||||
|
||||
#[cfg(feature = "audit-kafka")]
|
||||
impl KafkaAuditTarget {
|
||||
pub fn new(brokers: &str, topic: &str) -> Self {
|
||||
let producer: FutureProducer = ClientConfig::new()
|
||||
.set("bootstrap.servers", brokers)
|
||||
.set("message.timeout.ms", "5000")
|
||||
.create()
|
||||
.expect("Kafka producer creation failed");
|
||||
Self {
|
||||
producer,
|
||||
topic: topic.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "audit-kafka")]
|
||||
impl AuditTarget for KafkaAuditTarget {
|
||||
fn send(&self, entry: AuditEntry) {
|
||||
let topic = self.topic.clone();
|
||||
let span_id = entry.span_id.clone();
|
||||
let payload = serde_json::to_string(&entry).unwrap();
|
||||
// let record = FutureRecord::to(&topic).payload(&payload).key(&span_id);
|
||||
tokio::spawn({
|
||||
// 在异步闭包内部创建 record
|
||||
let topic = topic;
|
||||
let payload = payload;
|
||||
let span_id = span_id;
|
||||
let producer = self.producer.clone();
|
||||
async move {
|
||||
let record = FutureRecord::to(&topic).payload(&payload).key(&span_id);
|
||||
if let Err(e) = producer.send(record, std::time::Duration::from_secs(0)).await {
|
||||
eprintln!("Failed to send to Kafka: {:?}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/// AuditLogger is a logger that logs audit entries
|
||||
/// to multiple targets
|
||||
///
|
||||
@@ -110,6 +193,7 @@ impl AuditLogger {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rustfs_logging::{AuditLogger, AuditEntry, FileAuditTarget};
|
||||
///
|
||||
/// let logger = AuditLogger::new(vec![Box::new(FileAuditTarget)]);
|
||||
/// ```
|
||||
pub fn new(targets: Vec<Box<dyn AuditTarget>>) -> Self {
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
/// log_info("This is an informational message");
|
||||
/// log_error("This is an error message");
|
||||
/// ```
|
||||
#[cfg(feature = "audit-kafka")]
|
||||
pub use audit::KafkaAuditTarget;
|
||||
#[cfg(feature = "audit-webhook")]
|
||||
pub use audit::WebhookAuditTarget;
|
||||
pub use audit::{AuditEntry, AuditLogger, AuditTarget, FileAuditTarget};
|
||||
pub use logger::{log_debug, log_error, log_info};
|
||||
pub use telemetry::Telemetry;
|
||||
@@ -66,11 +70,17 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
// #[cfg(feature = "audit-webhook")]
|
||||
// #[cfg(feature = "audit-kafka")]
|
||||
async fn test_main() {
|
||||
let telemetry = Telemetry::init();
|
||||
|
||||
// Initialize multiple audit objectives
|
||||
let audit_targets: Vec<Box<dyn AuditTarget>> = vec![Box::new(FileAuditTarget)];
|
||||
let audit_targets: Vec<Box<dyn AuditTarget>> = vec![
|
||||
Box::new(FileAuditTarget),
|
||||
// Box::new(KafkaAuditTarget::new("localhost:9092", "rustfs-audit")),
|
||||
// Box::new(WebhookAuditTarget::new("http://localhost:8080/audit")),
|
||||
];
|
||||
let audit_logger = AuditLogger::new(audit_targets);
|
||||
|
||||
// Test the PUT operation
|
||||
|
||||
Reference in New Issue
Block a user