-
Notifications
You must be signed in to change notification settings - Fork 733
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
Distributed error reporting: Capture more information about the error and its environment #12382
Distributed error reporting: Capture more information about the error and its environment #12382
Conversation
Build Artifacts
|
6459176
to
da7f8f0
Compare
component: this.vm.$options.name || this.vm.$options._componentTag || 'Unknown Component', | ||
browser: this.getBrowserInfo(), | ||
device: this.getDeviceInfo(), | ||
}, |
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.
context
can be movet to ErrorReport too. We can then do something like the below;
getErrorReport() {
const context = this.getContext();
return {
...
context: {
component: this.vm.$options.name || this.vm.$options._componentTag || 'Unknown Component',
...context,
},
};
}
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 even not even need to define getErrorReport
in this class, instead can just define it in the parent class once, and then here we do:
getContext() {
const context = super.getContext();
return {
...context,
component <etc>...
};
}
context: { | ||
browser: this.getBrowserInfo(), | ||
device: this.getDeviceInfo(), | ||
}, |
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 here https://github.com/learningequality/kolibri/pull/12382/files#r1662911798. Though we would just assign the context
context: this.getContext()
context: { | ||
browser: this.getBrowserInfo(), | ||
device: this.getDeviceInfo(), | ||
}, |
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.
kolibri/core/errorreports/schemas.py
Outdated
"device": { | ||
"type": "object", | ||
"properties": { | ||
"type": {"type": "string"}, # "desktop", "tablet", "mobile" |
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 json schema we can ensure the device is from a predefined list of device types
"url": {"type": "string", "optional": True}, | ||
"method": {"type": "string", "optional": True}, | ||
"headers": {"type": "object", "optional": True}, | ||
"body": {"type": "string", "optional": True}, |
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 properties are optional by default so there is not need to explicitly set them as optional. So you might as well do away with the optional flag?
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 can use the required property that specifies the required fields instead where necessary
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 are other sections in the schema with the same)
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.
@akolson I too had a doubt about this. But this comment suggested otherwise:
kolibri/kolibri/core/auth/models.py
Line 90 in dc7a843
# '"optional":True' is obsolete but needed while we keep using an |
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 the use of the optional
property is specific to a particular version of jsonschema, we'll carry on with using it, but create a follow up issue to to update all jsonschemas as python 2.7 support has been dropped (the main reason the current version of jsonschema is used)
kolibri/core/errorreports/schemas.py
Outdated
"platform": { | ||
"type": "string", | ||
"optional": True, | ||
}, # "windows", "mac", "linux", "android", "ios" |
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 json schema we can ensure the platform is from a predefined list of platforms
@@ -6,13 +6,35 @@ class ErrorReport { | |||
getErrorReport() { | |||
throw new Error('getErrorReport() method must be implemented.'); | |||
} | |||
getDeviceInfo() { | |||
return { | |||
type: /Mobi|Android/i.test(navigator.userAgent) ? 'Mobile' : 'Desktop', |
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 will return the wrong type
if kolibri is used on a tablet for example.
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.
(Just noting that this is quite an unreliable way to check for device type)
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, definitely just relying on the values already defined in browserInfo.js seems like the right way forward here, no need to write new code when we already have robust parsing of the userAgent.
getDeviceInfo() { | ||
return { | ||
type: /Mobi|Android/i.test(navigator.userAgent) ? 'Mobile' : 'Desktop', | ||
platform: navigator.platform, |
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 can use os
in browserInfo.js
getErrorReport() { | ||
return { | ||
error_message: this.e.message, | ||
traceback: this.e.stack, | ||
context: { | ||
component: this.vm.$options.name || this.vm.$options._componentTag || 'Unknown Component', | ||
browser: this.getBrowserInfo(), |
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 can use browser
in browserInfo.js
|
||
|
||
def get_python_version(): | ||
return version.split()[0] |
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 this would only pick the first version of the tuple. See here
@@ -47,25 +54,55 @@ def allow_migrate(self, db, app_label, model_name=None, **hints): | |||
|
|||
|
|||
class ErrorReports(models.Model): |
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 noting that
kolibri/kolibri/core/device/api.py
Line 201 in 2418619
info["installer"] = installation_type() |
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.
Similarly, we should store is_touch_device
to clearly distinguish between touch and non touch devices
kolibri/core/errorreports/tasks.py
Outdated
errors_json = serialize_error_reports_to_json_response(errors) | ||
|
||
requests.post( | ||
join_url(server, "/api/v1/errors/"), |
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 change the resource to /api/v1/errors/report/
for clarity.
device.type = device.type || 'unknown'; | ||
device.model = device.model || 'unknown'; | ||
device.vendor = device.vendor || 'unknown'; | ||
export const deviceWithTouch = { |
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 this would be superfluous!
I think it would be more straight forward to just export device
info only. I dont see the need to bundle it up with the device
info.
I think the below would suffice
export const device = info.device;
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 deviceWithTouch
could be misinterpreted to mean that it represents an object with touch capabilities which is not really the case -- isTouchDevice
is a runtime value.
// this is better than undefined | ||
device.type = device.type || 'unknown'; | ||
device.model = device.model || 'unknown'; | ||
device.vendor = device.vendor || 'unknown'; |
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 not sure there any value add of doing this!
@@ -1,3 +1,5 @@ | |||
import { browser, os, deviceWithTouch } from './browserInfo'; |
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.
Lets import device
and isTouchDevice
instead of deviceWithTouch
here. See https://github.com/learningequality/kolibri/pull/12382/files#r1675763461
browser: browser, | ||
os: os, | ||
device: { | ||
...deviceWithTouch, |
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 deviceWithTouch
, we would have
device: {
...device,
isTouchDevice,
screen: {
...
}
}
"name": {"type": "string", "optional": True}, | ||
"major": {"type": "string", "optional": True}, | ||
"minor": {"type": "string", "optional": True}, | ||
"patch": {"type": "string", "optional": True}, |
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.
These can de defined as a property and referenced instead...
kolibri/core/errorreports/schemas.py
Outdated
"name": {"type": "string", "optional": True}, | ||
"major": {"type": "string", "optional": True}, | ||
"minor": {"type": "string", "optional": True}, | ||
"patch": {"type": "string", "optional": True}, |
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.
For ecample it would mean adding definitions
....
"definitions": {
"versionInfo": {
"type": "object",
"properties": {
"name": {"type": "string", "optional": True},
"major": {"type": "string", "optional": True},
"minor": {"type": "string", "optional": True},
"patch": {"type": "string", "optional": True},
}
}
}
and then use it as below;
"os": {
"$ref": "#/definitions/versionInfo",
},
...
"browser": {
"$ref": "#/definitions/versionInfo",
}
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.
Hi @thesujai! Great work on the changes, The pr is now taking shape! I left a few comments that would be good to implement. Thanks
@@ -64,6 +64,13 @@ export const os = { | |||
patch: osVersion[2], | |||
}; | |||
|
|||
// Device info | |||
export const device = { | |||
type: info.device.type || 'desktop', |
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.
Why are we defaulting to 'desktop'?
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.
The ua-parser library gives undefined for desktop. As according to them desktop
is not an device type.
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.
…d, remove mark_as_reported
b10318b
to
c5eb2ae
Compare
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.
Sorry, I had missed the 500
errors previously, these should not be explicitly raised, and instead should raise a 400 error, ideally with a specific description of why the request was rejected. Will need to update the tests accordingly.
kolibri/core/errorreports/api.py
Outdated
logger.error("Error while saving error report: {}".format(e)) | ||
return Response( | ||
{"error": "Error while saving error report"}, | ||
{"error": "Error while saving error report: {}".format(e)}, | ||
status=status.HTTP_500_INTERNAL_SERVER_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.
Sorry, I had missed this previously - we should never deliberately raise a 500 error, it indicates that something has gone wrong and is unhandled. Instead, this should be a 400 error.
kolibri/core/errorreports/api.py
Outdated
) | ||
return Response( | ||
{"error_id": error.id if error else None}, status=status.HTTP_200_OK | ||
) | ||
except (AttributeError, Exception) as e: | ||
|
||
except (AttributeError, ValidationError, Exception) as e: |
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 shouldn't catch a bare Exception here - AttributeError and ValidationErrors we expect, because we are accessing specific attributes and validation the data.
This is looking good in general - and I think most of the previous request for changes have been addressed, I just noticed one more thing around error handling. |
Updated the PR |
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.
All my comments have been addressed!
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.
The updates and code generally looks cleaner. Thanks @thesujai.
d8a713e
into
learningequality:distributed-error-reporting
Summary
error_from
tocategory
and more.References
Closes #12376
Reviewer guidance
Cannot find a way yo get the node version in frontend
Testing checklist
PR process
Reviewer checklist
yarn
andpip
)