github.com/hejiyun/Nod…
express
- Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。
npm install -g express-generator
- 使用express创建node项目
express NodeExpress

- 启动项目
npm i npm start / nodo ./bin/www
winston
- 安装 nodemon 来监控 node.js 源代码的任何变化和自动重启你的服务器
npm install -g nodemon
- 添加打印日志文件, 安装 winston
npm i winston
- winston使用

- 日志
const levels = {error:0,warn:1,info:2,http:3,verbose:4,debug:5,silly:6}
- 创建日志
const logger = winston.createLogger({
transports:[
new winston.transports.Console(),
new winston.transports.File({filename:'combined.log'})
]
});
- 自定义格式 format, format.combine设置打印日志的格式
format: format.combine(
format.label({
label: path.basename(process.mainModule.filename)
}),
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' })
),
- 修改app.js, 修改error处理逻辑
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var morgan = require('morgan');
var logger = require('./logger')
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(morgan('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use(function(req, res, next) {
next(createError(404));
});
const _errorHandler = (err, req, res, next) => {
logger.error(`${req.method} ${req.originalUrl} ` + err.message)
const errorMsg = err.message
res.status(err.status || 500).json({
code: -1,
success: false,
message: errorMsg,
data: {}
})
}
app.use(_errorHandler)
module.exports = app;
- 在项目的根目录添加一个配置打印日志格式的文件 logger.js,
const { createLogger, format, transports } = require('winston');
const fs = require('fs');
const path = require('path');
const env = process.env.NODE_ENV || 'development';
const logDir = 'log';
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
const filename = path.join(logDir, 'results.log');
const logger = createLogger({
level: env === 'production' ? 'info' : 'debug',
format: format.combine(
format.label({ label: path.basename(process.mainModule.filename) }),
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' })
),
transports: [
new transports.Console({
format: format.combine(
format.colorize(),
format.printf(
info =>
`${info.timestamp} ${info.level} [${info.label}]: ${info.message}`
)
)
}),
new transports.File({
filename,
format: format.combine(
format.printf(
info =>
`${info.timestamp} ${info.level} [${info.label}]: ${info.message}`
)
)
})
]
});
module.exports = logger;
- 测试一下日志是否成功, 随便增加一个域名后缀

连接mysql
- 在根目录下添加config.js。 记录连接数据库的信息, 并且在上传时忽略
const configs = {
mysql: {
host: '127.0.0.1',
port: '3306',
user: 'root',
password: '00000000',
database: 'NodeExpress'
},
log: {
error (message) {
console.log('[knex error]', message)
}
}
}
module.exports = configs
数据库添加,表添加
- 使用navicat数据库管理工具新建数据库 database 名称为: NodeExpress, 并且在库下新建user表
- 右键mysql数据库, 选择新建数据库,填入NodeExpress, utf8, 确认创建。 然后右键NodeExpress, 选择新建表, 创建user, user新增三个字段, id, name, phone.

- 在user中随意添加几条数据

使用 Knex 增删改查数据库
- Knex.js是为 Postgres,MSSQL,MySQL,MariaDB,SQLite3,Oracle 和 Amazon Redshift 设计的 SQL 查询构建器,其设计灵活,便于携带并且使用起来非常有趣。Knex 的主要目标环境是 Node.js,
- 安装knex
npm install -save knex mysql
- 先在根目录增加 .gitignore文件忽略上传, config.js需要忽略保证安全
.DS_Store
.idea
npm-debug.log
yarn-error.log
node_modules
log
config.js
- 添加数据库配置。 在根目录下创建models文件夹, 然后创建 knex.js文件进行数据库配置
const configs = require('../config');
module.exports = require('knex')({
client: 'mysql',
connection: {
host: configs.mysql.host,
port: configs.mysql.port,
user: configs.mysql.user,
password: configs.mysql.password,
database: configs.mysql.database
},
log: {
error (message) {
console.log('[knex error]', message)
}
}
})
- 配置好连接数据库信息的文件后, 还需要添加一个base.js文件, 这个文件里 配置增删改查的逻辑
const knex = require('../models/knex');
class Base{
constructor(props){
this.table = props;
}
all (){
return knex(this.table).select();
}
insert (params){
return knex(this.table).insert(params);
}
update (id, params){
return knex(this.table).where('id', '=', id).update(params);
}
delete (id){
return knex(this.table).where('id', '=', id).del();
}
}
module.exports = Base;
- 配置好了增删改查后, 还需要设计一下用户模型,在models下新增user.js
const Base = require('./base');
class User extends Base {
constructor(props = 'user'){
super(props);
}
}
module.exports = new User();
- 在根目录新建控制器文件夹 controllers,在 controllers 新建 user.js,并设置 showUser 方法
const User = require('../models/user.js');
const userController = {
showUser: async function(req,res,next){
try{
let userData = await User.all()
res.json({
code: 200,
message: "操作成功",
data: userData
})
}catch(e){
res.json({ code: 0, message: "操作失败", data: e })
}
},
addUser: async function(req,res,next){
try{
await User.insert(req.body)
res.json({
code: 200,
message: "操作成功",
data: {}
})
}catch(e){
res.json({ code: 0, message: "操作失败", data: e })
}
},
updateUser: async function(req,res,next){
try{
await User.update(req.body.id, req.body)
res.json({
code: 200,
message: "操作成功",
data: {}
})
}catch(e){
res.json({ code: 0, message: "操作失败", data: e })
}
},
deleteUser: async function(req,res,next){
console.log(req.body, 'zheki')
try{
await User.delete(req.body.id)
res.json({
code: 200,
message: "操作成功",
data: {}
})
}catch(e){
res.json({ code: 0, message: "操作失败", data: e })
}
}
}
module.exports = userController;
- 然后添加请求用户信息的接口,修改路由 routes/index.js,添加获取用户信息的接口
var express = require('express');
var router = express.Router();
const userController = require('../controllers/user');
const multer = require('multer');
const upload = multer({dest:'uploads/'})
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.get('/get_user', userController.showUser);
router.post('/add_user', userController.addUser);
router.post('/update_user', userController.updateUser);
router.post('/delete_user', userController.deleteUser);
module.exports = router;
- 阶段文件展示

- 测试接口是否成功, 有返回表数据即成功
访问一下 http://localhost:3000/get_user 页面反馈

express中get 与post(三种参数类型:简单键值对/json/form-data)请求参数获取, express.urlencoded && bodypaser.urlencoded
- 在低于4.16.0版本的express中, 通常使用body-parser 来设置请求参数类型,以达到在req中添加body的作用
- 在4.16.0+版本的express中,提供了 express.urlencoded,来代替body-parser的作用。 两者的使用方式是一样的
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
var express = require('express');
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
- 配置后,post使用raw-->json格式类型数据 或者简单的键值对格式,即可正常请求操作


