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

Custom UserTiming Payloads #3

Closed
nicjansma opened this Issue Apr 8, 2015 · 21 comments

Comments

Projects
None yet
4 participants
@nicjansma

nicjansma commented Apr 8, 2015

Forked from w3c/performance-timeline#2 and relevant for w3c/performance-timeline#9 (comment).

It might be useful to be able to specify a payload that would be stored along with your marks or measures.

The new method signature might be:

void mark(DOMString markName, object data);

data could be whatever, say {taskid: 1, widgets: 100}

This data attribute would be persisted along with that mark and attached to the PerformanceMark interface:

interface PerformanceMark : PerformanceEntry {
    object data;
};

(I don't know the correct IDL).

data could also be a DOMString. If so, I think there should be a specified string length limit, so people don't just JSON.stringify() a huge payload into it (which would be a lot less efficient than a native object).

This may help with w3c/performance-timeline#9, which discusses a "group" concept useful for any PerformanceEntry in the PerformanceTimeline. @igrigorik mentions being able to query for any entries that share an attribute via a new getEntries(DOMString attributeName, optional DOMString attributeValue) method, but a lone data attribute on UserTiming might not then be useful, unless its properties were automatically attached to the base PerformanceEntry (which I'm sure would introduce headaches with naming collisions).

@stevesouders

This comment has been minimized.

Show comment
Hide comment
@stevesouders

stevesouders Apr 26, 2015

I need a way to specify a mark's startTime that is NOT the current time.

It's counter-intuitive to create a mark (which results in a startTime value of the current time), and then setting the startTime value to my desired time value. I also don't think that's a preferred approach. (We probably want to make startTime readonly, altho it's currently not in Chrome.)

Would there be a way to set the startTime property with the approach suggested here? If not, how can I do that?

Possible use case, I want a user timing mark for when the first image request above-the-fold started. Or I want a mark based on a comparison, such as the max of duration of three images.

stevesouders commented Apr 26, 2015

I need a way to specify a mark's startTime that is NOT the current time.

It's counter-intuitive to create a mark (which results in a startTime value of the current time), and then setting the startTime value to my desired time value. I also don't think that's a preferred approach. (We probably want to make startTime readonly, altho it's currently not in Chrome.)

Would there be a way to set the startTime property with the approach suggested here? If not, how can I do that?

Possible use case, I want a user timing mark for when the first image request above-the-fold started. Or I want a mark based on a comparison, such as the max of duration of three images.

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Apr 27, 2015

Member

@stevesouders I agree, we shouldn't muck with UA generated startTime.

Based on @nicjansma's suggestion, you'd pass in a data object, which would then be accessible as a property on the event.. e.g:

mark("custom-mark", {time: ..., other: ...});

var m = performance.getEntriesByName("custom-mark");
console.log(m.data.time);
...
Member

igrigorik commented Apr 27, 2015

@stevesouders I agree, we shouldn't muck with UA generated startTime.

Based on @nicjansma's suggestion, you'd pass in a data object, which would then be accessible as a property on the event.. e.g:

mark("custom-mark", {time: ..., other: ...});

var m = performance.getEntriesByName("custom-mark");
console.log(m.data.time);
...
@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jun 18, 2015

Bringing over my use cases from w3c/performance-timeline#2, I like this proposal, but would want to extend this to measures as well. Unfortunately the nature of .measure taking optional parameters would mean a little weirdness with its signature if we adopted just another argument. Forcing the argument to be an object helps though.

@stevesouders Wouldn't your use case be better suited to .measure? e.g.:

performance.mark('desiredStart');
...
performance.measure('customMark', 'desiredStart');

All in all, I am for this change and would like to see it move forward.

eliperelman commented Jun 18, 2015

Bringing over my use cases from w3c/performance-timeline#2, I like this proposal, but would want to extend this to measures as well. Unfortunately the nature of .measure taking optional parameters would mean a little weirdness with its signature if we adopted just another argument. Forcing the argument to be an object helps though.

@stevesouders Wouldn't your use case be better suited to .measure? e.g.:

