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

feature(views): move views registration to MeterProvider constructor #3066

Conversation

pichlermarc
Copy link
Member

@pichlermarc pichlermarc commented Jun 29, 2022

Which problem is this PR solving?

When create<instrument_name_here>() is called before addView(), the View is not applied to the instrument.
Having addView() on the MeterProvider the gives the wrong impression that this can be done after creating instruments.

This PR moves the functionality provided by addView() to the MeterProvider constructor. This is how most SDKs (Python, Java, .NET) handle this today.

This makes it clear that views have to be created before creating instruments (as no Meter, and therefore no Instrument can be created without a MeterProvider), and brings us in line with other SDKs.

Also fixes a specification inconsistency where views would allow empty instrument/meter selectors, but not empty view options (see spec here).

Fixes #3056

Short description of the changes

  • Moves View registration to the constructor
  • Moves checking for multiple instrument selection on named views to the View class.
  • Combines the ViewOptions and SelectorOptions into one interface to reduce nesting when creating Views
  • Allows empty View configuration (all empty name, aggregation, description, attributeKeys)
  • Disallows empty Instrument selectors (all empty instrumentName, instrumentType, meterName, meterSchemaUrl, meterVersion)

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

  • Existing tests

Checklist:

  • Followed the style guidelines of this project
  • Unit tests have been added
  • Documentation has been updated

@codecov
Copy link

codecov bot commented Jun 29, 2022

Codecov Report

Merging #3066 (da511f3) into main (db32860) will increase coverage by 0.01%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main    #3066      +/-   ##
==========================================
+ Coverage   93.08%   93.09%   +0.01%     
==========================================
  Files         188      188              
  Lines        6261     6256       -5     
  Branches     1323     1319       -4     
==========================================
- Hits         5828     5824       -4     
+ Misses        433      432       -1     
Impacted Files Coverage Δ
...pentelemetry-sdk-metrics-base/src/MeterProvider.ts 100.00% <100.00%> (ø)
...es/opentelemetry-sdk-metrics-base/src/view/View.ts 100.00% <100.00%> (ø)
...elemetry-sdk-metrics-base/src/view/ViewRegistry.ts 100.00% <100.00%> (ø)
...emetry-core/src/platform/node/RandomIdGenerator.ts 93.75% <0.00%> (+6.25%) ⬆️

