-
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
GlobalTracer ported from Java #61
Conversation
fyi: I'll probably merge this on Tuesday morning (UTC) if there's no contrary comments. |
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.
For your consideration here what I had locally for a similar class:
public static class TracerProvider
{
public delegate ITracer ExchangeTracerDelegate(ITracer existingTracer);
private static readonly object lockObj = new object();
private static ITracer tracer = NoopTracer.Instance;
public static ITracer Tracer
{
get { return tracer; }
set
{
lock (lockObj)
{
tracer = value;
}
}
}
public static ITracer ExchangeTracer([NotNull] ExchangeTracerDelegate newTracerDelegate)
{
lock (lockObj)
{
Tracer = newTracerDelegate(Tracer);
return Tracer;
}
}
}
/// all behave as if obtained <em>after</em> initialization once properly initialized.<br/> | ||
/// As a minor additional benefit it makes it harder to circumvent the <see cref="ITracer"/> API. | ||
/// </summary> | ||
private static readonly GlobalTracer s_instance = new GlobalTracer(); |
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 GlobalTracer have a private ctor since it appears it's meant to be a singleton?
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.
yes, thank you! will fix it!
/// <returns>Whether a tracer has been registered.</returns> | ||
public static bool IsRegistered() | ||
{ | ||
return !(s_instance._tracer is NoopTracer); |
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.
[nit] Slightly misleading as the user could have registered the NoopTracer
. I'd rather set a variable than rely on the 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.
That's a feature. Having a "NoopTracer" by definition means that no tracer is set.
src/OpenTracing/Util/GlobalTracer.cs
Outdated
private ITracer _tracer = NoopTracerFactory.Create(); | ||
|
||
// <inheritdoc/> | ||
public IScopeManager ScopeManager => _tracer.ScopeManager; |
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.
Given this isn't used in this class seems to suggest we are doing more than SRP, which I'd argue we are. We're trying to wrap a tracer while also trying to provide singleton global access. Might I suggest something more like
public static class GlobalTracer/TracerProvider
{
public static ITracer Tracer { get; set; } /* more details about how to implement this addressing the relevant concerns in another comment */
}
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 problem with this is that people could save that TracerProvider.Tracer
variable locally and therefore never get a new tracer if someone replaces it. That's the reason why in this class, GlobalTracer.Instance
always returns the one singleton GlobalTracer
.
The instance types are not used because GlobalTracer is just a simple decorator/wrapper 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.
The instance types are not used because GlobalTracer is just a simple decorator/wrapper type.
Correct, this is the expected behavior :)
/// </summary> | ||
/// <returns>The global tracer constant.</returns> | ||
/// <seealso cref="Register(ITracer)"/> | ||
public static ITracer Instance => s_instance; |
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.
Minor concern, but could add up.
Since this is itself exposed as an ITracer
rather than it's real type, it will result in all calls to it like BuildSpan
becoming CallVirt
's. Then, since it wraps another, it has another CallVirt
.
I really like information hiding, but in this case exposing it's real type may be best since it's likely all tracing will come through this class.
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.
Hmmm... no idea what the perf impact is here. I'll do a little bit of research. Returning GlobalTracer
probably means that IntelliSense no longer shows documentation since <inheritdoc/>
is not really supported in IDEs AFAIK. We would have to copy all the documentation.
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 looks like something that could be done afterwards as an optimization (an issue around this could be created so it doesn't get forgotten, and eventually discussed/handled).
/// during its initialization. | ||
/// </summary> | ||
/// <param name="tracer">Tracer to use as global tracer.</param> | ||
public static void Register(ITracer tracer) |
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's possible a developer at the composition layer wants to register one tracer but someone wants to wrap that for composition (or they have a InitializeStandardTracer in a base class and then some custom wrapping they want). The current code prevents that.
A possible way to handle it is
public delegate ITracer ExchangeTracerDelegate(ITracer existingTracer);
public static ITracer ExchangeTracer([NotNull] ExchangeTracerDelegate newTracerDelegate) // Oops, my love for nullability annotations from ReSharper is showing :)
{
lock (s_lock)
{
s_instance._tracer = newTracerDelegate(s_instance._tracer);
}
}
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.
Note we also do not support someone saying "I want to intercept trace messages from my callee here and down", which would require AsyncLocal for the tracer.
While I'd like to support that, I wouldn't do so until I had measured the performance of AsyncLocal.
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.
These are requirements that are not yet formally specified (opentracing/specification#9 [multiple tracers], opentracing/specification#76 [observers]) so they are no-goals of this implementation.
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.
@ndrwrbgs That's an interesting feature. I'd personally recommend you post an issue against the Java repo and have people discuss it ;)
This is a port from the
opentracing-java
project. It currently has the following deviations:synchronized
keyword and usage ofMethodImpl.Synchronized
is not really recommended so I'm using regular locking instead. I might have messed that up so any feedback is welcome. :)tracer
variable is static. I do not really know why asGlobalTracer
is more or less a regular wrapper/decorator type so it should be an instance variable.Note that this currently does not compile as it requires the NoopTracer from #57. That's also the reason why there's no unit tests yet.
This will resolve #45.