1 | 'use strict';
|
2 |
|
3 | var R = require('ramda');
|
4 | var util = require('util');
|
5 | var events = require('events');
|
6 | var fs = require('fs-extra');
|
7 | var gs = require('goldwasher-schedule');
|
8 | var cs = require('chronostore');
|
9 | var bunyan = require('bunyan');
|
10 | var BunyanToLoggly = require('bunyan-loggly').Bunyan2Loggly;
|
11 | var BunyanToSlack = require('bunyan-slack');
|
12 | var logentries = require('le_node');
|
13 | var log;
|
14 |
|
15 | function getSettings(settings) {
|
16 | var result;
|
17 |
|
18 | if (R.is(Object, settings)) {
|
19 | result = settings;
|
20 | } else {
|
21 | result = fs.readJsonSync(settings || './sitesampler.json');
|
22 | }
|
23 |
|
24 | if (!result.targets || !R.isArrayLike(result.targets) || result.targets.length < 1) {
|
25 | throw new Error('No targets provided. Remember to set settings.targets.');
|
26 | }
|
27 |
|
28 | if (typeof result.rethrowErrors === 'undefined') {
|
29 | result.rethrowErrors = true;
|
30 | }
|
31 |
|
32 | return result;
|
33 | }
|
34 |
|
35 | function getLogglyLogger() {
|
36 | var loggly = {
|
37 | 'token': process.env['SITESAMPLER_LOGGLY_TOKEN'],
|
38 | 'subdomain': process.env['SITESAMPLER_LOGGLY_SUBDOMAIN'],
|
39 | 'level': process.env['SITESAMPLER_LOGGLY_LEVEL'] || 'info'
|
40 | };
|
41 |
|
42 | if (loggly.token && loggly.subdomain) {
|
43 | return {
|
44 | 'level': loggly.level,
|
45 | 'type': 'raw',
|
46 | 'stream': new BunyanToLoggly(loggly)
|
47 | };
|
48 | }
|
49 | }
|
50 |
|
51 | function getSlackLogger() {
|
52 | var slack = {
|
53 | 'webhook_url': process.env['SITESAMPLER_SLACK_WEBHOOKURL'],
|
54 | 'channel': process.env['SITESAMPLER_SLACK_CHANNEL'] || null,
|
55 | 'level': process.env['SITESAMPLER_SLACK_LEVEL'] || 'info',
|
56 | 'username': process.env['SITESAMPLER_SLACK_USERNAME'] || 'sitesampler'
|
57 | };
|
58 |
|
59 | if (slack.webhook_url) {
|
60 | return {
|
61 | 'level': slack.level,
|
62 | 'stream': new BunyanToSlack(slack)
|
63 | };
|
64 | }
|
65 | }
|
66 |
|
67 | function getLogentriesLogger() {
|
68 | var logger = {
|
69 | 'token': process.env['SITESAMPLER_LOGENTRIES_TOKEN']
|
70 | };
|
71 |
|
72 | if (logger.token) {
|
73 | return logentries.bunyanStream(logger);
|
74 | }
|
75 | }
|
76 |
|
77 | function setupLogger(enabled) {
|
78 | if (enabled) {
|
79 | var logger = {
|
80 | 'name': 'sitesampler',
|
81 | 'streams': [{'level': 'info', 'stream': process.stdout}]
|
82 | };
|
83 |
|
84 | var loggly = getLogglyLogger();
|
85 | if (loggly) {
|
86 | logger.streams.push(loggly);
|
87 | }
|
88 |
|
89 | var slack = getSlackLogger();
|
90 | if (slack) {
|
91 | logger.streams.push(slack);
|
92 | }
|
93 |
|
94 | var logentries = getLogentriesLogger();
|
95 | if (logentries) {
|
96 | logger.streams.push(logentries);
|
97 | }
|
98 |
|
99 | log = bunyan.createLogger(logger);
|
100 |
|
101 | log.info('Logging enabled.');
|
102 |
|
103 | if (loggly) {
|
104 | log.info('Loggly enabled.');
|
105 | }
|
106 |
|
107 | if (slack) {
|
108 | log.info('Slack enabled.');
|
109 | }
|
110 |
|
111 | if (logentries) {
|
112 | log.info('Logentries enabled.');
|
113 | }
|
114 |
|
115 | } else {
|
116 | log = bunyan.createLogger({'name': 'testing', 'streams': []});
|
117 | }
|
118 | }
|
119 |
|
120 | var SiteSampler = function(settingsPathOrObject) {
|
121 | var self = this;
|
122 | var settings = getSettings(settingsPathOrObject);
|
123 |
|
124 | setupLogger(process.env['SITESAMPLER_LOG']);
|
125 |
|
126 | log.info('Setting up sitesampler...');
|
127 |
|
128 | self.gs = gs(settings.targets, settings.options);
|
129 |
|
130 | self.gs.on('running', function(options) {
|
131 | log.info(options, 'Sitesampler running.');
|
132 | });
|
133 |
|
134 | self.gs.on('result', function(results, options) {
|
135 | options.ms = options.end - options.start;
|
136 | log.info(options, 'Result from ' + options.url + ' in ' + options.ms + ' ms.');
|
137 | self.writeToDisk(results, options, settings);
|
138 | });
|
139 |
|
140 | self.gs.on('error', function(error, options) {
|
141 | log.error(error, 'Error with results from goldwasher for "' + options.url + '". Aborting write.');
|
142 | if (settings.rethrowErrors) {
|
143 | self.emit('error', error);
|
144 | }
|
145 | });
|
146 |
|
147 | events.EventEmitter.call(this);
|
148 | log.info('Setup completed.');
|
149 | };
|
150 |
|
151 | util.inherits(SiteSampler, events.EventEmitter);
|
152 |
|
153 | SiteSampler.prototype.start = function() {
|
154 | var self = this;
|
155 |
|
156 | setImmediate(function() {
|
157 | log.info('Starting sitesampler.');
|
158 | self.emit('start');
|
159 | });
|
160 |
|
161 | self.gs.start();
|
162 |
|
163 | return self;
|
164 | };
|
165 |
|
166 | SiteSampler.prototype.stop = function() {
|
167 | var self = this;
|
168 |
|
169 | log.info('Stopping sitesampler.');
|
170 | self.emit('stop');
|
171 |
|
172 | self.gs.stop();
|
173 |
|
174 | return self;
|
175 | };
|
176 |
|
177 | SiteSampler.prototype.writeToDisk = function(results, options, settings) {
|
178 | var self = this;
|
179 |
|
180 | if (results.length > 0) {
|
181 | log.info({'url': options.url}, 'Writing results to chronostore...');
|
182 |
|
183 | cs.objectToStream(results)
|
184 | .pipe(cs.writeObject(settings.chronostore))
|
185 | .on('error', function(error) {
|
186 | log.error(error, 'Error when writing results to chronostore for "' + options.url + '".');
|
187 | if (settings.rethrowErrors) {
|
188 | self.emit('error', error);
|
189 | }
|
190 | })
|
191 | .on('finish', function() {
|
192 | log.info({'url': options.url}, 'Write to chronostore completed.');
|
193 | self.emit('results', results, options);
|
194 | });
|
195 | } else {
|
196 | log.warn({'target': options}, 'No results from goldwasher. Aborting write.');
|
197 | self.emit('results', results, options);
|
198 | }
|
199 | };
|
200 |
|
201 |
|
202 | module.exports = function(settingsPathOrObject) {
|
203 | return new SiteSampler(settingsPathOrObject);
|
204 | };
|