-
Notifications
You must be signed in to change notification settings - Fork 73
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
Added SetTag and WithTag for use with OpenTracing.Tag.Tags #72
Conversation
src/OpenTracing/Mock/MockSpan.cs
Outdated
|
||
public override string ToString() | ||
{ | ||
return $"Timestamp: {Timestamp}, Fields: " + string.Join("; ", this.Fields.Select(e => $"{e.Key} = {e.Value}")); |
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.
Just a note - unrelated change
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.
Agreed. Please remove this.
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.
Thanks for noticing, it just slipped into the PR from my local changes.
@Falco20019 any reason you could not just use the |
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 we are going to have AbstractTag<T>
at all, then it makes sense to have these methods.
Tangential note - having it requires CallVirt
s which aren't the best in terms of performance, and Tracing should have a low footprint. But that's for another day.
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'm not a fan of the existing non-fluent syntax either so I really like this idea.
We now have feature-parity with Java and I'd like to stay as close to them as possible so ideally this should be proposed in Java as well and merged only if both languages agree on it. Are you interested in creating a similar Java PR? If not, I'll do it.
Note that I flagged it as "breaking-change" as it adds members to the interface and therefore breaks implementations.
src/OpenTracing/ISpan.cs
Outdated
@@ -28,6 +29,9 @@ public interface ISpan | |||
/// <summary>Same as <see cref="SetTag(string,string)"/> but for numeric values.</summary> | |||
ISpan SetTag(string key, double value); | |||
|
|||
/// <summary>Set a tag on the Span using the helper in type.</summary> |
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.
Not sure if it's the fact that English is not my first language but this summary sounds weird. What's the "helper in type"?
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.
„Type“ was a reference to the parameter name. Sorry, English isn’t my first language so I didn’t notice it prior. Will adjust it.
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 renamed it to "tagSetter" and made it a paramref.
src/OpenTracing/Mock/MockSpan.cs
Outdated
|
||
public override string ToString() | ||
{ | ||
return $"Timestamp: {Timestamp}, Fields: " + string.Join("; ", this.Fields.Select(e => $"{e.Key} = {e.Value}")); |
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.
Agreed. Please remove this.
I don't see why it is not fluent, but I do agree it feels strange with the AbstractTag that you have to do
Maybe I'm wrong but I think this is not needed if you for example implement Could we solve this with extensions instead? What about public static class SpanBuilderExtensions
{
public static ISpanBuilder WithTag(this ISpanBuilder spanBuilder, AbstractTag<bool> key, bool value)
{
return spanBuilder.WithTag(key.Key, value);
}
public static ISpanBuilder WithTag(this ISpanBuilder spanBuilder, AbstractTag<int> key, int value)
{
return spanBuilder.WithTag(key.Key, value);
}
public static ISpanBuilder WithTag(this ISpanBuilder spanBuilder, AbstractTag<double> key, double value)
{
return spanBuilder.WithTag(key.Key, value);
}
public static ISpanBuilder WithTag(this ISpanBuilder spanBuilder, AbstractTag<string> key, string value)
{
return spanBuilder.WithTag(key.Key, value);
}
}
public static class SpanExtensions
{
public static ISpan SetTag(this ISpan span, AbstractTag<string> key, string value)
{
return span.SetTag(key.Key, value);
}
public static ISpan SetTag(this ISpan span, AbstractTag<bool> key, bool value)
{
return span.SetTag(key.Key, value);
}
public static ISpan SetTag(this ISpan span, AbstractTag<int> key, int value)
{
return span.SetTag(key.Key, value);
}
public static ISpan SetTag(this ISpan span, AbstractTag<double> key, double value)
{
return span.SetTag(key.Key, value);
}
} |
Having to use
Yes, we could. I'm not sure if we want though as this no longer gives implementations the possibility to optimize behavior. E.g. by using extension methods, it would no longer be possible to do an immediate noop for these calls in a NoopTracer - it would always have to go through this extension method. This is probably a very small difference though.
Do you have some perf comparisons/blog posts for this somewhere? I've done, some, googling, and it seems like C# uses callvirt in many cases anyway to ensure |
@dawallin One distinctive feature of I also thought about using extensions instead to avoid making it a breaking change by using |
Ammend + forced push didn't work for whatever reason...
I did some performance tests using the following piece of code: const int cmds = 1000000;
List<long> elapsedTicks = new List<long>();
var span = tracer.BuildSpan("Test").Start();
Stopwatch sw = new Stopwatch();
for (int iteration = 0; iteration < 10; iteration++)
{
sw.Restart();
for (int i = 0; i < cmds; i++)
{
//span.SetTag(Tags.Component, "grpc");
Tags.Component.Set(span, "grpc");
}
sw.Stop();
elapsedTicks.Add(sw.ElapsedTicks);
}
var elapsedAvg = elapsedTicks.Skip(2).Average() / cmds; I tried the following implementations and reported the average ticks per call.
The method proposed by @dawallin is of course the fastest since it doesnt use generics and sets the span directly (4th result). Doing it directly in the class by extending the interface is about the same (2nd result) and would allow to implement the I repeated everything for the
Looking at the results, the fastest seems to be option 2, doing it in the interface and without generics. Option 1 would still be a better future-proof fit in case that other types need to be supported. Otherwise, everytime a new type is added, this would be a breaking change. |
If we are going to try to keep these together indefinitely does it make sense to just merge the repos and have java + csharp in 1 repo so that issues and changes are done together?
The CallVirt is done because of the @Falco20019 |
No idea what the best process is here... It's currently pretty hard to synchronize and keep track of all languages. gRPC even puts ALL languages into one repository but this probably has quite a few disadvantages as well. Anyway, I guess this is something that should best be discussed e.g. in https://gitter.im/opentracing/cross-language Regarding this issue, I'm in favor of option 2 (having separate methods on the interface) as this gives tracers maximum flexibility. Also, adding a new type (e.g. DateTime) is a breaking change anyway as we would also have to add the regular |
@cwe1ss All points from the review were resolved. So please let me know if I can be of any further assistance here once you decided which option I should use in the PR. I saw that you are in favor of option 2. @ndrwrbgs @dawallin I would update the code to apply option 2 if there are no further opinions. |
I created a similar perf test with BenchmarkDotNet. I've been testing the following signatures with ISpan SetTag_Int(string key, int value);
ISpan SetTag_AbstractGeneric<TTagValue>(AbstractTag<TTagValue> tag, TTagValue value);
ISpan SetTag_AbstractTyped(AbstractTag<int> tag, int value);
ISpan SetTag_IntTag(IntTag tag, int value); Result: BenchmarkDotNet=v0.10.13, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.248)
Intel Core i5-4300U CPU 1.90GHz (Haswell), 1 CPU, 4 logical cores and 2 physical cores
Frequency=2435765 Hz, Resolution=410.5486 ns, Timer=TSC
.NET Core SDK=2.1.4
[Host] : .NET Core 2.0.5 (CoreCLR 4.6.26020.03, CoreFX 4.6.26018.01), 64bit RyuJIT
DefaultJob : .NET Core 2.0.5 (CoreCLR 4.6.26020.03, CoreFX 4.6.26018.01), 64bit RyuJIT
Using one It's surprising to me that using I would therefore prefer using the actual Tag-types directly! This would ensure, all existing Tag types are supported and it also looks better in IntelliSense IMO. Adding a new Tag type is a breaking change but as I've said before, this is breaking anyway. To sum it up, I would prefer the following additions to our interfaces: // ISpan
ISpan SetTag(BooleanTag tag, bool value);
ISpan SetTag(IntOrStringTag tag, string value);
ISpan SetTag(IntTag tag, int value);
ISpan SetTag(StringTag tag, string value);
// ISpanBuilder
ISpanBuilder WithTag(BooleanTag tag, bool value);
ISpanBuilder WithTag(IntOrStringTag tag, string value);
ISpanBuilder WithTag(IntTag tag, int value);
ISpanBuilder WithTag(StringTag tag, string value); Thoughts? |
LGTM. Stats showed 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.
LGTM!! Thank you! I'll leave it open for a few days to give people a chance to comment.
I'm working on jaegertracing/jaeger-client-csharp#44 and it would be great to have this. For now, I will have to get the key to use it with |
I just finished reviewing the opentracing-tutorial from @saschaholesch and lesson03 would have benefited of this PR. I only cross-link it so that someone may update the tutorial when this gets released. |
src/OpenTracing/Mock/MockSpan.cs
Outdated
@@ -157,6 +158,30 @@ public ISpan SetTag(string key, string value) | |||
return SetObjectTag(key, value); | |||
} | |||
|
|||
public ISpan SetTag(BooleanTag tag, bool value) | |||
{ | |||
tag.Set(this, value); |
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 we just call SetObjectTag(tag.Key, value)
in these new methods? Seems more readable than doing the additional jump through tag.Set()
.
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.
@Falco20019 what do you think? Could you update this if you agree?
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.
Sorry for the delay. Will do it tomorrow :) was out of office the last days and had some other projects to work on yesterday and today.
I'm also still bullish about this. I created opentracing/opentracing-java#271 to get some feedback from the Java folks! |
@Falco20019 Thanks for updating the PR! I'll merge this once we have agreement on what should be included in the next version - see #92. (There hasn't been any objections yet so we should be good to go soon) |
There hasn't been any concerns yet, so I'll merge it. Thank you very much @Falco20019 for creating the PR - this will be a great addition! @ all: Feel free to post any concerns in #92 before we do the next release or create a new issue afterwards. |
I found the usage of
OpenTracing.Tag.Tags
somewhat inconvinient, since it was not usable with Fluent API.