performance.mark('desiredStart');
...
performance.measure('customMark', 'desiredStart');

All in all, I am for this change and would like to see it move forward.

@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jun 26, 2015

@igrigorik there is very strong support for having this feature for a wide variety of use cases here at Mozilla, and I think we should make an effort on moving this forward from a standards perspective. We are going to experiment with this to see how well it works and will report back if there are any caveats.

eliperelman commented Jun 26, 2015

@igrigorik there is very strong support for having this feature for a wide variety of use cases here at Mozilla, and I think we should make an effort on moving this forward from a standards perspective. We are going to experiment with this to see how well it works and will report back if there are any caveats.

@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jun 26, 2015

Specifically what we want to experiment with:

void mark(DOMString markName, optional dictionary payload);
void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark, optional dictionary payload);

eliperelman commented Jun 26, 2015

Specifically what we want to experiment with:

void mark(DOMString markName, optional dictionary payload);
void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark, optional dictionary payload);
@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Jun 26, 2015

Member

@eliperelman crazy thought...

  • window.markPayloads = {} -> stores "markName -> payload"
  • patch mark and measure to automatically write and pull from both places, or.. define new methods

That's all we're looking for here, right?

Member

igrigorik commented Jun 26, 2015

@eliperelman crazy thought...

  • window.markPayloads = {} -> stores "markName -> payload"
  • patch mark and measure to automatically write and pull from both places, or.. define new methods

That's all we're looking for here, right?

@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jun 26, 2015

<bikshed>
Not sure about exposing another global on window, but maybe window.performance.payloads?
</bikeshed>

Not sure about the implementation at the moment, but for my specific use cases, I would need access to the payload data from Gecko as well as JS, so a function may be in order.

eliperelman commented Jun 26, 2015

<bikshed>
Not sure about exposing another global on window, but maybe window.performance.payloads?
</bikeshed>

Not sure about the implementation at the moment, but for my specific use cases, I would need access to the payload data from Gecko as well as JS, so a function may be in order.

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Jun 26, 2015

Member

@eliperelman I'm thinking something even simpler... Namely, I'm questioning if this needs to be specced and implemented at platform level at all, since you can easily implement this yourself.

Member

igrigorik commented Jun 26, 2015

@eliperelman I'm thinking something even simpler... Namely, I'm questioning if this needs to be specced and implemented at platform level at all, since you can easily implement this yourself.

@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jun 26, 2015

Yes, I suppose that could be done. Going back to @stevesouders case, it would probably mean wrapping the performance.mark call altogether:

var mark = function(markName, payload) {
  mark.cache[markName] = merge(payload, {
    time: Date.now()
  });
  performance.mark(markName);
};
mark.cache = {};

eliperelman commented Jun 26, 2015

Yes, I suppose that could be done. Going back to @stevesouders case, it would probably mean wrapping the performance.mark call altogether:

var mark = function(markName, payload) {
  mark.cache[markName] = merge(payload, {
    time: Date.now()
  });
  performance.mark(markName);
};
mark.cache = {};
@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jun 26, 2015

I guess the nice-to-have deals with the retrieving of data, either on the platform side, or the getEntries et al side. You would need to patch getEntries to also fetch the payload associated with the mark.

Given enough demand though for this type of use case, does it make sense for everyone to duplicate this feature in their apps, or bake it into the web? That's a question that I do not yet know the answer to, @igrigorik. It would be nice to know how people feel about it.

eliperelman commented Jun 26, 2015

I guess the nice-to-have deals with the retrieving of data, either on the platform side, or the getEntries et al side. You would need to patch getEntries to also fetch the payload associated with the mark.

Given enough demand though for this type of use case, does it make sense for everyone to duplicate this feature in their apps, or bake it into the web? That's a question that I do not yet know the answer to, @igrigorik. It would be nice to know how people feel about it.

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Jun 26, 2015

Member

Yep, that's exactly what I was thinking... except, with performance.now() instead of Date :)

We can document the pattern and apps can adopt it on as-needed basis. If we find lots of folks using it out in the wild, we can then revisit and see if offering a 'native' version offers any benefit. Yay/nay?

