用 Mongoose 插件記錄Node.js API 日誌

做者:Shailesh Shekhawat

翻譯:瘋狂的技術宅html

原文:https://www.freecodecamp.org/...前端

未經容許嚴禁轉載node

clipboard.png

本教程須要事先了解 mongoose 對象關係映射(ORM)技術

介紹

隨着程序的增加,日誌記錄成爲跟蹤全部內容的關鍵部分。它對於調試目的尤其重要。程序員

如今已經有了 npm 的日誌記錄模塊。這些模塊能夠將日誌存儲在不一樣格式或級別的文件中。咱們將使用流行的ORM Mongoose 討論 Node.js Express 程序中的 API 日誌記錄。面試

那麼如何建立一個 Mongoose 插件,以更清潔的方式爲你進行記錄並簡化 API 日誌?正則表達式

Mongoose 中的插件是什麼?

在 Mongoose 中,模式是可插入的。插件就像一個函數,你能夠在模式中使用它,並在模式實例上一次次地重用。mongodb

Mongoose 還提供全局插件,你能夠將其用於全部模式。例如咱們將會編寫一個插件,它將建立兩個 jsonsdiff 並寫入 mongodb數據庫

步驟1:建立基本日誌模式模型

讓咱們建立一個具備如下六個屬性的基本日誌模式:express

  • Action: 按照它的名稱,這是 API 的一個動做過程,不管是 createupdatedelete仍是別的什麼。
  • Category: API 類別。例如醫生和患者。它更像是一個階級。
  • CreatedBy:正在使用或調用 API 的用戶。
  • Message: 你能夠在此處包含你想要顯示的任何類型的消息,這些消息在調試過程當中有意義或有幫助。
  • Diff: 這是主要屬性,它是兩個 JSONdiff

若是你但願對本身的應用程序有意義,能夠添加更多字段,也能夠根據須要更改和升級架構。
這是咱們的模型:models/log.jsnpm

const mongoose = require('mongoose')
const Schema = mongoose.Schema
const { ObjectId } = Schema

const LogSchema = new Schema({
  action: { type: String, required: true },
  category: { type: String, required: true },
  createdBy: { type: ObjectId, ref: 'Account', required: true },
  message: { type: String, required: true },
  diff: { type: Schema.Types.Mixed },
},{
  timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
})

LogSchema.index({ action: 1, category: 1 })

module.exports = mongoose.model('Log', LogSchema)

步驟2:編寫一個函數來得到 2 個 JSON 之間的差別

因此下一步是你須要一個可重用的函數,它將動態建立兩個 JSON 的 diff

咱們稱之爲 diff.js

const _ = require('lodash')

exports.getDiff = (curr, prev) => {
  function changes(object, base) {
    return _.transform(object, (result, value, key) => {
      if (!_.isEqual(value, base[key]))
        result[key] = (_.isObject(value) && _.isObject(base[key])) ?                 changes(value, base[key]) : value
    })
 }
 return changes(curr, prev)
}

我使用了 lodash,這是一個提供相同功能的受歡迎的庫。

讓咱們分解上面的函數,看看發生了什麼:

  • _.transform: 它是數組 .reduce 的替代品。它會迭代你對象的 keysvalues。它提供了一個 accumulator,是第一個參數。 result 是累加器,是可變的。
  • _.isEqual: 在兩個值之間進行深度比較,以肯定它們是否相等。
isEqual:此方法支持比較數組、數組緩衝區、布爾值、日期對象、錯誤對象、映射、數字、對象、正則表達式、集合、字符串、符號和類型化數組。對象經過它們本身的方法比較,而不是經過繼承的、可枚舉的屬性進行比較。函數和 DOM 節點則進行嚴格相等的比較,即便用 ===

這裏咱們迭代每一個對象的屬性和值,並將它與舊對象進行比較。

若是當前對象的 value 不等於前一個對象中相同屬性的值:base[key] 若是該值是對象自己,咱們遞歸調用函數changes 直到它獲得一個值,它最終將做爲 result[key]=value 存儲在 result 中。

步驟3:建立一個插件用來 diff 並將其保存到數據庫

如今咱們須要跟蹤數據庫中的前一個 document 並在保存到 mongodb 以前建立一個 diff

const _ = require('lodash')
const LogSchema = require('../models/log')
const { getDiff } = require('../utils/diff')

const plugin = function (schema) {
  schema.post('init', doc => {
    doc._original = doc.toObject({transform: false})
  })
  schema.pre('save', function (next) {
    if (this.isNew) {
      next()
    }else {
      this._diff = getDiff(this, this._original)
      next()
    }
})

  schema.methods.log = function (data)  {
    data.diff = {
      before: this._original,
      after: this._diff,
    }
    return LogSchema.create(data)
  }
}

module.exports = plugin

在 Mongoose 中,有不一樣的鉤子可用。如今咱們須要使用架構上可用的 initsave 方法。

this.isNew():若是你正在建立新文檔,那麼只需返回 next()中間件。

schema.post('init')toObject()中:

doc._original = doc.toObject({transform: false})

Mongoose Model 繼承自 document,它有一個 toObject() 方法。它將 document 轉換爲 Object()transform:false是爲了避免容許轉換返回對象。

步驟4:用法 - 如何在express.js API中使用

在你的主server.jsapp.js中:

初始化全局 plugin 以便它可用於全部模式。你還能夠經過在架構模型中初始化它來將其用於特定架構。

const mongoose = require('mongoose')

mongoose.plugin(require('./app/utils/diff-plugin'))

這是 user 更新 API 的基本示例:

const User = require('../models/user')

exports.updateUser = (req, res, next) => {
  return User.findById(req.params.id)
    .then(user => {
        if (!user)
           throw new Error('Target user does not exist. Failed to update.')
       const { name } = req.body
       if (name) user.name = name
       return user.save()
     })
     .then(result => {
       res.json(result)
       return result
     })
     .catch(next)
     .then(user => {
         if (user && typeof user.log === 'function') { 
            const data = {
              action: 'update-user',
              category: 'users',
              createdBy: req.user.id,
              message: 'Updated user name',
         }
         return user.log(data)
     }
     }).catch(err => {
         console.log('Caught error while logging: ', err)
       })
}

結論

在本教程中,你學習瞭如何建立 Mongoose 插件並用它來記錄 API 中的 changes。你可使用插件執行更多操做來構建健壯的 Node.js 程序。


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索