- 如果是form-data格式的数据,一般也用于文件上传, 那么需要进一步修改, 先安装multer
npm i multer
- 然后在router.js中, 引入配置multer
const multer = require('multer');
const upload = multer({dest:'uploads/'})
- 再在router.js中添加一个新增用户信息带文件的接口
router.post(
'/add_file',
upload.single('file'),
(req,res,next) => {userController.addFile(req,res,next)}
);
- 添加好后, 需要去controlles, user.js中添加addfile的控制器,并且注意, 需要在存的时候把file改成url形式存到表中,以便于契合实际服务器逻辑(一般文件都会存储在第三方中,表中存的是url)
addFile: async function(req,res,next){
console.log(req.body, req.file)
req.body.file = `http:
try{
await User.insert(req.body)
res.json({
code: 200,
message: "操作成功",
data: {}
})
}catch(e){
res.json({ code: 0, message: "操作失败", data: e })
}
},
- 测试一下是否可用,

- 终端打印结果

- 当然, 在发送请求,需要往user表中新增一个file字段,

查询数据库数据
- 查询使用get请求, 可以直接在地址栏拼接接口地址以查看数据
http://localhost:3000/get_user
向数据库新增一条数据 ,post方法则不能使用地址栏, 通过使用postman进行请求
- 为router.js添加新增add数据方法, 然后在控制器controller.js-》user.js中增加addUser方法
....
router.post(
'/add_user',
userController.addUser);
....
addUser: async function(req,res,next){
try{ await User.insert(req.body)
res.json({ code: 200, message: "操作成功", data: {} })
}catch(e){ res.json({ code: 0, message: "操作失败", data: e }) } },


向数据库更新一条数据
- 为router.js添加新增update数据方法, 然后在控制器controller.js-》user.js中增加updateUser方法
// router.js .... // 更新用户信息 router.post('/update_user', userController.updateUser); // controllerjs ----> user.js .... updateUser: async function(req,res,next){ try{ await User.update(req.body.id, req.body) res.json({ code: 200, message: "操作成功", data: {} }) }catch(e){ res.json({ code: 0, message: "操作失败", data: e }) } },


删除数据库一条数据
- 为router.js添加新增delete数据方法, 然后在控制器controller.js-》user.js中增加deleteUser方法
....
router.post('/delete_user', userController.deleteUser);
....
deleteUser: async function(req,res,next){ console.log(req.body, 'zheki')
try{
await User.delete(req.body.id)
res.json({ code: 200, message: "操作成功", data: {} })
}catch(e){ res.json({ code: 0, message: "操作失败", data: e }) } },


koa2
express 是目前最流行的node.jsweb应用级框架,它是基于es5设计的语法,与新的被称作下一代框架的koa相比,
它实现异步的过程会显得比较臃肿冗余, 而koa则是原团队基于es6的 Generator 语法所设计的,
在node。js支持async await后, koa又基于Promise 和 async 和 await 之上设计了koa2,
当然, 这也意味着需要node 7以上的版本才能支持,
在使用上,发送请求信息方面 ,express与koa不同点在于express通过的是函数创建的方式, 而koa则是通过 new 来创建。
Koa 提供了一个 Context 对象, 它包含了req与res封装对象, 通过它来操作http的响应与请求。
路由方面, express自带了 router中间件系统, 而koa则需要使用koa-route/koa-router路由模块。
静态资源方面, 也同样如此,koa需要额外使用koa-static模块 在中间件合成方面,
express通过app.use()可以传入中间件的数据, 而koa需要使用koa-compose模块进行合成
在表单处理方面,也就是post请求的键值对获取,express自带了表单解析功能, 而koa需要使用koa-body来获取,
在文件上传方面, express通过multer中间件来处理表单数据, 而koa通过使用 koa-body进行操作
由此可以看出, koa更像一个轻量级的express,它没有捆绑中间件, 所以保持了一个很小的体积,
至于具体的使用, 还是看个人需求, koa2的优势在于代码简洁。
- 安装koa, 这里直接使用脚手架搭建项目
npm install -g koa-generator / sudo(mac权限) npm install -g koa-generator
- 创建koa2项目, node 7+以上
koa2 -e NodeKoa2(项目名称)
- 安装依赖, 执行项目, 同样安装nodemon来动态监测node,
npm i
npm start
sudo npm install -g nodemon
// 修改package.json中start
"scripts": {
"start": "nodemon bin/www",
...
},
- 对于数据库操作, 就依然沿用mysql和knex,省略下步骤。 log也沿用
npm install -save knex mysql
npm install -g nodemon
- 那么还是根目录下创建 controllers/models/config.js/loggerjs,如果上面的已经创建了, 可以直接复制,基本可以复用
- 然后再修改app.js
const Koa = require('koa')
const app = new Koa()
const views = require('koa-views')
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const koaLogger = require('koa-logger')
var logger = require('./logger')
const index = require('./routes/index')
const users = require('./routes/users')
onerror(app)
app.use(bodyparser({
enableTypes:['json', 'form', 'text']
}))
app.use(json())
app.use(koaLogger())
app.use(require('koa-static')(__dirname + '/public'))
app.use(views(__dirname + '/views', {
extension: 'ejs'
}))
app.use(async (ctx, next) => {
const start = new Date()
await next()
const ms = new Date() - start
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})
app.use(index.routes(), index.allowedMethods())
app.use(users.routes(), users.allowedMethods())
app.on('error', (err, ctx) => {
logger.error(`${ctx.method} ${ctx.url} ` + err.message)
});
module.exports = app
- 再修改router.js, express和koa的区别在于 koa将req和res进行了一个封装对象ctx
const router = require('koa-router')()
const userController = require('../controllers/user');
const multer = require('koa-multer');
const upload = multer({dest:'uploads/'})
router.get('/', async (ctx, next) => {
await ctx.render('index', {
title: 'Hello Koa 2!'
})
})
router.get('/string', async (ctx, next) => {
ctx.body = 'koa2 string'
})
router.get('/json', async (ctx, next) => {
ctx.body = {
title: 'koa2 json'
}
})
router.get('/get_user', userController.showUser);
router.post('/add_user', userController.addUser);
router.post('/update_user', userController.updateUser);
router.post('/delete_user', userController.deleteUser);
router.post('/add_file',upload.single('file'), (ctx) => {userController.addFile(ctx)});
module.exports = router
- 既然req.res变成了ctx。 那么就需要修改一下controllers里的方法参数
const User = require('../models/user.js');
const userController = {
showUser:async function(ctx, name){
try{
let userData = await User.all()
ctx.body = {
code: 200,
message: "操作成功",
data: userData
}
}catch(e){
ctx.app.emit('error', e, ctx);
ctx.body = {
code: ctx.response.status,
msg: e.message
};
}
},
addUser: async function(ctx, name){
try{
await User.insert(ctx.request.body)
ctx.body = {
code: 200,
message: "操作成功",
data: {}
}
}catch(e){
ctx.app.emit('error', e, ctx);
ctx.body = {
code: ctx.response.status,
msg: e.message
};
}
},
updateUser: async function(ctx,name){
console.log(ctx.request.body)
try{
await User.update(ctx.request.body.id, ctx.request.body)
ctx.body = {
code: 200,
message: "操作成功",
data: {}
}
}catch(e){
ctx.app.emit('error', e, ctx);
ctx.body = {
code: ctx.response.status,
msg: e.message
};
}
},
deleteUser:async function(ctx,name){
console.log(ctx.request.body)
try{
await User.delete(ctx.request.body.id)
ctx.body = {
code: 200,
message: "操作成功",
data: {}
}
}catch(e){
ctx.app.emit('error', e, ctx);
ctx.body = {
code: ctx.response.status,
msg: e.message
};
}
},
addFile: async function(ctx,next){
console.log(ctx.request.body, ctx.req.body)
ctx.req.body.file = `http://www.xxxx2.com?` + ctx.req.file.originalname
ctx.body = {
filename: ctx.req.file.filename,
body:ctx.req.body
}
try{
ctx.body = {
code: 200,
message: "操作成功",
data: ctx.req.body
}
await User.insert(ctx.req.body)
}catch(e){
ctx.app.emit('error', e, ctx);
ctx.body = {
code: ctx.response.status,
msg: e.message
};
}
},
}
module.exports = userController;
- 用法和理解上基本不存在区别, 唯一值得注意的地方是, ctx里, req,/request 与 res/response 含义的区别
ctx.app 为应用程序实例引用。
ctx.req 为 Node 的 request 对象。
ctx.res 为 Node 的 response 对象。
ctx.request 为 koa 的 Request 对象。
ctx.response 为 koa 的 Response 对象。
- 在普通非文件上传的接口中, 通过ctx.request.body获取参数, 而在文件上传接口formdata中, 通过ctx.req.body获取参数。
egg
创建一个egg项目
npm init egg --type=simple --registry=china
egg中多路由管理
module.exports = app => {
app.router.get('/', app.controller.home.index); app.router.get('/model1/list',
app.controller.model1.list.index)
app.router.get('/model1/detail', app.controller.model1.detail.index) };
'use strict';
const { Controller } = require('egg');
class DetailController extends Controller {
async index() {
const { ctx } = this;
ctx.body = 'this is the detail page return';
}
}
module.exports = DetailController;
'use strict';
const { Controller } = require('egg');
class ListController extends Controller {
async index() {
const { ctx } = this;
ctx.body = 'this is the list page return';
}
}
module.exports = ListController;
'use strict';
module.exports = app => { require('./router/model1')(app) };
- 测试路由管理是否可用, 在地址栏后,添加/model1/list测试是否成功返回写入文案。


