-
Notifications
You must be signed in to change notification settings - Fork 116
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
New: Emit preview metric messages #648
New: Emit preview metric messages #648
Conversation
…ew into Emit-preview_error-messsages
…ew into Emit-preview_error-messsages
…ew into Emit-preview_error-messsages
…ew into Emit-preview_error-messsages
Verified that @JustinHoldstock has signed the CLA. Thanks for the pull request! |
Note that this is unfinished. I want to get some eyes on it before I continue |
src/lib/Preview.js
Outdated
* @param {Error} error - The error that occurred. | ||
* @return {void} | ||
*/ | ||
logPreviewError(error) { |
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.
Maybe different names to differentiate from logPreviewEvent()
since this isn't actually logging anything while the other function is actually making a log 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'll rename both to emitPreviewError()
and emitLoadMetric()
src/lib/Preview.js
Outdated
@@ -867,6 +888,8 @@ class Preview extends EventEmitter { | |||
const { apiHost, queryParams } = this.options; | |||
const fileVersionId = this.getFileOption(this.file.id, FILE_OPTION_FILE_VERSION_ID) || ''; | |||
|
|||
Timer.start(`${LOAD_METRIC.fileInfoTime}_${this.file.id}`); |
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.
Let's use file_${this.file.id}_XXX
since we're using file_${this.file.id}
for the internal file object key. We probably don't need to worry about file versions for now but this will disambiguate if we choose to do so in the future.
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.
DONE!
src/lib/Preview.js
Outdated
|
||
const event = { | ||
event_name: 'preview_load', | ||
value: total, // Sum of all available load times. |
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.
Have you tested whether this value matches the legacy total_load_time?
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.
From the handful of tests I've run, this total is closer to Chrome's Network tab
src/lib/RepStatus.js
Outdated
@@ -80,6 +83,8 @@ class RepStatus extends EventEmitter { | |||
return Promise.resolve(); | |||
} | |||
|
|||
// Using content.url_template to guarantee uniqueness | |||
Timer.start(`${LOAD_METRIC.convertTime}_${this.representation.content.url_template}`); |
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 is going to get started multiple times since when we hit pending
as a status, we call this function again.
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.
Also, we don't really need to key on the url template right? We just care about 'How long did this file spend converting'. Doesn't matter which representation (or does it?)
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've made it so start only starts the timer once. I'm now extracting the file id from the template URL, for a similar tag name to the rest
src/lib/Timer.js
Outdated
Object.keys(this.times).forEach((key) => { | ||
delete this.times[key]; | ||
}); | ||
} |
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.
JSDoc when you get a chance :)
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.
Done
src/lib/Preview.js
Outdated
|
||
// Do nothing if there is nothing worth logging. | ||
const infoTime = Timer.get(`${LOAD_METRIC.fileInfoTime}_${this.file.id}`) || {}; | ||
if (!infoTime.elapsed) { |
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 elapsed be 0 here? e.g. if something is already cached we don't spend any time fetching file info?
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 file info is in the cache already, will the representation asset that it belongs to be loaded into browser cache? If that's the case, we don't want to log how fast the browser can load things from cache
src/lib/Preview.js
Outdated
@@ -163,6 +166,13 @@ class Preview extends EventEmitter { | |||
this.navigateLeft = this.navigateLeft.bind(this); | |||
this.navigateRight = this.navigateRight.bind(this); | |||
this.keydownHandler = this.keydownHandler.bind(this); | |||
|
|||
this.on(PREVIEW_ERROR, (msg) => { |
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.
Arrow function is unnecessary 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.
This section is for debugging purposes only
src/lib/Preview.js
Outdated
@@ -163,6 +166,13 @@ class Preview extends EventEmitter { | |||
this.navigateLeft = this.navigateLeft.bind(this); | |||
this.navigateRight = this.navigateRight.bind(this); | |||
this.keydownHandler = this.keydownHandler.bind(this); | |||
|
|||
this.on(PREVIEW_ERROR, (msg) => { |
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.
Does this mean we need to remove the console.error
from everywhere else in the code base?
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 section is for debugging purposes only
src/lib/Preview.js
Outdated
console.error(msg); | ||
}); | ||
this.on(PREVIEW_METRIC, (msg) => { | ||
console.log(msg); |
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.
Do we always want to log these?
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 section is for debugging purposes only, but yes, we will always want to log PREVIEW_METRICs
src/lib/viewers/BaseViewer.js
Outdated
@@ -260,6 +262,16 @@ class BaseViewer extends EventEmitter { | |||
}, this.loadTimeout); | |||
} | |||
|
|||
/** | |||
* Start the laod timer for fullDocumentLoad event. |
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.
load
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.
done
src/lib/Timer.js
Outdated
} | ||
|
||
reset() { | ||
Object.keys(this.times).forEach((key) => { |
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 just set it back to {}?
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 there ever be a case that you would want to reset only a single timer?
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've decided to just reset the values in the struct to undefined
. Also added case to target a single timer.
src/lib/__tests__/Preview-test.js
Outdated
expect(log.timestamp).to.exist; | ||
expect(log.file_id).to.equal(id); | ||
expect(log.file_version_id).to.exist; | ||
expect(log.content_type).to.exsit; |
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.
typeo?
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 want to know why these tests passed, locally, if this was here :O
src/lib/Preview.js
Outdated
* @private | ||
* @return {Object} Log details for viewer session and current file. | ||
*/ | ||
createLog() { |
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.
to follow Tony's comment below, maybe this becomes createEvent
?
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.
createLogEvent()
? Since it contains relevant logging values?
src/lib/Preview.js
Outdated
* @private | ||
* @return {void} | ||
*/ | ||
logLoadMetrics() { |
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 and the above function both actually emit as opposed to log. How do you feel about emitLoadMetrics
and emitPreviewError
instead?
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.
See above response to Tony's comment. It's exactly that :)
src/lib/Preview.js
Outdated
error: err, | ||
...this.createLog() | ||
}; | ||
|
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.
We could move all of our console.errors here whenever we call logPreview error (or maybe in triggerError), to reduce console.error clutter.
@@ -129,6 +129,7 @@ class MediaBaseViewer extends BaseViewer { | |||
return this.getRepStatus() | |||
.getPromise() | |||
.then(() => { | |||
this.startLoadTimer(); |
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 we move the startLoadTimer to a part of the promise chain in RepStatus.js? We could then
it over there once and avoid doing it everywhere else. Basically if the repstatus is success then we want to start timing the content load.
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 is not meant to track time for RepStatus. This begins the timer for when we setup and send the GET request for the representation. Inside of RepStatus, you'll see a timer related to representation conversion time
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.
Right, but since we want to track from once rep status comes back to success until the file is done loading, why not append to the end of rep status so we only have to add it in one place?
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.
Oh, I see, I misunderstood your Q. We don't want to do that because we don't want to include the time to process the JS between the rep coming back and then making the request for the rep. We ONLY want to be tracking network related times, for that metric.
…ew into Emit-preview_metric-messages
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.
Some tiny nit-picky things but this is AWESOME 🎉
src/lib/Preview.js
Outdated
@@ -882,6 +899,9 @@ class Preview extends EventEmitter { | |||
*/ | |||
handleFileInfoResponse(response) { | |||
let file = response; | |||
// Stop timer for file info time event. |
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.
newline
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.
done
src/lib/Preview.js
Outdated
@@ -1096,7 +1128,6 @@ class Preview extends EventEmitter { | |||
} else { | |||
// Bump up preview count | |||
this.count.success += 1; | |||
|
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.
leave this newline
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.
done
@@ -1195,12 +1226,15 @@ class Preview extends EventEmitter { | |||
|
|||
// Check if hit the retry limit | |||
if (this.retryCount > RETRY_COUNT) { | |||
let errorCode = ERROR_CODE.retriesExceeded; | |||
let errorMessage = __('error_refresh'); | |||
if (err.response && err.response.status === 429) { |
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.
new line
src/lib/Preview.js
Outdated
*/ | ||
emitPreviewError(error) { | ||
const err = error; | ||
// If we haven't supplied a code, then it was thrown by the browser |
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.
newline before 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.
done
src/lib/Timer.js
Outdated
*/ | ||
stop(tag) { | ||
const time = this.get(tag); | ||
// The timer has already been stopped, or hasn't started. Don't stop it again. |
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.
newline
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.
done
src/lib/Timer.js
Outdated
reset(tag) { | ||
if (tag) { | ||
const time = this.get(tag); | ||
// We don't need to clean up nothin' |
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.
newline and maybe cleanup the comment :P
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.
done
src/lib/Timer.js
Outdated
time.end = undefined; | ||
time.elapsed = undefined; | ||
} else { | ||
Object.keys(this.times).forEach((timeTag) => { |
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.
Object.keys(this.times).forEach(this.reset);
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.
boom, done
/* global BoxAnnotations */ | ||
const boxAnnotations = this.options.boxAnnotations || new BoxAnnotations(viewerOptions); | ||
if (!global.BoxAnnotations) { | ||
const error = createPreviewError(ERROR_CODE.annotationsLoadFail); |
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.
😍
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.
🎉
…ew into Emit-preview_metric-messages
…ew into Emit-preview_metric-messages
The intention of this PR is to
preview_metric
eventspreview_metric
event with load related metricsHow to use
Timer Mechanism
To begin timing the load events, we call
Timer.start(tag)
, thetag
being a unique tag for that event. I've been using the name of the event, paired with the file id of the file we're tracking. ie)Timer.start('file_info_time_1234545')
To stop a timer, to get an accurate reading of how long it took, call
Timer.stop(tag)
with the same tag. ie)Timer.stop('file_info_time_1234545')
To get timer values, call
Timer.get(tag)
, and you'll receive an object withstart
,end
, andelapsed
values. If thetag
has been started and not stopped, noend
orelapsed
will exist. If you've never started thetag
, then you will receive either an object withundefined
values ORundefined
.I've been using
Timer.start()
either right before aGET
is made on the URL, or right before the function call that does so. I've been usingTimer.stop()
when the asset is received, parsed, and usable. IE) Forfull_document_load_time
andDocBaseViewer
, I start when we get the PDF loading task (ininitViewer()
) and I stop inside offinishLoading()
, (inPreview.
), when we emit 'load'.Finally, on
Preview.destroy()
orPreview.finishLoading()
(guarantees we log either when the document loads or errored out along the way), we emit the message with the relevant props.Relies of PR #619