1/* 2 * Copyright (C) 2929, Unicode, Inc. 3 * For terms of use, see http://www.unicode.org/terms_of_use.html 4 */ 5 6const express = require('express'); 7const process = require('process'); 8const server = express(); 9const client = require('prom-client'); 10const bent = require('bent'); 11const getJson = bent('json'); 12const config = require('./config.json'); 13const {register} = client; // global registry 14 15const items = { 16 responses: new client.Counter({ 17 name: 'surveytool_exporter_responses', 18 help: 'Number of pages served by this exporter' 19 }), 20 oks: new client.Counter({ 21 name: 'surveytool_exporter_oks', 22 help: 'Number of OK fetches', 23 labelNames: ['instance'] 24 }), 25 fails: new client.Counter({ 26 name: 'surveytool_exporter_fails', 27 help: 'Number of failed fetches', 28 labelNames: ['instance'] 29 }), 30 ok: new client.Gauge({ 31 name: 'surveytool_ok', 32 help: '1 if the surveytool is ok, otherwise 0', 33 labelNames: ['instance'] 34 }), 35 isSetup: new client.Gauge({ 36 name: 'surveytool_setup', 37 help: '1 if the surveytool is setup, otherwise 0', 38 labelNames: ['instance'] 39 }), 40 isBusted: new client.Gauge({ 41 name: 'surveytool_busted', 42 help: '1 if the surveytool is busted, otherwise 0', 43 labelNames: ['instance'/*, 'err'*/] 44 }), 45 fetchTime: new client.Gauge({ 46 name: 'surveytool_fetchTime', 47 help: 'time of successful fetch', 48 labelNames: ['instance'] 49 }), 50 fetchErr: new client.Gauge({ 51 name: 'surveytool_fetchErr', 52 help: 'error code on failed fetch, or 200', 53 labelNames: ['instance'/*, 'err'*/] 54 }), 55 pages: new client.Gauge({ 56 name: 'surveytool_pages', 57 help: 'page count', 58 labelNames: ['instance'] 59 }), 60 users: new client.Gauge({ 61 name: 'surveytool_users', 62 help: 'user count', 63 labelNames: ['instance'] 64 }), 65 stamp: new client.Gauge({ 66 name: 'surveytool_stamp', 67 help: 'survey running stamp', 68 labelNames: ['instance' /*, 69 'phase', 'sysprocs', 'environment', 'currev', 'newVersion'*/] 70 }), 71 memtotal: new client.Gauge({ 72 name: 'surveytool_memtotal', 73 help: 'total memory in process', 74 labelNames: ['instance'] 75 }), 76 memfree: new client.Gauge({ 77 name: 'surveytool_memfree', 78 help: 'total free memory', 79 labelNames: ['instance'] 80 }), 81 dbused: new client.Gauge({ 82 name: 'surveytool_dbused', 83 help: 'db queries used', 84 labelNames: ['instance'] 85 }), 86 sysload: new client.Gauge({ 87 name: 'surveytool_sysload', 88 help: 'system load, if available', 89 labelNames: ['instance'] 90 }), 91}; 92 93async function update(e) { 94 const [instance, url] = e; 95 try { 96 const res = await getJson(url); 97 items.oks.inc({instance}); 98 items.fetchErr.set({ instance }, 200); 99 items.fetchTime.set({ instance }, new Date().getTime()/1000); 100 items.ok.set({instance}, Number(res.SurveyOK)); 101 items.isSetup.set({instance}, Number(res.isSetup)); 102 if(res.status) { 103 const{phase, memtotal, sysprocs, isBusted, isUnofficial, lockOut, 104 users, uptime, memfree, environment, pages, specialHeader, currev, 105 dbopen, surveyRunningStamp, guests, dbused,sysload, isSetup, newVersion} = res.status; 106 items.pages.set({instance}, Number(pages)); 107 items.users.set({instance}, Number(users)); 108 items.memtotal.set({instance}, Number(memtotal)); 109 items.memfree.set({instance}, Number(memfree)); 110 items.dbused.set({instance}, Number(dbused)); 111 items.sysload.set({instance}, Number(sysload)); 112 items.stamp.set({instance /*, 113 phase, sysprocs, environment, currev, newVersion*/}, 114 surveyRunningStamp); 115 if(isBusted) { 116 items.isBusted.set({instance/*, err: isBusted*/}, Number(1)); 117 } else { 118 items.isBusted.set({instance/*, err: (res.err || '')*/}, Number(res.isBusted)); 119 } 120 } else { 121 items.isBusted.set({instance/*, err: (res.err || '')*/}, Number(res.isBusted)); 122 } 123 } catch(ex) { 124 items.fails.inc({instance}); 125 items.fetchErr.set({ instance /*, err: ex.toString()*/ }, 999); 126 } 127} 128 129async function updateAll() { 130 return Promise.all(Object.entries(config.instances) 131 .map(e => update(e))); 132} 133 134server.get('/metrics', async (req, res) => { 135 items.responses.inc(); 136 res.contentType(register.contentType); 137 await updateAll(); 138 res.end(register.metrics()); 139}); 140 141const port = process.env.PORT || config.port || 3000; 142 143server.listen(port, () => { 144 console.log('ST exporter listening on port ' + port); 145});