Slack messaging #169
13 changed files with 81 additions and 34 deletions
|
@ -3,7 +3,10 @@
|
||||||
"LbryClaimAddress": "LBRY_CLAIM_ADDRESS"
|
"LbryClaimAddress": "LBRY_CLAIM_ADDRESS"
|
||||||
},
|
},
|
||||||
"Database": {
|
"Database": {
|
||||||
"username": "MYSQL_USERNAME",
|
"Username": "MYSQL_USERNAME",
|
||||||
"password": "MYSQL_PASSWORD"
|
"Password": "MYSQL_PASSWORD"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"SlackWebHook": "SLACK_WEB_HOOK"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,9 +6,9 @@
|
||||||
"GoogleId": "none"
|
"GoogleId": "none"
|
||||||
},
|
},
|
||||||
"Database": {
|
"Database": {
|
||||||
"database": "lbry",
|
"Database": "lbry",
|
||||||
"username": "none",
|
"Username": "none",
|
||||||
"password": "none"
|
"Password": "none"
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": "none"
|
"LogLevel": "none"
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
"MySqlConnectionUri": "none"
|
"MySqlConnectionUri": "none"
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": "silly"
|
"LogLevel": "silly",
|
||||||
|
"SlackErrorChannel": "#staging_speech-errors",
|
||||||
|
"SlackInfoChannel": "none"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,6 +12,10 @@ module.exports = (winston, logLevel) => {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// winston.on('error', (err) => {
|
||||||
|
// console.log('unhandled exception in winston >> ', err);
|
||||||
|
// });
|
||||||
|
|
||||||
winston.error('Level 0');
|
winston.error('Level 0');
|
||||||
winston.warn('Level 1');
|
winston.warn('Level 1');
|
||||||
winston.info('Level 2');
|
winston.info('Level 2');
|
|
@ -9,6 +9,8 @@
|
||||||
"MySqlConnectionUri": "none"
|
"MySqlConnectionUri": "none"
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": "verbose"
|
"LogLevel": "verbose",
|
||||||
|
"SlackErrorChannel": "#speech-errors",
|
||||||
|
"SlackInfoChannel": "#speech-logs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
config/slackLoggerConfig.js
Normal file
27
config/slackLoggerConfig.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
const config = require('config');
|
||||||
|
const SLACK_WEB_HOOK = config.get('Logging.SlackWebHook');
|
||||||
|
const SLACK_ERROR_CHANNEL = config.get('Logging.SlackErrorChannel');
|
||||||
|
const SLACK_INFO_CHANNEL = config.get('Logging.SlackInfoChannel');
|
||||||
|
const winstonSlackWebHook = require('winston-slack-webhook').SlackWebHook;
|
||||||
|
|
||||||
|
module.exports = (winston) => {
|
||||||
|
// add a transport for errors
|
||||||
|
winston.add(winstonSlackWebHook, {
|
||||||
|
name : 'slack-errors-transport',
|
||||||
|
level : 'error',
|
||||||
|
webhookUrl: SLACK_WEB_HOOK,
|
||||||
|
channel : SLACK_ERROR_CHANNEL,
|
||||||
|
username : 'spee.ch',
|
||||||
|
iconEmoji : ':face_with_head_bandage:',
|
||||||
|
});
|
||||||
|
winston.add(winstonSlackWebHook, {
|
||||||
|
name : 'slack-info-transport',
|
||||||
|
level : 'info',
|
||||||
|
webhookUrl: SLACK_WEB_HOOK,
|
||||||
|
channel : SLACK_INFO_CHANNEL,
|
||||||
|
username : 'spee.ch',
|
||||||
|
iconEmoji : ':nerd_face:',
|
||||||
|
});
|
||||||
|
// send test message
|
||||||
|
winston.error('Testing slack logging... slack logging is online.');
|
||||||
|
};
|
|
@ -35,7 +35,7 @@ module.exports = {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
logger.error('Sequelize error', error);
|
logger.error('Sequelize error >>', error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
sendGoogleAnalytics (action, headers, ip, originalUrl) {
|
sendGoogleAnalytics (action, headers, ip, originalUrl) {
|
||||||
|
@ -137,7 +137,7 @@ module.exports = {
|
||||||
resolve({ records: resultHashTable, totals: { totalServe, totalPublish, totalShow, totalCount, totalSuccess, totalFailure }, percentSuccess });
|
resolve({ records: resultHashTable, totals: { totalServe, totalPublish, totalShow, totalCount, totalSuccess, totalFailure }, percentSuccess });
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
logger.error('sequelize error', error);
|
logger.error('sequelize error >>', error);
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -161,7 +161,7 @@ module.exports = {
|
||||||
resolve(results);
|
resolve(results);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
logger.error('sequelize error', error);
|
logger.error('sequelize error >>', error);
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,32 +1,38 @@
|
||||||
const logger = require('winston');
|
const logger = require('winston');
|
||||||
const { postToStats } = require('../controllers/statsController.js');
|
const { postToStats } = require('../controllers/statsController.js');
|
||||||
|
|
||||||
|
function useObjectPropertiesIfNoKeys (err) {
|
||||||
|
if (Object.keys(err).length === 0) {
|
||||||
|
let newErrorObject = {};
|
||||||
|
Object.getOwnPropertyNames(err).forEach((key) => {
|
||||||
for errors created by node (e.g. for errors created by node (e.g. `new Error('error message')`) the stack and message are non-enumerable
|
|||||||
|
newErrorObject[key] = err[key];
|
||||||
|
});
|
||||||
|
return newErrorObject;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
handleRequestError (action, originalUrl, ip, error, res) {
|
handleRequestError (action, originalUrl, ip, error, res) {
|
||||||
logger.error('Request Error >>', error);
|
logger.error('Request Error:', useObjectPropertiesIfNoKeys(error));
|
||||||
|
postToStats(action, originalUrl, ip, null, null, error);
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
postToStats(action, originalUrl, ip, null, null, error.response.data.error.messsage);
|
|
||||||
res.status(error.response.status).send(error.response.data.error.message);
|
res.status(error.response.status).send(error.response.data.error.message);
|
||||||
} else if (error.code === 'ECONNREFUSED') {
|
} else if (error.code === 'ECONNREFUSED') {
|
||||||
postToStats(action, originalUrl, ip, null, null, 'Connection refused. The daemon may not be running.');
|
|
||||||
res.status(503).send('Connection refused. The daemon may not be running.');
|
res.status(503).send('Connection refused. The daemon may not be running.');
|
||||||
} else if (error.message) {
|
} else if (error.message) {
|
||||||
postToStats(action, originalUrl, ip, null, null, error);
|
|
||||||
res.status(400).send(error.message);
|
res.status(400).send(error.message);
|
||||||
} else {
|
} else {
|
||||||
postToStats(action, originalUrl, ip, null, null, error);
|
|
||||||
res.status(400).send(error);
|
res.status(400).send(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handlePublishError (error) {
|
handlePublishError (error) {
|
||||||
|
logger.error('Publish Error:', useObjectPropertiesIfNoKeys(error));
|
||||||
if (error.code === 'ECONNREFUSED') {
|
if (error.code === 'ECONNREFUSED') {
|
||||||
logger.error('Publish Error:', 'Connection refused. The daemon may not be running.');
|
|
||||||
return 'Connection refused. The daemon may not be running.';
|
return 'Connection refused. The daemon may not be running.';
|
||||||
} else if (error.response.data.error) {
|
} else if (error.response.data.error) {
|
||||||
logger.error('Publish Error:', error.response.data.error);
|
|
||||||
return error.response.data.error.message;
|
return error.response.data.error.message;
|
||||||
} else {
|
} else {
|
||||||
logger.error('Unhandled Publish Error:', error.message);
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -110,11 +110,12 @@ module.exports = {
|
||||||
if (data.result) {
|
if (data.result) {
|
||||||
resolve(data.result.download_directory);
|
resolve(data.result.download_directory);
|
||||||
} else {
|
} else {
|
||||||
reject(new Error('Successfully connected to lbry daemon, but unable to retrieve the download directory.'));
|
// reject(new Error('Successfully connected to lbry daemon, but unable to retrieve the download directory.'));
|
||||||
|
return new Error('Successfully connected to lbry daemon, but unable to retrieve the download directory.');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
logger.error('Unable to retrieve daemon download directory. Restart spee.ch once the daemon is ready. Using default "/home/lbry/Downloads".', error);
|
logger.error('Lbrynet Error:', error);
|
||||||
resolve('/home/lbry/Downloads/');
|
resolve('/home/lbry/Downloads/');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,7 +11,7 @@ function createOpenGraphInfo ({ fileType, claimId, name, fileName, fileExt }) {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
serveFile ({ fileName, fileType, filePath }, res) {
|
serveFile ({ fileName, fileType, filePath }, res) {
|
||||||
logger.info(`serving file ${fileName}`);
|
logger.verbose(`serving file ${fileName}`);
|
||||||
// set default options
|
// set default options
|
||||||
let options = {
|
let options = {
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
@ -6,9 +6,9 @@ const config = require('config');
|
||||||
const db = {};
|
const db = {};
|
||||||
const logger = require('winston');
|
const logger = require('winston');
|
||||||
|
|
||||||
const database = config.get('Database.database');
|
const database = config.get('Database.Database');
|
||||||
const username = config.get('Database.username');
|
const username = config.get('Database.Username');
|
||||||
const password = config.get('Database.password');
|
const password = config.get('Database.Password');
|
||||||
const sequelize = new Sequelize(database, username, password, {
|
const sequelize = new Sequelize(database, username, password, {
|
||||||
host : 'localhost',
|
host : 'localhost',
|
||||||
dialect: 'mysql',
|
dialect: 'mysql',
|
||||||
|
@ -52,7 +52,7 @@ function getLongClaimIdFromShortClaimId (name, shortId) {
|
||||||
.then(result => {
|
.then(result => {
|
||||||
switch (result.length) {
|
switch (result.length) {
|
||||||
case 0:
|
case 0:
|
||||||
return reject(new Error('That is an invalid Short Claim Id'));
|
throw new Error('That is an invalid Short Claim Id');
|
||||||
default: // note results must be sorted
|
default: // note results must be sorted
|
||||||
return resolve(result[0].claimId);
|
return resolve(result[0].claimId);
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ db['getShortClaimIdFromLongClaimId'] = (claimId, claimName) => {
|
||||||
.then(result => {
|
.then(result => {
|
||||||
switch (result.length) {
|
switch (result.length) {
|
||||||
case 0:
|
case 0:
|
||||||
return reject(new Error('That is an invalid claim name'));
|
throw new Error('That is an invalid claim name');
|
||||||
default:
|
default:
|
||||||
return resolve(sortResult(result, claimId));
|
return resolve(sortResult(result, claimId));
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,7 @@ db['getShortChannelIdFromLongChannelId'] = (channelName, longChannelId) => {
|
||||||
.then(result => {
|
.then(result => {
|
||||||
switch (result.length) {
|
switch (result.length) {
|
||||||
case 0:
|
case 0:
|
||||||
return reject(new Error('That is an invalid channel name'));
|
throw new Error('That is an invalid channel name');
|
||||||
default:
|
default:
|
||||||
return resolve(sortResult(result, longChannelId));
|
return resolve(sortResult(result, longChannelId));
|
||||||
}
|
}
|
||||||
|
@ -238,7 +238,7 @@ db['resolveClaim'] = (name, claimId) => {
|
||||||
case 1:
|
case 1:
|
||||||
return resolve(result[0]);
|
return resolve(result[0]);
|
||||||
default:
|
default:
|
||||||
return new Error('more than one entry matches that name and claimID');
|
throw new Error('more than one entry matches that name and claimID');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
@ -255,7 +255,7 @@ db['getClaimIdByLongChannelId'] = (channelId, claimName) => {
|
||||||
.then(result => {
|
.then(result => {
|
||||||
switch (result.length) {
|
switch (result.length) {
|
||||||
case 0:
|
case 0:
|
||||||
return reject(new Error('There is no such claim for that channel'));
|
throw new Error('There is no such claim for that channel');
|
||||||
default:
|
default:
|
||||||
return resolve(result[0].claimId);
|
return resolve(result[0].claimId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,8 @@
|
||||||
"socket.io": "^2.0.1",
|
"socket.io": "^2.0.1",
|
||||||
"socketio-file-upload": "^0.6.0",
|
"socketio-file-upload": "^0.6.0",
|
||||||
"universal-analytics": "^0.4.13",
|
"universal-analytics": "^0.4.13",
|
||||||
"winston": "^2.3.1"
|
"winston": "^2.3.1",
|
||||||
|
"winston-slack-webhook": "billbitt/winston-slack-webhook"
|
||||||
},
|
},
|
||||||
should this be billbitt? should this be billbitt?
I made my own fork to fix an issue with the package, and submitted a PR to the main package. The PR was accepted, so will update this to point to main package in next update. I made my own fork to fix an issue with the package, and submitted a PR to the main package. The PR was accepted, so will update this to point to main package in next update.
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "3.19.0",
|
"eslint": "3.19.0",
|
||||||
|
|
|
@ -14,7 +14,8 @@ const db = require('./models'); // require our models for syncing
|
||||||
|
|
||||||
// configure logging
|
// configure logging
|
||||||
const logLevel = config.get('Logging.LogLevel');
|
const logLevel = config.get('Logging.LogLevel');
|
||||||
require('./config/loggerSetup.js')(logger, logLevel);
|
require('./config/loggerConfig.js')(logger, logLevel);
|
||||||
|
require('./config/slackLoggerConfig.js')(logger);
|
||||||
|
|
||||||
// trust the proxy to get ip address for us
|
// trust the proxy to get ip address for us
|
||||||
app.enable('trust proxy');
|
app.enable('trust proxy');
|
||||||
|
@ -134,7 +135,7 @@ app.set('view engine', 'handlebars');
|
||||||
db.sequelize
|
db.sequelize
|
||||||
.sync() // sync sequelize
|
.sync() // sync sequelize
|
||||||
.then(() => { // get the download directory from the daemon
|
.then(() => { // get the download directory from the daemon
|
||||||
logger.info('Retrieving daemon download directory');
|
logger.info('Retrieving daemon download directory...');
|
||||||
return getDownloadDirectory();
|
return getDownloadDirectory();
|
||||||
})
|
})
|
||||||
.then(hostedContentPath => {
|
.then(hostedContentPath => {
|
||||||
|
@ -154,5 +155,5 @@ db.sequelize
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
logger.error('Startup Error >>', error);
|
logger.error(`Startup Error >> ${error.message}`, error);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue
When is there a non-enumerable property being passed that this is required?