-
Notifications
You must be signed in to change notification settings - Fork 2
/
app.js
159 lines (135 loc) · 4.91 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
'use strict';
// app.js - cognicity-reports-powertrack application setup
/**
* @file Collect unconfirmed reports from data sources & send report verification tweets
* @copyright (c) Tomas Holderness & SMART Infrastructure Facility January 2014
* @license Released under GNU GPLv3 License (see LICENSE.txt).
* @example
* Usage:
* node app.js cognicity-reports-config.js
*/
// Node dependencies
var path = require('path');
// Node.js fs filesystem module
var fs = require('fs');
// Modules
// Postgres interface module
var pg = require('pg');
// Winston logger module
var logger = require('winston');
// Verify expected arguments
if (process.argv[2]) {
var config = require( __dirname + path.sep + process.argv[2] );
} else {
throw new Error('No config file. Usage: node app.js config.js');
}
// Logging configuration
var logPath = ( config.logger.logDirectory ? config.logger.logDirectory : __dirname );
// Check that log file directory can be written to
try {
fs.accessSync(logPath, fs.W_OK);
} catch (e) {
console.log( "Log directory '" + logPath + "' cannot be written to" );
throw e;
}
logPath += path.sep;
logPath += config.logger.filename + ".log";
logger
// Configure custom File transport to write plain text messages
.add(logger.transports.File, {
filename: logPath, // Write to projectname.log
json: false, // Write in plain text, not JSON
maxsize: config.logger.maxFileSize, // Max size of each file
maxFiles: config.logger.maxFiles, // Max number of files
level: config.logger.level // Level of log messages
})
// Console transport is no use to us when running as a daemon
.remove(logger.transports.Console);
// FIXME This is a workaround for https://github.com/flatiron/winston/issues/228
// If we exit immediately winston does not get a chance to write the last log message.
// So we wait a short time before exiting.
function exitWithStatus(exitStatus) {
logger.info( "Exiting with status " + exitStatus );
setTimeout( function() {
process.exit(exitStatus);
}, 500 );
}
logger.info("Application starting...");
// Verify DB connection is up
pg.connect(config.pg.conString, function(err, client, done){
if (err){
logger.error("DB Connection error: " + err);
logger.error("Fatal error: Application shutting down");
done();
exitWithStatus(1);
} else {
logger.info("DB connection successful");
}
});
var Reports = require('./Reports');
/**
* Instance of reports module.
* @type {Reports}
*/
var reports = new Reports( config, pg, logger, exitWithStatus );
// Handle postgres idle connection error (generated by RDS failover among other possible causes)
pg.on('error', function(err) {
logger.error('Postgres connection error: ' + err);
logger.info('Enabling caching mode and attempting to reconnect at intervals');
reports.enableCacheMode();
var reconnectionAttempts = 0;
var reconnectionFunction = function() {
// Try and reconnect
pg.connect(config.pg.conString, function(err, client, done){
if (err) {
reconnectionAttempts++;
if (reconnectionAttempts >= config.pg.reconnectionAttempts) {
// We have tried the maximum number of times, tweet admin and exit in failure state
logger.error( 'Postgres reconnection failed' );
logger.error( 'Maximum reconnection attempts reached, exiting' );
exitWithStatus(1);
} else {
// If we failed, try and reconnect again after a delay
logger.error( 'Postgres reconnection failed, queuing next attempt for ' + config.pg.reconnectionDelay + 'ms' );
setTimeout( reconnectionFunction, config.pg.reconnectionDelay );
}
} else {
// If we succeeded, disable reports caching mode
logger.info( 'Postgres reconnection succeeded, re-enabling real time processing' );
reports.disableCacheMode();
}
});
};
reconnectionFunction();
});
// Catch unhandled exceptions, log, and exit with error status
process.on('uncaughtException', function (err) {
logger.error('uncaughtException: ' + err.message + ", " + err.stack);
logger.error("Fatal error: Application shutting down");
exitWithStatus(1);
});
// Catch kill and interrupt signals and log a clean exit status
process.on('SIGTERM', function() {
logger.info('SIGTERM: Application shutting down');
exitWithStatus(0);
});
process.on('SIGINT', function() {
logger.info('SIGINT: Application shutting down');
exitWithStatus(0);
});
// Load a data source plugin
function loadDataSource( dataSourceFolder ) {
logger.info("Loading data source from: " + dataSourceFolder );
// Find data source descriptor file (containing class name & config file name)
var dataSourceModule = require( "./" + dataSourceFolder );
// Construct instance of data source
var dataSource = dataSourceModule( reports );
// Add data source to reports
reports.addDataSource( dataSource );
logger.info("Data source '" + dataSource.constructor.name + "' loaded");
}
config.dataSources.forEach( function( dataSourceFolder ){
loadDataSource( dataSourceFolder );
});
// Start reports module
reports.start();