-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathindex.js
More file actions
108 lines (94 loc) · 3.22 KB
/
index.js
File metadata and controls
108 lines (94 loc) · 3.22 KB
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
const parser = require('ua-parser-js');
// settings
const MAX_REQUESTS_PER_BATCH = process.env.MAX_REQUESTS_PER_BATCH || 150;
const MAX_TIME_AWAIT_PER_BATCH = process.env.MAX_TIME_AWAIT_PER_BATCH || 10 * 1000;
const INFLUXDB_HOST = process.env.INFLUXDB_HOST;
const INFLUXDB_DATABASE = process.env.INFLUXDB_DATABASE;
const INFLUXDB_METRIC = process.env.INFLUXDB_METRIC;
const INFLUXDB_USERNAME = process.env.INFLUXDB_USERNAME;
const INFLUXDB_PASSWORD = process.env.INFLUXDB_PASSWORD;
const INFLUXDB_URL = `${INFLUXDB_HOST}/write?db=${INFLUXDB_DATABASE}&precision=s&u=${INFLUXDB_USERNAME}&p=${INFLUXDB_PASSWORD}`;
// global vars
let requests = [];
let batchIsRunning = false;
addEventListener('fetch', event => {
event.passThroughOnException();
event.respondWith(logRequests(event));
})
async function logRequests(event) {
let requestStartTime, requestEndTime;
if (!batchIsRunning) {
event.waitUntil(handleBatch(event));
}
if (requests.length >= MAX_REQUESTS_PER_BATCH) {
event.waitUntil(sendMetricsToInfuxDB())
}
requestStartTime = Date.now();
const response = await fetch(event.request);
requestEndTime = Date.now();
if (event.request.headers.get('DNT') === '1') {
return response;
}
requests.push(getRequestData(event.request, response, requestStartTime, requestEndTime));
return response;
}
async function handleBatch(event) {
batchIsRunning = true;
await sleep(MAX_TIME_AWAIT_PER_BATCH);
try {
if (requests.length) event.waitUntil(sendMetricsToInfuxDB())
} catch (e) {
console.error(e);
}
requests = [];
batchIsRunning = false;
}
function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
function getRequestData(request, response, startTime, endTime) {
const cfData = request.cf || {};
const timestamp = Math.floor(Date.now() / 1000);
const originResponse = response || {};
return {
'timestamp': timestamp,
'userAgent': request.headers.get('user-agent'),
'referer': request.headers.get('Referer'),
'ip': request.headers.get('CF-Connecting-IP'),
'countryCode': cfData.country,
'url': request.url,
'method': request.method,
'status': originResponse.status,
'originTime': (endTime - startTime),
'cfCache': (originResponse) ? (response.headers.get('CF-Cache-Status') || 'miss') : 'miss',
};
}
function formMetricLine(data) {
let referer;
const url = new URL(data.url);
const utmSource = url.searchParams.get('utm_source') || 'empty';
const ua = parser(data.userAgent);
try {
referer = new URL(data.referer);
} catch {
referer = {
hostname: 'empty'
};
}
return `${INFLUXDB_METRIC},status_code=${data.status},url=${data.url},hostname=${url.hostname},pathname=${url.pathname},method=${data.method},cf_cache=${data.cfCache},country=${data.countryCode},referer=${referer.hostname},utm_source=${utmSource},browser=${ua.browser.name},os=${ua.os.name},device=${ua.device.type} duration=${data.originTime} ${data.timestamp}`
}
async function sendMetricsToInfuxDB() {
const metrics = requests.map(formMetricLine).join('\n');
try {
return fetch(INFLUXDB_URL, {
method: 'POST',
body: metrics,
}).then(function (r) {
return r;
});
} catch (err) {
console.log(err.stack || err);
}
}