-
Notifications
You must be signed in to change notification settings - Fork 2.2k
chore(observability): emit component_sent events by source and service
#17549
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
796def9
b205354
d0c36ed
ec666b0
3e06791
3a5fe02
15ea497
d66654c
d1c7df6
c895653
d28a809
4ade9be
3632fa2
4d9c5df
c9640d9
a758704
5cbfee4
9eab386
ee50bf5
9fd7854
01d8ad1
87ca474
ebd10c8
b30a4bb
8b74470
b11c7a6
3327904
7a43045
f43af1b
b141f30
8ac7020
243ec8b
574cbae
73131e6
4dee27c
f1af663
8f0cc1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,13 +6,19 @@ mod events_sent; | |
| mod prelude; | ||
| pub mod service; | ||
|
|
||
| use std::{ | ||
| collections::BTreeMap, | ||
| ops::{Add, AddAssign}, | ||
| sync::{Arc, RwLock}, | ||
| }; | ||
|
|
||
| pub use metrics::SharedString; | ||
|
|
||
| pub use bytes_received::BytesReceived; | ||
| pub use bytes_sent::BytesSent; | ||
| pub use component_events_dropped::{ComponentEventsDropped, INTENTIONAL, UNINTENTIONAL}; | ||
| pub use events_received::EventsReceived; | ||
| pub use events_sent::{EventsSent, DEFAULT_OUTPUT}; | ||
| pub use events_sent::{EventsSent, TaggedEventsSent, DEFAULT_OUTPUT}; | ||
| pub use prelude::{error_stage, error_type}; | ||
| pub use service::{CallError, PollReadyError}; | ||
|
|
||
|
|
@@ -107,9 +113,24 @@ pub struct ByteSize(pub usize); | |
| pub struct Count(pub usize); | ||
|
|
||
| /// Holds the tuple `(count_of_events, size_of_events_in_bytes)`. | ||
| #[derive(Clone, Copy)] | ||
| #[derive(Clone, Copy, Debug)] | ||
| pub struct CountByteSize(pub usize, pub usize); | ||
|
|
||
| impl AddAssign for CountByteSize { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commenting here because the struct name didn't actually change in this PR, but: it's kind of unfortunate we never changed this to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels like an easy enough find and replace fix? |
||
| fn add_assign(&mut self, rhs: Self) { | ||
| self.0 += rhs.0; | ||
| self.1 += rhs.1; | ||
| } | ||
| } | ||
|
|
||
| impl Add<CountByteSize> for CountByteSize { | ||
| type Output = CountByteSize; | ||
|
|
||
| fn add(self, rhs: CountByteSize) -> Self::Output { | ||
| CountByteSize(self.0 + rhs.0, self.1 + rhs.1) | ||
| } | ||
| } | ||
|
|
||
| // Wrapper types used to hold parameters for registering events | ||
|
|
||
| pub struct Output(pub Option<SharedString>); | ||
|
|
@@ -224,3 +245,38 @@ macro_rules! registered_event { | |
| } | ||
| }; | ||
| } | ||
|
|
||
| #[derive(Clone)] | ||
| pub struct Cached<Tags, Event, Register> { | ||
| cache: Arc<RwLock<BTreeMap<Tags, Event>>>, | ||
| register: Register, | ||
| } | ||
|
|
||
| impl<Tags, Event, Register, Data> Cached<Tags, Event, Register> | ||
| where | ||
| Data: Sized, | ||
| Register: Fn(&Tags) -> Event, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This register function should likely be co-located with the event and not the cache. Could it be made a trait on the event type, potentially generated by a macro (maybe the same |
||
| Event: InternalEventHandle<Data = Data>, | ||
| Tags: Ord + Clone, | ||
| { | ||
| pub fn new(register: Register) -> Self { | ||
| Self { | ||
| cache: Arc::new(RwLock::new(BTreeMap::new())), | ||
| register, | ||
| } | ||
| } | ||
|
|
||
| pub fn emit(&self, tags: &Tags, value: Data) { | ||
| let read = self.cache.read().unwrap(); | ||
| if let Some(event) = read.get(tags) { | ||
| event.emit(value); | ||
| } else { | ||
| let event = (self.register)(tags); | ||
| event.emit(value); | ||
|
|
||
| // Ensure the read lock is dropped so we can write. | ||
| drop(read); | ||
| self.cache.write().unwrap().insert(tags.clone(), event); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,79 @@ | ||
| use std::collections::HashMap; | ||
| use std::ops::Add; | ||
|
|
||
| use crate::internal_event::CountByteSize; | ||
|
|
||
| pub type EventCountTags = (Option<String>, Option<String>); | ||
|
|
||
| pub trait GetEventCountTags { | ||
| fn get_tags(&self) -> EventCountTags; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice to see some doc text on what these are about. Also, could the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes absolutely. I'll update the PR description to indicate what I still thinks need doing. |
||
| } | ||
|
|
||
| /// Struct that keeps track of the estimated json size of a given | ||
| /// batch of events by source and service. | ||
| #[derive(Clone, Debug, Default)] | ||
| pub struct RequestCountByteSize { | ||
| sizes: HashMap<EventCountTags, CountByteSize>, | ||
| } | ||
|
|
||
| impl RequestCountByteSize { | ||
| pub fn sizes(&self) -> &HashMap<EventCountTags, CountByteSize> { | ||
| &self.sizes | ||
| } | ||
|
|
||
| pub fn add_event<E>(&mut self, event: &E, json_size: usize) | ||
| where | ||
| E: GetEventCountTags, | ||
| { | ||
| let size = CountByteSize(1, json_size); | ||
| let tags = event.get_tags(); | ||
|
|
||
| match self.sizes.get_mut(&tags) { | ||
| Some(current) => { | ||
| *current += size; | ||
| } | ||
| None => { | ||
| self.sizes.insert(tags, size); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<CountByteSize> for RequestCountByteSize { | ||
| fn from(value: CountByteSize) -> Self { | ||
| let mut sizes = HashMap::new(); | ||
| sizes.insert((None, None), value); | ||
|
|
||
| Self { sizes } | ||
| } | ||
| } | ||
|
|
||
| impl<'a> Add<&'a RequestCountByteSize> for RequestCountByteSize { | ||
| type Output = RequestCountByteSize; | ||
|
|
||
| fn add(mut self, other: &'a Self::Output) -> Self::Output { | ||
| for (key, value) in &other.sizes { | ||
| match self.sizes.get_mut(&key) { | ||
| Some(size) => *size += *value, | ||
| None => { | ||
| self.sizes.insert(key.clone(), *value); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Self { sizes: self.sizes } | ||
| } | ||
| } | ||
|
|
||
| /// Metadata for batch requests. | ||
| #[derive(Clone, Copy, Debug, Default)] | ||
| #[derive(Clone, Debug, Default)] | ||
| pub struct RequestMetadata { | ||
| /// Number of events represented by this batch request. | ||
| event_count: usize, | ||
| /// Size, in bytes, of the in-memory representation of all events in this batch request. | ||
| events_byte_size: usize, | ||
| /// Size, in bytes, of the estimated JSON-encoded representation of all events in this batch request. | ||
| events_estimated_json_encoded_byte_size: usize, | ||
| events_estimated_json_encoded_byte_size: RequestCountByteSize, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It feels a little weird that we have this field which might be grouped, might not, while the others are never grouped. Not really sure, as I type this comment, what a better solution would be but it does feel like it adds on more complexity to what was already (admittedly, given that I designed it) an already somewhat opaque type.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it is unfortunate primarily because this struct can no longer be
A very elusive bug could creep in. I'm all ears for a better solution. |
||
| /// Uncompressed size, in bytes, of the encoded events in this batch request. | ||
| request_encoded_size: usize, | ||
| /// On-the-wire size, in bytes, of the batch request itself after compression, etc. | ||
|
|
@@ -25,7 +90,7 @@ impl RequestMetadata { | |
| events_byte_size: usize, | ||
| request_encoded_size: usize, | ||
| request_wire_size: usize, | ||
| events_estimated_json_encoded_byte_size: usize, | ||
| events_estimated_json_encoded_byte_size: RequestCountByteSize, | ||
| ) -> Self { | ||
| Self { | ||
| event_count, | ||
|
|
@@ -47,8 +112,8 @@ impl RequestMetadata { | |
| } | ||
|
|
||
| #[must_use] | ||
| pub const fn events_estimated_json_encoded_byte_size(&self) -> usize { | ||
| self.events_estimated_json_encoded_byte_size | ||
| pub fn events_estimated_json_encoded_byte_size(&self) -> &RequestCountByteSize { | ||
| &self.events_estimated_json_encoded_byte_size | ||
| } | ||
|
|
||
| #[must_use] | ||
|
|
@@ -64,7 +129,7 @@ impl RequestMetadata { | |
| /// Constructs a `RequestMetadata` by summation of the "batch" of `RequestMetadata` provided. | ||
| #[must_use] | ||
| pub fn from_batch<T: IntoIterator<Item = RequestMetadata>>(metadata_iter: T) -> Self { | ||
| let mut metadata_sum = RequestMetadata::new(0, 0, 0, 0, 0); | ||
| let mut metadata_sum = RequestMetadata::new(0, 0, 0, 0, Default::default()); | ||
|
|
||
| for metadata in metadata_iter { | ||
| metadata_sum = metadata_sum + &metadata; | ||
|
|
@@ -82,7 +147,7 @@ impl<'a> Add<&'a RequestMetadata> for RequestMetadata { | |
| event_count: self.event_count + other.event_count, | ||
| events_byte_size: self.events_byte_size + other.events_byte_size, | ||
| events_estimated_json_encoded_byte_size: self.events_estimated_json_encoded_byte_size | ||
| + other.events_estimated_json_encoded_byte_size, | ||
| + &other.events_estimated_json_encoded_byte_size, | ||
| request_encoded_size: self.request_encoded_size + other.request_encoded_size, | ||
| request_wire_size: self.request_wire_size + other.request_wire_size, | ||
| } | ||
|
|
@@ -92,5 +157,5 @@ impl<'a> Add<&'a RequestMetadata> for RequestMetadata { | |
| /// Objects implementing this trait have metadata that describes the request. | ||
| pub trait MetaDescriptive { | ||
| /// Returns the `RequestMetadata` associated with this object. | ||
| fn get_metadata(&self) -> RequestMetadata; | ||
| fn get_metadata(&self) -> &RequestMetadata; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,10 @@ use std::{ | |
| }; | ||
|
|
||
| use chrono::{DateTime, Utc}; | ||
| use vector_common::EventDataEq; | ||
| use vector_common::{ | ||
| request_metadata::{EventCountTags, GetEventCountTags}, | ||
| EventDataEq, | ||
| }; | ||
| use vector_config::configurable_component; | ||
|
|
||
| use crate::{ | ||
|
|
@@ -476,6 +479,15 @@ impl Finalizable for Metric { | |
| } | ||
| } | ||
|
|
||
| impl GetEventCountTags for Metric { | ||
| fn get_tags(&self) -> EventCountTags { | ||
| let source = self.metadata().source_id().map(|output| output.to_string()); | ||
| // Metrics do not contain a service field. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this an intentional choice? I don't see a real-life reason why metrics couldn't be associated with a service.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently only Log events can have schemas. Once we expand it so that a meaning can point to a metric tag then we will be able to extract the service here.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just want to double check that we will be emitting the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I didn't consider the |
||
|
|
||
| (source, None) | ||
| } | ||
| } | ||
|
|
||
| /// Metric kind. | ||
| /// | ||
| /// Metrics can be either absolute of incremental. Absolute metrics represent a sort of "last write wins" scenario, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's too bad the macro doesn't let you make one call to
make_tagsand reuse it for both of these counters. Good thing it's just a setup function.