Make SuppressInstrumentation an IDisposable#988
Make SuppressInstrumentation an IDisposable#988cijothomas merged 13 commits intoopen-telemetry:masterfrom
Conversation
|
This is a bit different from what I was originally thinking - I was exploring something on the So instead of having: var foo = RuntimeContext.GetValue<int>("foo");
var bar = RuntimeContext.GetValue<string>("bar");
try
{
RuntimeContext.SetValue("foo", 1);
RuntimeContext.SetValue("bar", "hello");
// do something
}
finally
{
RuntimeContext.SetValue("foo", foo);
RuntimeContext.SetValue("bar", bar);
}One could use (not sure if people would like using (RuntimeContext.With("foo", 1).With("bar", "hello"))
{
// do something
}And for the flag (I don't know if it is possible in C# to have the following syntax - and meanwhile still have using (Sdk.SuppressInstrumentation(true))
{
// do something
} |
|
Hmm, I see. These are interesting ideas. Though both of them still raise the question of what the behavior should be if a Though, I could also be ok with just taking the simple approach, because in practice this may not likely arise. |
|
I kind of like what @alanwest has. I would probably add "Scope" (or "Context") into the name, a la: using (SuppressInstrumentationScope.Begin())
{
await ExportAsync(batch).ConfigureAwait(false);
}I think for the common case we don't need to pass Now if SuppressInstrumentationScope was just wrapping
What you are doing is (essentially): using (var scope = SuppressInstrumentation.Begin())
using (var innerScope = SuppressInstrumentation.Begin())
scope.Dispose();
innerScope.Dispose();I would simply say, unsupported 😄 Have to dispose them in the correct order. using (var scope = SuppressInstrumentation.Begin())
using (var innerScope = SuppressInstrumentation.Begin())
innerScope.Dispose();
scope.Dispose();That should work fine. |
|
I do like the
I was also imagining this to be true. Though also trying to stretch my brain 🧠 in search of use cases I may not be thinking of. @reyang, was there a use case you were thinking of? |
I'm being serious! hah I don't think the expectation on these scope/context type of things is that you can dispose them out of order. I'm thinking about Activity.Current. It has the same issue. If you dispose/stop a parent, it sets its parent as Activity.Current. If someone then does Activity.Current.Stop thinking it's the child, havoc ensues. Same idea for log scopes. |
I don't have a use case at all. It was mainly coming from natural language. For example |
|
It reads really naturally to me, but I'm working in a codebase that uses a lot of log scopes. Or its cousin, the change token. Basically a newer pattern in .NET where you return IDisposable as the mechanism for calling stop/end/cancel. I really like it 🤷 |
Then go for it 💯 😄 |
|
Yea me, too! Sorry if I sounded sarcastic 😅. I completely agree that it make sense not to support out-of-order disposal. I figured this was a case of over-thinking, so I always like to just stop and see what others think.
Sounds like a decent consensus here. I appreciate the thoughts around natural language, though I also agree that there is enough of a pattern and practice in .NET for the I'll get this PR cleaned up. |
| /// Gets a value indicating whether automatic telemetry | ||
| /// collection in the current context should be suppressed (disabled). | ||
| /// </summary> | ||
| public static bool IsSuppressed => SuppressInstrumentationRuntimeContextSlot.Get() != default; |
There was a problem hiding this comment.
Consider implicit operator?
public sealed class SuppressInstrumentationScope : IDisposable
{
private readonly bool value;
public static implicit operator bool(SuppressInstrumentationScope _) => SuppressInstrumentationRuntimeContextSlot.Get();
public SuppressInstrumentationScope(bool value = true)
{
this.value = value;
SuppressInstrumentationRuntimeContextSlot.Set(value);
}
public void Dispose()
{
SuppressInstrumentationRuntimeContextSlot.Set(this.value);
}
public IDisposable Begin(bool value = true)
{
return new SuppressInstrumentationScope(value);
}
}
public readonly static SuppressInstrumentationScope SuppressInstrumentation = new SuppressInstrumentationScope(false);There was a problem hiding this comment.
Then from consumption side:
if (Sdk.SuppressInstrumentation)
{
// do something
}
using (Sdk.SuppressInstrumentation.Begin())
{
// do something
}There was a problem hiding this comment.
Interesting, I would not have thought of this approach!
There was a problem hiding this comment.
Ok pushed up something that works.
| { | ||
| public sealed class SuppressInstrumentationScope : IDisposable | ||
| { | ||
| private static readonly RuntimeContextSlot<bool> SuppressInstrumentationRuntimeContextSlot = RuntimeContext.RegisterSlot<bool>("otel.suppress_instrumentation"); |
There was a problem hiding this comment.
Given this is inside a class SuppressInstrumentationScope, we can probably simplify the name to ContextSlot or even Slot.
| SuppressInstrumentationRuntimeContextSlot.Set(value); | ||
| } | ||
|
|
||
| public static implicit operator bool(SuppressInstrumentationScope x) => SuppressInstrumentationRuntimeContextSlot.Get(); |
There was a problem hiding this comment.
| public static implicit operator bool(SuppressInstrumentationScope x) => SuppressInstrumentationRuntimeContextSlot.Get(); | |
| public static implicit operator bool(SuppressInstrumentationScope unused) => SuppressInstrumentationRuntimeContextSlot.Get(); |
There was a problem hiding this comment.
Or other name that explains the intention.
There was a problem hiding this comment.
😄 agree unused is better than x. I changed it. Though this did leave me wondering if people would prefer to be able to use _ in this case. I think it's pretty common practice, though I imagine allowing it means allowing any variable to start with a _ which I'm guessing we don't want. No biggie unused is great.
There was a problem hiding this comment.
I like _ and initially I was trying to use it, then caught by a style cop 😄
cijothomas
left a comment
There was a problem hiding this comment.
PLease address the small comments about naming which Reiley already left.
Can you also add this to changelog.md?
…alanwest/opentelemetry-dotnet into alanwest/using-suppress-instrumentation
Codecov Report
@@ Coverage Diff @@
## master #988 +/- ##
==========================================
+ Coverage 68.47% 68.55% +0.07%
==========================================
Files 220 221 +1
Lines 6002 6010 +8
Branches 983 984 +1
==========================================
+ Hits 4110 4120 +10
+ Misses 1619 1617 -2
Partials 273 273
|
|
@alanwest conflict here :-) |
|
You're a mergin' machine, @cijothomas! Thanks for shepherding so many PRs through! |
Implementing the suggestion from @reyang and 👍'ed by @CodeBlanch and myself in #960 to introduce
usingsyntactic sugar 🍬 forSuppressInstrumentation.Before I go too far, I wanted to have a quick discussion for what kind of behavior we'd like in the event of a nested situation like the following where Dispose is explicitly called:
The current logic is simplistic and would not pass this test. Though, if people agree this is the correct flow we'd like to expect, then I can implement it with a mechanism where the inner scope signals the parent that it is ok to dispose. However, I just want to make sure things aren't becoming more complicated than people would hope for.