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

CPLAT-7721 Add error logging to ErrorBoundary #370

Merged
merged 5 commits into from
Oct 16, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 23 additions & 41 deletions lib/src/component/error_boundary.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,19 @@ class _$ErrorBoundaryProps extends UiProps {

/// The name to use when the component's logger logs an error via [ErrorBoundaryComponent.componentDidCatch].
///
/// Not used if a custom [logger] is specified.
///
/// > Default: 'over_react.ErrorBoundary'
String loggerName;

/// Whether errors caught by this [ErrorBoundary] should be logged using a [Logger].
///
/// > Default: `true`
bool shouldLogErrors;

/// An optional custom logger instance that will be used to log errors caught by
/// this [ErrorBoundary] when [shouldLogErrors] is true.
Logger logger;
}

@State()
Expand Down Expand Up @@ -199,6 +210,7 @@ class ErrorBoundaryComponent<T extends ErrorBoundaryProps, S extends ErrorBounda
Map getDefaultProps() => (newProps()
..identicalErrorFrequencyTolerance = Duration(seconds: 5)
..loggerName = defaultErrorBoundaryLoggerName
..shouldLogErrors = true
);

@override
Expand All @@ -207,33 +219,6 @@ class ErrorBoundaryComponent<T extends ErrorBoundaryProps, S extends ErrorBounda
..showFallbackUIOnError = props.fallbackUIRenderer != null
);

@mustCallSuper
@override
void componentWillMount() {
super.componentWillMount();

_logger = Logger(_getLoggerName());
}

@mustCallSuper
@override
void componentWillUnmount() {
super.componentWillUnmount();

_logger.clearListeners();
}

@mustCallSuper
@override
void componentWillReceiveProps(Map nextProps) {
super.componentWillReceiveProps(nextProps);

final tNextProps = typedPropsFactory(nextProps);
if (tNextProps.loggerName != props.loggerName) {
_logger = Logger(_getLoggerName(tNextProps));
}
}

@mustCallSuper
/*@override*/
S getDerivedStateFromError(_) {
Expand Down Expand Up @@ -334,7 +319,7 @@ class ErrorBoundaryComponent<T extends ErrorBoundaryProps, S extends ErrorBounda
// wrap in a try catch just in case `findDomNode` throws as a result of the
// wrapped react tree rendering a string instead of a composite or dom component.
//
// [3] Log the caught error using the instance Logger.
// [3] Log the caught error using a logger if `props.shouldLogErrors` is true.
// ---------------------------------------------- /\ ----------------------------------------------

String _domAtTimeOfError;
Expand Down Expand Up @@ -428,27 +413,24 @@ class ErrorBoundaryComponent<T extends ErrorBoundaryProps, S extends ErrorBounda
String _getReadableErrorInfo(/*NativeJavascriptObject*/dynamic jsErrorInfo) =>
getProperty(jsErrorInfo, 'componentStack');

/// The logger that logs errors when a component somewhere within the React tree wrapped by
/// this [ErrorBoundary] instance throws within the React lifecycle.
Logger get logger => _logger;
Logger _logger;
/// The value that will be used when creating a [Logger] to log errors from this component.
String get loggerName {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a use-case for making this public? Consumers know which logger/loggerName they passed in; we shouldn't need to expose that info via an API method.

if (props.logger != null) return props.logger.name;
Copy link
Contributor

Choose a reason for hiding this comment

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

Related to the above comment; if this isn't public, this won't be necessary.


String _getLoggerName([T propsMap]) {
propsMap ??= props;
if (propsMap.loggerName != null && propsMap.loggerName.isNotEmpty) return propsMap.loggerName;

return defaultErrorBoundaryLoggerName;
return props.loggerName ?? defaultErrorBoundaryLoggerName;
}

// ----- [3] ----- //
void _logErrorCaughtByErrorBoundary(
/*Error|Exception*/ dynamic error,
/*ReactErrorInfo*/ dynamic info, {
/*ReactErrorInfo*/ String info, {
bool isRecoverable = true,
}) {
if (!props.shouldLogErrors) return;

String message = isRecoverable
? 'An error was caught by an ErrorBoundary'
: 'An unrecoverable error was caught by an ErrorBoundary (the entire react tree had to be unmounted)';
? 'An error was caught by an ErrorBoundary: \nInfo: $info'
: 'An unrecoverable error was caught by an ErrorBoundary (attempting to remount it was unsuccessful): \nInfo: $info';

dynamic stackTrace;
try {
Expand All @@ -457,6 +439,6 @@ class ErrorBoundaryComponent<T extends ErrorBoundaryProps, S extends ErrorBounda
// The error / exception doesn't extend from Error or Exception
}

_logger.severe(message, error, stackTrace);
(props.logger ?? Logger(loggerName)).severe(message, error, stackTrace);
}
}
54 changes: 52 additions & 2 deletions lib/src/component/error_boundary.over_react.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading