next_web_ai/observation/
observation.rs1use std::collections::VecDeque;
2
3use next_web_core::{async_trait, convert::into_box::IntoBox, error::BoxError, DynClone};
4
5use crate::{
6 chat::observation::observation_convention::ObservationConvention,
7 observation::{noop_observation::NoopObservation, simple_event::SimpleEvent},
8 util::{key_value::KeyValue, key_values::KeyValues},
9};
10
11use super::{
12 observation_documentation::BoxObservationConvention, observation_registry::ObservationRegistry,
13 simple_observation::SimpleObservation,
14};
15
16pub trait Observation: Send + Sync {
18 fn start(&mut self);
19
20 fn stop(&mut self);
21
22 fn context(&mut self) -> &mut dyn Context;
23
24 fn contextual_name(&mut self, contextual_name: &str);
25
26 fn parent_observation(&mut self, parent_observation: Box<dyn Observation>);
27
28 fn low_cardinality_key_value(&mut self, key_value: Box<dyn KeyValue>);
29
30 fn high_cardinality_key_value(&mut self, key_value: Box<dyn KeyValue>);
31
32 fn observation_convention(&mut self, observation_convention: BoxObservationConvention);
33
34 fn error(&mut self, error: &BoxError);
35
36 fn event(&mut self, event: Box<dyn Event>);
37
38 fn open_scope(&self) -> Box<dyn Scope>;
39}
40
41#[async_trait]
42pub trait Observable {
43 async fn observe<R, 'a>(
44 &mut self,
45 run: impl std::future::Future<Output = Result<R, BoxError>> + Send + 'a,
46 ) -> Result<R, BoxError>;
47}
48
49#[async_trait]
50impl<T: Observation + ?Sized> Observable for T {
51 async fn observe<R, 'a>(
52 &mut self,
53 run: impl std::future::Future<Output = Result<R, BoxError>> + Send + 'a,
54 ) -> Result<R, BoxError> {
55 self.start();
56 match run.await {
57 Ok(value) => {
58 self.stop();
59 Ok(value)
60 }
61 Err(e) => {
62 self.error(&e);
63 self.stop();
64
65 Err(e)
66 }
67 }
68 }
69}
70
71pub struct ObservationImpl;
72
73impl ObservationImpl {
74 pub fn create_not_started(
75 custom_convention: Option<BoxObservationConvention>,
76 default_convention: Option<BoxObservationConvention>,
77 context: impl Context + 'static,
78 registry: Option<Box<dyn ObservationRegistry>>,
79 ) -> Box<dyn Observation> {
80 if registry.is_none() || registry.as_ref().map(|s| s.is_noop()).unwrap_or(false) {
81 return Self::noop().into_boxed();
82 }
83
84 let registry = registry.unwrap();
85
86 let mut context = Box::new(context);
87 let convention: Box<dyn ObservationConvention<Box<dyn Context>>>;
88 if let Some(custom_convention) = custom_convention {
89 convention = custom_convention;
90 } else {
91 convention = registry
92 .observation_config()
93 .observation_convention(context.as_ref(), default_convention)
94 .unwrap();
95 }
96
97 let is_observation_enabled = !registry
98 .observation_config()
99 .is_observation_enabled(convention.name().unwrap_or_default(), context.as_ref());
100
101 context.set_parent_from_current_observation(registry.as_ref());
102
103 if is_observation_enabled {
104 return Self::noop().into_boxed();
105 }
106
107 let convention = Some(convention);
108 SimpleObservation {
109 context,
110 registry,
111 convention,
112 handlers: VecDeque::new(),
113 filters: Vec::new(),
114 }
115 .into_boxed()
116 }
117
118 pub fn start(name: impl Into<String>, registry: Box<dyn ObservationRegistry>) {
119 return Self::create_not_started_from_name(name.into(), DefaultContext::new(), registry)
120 .start();
121 }
122
123 pub fn create_not_started_from_name(
124 name: impl Into<String>,
125 mut context: impl Context + 'static,
126 registry: Box<dyn ObservationRegistry>,
127 ) -> Box<dyn Observation> {
128 if registry.is_noop() {
129 return Self::noop().into_boxed();
130 }
131
132 let name = name.into();
133 let is_observation_enabled = !registry
134 .observation_config()
135 .is_observation_enabled(name.as_str(), &context);
136
137 context.set_parent_from_current_observation(registry.as_ref());
138
139 if is_observation_enabled {
140 return Self::noop().into_boxed();
141 }
142
143 SimpleObservation::new(name, registry, context).into_boxed()
144 }
145 pub fn noop() -> impl Observation {
146 NoopObservation::default()
147 }
148}
149
150pub trait Context: Send + Sync + DynClone {
151 fn set_parent_from_current_observation(&mut self, registry: &dyn ObservationRegistry);
152
153 fn add_low_cardinality_key_values(&mut self, key_values: KeyValues<Box<dyn KeyValue>>);
154
155 fn set_name(&mut self, name: &str);
156
157 fn set_contextual_name(&mut self, contextual_name: &str);
158}
159
160next_web_core::clone_trait_object!(Context);
161
162#[derive(Clone)]
163pub struct DefaultContext {}
164
165impl DefaultContext {
166 pub fn new() -> Self {
167 Self {}
168 }
169}
170impl Context for DefaultContext {
171 fn set_parent_from_current_observation(&mut self, registry: &dyn ObservationRegistry) {}
172
173 fn set_name(&mut self, name: &str) {}
174
175 fn add_low_cardinality_key_values(&mut self, key_values: KeyValues<Box<dyn KeyValue>>) {}
176
177 fn set_contextual_name(&mut self, contextual_name: &str) {}
178}
179
180pub trait Event: Send + Sync {
181 fn name(&self) -> &str;
182
183 fn wall_time(&self) -> u64 {
184 0
185 }
186
187 fn contextual_name(&self) -> &str {
188 self.name()
189 }
190}
191
192pub struct EventImpl;
193
194impl EventImpl {
195 pub fn of<T>(name: T, contextual_name: T) -> impl Event
196 where
197 T: Into<String>,
198 {
199 SimpleEvent::new(name, contextual_name)
200 }
201
202 pub fn of_name<T>(name: T) -> impl Event
203 where
204 T: Into<String>,
205 {
206 let name: String = name.into();
207 SimpleEvent::new(name.clone(), name)
208 }
209}
210
211pub trait Scope: Send + Sync {
212 fn current_observation(&self) -> Option<&dyn Observation>;
213
214 fn previous_observation_scope(&self) -> Option<&dyn Scope>;
215
216 fn close(&mut self);
217
218 fn reset(&mut self);
219
220 fn make_current(&mut self);
221
222 fn is_noop(&self) -> bool {
223 true
224 }
225}