行为日志

行为日志(logger)

在诸多 CMS 系统中,权限被分配给用户后,那么就代表这一用户拥有某个权限的绝对控制权。但是有些权限又比较危险,比如删除所有图书这个权限,它很可能直接清除了系统里面的所有图书数据,这显然是一个危险的操作。那么这么危险的操作,如果发生了,作为管理员你该怎么办了。很简单,找到该用户,并禁用该用户,随后联系开发者,恢复数据。

logger 主要用来解决这一类的问题,当然你可以将记录任何你觉得敏感的操作,比如某用户访问了某私密数据。接下来,我们来实操一下 logger 的使用。

我们修改get_book这个视图函数为:

  1. from lin.log import Logger
  2. # 省略诸多代码
  3. @book_api.route('/<id>', methods=['GET'])
  4. @Logger(template='某用户查询了一本图书') # 推送的消息
  5. def get_book(id):
  6. book = Book.query.filter_by(id=id).first()
  7. if book is None:
  8. raise NotFound(msg='没有找到相关书籍')
  9. return jsonify(book)

接下来,当有任何用户请求这个 API 时,均会在数据库中写入一条日志信息。该日志信息的数据模型的定义在lin.core中,对应的数据表名为lin_log

但有时,某用户查询了一本图书这样的信息未免显得太过于单薄,它无法很好的向前端说明更多的信息。因此 Lin 提供了一个简单的模板语法,你可以在template这个参数中,写入一些变量,如{user.nickname}查询了一本图书,请记住每一个{}中就可以写入一个变量,user.nickname就表示当前用户的昵称。如下:

  1. from lin.log import Logger
  2. from lin import route_meta, group_required, login_required
  3. # 省略诸多代码
  4. @book_api.route('/<id>', methods=['GET'])
  5. @Logger(template='{user.nickname}查询了一本图书') # 推送的消息
  6. @login_required # 必须,如果用户不登陆,就没有user这个实例
  7. def get_book(id):
  8. book = Book.query.filter_by(id=id).first()
  9. if book is None:
  10. raise NotFound(msg='没有找到相关书籍')
  11. return jsonify(book)

此时,你每请求一次这个 API,它就会在数据中写下下面类似的信息(请注意,这个 API 现在请求必须登陆)。

  1. +----+---------------------+---------------------+---------+-----------+-------------+--------+------------+-----------+
  2. | id | message | time | user_id | user_name | status_code | method | path | authority |
  3. +----+---------------------+---------------------+---------+-----------+-------------+--------+------------+-----------+
  4. | 1 | pedro查询了一本图书 | 2018-10-24 18:11:40 | 4 | pedro | 200 | GET | /v1/book/1 | |
  5. +----+---------------------+---------------------+---------+-----------+-------------+--------+------------+-----------+
  6. 1 row in set (0.13 sec)

WARNING

如果你此时请求,发现没有找到相关书籍的异常时,请不要惊慌,因为在前面的教程中,我们确实通过软删除删除了所有的图书数据所以此时你有两种选择:一、前往数据库将 delete_time 置为 NULL。二、在 filter_by 函数中,将 soft 参数置为 False,如Book.query.filter_by(soft=False,id=id).first()

我们的模板语法,不仅仅可以在其中嵌入user这个实例,你还可以嵌入 Flask Response 的任何属性,如:response.status,还有 Flask Request 的诸多属性,如request.url

name说明类型
useruser_model 实例object
responseFlask Response 实例object
requestFlask Request 实例object

这一切取决于你的需求,关于 user 的所有属性,请阅读 user_model 中的所有属性,response 和 request 的所有属性,请阅读Response行为日志 - 图1Request行为日志 - 图2