-
Notifications
You must be signed in to change notification settings - Fork 825
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
context.withSpan - proposition #1923
Comments
So mostly the same as Not sure if Spec allows to add more methods to global API objects. If not a free function could be used instead, similar as the existing |
The spec doesn't really define functions like setSpan etc. it is up to us to name them etc. The new helpers will be nothing more then just a shortcut sitting in the same "namespace" |
I found the below helper function useful but it goes a bit further and auto-ends the span. Not sure if this is good practice or not. It has worked OK for me :-). Before I had problems with spans not getting closed in some cases because developers forgot to call span.end(). /** Function I want to trace */
async function readFileWithRefs(fileName: string): Promise<FileWithRefs> {
return await withSpan("readFileWithRefs", async (span) => {
span.setAttribute("filename", fileName);
return JSON.parse(await fsp.readFile(fileName, "utf8"));
});
}
/**
* Helper function for running code within a span.
* Create a new span, make it the active span on a new context and execute the provided
* function within that context
*/
export function withSpan<T extends (span: Span) => ReturnType<T>>(operationName: string, fn: T): ReturnType<T> {
const tracer = api.trace.getTracer("default");
const span = tracer.startSpan(operationName);
const ctx = api.setActiveSpan(api.context.active(), span);
const fn2 = (): ReturnType<T> => {
const result = fn(span);
// The result might be a promise but there is no safe way to tell,
// instead convert the value to something that we know for sure is a promise
const promise = Promise.resolve(result);
promise.then(() => {
// End the span when promise resolves
// In case we wrapped a value it will be resolved directly
if (span.isRecording()) {
span.end();
}
});
return result;
};
return api.context.with(ctx, fn2);
} |
@jonaskello the helper function you posted is something different then what we are talking here, it will be just a shortcut for the code snippet I posted, nothing more |
Yes, it does a lot more work than being a shortcut. I'm new to all this so I'm curious where/how the proposed shortcut helper would be used and if I am somehow overdoing it with my own helper. |
we haven't yet decided if we add it or not, if we add this it will sit in api context, feel free to add your thoughts if you think such a shortcut will be useful, thx |
Other languages I have seen have a method that starts a span and sets it active on the context. ruby and python have these mechanisms. @mwear how has that worked out for ruby? do you expect it would be a useful addition here? |
The current JS API to use const rootSpan = tracer.startSpan('root')
const scopeRoot = contextSetActiveSpan(rootSpan)
const childSpan = tracer.startSpan('child')
context.current = setSpan(context.active(), childSpan)
const scopeChild = contextSetActiveSpan(childSpan)
//... other code and possible child spans of childSpan
scopeRoot.done() // <= which context is active after this call?
scopeChild.done() // <= which context is active after this call?
// what if the sequence is exchanged? if you transform this to use Besides that this would require that the underlying context manager allows to modify the current context. This is supported by Node |
I would not recommend the tracer.startActiveSpan(name, opts, ctx, () => {
// this is just a thin wrapper around api.context.with
});
|
Just dropping in to say please, please add shortcuts for these things. |
Ruby has two methods that work well for us
I think either one of these would be a good addition. |
As long as we support more ContextManagers/TracerProviders operations on Besides that I would love to see shortcuts writen in spec to get some consistency between languages. |
Context in general is difficult to teach to users, but so is having multiple ways to create and activate spans (not to mention the nomenclature trouble between started and "active"). FWIW most users use the auto instrumentation, and those that use manual tracing don't seem to be confused (or are at least not telling us if they are).
We used to have |
It was my understanding that the difference between a started span and a span that was the active span on the context was an important part of the context distinction and was done in the spec quite intentionally. If that distinction is important in the spec then why would it be encouraged to obfuscate it in the client implementations? |
One subtle difference between the JS implementation and the Ruby implementation is that |
This changes |
@dyladan not
|
I still think it isn't any less confusing, just a little more convenient to type. It still requires the user to understand the concept of the background context. |
My vote would be for |
Does this assume the |
My reading of the spec is that only static methods are allowed so the method we removed on I think the best thing we can do is to add // in API, uses global context manager
api.context.withSpan(span, fn, thisArg, ...args)`
// and on context manager (to allow use of non global context managers)
contextManager.withSpan(span, fn, thisArg, ...args)` Maybe also Another option would be to add methods to the I would not put it on |
It assumes |
to avoid confusion and explanation where this sits I would keep it simple
All other edge / advance cases are already covered with current functionality. I want to simplify things for 80% the most simple common scenarios without writing a big documentation and adding more params etc. The rest 20% of cases can be covered with what we have |
@Flarna just to clarify, there is no reason from a spec perspective that you can't add high level convenience methods. I recommend having these functions sit directly in the api package, like @obecny suggests. Ideally, application developers can stick to this single package, since their needs are not complex. This makes it easy to get started with OpenTelemetry, and then progressively learn the details later. I really do want to stress that this is an important feature. The lower level objects are well factored, not questioning that. But I train a lot of people and this kind of simplified UX makes a huge difference. |
I think this works except I would suggest |
@dyladan as long as it is in the same package I can live, but users do have a clear preference for a unified API. It's shorter to type and makes discovery easier for them. From my experience, users would be happiest with having it all at their fingertips. Not just |
@tedsuo I think everything we've talked about has been in the API package which is only one package (except metrics) |
I'm 100% with you on
My biggest gripe here is with the "etc." part. Without specification, how can I know what we should implement here? What happens if the spec adds some of these methods later that have subtly different implementations than what we implement? |
Hmmmm, I'm not sure that complexity would be helpful for users. You can't break them once you add them, and if you ended up creating an Personally, I think defining helpers like The main reason that I'm not as worried about deviation in this area is because these are higher level functions. As long as they just represent what a combination of lower-level functions would do, there's no risk that somehow the design of these helpers would be wrong in the sense that python would suddenly become incompatible with ruby, or break an important edge case. |
Maybe the spec can probably have a "suggested helpers" section or something where helpers and name suggestions are listed but are not required to be spec compliant. Suggested HelpersMethods in this section are not required for a client to be specification compliant, but are encouraged in cases where it makes sense for the language. Start a span and make it the active span on the contextName Suggestion: Functionally equivalent code: api.withSpan(span, () => {
// do something here
});
api.context.with(api.setSpan(span), () => {
// do something here
}); Set an attribute to the currently active spanNameSuggestion: Functionally equivalent code: tracer.setSpanAttribute(attributeName, attributeValue);
tracer.getCurrentSpan().setAttribute(attributeName, attributeValue); etc. |
Well with a quick look on other implementations:
I believe we should be namespacing helper, as i said in #7, i prefer using |
@vmarchaud but you are aware then we end up with having more helpers in many different namespace so the user will still have hard times with understanding which namespace he should be using, whereas keeping it on global api will simply allow them to use it without necessity of understanding first where this helper should belong to ? |
Yes i am, i believe that if the user wants to instrument his code he should be able to read a documentation or understand how our 3 api (trace, context and baggage) works. In the context of the issue, the user want to set a span as active in the context so the helper is available in the |
@vmarchaud |
I expect that he knows that he need to turn his steering wheel to turn his car. Generally speaking i will stand my point that we need to namespace the helpers, as i linked major implementation didn't export helpers on the global namespace so i believe we should be coherent. We can discuss with its best to expose it in |
vmarchaud we'll be focusing on improving our approach to having a convenience API across all languages over the next month, so we can have more of a discussion there. I do think you're being kind of rough by saying that users should just RTFM. Independent of whether a namespace is appropriate or not, we should strive to make the user have as intuitive an experience as we can. I would encourage the JS maintainers to actively seek user feedback on the convenience issue. |
I also think a namespace is appropriate in general. For Namespaces can cause issues with code minifiers, which is an issue in JS that other languages likely don't really care about, but we may be able to get around that by having very descriptive top-level function names like |
In this case can't we wait for a decision at the spec level and publish a minor release after RC ? It seems a better idea to me.
I understand how you could see my point as RTFM but it was in the context of using namespacing or not. I believe there a difference between explaining that we have different APIs, so they should find helper in the correct API instead of throwing helpers as top level because they will have completion for it. Furthermore, i don't think an user that doesn't know much about otel will be better able to use a
I remember seeing this document circulating about user research on gitter, is there any result about the JS API/SDK about this ? I don't know if the result are openly accessible. |
As a short-term solution, might I suggest someone take it upon themselves to write up a concise explanation of how to use the low-level API for the docs site? |
I am working on that now for our API repo. The docs site can use the same documentatio |
Fantastic! If you tag me in the PR I'll copy it over. |
open-telemetry/opentelemetry-js-api#14 opened but waiting on: should it be |
@naseemkullah I think this will be decided at the spec level |
Are we still waiting for a convenience spec before implementing this? My understanding is the convenience spec will be language agnostic and might not answer all of the questions above anyway. It would be really great to have this for the 1.0 release, I think most newcomers are expecting new spans to automatically become children and then can't figure out how to do this at all. I see the question "how do I set the parent span" all the time.
Python uses the with keyword for automatically ending a span, but to set the span in context automatically, there is Has adding tracer.spartSpan('span name').with((span: Span) => {
//...
}); |
to mimic what python does
I'm all for such convenience self explanatory method. |
Both namespace vs top level have pros and cons, abstracting a little less to leak the mechanics in a good way vs abstracting more in the name of user friendliness, and there is no obvious choice or current spec recommendation. However the time has come to make a choice, JS has 3 maintainers making a stalemate impossible if this were voted on. How about a vote to settle this?
|
What is throwing me off as well is that |
Maybe we could expose two layers of abstraction and maintain both |
Not sure what makes you think a choice needs to be forced now since these convenience methods could easily be added later and there is an ongoing spec effort to add these types of convenience methods. If forced into a decision now, I would prefer to keep the |
"Bias for action" as they say 😄 |
closed via open-telemetry/opentelemetry-js-api#54 which adds |
follow up to implement in Tracer created: #2210 |
In many places we will have now a repeatable code
Could we somehow simplify it by adding an extra helper function into something like
This is just proposition, to make it easier and to be more intuitive.
The text was updated successfully, but these errors were encountered: