Pole raske mõista, et mõned inimesed näevad vaeva vigade käsitlemisega ja mõnedel on see isegi täiesti puudu. Vigade nõuetekohane käsitlemine tähendab mitte ainult arendusaja lühendamist vigade ja vigade hõlpsa leidmisega, vaid ka tugeva koodibaasi väljatöötamist suuremahuliste rakenduste jaoks.
Eriti, Node.js arendajad leiavad mõnikord, et töötavad mitmesuguste vigade käsitlemisel mitte eriti puhta koodiga, rakendades valesti kõikjal sama loogikat nende lahendamiseks. Nad muudkui küsivad endalt 'Kas Node.js on vigade käsitlemisel halb?' või Kui ei, siis kuidas nendega hakkama saada? ' Minu vastus neile on 'Ei, Node.js pole üldse halb. See sõltub meist arendajatest. '
Siin on üks minu lemmiklahendusi selleks.
Kõigepealt on vaja Node.js vigadest selgelt aru saada. Üldiselt on Node.js vead jagatud kahte erinevasse kategooriasse: töövead ja programmeerija vead .
Seda silmas pidades ei tohiks teil olla probleemi nende kahe veakategooria eristamisel: töövead on rakenduse loomulik osa ja programmeerija vead on arendajate põhjustatud vead. Järgnev loogiline küsimus on: 'Miks on kasulik jagada need kahte kategooriasse ja nendega tegeleda?'
Ilma vigade selge mõistmiseta võib tekkida tunne, et tõrke ilmnemisel soovite rakenduse taaskäivitada. Kas on mõttekas rakendust taaskäivitada vigade „Faili ei leitud” tõttu, kui tuhanded kasutajad seda rakendust naudivad? Absoluutselt mitte.
Aga kuidas on programmeerija vigadega? Kas on mõttekas rakendust töös hoida, kui ilmub tundmatu viga, mis võib põhjustada rakenduses ootamatu lumepalli efekti? Jällegi kindlasti mitte!
Eeldades, et teil on async JavaScripti ja Node.js-iga mõningane kogemus, võib teil olla tagasilöökide kasutamisel vigadega tegelemisel puudusi. Need sunnivad teid vigu kontrollima pesastunud vigadeni, põhjustades kurikuulsaid tagasihelistamisprobleeme, mis raskendavad koodivoo järgimist.
Lubaduste või async / await kasutamine on tagasihelistuste hea asendaja. Async / await tüüpiline koodivoog näeb välja järgmine:
const doAsyncJobs = async () => { try { const result1 = await job1(); const result2 = await job2(result1); const result3 = await job3(result2); return await job4(result3); } catch (error) { console.error(error); } finally { await anywayDoThisJob(); } }
Node.js sisseehitatud tõrkeobjekti kasutamine on hea tava, kuna see sisaldab intuitiivset ja selget teavet selliste vigade kohta nagu StackTrace, millest enamik arendajaid sõltub vea juurte jälgimiseks. Ja täiendavad sisukad atribuudid, nagu HTTP olekukood ja kirjeldus, laiendades klassi Error, muudavad selle informatiivsemaks.
class BaseError extends Error { public readonly name: string; public readonly httpCode: HttpStatusCode; public readonly isOperational: boolean; constructor(name: string, httpCode: HttpStatusCode, description: string, isOperational: boolean) { super(description); Object.setPrototypeOf(this, new.target.prototype); this.name = name; this.httpCode = httpCode; this.isOperational = isOperational; Error.captureStackTrace(this); } } //free to extend the BaseError class APIError extends BaseError { constructor(name, httpCode = HttpStatusCode.INTERNAL_SERVER, isOperational = true, description = 'internal server error') { super(name, httpCode, isOperational, description); } }
Rakendasin lihtsuse huvides ainult mõned HTTP-olekukoodid, kuid võite hiljem lisada rohkem.
export enum HttpStatusCode { OK = 200, BAD_REQUEST = 400, NOT_FOUND = 404, INTERNAL_SERVER = 500, }
BaseErrorit ega APIErrorit pole vaja laiendada, kuid tavaliste vigade korral on seda võimalik laiendada vastavalt teie vajadustele ja isiklikele eelistustele.
class HTTP400Error extends BaseError { constructor(description = 'bad request') { super('NOT FOUND', HttpStatusCode.BAD_REQUEST, true, description); } }
Kuidas siis seda kasutada? Viska see lihtsalt sisse:
... const user = await User.getUserById(1); if (user === null) throw new APIError( 'NOT FOUND', HttpStatusCode.NOT_FOUND, true, 'detailed explanation' );
Nüüd oleme valmis ehitama oma Node.js tõrketöötlussüsteemi põhikomponendi: tsentraliseeritud veakäsitluskomponendi.
Tavaliselt on mõistlik ehitada tsentraliseeritud veakäsitluskomponent, et vältida võimalikke koodide dubleerimisi vigade käsitlemisel. Veakäsitluse komponent vastutab püütud vigade mõistetavuse eest, näiteks süsteemiadministraatoritele teadete saatmisega (vajaduse korral), sündmuste edastamisega jälgimisteenusele nagu Sentry.io ja nende sisselogimisega.
Siin on põhiline töövoog vigade käsitlemiseks:
Mõnes koodi osas tabatakse vigu, mis viivad üle veakäsitluse vahevara.
... try { userService.addNewUser(req.body).then((newUser: User) => { res.status(200).json(newUser); }).catch((error: Error) => { next(error) }); } catch (error) { next(error); } ...
Veakäsitluse vahevara on hea koht veatüüpide eristamiseks ja tsentraliseeritud veakäsitluskomponenti saatmiseks. Teades vigade käsitlemise põhitõdesid Express.js vahevara aitaks kindlasti.
app.use(async (err: Error, req: Request, res: Response, next: NextFunction) => { if (!errorHandler.isTrustedError(err)) { next(err); } await errorHandler.handleError(err); });
Nüüdseks võib ette kujutada, milline peaks olema tsentraliseeritud komponent, sest oleme selle funktsioone juba kasutanud. Pidage meeles, et selle rakendamine sõltub täielikult teie otsusest, kuid see võib välja näha järgmine:
class ErrorHandler { public async handleError(err: Error): Promise { await logger.error( 'Error message from the centralized error-handling component', err, ); await sendMailToAdminIfCritical(); await sendEventsToSentry(); } public isTrustedError(error: Error) { if (error instanceof BaseError) { return error.isOperational; } return false; } } export const errorHandler = new ErrorHandler();
Mõnikord raskendab vaikekontrolli “console.log” väljund vigade jälgimist. Pigem võiks olla palju parem vigade vormindamine, et arendajad saaksid probleemidest kiiresti aru ja veenduksid, et need on lahendatud.
Üldiselt säästab see arendajate aega, muutes vigade jälgimise ja nende käsitlemise lihtsamaks, suurendades nende nähtavust. Hea otsus on kasutada kohandatavat loggerit winston või morgan .
Siin on kohandatud Winstoni loger:
const customLevels = { levels: { trace: 5, debug: 4, info: 3, warn: 2, error: 1, fatal: 0, }, colors: { trace: 'white', debug: 'green', info: 'green', warn: 'yellow', error: 'red', fatal: 'red', }, }; const formatter = winston.format.combine( winston.format.colorize(), winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.splat(), winston.format.printf((info) => { const { timestamp, level, message, ...meta } = info; return `${timestamp} [${level}]: ${message} ${ Object.keys(meta).length ? JSON.stringify(meta, null, 2) : '' }`; }), ); class Logger { private logger: winston.Logger; constructor() { const prodTransport = new winston.transports.File({ filename: 'logs/error.log', level: 'error', }); const transport = new winston.transports.Console({ format: formatter, }); this.logger = winston.createLogger({ level: isDevEnvironment() ? 'trace' : 'error', levels: customLevels.levels, transports: [isDevEnvironment() ? transport : prodTransport], }); winston.addColors(customLevels.colors); } trace(msg: any, meta?: any) { this.logger.log('trace', msg, meta); } debug(msg: any, meta?: any) { this.logger.debug(msg, meta); } info(msg: any, meta?: any) { this.logger.info(msg, meta); } warn(msg: any, meta?: any) { this.logger.warn(msg, meta); } error(msg: any, meta?: any) { this.logger.error(msg, meta); } fatal(msg: any, meta?: any) { this.logger.log('fatal', msg, meta); } } export const logger = new Logger();
Põhimõtteliselt pakub see mitmel erineval tasemel logimist vormindatud, selgete värvidega ja erinevatesse väljundkandjatesse logimist vastavalt käitamise keskkonnale. Hea on see, et saate logisid vaadata ja päringuid teha, kasutades Winstoni sisseehitatud API-sid. Lisaks saate vormindatud logifailide analüüsimiseks logianalüüsi tööriista abil saada rakenduse kohta rohkem kasulikku teavet. See on vinge, kas pole?
Siiani arutasime enamasti operatsioonivigade käsitlemist. Kuidas oleks programmeerija vigadega? Parim viis nende vigadega toimetulemiseks on kohe kokku kukkuda ja taaskäivitada automaatse taaskäivitajaga nagu PM2 - põhjuseks on see, et programmeerija vead on ootamatud, kuna need on tegelikud vead, mis võivad põhjustada rakenduse valesse olekusse sattumise ja käitumise ootamatul viisil.
process.on('uncaughtException', (error: Error) => { errorHandler.handleError(error); if (!errorHandler.isTrustedError(error)) { process.exit(1); } });
Lõpuks mainin käsitlemata lubaduste tagasilükkamist ja erandeid.
Võib juhtuda, et kulutate Node.js / Expressi rakenduste kallal lubadustega tegelemisele palju aega. Kui unustate keeldumisi käsitseda, pole raske näha hoiatavaid sõnumeid lubaduste tagasilükkamisest.
Hoiatusteated ei tee suurt midagi peale logimise, kuid hea tava on kasutada korralikku varuvarianti ja tellida process.on(‘unhandledRejection’, callback)
Tüüpiline tõrkeotsingu voog võib välja näha järgmine:
// somewhere in the code ... User.getUserById(1).then((firstUser) => { if (firstUser.isSleeping === false) throw new Error('He is not sleeping!'); }); ... // get the unhandled rejection and throw it to another fallback handler we already have. process.on('unhandledRejection', (reason: Error, promise: Promise) => { throw reason; }); process.on('uncaughtException', (error: Error) => { errorHandler.handleError(error); if (!errorHandler.isTrustedError(error)) { process.exit(1); } });
Kui kõik on öeldud ja tehtud, peaksite mõistma, et veakäsitlus ei ole valikuline lisa, vaid pigem rakenduse oluline osa nii arendusetapis kui ka tootmises.
Node.js ühe komponendi vigade käsitlemise strateegia tagab arendajatele väärtusliku aja kokkuhoiu ning puhta ja hooldatava koodi kirjutamise, vältides koodi dubleerimist ja vea konteksti puudumist.
Loodetavasti meeldis teile seda artiklit lugeda ja leidsite, et arutletud tõrkeotsingu töövoog ja juurutamine on a kindel koodibaas Node.js-is .
Nõuetekohane veakäsitlus muudab rakendused töökindlaks, mille tulemuseks on suurepärane kasutuskogemus ja parem tootlikkus.
Lubaduste või asünkroonimise / ootamise kasutamine, tsentraliseeritud komponendis vigade käsitlemine, püüdmata erandite käsitlemine.
Veakäsitlus on kõigi rakenduste kohustuslik osa. See hoiab ära rakenduste veaohtlikkuse ja säästab väärtuslikku arendusaega.
Tellides protsessi.on (‘käsitsemata tagasilükkamine’), protsess.on (‘püüdmataException’)
Ei see ei ole. See sõltub arendajatest ja viisist, kuidas nad nendega hakkama saavad.