FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: Flexible Error/Exception handling (#5929) · NativeScript/NativeScript@3dc3a41 · GitHub

Commit 3dc3a41

Browse files
Alexander Vakrilov
authored
feat: Flexible Error/Exception handling (#5929)
* feat: trace.error() implementation * refactor: Based on PR review * chore: adding error-handling guide * docs: fix typos
1 parent a75505f commit 3dc3a41

6 files changed

Lines changed: 164 additions & 11 deletions

File tree

‎CONTRIBUTING.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,12 @@ git checkout -b <my-fix-branch> master
6161

6262
4. The fun part! Make your code changes. Make sure you:
6363
- Follow the [code conventions guide](CodingConvention.md).
64+
- Follow the [guide on handling errors and exceptions](HandlingErrors.md).
6465
- Write unit tests for your fix or feature. Check out [writing unit tests guide](WritingUnitTests.md).
6566

6667
5. Before you submit your PR:
6768
- Rebase your changes to the latest master: `git pull --rebase upstream master`.
68-
- Ensure all unit test are green for Android and iOS. Check [running unit tests](DevelopmentWorkflow.md#running-unit-tests).
69+
- Ensure all unit test are green for Android and iOS. Check [running unit tests](DevelopmentWorkflow.md#running-unit-tests).
6970
- Ensure your changes pass tslint validation. (run `npm run tslint` in the root of the repo).
7071

7172
6. Push your fork. If you have rebased you might have to use force-push your branch:

‎HandlingErrors.md‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Handling Errors in NativeScript Core Modules
2+
3+
One big difference between web-app and NativeScript app is that throwing an `Error` in NativeScript app causes the app to **crash**. Such crashes can be a serious problem for a production applications as they hurt the application credibility and drive away customers.
4+
5+
We want to provide the application developers with the flexibility to handle errors differently in *development* and *production* modes.
6+
7+
## Using the Trace Module
8+
The `tns-core-modules/trace` utility module provides a good way to streamline error logging and handling throughout the framework. It gives application developers a way to define custom `TraceWriter`s and `ErrorHandler`s for their apps and even specify different sets of those to be used in during development and in production.
9+
10+
Here are the guidelines how to use this when contributing to core-modules or creating your own plugins.
11+
12+
### Use `trace.write()`
13+
Use trace.write() with the appropriate type to log non critical errors.
14+
15+
>Note: For the `error` message level all loggers will be notified unconditionally, for all other levels (`log`,`info`,`warn`), tracing should be enabled and the corresponding categories should be added.
16+
17+
### Use `trace.error()`
18+
Using `trace.error()` gives the user of you API for a [flexible way of handling the errors](https://github.com/NativeScript/NativeScript/issues/5914).
19+
20+
Use the `error()` when an error has occurred which compromises the stability of the app. The default `ErrorHandler` provided in the `trace` module will throw the error which will include the stack trace and information useful for debugging during development. Application developers can handle this error using a custom `ErrorHandler` in production and decide if they should trigger a crash, send an error report, try to recover the application in other way or combination of those.
21+
22+
After calling `trace.error()` consider just returning from the function you are currently in without completing.
23+
24+
There are cases when code execution jumps between native code (ex. Android/iOS SDKs) and JavaScript trough callbacks. In those cases it is most difficult to determine if an error (ex. expected argument is `undefined` or current state of components is invalid) is critical or not. Although, it seems that error is unrecoverable, it might be the case that the callback is called when the app has gone to the background or trough activity/window that is not longer visible. So just reporting the error with `write()` or `error()` is a good option in such cases.
25+
26+
27+
## Throw the Error directly in code
28+
Avoid throwing errors directly, especially in code that is not directly called from application developers (for example in properties set in markup/CSS or in callbacks called form native code). This will cause a crash and usually it will be hard for users of the code to `try/catch` and handle the error. Resort to throwing for cases when:
29+
30+
1. Continuing execution will cause data loss or corruption. Compromising future runs of the application or persisting corrupt data is even worse than crashing.
31+
2. Obviously misused public APIs (ex. wrong arguments types) which developers will call directly.
32+
33+
## Clearing Legacy Code
34+
Not all the code in the `tns-core-modules` might conform to this guide as it might be written before some of the improvements of the trace modules (ex. `error()`). If you came across to such code you can always [give us a PR](CONTRIBUTING.md) referencing [this issue](https://github.com/NativeScript/NativeScript/issues/5914).
35+
36+

‎tests/app/testRunner.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ allTests["FILE-NAME-RESOLVER"] = fileNameResolverTests;
9999
import * as weakEventsTests from "./ui/core/weak-event-listener/weak-event-listener-tests";
100100
allTests["WEAK-EVENTS"] = weakEventsTests;
101101

102+
import * as traceErrorTests from "./trace/trace-error-tests";
103+
allTests["TRACE-ERROR"] = traceErrorTests;
104+
102105
import * as connectivityTests from "./connectivity/connectivity-tests";
103106
allTests["CONNECTIVITY"] = connectivityTests;
104107

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {
2+
ErrorHandler, getErrorHandler, setErrorHandler, DefaultErrorHandler,
3+
error as traceError
4+
} from "tns-core-modules/trace";
5+
import * as TKUnit from "../TKUnit";
6+
7+
let cachedErrorHandler: ErrorHandler;
8+
export function setUpModule() {
9+
cachedErrorHandler = getErrorHandler();
10+
}
11+
12+
// before each
13+
export function tearDown() {
14+
setErrorHandler(cachedErrorHandler)
15+
}
16+
17+
export function test_DefaultErrorHandler_throws() {
18+
setErrorHandler(new DefaultErrorHandler());
19+
TKUnit.assertThrows(() => {
20+
traceError(new Error("TEST"))
21+
}, "DefaultErrorHandler should throw.", "TEST")
22+
}
23+
24+
export function test_trace_error_should_call_handler() {
25+
let called = false;
26+
setErrorHandler({
27+
handlerError() {
28+
called = true;
29+
}
30+
});
31+
traceError(new Error("TEST"));
32+
33+
TKUnit.assert(called, "trace.error() should call handler")
34+
}
35+
36+
export function test_trace_error_should_create_error_from_string() {
37+
let called = false;
38+
let actualError: Error;
39+
setErrorHandler({
40+
handlerError(error) {
41+
called = true;
42+
actualError = error;
43+
}
44+
});
45+
traceError("TEST");
46+
47+
TKUnit.assert(called, "trace.error() should call handler;")
48+
TKUnit.assert(actualError instanceof Error, "trace.error() wrap string in error")
49+
}
50+
51+
export function test_trace_error_should_pass_errors() {
52+
let called = false;
53+
let testError = new Error("TEST");
54+
let actualError: Error;
55+
56+
setErrorHandler({
57+
handlerError(error) {
58+
called = true;
59+
actualError = error;
60+
61+
}
62+
});
63+
traceError(testError);
64+
65+
TKUnit.assert(called, "trace.error() should call handler;")
66+
TKUnit.assertDeepEqual(actualError, testError)
67+
}

‎tns-core-modules/trace/trace.d.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ export function isCategorySet(category: string): boolean;
6363
*/
6464
export function write(message: any, category: string, type?: number);
6565

66+
/**
67+
* Passes an error to the registered ErrorHandler
68+
* @param error The error to be handled.
69+
*/
70+
export function error(error: string | Error);
6671
/**
6772
* Notifies all the attached listeners for an event that has occurred in the sender object.
6873
* @param object The Object instance that raised the event.
@@ -75,6 +80,10 @@ export function addEventListener(listener: EventListener);
7580

7681
export function removeEventListener(listener: EventListener);
7782

83+
export function getErrorHandler(): ErrorHandler;
84+
85+
export function setErrorHandler(handler: ErrorHandler);
86+
7887
/**
7988
* An enum that defines all predefined categories.
8089
*/
@@ -122,3 +131,14 @@ export interface EventListener {
122131
filter: string;
123132
on(object: Object, name: string, data?: any);
124133
}
134+
135+
/**
136+
* An interface used to for handling trace error
137+
*/
138+
export interface ErrorHandler {
139+
handlerError(error: Error);
140+
}
141+
142+
export class DefaultErrorHandler implements ErrorHandler {
143+
handlerError(error);
144+
}

‎tns-core-modules/trace/trace.ts‎

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import * as definition from ".";
1+
import { EventListener, TraceWriter, ErrorHandler } from ".";
22

33
let enabled = false;
44
let _categories = {};
5-
let _writers: Array<definition.TraceWriter> = [];
6-
let _eventListeners: Array<definition.EventListener> = [];
5+
let _writers: Array<TraceWriter> = [];
6+
let _eventListeners: Array<EventListener> = [];
7+
let _errorHandler: ErrorHandler;
78

89
export function enable() {
910
enabled = true;
@@ -21,11 +22,11 @@ export function isCategorySet(category: string): boolean {
2122
return category in _categories;
2223
}
2324

24-
export function addWriter(writer: definition.TraceWriter) {
25+
export function addWriter(writer: TraceWriter) {
2526
_writers.push(writer);
2627
}
2728

28-
export function removeWriter(writer: definition.TraceWriter) {
29+
export function removeWriter(writer: TraceWriter) {
2930
let index = _writers.indexOf(writer);
3031
if (index >= 0) {
3132
_writers.splice(index, 1);
@@ -79,7 +80,7 @@ export function notifyEvent(object: Object, name: string, data?: any) {
7980
}
8081

8182
let i,
82-
listener: definition.EventListener,
83+
listener: EventListener,
8384
filters: Array<string>;
8485
for (i = 0; i < _eventListeners.length; i++) {
8586
listener = _eventListeners[i];
@@ -96,11 +97,11 @@ export function notifyEvent(object: Object, name: string, data?: any) {
9697
}
9798
}
9899

99-
export function addEventListener(listener: definition.EventListener) {
100+
export function addEventListener(listener: EventListener) {
100101
_eventListeners.push(listener);
101102
}
102103

103-
export function removeEventListener(listener: definition.EventListener) {
104+
export function removeEventListener(listener: EventListener) {
104105
var index = _eventListeners.indexOf(listener);
105106
if (index >= 0) {
106107
_eventListeners.splice(index, 1);
@@ -148,7 +149,7 @@ export module categories {
148149
}
149150
}
150151

151-
class ConsoleWriter implements definition.TraceWriter {
152+
class ConsoleWriter implements TraceWriter {
152153
public write(message: any, category: string, type?: number) {
153154
if (!console) {
154155
return;
@@ -177,6 +178,31 @@ class ConsoleWriter implements definition.TraceWriter {
177178
}
178179
}
179180
}
180-
181181
// register a ConsoleWriter by default
182182
addWriter(new ConsoleWriter());
183+
184+
export class DefaultErrorHandler implements ErrorHandler {
185+
handlerError(error) {
186+
throw error;
187+
}
188+
}
189+
setErrorHandler(new DefaultErrorHandler());
190+
191+
export function getErrorHandler(): ErrorHandler {
192+
return _errorHandler;
193+
}
194+
195+
export function setErrorHandler(handler: ErrorHandler) {
196+
_errorHandler = handler;
197+
}
198+
export function error(error: string | Error) {
199+
if (!_errorHandler) {
200+
return;
201+
}
202+
203+
if (typeof error === "string") {
204+
error = new Error(error);
205+
}
206+
207+
_errorHandler.handlerError(error);
208+
}

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL