mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 11:35:55 +01:00
closes #4658
This commit is contained in:
64
public/src/admin/advanced/errors.js
Normal file
64
public/src/admin/advanced/errors.js
Normal file
@@ -0,0 +1,64 @@
|
||||
"use strict";
|
||||
/*global config, define, app, socket, ajaxify, bootbox, templates, Chart, utils */
|
||||
|
||||
define('admin/advanced/errors', ['Chart'], function(Chart) {
|
||||
var Errors = {};
|
||||
|
||||
Errors.init = function() {
|
||||
var notFoundCanvas = document.getElementById('not-found'),
|
||||
tooBusyCanvas = document.getElementById('toobusy'),
|
||||
dailyLabels = utils.getDaysArray();
|
||||
|
||||
dailyLabels.length = 7;
|
||||
|
||||
if (utils.isMobile()) {
|
||||
Chart.defaults.global.showTooltips = false;
|
||||
}
|
||||
|
||||
var data = {
|
||||
'not-found': {
|
||||
labels: dailyLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: "",
|
||||
fillColor: "rgba(186,139,175,0.2)",
|
||||
strokeColor: "rgba(186,139,175,1)",
|
||||
pointColor: "rgba(186,139,175,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(186,139,175,1)",
|
||||
data: ajaxify.data.analytics['not-found']
|
||||
}
|
||||
]
|
||||
},
|
||||
'toobusy': {
|
||||
labels: dailyLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: "",
|
||||
fillColor: "rgba(151,187,205,0.2)",
|
||||
strokeColor: "rgba(151,187,205,1)",
|
||||
pointColor: "rgba(151,187,205,1)",
|
||||
pointStrokeColor: "#fff",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||
data: ajaxify.data.analytics['toobusy']
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
notFoundCanvas.width = $(notFoundCanvas).parent().width();
|
||||
tooBusyCanvas.width = $(tooBusyCanvas).parent().width();
|
||||
new Chart(notFoundCanvas.getContext('2d')).Line(data['not-found'], {
|
||||
responsive: true,
|
||||
animation: false
|
||||
});
|
||||
new Chart(tooBusyCanvas.getContext('2d')).Line(data['toobusy'], {
|
||||
responsive: true,
|
||||
animation: false
|
||||
});
|
||||
};
|
||||
|
||||
return Errors;
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
/*global config, define, app, socket, ajaxify, bootbox, templates, Chart, utils */
|
||||
/*global config, define, app, socket, ajaxify, bootbox, templates, utils */
|
||||
|
||||
define('admin/manage/category-analytics', [], function() {
|
||||
define('admin/manage/category-analytics', ['Chart'], function(Chart) {
|
||||
var CategoryAnalytics = {};
|
||||
|
||||
CategoryAnalytics.init = function() {
|
||||
|
||||
@@ -187,4 +187,11 @@ var db = require('./database');
|
||||
}, callback);
|
||||
};
|
||||
|
||||
Analytics.getErrorAnalytics = function(callback) {
|
||||
async.parallel({
|
||||
'not-found': async.apply(Analytics.getDailyStatsForSet, 'analytics:errors:404', Date.now(), 7),
|
||||
'toobusy': async.apply(Analytics.getDailyStatsForSet, 'analytics:errors:503', Date.now(), 7)
|
||||
}, callback);
|
||||
};
|
||||
|
||||
}(exports));
|
||||
@@ -14,6 +14,7 @@ var adminController = {
|
||||
},
|
||||
events: require('./admin/events'),
|
||||
logs: require('./admin/logs'),
|
||||
errors: require('./admin/errors'),
|
||||
database: require('./admin/database'),
|
||||
postCache: require('./admin/postCache'),
|
||||
plugins: require('./admin/plugins'),
|
||||
|
||||
20
src/controllers/admin/errors.js
Normal file
20
src/controllers/admin/errors.js
Normal file
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var meta = require('../../meta'),
|
||||
analytics = require('../../analytics');
|
||||
|
||||
var errorsController = {};
|
||||
|
||||
errorsController.get = function(req, res) {
|
||||
async.parallel({
|
||||
'not-found': async.apply(meta.errors.get),
|
||||
analytics: async.apply(analytics.getErrorAnalytics)
|
||||
}, function(err, data) {
|
||||
res.render('admin/advanced/errors', data);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports = errorsController;
|
||||
@@ -373,12 +373,14 @@ Controllers.handle404 = function(req, res) {
|
||||
} else if (isLanguage.test(req.url)) {
|
||||
res.status(200).json({});
|
||||
} else if (req.path.startsWith(relativePath + '/uploads') || (req.get('accept') && req.get('accept').indexOf('text/html') === -1) || req.path === '/favicon.ico') {
|
||||
meta.errors.log404(req.path || '');
|
||||
res.sendStatus(404);
|
||||
} else if (req.accepts('html')) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.warn('Route requested but not found: ' + req.url);
|
||||
}
|
||||
|
||||
meta.errors.log404(req.path.replace(/^\/api/, '') || '');
|
||||
res.status(404);
|
||||
|
||||
if (res.locals.isAPI) {
|
||||
|
||||
@@ -24,6 +24,7 @@ var async = require('async'),
|
||||
require('./meta/sounds')(Meta);
|
||||
require('./meta/settings')(Meta);
|
||||
require('./meta/logs')(Meta);
|
||||
require('./meta/errors')(Meta);
|
||||
require('./meta/tags')(Meta);
|
||||
require('./meta/dependencies')(Meta);
|
||||
Meta.templates = require('./meta/templates');
|
||||
|
||||
36
src/meta/errors.js
Normal file
36
src/meta/errors.js
Normal file
@@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async'),
|
||||
winston = require('winston'),
|
||||
validator = require('validator');
|
||||
|
||||
var db = require('../database'),
|
||||
analytics = require('../analytics');
|
||||
|
||||
module.exports = function(Meta) {
|
||||
|
||||
Meta.errors = {};
|
||||
|
||||
Meta.errors.log404 = function(route, callback) {
|
||||
callback = callback || function() {};
|
||||
route = route.replace(/\/$/, ''); // remove trailing slashes
|
||||
analytics.increment('errors:404');
|
||||
db.sortedSetIncrBy('errors:404', 1, route, callback);
|
||||
};
|
||||
|
||||
Meta.errors.get = function(callback) {
|
||||
db.getSortedSetRevRangeByScoreWithScores('errors:404', 0, -1, '+inf', '-inf', function(err, data) {
|
||||
data = data.map(function(nfObject) {
|
||||
nfObject.value = validator.escape(nfObject.value);
|
||||
return nfObject;
|
||||
});
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
};
|
||||
|
||||
Meta.errors.clear = function(callback) {
|
||||
console.log('clear errors');
|
||||
callback();
|
||||
};
|
||||
};
|
||||
@@ -302,6 +302,7 @@ middleware.privateUploads = function(req, res, next) {
|
||||
|
||||
middleware.busyCheck = function(req, res, next) {
|
||||
if (global.env === 'production' && (!meta.config.hasOwnProperty('eventLoopCheckEnabled') || parseInt(meta.config.eventLoopCheckEnabled, 10) === 1) && toobusy()) {
|
||||
analytics.increment('errors:503');
|
||||
res.status(503).type('text/html').sendFile(path.join(__dirname, '../../public/503.html'));
|
||||
} else {
|
||||
next();
|
||||
|
||||
@@ -82,6 +82,7 @@ function addRoutes(router, middleware, controllers) {
|
||||
router.get('/advanced/database', middlewares, controllers.admin.database.get);
|
||||
router.get('/advanced/events', middlewares, controllers.admin.events.get);
|
||||
router.get('/advanced/logs', middlewares, controllers.admin.logs.get);
|
||||
router.get('/advanced/errors', middlewares, controllers.admin.errors.get);
|
||||
router.get('/advanced/post-cache', middlewares, controllers.admin.postCache.get);
|
||||
|
||||
router.get('/development/logger', middlewares, controllers.admin.logger.get);
|
||||
|
||||
61
src/views/admin/advanced/errors.tpl
Normal file
61
src/views/admin/advanced/errors.tpl
Normal file
@@ -0,0 +1,61 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 text-center">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div><canvas id="not-found" height="250"></canvas></div>
|
||||
</div>
|
||||
<div class="panel-footer"><small><strong>Figure 1</strong> – <code>404 Not Found</code> events per day</small></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 text-center">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div><canvas id="toobusy" height="250"></canvas></div>
|
||||
</div>
|
||||
<div class="panel-footer"><small><strong>Figure 2</strong> – <code>503 Service Unavailable</code> events per day</small></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><i class="fa fa-exclamation-triangle"></i> 404 Not Found</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<th>Route</th>
|
||||
<th>Count</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- BEGIN not-found -->
|
||||
<tr>
|
||||
<td>{../value}</td>
|
||||
<td>{../score}</td>
|
||||
</tr>
|
||||
<!-- END not-found -->
|
||||
<!-- IF !not-found.length -->
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="alert alert-success">
|
||||
Hooray! There are no routes that were not found.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- ENDIF !not-found.length -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 acp-sidebar">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Manage Error Log</div>
|
||||
<div class="panel-body">
|
||||
<div class="btn-group-vertical btn-block" role="group">
|
||||
<button class="btn btn-info" data-action="export"><i class="fa fa-download"></i> Export Error Log (CSV)</button>
|
||||
<button class="btn btn-danger" data-action="clear"><i class="fa fa-trash"></i> Clear Error Log</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,6 +95,7 @@
|
||||
<li><a href="{relative_path}/admin/advanced/database">Database</a></li>
|
||||
<li><a href="{relative_path}/admin/advanced/events">Events</a></li>
|
||||
<li><a href="{relative_path}/admin/advanced/logs">Logs</a></li>
|
||||
<li><a href="{relative_path}/admin/advanced/errors">Errors</a></li>
|
||||
<li><a href="{relative_path}/admin/advanced/post-cache">Post Cache</a></li>
|
||||
<!-- IF env -->
|
||||
<li><a href="{relative_path}/admin/development/logger">Logger</a></li>
|
||||
@@ -247,6 +248,7 @@
|
||||
<li><a href="{relative_path}/admin/advanced/database">Database</a></li>
|
||||
<li><a href="{relative_path}/admin/advanced/events">Events</a></li>
|
||||
<li><a href="{relative_path}/admin/advanced/logs">Logs</a></li>
|
||||
<li><a href="{relative_path}/admin/advanced/errors">Errors</a></li>
|
||||
<li><a href="{relative_path}/admin/advanced/post-cache">Post Cache</a></li>
|
||||
<!-- IF env -->
|
||||
<li><a href="{relative_path}/admin/development/logger">Logger</a></li>
|
||||
|
||||
Reference in New Issue
Block a user