以前用express+log4js,配置式参数使用的好好的。。
换做egg后,这个日志格式看得我各种别扭。
包括但不限于:
应该是基于process.hrtime()的高精度时间,而不是new Date().getTime()吧?。
比如 egg-logger/lib/egg/context_logger.js 25行,这种计算方式得出的use时间,真的可用么?
token或者其他authorization信息,响应时间,响应状态(http status)log4js的layouts - Pattern Format方案?尝试使用自写中间件完成accessLogger的功能呢,结果发现绑定到Context Logger时会因为
每行日志会自动记录上当前请求的一些基本信息, 如 [$userId/$ip/$traceId/${cost}ms $method $url]
参见Context Logger
查看源码,egg-logger/lib/egg/context_logger.js#43,并无可以配置的地方。
所以只能使用App Logger在加载中间件,将中间件绑定到app上,来实现access日志的记录
直接引入egg-logger后,原生的日志输出不符合基本需求,并且格式不统一
[2018-01-22 15:55:07.242] [cfork:master:34595] worker:34638 disconnect (exitedAfterDisconnect: true, state: disconnected, isDead: false, worker.disableRefork: false)
[2018-01-22 15:55:07.242] [cfork:master:34595] don't fork new work (refork: false)
2018-01-22 15:55:07,242 INFO 34595 [master] app_worker#4:34638 disconnect, suicide: true, state: disconnected, current workers: ["5"]
[2018-01-22 15:55:07.243] [cfork:master:34595] worker:34638 exit (code: 0, exitedAfterDisconnect: true, state: dead, isDead: true, isExpected: true, worker.disableRefork: false)
既然是企业级框架,就应该需要考虑每个企业有自己的一套日志体系吧?
按照log4js去配置是一件相对简单的事情,只不过express中res的finish和close监听需要耗费一部分代码去完成。
很多日志最后都是使用filebeat+logstash去采集的,日志格式统一,有利于L的快速过滤?
request - header中,因公司不同,可能使用的全链路唯一标志不同。
有的公司用traceId、而有的用request-id,诸如此类的,如果都需要去改源码去完成,是否对生产的部署是一种障碍?
这些原因大概是我选择关闭egg原生日志,#1667。
转而去寻找一种可能,使用koa-log4来复现已经成型的日志体系。
以上,如果我对日志的使用违反egg的日志规则,请指出
思考的很细,感谢反馈。
请求响应日志 目前我们更多是在前面的 Nginx 那层去记录,这块要自己定制一个并不难,ctx.logger 那块应该可以覆盖掉默认的 format 的。原生的日志输出 cfork 那个你可以理解为是一个第三方库输出到 stdout 的,这个肯定不好控制,但它并不会被写入到 file 里面的,不影响你分析。多级别日志分装,每个 logger 都可以单独配置 level 的。全链路标记可配置 这块属于 tracelog 范畴,这块其实跟企业内部的架构有关,需要去定制化的。我们内部有鹰眼系统,以及对应的插件,有兴趣可以跟进和推动下这个 RFC。我的2、5实际上可以通过一套解决方案来实现,2的目的也是为了5.
即通过记录请求响应记录来复现全链路路由。
看了那个RFC,可能大家理解的方向不同。
抛个砖。。。
理论上,所谓tracer也好,全链路也罢。
要能做到追踪每个请求的完整调用链路,收集调用链路上每个服务的性能数据,计算性能数据和比对性能指标(SLA),甚至在更远的未来能够再反馈到服务治理中,那么这就是分布式跟踪的目标了
其目的,一言以蔽之:追踪从入到出的全链路路由,并且可以回溯各阶段响应情况。
主要几点:
简单说下思路
每个节点
如
[2018-01-24T11:25:16.747] [INFO] access - {"remoteIP":"127.0.0.1","originalUrl":"/","x-trace-id":"151382266795060c4-40d7-920d-09bb777a0f30","x-app-keys":"SC-CX-DD","req":{"method":"GET","header":{},"query":{},"body":{},"requestAt":"2018-01-24 11:25:16.256"},"res":{"status":200,"responseTime":"10.219ms","responseAt":"2018-01-24 11:25:16.266"}}
json格式化下
{
"remoteIP": "127.0.0.1",
"originalUrl": "/",
"x-trace-id":"151382266795060c4-40d7-920d-09bb777a0f30",
"x-app-keys": "SC-CX-DD",
"req": {
"method": "GET",
"header": {},
"query": {},
"body": {},
"requestAt": "2018-01-24 11:25:16.256"
},
"res": {
"status": 200,
"responseTime": "10.219ms",
"responseAt": "2018-01-24 11:25:16.266"
}
}
1和2,我在尝试写一个中间件来自己实现下。
赞。这里还是要澄清一点:
Egg 是微内核 + 插件生态的方式的。
这些高级功能,都是在插件里面去实现的,因此:
因此我们非常欢迎社区开发者能分享出自己的实践,来碾压我们的某些插件。
个人感觉:如果要接入elk的话,egg的日志格式就不太适用,希望可以自定义日志输出格式!
egg-logger 现在的自定义输出有什么问题么?不是一直支持么?
请求响应日志 目前我们更多是在前面的 Nginx 那层去记录,这块要自己定制一个并不难,ctx.logger 那块应该可以覆盖掉默认的 format 的。
ctx.logger的format应该是这个吧,
https://github.com/eggjs/egg-logger/blob/master/lib/egg/context_logger.js#L54
这个formatter函数,并没有接受任何config的传入。
我尝试传入format函数,并无法改变此部分的逻辑。
因为这个地方的逻辑已经绑定了这个 context_logger的函数。。。
我自己封装了一个middleware,但并无法改变前边这一部分。
2018-01-27 15:40:32,534 INFO 48536 [-/::1/-/7ms GET /user/judge?a=2] {"remoteIP":"127.0.0.1","originalUrl":"/user/judge?a=2","req":{"method":"GET","header":{"Content-Type":"","token":""},"query":{"a":"2"},"body":{},"requestAt":"2018-01-27 15:40:32 "},"res":{"status":200,"responseTime":"3.830ms","responseAt":"2018-01-27 15:40:32 "}}
如果想要改变输出的format,应该是需要自定义Logger了,没法直接使用ctx.logger。
如果直接把
https://github.com/eggjs/egg-logger/blob/master/lib/egg/context_logger.js#L54
这个方法给改了,可以实现。但这样就侵入式修改egg的框架内容了。
贴一个我自己写了一部分的code
在 /app/middleware/accessLogger.js中:
/**
* Created by xxx on 2018/1/22.
* Copyright© 2015-2020
* @version 0.0.1 created
*/
'use strict';
/* eslint no-extend-native: ["error", { "exceptions": ["Date"] }] */
Date.prototype.format = DateForm; // Date原型链绑定时间格式化函数
/**
* 请求响应日志
* header信息获取 参见 https://eggjs.org/zh-cn/basics/controller.html#header
* @return {accessLogger} 日志中间件
*/
module.exports = () => {
return async function accessLogger(ctx, next) {
const TIME_FORMAT = 'yyyy-MM-dd hh:mm:ss ';
const { request, response } = ctx;
const startedAt = process.hrtime(); // 获取高精度时间
const log = { // 日志信息
// uuid: ctx.get(), // 全链路唯一标记
remoteIP: getIP(request), // 客户端IP
originalUrl: request.originalUrl, // 请求地址
// appKey: '', // 当前应用的标记
req: {
method: request.method,
header: {
'Content-Type': ctx.get('Content-Type'),
token: ctx.get('auth_token'), // token权限
},
query: request.query,
body: request.body,
requestAt: new Date().format(TIME_FORMAT),
},
res: {},
};
await next();
log.res = {
status: response.status,
responseTime: calcResponseTime(startedAt),
responseAt: new Date().format(TIME_FORMAT),
};
const logger = ctx.getLogger('accessLogger');
/**
* 因Context Logger会增加 meta [$userId/$ip/$traceId/${cost}ms $method $url] 信息
* 所以如果需要特定格式log,只能使用App Logger
* 参见 https://eggjs.org/zh-cn/core/logger.html#context-logger
* 参见 https://github.com/eggjs/egg-logger/blob/master/lib/egg/context_logger.js#L43
*/
// console.info(Object.keys(logger._logger.options.formatter))
logger._logger.options.formatter = meta => { // xxx 并没有用
return '[' + meta.date + '] '
+ meta.level + ' '
+ meta.pid + ' '
+ meta.message;
};
logger.info(JSON.stringify(log));
};
};
/**
* nginx转发后获取实际IP信息
* @param {object} req 请求参数
* @return {string} 格式化IP
*/
function getIP(req) {
let ip = req.get('x-forwarded-for'); // 获取代理前的ip地址
if (ip && ip.split(',').length > 0) {
ip = ip.split(',')[ 0 ];
} else {
ip = req.ip;
}
const ipArr = ip.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g);
return ipArr && ipArr.length > 0 ? ipArr[ 0 ] : '127.0.0.1';
}
/**
* Date原型链绑定时间格式化函数
* @param {string} format 格式化
* @return {*} 格式化后时间
*/
function DateForm(format) {
const o = {
'M+': this.getMonth() + 1, // month
'd+': this.getDate(), // day
'h+': this.getHours(), // hour
'm+': this.getMinutes(), // minute
's+': this.getSeconds(), // second
'w+': this.getDay(), // week
'q+': Math.floor((this.getMonth() + 3) / 3), // quarter
S: this.getMilliseconds(), // millisecond
};
if (/(y+)/.test(format)) { // year
format = format.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (const k in o) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(RegExp.$1
, RegExp.$1.length === 1 ? o[ k ] : ('00' + o[ k ]).substr(('' + o[ k ]).length));
}
}
return format;
}
/**
* 计算响应时间
* @param {Array} startedAt 请求时间
* @return {string} 响应时间字符串
*/
function calcResponseTime(startedAt) {
const diff = process.hrtime(startedAt);
// 秒和纳秒换算为毫秒,并保留3位小数
return `${(diff[ 0 ] * 1e3 + diff[ 1 ] * 1e-6).toFixed(3)}ms`;
}
在 config/config.default.js中
const path = require('path');
module.exports = appInfo => {
// 自定义日志
customLogger: {
// 请求响应日志
accessLogger: {
file: path.join(appInfo.root, 'logs/access.log'),
format: meta => {
return '[' + meta.date + '] '
+ meta.level + ' '
+ meta.pid + ' '
+ meta.message;
},
formatter: meta => {
return '[' + meta.date + '] '
+ meta.level + ' '
+ meta.pid + ' '
+ meta.message;
},
},
},
}
之后,format没起作用。
感觉应该是我什么地方的配置没加载。。。
很尴尬。。。😓
lsof修复方案见 https://github.com/eggjs/egg/issues/2006#issuecomment-395663629
一个可用的解决方案
全自写middleware来实现日志记录器
app/middle/accessLogger.js中/**
* Created by xxx on 2018/1/22.
* Copyright© 2015-2020
* @version 0.0.1 created
*/
'use strict';
const { Logger , FileTransport , ConsoleTransport } = require('egg-logger');
/**
* 请求响应日志
* header信息获取 参见 https://eggjs.org/zh-cn/basics/controller.html#header
* 自定义日志器 参见https://github.com/eggjs/egg-logger#usage
* @return {Function} 日志中间件
*/
module.exports = () => {
return async function accessLogger(ctx, next) {
// const TIME_FORMAT = 'yyyy-MM-dd hh:mm:ss S';
const { request, response } = ctx;
const startedAt = process.hrtime(); // 获取高精度时间
const log = { // 日志信息
// uuid: ctx.get(), // 全链路唯一标记
remoteIP: getIP(request), // 客户端IP
originalUrl: request.originalUrl, // 请求地址
// appKey: '', // 当前应用的标记
req: {
method: request.method,
header: {
'Content-Type': ctx.get('Content-Type'),
token: ctx.get('auth_token'), // token权限
},
query: request.query,
body: request.body,
requestAt: DateForm(),
},
res: {},
};
await next();
log.res = {
status: response.status,
responseTime: calcResponseTime(startedAt),
responseAt: DateForm(),
};
const logger = new Logger(); // 声明一个新的日志记录器
// 配置文件输出/存储
logger.set('file', new FileTransport({
file: 'logs/access.log',
level: 'INFO',
}));
// 配置控制台输出
logger.set('console', new ConsoleTransport({
level: 'DEBUG',
}));
// —————— TODO - 自定义日志输出格式 ————————
logger.info('[' + DateForm() + '] [INFO] access - ' + JSON.stringify(log));
};
};
/**
* 获取实际IP信息
* @param {object} req 请求参数
* @return {string} 格式化IP
*/
function getIP(req) {
let ip = req.get('x-forwarded-for'); // 获取代理前的ip地址
if (ip && ip.split(',').length > 0) {
ip = ip.split(',')[ 0 ];
} else {
ip = req.ip;
}
const ipArr = ip.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g);
return ipArr && ipArr.length > 0 ? ipArr[ 0 ] : '127.0.0.1';
}
/**
* 时间格式化函数
* @param {Date} date 时间
* @param {string} format 格式化
* @return {*} 格式化后时间
*/
function DateForm(date = new Date(), format = 'yyyy-MM-dd hh:mm:ss S') {
const o = {
'M+': date.getMonth() + 1, // month
'd+': date.getDate(), // day
'h+': date.getHours(), // hour
'm+': date.getMinutes(), // minute
's+': date.getSeconds(), // second
'w+': date.getDay(), // week
'q+': Math.floor((date.getMonth() + 3) / 3), // quarter
S: date.getMilliseconds(), // millisecond
};
if (/(y+)/.test(format)) { // year
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (const k in o) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(RegExp.$1
, RegExp.$1.length === 1 ? o[ k ] : ('00' + o[ k ]).substr(('' + o[ k ]).length));
}
}
return format;
}
/**
* 计算响应时间
* @param {Array} startedAt 请求时间
* @return {string} 响应时间字符串
*/
function calcResponseTime(startedAt) {
const diff = process.hrtime(startedAt);
// 秒和纳秒换算为毫秒,并保留3位小数
return `${(diff[ 0 ] * 1e3 + diff[ 1 ] * 1e-6).toFixed(3)}ms`;
}
config/config.default.js中或其他对应环境配置文件中// 加载中间件
exports.middleware = [ 'accessLogger' ];
@atian25
就好比下面这条日志:
2018-01-27 15:40:32,534 INFO 48536 [-/::1/-/7ms GET /user/judge?a=2] {"remoteIP":"127.0.0.1","originalUrl":"/user/judge?a=2","req":{"method":"GET","header":{"Content-Type":"","token":""},"query":{"a":"2"},"body":{},"requestAt":"2018-01-27 15:40:32 "},"res":{"status":200,"responseTime":"3.830ms","responseAt":"2018-01-27 15:40:32 "}}2018-01-27 15:40:32,534 INFO 48536 [-/::1/-/7ms GET /user/judge?a=2] {"remoteIP":"127.0.0.1","originalUrl":"/user/judge?a=2","req":{"method":"GET","header":{"Content-Type":"","token":""},"query":{"a":"2"},"body":{},"requestAt":"2018-01-27 15:40:32 "},"res":{"status":200,"responseTime":"3.830ms","responseAt":"2018-01-27 15:40:32 "}}
前面这段 2018-01-27 15:40:32,534 INFO 48536 [-/::1/-/7ms GET /user/judge?a=2] 格式是定死的,你觉得没问题?日志系统都需要这样的格式吗?貌似并不是吧,一般开发者要么就是改变配置将输出格式设置为JSON,要么就是向上面说的自己写中间件,个人觉得不是很理想,还有就是日志的储存位置,就算我改了日志的摆放位置。系统的终端日志还是会打到根目录下(非本地开发 npm run dev),个人觉得没必要打出来,想知道打出来有什么意义?
@Webjiacheng
context logger 目前的 format 不支持覆盖,可以通过 2 的方式去定制自己的先,或者提 PR 优化下。系统的终端日志还是会打到根目录下
这个没太看懂,默认的配置是 appInfo.root 下,参见文档 日志路径 ,你是可以修改配置的。
我就想知道错误日志,怎么能加上 ip ua 和请求参数信息...
要上下文必须用 ctx.logger
// app/context_logger.js
class ContextLogger extends require('egg-logger').EggContextLogger {
paddingMessage() {
return '';
}
}
// app/extend/application.js
exports.ContextLogger = require('../context_logger');
可以这样自定义
@popomore 如果我只想改掉某个 custom logger 的 context logger 的 formattor 呢?
这个不能改 format,只能在原来的 logger 上增加上下文信息
后面在 example 里面加个例子好了
我看着默认实现好像带了一些参数,但是实际打的日志,我没看到 ip(更新,是我看错了...)
get paddingMessage() {
const ctx = this.ctx;
// Auto record necessary request context infomation, e.g.: user id, request spend time
// format: '[$userId/$ip/$traceId/$use_ms $method $url]'
const userId = ctx.userId || '-';
const traceId = ctx.tracer && ctx.tracer.traceId || '-';
const use = ctx.starttime ? Date.now() - ctx.starttime : 0;
return '[' +
userId + '/' +
ctx.ip + '/' +
traceId + '/' +
use + 'ms ' +
ctx.method + ' ' +
ctx.url +
']';
}
我的需求比较简单,只有错误日志才想多加几个字段
你把你的日志发一下
是否能定制一个通用日志格式,然后再根据需求,覆盖某种日志格式(比如错误日志)
格式里的字段,是否能传入日志对象里,还是说,必须按你说的方式去继承
@popomore 我的诉求是这样
config.customLogger = {
biz: {
file: path.join(appInfo.root, 'logs/biz.log'),
formatter(meta) {
console.log(meta);
return `### ${JSON.stringify(meta)}`;
},
// 或者再支持个 contextFormatter(meta, ctx)
},
};
this.app.getLogger('biz').warn('app', 'msg');
this.ctx.getLogger('biz').warn('ctx', 'msg');
实际输出, context logger 的 formatter 是无法自定义的
### {"level":"WARN","date":"2018-04-09 15:22:48,429","pid":53978,"hostname":"TZ-Mac.local","message":"app msg"}
2018-04-09 15:22:48,431 WARN 53978 [-/127.0.0.1/-/11ms GET /] ctx msg
egg 会打印访问日志么?还是说必须看 nginx 的访问日志?
@occultskyrong
[!注意]请求响应日志,自定义中间件
const { Logger , FileTransport , ConsoleTransport } = require('egg-logger');
const logger = new Logger(); // 声明一个新的日志记录器
// 配置文件输出/存储
logger.set('file', new FileTransport({
file: 'logs/access.log',
level: 'INFO',
}));
// 配置控制台输出
logger.set('console', new ConsoleTransport({
level: 'DEBUG',
}));
您之前代码是每次写出都new一次。我放到开头的地方只new一次。会好很多吧。
@occultskyrong
兄弟 你这个代码 有坑。文件数量无限打开。
/**
* Created by xxx on 2018/1/22.
* Copyright© 2015-2020
* @version 0.0.1 created
*/
'use strict';
const { Logger , FileTransport , ConsoleTransport } = require('egg-logger');
const logger = new Logger(); // 声明一个新的日志记录器
// 配置文件输出/存储
logger.set('file', new FileTransport({
file: 'logs/access.log',
level: 'INFO',
}));
// 配置控制台输出
logger.set('console', new ConsoleTransport({
level: 'DEBUG',
}));
/**
* 请求响应日志
* header信息获取 参见 https://eggjs.org/zh-cn/basics/controller.html#header
* 自定义日志器 参见https://github.com/eggjs/egg-logger#usage
* @return {Function} 日志中间件
*/
module.exports = () => {
return async function accessLogger(ctx, next) {
// const TIME_FORMAT = 'yyyy-MM-dd hh:mm:ss S';
const { request, response } = ctx;
const startedAt = process.hrtime(); // 获取高精度时间
const log = { // 日志信息
// uuid: ctx.get(), // 全链路唯一标记
remoteIP: getIP(request), // 客户端IP
originalUrl: request.originalUrl, // 请求地址
// appKey: '', // 当前应用的标记
req: {
method: request.method,
header: {
'Content-Type': ctx.get('Content-Type'),
token: ctx.get('auth_token'), // token权限
},
query: request.query,
body: request.body,
requestAt: DateForm(),
},
res: {},
};
await next();
log.res = {
status: response.status,
responseTime: calcResponseTime(startedAt),
responseAt: DateForm(),
};
// —————— TODO - 自定义日志输出格式 ————————
logger.info('[' + DateForm() + '] [INFO] access - ' + JSON.stringify(log));
};
};
/**
* 获取实际IP信息
* @param {object} req 请求参数
* @return {string} 格式化IP
*/
function getIP(req) {
let ip = req.get('x-forwarded-for'); // 获取代理前的ip地址
if (ip && ip.split(',').length > 0) {
ip = ip.split(',')[ 0 ];
} else {
ip = req.ip;
}
const ipArr = ip.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g);
return ipArr && ipArr.length > 0 ? ipArr[ 0 ] : '127.0.0.1';
}
/**
* 时间格式化函数
* @param {Date} date 时间
* @param {string} format 格式化
* @return {*} 格式化后时间
*/
function DateForm(date = new Date(), format = 'yyyy-MM-dd hh:mm:ss S') {
const o = {
'M+': date.getMonth() + 1, // month
'd+': date.getDate(), // day
'h+': date.getHours(), // hour
'm+': date.getMinutes(), // minute
's+': date.getSeconds(), // second
'w+': date.getDay(), // week
'q+': Math.floor((date.getMonth() + 3) / 3), // quarter
S: date.getMilliseconds(), // millisecond
};
if (/(y+)/.test(format)) { // year
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (const k in o) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(RegExp.$1
, RegExp.$1.length === 1 ? o[ k ] : ('00' + o[ k ]).substr(('' + o[ k ]).length));
}
}
return format;
}
/**
* 计算响应时间
* @param {Array} startedAt 请求时间
* @return {string} 响应时间字符串
*/
function calcResponseTime(startedAt) {
const diff = process.hrtime(startedAt);
// 秒和纳秒换算为毫秒,并保留3位小数
return `${(diff[ 0 ] * 1e3 + diff[ 1 ] * 1e-6).toFixed(3)}ms`;
}
@occultskyrong 你这个非常坑,根本不适用 楼上说的 文件描述符
@popomore 我的诉求是这样
config.customLogger = { biz: { file: path.join(appInfo.root, 'logs/biz.log'), formatter(meta) { console.log(meta); return `### ${JSON.stringify(meta)}`; }, // 或者再支持个 contextFormatter(meta, ctx) }, }; this.app.getLogger('biz').warn('app', 'msg'); this.ctx.getLogger('biz').warn('ctx', 'msg');实际输出, context logger 的 formatter 是无法自定义的
### {"level":"WARN","date":"2018-04-09 15:22:48,429","pid":53978,"hostname":"TZ-Mac.local","message":"app msg"} 2018-04-09 15:22:48,431 WARN 53978 [-/127.0.0.1/-/11ms GET /] ctx msg
自定义日志可以输出userId, traceId等上下文相关的内容吗? @atian25
用 ctx.getLogger
用 ctx.getLogger
这个不能自定义格式吧 @popomore
自定义 ContextLogger
直接实例化const EggLogger = require('egg-logger').EggLogger; 调自己的logger
这个日志真的是搞死我了。。。我觉得我是个十足的菜鸡,真的整不明白!!!!!!
首先致敬大佬,感谢创造轮子,但是这轮子我装不上啊, 啊,(;´༎ຶД༎ຶ`)
正题:
第一点:我发现logger.set的时候会出现logger上没有set的属性,应该是d.ts中的Logger没有继承Map造成的。
第二点:我想改变日期的格式,感谢上面大佬贡献的代码 通过中间件的事件已经实现,但是我发现我不会使用中间件。TOT,并且级别不能检索出来,需要再加一步级别的判定。
第三点:我还是感觉没有解决内置日志的格式更改,只能去customLog中配置一些自定义日志的格式。
我的诉求:自定义内置日志的格式
我可能说的很多地方都不对,我也是刚接触这方面,我搞了将近一周的时间了,真的不会了。。。
可能我还是在浮躁了,沉下心在研究一下吧,周一就要开例会报告了,啊,(〒︿〒)
内心十分期盼大佬的回答,谢谢~
@zheng199512
自定义 ContextLogger 可以看下这个示例: https://github.com/atian25/egg-showcase/pull/11
有没有直接使用 的 elk 日志中间件啊 @atian25
有没有直接使用 的 elk 日志中间件啊 @atian25
官方没维护,可以自己写个
看很多人这么煎熬。。。
感觉是可以用这个。。。来自定义中间件加进来
满足绝大多数有关日志的需求。。。
日志分级、回收循环(rotate)、多管道(transport)
具体怎么用,自己看下文档。。。。上手已经是很简单了。。
至于与elk的结合。。。
自己用format来配置,然后Logstash里面配置对应的解析。。。
常规操作。。。(逃
看我上面 https://github.com/eggjs/egg/issues/2006#issuecomment-451334296 给的链接
看很多人这么煎熬。。。
感觉是可以用这个。。。来自定义中间件加进来满足绝大多数有关日志的需求。。。
日志分级、回收循环(rotate)、多管道(transport)具体怎么用,自己看下文档。。。。上手已经是很简单了。。
至于与elk的结合。。。
自己用format来配置,然后Logstash里面配置对应的解析。。。常规操作。。。(逃
在egg里面用winston,如何把traceId加进去?
Most helpful comment
赞。这里还是要澄清一点:
Egg 是微内核 + 插件生态的方式的。
这些高级功能,都是在插件里面去实现的,因此:
因此我们非常欢迎社区开发者能分享出自己的实践,来碾压我们的某些插件。