JSNLog - JavaScript 日志集成 ASP.NET 和 MVC






4.80/5 (7投票s)
JSNLog 允许您在 JavaScript 代码中放置日志记录器,在 web.config 中配置它们,并在服务器上捕获它们的输出。
了解您的 JavaScript 应用程序在上线后在做什么。
引言
JavaScript 应用程序就像您最喜欢的孩子。您会充满爱意地对待它,尽您所能地为它的生活做好准备,然后将其投入生产环境,在那里它将面对您从未想象过的用户以及比您想象的更慢的服务器。
问题在于,现在您的应用程序已上线,会有用户。如果用户不满意,他们会向您的经理(或您的客户)抱怨。这反过来又可能让您不开心。您不希望用户不满意。
为了让您的用户满意,您需要了解他们的体验是否良好,如果不良好,就进行修复。在餐厅里,服务员会到您的餐桌旁询问一切是否都好。您可以打电话给用户,但除非您只有几位,否则向应用程序询问会更容易。毕竟,它能以一种餐厅餐饮无法做到的方式与您交流。
具体来说,您将想知道:
- 异常。任何语法错误或未定义的变量都会导致其中之一。是的,无论您多么精心编写和测试代码,它们都会发生。
- 失败的 AJAX 请求。您会想了解不可靠的服务器。
- 冗长的 AJAX 请求。用户等待 10 秒钟仍然看到加载动画并不开心。当一个请求花费太长时间时,您需要了解它,以便知道它发生的频率、涉及的服务器等。
- 调试数据。用户报告了您在开发环境中无法重现的错误。因此,您会添加代码来告诉您生产环境中发生了什么。您希望从一个中心点控制这一点,而无需深入到代码的深处添加和删除行。
好吧,所以您需要客户端日志记录。但是
- 您不想被日志数据淹没。您需要过滤器来只获取有用的信息。
- 日志数据需要进入您的服务器端日志。这样,您就可以访问它们。
- 您希望将客户端和服务器端日志数据关联起来,以便知道哪些日志数据属于同一个请求。
- 理想情况下,您可以通过服务器上的配置文件来控制所有内容,而无需更改您的 JavaScript 代码。
有很多 JavaScript 日志包,包括 JSLog、Log4js 和 log4javascript。在本文中,您将了解 JSNLog,一个相对较新的 JavaScript 包,它满足了所有这些要求。它是开源的,并且大小约为 1.5kb,大约相当于一个小图像的大小。它还与 ASP.NET MVC 和 Webforms 集成得非常好,并且还集成了 PHP。
如果您喜欢这篇文章,请 投票支持。
本系列内容
- JavaScript 日志记录的原理和方法
- 在 JavaScript 中插入日志记录器
- 在 web.config 中配置您的 JavaScript 日志记录器
让我们开始记录
然后,要在服务器上记录某些内容,请在您的 JavaScript 中编写类似以下内容:
JL().info("log message");
这会调用函数 JL(),该函数返回一个日志记录器。然后,它在该日志记录器上调用 info 函数来记录一条信息消息。
JSNLog 提供了许多选项,命名日志记录器等。但如果您只想记录某条内容,那就足够了。
您可以传递字符串、对象和函数。如果您传递一个函数,它将执行该函数,然后记录函数返回的任何内容。
它不仅发送日志数据,还发送时间、浏览器类型等。
工作原理
JavaScript 日志记录器将消息传递给 AJAX appender,这是 JSNLog 内部的一段代码,它将日志消息发送到运行您的 MVC 或 Webforms 网站或 PHP 网站的服务器。JSNLog 服务器端组件然后使用您已有的任何日志记录包(对于 .Net,支持 Log4Net、NLog、Elmah 和 Common.Logging)将日志消息存储在您的服务器端日志中。

如果您使用 Java、Python 或其他没有服务器端组件的技术,您可以使用独立的 JSNLog.js 库。JSNLog 文档提供了日志消息的格式,使其易于自己创建服务器端组件。
如果“appender”和“info”等术语听起来很熟悉,那是有道理的。JSNLog 的灵感来自 Log4J 及其分支,但增加了自己的一些特色,如简短简化的语法和使它在客户端工作得更好的功能。
本文的其余部分假设您有一个 ASP.NET MVC 或 Webforms 网站。它展示了如何从服务器端的 web.config 文件控制您的 JavaScript 日志记录器,所有 JSNLog 定义都位于 <jsnlog> 元素中。
但请注意,您也可以在 JavaScript 中轻松配置您的日志记录器(方法)。此外,JSNLog 带有合理的默认配置,因此对于简单的日志记录,您根本不需要进行配置。
控制台 Appender
在开发过程中,只需将日志项发送到 Chrome、Firefox 和 Internet Explorer 的 F12 开发者工具提供的控制台可能会很有用。为了启用此功能,JSNLog 包括一个控制台 Appender。

