egg框架的異常返回頁(yè)面的生成

說(shuō)來(lái)慚愧,業(yè)務(wù)到現(xiàn)在都是單點(diǎn)服務(wù)翔烁,且無(wú)上報(bào)匾灶,對(duì)于測(cè)試發(fā)現(xiàn)的問(wèn)題只有同步庫(kù)查日志(測(cè)試環(huán)境日志都很難找),萌生了異常上報(bào)的想法租漂,說(shuō)到異常阶女,不由地說(shuō)egg的錯(cuò)誤頁(yè)面,多么精致哩治,今天就來(lái)分析一下頁(yè)面的產(chǎn)生

egg-onerror的處理

egg框架在egg包中調(diào)用了egg-onerror包秃踩,并且開(kāi)啟了這個(gè)plugin。進(jìn)入看一下egg-onerror/app.js

const onerror = require('koa-onerror');
...
module.exports = app => {
  // 1
  const config = app.config.onerror;
  const viewTemplate = fs.readFileSync(config.templatePath, 'utf8');
  
  // 2
  app.on('error', (err, ctx) => {
    ctx = ctx || app.createAnonymousContext();
    if (config.appErrorFilter && !config.appErrorFilter(err, ctx))
      return;

    const status = detectStatus(err);
    // 5xx
    if (status >= 500) {
      try {
        ctx.logger.error(err);
      } catch (ex) {
        app.logger.error(err);
        app.logger.error(ex);
      }
      return;
    }

    // 4xx
    try {
      ctx.logger.warn(err);
    } catch (ex) {
      app.logger.warn(err);
      app.logger.error(ex);
    }
  }
  
  // 3
  const errorOptions = {
    ...
  }
  // support customize error response
  [ 'all', 'html', 'json', 'text', 'js' ].forEach(type => {
    if (config[type]) errorOptions[type] = config[type];
  });
  
  // 4
  onerror(app, errorOptions);
}

可以看到這貨接下來(lái)調(diào)用的是koa-onerror, 我們先停留在這層做個(gè)簡(jiǎn)單分析业筏。這層到底做了什么憔杨?簡(jiǎn)要概括如下

  1. 讀取預(yù)配置,默認(rèn)讀取./lib/onerror_page.mustache的模板蒜胖,并且通過(guò)config可以自定義配置信息
  2. 監(jiān)聽(tīng)對(duì)error事件監(jiān)聽(tīng)消别,并且執(zhí)行動(dòng)作。
  3. 構(gòu)建errorOptions對(duì)象台谢,完成對(duì) accepts寻狂、html、json朋沮、js的處理handler方法蛇券。并且注入到config中,參考注釋方法
  4. 傳遞調(diào)用koa-onerror方法

關(guān)于app的onerror事件,是對(duì)狀態(tài)碼判斷并輸送至指定日志對(duì)象上纠亚,輸出日志作用塘慕。這里是對(duì)處理的一個(gè)截?cái)啵覀兝^續(xù)往下看koa-onerror

koa-onerror的處理

這里的處理全部在app.context.onerror的對(duì)象方法賦值上. 那么問(wèn)題來(lái)了蒂胞,app的app.on('error',func)app.context.onerror有what子區(qū)別图呢。


咳咳咳,先暫停一下骗随,小的有事稟報(bào)...

插曲1:分析 app.on('error',func) 和 app.context.onerror 的淺析

(ps: 這個(gè)時(shí)候我來(lái)沒(méi)看過(guò)koa相關(guān)的東西...)

期初拿到這個(gè)問(wèn)題岳瞭,我腦子里面蹦出來(lái)的幾點(diǎn)念想:

  1. 都知道,egg是個(gè)框架蚊锹,egg框架里面對(duì)于app的引用自然很多,我該怎么找稚瘾。
  2. (猜測(cè)1)指不定onerror就是某個(gè)文件的某個(gè)地方是app.on('xxxx',func)func對(duì)象呢牡昆?(猜測(cè)2)event事件會(huì)不會(huì)是個(gè)截?cái)啵蜕衔念?lèi)似摊欠?

接著往下看丢烘,剛才koa-onerror的代碼某處有一點(diǎn):koa-onerror/index.js

app.context.onerror = function(err) {
  ... 
  this.app.emit('error', err, this);
  ...
}

就是說(shuō)一個(gè)context.onerror是個(gè)方法,另外這行代碼有點(diǎn)刺眼些椒。是方法總有地方會(huì)調(diào)用吧播瞳,其二,為什么會(huì)在一個(gè)onerror單詞emit執(zhí)行了error的event事件免糕,且將自己的err參數(shù)發(fā)過(guò)去了赢乓,明白這是一個(gè)主動(dòng)觸發(fā)。context哪來(lái)的石窑,egg.ctx給的牌芋。egg哪來(lái)的?koa...(說(shuō)npm下載下來(lái)的準(zhǔn)備開(kāi)打了...)
koa的里面有application.js松逊、context.js躺屁、requset\response.js, ...(中間省略幾行我傻不拉幾去找eggApplication的error處理的過(guò)程), 大家都是到koa是怎么調(diào)用的, KOA以下代碼來(lái)自官文,不解釋了经宏。

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);

app.listen對(duì)吧犀暑,OK, 我們看下KOA的koa/application.js

listen(...args) {
  debug('listen');
  const server = http.createServer(this.callback());
  return server.listen(...args);
}

返回調(diào)用在this.callback()上,繼續(xù)追進(jìn)去

