-
Notifications
You must be signed in to change notification settings - Fork 821
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
Tracer web #334
Tracer web #334
Conversation
Codecov Report
@@ Coverage Diff @@
## master #334 +/- ##
=========================================
- Coverage 97.9% 95.71% -2.2%
=========================================
Files 106 108 +2
Lines 5213 5154 -59
Branches 431 433 +2
=========================================
- Hits 5104 4933 -171
- Misses 109 221 +112
|
[![devDependencies][devDependencies-image]][devDependencies-url] | ||
[![Apache License][license-image]][license-image] | ||
|
||
This module provides automatic tracing for Node.js applications. |
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.
Could you update this readme to talk about basic tracing for the web browser?
@@ -0,0 +1,34 @@ | |||
import { WebTracer } from '../../src'; | |||
|
|||
import * as shimmer from 'shimmer'; |
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.
Although I think use of shimmer is cool/useful in automatic tracing contexts, would it make sense for the example to be a little more basic and just show how to e.g. create a span for some code that does a timeout or XHR call?
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 have used it as some playground to debug stuff and understand this better :). Wasn't really sure what to show as an example and how many of them we should have. Maybe we can talk a bit more to have similar examples in other places too?
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.
Yeah, I think this is OK for now. In the future, we can always improve the examples in various ways.
|
||
import { ScopeManager } from '@opentelemetry/scope-base'; | ||
|
||
export class StackScopeManager implements 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.
Could you add some documentation for this class about how it works? In particular, to highlight its limitations in terms of not keeping context across async calls?
Related to that, what's the value of the StackScopeManager here over a simpler model where it just stores a single scope at a time? I think storing a single scope will work for basic web cases such as the initial load or even user interactions where we assume only one thing is happening at a time.
For concurrent user interactions, I think we really need something that is async-aware like a Zone.js based scope manager - but that's a more advanced use case.
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.
With regards to value of StackScopeManager over simpler model. Can you please provide what you mean by simpler model ?. The BasicTracer expects to define a scopeManager with certain functionality:
https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-basic-tracer/src/types.ts#L63.
So I would say that StackScopeManager is exactly the simpler model , unless I'm missing something. The more advanced would be the one that will cover the async functionality as you said.
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.
What if you had a ScopeManager implementation that looked like this:
export interface ScopeManager {
private _scope: unknown = {};
active(): unknown {
return this._scope;
}
with<T extends (...args: unknown[]) => ReturnType<T>>(
scope: unknown,
fn: T
): ReturnType<T> {
this._scope = scope;
return fn.apply(args);
}
bind<T>(target: T, scope?: unknown): T {
if (scope !== undefined) {
this._scope = scope;
}
return target;
}
}
That to me feels like the most minimal scope manager that actually does something: it stores the scope that you give it in a single variable and makes no attempt to propagate it or make it hierarchical or async in any way. (Of course it's missing some things like documentation, error handling, etc. but gives the idea)
That would be sufficient for some browser use cases (e.g. the initial document load) where we really just have one global scope.
Sorry for the slow response on this! I think I missed your reply an earlier time I looked at 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.
Alternatively, here is a slightly more complex version of with
that captures the scope and restores it without needing an explicit data structure (could do the same for bind
):
with<T extends (...args: unknown[]) => ReturnType<T>>(
scope: unknown,
fn: T
): ReturnType<T> {
const oldScope = this._scope;
return (...args) => {
this._scope = scope;
try {
return fn.apply(args);
} finally {
this._scope = oldScope;
}
};
}
This still feels a little lighter weight, but I think I would prefer the most simple version where we just have one scope at a time explicitly since we don't do any async scope propagation, and then have the Zone.js based one do the fancy propagation and hierarchical wrapping.
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.
@draffensperger ok I have simplified the scope manager. Previously I had identified the scopes by adding some of ids to them. It was also to be able to identify them later in async but that is not the case anymore as we will rather use the new scope manager for that something like ZoneScopeManager
. I have also realised that there might be multiple tracer so adding id to scope is not the best idea. Keeping it shortly then I would leave the StackScopeManager and treat it as really the basic one as in your example there would be a simple problem with things like calling the callback with scope inside another callback - it would not be restored after all.
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.
Yeah, that makes sense to want to make sure we can at least restore scope for simple callbacks.
Is there a way you can use closures (function wrapping via arrow functions) to restore the scope rather than using UIDs and an object? I think that might be a little bit cleaner and use less code, but maybe there's a use case I'm not understanding here
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.
@draffensperger extra closure is not needed in this case , the fn
is a callback function that is called immediately with the current scope, there is no waiting for that like with bind
function. Unless I'm not aware of something.
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.
Ah, I see. I guess then bind
would need storage via the closure, and with
could just store it in a local variable?
Between those techniques is it possible to change _scopesStack
to just an _scope
variable and simplify the logic?
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.
@draffensperger yes I was just referring to the extra closure as I wasn't sure if I'm not missing anything, the rest is fine, just pushed the changes you can have a look, thx
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 the refactoring! I will make a few more comments, but they are more stylistic. I think this is really coming together well!
@@ -0,0 +1,4 @@ | |||
/bin |
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.
Instead of opentelemetry-tracer-web
, what do you think about opentelemetry-web-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.
this name has been already decided in OpenTelem web draft
I don't have strong opinion on that but personally I would keep opentelemetry-tracer-web
, opentelemetry-tracer-node
, opentelemetry-tracer-basic
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.
@mayurkale22 so what do you think about it ?, I'm fine for both, but I didn't propose opentelemetry-tracer-web
although thinking this name to be easier as we can have then the namespace of opentelemetry-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.
I am fine with opentelemetry-tracer-web
, which means we need to rename opentelemetry-basic-tracer
-> opentelemetry-tracer-basic
.
"types": "build/src/index.d.ts", | ||
"repository": "open-telemetry/opentelemetry-js", | ||
"scripts": { | ||
"test": "nyc ts-mocha -p tsconfig.json test/**/**/*.ts", | ||
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.ts' --exclude 'test/index-webpack.ts'", |
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 think we have already excluded test/index-webpack.ts
in .nycrc
. https://github.com/open-telemetry/opentelemetry-js/blob/master/.nycrc#L11
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 I remove it then it doesn't work
"types": "build/src/index.d.ts", | ||
"repository": "open-telemetry/opentelemetry-js", | ||
"scripts": { | ||
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.ts' --exclude 'test/index-webpack.ts'", |
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.
Same comment here about .nycrc
.
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.
Excited for this - great job @obecny on getting this tracer working for the web!
I think the architecture is there, just made a few more comments around types, styling, etc.
/** | ||
* Keeps the reference to current scope | ||
*/ | ||
public _currentScope: any; |
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.
Since we aren't actually doing anything with the scope, can we use unknown
here?
writable: false, | ||
value: target.length, | ||
}); | ||
return contextWrapper as any; |
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 you do contextWrapper as unknown as T
can you remove the any
here and in the const contextWrapper: any =
above?
* @param target | ||
* @param scope | ||
*/ | ||
bind<T>(target: T | any, scope?: unknown): T { |
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.
Would it be possible to make the type of target
only be T
?
* @param fn Callback function | ||
*/ | ||
with<T extends (...args: unknown[]) => ReturnType<T>>( | ||
scope: any, |
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.
Can scope
be unknown
since we aren't doing anything with its contents?
*/ | ||
with<T extends (...args: unknown[]) => ReturnType<T>>( | ||
scope: any, | ||
fn: any |
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.
Can fn
be () => (scope: unknown)
to specify that it will be called with a single argument of the scope 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.
when fn
is a mentioned type then I see ts lint errors,
would the following be fine ?
with<T extends (...args: unknown[]) => ReturnType<T>>(
scope: unknown,
fn: () => ReturnType<T>
): ReturnType<T> {
if (typeof scope === 'undefined' || scope === null) {
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, that's great!
import { StackScopeManager } from '../src'; | ||
|
||
// @ts-ignore | ||
if (typeof global.window === 'undefined') { |
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 would think the opentelemetry-tracer-web
tests would only ever be meant to run in a browser context, not a Node context.
Given that, could you modify package.json
to make it so that running the Node tests is just a no-op so we can remove this? As a minor other pro to that approach, the CI build will save a bit of time from not having to run these tests in Node.
return done(); | ||
}); | ||
|
||
it('should finally restore an old scope', done => { |
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.
Nice to see this tested and working!
}); | ||
assert.strictEqual(scopeManager.active(), window); | ||
}); | ||
it('should finally restore an old scope when scope is an object', done => { |
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: should we put a newline before this it
block?
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.
💯
Looks like this PR is ready, would you like to include this in the alpha release? |
@mayurkale22 I think yes |
Could you please remove |
or changing the |
Sorry for the confusion. Please ignore the 1st point (I thought it is present in For 2nd point, changing 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.
Happy to have this in the alpha
…e span (open-telemetry#334) * tchannelbridge error logging fixed Signed-off-by: Varsha <[email protected]> * Test added for error logging Signed-off-by: Varsha <[email protected]> * tchannel err test modified Signed-off-by: Varsha <[email protected]>
* feat(tracer-web): adding tracer web * feat(basic-tracer): adding karma tests * feat(tracer-web): adding some example for easier debugging in browser - for development purposes * fix: lint * fix: creating base for karma * fix: fixing problem with target for browser, cleanup tests * refactor: moving polyfills for node karma tests to one file * fix: adding missing package * refactor: removing unneeded file * refactor: prefixing privates, cleanup * fix: duplicate package * refactor: aligning tslint with other tslint packages * refactor: cleanups, adding comments for class * fix: linting * fix: type * refactor: generation of id for scope * refactor: removed previous uid for scope as originally it was meant to be used with async which is not the case anymore * chore: adding test for restoring scope * fix: lint * refactor: simplifying the stack scope manager * chore: updating readme with basic example * chore: fixes after merge * fix: updating test to accept greater or equal - fails on browser * refactor: moving example for web tracer * refactor: removing WebTracerConfig to use BasicTracerConfig which changed recently * chore: updating types * chore: spacing * chore: removing mocha tests for tracer-web * chore: updating types and linting * chore: updating packages after merge * chore: adding nyc report for karma tests for browser * chore: updating lerna script to run coverage for browsers * feat(tracer-web): bump version to 0.1.0
Co-authored-by: Bartlomiej Obecny <[email protected]> Co-authored-by: Valentin Marchaud <[email protected]>
Which problem is this PR solving?
Short description of the changes
I have also added some helper example to be able to debug things directly in browser - this might stay for future use or I can remove. It was helpful during development to better understand what is happening.