节点日志_您的节点应用程序的az日志记录

节点日志

This article is for all the developers who are either starting out with their nodeJs journey or have built great applications with it but want to understand the best practices and ways of logging. I will be covering a widely popular npm package for logging i.e. Winston and how you can customize it to suit your demands of logging.

本文适用于所有开始他们的nodeJ之旅或已经开发了很棒的应用程序但想要了解日志的最佳实践和方式的开发人员。 我将介绍一个广泛流行的用于日志记录的npm软件包,即Winston,以及如何自定义它以适合您的日志记录需求。

But before diving into the practices let’s start with the fundamental question that may pop in many of your minds - why do we need logs?

但是,在深入实践之前,让我们从您的脑海中浮现出一个基本问题开始-为什么我们需要日志?

日志简介以及我们为什么需要它? (Introduction to logging & why do we need it?)

Speaking strictly in a theoretical manner :

从理论上严格讲:

Logging enables us to persist data of our application which can be used for analysis at a later time

日志记录使我们能够保留应用程序的数据,这些数据可在以后用于分析

The most common use case for logging is to trace how a bug actually occurred in your system. Now some of you may argue that logging adds extra overhead in your development timeline and in case a bug arises later, you can debug the issue pretty easily in your application.

记录的最常见用例是跟踪系统中实际发生错误的方式。 现在,有些人可能会争辩说,日志记录会在开发时间线中增加额外的开销,以防万一以后出现错误,您可以在应用程序中轻松调试问题。

But wait! While this is fair reasoning. I have only one word for you — scale.

可是等等! 虽然这是合理的推理。 我只为你一个字-规模。

What happens when your application scales up to serve a large number of users concurrently and is doing multiple operations simultaneously. Will you be able to figure out the problem without a log to help you?

当您的应用程序扩展为同时服务于大量用户并且同时执行多项操作时,会发生什么情况。 您能在没有日志帮助的情况下解决问题吗?

If your palms are sweating just by playing this horrific scenario in your head, you now have the ansewer to the WHY. Now let’s move to another question that generally follows.

如果您只是在脑海里玩这种可怕的场景而手心冒汗,那么您现在就对WHY有了答案。 现在让我们转到另一个普遍存在的问题。

什么以及何时登录 (What & when to log)

The answer to this question can be summarized in a single line:

这个问题的答案可以总结为一行:

Log as much as you can!

尽可能多地记录日志!

What I mean by that line is the richer your logging is in an application the easier it becomes for you to visualise and take suitable actions on the log data.

我的意思是,您在应用程序中的日志记录越丰富,就越容易对日志数据进行可视化并采取适当的操作。

记录级别 (Levels of logging)

There are many levels at which you can log, which are :

您可以登录的级别很多,分别是:

  • debug

    调试
  • verbose

    冗长的
  • info

    信息
  • warn

    警告
  • error

    错误

A combination of these levels can be used to enable proper logs for every environment of your application. For eg., you will need debug/warn logs in your development & testing environments but would prefer to not have them in your staging & production environments.

这些级别的组合可用于为应用程序的每个环境启用正确的日志。 例如,您将需要在开发和测试环境中使用调试/警告日志,但更希望不在暂存和生产环境中使用它们。

console.log有什么问题? (What’s the issue with console.log ?)

For those of you thinking why we can’t use the default console.log for logging, I would like to clarify that there is nothing wrong with using the inbuilt function provided by node but you will find that Winston provides a lot of other features which help you in designing a scalable system. A few of them are as follows:

对于那些想知道为什么我们不能使用默认console.log进行日志记录的人,我想澄清一下,使用node提供的内置函数没有什么问题,但是您会发现Winston提供了许多其他功能,帮助您设计可扩展的系统。 其中一些如下:

  • Streams: Winston internally uses streams provided by node which helps it process a large amount of data without incurring a large overhead on your application. You can read about streams in detail here.

    流: Winston在内部使用节点提供的流,这有助于它处理大量数据,而不会在您的应用程序上产生大量开销。 您可以在此处详细了解流。

  • Transports: Apart from logging just to the console or standard output, you may also want to write data to files or store them in the cloud or stream them to other third-party services for analysis. Winston provides you a fairly simple way of managing all of these in a centralized manner.

    传输:除了仅记录到控制台或标准输出外,您可能还希望将数据写入文件或将其存储在云中,或将其流式传输到其他第三方服务以进行分析。 Winston为您提供了一种相当简单的方法来集中管理所有这些。

  • Formatting: The default console function only deals with strings and hence doesn’t provide a lot of flexibility in formatting your log messages. In comparison, Winston provides advanced features to color your logs, JSON formatting and even transforming your logs before sending them to any of your desired end log locations.

    格式化:默认的控制台功能仅处理字符串,因此在格式化日志消息时没有提供很大的灵活性。 相比之下,Winston提供了高级功能,可以在将日志发送到任何所需的最终日志位置之前为日志着色,JSON格式甚至转换日志。

Now that we have our basics clear, let’s dive into setting up Winston for logging inside our application.

现在我们已经弄清了基础知识,让我们开始设置Winston以便在应用程序内部登录。

设置温斯顿 (Setting up Winston)

Installing Winston is pretty straight-forward. Just run the following command.

安装Winston非常简单。 只需运行以下命令。

npm i winston --save

And then you can set it up using the below code in your application and save it to any file(I have saved it in logger.js).

然后,您可以在应用程序中使用以下代码进行设置,并将其保存到任何文件中(我已经将其保存在logger.js中)。

const { createLogger, transports } = require('winston');
const generalLogger = createLogger({
transports : [
new transports.Console()
]
});generalLogger.stream = {
write : (info) => {
generalLogger.info(info);
}
};
let log = (message) => {
generalLogger.log({
level : 'verbose',
message : message
});
};
let debug = (message) => {
generalLogger.log({
level : 'debug',
message : message
});
};
let info = (message) => {
generalLogger.log({
level : 'info',
message : message
});
};
let warn = (message) => {
generalLogger.log({
level : 'warn',
message : message
});
};
let error = (message) => {
generalLogger.log({
level : 'error',
message : message
});
};
module.exports = {
log,
debug,
info,
warn,
error
};

And then use the file in your main file (I have used index.js).

然后在您的主文件中使用该文件(我已经使用过index.js)。

const express = require('express');
const logger = require('./logger');
const APP_DEFAULT_PORT = 12341;
const app = express();
app.set('port', process.env.PORT || APP_DEFAULT_PORT);
const server = app.listen(app.get(’port’), function() {
logger.info(`Express server listening on port ${server.address().port}`);
});

Running this app would give you an output of the following form.

运行该应用程序将为您提供以下形式的输出。

Image for post

This output seems very basic, so let’s try formatting it a bit.

此输出看起来非常基础,因此让我们尝试对其进行格式化。

For this, we’ll implement a custom logger and use the functionality provided by Winston

为此,我们将实现自定义记录器并使用Winston提供的功能

Change your logger file to accommodate the following coding lines.

更改您的记录器文件以适应以下代码行。

const { createLogger, format, transports } = require('winston');
const { combine, timestamp, printf, colorize } = format;
const formatter = printf((info) => {
let object = {
message : info.message
};
return `${info.timestamp} ${info.level} ${JSON.stringify(object)}`;
});
const generalLogger = createLogger({
format : combine(
colorize(),
timestamp(),
formatter
),
transports : [
new transports.Console({
json : true,
colorize : process.stdout.isTTY,
timestamp: function () {
return (new Date()).toLocaleTimeString();
},
prettyPrint : true
})
]
});

Add the following extra logs in your main file.

在主文件中添加以下额外的日志。

logger.warn(`I am a warn log message`);
logger.error(`I am an error log message`);

You would get a colored response with a timestamp as shown below.

您将获得带有时间戳的彩色响应,如下所示。

Image for post

Look closely at the code above and you will find that we have specified a colorize key while declaring the logger.

仔细查看上面的代码,您会发现我们在声明记录器时指定了一个colorize键。

process.stdout.isTTY

The statement above evaluates to true only if logging is happening on standard output. Now that we have taken care of standard logging, let’s think about some corner cases. If you want you can look and explore the other options of Winston here.

仅当在标准输出上记录日志时,以上语句的评估结果才为true。 现在我们已经处理了标准日志记录,现在让我们考虑一些极端情况。 如果您愿意,可以在此处查找和探索Winston的其他选项。

Add the following line in your main file and then observe what happens when you execute it.

在主文件中添加以下行,然后观察执行该文件时发生的情况。

try{
throw new Error('I am a custom error');
}catch(err){
logger.error(err);
}

The error message evaluates to {}, but why?

错误消息的评估结果为{},但是为什么呢?

If you read the javascript documentation you’ll come to know that the error object does not have any enumerable properties. So in order to counter that we’ll use the extra option of JSON.stringify function to our advantage and change the code as follows:

如果您阅读了javascript文档,就会知道该错误对象没有任何可枚举的属性 。 因此,为了解决这个问题,我们将使用JSON.stringify函数的额外选项来发挥我们的优势,并按如下所示更改代码:

const replaceErrors = (key, value) => {
if (value instanceof Error) {
let error = {};
Object.getOwnPropertyNames(value).forEach((newKey) => {
error[newKey] = value[newKey];
});
return error;
}
return value;
};

And use it as shown below.

并如下所示使用它。

JSON.stringify(object, replaceErrors);

And now run the app again, you would get the correct output.

现在再次运行该应用程序,您将获得正确的输出。

Image for post

Finally, to complete your express server we would need to log request and response. And we can log each and every request and response of the system using the code below. Create a new file (I called it requestLogger.js) and add the following to it.

最后,要完成您的快递服务器,我们需要记录请求和响应。 我们可以使用以下代码记录系统的每个请求和响应。 创建一个新文件(我将其称为requestLogger.js),并将以下内容添加到其中。

const logger = require('./logger');const logRequestMeta = (req, response) => {
const { url, body = {}, headers = {}, params = {}, query = {} }
= req; let logObject = {
url,
body,
headers,
params,
query,
response
}; logger.info(logObject);
};const logResponse = (req, res, next) => {
let originalSend = res.send;
let chunks = []; res.send = function(chunk) {
let response, body; if (chunk) {
chunks.push(Buffer.from(JSON.stringify(chunk)));
} try {
body = Buffer.concat(chunks).toString('utf8');
} catch (err) {
logger.error(err);
} try {
response = JSON.parse(body);
} catch (err) {
response = String(body);
} logRequestMeta(req, response); originalSend.apply(res, arguments);
}; next();
};module.exports = logResponse;

In the function logRequestMeta we have extracted the basic parameters from the request and de-structured the same and sent to our previously implemented logger function.

在函数logRequestMeta中,我们从请求中提取了基本参数,并对其进行了解构,然后发送给了我们先前实现的记录器功能。

In logResponse function we create a wrapper around the res.send function exposed by nodeJs and call our custom logger before the response is sent to the system. I have implemented it in such a way that it can be used as a middleware, you can achieve the same with the following code.

logResponse函数中,我们围绕nodeJs公开的res.send函数创建包装器,并在将响应发送到系统之前调用自定义记录器。 我已经实现了它可以用作中间件的方式,您可以通过以下代码实现相同的目的。

app.use(logResponse);

NOTE: you should use this middleware before declaring any routes in your app so that each and every request and response is logged correctly.

注意:您应该在声明应用程序中的任何路由之前使用此中间件,以便正确记录每个请求和响应。

Create a dummy route in your application and test the code and you should have an output like the following.

在您的应用程序中创建一个虚拟路由并测试代码,您应该获得类似以下的输出。

Image for post

结论 (Conclusion)

By completing the above steps you have successfully configured the following types of logs for your application :

通过完成上述步骤,您已经成功为应用程序配置了以下类型的日志:

  • General logs

    常规日志
  • Errors

    失误
  • Request-response

    请求-响应

You can always add extra features to suit the needs of your application and make it handle all types of use cases you might encounter in the real-world scenario.

您始终可以添加额外的功能来满足您的应用程序的需求,并使其能够处理您在实际场景中可能遇到的所有类型的用例。

You can refer to the git repository for the same or drop a comment if you want to ask anything.

您可以参考git存储库中的相同内容,也可以在任何问题上发表评论。

At the end I would just say good luck building your application and

最后,我只是说祝您构建应用程序好运,

Happy coding :)

快乐的编码:)

翻译自: https://medium.com/@aashishail/a-z-logging-for-your-node-application-adedee467da2

节点日志

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值