Member

igrigorik commented Jun 26, 2015

Yep, that's exactly what I was thinking... except, with performance.now() instead of Date :)

We can document the pattern and apps can adopt it on as-needed basis. If we find lots of folks using it out in the wild, we can then revisit and see if offering a 'native' version offers any benefit. Yay/nay?

@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jun 26, 2015

@igrigorik the performance.mark call will already have the startTime, so adding another performance.now() time is redundant. I used Date.now() just as an example of some sample data, in case you wanted to know the Unix epoch of the mark in addition to the High-Res time. :)

eliperelman commented Jun 26, 2015

@igrigorik the performance.mark call will already have the startTime, so adding another performance.now() time is redundant. I used Date.now() just as an example of some sample data, in case you wanted to know the Unix epoch of the mark in addition to the High-Res time. :)

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Jun 26, 2015

Member

Re .now(): gotcha, makes sense.

Good point on getEntries; same applies to observers. It definitely adds some friction, but it's not clear to me if this is such a big deal.. In fact, let me make the argument in other direction: if some script attaches a custom payload to an event then another observer that's not related to that script doesn't know what to do with that payload, so its overhead to propagate it to everyone; observers that care about these extra payloads can just check the appropriate place for additional data?

Re, duplication: I think the fact that you can implement it on your own and in few dozen lines of well-formatted code makes it less appealing from platform perspective. Also, if/once patterns of use emerge, we reserve the right to jump in and offer it as part of standard API? :)

Member

igrigorik commented Jun 26, 2015

Re .now(): gotcha, makes sense.

Good point on getEntries; same applies to observers. It definitely adds some friction, but it's not clear to me if this is such a big deal.. In fact, let me make the argument in other direction: if some script attaches a custom payload to an event then another observer that's not related to that script doesn't know what to do with that payload, so its overhead to propagate it to everyone; observers that care about these extra payloads can just check the appropriate place for additional data?

Re, duplication: I think the fact that you can implement it on your own and in few dozen lines of well-formatted code makes it less appealing from platform perspective. Also, if/once patterns of use emerge, we reserve the right to jump in and offer it as part of standard API? :)

@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jun 26, 2015

@igrigorik I'd like to point out how we would like to use such a feature in Firefox OS for one of the use cases (there are more), and get your thoughts on it.

When we create performance marks, Gecko has a reference to the origin of the document that generated the mark. For example, if we launch our Clock app and create a mark, we get some output in adb logcat:

performance.mark('fullyLoaded');
I/PerformanceTiming( 6118): Performance Entry: clock.gaiamobile.org|mark|fullyLoaded|1074.739956|0.000000|1434771805380

In our home screen, we also create a performance mark, but have a convention that basically overrides the context of the mark. This lets fake our way to cross-page marks:

// In home screen
performance.mark('appLaunch@clock.gaiamobile.org');

Of course this is hacky. :) It would be preferred that this wasn't encoded in the mark name:

performance.mark('appLaunch', { context: 'clock.gaiamobile.org' });

There is much more information about the whys and hows surrounding this and I don't want to get deep into this here, but basically we are using conventions as a way to get to out-of-page marking, so maybe this is an indication we have something else missing.

eliperelman commented Jun 26, 2015

@igrigorik I'd like to point out how we would like to use such a feature in Firefox OS for one of the use cases (there are more), and get your thoughts on it.

When we create performance marks, Gecko has a reference to the origin of the document that generated the mark. For example, if we launch our Clock app and create a mark, we get some output in adb logcat:

performance.mark('fullyLoaded');
I/PerformanceTiming( 6118): Performance Entry: clock.gaiamobile.org|mark|fullyLoaded|1074.739956|0.000000|1434771805380

In our home screen, we also create a performance mark, but have a convention that basically overrides the context of the mark. This lets fake our way to cross-page marks:

// In home screen
performance.mark('appLaunch@clock.gaiamobile.org');

Of course this is hacky. :) It would be preferred that this wasn't encoded in the mark name:

