Skip to content
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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

pitoniak32
Copy link
Contributor

@pitoniak32 pitoniak32 commented Nov 22, 2024

fixes #2320

Changes

  • Add ResourceBuilder to more easily create resources.

example usage:

let resource = Resource::builder()
    .with_detector(Box::new(EnvResourceDetector::new()))
    .with_key_value(KeyValue::new("test1", "test_value"))
    .with_key_values(vec![
        KeyValue::new("test1", "test_value1"),
        KeyValue::new("test2", "test_value2"),
    ])
    .build();

Merge requirement checklist

  • CONTRIBUTING guidelines followed
  • Unit tests added/updated (if applicable)
  • Appropriate CHANGELOG.md files updated for non-trivial, user-facing changes
  • Changes in public API reviewed (if applicable)

@pitoniak32 pitoniak32 requested a review from a team as a code owner November 22, 2024 01:30
Copy link

codecov bot commented Nov 22, 2024

Codecov Report

Attention: Patch coverage is 88.23529% with 8 lines in your changes missing coverage. Please review.

Project coverage is 79.5%. Comparing base (a3c469b) to head (c00aff4).

Files with missing lines Patch % Lines
opentelemetry-sdk/src/resource/builder.rs 91.9% 5 Missing ⚠️
opentelemetry-sdk/src/resource/mod.rs 50.0% 3 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##            main   #2322   +/-   ##
=====================================
  Coverage   79.5%   79.5%           
=====================================
  Files        123     124    +1     
  Lines      21492   21560   +68     
=====================================
+ Hits       17096   17156   +60     
- Misses      4396    4404    +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@pitoniak32
Copy link
Contributor Author

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 :)

@utpilla
Copy link
Contributor

utpilla commented Nov 23, 2024

@pitoniak32 Could you split this into two PRs? Have one PR just for removing the timeout argument and one for resource builder. That way we can merge the timeout related PR while we review the builder related changes.

@pitoniak32
Copy link
Contributor Author

@pitoniak32 Could you split this into two PRs? Have one PR just for removing the timeout argument and one for resource builder. That way we can merge the timeout related PR while we review the builder related changes.

split those changes into: #2332

}

/// Add a [KeyValue] to the resource.
pub fn with_key_value(self, kv: KeyValue) -> Self {
Copy link
Member

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...

Copy link
Contributor Author

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 😅

Copy link
Member

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.

@pitoniak32
Copy link
Contributor Author

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])
}

@pitoniak32 pitoniak32 force-pushed the feat/add-resource-builder branch 2 times, most recently from 5976ef0 to e501d4f Compare November 24, 2024 16:02
"service.name",
"metrics-advanced-example",
)]))
.with_resource(resource)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if we can do with_servicename_resource("metrics-advanced-example") ? This will be the most common use case.
Or just Resource::builder().with_servicename("metrics-advanced-example")......build() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that, I was using the go implementation for Resource builder this weekend and they had something similar to this and it felt pretty nice

sResource, err := resource.New(
  ctx,
  resource.WithTelemetrySDK(),
  resource.WithAttributes(semconv.ServiceName("trace-export-service")),
)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the option of .with_service_name("name")

Copy link
Contributor

@TommyCpp TommyCpp Dec 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm should we give special treatment to service name? I feel like it will open a can of worms that which attributes should get dedicate builder method. I think we should keep the API lean for now.

On the long term we can use dedicate trait to enrich the builder. Image something like

pub trait SemCov {
  fn with_service_name(&str);
  fn with_service_version(&str);
  ...
  // those method can probably generated in sem cov crate
}

impl SemCov for ResourceBuilder {
  fn with_service_name(&str) {
    ...
   // again we can generated those code in sem cov crate
  }
}

// main.rs
import {ResourceBuilder, SemCov}

let builder = ResourceBuilder::new().with_service_name("test")

So for people don't care about build size, they can opt in method for all attributes on resource builder by importing SemCov trait.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable to me, I was kind of wondering the same thing where we would want to use the key from sem conv instead of the hardcoded one in the sdk. I will remove this, and it can become another discussion

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we give special treatment to service name? I feel like it will open a can of worms that which attributes should get dedicate builder method. I think we should keep the API lean for now.

Yes. As spec gives special treatment for service name, with a dedicated env variable just for that. No other Resource attribute has that treatment.

Copy link
Contributor

@hdost hdost left a 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 😊

}

/// Create ResourceBuilder with a default [Resource].
pub fn new_default() -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn new_default() -> Self {
pub fn default() -> Self {

Probably should be done as an impl Default for ResourceBuilder to make it more generally compatible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated


impl ResourceBuilder {
/// Create ResourceBuilder with an empty [Resource].
pub fn new_empty() -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn new_empty() -> Self {
pub fn new() -> Self {

This makes it easier to reason about. Since on Resource the new is the same as empty but it accepts key values. Since this is a builder most would assume it's empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@lalitb lalitb added this to the 0.27.1 milestone Nov 26, 2024
}

/// Add multiple [KeyValue]s to the resource.
pub fn with_key_values<T: IntoIterator<Item = KeyValue>>(mut self, kvs: T) -> Self {
Copy link
Member

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

self.resource = self.resource.merge(&Resource::new(kvs));
self
}

Copy link
Member

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 ?

@lalitb
Copy link
Member

lalitb commented Dec 2, 2024

/// Add a [KeyValue] to the resource.
pub fn with_key_value<K, V>(self, key: K, value: V) -> Self
where
K: Into,
V: Into,
{
self.with_key_values(vec![KeyValue::new(key, value)])
}

I vote for this, it's similar to what is followed for LogRecord::add_attribute:

fn add_attribute<K, V>(&mut self, key: K, value: V)
where
K: Into<Key>,
V: Into<AnyValue>,
{

@cijothomas cijothomas removed this from the 0.27.1 milestone Dec 2, 2024
}

impl Default for ResourceBuilder {
/// Create ResourceBuilder with [Resource::default()].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we mention what the default would be here also? i.e it'll contain the telemetry,env etc.?

}

/// Create a [Resource] with the options provided to the [ResourceBuilder].
pub fn build(self) -> Resource {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we implement Into<Resource> for ResourceBuilder to make it easier to use ResourceBuilder as Resource in parameters

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve Resource related public APIs
6 participants