实际场景
要记录任何异常,以便您知道您的代码中存在错误,您可以使用 windows.onerror。浏览器为每个未捕获的异常调用此处理程序。
window.onerror = function myErrorHandler(errorMsg, url, lineNumber) { // Send message with severity fatal JL().fatal("Exception - " + errorMsg + ", url: " + url + ", line number: " + lineNumber); return false; }
如果您只对某些特定代码中的异常感兴趣,而不是所有异常,您可以使用 try catch。
try { // Code that may throw exception } catch (e) { // e contain a string or object with exception data JL().fatalException("message", e); // Handle the exception, ignore it, or throw it again using throw e }
fatalException 同时接受消息和异常。两者都将在同一日志消息中记录。在 Chrome、Firefox 和 IE10 及更高版本中,还会记录堆栈跟踪。
如果 AJAX 请求失败或花费时间过长,则会收到一条日志消息。
var msBeforeAjaxCall = new Date().getTime(); // Send AJAX request and receive response <a href="https://api.jqueryjs.cn/jQuery.ajax/">$.ajax( "url" ) .done(function() { alert( "success" ); }) .fail(function() { // Send a message with severity error if AJAX request failed JL().error("Request to ..... failed."); }) .always(function() { // Send error message if response took longer than 10 seconds to arrive var msAfterAjaxCall = new Date().getTime(); var timeTakenInMs = msAfterAjaxCall - msBeforeAjaxCall; if (timeTakenInMs > 10000) { JL().error("Request to ..... took " + timeTakenInMs + " ms."); } });
减少生成的日志数据
日志记录器的一个问题是它们会生成大量无用数据。JSNLog 包含许多选项,可帮助您获取所需的数据。
打开和关闭 JavaScript 日志记录器
当您记录某条内容时,您可以为其指定严重性。
JL().trace("message with severity trace"); JL().debug("message with severity debug"); JL().info("message with severity info"); JL().warn("message with severity warn"); JL().error("message with severity error"); JL().fatal("message with severity fatal");
在前面的示例中,您已经看到了 info()、error() 和 fatal()。
过滤掉低于给定严重性的所有消息。
<!--Only send error and fatal log messages--> <logger name="" level="ERROR" />
停止所有消息。
<!--Stop all messages--> <logger name="" level="OFF" />
命名日志记录器
如果您在多个地方进行日志记录,只有一个日志记录器很快就会变得有限。如果您想在某些函数中进行调试日志记录,而在其他所有地方只进行错误日志记录,该怎么办?
解决方案是使用命名日志记录器。
<!--Set options on logger1. If it does not exist, it will be created.--> <logger name="logger1" level="DEBUG" /> <!--Set other options on logger2. This does not affect logger1.--> <logger name="logger2" level="ERROR" />
在您的 JavaScript 代码中,使用命名日志记录器的外观如下:
JL("logger1").error("message from logger1");
在拥有数十个日志记录器的应用程序中,单独设置每个日志记录器的选项可能会很枯燥。因此,JSNLog 使用与 Log4J 和 Log4Net 相同的继承机制。以下是它的工作原理:
- 日志记录器名称可以有多个部分,用句点分隔,例如“abc.def”和“abc.def.ghi”。
- 如果日志记录器名称的部分与另一个日志记录器名称的第一个部分匹配,则第一个日志记录器被称为其他日志记录器的父级。例如:
- 日志记录器“abc.def.ghi”的父级是日志记录器“abc.def”。
- 日志记录器“abc.def”的父级是日志记录器“abc”。
- 日志记录器“abc”的父级是根日志记录器。
日志记录器从其祖先继承其选项。因此,如果您在日志记录器“abc”上设置了选项,那么“abc.def”、“abc.def.ghi”以及“abc”的所有其他后代都将获得相同的选项,除非您特别覆盖它们。
根日志记录器是唯一没有名称的日志记录器。正如您在本文开头所见,您只需省略名称即可访问它。
<!--Set options on root logger--> <logger name="" level="ERROR" /> <!--Or leave out the name attribute altogether--> <logger level="ERROR" />
既然您已经了解了如何按严重性过滤消息以及如何通过继承配置多个 JavaScript 日志记录器,那么让我们来看看其他选项。
按正则表达式过滤
有时修复一些罕见的异常并不值得花费时间。或者有一个第三方组件抛出异常,而您无能为力。
disallow 选项会抑制所有匹配给定正则表达式的消息。如果您记录的是一个对象,它将被转换为 JSON 然后进行匹配。
<!--Suppress messages that match regular expression [Ss]uppress\s+me--> <logger name="" disallow="[Ss]uppress\\s+me" />
按浏览器类型过滤
想象一下试图解决一个仅在每个人都喜欢的浏览器 IE 7 和 8 中才会出现的错误。您想启用 JavaScript 日志记录器,但仅限于那些浏览器。
userAgentRegex 选项是您的朋友。它会禁用日志记录器,除非客户端的用户代理字符串匹配给定的正则表达式。
<!--Disable logger, except for IE 7 and IE 8--> <logger name="" userAgentRegex="MSIE 7|MSIE 8" />
过滤掉(近乎)重复项
想象一个循环执行 50 次,而在这个循环的某个地方,一个日志记录器决定发送一条消息。您会收到 50 次相同的消息,如下所示。哎哟!
Something is wrong - x = 1 Something is wrong - x = 2 Something is wrong - x = 3 ... Something is wrong - x = 49 Something is wrong - x = 50
onceOnly 选项将为您清除近乎重复的消息。它接受一个或多个正则表达式。当它看到一条与其中一个匹配的消息时,它会记住它已经看到了一条匹配该特定正则表达式的消息。如果它收到另一条匹配该正则表达式的消息,它将对其进行抑制。
<!--Send only one message that matches regular expression "Something is wrong - x ="--> <logger name="" onceOnly="Something is wrong - x =" />
有了 onceOnly 选项,您只会收到第一条消息。
Something is wrong - x = 1
限制消息数量
您可以设置 maxMessages 选项来限制客户端发送的总消息数量。这是一个粗暴的工具,因为它在达到限制后停止发送消息,而不管哪些 JavaScript 日志记录器正在尝试发送或消息的严重性如何。
<!--Ensure that the client sends no more than 3 log messages--> <jsnlog maxmessages="3" > </jsnlog>
请注意,此选项应用于 <jsnlog> 元素本身。这是因为此选项适用于整个 JSNLog 库,而不是单个日志记录器。
仅当发生异常时才发送调试消息
当异常发生时,并不总是清楚它发生的原因。因此,您可能需要在多个地方生成调试消息以查找原因。
window.onerror = function myErrorHandler(errorMsg, url, lineNumber) { JL().fatal("Exception - " + errorMsg + ", url: " + url + ", line number: " + lineNumber); return false; } function abc(x) { JL().debug("abc: x=" + x); ... } function def(y) { JL().debug("def: y=" + y); ... }
问题在于,您想要调试信息,但前提是发生异常。如果没有异常,调试信息就没有价值。
为了解决这个问题,您可以将调试消息存储在浏览器内存中,并且仅在发生致命消息时才将其发送到服务器。由于这可能涉及多个命名 JavaScript 日志记录器,因此它是在AJAX appender(JSNLog 组件,它接收来自 JavaScript 日志记录器的消息并将其发送到服务器)上配置的。
以下代码将使这一切生效。有关详细信息,请参阅 JSNLog 文档。
<!--Create new appender--> <ajaxAppender name="appender1" storeInBufferLevel="DEBUG" level="ERROR" sendWithBufferLevel="FATAL" bufferSize="20"/> <!--Tell the root logger to send all messages to the new appender--> <logger appenders="appender1"/>
这一切魔法的结果是什么?
- 创建了一个新的 appender。
- 根日志记录器被告知将其所有消息发送到新的 appender。由于根日志记录器是所有日志记录器的祖先,因此您拥有的任何其他日志记录器现在也将把它们的日志发送到新的 appender。
- 当 appender 从日志记录器接收到严重性debug 或更高的消息时,它会将其存储在内存缓冲区中。
- 当它接收到严重性error 或更高的消息时,它不会被缓冲而是发送到服务器。缓冲区中的所有其他内容仍然保留在缓冲区中。
- 当它接收到严重性fatal 的消息时,它会与缓冲区中的所有消息一起发送到服务器。
- 缓冲区最多可容纳 20 条消息。如果需要存储另一条消息,将删除缓冲区中最旧的消息。
这样,您就可以生成任意多的调试数据,而无需担心被淹没,因为只有在您真正需要时才会发送它们。
关联与请求相关的所有消息
在复杂的应用程序中,您可能会为单个请求获得多个日志消息,有些在服务器端生成,有些在客户端生成。您将希望用请求 ID 标记每条日志消息。这样,您就可以按请求 ID 和时间戳对日志进行排序,以获得每个请求所发生事件的统一视图,首先是在服务器端,然后是在客户端。
JSNLog 的 .Net 版本为每个请求生成一个唯一的 ID。当 JavaScript 日志记录器发送日志消息时,该消息包含请求 ID,因此可以在您的服务器端日志中记录。
它还提供了一个服务器端方法 RequestId,它为您提供相同的 ID。这样,您就可以在服务器端日志记录器记录消息时包含相同的请求 ID。
JSNLog 使用 IIS 内部使用的相同请求 ID。这样,您可以与使用Windows 事件跟踪 (ETW) 进行的跟踪进行关联。如果您没有启用 ETW 跟踪,JSNLog 会生成一个随机 GUID。
高级选项
JSNLog 支持超出本文范围的高级选项。这些选项包括将多个日志消息批量处理到单个请求中以提高效率,以及按客户端 IP 地址过滤以调查特定用户报告的问题。
结论
一旦您的 JavaScript 上线,它就容易出现意外事件,例如异常、失败的 AJAX 请求以及耗时过长的 AJAX 请求。要了解这些问题以便快速修复,您需要将它们记录到您的服务器端日志中。
JSNLog 是一个相对较新的客户端日志记录包。它针对客户端上的生活进行了高度优化,将日志消息发送到运行您的 MVC、Webforms 或 PHP 网站的服务器,并包含许多选项来减少日志消息的量。
下一部分将详细介绍 JSNLog 提供的所有 JavaScript 函数。