Skip to content

Commit

Permalink
feat: improve error debugging (#515)
Browse files Browse the repository at this point in the history
  • Loading branch information
alimd authored Dec 18, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 18fc6e5 + 1c3d7a1 commit 31a59bd
Showing 21 changed files with 114 additions and 77 deletions.
16 changes: 8 additions & 8 deletions core/fetch/src/fetch.ts
Original file line number Diff line number Diff line change
@@ -37,28 +37,28 @@ export async function serviceRequest<TData = Record<string, unknown>, TMeta = Re
response = await fetch(options);
}
catch (err) {
logger.error('serviceRequest', (err as Error).message || 'fetch_failed', (err as Error).stack || err, options);
throw new Error('fetch_failed');
logger.error('serviceRequest', (err as Error).message || 'fetch_failed', err, options);
throw err;
}

let responseText: string;
try {
responseText = await response.text();
}
catch (err) {
logger.error('serviceRequest', 'invalid_response', (err as Error).message || err, {
logger.error('serviceRequest', 'invalid_response', err, {
response,
});
throw new Error('invalid_response');
throw err;
}

let responseJson: AlwatrServiceResponse<TData, TMeta>;
try {
responseJson = JSON.parse(responseText);
}
catch (err) {
logger.error('serviceRequest', 'invalid_json', (err as Error).message || err, {responseText});
throw new Error('invalid_json');
logger.error('serviceRequest', 'invalid_json', err, {responseText});
throw err;
}

if (responseJson.ok !== true) {
@@ -114,7 +114,7 @@ function _processOptions(options: FetchOptions): Required<FetchOptions> {
options.removeDuplicate ??= 'never';

if (options.cacheStrategy !== 'network_only' && cacheSupported !== true) {
logger.accident('fetch', 'fetch_cache_strategy_ignore', 'Cache storage not support in this browser', {
logger.incident('fetch', 'fetch_cache_strategy_ignore', 'Cache storage not support in this browser', {
cacheSupported,
});
options.cacheStrategy = 'network_only';
@@ -289,7 +289,7 @@ async function _handleRetryPattern(options: Required<FetchOptions>): Promise<Res
throw new Error('fetch_server_error');
}
catch (err) {
logger.accident('fetch', 'fetch_failed_retry', (err as Error)?.message ?? 'fetch failed and retry', err);
logger.accident('fetch', 'fetch_failed_retry', (err as Error)?.message || 'fetch failed and retry', err);

await _wait(options.retryDelay);

7 changes: 5 additions & 2 deletions core/i18n/src/i18n.ts
Original file line number Diff line number Diff line change
@@ -139,7 +139,7 @@ l10n.resourceChangeSignal.setProvider(async (locale) => {
}
}
catch (err) {
logger.error('resourceProvider', 'fetch_failed', (err as Error).stack || err, {locale, url});
logger.error('resourceProvider', 'fetch_failed', err, {locale, url});
// TODO: user error signal.
return;
}
@@ -204,7 +204,10 @@ function localize(key?: string | null): string | null {

const localized = l10n.resource[key];
if (localized == null) {
logger.accident('localize', 'l10n_key_not_found', 'Key not defined in the localization resource', {key});
logger.accident('localize', 'l10n_key_not_found', 'Key not defined in the localization resource', {
key,
local: l10n.resource?._code,
});
return `{${key}}`;
}

6 changes: 3 additions & 3 deletions core/logger/README.md
Original file line number Diff line number Diff line change
@@ -148,7 +148,7 @@ try {
...
}
catch (err) {
logger.error('myMethod', 'error_code', (err as Error).stack || err, {a: 1, b: 2});
logger.error('myMethod', 'error_code', err, {a: 1, b: 2});
}
```

@@ -175,7 +175,7 @@ Best practices to catch the error and log it:
```ts
// Unhandled promise rejection (just log it)
failPromiseTest().catch((err) =>
logger.error('myMethod', (err as Error).message || 'error_code', (err as Error).stack || err)
logger.error('myMethod', (err as Error).message || 'error_code', err)
);

// Handled promise rejection
@@ -186,7 +186,7 @@ try {
'myMethod',
'error_code',
'failPromiseTest failed!, ' + (err as Error).message,
(err as Error).stack || err
err
);
// do something to handle the error...
}
4 changes: 2 additions & 2 deletions core/logger/src/type.ts
Original file line number Diff line number Diff line change
@@ -127,11 +127,11 @@ export interface AlwatrLogger {
* ...
* }
* catch (err) {
* logger.error('myMethod', 'error_code', (err as Error).stack || err, {a: 1, b: 2});
* logger.error('myMethod', 'error_code', err, {a: 1, b: 2});
* }
* ```
*/
error(method: string, code: string, errorStack: string | unknown, ...args: unknown[]): void;
error(method: string, code: string, ...args: unknown[]): void;

/**
* Simple `console.debug` with styled scope.
33 changes: 19 additions & 14 deletions core/nano-server/src/nano-server.ts
Original file line number Diff line number Diff line change
@@ -165,20 +165,18 @@ export class AlwatrNanoServer {
}

protected _errorListener(err: NodeJS.ErrnoException): void {
this._logger.accident('server.onError', 'http_server_catch_error', 'HTTP server catch an error', {
errCode: err.code,
errMessage: err.message,
});

if (err.code === 'EADDRINUSE') {
this._logger.logOther('Address in use, retrying...');
this._logger.incident('server.onError', 'address_in_use', 'Address in use, retrying...', err);
setTimeout(() => {
this.httpServer.close();
this.httpServer.listen(this._config.port, this._config.host, () => {
this._logger.logOther(`listening on ${this._config.host}:${this._config.port}`);
});
}, 2000);
}
else {
this._logger.error('server.onError', 'http_server_catch_error', err.message || 'HTTP server catch an error', err);
}
}

protected _clientErrorListener(err: NodeJS.ErrnoException, socket: Duplex): void {
@@ -247,7 +245,8 @@ export class AlwatrNanoServer {
this._notFoundListener(connection);
}
}
catch (err) {
catch (_err) {
const err = _err as Error;
this._logger.error('handleRequest', 'http_server_middleware_error', err, {
method: connection.method,
route,
@@ -256,6 +255,11 @@ export class AlwatrNanoServer {
ok: false,
statusCode: 500,
errorCode: 'http_server_middleware_error',
meta: {
name: err?.name,
message: err?.message,
cause: err?.cause,
},
});
}
}
@@ -325,8 +329,14 @@ export class AlwatrConnection {
this._logger.logMethodArgs('reply', {ok: content.ok, statusCode: content.statusCode});

if (this.serverResponse.headersSent) {
this._logger.accident('reply', 'http_header_sent', 'Response headers already sent');
return;
this._logger.error('reply', 'http_header_sent', 'Response headers already sent');
if (content.ok === false) return; // prevent loop.
throw new Error('http_header_sent');
}

if (!this._logger.debug && !content.ok && content.meta) {
// clear debug info from client for security reasons.
delete content.meta;
}

let buffer: Buffer;
@@ -393,11 +403,6 @@ export class AlwatrConnection {
* ```
*/
async getBody(): Promise<string | null> {
// method must be POST or PUT
if (!(this.method === 'POST' || this.method === 'PUT' || this.method === 'PATCH')) {
return null;
}

let body = '';

this.incomingMessage.on('data', (chunk: unknown) => {
6 changes: 3 additions & 3 deletions core/signal/src/core.ts
Original file line number Diff line number Diff line change
@@ -68,14 +68,14 @@ function __callListeners<SignalName extends keyof AlwatrSignals>(signal: SignalO
const ret = listener.callback(signal.value);
if (ret instanceof Promise) {
ret.catch((err) =>
logger.error('_callListeners', 'call_listener_failed', (err as Error).stack || err, {
logger.error('_callListeners', 'call_listener_failed', err, {
signalName: signal.name,
}),
);
}
}
catch (err) {
logger.error('_callListeners', 'call_listener_failed', (err as Error).stack || err, {
logger.error('_callListeners', 'call_listener_failed', err, {
signalName: signal.name,
});
}
@@ -129,7 +129,7 @@ export function _addSignalListener<SignalName extends keyof AlwatrSignals>(
listenerCallback(signal.value);
}
catch (err) {
logger.error('_addSignalListener', 'call_signal_callback_failed', (err as Error).stack || err, {
logger.error('_addSignalListener', 'call_signal_callback_failed', err, {
signalName: signal.name,
});
}
2 changes: 1 addition & 1 deletion core/storage-client/README.md
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ try {
if ((err as Error)?.message === 'document_not_found') {
console.log('user_5000 id not found!');
} else {
console.err((err as Error)?.message ?? err);
console.error(err);
}
}
```
19 changes: 10 additions & 9 deletions core/storage-client/src/storage-client.ts
Original file line number Diff line number Diff line change
@@ -61,7 +61,11 @@ alwatrRegisteredList.push({
* }
*/
export class AlwatrStorageClient<DocumentType extends AlwatrDocumentObject = AlwatrDocumentObject> {
protected _logger = createLogger('alwatr-storage-client:' + this.config.name, undefined, this.config.debug);
protected _logger = createLogger(
'alwatr-storage-client' + (this.config.name == null ? '' : ':' + this.config.name),
undefined,
this.config.debug,
);

/**
* Default fetch options.
@@ -98,7 +102,7 @@ export class AlwatrStorageClient<DocumentType extends AlwatrDocumentObject = Alw
* console.log('user_5000 id not found!');
* }
* else {
* console.err((err as Error)?.message ?? err);
* console.err((err as Error)?.message || err);
* }
* }
* ```
@@ -107,7 +111,7 @@ export class AlwatrStorageClient<DocumentType extends AlwatrDocumentObject = Alw
documentId: string,
storage: string | undefined = this.config.name,
): Promise<T> {
this._logger.logMethodArgs('get', {documentId});
this._logger.logMethodArgs('get', {storage, documentId});
if (storage == null) throw new Error('storage_not_defined');

const responseJson = await serviceRequest<T>({
@@ -118,10 +122,7 @@ export class AlwatrStorageClient<DocumentType extends AlwatrDocumentObject = Alw
},
});

if (
typeof responseJson.data !== 'object' ||
typeof responseJson.data.id !== 'string'
) {
if (typeof responseJson.data !== 'object' || typeof responseJson.data.id !== 'string') {
this._logger.error('get', 'invalid_response_data', {responseJson});
throw new Error('invalid_response_data');
}
@@ -142,7 +143,7 @@ export class AlwatrStorageClient<DocumentType extends AlwatrDocumentObject = Alw
* ```
*/
async has(documentId: string, storage: string | undefined = this.config.name): Promise<boolean> {
this._logger.logMethodArgs('has', {documentId});
this._logger.logMethodArgs('has', {storage, documentId});
if (storage == null) throw new Error('storage_not_defined');

const responseJson = await serviceRequest<{has: boolean | unknown}>({
@@ -212,7 +213,7 @@ export class AlwatrStorageClient<DocumentType extends AlwatrDocumentObject = Alw
* ```
*/
async delete(documentId: string, storage: string | undefined = this.config.name): Promise<void> {
this._logger.logMethodArgs('delete', {documentId});
this._logger.logMethodArgs('delete', {storage, documentId});
if (storage == null) throw new Error('storage_not_defined');

await serviceRequest({
2 changes: 1 addition & 1 deletion demo/logger/index.ts
Original file line number Diff line number Diff line change
@@ -34,5 +34,5 @@ try {
throw new Error('my_error_message');
}
catch (err) {
logger1.error('myMethod', 'error_code', (err as Error).stack || err, {a: 1, b: 2});
logger1.error('myMethod', 'error_code', err, {a: 1, b: 2});
}
5 changes: 1 addition & 4 deletions demo/storage-client/benchmark.ts
Original file line number Diff line number Diff line change
@@ -12,10 +12,7 @@ interface User extends AlwatrDocumentObject {
token: string;
}

const token = process.env.TOKEN;
if (token == null) {
throw new Error('token_not_defined');
}
const token = process.env.TOKEN ?? 'YOUR_SECRET_TOKEN';

const db = new AlwatrStorageClient<User>({
name: 'junk-data',
2 changes: 1 addition & 1 deletion services/comment/src/lib/storage.ts
Original file line number Diff line number Diff line change
@@ -3,4 +3,4 @@ import {AlwatrStorageClient} from '@alwatr/storage-client';
import {config} from '../config.js';
import {Message} from './type.js';

export const storage = new AlwatrStorageClient<Message>(config.storage);
export const storageClient = new AlwatrStorageClient<Message>(config.storage);
14 changes: 10 additions & 4 deletions services/comment/src/route/patch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {config, logger} from '../config.js';
import {nanoServer} from '../lib/nano-server.js';
import {storage} from '../lib/storage.js';
import {storageClient} from '../lib/storage.js';
import {Message} from '../lib/type.js';

import type {AlwatrConnection} from '@alwatr/nano-server';
@@ -29,15 +29,21 @@ async function setComment(connection: AlwatrConnection): Promise<void> {
try {
connection.reply({
ok: true,
data: await storage.set(bodyJson, params.storage),
data: await storageClient.set(bodyJson, params.storage),
});
}
catch (err) {
logger.error('setComment', (err as Error).message ?? 'storage_error', (err as Error).stack ?? err);
catch (_err) {
const err = _err as Error;
logger.error('setComment', err.message || 'storage_error', err);
connection.reply({
ok: false,
statusCode: 500,
errorCode: 'storage_error',
meta: {
name: err.name,
message: err.message,
cause: err.cause,
},
});
}
}
14 changes: 10 additions & 4 deletions services/comment/src/route/storage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {config, logger} from '../config.js';
import {nanoServer} from '../lib/nano-server.js';
import {storage} from '../lib/storage.js';
import {storageClient} from '../lib/storage.js';

import type {AlwatrConnection} from '@alwatr/nano-server';

@@ -16,14 +16,20 @@ async function getStorage(connection: AlwatrConnection): Promise<void> {
if (params == null) return;

try {
connection.reply(await storage.getStorage(params.name));
connection.reply(await storageClient.getStorage(params.name));
}
catch (err) {
logger.error('getStorage', (err as Error).message ?? 'storage_error', (err as Error).stack ?? err);
catch (_err) {
const err = _err as Error;
logger.error('getStorage', err.message || 'storage_error', err);
connection.reply({
ok: false,
statusCode: 500,
errorCode: 'storage_error',
meta: {
name: err.name,
message: err.message,
cause: err.cause,
},
});
}
}
2 changes: 1 addition & 1 deletion services/flight-crawler/src/crawl.ts
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ export async function crawlAllJobs(): Promise<void> {
await storageClient.set(job);
}
catch (err) {
logger.error('crawlAllJobs', 's', (err as Error).stack);
logger.error('crawlAllJobs', 'crawling_failed', err);
}
}
}
Loading

0 comments on commit 31a59bd

Please sign in to comment.