callback() {
  ...
  if (!this.listenerCount('error')) this.on('error', this.onerror);
  const handleRequest = (req, res) => {
    const ctx = this.createContext(req, res);
    return this.handleRequest(ctx, fn);
  };
  return handleRequest;
}

handleRequest(ctx, fnMiddleware) {
  const res = ctx.res;
  res.statusCode = 404;
  const onerror = err => ctx.onerror(err);
  const handleResponse = () => respond(ctx);
  onFinished(res, onerror);
  return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}

event方法listenerCount就不解釋了烁兰,返回監(jiān)聽(tīng)這個(gè)事件的數(shù)量耐亏,Application是繼承Emitter的,因此這里判斷沪斟,如果沒(méi)有error事件監(jiān)聽(tīng)苹熏,就丟給了this.onerror,也就是application.onerror(注意這里對(duì)象是app)。這里還不是context的轨域,很容易理解啊袱耽,在其位謀其政,app的onerror是處理平臺(tái)級(jí)的錯(cuò)誤信息干发。繼續(xù)看handleRequest是不是就找到了context.onerror了朱巨,當(dāng)然走到這步了就繼續(xù)看看koa.context對(duì)onerror的實(shí)現(xiàn)

onerror(err) {
    // don't do anything if there is no error.
    // this allows you to pass `this.onerror`
    // to node-style callbacks.
    if (null == err) return;

    if (!(err instanceof Error)) err = new Error(util.format('non-error thrown: %j', err));

    let headerSent = false;
    if (this.headerSent || !this.writable) {
      headerSent = err.headerSent = true;
    }

    // delegate
    this.app.emit('error', err, this);

    // nothing we can do here other
    // than delegate to the app-level
    // handler and log.
    if (headerSent) {
      return;
    }

    const { res } = this;

    // first unset all headers
    /* istanbul ignore else */
    if (typeof res.getHeaderNames === 'function') {
      res.getHeaderNames().forEach(name => res.removeHeader(name));
    } else {
      res._headers = {}; // Node < 7.7
    }

    // then set those specified
    this.set(err.headers);

    // force text/plain
    this.type = 'text';

    // ENOENT support
    if ('ENOENT' == err.code) err.status = 404;

    // default to 500
    if ('number' != typeof err.status || !statuses[err.status]) err.status = 500;

    // respond
    const code = statuses[err.status];
    const msg = err.expose ? err.message : code;
    this.status = err.status;
    this.length = Buffer.byteLength(msg);
    res.end(msg);
  }

注釋還是很清楚,是不是有點(diǎn)似曾相識(shí)的味道枉长,就是同樣會(huì)emit帶著err到app的onerror這個(gè)娘家上冀续。綜上。必峰。就了解context.onerror和app的’error‘event事件了吧洪唐。總的來(lái)說(shuō)就是如果沒(méi)有復(fù)寫(xiě)context下吼蚁,在請(qǐng)求時(shí)發(fā)生的異常會(huì)拋到app.on('error',func)上凭需。也算是了解一遭了,小插曲結(jié)束肝匆。
繼續(xù)上面的話題粒蜈,繼續(xù)探討koa-onerror的處理


可以看到koa.context已經(jīng)對(duì)異常的處理了,并且最后會(huì)發(fā)送出去旗国,koa-onerror之所以對(duì)onerror復(fù)寫(xiě)我想就是因?yàn)橐皩?duì)癥下藥”枯怖,判斷究竟是json\html還是其他的接收,是不是很眼熟能曾,對(duì)度硝,就是文章開(kāi)頭的代碼尾部的處理,而我們能看到精美的egg錯(cuò)誤頁(yè)面也是在這里寿冕。這里通過(guò)對(duì)錯(cuò)誤的截取渲染相關(guān)頁(yè)面模板塘淑,并且輸送出去。

至此蚂斤,egg的異常結(jié)果頁(yè)面就產(chǎn)生啦存捺。

對(duì)于異常的解析,拆分曙蒸,未完待續(xù)捌治。。先改bug了纽窟。肖油。稍后打卡更新

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市臂港,隨后出現(xiàn)的幾起案子森枪,更是在濱河造成了極大的恐慌视搏,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件县袱,死亡現(xiàn)場(chǎng)離奇詭異浑娜,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)式散,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)筋遭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人暴拄,你說(shuō)我怎么就攤上這事漓滔。” “怎么了乖篷?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵响驴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我撕蔼,道長(zhǎng)豁鲤,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任罕邀,我火速辦了婚禮,結(jié)果婚禮上养距,老公的妹妹穿的比我還像新娘诉探。我一直安慰自己,他們只是感情好棍厌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布肾胯。 她就那樣靜靜地躺著,像睡著了一般耘纱。 火紅的嫁衣襯著肌膚如雪敬肚。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天束析,我揣著相機(jī)與錄音艳馒,去河邊找鬼。 笑死员寇,一個(gè)胖子當(dāng)著我的面吹牛弄慰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝶锋,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼陆爽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了扳缕?” 一聲冷哼從身側(cè)響起慌闭,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤别威,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后驴剔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體省古,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年仔拟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衫樊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡利花,死狀恐怖科侈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炒事,我是刑警寧澤臀栈,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站挠乳,受9級(jí)特大地震影響权薯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睡扬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一盟蚣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卖怜,春花似錦屎开、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至甩鳄,卻和暖如春逞度,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妙啃。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工档泽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人揖赴。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓茁瘦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親储笑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甜熔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容