performance.mark('appLaunch', { context: 'clock.gaiamobile.org' });

There is much more information about the whys and hows surrounding this and I don't want to get deep into this here, but basically we are using conventions as a way to get to out-of-page marking, so maybe this is an indication we have something else missing.

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Jun 26, 2015

Member

@eliperelman interesting. I'm not familiar with the guts of FFOS, so please bear with me... when you say "launch X app", what does that actually do? Is it a separate document, worker, ...?

It sounds like the underlying primitive you're after is a way to translate timestamps between different contexts? As in, you get a timestamp in A and you want to map it onto B's timeline? If so, have you checked our discussion in w3c/hr-time#6 (comment)? The plan is to add a new translateTime method.. which sounds like something you might be able to leverage.

Member

igrigorik commented Jun 26, 2015

@eliperelman interesting. I'm not familiar with the guts of FFOS, so please bear with me... when you say "launch X app", what does that actually do? Is it a separate document, worker, ...?

It sounds like the underlying primitive you're after is a way to translate timestamps between different contexts? As in, you get a timestamp in A and you want to map it onto B's timeline? If so, have you checked our discussion in w3c/hr-time#6 (comment)? The plan is to add a new translateTime method.. which sounds like something you might be able to leverage.

@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jun 30, 2015

@igrigorik when we launch applications, they are self-contained windows in their own process, similar to tabs with their own process. I haven't really thought about the communication mechanisms to allow mapping of these values in this way. I'll have to think through that some more.

eliperelman commented Jun 30, 2015

@igrigorik when we launch applications, they are self-contained windows in their own process, similar to tabs with their own process. I haven't really thought about the communication mechanisms to allow mapping of these values in this way. I'll have to think through that some more.

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Jul 7, 2015

Member

Are we OK with resolving this as a wontfix?

To recap, I believe the behavior we're after here is easy enough to polyfill (see #3 (comment)) and I don't believe there are any strong benefits in requiring that as a native API. In fact, as a first step, I'd like to see folks experiment with such an API on their own and report back with their findings; if there are gaps that can't be filled in via application code, then I think it would make sense to revisit this discussion once more.

/cc @plehegar @toddreifsteck

Member

igrigorik commented Jul 7, 2015

Are we OK with resolving this as a wontfix?

To recap, I believe the behavior we're after here is easy enough to polyfill (see #3 (comment)) and I don't believe there are any strong benefits in requiring that as a native API. In fact, as a first step, I'd like to see folks experiment with such an API on their own and report back with their findings; if there are gaps that can't be filled in via application code, then I think it would make sense to revisit this discussion once more.

/cc @plehegar @toddreifsteck

@eliperelman

This comment has been minimized.

Show comment
Hide comment
@eliperelman

eliperelman Jul 9, 2015

As much as I don't want to concede on this issue, I feel it's probably necessary until we can flesh out any root holes that the web isn't giving us. I'm fine with wontfixing this.

eliperelman commented Jul 9, 2015

As much as I don't want to concede on this issue, I feel it's probably necessary until we can flesh out any root holes that the web isn't giving us. I'm fine with wontfixing this.

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Jul 9, 2015

Member

@eliperelman great, thanks. @nicjansma does this sound reasonable to you as well?

Member

igrigorik commented Jul 9, 2015

@eliperelman great, thanks. @nicjansma does this sound reasonable to you as well?

@nicjansma

This comment has been minimized.

Show comment
Hide comment
@nicjansma

nicjansma Jul 9, 2015

Yes, I haven't come up with any scenario that wouldn't work with a polyfill / 100% JS solution.

nicjansma commented Jul 9, 2015

Yes, I haven't come up with any scenario that wouldn't work with a polyfill / 100% JS solution.

@igrigorik

This comment has been minimized.

Show comment
Hide comment
@igrigorik

igrigorik Jul 9, 2015

Member

Great, thanks guys. Closing, feel free to reopen if anything new comes up.

Member

igrigorik commented Jul 9, 2015

Great, thanks guys. Closing, feel free to reopen if anything new comes up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment