当前位置: 首页 > 后端技术 > Node.js

如何更好的登录Node

时间:2023-04-03 18:56:53 Node.js

如何更好的登录Node全栈方向学习前端工程系列Node进阶系列在服务器应用(后端项目)中,一个完整的、结构化的日志不仅可以帮助定位问题并更好地重现问题,还能找到性能问题的线索。甚至可以帮助解决在线CPU和内存爆炸的问题。本文将讲解如何使用Node更好的在服务器端进行日志记录应该记录在什么地方:AccessLog、SQLLog、BusinessLog应该记录什么日志:server_name、timestamp及相关类型的日志记录什么:winston、log4j、bunyan生成后日志,下一章会讲解日志的收集和处理,以及检索日志的类型。在服务器应用程序中,无论是作为生产者还是作为消费者,它都需要与来自各方的数据进行交互。除了最常见的与客户端的交互,还有数据库、缓存、消息队列、第三方服务等。重要的数据交互需要日志记录。除了对外交互,自身产生的异常信息、关键业务逻辑、定时任务信息等也需要记录。下面简要说明需要记录的日志类型和涉及的字段:AccessLog:这是最常见的日志类型。一般在nginx等方向代理中都有日志记录,但是在业务系统中有时候需要更详细的日志记录,比如API耗时,DetailedrequestbodyandresponsebodySQLLog:关于数据库查询日志,记录SQL,涉及的表,以及执行时间。从此可以过滤掉执行速度太慢的SQL,也可以过滤掉某个API对应的SQL条数。RequestLog:请求第三方服务产生的日志Exception:异常RedisLog:缓存,还有一些非缓存操作如zset、分布式锁等MessageQueueLog:记录生产和消费消息的日志CronLog:记录scheduled的执行时间任务和是否成功关键业务逻辑日志的基本字段对于所有的日志,都会有一些共同的基本字段,比如在那个服务器上,此时生成的日志app是当前项目的名称,是可以将生产环境多个项目的日志聚合在一起,通过app很容易定位到当前项目的serverName,也就是服务器的hostname,通过它很容易定位到有问题的服务器/容器.现在相当多的公司在生产环境应用中使用kubernetes进行编排,k8s中各个POD的hostname如下,所以很容易定位到Deployment:whichapplication/projectReplicaSet:whichonlinePod:whichPod#shanyue-production指的是Deployment名称#69d9884864指的是升级时ReplicaSet对应的hash#vt22t指的是一个Pod对应的hash$hostnameshanyue-production-69d9884864-vt22ttimestamp指的是日志产生的时间,以及最好使用ISO8601格式人类可读性和机器可读性{"timestamp":"2020-04-24T04:50:57.651Z",}requestId/traceId和完整链接日志中的唯一id,通过requestId,你可以把相关的Linking微服务放到一个日志里,包括前端,后端,上游微服务,数据库,redis全链路日志平台可以更好的分析一个请求在每个微服务中的生命周期。目前比较流行的有:下面是他们官网介绍zipkin的:Zipkin是一个分布式追踪系统。它有助于收集解决服务架构中的延迟问题所需的计时数据。功能包括收集和查找此数据。jaeger:开源,端到端的分布式tracinglabel是指日志的类型,比如SQL、Request、Access、Corn等,userId是指用户信息。当然,有些服务可能没有用户信息,这取决于后端服务的性质。当用户未登录时,将其替换为-1以便于索引。{"userId":10086,//当用户不处于状态时,将"userId"替换为-1:-1,}Node如何登录:winstonwinston是Node中最流行的日志工具,支持各种Transport,它允许你定义各种存储位置和日志格式,当然还有其他选项:例如[]{defaultMeta:{app:'shici-service',serverName:os.hostname(),label}}importwinston,{format}from'winston'importosfrom'os'import{session}from'./session'constrequestId=format((info)=>{//关于CLS中的requestIdinfo.requestId=session.get('requestId')returninfo})functioncreateLogger(label:string){returnwinston.createLogger({defaultMeta:{serverName:os.hostname(),//指定日志类型,如SQL/Request/Accesslabel},format:format.combine(//打印时间戳format.timestamp(),//打印requestIdrequestId(),//以json格式打印format.json()),transports:[//存储在文件中newwinston.transports.File({目录名:'./logs',文件名:`${label}.log`,})]})}constaccessLogger=createLogger('access')日志结构结构化的日志便于索引,JSON是最容易解析的格式。因此,生产环境日志往往以JSON格式打印。可以使用其他格式吗?可以,但是解析有点麻烦。当然JSON也有缺点,就是数据冗余过多会造成带宽的浪费。http{包括mime.types;default_type应用程序/八位字节流;json_log_fieldsmain'remote_addr''remote_user''request''time_local''status''body_bytes_sent''http_user_agent''http_x_forwarded_for';}npmscripts:优化本地日志和过滤在morgan中,可以优化日志的可读性,打印在terminalmorgan(':method:url:status:res[content-length]-:response-timems')上面不管是生产环境还是测试环境本地环境,都使用json格式输出到一份文件。这时候可读性不是很差吗?不用担心,这里使用npmscripts来处理,不仅可读性更好,而且更灵活{"log":"tail-flogs/api-$(date+'%Y-%m-%d').log|jq","log:db":"tail-flogs/db-$(date+'%Y-%m-%d').log|jq"}通过命令行tail和jq,做一个更好的可视化。jq是json处理的命令行工具,需要提前下载$brewinstalljq因为打印日志是基于jq的,所以也可以写jq脚本过滤日志$npmrunlog'。|{message,req}'请求日志:AccessLogAccessLog几乎是一个后端项目中最重要的日志。Morgan在传统的Node项目中经常使用,但是对机器阅读不是很友好。下面是一个基于koa的日志中间件:对于Options、健康检查和一些不重要的请求,不进行日志记录。使用持续时间字段记录响应的执行时间。对于请求的body和query,需要进行序列化(stringify)处理,避免在EliticSearch或者一些日志平台中索引过多,乱序记录全局上下文信息,比如User和一些业务相关的数据//创建一个accesslog并存入./logs/access.logconstaccessLogger=createLogger('access')app.use(async(ctx,next)=>{if(//如果是Optionsandhealthcheck或者不重要的API,那么跳过日志ctx.req.method==='OPTIONS'||_.includes(['/healthCheck','/otherApi'],ctx.req.url)){awaitnext()}else{constnow=Date.now()constmsg=`${ctx.req.method}${ctx.req.url}`awaitnext()apiLogger.info(msg,{req:{..._.pick(ctx.request,['url','method','httpVersion','length']),//body/query被序列化以避免过多的索引body:JSON.stringify(ctx.request.body),query:JSON.stringify(ctx.request.query)},res:_.pick(ctx.response,['status']),//用户信息userId:ctx.user.id||-1,//一些重要的业务相关信息businessId:ctx.business.id||-1,持续时间n:Date.now()-now})}})数据库日志:SQLLog对于流行的服务端框架,一般使用ORM操作来操作数据库。对于Node,在这里选择sequelize。下面是基于sequelize的数据库日志和代码解释:绑定到CLS(ContinuesLocalStorage),可以通过requestId查看每个API对应的数据库查询数,方便定位性能问题。使用duration字段记录查询的执行时间,可以过滤1s以上的数据库操作,发现性能问题。使用tableNames字段记录查询涉及到的表,方便发现性能问题//创建访问日志,存放在./logs/sql.logconstsqlLogger=createLogger('sql')//绑定继续LocalStorageSequelize.useCLS(session)constsequelize=newSequelize({...options,benchmark:true,logging(msg,duration,context){sqlLogger.info(msg,{//记录涉及到的表和类型...__。pick(context,['tableNames','type']),//记录SQL执行时长})},})Redis日志:RedisLogRedis日志一般不是很重要,有需要可以记录如果使用ioredis作为redis操作库,可以侵入Redis.prototype.sendCommand打印日志,封装redis如下importRedisfrom'ioredis'import{redisLogger}from'./logger'constredis=newRedis()const{sendCommand}=Redis.prototypeRedis.prototype.sendCommand=asyncfunction(...options:any[]){constresponse=awaitsendCommand.call(this,...options);}//记录查询日志redisLogger.info(options[0].name,{...options[0],//至于结果,考虑不打印,有时候数据可能太大response})returnresponse}export{redis}微服务请求日志:RequestLog第三方请求可以通过axios发送请求,在axios.interceptors中拦截请求打印日志。主要是这个时候不仅注入了日志,还注入了requestId传递给下一个微服务import{requestLogger}from'./logger'axios.interceptors.request.use(function(config){//在发送请求之前做一些事情constmessage=`${config.method}${config.url}`requestLogger.info(message,config)//从CLS获取RequestId并传递给微服务,形成一个完整的链接config.headers['X-Request-Id']=session.requestIdreturnconfig},function(error){returnPromise.reject(error)})总结问题,思考业务,向全栈方向学习。前端工程系列Node进阶系列在一个后端项目中,需要记录以下类型。本文介绍如何使用Node来完成这些过程,附上代码AccessLog:这是最常见的日志类型。一般在nginx等方向代理中都有日志记录,但业务系统中有时需要更详细的日志记录,比如API耗时,详细的请求体和响应体SQLLog:关于数据库查询的日志,记录SQL,涉及的表,以及执行时间。从此可以过滤掉执行速度太慢的SQL,也可以过滤掉某个API对应的SQL条数。RequestLog:请求第三方服务产生的日志Exception:异常RedisLog:缓存,还有一些非缓存的操作比如zset和分布式锁等MessageQueueLog:记录生产和消费消息的日志我的微信,注意进群,加入进阶前端进阶群

加我微信拉你进面试交流群面试交流群
欢迎关注公众号【全栈成长之路】,定期推送Node原创和全栈成长文章
欢迎关注全栈成长之路