forked from mattpker/pm2-slack
-
Notifications
You must be signed in to change notification settings - Fork 16
/
index.js
214 lines (177 loc) · 6.77 KB
/
index.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
'use strict';
var os = require('os');
var pm2 = require('pm2');
var pmx = require('pmx');
var request = require('request');
// Get the configuration from PM2
var conf = pmx.initModule();
// initialize buffer and queue_max opts
// buffer seconds can be between 1 and 5
conf.buffer_seconds = (conf.buffer_seconds > 0 && conf.buffer_seconds < 5) ? conf.buffer_seconds : 1;
// queue max can be between 10 and 100
conf.queue_max = (conf.queue_max > 10 && conf.queue_max <= 100) ? conf.queue_max : 100;
// Set the events that will trigger the color red
var redEvents = ['stop', 'exit', 'delete', 'error', 'kill', 'exception', 'restart overlimit', 'suppressed'];
// create the message queue
var messages = [];
// create the suppressed object for sending suppression messages
var suppressed = {
isSuppressed: false,
date: new Date().getTime()
};
// Function to send event to Slack's Incoming Webhook
function sendSlack(message) {
var name = message.name;
var event = message.event;
var description = message.description;
// If a Slack URL is not set, we do not want to continue and nofify the user that it needs to be set
if (!conf.slack_url) return console.error("There is no Slack URL set, please set the Slack URL: 'pm2 set pm2-slack:slack_url https://slack_url'");
// The default color for events should be green
var color = '#008E00';
// If the event is listed in redEvents, set the color to red
if (redEvents.indexOf(event) > -1) {
color = '#D00000';
}
// The JSON payload to send to the Webhook
var payload = {
username: os.hostname(),
attachments: [{
fallback: name + ' - ' + event + ' - ' + description,
color: color,
fields: [{
title: name + ' - ' + event,
value: description,
short: true
}]
}]
};
// Options for the post request
var options = {
method: 'post',
body: payload,
json: true,
url: conf.slack_url
};
// Finally, make the post request to the Slack Incoming Webhook
request(options, function(err, res, body) {
if (err) return console.error(err);
if (body !== 'ok') {
console.error('Error sending notification to Slack, verify that the Slack URL for incoming webhooks is correct.');
}
});
}
// Function to get the next buffer of messages (buffer length = 1s)
function bufferMessage() {
var nextMessage = messages.shift();
if (!conf.buffer) { return nextMessage; }
nextMessage.buffer = [nextMessage.description];
// continue shifting elements off the queue while they are the same event and timestamp so they can be buffered together into a single request
while (messages.length
&& (messages[0].timestamp >= nextMessage.timestamp && messages[0].timestamp < (nextMessage.timestamp + conf.buffer_seconds))
&& messages[0].event === nextMessage.event) {
// append description to our buffer and shift the message off the queue and discard it
nextMessage.buffer.push(messages[0].description);
messages.shift();
}
// join the buffer with newlines
nextMessage.description = nextMessage.buffer.join("\n");
// delete the buffer from memory
delete nextMessage.buffer;
return nextMessage;
}
// Function to process the message queue
function processQueue() {
// If we have a message in the message queue, removed it from the queue and send it to slack
if (messages.length > 0) {
sendSlack(bufferMessage());
}
// If there are over conf.queue_max messages in the queue, send the suppression message if it has not been sent and delete all the messages in the queue after this amount (default: 100)
if (messages.length > conf.queue_max) {
if (!suppressed.isSuppressed) {
suppressed.isSuppressed = true;
suppressed.date = new Date().getTime();
sendSlack({
name: 'pm2-slack',
event: 'suppressed',
description: 'Messages are being suppressed due to rate limiting.'
});
}
messages.splice(conf.queue_max, messages.length);
}
// If the suppression message has been sent over 1 minute ago, we need to reset it back to false
if (suppressed.isSuppressed && suppressed.date < (new Date().getTime() - 60000)) {
suppressed.isSuppressed = false;
}
// Wait 10 seconds and then process the next message in the queue
setTimeout(function() {
processQueue();
}, 10000);
}
// Start listening on the PM2 BUS
pm2.launchBus(function(err, bus) {
// Listen for process logs
if (conf.log) {
bus.on('log:out', function(data) {
if (data.process.name !== 'pm2-slack') {
messages.push({
name: data.process.name,
event: 'log',
description: JSON.stringify(data.data),
timestamp: Math.floor(Date.now() / 1000),
});
}
});
}
// Listen for process errors
if (conf.error) {
bus.on('log:err', function(data) {
if (data.process.name !== 'pm2-slack') {
messages.push({
name: data.process.name,
event: 'error',
description: JSON.stringify(data.data),
timestamp: Math.floor(Date.now() / 1000),
});
}
});
}
// Listen for PM2 kill
if (conf.kill) {
bus.on('pm2:kill', function(data) {
messages.push({
name: 'PM2',
event: 'kill',
description: data.msg,
timestamp: Math.floor(Date.now() / 1000),
});
});
}
// Listen for process exceptions
if (conf.exception) {
bus.on('process:exception', function(data) {
if (data.process.name !== 'pm2-slack') {
messages.push({
name: data.process.name,
event: 'exception',
description: JSON.stringify(data.data),
timestamp: Math.floor(Date.now() / 1000),
});
}
});
}
// Listen for PM2 events
bus.on('process:event', function(data) {
if (conf[data.event]) {
if (data.process.name !== 'pm2-slack') {
messages.push({
name: data.process.name,
event: data.event,
description: 'The following event has occured on the PM2 process ' + data.process.name + ': ' + data.event,
timestamp: Math.floor(Date.now() / 1000),
});
}
}
});
// Start the message processing
processQueue();
});