-
Notifications
You must be signed in to change notification settings - Fork 448
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
Feat/add resource builder #2322
base: main
Are you sure you want to change the base?
Feat/add resource builder #2322
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2322 +/- ##
=======================================
- Coverage 79.4% 79.4% -0.1%
=======================================
Files 123 124 +1
Lines 21485 21679 +194
=======================================
+ Hits 17068 17214 +146
- Misses 4417 4465 +48 ☔ View full report in Codecov by Sentry. |
fdd9fd1
to
b0bbed5
Compare
I still need to expose the new builder to the API, and would like feedback on #2324 before going further since the changes are related :) |
@pitoniak32 Could you split this into two PRs? Have one PR just for removing the |
split those changes into: #2332 |
f08a237
to
afb8ba4
Compare
} | ||
|
||
/// Add a [KeyValue] to the resource. | ||
pub fn with_key_value(self, kv: KeyValue) -> Self { |
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.
interesting why this does not need mut self
...
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 might be since the with_detectors consumes the builder and its not a &mut
reference? That's just a guess I'm actually not sure about that 😅
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.
with_key_value
doesn’t need mut self
because it doesn’t directly modify the builder; it delegates to with_key_values
, which takes mut self to perform the actual mutation.
I was also thinking of doing something like this for with_key_value, I'm wondering what others think? let resource = Resource::builder()
.with_key_value(KeyValue::new("test1", "test_value"))
.build();
// vs
let resource = Resource::builder()
.with_key_value("test1", "test_value")
.build(); /// Add a [KeyValue] to the resource.
pub fn with_key_value<K, V>(self, key: K, value: V) -> Self
where
K: Into<Key>,
V: Into<Value>,
{
self.with_key_values(vec![KeyValue::new(key, value)])
} currently its: /// Add a [KeyValue] to the resource.
pub fn with_key_value(self, kv: KeyValue) -> Self {
self.with_key_values(vec![kv])
} |
5976ef0
to
e501d4f
Compare
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.
Thank you for the contribution 😊
9fca93a
to
155f875
Compare
} | ||
|
||
/// Add multiple [KeyValue]s to the resource. | ||
pub fn with_key_values<T: IntoIterator<Item = KeyValue>>(mut self, kvs: T) -> Self { |
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.
should the method be named with_attribute
and with_attributes
- Just that the spec uses the term "attribute" consistently when describing resource data.
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.
+1
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.
fixed this
self.resource = self.resource.merge(&Resource::new(kvs)); | ||
self | ||
} | ||
|
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.
should there also be a method with_schema_url
to add schema url ?
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.
How would we like the behavior of the with_schema_url
to act? Do we want to follow the rules that from_schema_url
has? Or override the current one with the specified one, or maybe use TypeState pattern to only allow the schema_url to be set once?
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.
added this, would love to hear feedback on the implementation!
I vote for this, it's similar to what is followed for opentelemetry-rust/opentelemetry-sdk/src/logs/record.rs Lines 98 to 102 in a3c469b
|
|
||
impl ResourceBuilder { | ||
/// Create ResourceBuilder with [Resource::empty()]. | ||
pub fn new() -> Self { |
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.
is this needed, given Resource already expose public methods to create a ResourceBuilder?
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.
wondering what is the best option?
Should users have the ability to get Builder instance from Builder itself, or via the thing it is ultimately building or both? We need to review rest of the codebase to ensure we do this consistently across.
I think its best to offer just one way to create a builder, and that must be via a method on the thing we are ultimately building.
so Resource::builder().....build() - only this should be required.
and not ResourceBuilder::new()....build().
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.
the others are hiding the new method, I will update this, I agree with you.
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.
I think the reason that was public is to allow the creation of an empty resource vs a default resource. In this case would it make more sense to make it pub(super)
? so the resource is still able to use it? Or only allow starting from an empty resource, and then expose a with_default()
?
/// If you want to start from a [Resource::default()] see [Resource::builder_default()]. | ||
/// | ||
/// Starts with a [Resource::empty()]. | ||
pub fn builder() -> ResourceBuilder { |
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.
I am still debating about this.. It maybe better to name this builder_empty, to make it very obvious that this starts with an empty Resource, and not with OTel SDK defaults?
resource: Resource, | ||
} | ||
|
||
impl Default for ResourceBuilder { |
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.
we can remove the Default implementation for the builder, so as to force users to always go via Resource to get hold of a builder.
"service.name", | ||
"metrics-advanced-example", | ||
)])) | ||
.with_resource(resource) |
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.
- If user never called with_resource.- use SDK defaults.
- with_resource(Resource::builder().with..with...build()) - Start with SDK defaults, and merge the rest.
- with_resource(Resource::builder_empty().with..with...build()) - Start with empty, and merge the rest.
- provider.append_resource(..) - merges with existing Resource(s)
^ Does this look the right approach @open-telemetry/rust-approvers please share your thoughts, this is important to get closure soon.
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.
After some more thinking into this, I believe there mainly two use-cases here:
- Set resource to something very specific (remove default resources)
- Append more resources to the existing ones
I believe a simple solution here could be:
- Introduce an API named
set_resource(resource)
which will replace the provider's resource with the value in the method argument. This would cover the 1st use case. - Use the existing
with_resource(resource)
API (or rename it toappend_resource(resource)
to allow users to append/merge resources with the existing provider resource. The existing resource would either be the default ones or something else if the user calledset_resource(resource)
. This would cover the 2nd use case.
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.
Do we have the use-case where resource would be updated/merged in Provider after it's creation. As I understand, the user needs to ensure that it creates the resource object by merging all the default/custom/required detectors before creating the Provider. And in that case, set_resource(), add_resource() and merge_resource() on Provider is not required. So,
These looks fine:
- If user never called with_resource.- use SDK defaults.
This is also fine:
- with_resource(Resource::builder().with..with...build()) - Start with SDK defaults, and merge the rest.
- with_resource(Resource::builder_empty().with..with...build()) - Start with empty, and merge the rest.
Or maybe another option could be:
Resource::builder(start_with_default: bool) -> ResourceBuilder
i.e,
- with_resource(Resource::builder(start_with_default: true).with..with...build()) - Start with SDK defaults, and merge the rest.
- with_resource(Resource::builder(start_with_default: false).with..with...build()) - Start with empty resource, and merge the rest.
AndResourceBuilder::new()
andResourceBuilder::default()
can be made private/removed.
But NOT this, as I don't see it's use-case:
- provider.append_resource(..) - merges with existing Resource(s).
the user should instead do:
let merged_resource = resource.merge(to_be_merged);
let provider = Provider::builder().with_resource(merged_resource)... .build();
@@ -61,7 +63,29 @@ impl Default for Resource { | |||
} | |||
} | |||
|
|||
impl From<ResourceBuilder> for Resource { |
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.
I don't think we need to implement this conversion. I believe the intuitive thing to do is call build()
on a builder type and not into()
.
fixes #2320
Changes
ResourceBuilder
to more easily create resources.example usage:
Merge requirement checklist
CHANGELOG.md
files updated for non-trivial, user-facing changes