@pichlermarc pichlermarc force-pushed the meterprovider-constructor-views branch from cca5c73 to a44657e Compare July 4, 2022 13:30
@pichlermarc pichlermarc marked this pull request as ready for review July 5, 2022 16:33
@pichlermarc pichlermarc requested a review from a team as a code owner July 5, 2022 16:33
export type ViewOptions = {
// View
/**
* If not provided, the Instrument name will be used by default. This will be used as the name of the metrics stream.
Copy link
Member

Choose a reason for hiding this comment

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

Note, after this change, the options object does contain an instrumentName that can be confused with the term "Instrument name" in this statement.

Suggested change
* If not provided, the Instrument name will be used by default. This will be used as the name of the metrics stream.
* If not provided, the original Instrument name will be used by default. This will be used as the name of the metrics stream.

Or can we update the instrumentName to be selectInstrumentName or something else to clarify that?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. I updated the documentation in 59e69ae. 🙂
I hope that the documentation is sufficient, but I'm not opposed to changing the name too.

constructor(viewOptions: ViewOptions) {
// If no criteria is provided, the SDK SHOULD treat it as an error.
// It is recommended that the SDK implementations fail fast.
if (isSelectorProvided(viewOptions)) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm confused by this assertion. Views without name specified should be allowed to not set instrument selection criteria. Like:

new View({
 aggregation: DEFAULT_AGGREGATION
});

Copy link
Member Author

Choose a reason for hiding this comment

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

I fully agree, I was quite surprised as well when I re-read the spec and found that this should not be allowed.

Here it states, in context of instrument selection criteria that:

If no criteria is provided, the SDK SHOULD treat it as an error. It is
recommended that the SDK implementations fail fast. Please refer to Error
handling in OpenTelemetry for the general guidance.

That being said: it is not completely impossible to get the same outcome as before. It now requires providing a wildcard instrumentName like so:

new View({
 aggregation: DEFAULT_AGGREGATION,
 instrumentName: '*'
});

So even with this change to make it comply with the spec, the functionality is still retained, but I agree that this specific case is more confusing now than it was before. 😕

this.description = config?.description;
this.aggregation = config?.aggregation ?? Aggregation.Default();
this.attributesProcessor = config?.attributesProcessor ?? AttributesProcessor.Noop();
constructor(viewOptions: ViewOptions) {
Copy link
Member

Choose a reason for hiding this comment

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

I find that isViewOptionsEmpty is no longer applied on creating the views. Is there any specific reason for removing that?

Copy link
Member Author

Choose a reason for hiding this comment

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

When I implemented isViewOptionsEmpty() in #2820 I read the spec wrong, and it is actually not disallowed to create an empty view, but it is disallowed to create view without selector options specified. I explain it in more detail and link to the spec section in question in this comment here.

@legendecas
Copy link
Member

  • Allows empty View configuration (all empty name, aggregation, description, attributeKeys)
  • Disallows empty Instrument selectors (all empty instrumentName, instrumentType, meterName, meterSchemaUrl, meterVersion)

I'm confused by this change. IMO, empty view configuration is not meaningful -- it's just the default view and doesn't need to be added anyways, whilst empty view selection criteria are helpful in adding a custom default view.

Would you mind elaborating on this change?

@pichlermarc
Copy link
Member Author

  • Allows empty View configuration (all empty name, aggregation, description, attributeKeys)
  • Disallows empty Instrument selectors (all empty instrumentName, instrumentType, meterName, meterSchemaUrl, meterVersion)

I'm confused by this change. IMO, empty view configuration is not meaningful -- it's just the default view and doesn't need to be added anyways, whilst empty view selection criteria are helpful in adding a custom default view.

Would you mind elaborating on this change?

I can see that this is an odd change. It comes from a misunderstanding I had when implementing it in #2820. More details can be found in this comment here.

Copy link
Member

@legendecas legendecas left a comment

Choose a reason for hiding this comment

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

Code-wise LGTM. Still not sure about allowing empty views. But this is a trivial behavior, I'm not going to block this from landing.

@pichlermarc
Copy link
Member Author

pichlermarc commented Jul 12, 2022

Code-wise LGTM. Still not sure about allowing empty views. But this is a trivial behavior, I'm not going to block this from landing.

Thank you for your review! 🙂

If you like I can open a discussion issue and maybe we can add it back in. I'm actually of the same opinion as you are on this. When I first implemented this check, it was a natural thing to check if the view is empty. In this PR I allowed empty views again as the spec does not explicitly prohibit it. Other SDKs (relevant code for Java, Python and .NET) also allow empty views, so we would be going against the grain on this.

@dyladan
Copy link
Member

dyladan commented Jul 12, 2022

An empty view is never selected right? Shouldn't they just be dropped? Maybe i'm misunderstanding

@pichlermarc
Copy link
Member Author

pichlermarc commented Jul 12, 2022

An empty view is never selected right? Shouldn't they just be dropped? Maybe i'm misunderstanding

Yes, it is effectively a no-op. The changes here have the effect that creating empty views does not throw anymore.

Edit: formatting.

@legendecas
Copy link
Member

legendecas commented Jul 12, 2022

An empty view is never selected right? Shouldn't they just be dropped? Maybe i'm misunderstanding

We have two conditions here:

  • (1) empty view configuration,
  • (2) empty view selection criteria.

Before this change, for the above conditions, we have:

  • (1) not allowed, because it is basically the same as the default view, no need to add it manually,
  • (2) allowed, interpreted as a wildcard selection for convenience on configuring a new default view like new View({ aggregation: MyAggregation }).

With this change, the conditions are on the contrary:

  • (1) allowed,
  • (2) not allowed.

Does the previous behavior of (2) sound ambiguous on whether it means "select all" or "select nothing" to you? If so, maybe the change in this PR made (2) more precise.

Copy link
Member

@dyladan dyladan left a comment

Choose a reason for hiding this comment

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

LGTM I think this is way less likely to be misconfigured.

Can you update the README also?

@pichlermarc
Copy link
Member Author

LGTM I think this is way less likely to be misconfigured.

Can you update the README also?

Added a simple example to the README.md in dd13da0. 🙂

Copy link
Member

@legendecas legendecas left a comment

Choose a reason for hiding this comment

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

Still LGTM

@vmarchaud
Copy link
Member

I wonder if that would not create another problem for end user that want to change aggregation for auto instrumentations where they need to do it before importing instrumentations (which looks like the same issue for module monkey patching). Now user will need to have to construct trace and metrics provider, add view on metrics and then import instrumentations ?

PS: @pichlermarc you will need to rebase your branch

@pichlermarc
Copy link
Member Author

I wonder if that would not create another problem for end user that want to change aggregation for auto instrumentations where they need to do it before importing instrumentations (which looks like the same issue for module monkey patching). Now user will need to have to construct trace and metrics provider, add view on metrics and then import instrumentations ?

PS: @pichlermarc you will need to rebase your branch

@vmarchaud yes, but since addView() did not work retroactively on already created instruments, I think this problem existed already. Calling addView() on the MeterProvider before creating any Instruments was the only surefire way to have all of the views applied, and that meant that it had to be done before loading any instrumentations. It could be that I'm misunderstanding something, though. 🤔

One of my motivations in this PR is that it allows us to add an addView() method that reconfigures existing instruments to the MeterProvider in the future without it being a breaking change on the MeterProvider.

I rebased the branch. 🙂
Not sure why the tests are failing, I'm investigating.

@vmarchaud
Copy link
Member

think this problem existed already

Yeah i understood this too with your PR, i though initially that adding views would reconfigure instrument.

One of my motivations in this PR is that it allows us to add an addView() method that reconfigures existing instruments to the MeterProvider in the future without it being a breaking change on the MeterProvider.

Cool to know its the end goal ! I was already imagining end users complaining that this isn't friendly so thats why i brought the subject :)

@pichlermarc
Copy link
Member Author

Previously failing test seems to be related to lerna. It can sometimes be reproduced on main as well, seems unrelated to this PR. See lerna/lerna#3201

@vmarchaud
Copy link
Member

@dyladan since you had nits since last review i'll let you merge if you are still good

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.

View is not applied when instrument is created before addView() is called
4 participants