next_web_ai/observation/
observation.rs

1use 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
16// #[async_trait]
17pub 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}