1use std::{fmt::Debug, sync::Arc};
2
3use reqwest::Client;
4use serde_json::{json, Map, Value};
5use tokio::runtime::Handle;
6use tracing::{
7 field::{Field, Visit},
8 Event, Metadata, Subscriber,
9};
10use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer};
11
12pub struct AuditLayer {
13 client: Arc<Client>,
14 username: String,
15 password: String,
16 log_endpoint: String,
17 runtime_handle: Handle,
18}
19
20impl AuditLayer {
21 pub fn new(
24 log_endpoint: String,
25 username: String,
26 password: String,
27 runtime_handle: Handle,
28 ) -> Self {
29 let client = Arc::new(reqwest::Client::new());
30
31 Self {
32 client,
33 log_endpoint,
34 username,
35 password,
36 runtime_handle,
37 }
38 }
39}
40
41impl<S> Layer<S> for AuditLayer
42where
43 S: Subscriber + for<'a> LookupSpan<'a>,
44{
45 fn enabled(&self, _: &Metadata<'_>, _: Context<'_, S>) -> bool {
46 true }
48
49 fn on_event(&self, event: &Event<'_>, _: Context<'_, S>) {
50 let mut visitor = AuditVisitor::default();
51 event.record(&mut visitor);
52
53 if visitor.audit {
54 visitor
55 .json
56 .insert("message".to_owned(), json!(visitor.message));
57
58 let req = self
59 .client
60 .post(&self.log_endpoint)
61 .basic_auth(&self.username, Some(&self.password))
62 .json(&visitor.json);
63
64 self.runtime_handle.spawn(async move {
65 match req.send().await {
66 Ok(r) => {
67 if let Err(e) = r.error_for_status() {
68 println!("{e}")
69 }
70 }
71 Err(e) => eprintln!("Failed to send audit event: {}", e),
72 }
73 });
74 }
75 }
76}
77
78#[derive(Debug, Default)]
79struct AuditVisitor {
80 message: String,
81 json: Map<String, Value>,
82 audit: bool,
83}
84
85impl Visit for AuditVisitor {
86 fn record_bool(&mut self, field: &Field, value: bool) {
87 if field.name() == "audit" {
88 self.audit = value;
89 } else {
90 self.json.insert(field.name().to_owned(), json!(value));
91 }
92 }
93
94 fn record_str(&mut self, field: &Field, value: &str) {
95 if field.name() == "message" {
96 self.message = value.to_owned();
97 } else {
98 self.json.insert(field.name().to_owned(), json!(value));
99 }
100 }
101
102 fn record_f64(&mut self, field: &Field, value: f64) {
103 self.json.insert(field.name().to_owned(), json!(value));
104 }
105
106 fn record_i64(&mut self, field: &Field, value: i64) {
107 self.json.insert(field.name().to_owned(), json!(value));
108 }
109
110 fn record_u64(&mut self, field: &Field, value: u64) {
111 self.json.insert(field.name().to_owned(), json!(value));
112 }
113
114 fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
115 if field.name() == "message" {
116 self.message = format!("{value:?}");
117 } else {
118 self.json
119 .insert(field.name().to_owned(), json!(format!("{value:?}")));
120 }
121 }
122}