客户端应用程序
这篇文章由Panayiotis«pvgr»Velisarakos , James Wright和Stephan Max进行了同行评审。 感谢所有SitePoint的同行评审员使SitePoint内容达到最佳状态!
目录
无论是在主动开发期间还是在生产模式下运行时,日志记录都是任何软件应用程序的重要组成部分。
在服务器上工作时,无论选择哪种服务器端语言,都有数百种可用的库,广泛的存储机制以及可用于处理结果日志的各种工具。
但是,当涉及到客户端应用程序时,日志记录通常会被忽略,并且向您开放的选项受到了更大的限制。
在本文中,我将介绍在客户端应用程序中实现日志记录的一些方法。 特别是在JavaScript繁重的单页应用程序(SPA)中。
控制台
记录错误和消息的最常见,最明显的方法就是控制台 。 尽管它似乎是一个原始的解决方案,但毫无疑问,它是开发过程中调试的宝贵工具,因此它可能是一个不错的起点。
console
的实现并不总是一致的-尤其是在IE中,也许不足为奇-但总的来说,有四种主要方法可供您使用:
console.log()
console.info()
console.warn()
console.error()
这四种方法中每种方法的输出都略有不同,大多数Web控制台实现(即Dev Tools)都允许您根据所使用的方法来过滤消息。 即日志记录级别。
为了减轻浏览器之间的差异,您可以使用包装函数,例如Paul Irish的包装函数。 WHATWG正在尝试标准化控制台API ,但是该规范仍处于早期阶段,不太可能在一段时间内实现。
提示:如果发现代码中充满
console.log()
语句,则可能会发现诸如grunt-remove-logging或grunt-strip(对于Grunt)或gulp-strip-debug(对于Gulp)等工具对于移动应用到生产中。
增强控制台
您可以使用几个库对控制台进行“超级充电”。
登出
Logdown是一个很小的库,它为控制台提供了一些增强功能。 您将在此处找到一个演示 。
Logdown允许您在实例化时指定前缀; 一种可能的用途是按模块分隔您的日志消息,例如:
var uiLogger = new Logdown({prefix: 'MyApp:UI'});
var networkServiceLogger = new Logdown({prefix: 'MyApp:Network'});
然后,您可以按其前缀启用或禁用记录器,例如:
Logdown.disable('MyApp:UI');
Logdown.enable('MyApp:Network');
Logdown.disable('MyApp:*'); // wildcards are supported, too
禁用记录器实际上可以使其静音。
一旦对一个或多个记录器进行了实例化,就可以使用log()
, warn()
, info()
和error()
方法记录消息:
var logger = new Logdown();
logger.log('Page changed');
logger.warn('XYZ has been deprecated in favour of 123');
logger.info('Informational message here');
logger.error('Server API not available!');
Logdown还提供Markdown支持:
var logger = new Logdown({markdown: true}); // Technically "markdown: true" isn't required; it's enabled by default
logger.warn('_XYZ_ has been *deprecated* in favour of _123_');
控制台消息
console.message是另一个美化控制台输出的库。
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
这是文档中的快速动画,展示了其某些功能:
本质上,该库提供了一种可链接的接口,其中包含允许您格式化文本,将消息分组在一起并使它们可折叠,将交互式DOM元素或对象发送到日志(甚至包括图像)的方法。
控制台的局限性
在构建应用程序时,该控制台非常有用,并且可以在您面前打开它,但是除非您碰巧看着用户的肩膀, 并且碰巧他们在浏览器中打开了Web控制台,否则您不会不能看到结果。
相反,我们可以做的是将任何错误(甚至在开发过程中调试消息)发送到某处的服务器,以便我们可以远程访问它们。
其他要考虑的事情
现在,我们已经为您提供了一些解决方案,下面让我们看一些其他注意事项。
捕获全局错误
至少,值得捕获和记录任何未处理的异常。 您可以使用window.onerror
做到这一点。 这是一个非常简单的示例:
window.onerror = function(message, file, line) {
console.log('An error occured at line ' + line + ' of ' + file + ': ' + message);
};
堆栈痕迹
发生错误时, 堆栈跟踪可提供更多详细信息,您可能希望在开发中使用它。 有几个库可以帮助构建它们。
跟踪工具
TraceKit允许您将堆栈跟踪注入异常中,并对它们进行某些处理(例如,将其发送到服务器端日志记录组件),方法是订阅它们。
代码如下所示:
TraceKit.report.subscribe(function yourLogger(errorReport) {
//send via ajax to server, or use console.error in development
//to get you started see: https://gist.github.com/4491219
});
然后,在您的应用程序中:
try {
/*
* your application code here
*
*/
throw new Error('oops');
} catch (e) {
TraceKit.report(e); //error with stack trace gets normalized and sent to subscriber
}
stacktrace.js
引用该文档, stacktrace.js是“ [a]框架无关的微库,用于在所有Web浏览器中获取堆栈跟踪”。
它提供了一个名为printStackTrace()
的方法,您可以在错误处理程序中使用该方法将堆栈跟踪添加到日志记录函数中。 例如,我们可以如下增强服务器端记录器:
function log(data, level) {
$.post(
'https://your-app.com/api/logger',
{
context : navigator.userAgent,
level : level || 'error',
data : data,
stack_trace : printStackTrace()
}
);
}
将客户端错误记录到服务器
将日志条目发送到服务器具有许多优点:
- 您可以从应用程序中捕获日志条目,而无需亲自坐在计算机上(完美地投入生产)
- 您可以在同一位置(可能使用相同的工具)管理服务器端和客户端日志
- 您可以设置警报(例如,如果发生严重错误,则显示松弛通知或短信)
- 控制台不可用或难以查看的地方(例如,使用手机的网络视图时),更容易查看正在发生的情况
让我们看一下几种方法。
滚动自己的服务器端记录器
在某些情况下,最简单的解决方案可能是使用自己的服务器端日志记录机制。
这是使用jQuery的客户端部分的极小示例:
function log(data, level) {
$.post(
'https://your-app.com/api/logger',
{
context : navigator.userAgent,
level : level || 'error',
data : data
}
);
}
一些用法示例:
try {
// some function
} catch (e) {
log({
error : e.message
});
}
log('Informational message here', 'info');
考虑到这一点,下面是一个非常基本的服务器端组件,该示例与Node.js和Express一起使用,并带有出色的Winston日志记录库:
/**
* Load the dependencies
*/
var express = require( 'express' );
var bodyParser = require('body-parser');
var winston = require( 'winston' );
/**
* Create the Express app
*/
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
/**
* Instantiate the logger
*/
var logger = new ( winston.Logger )({
transports: [
new ( winston.transports.Console )(
{
level: 'error'
}
),
new ( winston.transports.DailyRotateFile )(
{
filename: 'logs/client.log',
datePattern: '.yyyy-MM-dd'
}
)
]
});
app.post ('/api/logger', function( req, res, next ) {
logger.log(
req.body.level || 'error',
'Client: ' + req.body.data
);
return res.send( 'OK' );
});
var server = app.listen( 8080, function() {
console.log( 'Listening on port %d', server.address().port );
});
实际上,这种过度简化的记录器存在一些基本限制:
- 大多数日志记录机制允许您配置最低日志记录级别,以便可以过滤出某些条目
- 它将立即发送日志条目,这可能导致服务器端组件过载
解决第二个问题的更好方法是缓冲日志条目并分批发送。 一种常见的方法是使用localStorage
存储日志条目,然后以特定的时间间隔发送日志-基于时间,当达到未决条目数量的特定阈值时,或者当用户关闭窗口或导航到远离您的位置时利用window.onbeforeunload
事件应用程序。
为了解决这些问题,让我们看一个现成的解决方案,用于从JS应用程序进行日志记录。
log4javascript
log4javascript基于无处不在的log4j ,它也是已移植到PHP的Java日志记录框架,因此,如果您来自服务器端背景,则可能已经对此有所了解。
log4javascript使用appenders的概念,该概念决定了在调用其日志记录方法之一时会发生什么。 当您拥有大多数现代浏览器提供的开发工具时,默认的PopUpAppender
可以说是相当多余的。
也许更有用的是AjaxAppender ,您可以使用它将日志条目发送回服务器。 您可以配置AjaxAppender
以一定的时间间隔发送使用分批条目setTimed()
使用一定数量的setBatchSize()
或当使用卸载窗口setSendAllOnUnload()
可以从Sourceforge下载log4javascript,或者在Github上可以使用类似的Log4js。 您可以参考快速入门来快速启动和运行。
这是一个例子:
var log = log4javascript.getLogger();
var ajaxAppender = new log4javascript.AjaxAppender('http://example.com/api/logger');
ajaxAppender.setThreshold(log4javascript.Level.ERROR);
ajaxAppender.setBatchSize(10); // send in batches of 10
ajaxAppender.setSendAllOnUnload(); // send all remaining messages on window.beforeunload()
log.addAppender(ajaxAppender);
或者,以特定的时间间隔发送消息:
ajaxAppender.setTimed(true);
ajaxAppender.setTimerInterval(10000); // send every 10 seconds (unit is milliseconds)
其他图书馆
如果您的项目使用jQuery,则可能需要研究jquery logger ,它允许您通过Ajax进行登录。 但是,它不支持批处理。 不过,它确实与Airbrake很好地集成为后端。
loglevel是一个轻量级且可扩展的基于JS的日志记录框架,它通过单独的serverSend插件支持Ajax。
推出自己的批次兼容记录仪
这是一个记录器的简单概念证明,它可以批量发送消息。 它是使用具有ES6功能的原始JavaScript编写的。
"use strict";
class Logger {
// Log levels as per https://tools.ietf.org/html/rfc5424
static get ERROR() { return 3; }
static get WARN() { return 4; }
static get INFO() { return 6; }
static get DEBUG() { return 7; }
constructor(options) {
if ( !options || typeof options !== 'object' ) {
throw new Error('options are required, and must be an object');
}
if (!options.url) {
throw new Error('options must include a url property');
}
this.url = options.url;
this.headers = options.headers || [ { 'Content-Type' : 'application/json' } ];
this.level = options.level || Logger.ERROR;
this.batch_size = options.batch_size || 10;
this.messages = [];
}
send(messages) {
var xhr = new XMLHttpRequest();
xhr.open('POST', this.url, true);
this.headers.forEach(function(header){
xhr.setRequestHeader(
Object.keys(header)[0],
header[Object.keys(header)[0]]
);
});
var data = JSON.stringify({
context : navigator.userAgent,
messages : messages
});
xhr.send(data);
}
log(level, message) {
if (level <= this.level) {
this.messages.push({
level : level,
message : message
});
if (this.messages.length >= this.batch_size) {
this.send(this.messages.splice(0, this.batch_size));
}
}
}
error(message) {
this.log(Logger.ERROR, message);
}
warn(message) {
this.log(Logger.WARN, message);
}
info(message) {
this.log(Logger.INFO, message);
}
debug(message) {
this.log(Logger.DEBUG, message);
}
}
用法很简单:
var logger = new Logger({
url : 'http://example.com/api/batch-logger',
batch_size : 5,
level : Logger.INFO
});
logger.debug('This is a debug message'); // No effect
logger.info('This is an info message');
logger.warn('This is a warning');
logger.error('This is an error message');
logger.log(Logger.WARN, 'This is a warning');
基于服务器的自托管选项
厄比特
Errbit是用于捕获错误的开源,自托管解决方案。 它在Ruby中实现,并使用MongoDB进行存储。
如果您想快速了解Errbit ,可以使用Chef Cookbook或Dockerfile 。 还有一个在线演示,您可以尝试。
要登录在线演示,请使用电子邮件
demo@errbit-demo.herokuapp.com
和密码password
。
基于SaaS服务器的选项
有许多用于日志记录的SaaS解决方案。 这些包括Loggly , track.js , ErrorCeption , Airbrake和New Relic 。
让我们简要看一下一些这样的解决方案。
Loggly
Loggly是这些SaaS解决方案中的一种。 我将以它为例,因为它很容易并且免费上手。 使用免费计划,您每天最多可以登录200MB,数据存储7天。
要从客户端应用程序使用Loggly,您需要包含以下代码段:
<script type="text/javascript" src="http://cloudfront.loggly.com/js/loggly.tracker.js" async></script>
<script>
var _LTracker = _LTracker || [];
_LTracker.push({'logglyKey': 'YOUR-LOGGING-KEY',
'sendConsoleErrors' : true });
</script>
注意:您需要通过使用Source Setup来将
YOUR-LOGGING-KEY
替换为应用程序特定的值,该值将在您注册并登录后得到。
如果检查此代码,您会看到_LTracker
对象最初被实例化为数组。 这是许多分析库中使用的“填充”技术,这意味着您可以在加载库之前对其调用push()
。 当库可用时,您推送到该阵列的任何错误或消息都将排队等待。
用法很简单:
_LTracker.push(data);
您可以使用它发送一段文本:
_LTracker.push( 'An error occured: ' + e.message );
或者,也许更有用的是,您可以使用JSON-例如:
try {
// some operation
} catch (e) {
_LTracker.push({
level : 'error',
message : e.message,
trace : e.trace,
context : navigator.userAgent
});
}
虽然这是一个非常基本的解决方案,但是您可以简单地使用以下代码捕获错误:
window.onerror = function(message, file, line) {
_LTracker.push({
context: navigator.userAgent,
error: message,
file: file,
line: line
});
};
这种方法有一些局限性。 如果您的内部版本稍有不同,或者要缩小JS代码,那么行号实际上是没有用的。
您还会在上面的Loggly代码段中注意到sendConsoleErrors
设置为TRUE
,这将自动为您记录某些错误,而无需手动发送。 例如,如果RequireJS超时发生,则以下内容将发送到Loggly:
{
"category": "BrowserJsException",
"exception": {
"url": "http://example.com/js/require.js",
"message": "Uncaught Error: Load timeout for modules: main\nhttp://requirejs.org/docs/errors.html#timeout",
"lineno": 141,
"colno": 15
},
"sessionId": "xyz-123-xyz-123"
}
{track.js}
{track.js}是另一个用于日志记录的SaaS解决方案。
他们提供免费计划; 它限制为每分钟10个错误,每月10,000次命中,并且您的数据仅存储24小时。 最基本的付费计划是每月29.99美元-您可以在其定价页面上找到更多详细信息。
注意:无论何时初始化库,都会记录“命中”。
进行设置非常简单:
<!-- BEGIN TRACKJS -->
<script type="text/javascript">window._trackJs = { token: 'YOUR-TOKEN-HERE' };</script>
<script type="text/javascript" src="//d2zah9y47r7bi2.cloudfront.net/releases/current/tracker.js" crossorigin="anonymous"></script>
<!-- END TRACKJS -->
在拉入适当文件并初始化库之后,可以使用诸如track()
:
/**
* Directly invokes an error to be sent to TrackJS.
*
* @method track
* @param {Error|String} error The error to be tracked. If error does not have a stacktrace, will attempt to generate one.
*/
trackJs.track("Logical error: state should not be null");
try {
// do something
} catch (e) {
trackJs.track(e);
}
或使用控制台,该控制台会将消息发送到Web服务:
trackJs.console.debug("a message"); // debug severity
trackJs.console.log("another message"); // log severity
{track.js}可以做更多的事情-请查看文档以获取更多信息。
综上所述
客户端日志通常被忽略,但是可以说与记录服务器端错误同样重要。 但是,毫无疑问,它更难设置。 但是,有很多选择,在本文中我们已经研究了其中许多。
您如何处理客户端应用程序中的日志记录? 您是否开发了自己的方法? 您是否使用此处未涵盖的内容? 在评论中让我知道。
翻译自: https://www.sitepoint.com/logging-errors-client-side-apps/
客户端应用程序