中间件middleware
- 在app目录下创建middleware文件夹
egg 约定一个中间件是一个放置在 app/middleware 目录下的单独文件,
它需要导出一个普通的函数,该函数接受两个参数:
options: 中间件的配置项,框架会将 app.config[${middlewareName}] 传递进来。
app: 当前应用 Application 的实例。
- 全局启用中间件 示例: slow.js
module.exports = (options, app) => {
return async function (ctx, next) {
const startTime = Date.now()
await next()
console.log(options, '这里是参数')
const consume = Date.now() - startTime
const { threshold = 0 } = options || {}
console.log(`${ctx.url}请求耗时${consume}毫秒`) } }
- 然后在config.default。js中注入使用, config.middleware = ['slow']填入中间件的集合数组, config.middleName对应各中间件参数传递的具体配置
...
config.middleware = ['slow']
// slow传递参数threshold ,值为1
config.slow = { threshold: 1, }
- 对局部路由添加特定中间件, 依然使用slow示例
方式一
...
config.middleware = ['slow'];
config.slow = { threshold: 1, match: "/model1/list", };
方式二
...
slow.js config.middleware = [];
module.exports = app => {
app.router.get('/', app.controller.home.index); app.router.get('/model1/list',
app.middleware.slow({ threshold: 1 }), app.controller.model1.list.index)
app.router.get('/model1/detail', app.controller.model1.detail.index) };
- egg 把中间件分成应用层定义的中间件(app.config.appMiddleware):config.default.js 中添加在middleware中的中间件 和框架默认内置的中间件(app.config.coreMiddleware),
- 内置中间件的取消方法, 示例
...
config.i18n = { enable: false }
控制器
- 控制器方面, 跟上面类似, 主要的作用就是充当一个桥梁 ,通过将调用 service中写好的查表方法(获取相应的数据,然后对于这些数据, 做一些业务性的逻辑处理, 这些当然也可以在控制器中做)的结果进行包装处理返回给渲染。
const { Controller } = require('egg');
class PostController extends Controller {
async create() {
const { ctx, service } = this;
const createRule = { title: { type: 'string' },
content: { type: 'string' }, };
ctx.validate(createRule);
const data = Object.assign(ctx.request.body,
{ author: ctx.session.userId });
const res = await service.post.create(data);
ctx.body = { id: res.id }; ctx.status = 201; } }
module.exports = PostController;
- 设置通用controller. 在app目录下新建core来存放通用controller, 并创建base_Controller.js
const { Controller } = require('egg');
class BaseController extends Controller {
get user() { return this.ctx.session.user; }
success(data) { this.ctx.body = { success: true, data }; }
notFound(msg) { this.ctx.throw(404, msg || 'not found'); } }
module.exports = BaseController;
- 使用通用controller, 只需要在需要继承通用控制器的控制器文件里,将 require('egg') 替换成require('../core/base_controller')即可
const { Controller } = require('egg');
=========》 替换成下面即可在文件中访问通用controller中设定的方法和属性
const Controller = require('../core/base_controller');
const Controller = require('../core/base_controller');
class PostController extends Controller {
async list() {
const posts = await this.service.listByUser(this.user); this.success(posts); } }
- 和koa类似, 在controller中获取ctx参数使用 this.ctx.参数名。 具体配置如下
ctx.query:URL 中的请求参数(忽略重复 key)
ctx.quries:URL 中的请求参数(重复的 key 被放入数组中)
ctx.params:Router 上的命名参数
ctx.request.body:HTTP 请求体中的内容
ctx.request.files:前端上传的文件对象
ctx.getFileStream():获取上传的文件流
ctx.multipart():获取 multipart/form-data 数据
ctx.cookies:读取和设置
cookie ctx.session:读取和设置 session
ctx.service.xxx:获取指定 service 对象的实例(懒加载)
ctx.status:设置状态码
ctx.body:设置响应体
ctx.set:设置响应头
ctx.redirect(url):重定向
ctx.render(template):渲染模板
this.ctx 上下文对象是 egg 框架和 koa 框架中最重要的一个对象,不过需要注意的是,
有些属性并非直接挂在 app.ctx 对象上,而是代理了 request 或 response 对象的属性,
可以用 Object.keys(ctx) 打印查看具体属性
数据库操作, egg-knex /egg-sequelize
- 安装egg-knex/ egg-sequelize
npm install -save egg-knex mysql / npm install -save egg-knex mysql
- 然后在app目录config文件夹中的plugin.js中添加导入相应的插件, 二选一即可, 这里还是用knex
'use strict';
module.exports = {
static: {
}
config.security = { csrf: { enable: false, } }
config.multipart = { mode: 'file',
knex : { enable: true, package: 'egg-knex', }
sequelize : { enable: true, package: 'egg-sequelize', } };
- 然后在config.default.js中添加knex相关连接配置
....
exports.knex = {
client: {
dialect: 'mysql',
connection: {
host: '127.0.0.1',
port: '3306',
user: 'root',
password: '00000000',
database: 'NodeExpress',
},
pool: { min: 0, max: 5 },
acquireConnectionTimeout: 30000,
},
app: true,
agent: false,
};
- 在app目录下创建service文件夹, 存放数据库操作文件 user.js
'use strict';
const { Service } = require('egg');
class User extends Service {
async all() {
const all = await this.app.knex('user').select()
return all;
}
async insert(params) {
const insert = await this.app.knex('user').insert(params)
return insert
}
async update(id, params) {
const update = await this.app.knex('user')
.where('id', '=', id).update(params)
return update
}
async delete(id) {
const update = await this.app.knex('user')
.where('id', '=', id)
.del()
return update
}
}
module.exports = User;
- 然后在controller中直接调用操作表数据即可, 特别注意的是, 调用方法以文件名为准,即使内部导出为User, ctx.service.user 获取的是对应的文件。
'use strict';
const { Controller } = require('egg');
class DetailController extends Controller {
async index() {
const { ctx } = this;
const res = await ctx.service.user.all()
ctx.body = { code: 200, message: "操作成功", data: res }; } }
module.exports = DetailController;

- 其他方法操作都类似, knex的用法在egg中, 大致上只有配置方法不同, 其他没有什么区别
- 文件上传类型也简单, egg内置了文件上传, 所以只需要在config.default.js设置里添加config.multipart的mode类型即可
...
config.multipart = { mode: 'file',
- 然后获取参数的方法参数也与koa有点区别
'use strict';
const { Controller } = require('egg');
class ListController extends Controller {
async index() {
const { ctx } = this;
console.log( ctx.request.files[0],ctx.request.body )
ctx.request.body.file = `http://www.xxxx2.com?` + ctx.request.files[0].filename
ctx.body = { filename: ctx.request.files[0].filename,
const res = await ctx.service.user.insert(ctx.request.body)
ctx.body = { code: 200, message: "操作成功", data: res }; } }
module.exports = ListController;
- 当然, 需要注意的是, multipart属于全局中间件, 如果单独添加, 可以使用上面说的 ignore/match 进行自定义修改, 然后使用 app.middleware.multipart()单独设置文件大小和类型
module.exports = app => {
app.router.get('/', app.controller.home.index);
app.router.post('/model1/list',
app.middleware.multipart({ fileSize: '1mb',mode: 'strema' }), app.controller.model1.list.index)
app.router.get('/model1/detail', app.controller.model1.detail.index) };
日志打印
- 首先了解下egg打印的内容
Egg.js中自带了三种logger,分别是 Context Logger App Logger Agent Logger Context Logger
主要是用来记录请求相关的日志。每行日志都会在开头自动的记录当前请求的一些信息,
比如时间、ip、请求url等等。 App Logger用于记录应用级别的日志,比如程序启动日志。
Agent Logger用于记录多进程模式运行下的日志。
- 如何自定义请求日志
我们想自定义请求级别的日志,那重点就要从Context Logger去研究怎么做。 通常期望的效果即是,
将每个请求结果分类, 即根据请求状态level记录成不同的log文件夹,
方便需要时直接通过level类型文件快速查看请求及结果 然后每个level文件夹下, 又进行天数的分割形成对应的log.
DEBUG、INFO、WARN、ERROR以及NONE 而Context Logger是基于egg-logger的FileTransport类去进行文件落地的,
同时FileTransport也默认配置了egg-logrotator的日志拆分。
所以,在自定义日志的时候, 只需要继承FileTransport类,实现接口就可以了,
- 在app目录下创建extend扩展文件夹, 然后创建content.js基础文件。
- 然后在extend目录下创建一个基础实现类接口文件CoustomTransport.js来继承FileTransport
const FileTransport = require('egg-logger').FileTransport;
const moment = require('moment');
class CoustomTransport extends FileTransport {
constructor(options, ctx) {
super(options);
this.ctx = ctx;
}
log(level, args, meta) {
const prefixStr = this.buildFormat(level);
for (let i in args) {
if (args.hasOwnProperty(i)) {
if (parseInt(i, 10) === 0) { args[i] = `${prefixStr}${args[i]}`; }
if (parseInt(i, 10) === args.length - 1) { args[i] += '\n'; } } }
super.log(level, args, meta); }
buildFormat(level) {
const timeStr = `[${moment().format('YYYY-MM-DD HH:mm:ss.SSS')}]`;
const threadNameStr = `[${process.pid}]`;
const urlStr = `[${this.ctx.request.url}]`
console.log(`${timeStr}${threadNameStr}${urlStr}`)
return `${timeStr}${threadNameStr}${urlStr}`;
}
setUserId(userId) { this.userId = userId; } }
module.exports = CoustomTransport;
- 写好实现类后, 再在extend下创建一个CustomLogger.js, 用来初始化logger
const Logger = require('egg-logger').Logger;
const CoustomTransport = require('./CoustomTransport.js');
module.exports = function(ctx){
const logger = new Logger();
logger.set('file',
new CoustomTransport({
level: 'INFO', file: `./logs/NodeEgg/NodeEgg.log`,
}, ctx));
return logger;
};
- 然后在扩展中添加自定义请求配置, 修改context.js
const CustomLogger = require('./CustomLogger');
module.exports = { get swLog() { return CustomLogger(this); } };
- 因为需要记录的是每次请求的结果日志, 所以针对于所有请求都需要去配合调用写好的swlog方法, 所以修改basecontroller.js, 并把controller中的继承对象由egg改为basecontroller
const { Controller } = require('egg');
class BaseController extends Controller {
get user() { return this.ctx.session.user; }
success(data) {
this.ctx.swLog.info('Hello World');
this.ctx.body = { success: true, data };
}
notFound(msg) {
ctx.swLog.error('Hello World');
this.ctx.throw(404, msg || 'not found');
}
}
module.exports = BaseController;
'use strict';
const BaseController = require('../../core/base_Controller');
class DetailController extends BaseController {
async index() {
const { ctx } = this;
const res = await ctx.service.user.all()
this.success(res)
}
}
module.exports = DetailController;
'use strict';
const BaseController = require('../../core/base_Controller');
class ListController extends BaseController {
async index() {
const { ctx } = this;
console.log(ctx.request.files[0])
ctx.request.body.file = `http://www.xxxx2.com?` + ctx.request.files[0].filename
ctx.body = {
filename: ctx.request.files[0].filename,
body:ctx.request.body
}
const res = await ctx.service.user.insert(ctx.request.body)
this.success(res)
}
}
module.exports = ListController;

- 简单测试一下,可以在logs/NodeEgg/NodeEgg.js中查看到相应记录, 说明自定义监听日志已经成功挂载

- 日志监听及打印功能都设置完成后, 就可以进一步修改日志的格式,
const FileTransport = require('egg-logger').FileTransport;
const moment = require('moment');
class CoustomTransport extends FileTransport {
constructor(options, ctx) {
super(options);
this.ctx = ctx;
}
log(level, args, meta) {
const prefixStr = this.buildFormat(level);
for (let i in args) {
if (args.hasOwnProperty(i)) {
if (parseInt(i, 10) === 0) {
args[i] = `${prefixStr}${args[i]}`;
}
if (parseInt(i, 10) === args.length - 1) {
args[i] += '\n';
}
}
}
super.log(level, args, meta);
}
buildFormat(level) {
const timeStr = `[${moment().format('YYYY-MM-DD HH:mm:ss.SSS')}]`;
const threadNameStr = `[userID:${process.pid}]`;
const methodtype = `[${this.ctx.request.method}]`
const urlStr = `[${this.ctx.request.url}]`
return `${timeStr}${threadNameStr}${methodtype}${urlStr}`;
}
setUserId(userId) {
this.userId = userId;
}
}
module.exports = CoustomTransport;
- 查看输出结果
[2023-02-10 16:03:04.161][userID:10177][POST][/model1/list]success
[2023-02-10 16:03:08.423][userID:10177][POST][/model1/list]success
[2023-02-13 10:04:57.613][userID:968][GET][/model1/detail]success
[2023-02-13 10:05:35.103][userID:968][GET][/model1/detail]success
[2023-02-13 10:06:04.823][userID:1025][GET][/model1/detail]success