Skip to content
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

Merged
merged 42 commits into from
Oct 7, 2019
Merged

Tracer web #334

merged 42 commits into from
Oct 7, 2019

Conversation

obecny
Copy link
Member

@obecny obecny commented Sep 24, 2019

Which problem is this PR solving?

  1. It adds the skeleton for the tracer-web using BasicTracer , fixes Add tracer implementation for the browser #276
  2. It adds karma runner for BasicTracer and fixes them to pass in karma too

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.

@codecov-io
Copy link

codecov-io commented Sep 24, 2019

Codecov Report

Merging #334 into master will decrease coverage by 2.19%.
The diff coverage is 3.36%.

@@            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
Impacted Files Coverage Δ
packages/opentelemetry-tracer-web/src/WebTracer.ts 0% <0%> (ø)
...elemetry-tracer-web/test/StackScopeManager.test.ts 0% <0%> (ø)
...es/opentelemetry-tracer-web/test/WebTracer.test.ts 0% <0%> (ø)
...kages/opentelemetry-tracer-basic/test/Span.test.ts 100% <100%> (ø) ⬆️
...ges/opentelemetry-exporter-zipkin/src/transform.ts 100% <0%> (ø) ⬆️
.../opentelemetry-exporter-zipkin/test/zipkin.test.ts 100% <0%> (ø) ⬆️
...pe-async-hooks/test/AsyncHooksScopeManager.test.ts 100% <0%> (ø) ⬆️
...entelemetry-exporter-zipkin/test/transform.test.ts 100% <0%> (ø) ⬆️
...ackages/opentelemetry-exporter-zipkin/src/utils.ts
... and 4 more

packages/opentelemetry-basic-tracer/package.json Outdated Show resolved Hide resolved
packages/opentelemetry-core/src/platform/index.ts Outdated Show resolved Hide resolved
[![devDependencies][devDependencies-image]][devDependencies-url]
[![Apache License][license-image]][license-image]

This module provides automatic tracing for Node.js applications.
Copy link
Contributor

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';
Copy link
Contributor

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?

Copy link
Member Author

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?

Copy link
Contributor

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 {
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Contributor

@draffensperger draffensperger Sep 30, 2019

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.

Copy link
Member Author

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.

Copy link
Contributor

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

Copy link
Member Author

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.

Copy link
Contributor

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?

Copy link
Member Author

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

Copy link
Contributor

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!

packages/opentelemetry-tracer-web/src/StackScopeManager.ts Outdated Show resolved Hide resolved
packages/opentelemetry-tracer-web/.gitignore Outdated Show resolved Hide resolved
@@ -0,0 +1,4 @@
/bin
Copy link
Member

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?

Copy link
Member Author

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

Copy link
Member Author

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-*

Copy link
Member

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'",
Copy link
Member

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

Copy link
Member Author

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'",
Copy link
Member

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.

packages/opentelemetry-tracer-web/src/StackScopeManager.ts Outdated Show resolved Hide resolved
packages/opentelemetry-tracer-web/src/StackScopeManager.ts Outdated Show resolved Hide resolved
packages/opentelemetry-tracer-web/src/WebTracer.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@draffensperger draffensperger left a 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;
Copy link
Contributor

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;
Copy link
Contributor

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 {
Copy link
Contributor

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,
Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Member Author

@obecny obecny Oct 3, 2019

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) {

Copy link
Contributor

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') {
Copy link
Contributor

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 => {
Copy link
Contributor

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 => {
Copy link
Contributor

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?

Copy link
Contributor

@draffensperger draffensperger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

@mayurkale22
Copy link
Member

Looks like this PR is ready, would you like to include this in the alpha release?

@obecny
Copy link
Member Author

obecny commented Oct 4, 2019

@mayurkale22 I think yes

@mayurkale22
Copy link
Member

Could you please remove "private": true and make sure all the versions (tracer-web, examples, etc) are pointing to 0.1.0?

@obecny
Copy link
Member Author

obecny commented Oct 4, 2019

  1. not sure what you mean by removing "private": true ?
  2. make sure all the versions (tracer-web, examples, etc) are pointing to 0.1.0?
    you mean the dependencies from package.json ?
  "dependencies": {
    "@opentelemetry/core": "^0.1.0",
    "@opentelemetry/scope-base": "^0.1.0",
    "@opentelemetry/types": "^0.1.0",
    "@opentelemetry/tracer-basic": "^0.1.0"
  }

or changing the "version": "0.0.1" to "version": "0.1.0" and then updating examples/tracer-web/package.json to "@opentelemetry/tracer-web": "^0.1.0" ?

@mayurkale22
Copy link
Member

Sorry for the confusion. Please ignore the 1st point (I thought it is present in tracer-web's package.json).

For 2nd point, changing the "version": "0.0.1" to "version": "0.1.0" and then updating examples/tracer-web/package.json to "@opentelemetry/tracer-web": "^0.1.0" is what I expect.

Copy link
Contributor

@danielkhan danielkhan left a 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

@mayurkale22 mayurkale22 merged commit 82b5fad into open-telemetry:master Oct 7, 2019
@obecny obecny deleted the tracer-web branch July 8, 2020 12:16
lukaswelinder pushed a commit to agile-pm/opentelemetry-js that referenced this pull request Jul 24, 2020
…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]>
dyladan pushed a commit to dyladan/opentelemetry-js that referenced this pull request Sep 9, 2022
* 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
pichlermarc pushed a commit to dynatrace-oss-contrib/opentelemetry-js that referenced this pull request Dec 15, 2023
Co-authored-by: Bartlomiej Obecny <[email protected]>
Co-authored-by: Valentin Marchaud <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add tracer implementation for the browser
8 participants