Added logging framework.

This commit is contained in:
baldo 2016-05-24 16:40:57 +02:00
parent ad3f075d93
commit a82b66c637
12 changed files with 171 additions and 29 deletions

View file

@ -6,6 +6,10 @@
"databaseFile": "/tmp/ffffng.sqlite",
"peersPath": "/tmp/peers",
"logging": {
"directory": "/tmp/logs"
},
"email": {
"from": "Freifunk Knotenformular <no-reply@musterstadt.freifunk.net>",

View file

@ -45,6 +45,7 @@
"nodemailer": "~2.4.1",
"nodemailer-html-to-text": "~2.1.0",
"request": "~2.72.0",
"scribe-js": "~2.0.4",
"serve-static": "~1.10.2",
"sqlite3": "~3.1.4",
"time-grunt": "~1.3.0"

View file

@ -31,7 +31,7 @@ angular.module('ffffng').factory('app', function (fs, config, _) {
});
}
app.use( function (req, res, next) {
app.use(function (req, res, next) {
if (jsTemplateFiles.indexOf(req.path) >= 0) {
return serveTemplate('application/javascript', req, res, next);
}

View file

@ -11,6 +11,10 @@ var defaultConfig = {
databaseFile: '/tmp/ffffng.sqlite',
peersPath: '/tmp/peers',
logging: {
directory: '/tmp/logs'
},
email: {
from: 'Freifunk Knotenformular <no-reply@musterstadt.freifunk.net>',

View file

@ -6,8 +6,11 @@ var glob = require('glob');
var path = require('path');
var config = require('../config');
var Logger = require('../logger');
function applyPatch(db, file, callback) {
Logger.tag('database', 'migration').info('Checking if patch need to be applied: ' + file);
fs.readFile(file, function (err, contents) {
if (err) {
return callback(err);
@ -22,6 +25,7 @@ function applyPatch(db, file, callback) {
if (row) {
// patch is already applied. skip!
Logger.tag('database', 'migration').info('Patch already applied, skipping: ' + file);
return callback(null);
}
@ -30,12 +34,22 @@ function applyPatch(db, file, callback) {
+ 'INSERT INTO schema_version (version) VALUES (\'' + version + '\');\n'
+ 'END TRANSACTION;';
db.exec(sql, callback);
db.exec(sql, function (err) {
if (err) {
return callback(err);
}
Logger.tag('database', 'migration').info('Patch successfully applied: ' + file);
callback(null);
});
});
});
}
function applyMigrations(db, callback) {
Logger.tag('database', 'migration').info('Migrating database...');
var sql = 'BEGIN TRANSACTION; CREATE TABLE IF NOT EXISTS schema_version (\n'
+ ' version VARCHAR(255) PRIMARY KEY ASC,\n'
+ ' applied_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL\n'
@ -50,7 +64,7 @@ function applyMigrations(db, callback) {
return callback(err);
}
async.each(
async.eachSeries(
files,
function (file, fileCallback) {
applyPatch(db, file, fileCallback);
@ -64,10 +78,22 @@ function applyMigrations(db, callback) {
module.exports = {
init: function (callback) {
var SQLite3 = require('sqlite3');
var db = new SQLite3.Database(config.server.databaseFile);
var file = config.server.databaseFile;
Logger.tag('database').info('Setting up database: ' + file);
var db;
try {
db = new SQLite3.Database(file);
}
catch (error) {
Logger.tag('database').error('Error initialzing database:', error);
throw error;
}
applyMigrations(db, function (err) {
if (err) {
Logger.tag('database').error('Error migrating database:', err);
throw err;
}

View file

@ -1,11 +1,11 @@
'use strict';
angular.module('ffffng').factory('MailQueueJob', function (MailService) {
angular.module('ffffng').factory('MailQueueJob', function (MailService, Logger) {
return {
run: function () {
MailService.sendPendingMails(function (err) {
if (err) {
console.error(err);
Logger.tag('mail', 'queue').error('Error sending pending mails:', err);
}
});
}

View file

@ -1,11 +1,11 @@
'use strict';
angular.module('ffffng').factory('NodeInformationRetrievalJob', function (MonitoringService) {
angular.module('ffffng').factory('NodeInformationRetrievalJob', function (MonitoringService, Logger) {
return {
run: function () {
MonitoringService.retrieveNodeInformation(function (err) {
if (err) {
console.error(err);
Logger.tag('monitoring', 'information-retrieval').error('Error retrieving node data:', err);
}
});
}

View file

@ -8,10 +8,12 @@ _.each(jobFiles, function (jobFile) {
require(jobFile);
});
angular.module('ffffng').factory('Scheduler', function ($injector) {
angular.module('ffffng').factory('Scheduler', function ($injector, Logger) {
var cron = require('node-cron');
function schedule(expr, jobName) {
Logger.tag('jobs').info('Scheduling job: ' + expr + ' ' + jobName);
var job = $injector.get(jobName);
if (!_.isFunction(job.run)) {
@ -23,10 +25,20 @@ angular.module('ffffng').factory('Scheduler', function ($injector) {
return {
init: function () {
schedule('*/5 * * * * *', 'MailQueueJob');
// schedule('0 */1 * * * *', 'NodeInformationRetrievalJob');
schedule('*/10 * * * * *', 'NodeInformationRetrievalJob');
// schedule('0 */1 * * * *', 'NodeInformationCleanupJob');
Logger.tag('jobs').info('Scheduling background jobs...');
try {
schedule('0 */1 * * * *', 'MailQueueJob');
// schedule('0 */5 * * * *', 'NodeInformationRetrievalJob');
schedule('*/10 * * * * *', 'NodeInformationRetrievalJob');
// schedule('0 */1 * * * *', 'NodeInformationCleanupJob');
}
catch (error) {
Logger.tag('jobs').error('Error during scheduling of background jobs:', error);
throw error;
}
Logger.tag('jobs').info('Scheduling of background jobs done.');
}
};
});

30
server/logger.js Normal file
View file

@ -0,0 +1,30 @@
'use strict';
var config = require('./config');
var scribe = require('scribe-js')({
app: 'ffffng',
id: process.pid,
rootPath: config.server.logging.directory,
module: {
'router/Viewer': {
username: false,
password: false
}
}
});
process.console.addLogger('debug', 'grey', {
logInConsole: false
});
angular.module('ffffng').factory('Logger', function (app) {
app.use(scribe.express.logger());
app.use('/internal/logs', scribe.webPanel());
return process.console;
});
module.exports = process.console;

View file

@ -8,6 +8,8 @@ angular.module('ffffng', []);
require('./config');
require('./logger').tag('main', 'startup').info('Server starting up...');
require('./app');
require('./router');
require('./libs');
@ -32,7 +34,9 @@ require('./jobs/scheduler');
var db = require('./db/database');
db.init(function () {
angular.injector(['ffffng']).invoke(function (config, app, Scheduler, Router) {
angular.injector(['ffffng']).invoke(function (config, app, Logger, Scheduler, Router) {
Logger.tag('main').info('Initializing...');
Scheduler.init();
Router.init();

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('ffffng')
.service('MailService', function (Database, UrlBuilder, config, _, async, deepExtend, fs, moment) {
.service('MailService', function (Database, UrlBuilder, config, _, async, deepExtend, fs, moment, Logger) {
var MAIL_QUEUE_DB_BATCH_SIZE = 2;
var MAIL_QUEUE_MAX_PARALLEL_SENDING = 3;
@ -20,6 +20,13 @@ angular.module('ffffng')
}));
function sendMail(options, callback) {
Logger
.tag('mail', 'queue')
.info(
'Sending pending mail[' + options.id + '] of type ' + options.email + '. ' +
'Had ' + options.failures + ' failures before.'
);
var templateBasePath = __dirname + '/../mailTemplates/' + options.email;
async.parallel({
subject: _.partial(fs.readFile, templateBasePath + '.subject.txt'),
@ -39,13 +46,8 @@ angular.module('ffffng')
}
);
console.log(data);
function render(field) {
console.log(field);
var rendered = _.template(templates[field].toString())(data);
console.log(rendered);
return rendered;
return _.template(templates[field].toString())(data);
}
var mailOptions;
@ -58,6 +60,9 @@ angular.module('ffffng')
};
}
catch (error) {
Logger
.tag('mail', 'queue')
.error('Error rendering template for pending mail[' + options.id + ']:', error);
return callback(error);
}
@ -66,6 +71,8 @@ angular.module('ffffng')
return callback(err);
}
Logger.tag('mail', 'queue').info('Mail[' + options.id +'] has been send.');
callback(null);
});
}
@ -116,11 +123,10 @@ angular.module('ffffng')
}
function sendPendingMail(pendingMail, callback) {
console.log(pendingMail);
sendMail(pendingMail, function (err) {
if (err) {
// we only log the error and increment the failure counter as we want to continue with pending mails
console.error(err);
Logger.tag('mail', 'queue').error('Error sending pending mail[' + pendingMail.id + ']:', err);
return incrementFailureCounterForPendingEmail(pendingMail.id, function (err) {
if (err) {
@ -151,7 +157,7 @@ angular.module('ffffng')
},
sendPendingMails: function (callback) {
console.info('Start sending pending mails.');
Logger.tag('mail', 'queue').info('Start sending pending mails...');
var startTime = moment();
@ -160,13 +166,15 @@ angular.module('ffffng')
return callback(err);
}
Logger.tag('mail', 'queue').info('Sending next batch...');
findPendingMailsBefore(startTime, MAIL_QUEUE_DB_BATCH_SIZE, function (err, pendingMails) {
if (err) {
return callback(err);
}
if (_.isEmpty(pendingMails)) {
console.info('Done sending pending mails.');
Logger.tag('mail', 'queue').info('Done sending pending mails.');
return callback(null);
}

View file

@ -7,6 +7,7 @@ angular.module('ffffng')
config,
Database,
ErrorTypes,
Logger,
moment,
NodeService,
request,
@ -17,6 +18,10 @@ angular.module('ffffng')
var previousImportTimestamp = null;
function insertNodeInformation(nodeData, node, callback) {
Logger
.tag('monitoring', 'information-retrieval')
.debug('Node is new in monitoring, creating data:', nodeData.mac);
return Database.run(
'INSERT INTO node_state ' +
'(mac, state, last_seen, import_timestamp, last_status_mail_send) ' +
@ -33,11 +38,21 @@ angular.module('ffffng')
}
function updateNodeInformation(nodeData, node, row, callback) {
debugger;
Logger
.tag('monitoring', 'information-retrieval')
.debug('Node is known in monitoring:', nodeData.mac);
if (!moment(row.import_timestamp).isBefore(nodeData.importTimestamp)) {
Logger
.tag('monitoring', 'information-retrieval')
.debug('No new data for node, skipping:', nodeData.mac);
return callback();
}
Logger
.tag('monitoring', 'information-retrieval')
.debug('New data for node, updating:', nodeData.mac);
return Database.run(
'UPDATE node_state ' +
'WHERE id = ? AND mac = ?' +
@ -51,6 +66,9 @@ angular.module('ffffng')
}
function deleteNodeInformation(nodeData, node, callback) {
Logger
.tag('monitoring', 'information-retrieval')
.debug('Node is not being monitored, deleting monitoring data:', nodeData.mac);
return Database.run(
'DELETE FROM node_state WHERE mac = ? AND import_timestamp < ?',
[node.mac, nodeData.importTimestamp.unix()],
@ -60,6 +78,8 @@ angular.module('ffffng')
function storeNodeInformation(nodeData, node, callback) {
if (node.monitoring && node.monitoringConfirmed) {
Logger.tag('monitoring', 'information-retrieval').debug('Node is being monitored:', nodeData.mac);
return Database.get('SELECT * FROM node_state WHERE mac = ?', [node.mac], function (err, row) {
if (err) {
return callback(err);
@ -79,6 +99,8 @@ angular.module('ffffng')
var isValidMac = Validator.forConstraint(Constraints.node.mac);
function parseNodesJson(body, callback) {
Logger.tag('monitoring', 'information-retrieval').info('Parsing nodes.json...');
function parseTimestamp(timestamp) {
if (!_.isString(json.timestamp)) {
return moment.invalid();
@ -212,8 +234,9 @@ angular.module('ffffng')
},
retrieveNodeInformation: function (callback) {
console.info();
request(config.server.map.nodesJsonUrl, function (err, response, body) {
var url = config.server.map.nodesJsonUrl;
Logger.tag('monitoring', 'information-retrieval').info('Retrieving nodes.json:', url);
request(url, function (err, response, body) {
if (err) {
return callback(err);
}
@ -224,6 +247,15 @@ angular.module('ffffng')
}
if (previousImportTimestamp !== null && !data.importTimestamp.isAfter(previousImportTimestamp)) {
Logger
.tag('monitoring', 'information-retrieval')
.info(
'No new data, skipping.',
'Current timestamp:',
data.importTimestamp.format(),
'Previous timestamp:',
previousImportTimestamp.format()
);
return callback();
}
previousImportTimestamp = data.importTimestamp;
@ -231,16 +263,37 @@ angular.module('ffffng')
async.each(
data.nodes,
function (nodeData, nodeCallback) {
Logger.tag('monitoring', 'information-retrieval').debug('Importing:', nodeData.mac);
NodeService.findNodeDataByMac(nodeData.mac, function (err, node) {
if (err) {
Logger
.tag('monitoring', 'information-retrieval')
.error('Error importing:', nodeData.mac, err);
return nodeCallback(err);
}
if (!node) {
Logger
.tag('monitoring', 'information-retrieval')
.debug('Unknown node, skipping:', nodeData.mac);
return nodeCallback(null);
}
storeNodeInformation(nodeData, node, nodeCallback);
storeNodeInformation(nodeData, node, function (err) {
if (err) {
Logger
.tag('monitoring', 'information-retrieval')
.debug('Could not update / deleting node data:', nodeData.mac, err);
return nodeCallback(err);
}
Logger
.tag('monitoring', 'information-retrieval')
.debug('Updating / deleting node data done:', nodeData.mac);
nodeCallback();
});
});
},
callback