-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
143 lines (122 loc) · 4.51 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
const path = require('path');
const fs = require('fs');
const os = require('os');
const url = require("url");
const dgram = require("dgram");
// const buffer = require('buffer');
const dgramClient = dgram.createSocket("udp4");
class Metrix {
constructor(telegrafHost = "udp://127.0.0.1", appName = false, appVersion = false, debug = false) {
var parsed = url.parse(telegrafHost);
if (!parsed.protocol || parsed.protocol !== "udp:")
throw new Error("Invalid protocol by target");
parsed.host = parsed.hostname || "127.0.0.1";
parsed.port = parsed.port || 8094;
this.target = parsed;
this.debug = debug;
this.package = false;
this.appName = appName;
this.appVersion = appVersion;
this.pulseTimes = {};
if (fs.existsSync(process.cwd())) {
// try to find package.json file
var pj = path.join(process.cwd(), 'package.json');
if (fs.existsSync(pj)) this.package = require(pj);
}
// to avoid it binding to 0.0.0.0:<randomport> - we do not want to have the responsess
dgramClient.bind({
address: '127.0.0.1',
port: 0,
}, () => {
// console.log(`Server is listening at 127.0.0.1`);
});
}
l(...args) {
if (this.debug) console.log(...args);
}
pulseAuto(section, timeout = 1000) {
setInterval(() => {
this.pulse(section, timeout);
}, timeout)
}
pulse(section, throttle = 1000) {
if (!this.pulseTimes[section] || Date.now() > this.pulseTimes[section] + throttle) {
var tags = { section, host: os.hostname() };
if (this.package) {
tags.appName = this.package.name;
tags.appVersion = this.package.version;
}
if (this.appName) tags.appName = this.appName;
if (this.appVersion) tags.appVersion = this.appVersion;
this.pulseTimes[section] = Date.now();
return this.send('pulse', tags, { heartbeat: 1 });
}
return true;
}
send(measurement, tags, fields, timestamp = null) {
var l = this.line(measurement, tags, fields, timestamp);
if (l !== false) {
dgramClient.send(Buffer.from(l), this.target.port, this.target.host, (err) => {
// lets ignore all kind of errors to be more resilient
});
}
return l;
}
line(measurement, tags = null, fields = null, timestamp = null) {
// measurement must be a string
if (measurement && measurement.search(/[^a-zA-Z0-9_, .-]/) > -1) {
this.l("invalid measurement", measurement);
return false;
}
var tagsPart = "";
// check tags & compose the line
// tags shall be {} or null
if (typeof tags !== "object") return false;
if (tags) {
for (var ky in tags) {
// $k = preg_replace('/(,| )/',"\\\\".'${1}',$k);
if (ky.search(/[^a-zA-Z0-9_, .=]/) > -1) {
this.l("invalid tag key", ky);
return false;
}
if (!tags[ky]) continue
var ky1 = ky.replace(/(,| |=)/g, m => "\\" + m);
var vl1 = tags[ky].toString().replace(/(,| )/g, m => "\\" + m);
tagsPart += "," + ky1 + "=" + vl1;
}
}
// check fields
var tmpFields = [];
var fieldsPart = "";
if (fields !== null) {
if (typeof fields === 'number' || typeof fields === 'string') fields = { value: fields };
if (fields && typeof fields === "object") {
for (var ky in fields) {
if (ky.search(/[^a-zA-Z0-9_, .=]/) > -1) {
this.l("invalid field key", ky);
return false;
}
var ky1 = ky.replace(/(,| |=)/g, m => "\\" + m);
var vl1 = fields[ky];
// field value will be escaped based on its type
if (typeof vl1 === "bool")
tmpFields.push(ky1 + "=" + vl1 ? "true" : false);
else if (typeof vl1 === "string")
tmpFields.push(
ky1 + '="' + vl1.replace(/(\")/g, m => "\\" + m) + '"'
);
else if (typeof vl1 === "number")
tmpFields.push(ky1 + "=" + vl1);
else throw new Error("Unknown field value type by field", ky);
}
} else throw new Error("Unknown fields type", typeof fields);
// } else if (typeof fields === "number") tmpFields.push("value="+fields);
// else if (typeof fields === "string") tmpFields.push("value=\"+fields);
if (tmpFields.length > 0) fieldsPart = " " + tmpFields.join(",");
}
// lets escape , and whitespace
measurement = measurement.replace(/(,| )/g, m => "\\" + m);
return measurement + tagsPart + fieldsPart + (timestamp !== null ? (' ' + timestamp) : '');
}
}
module.exports = Metrix;