From 3ca0b487872885d8ec8c2b0e4d948c4ba82ad259 Mon Sep 17 00:00:00 2001 From: jysperm Date: Wed, 30 Jul 2014 18:14:31 +0800 Subject: [PATCH] public monitor --- core/router/public.coffee | 109 ++++++++++++++++++++++ core/static/style/public/monitor.less | 39 ++++++++ core/view/public/monitor.jade | 124 ++++++++++++++++++++++++++ plugin/linux/monitor.coffee | 15 ++-- plugin/linux/service.coffee | 2 +- 5 files changed, 281 insertions(+), 8 deletions(-) create mode 100644 core/static/style/public/monitor.less create mode 100644 core/view/public/monitor.jade diff --git a/core/router/public.coffee b/core/router/public.coffee index 0dc7927..abce257 100644 --- a/core/router/public.coffee +++ b/core/router/public.coffee @@ -1,6 +1,12 @@ +child_process = require 'child_process' +os = require 'os' +fs = require 'fs' + plugin = require '../plugin' {renderAccount} = require './middleware' +monitor = require '../../plugin/linux/monitor' + module.exports = exports = express.Router() exports.get '/services', renderAccount, (req, res) -> @@ -13,3 +19,106 @@ exports.get '/services', renderAccount, (req, res) -> res.render 'public/services', plans: _.values(config.plans) services: result + +exports.get '/monitor', renderAccount, (req, res) -> + async.parallel + resources_usage: (callback) -> + monitor.monitoringStorage -> + resources_usage = [] + + for username, usage of monitor.resources_usage + resources_usage.push + username: username + cpu: usage.cpu + memory: usage.memory + storage: parseInt monitor.storage_usage[username]?.size_used ? 0 + process: _.filter(monitor.last_plist, (i) -> i.user == username).length + + callback null, resources_usage + + system: (callback) -> + async.parallel + system: (callback) -> + fs.readFile '/etc/issue', (err, content) -> + callback err, content.toString().replace(/\\\w/g, '').trim() + + , (err, result) -> + callback null, _.extend result, + hostname: os.hostname() + cpu: os.cpus()[0]['model'] + uptime: os.uptime() + loadavg: _.map(os.loadavg(), (i) -> i.toFixed(2)).join(', ') + address: os.networkInterfaces()['eth0'][0].address + time: new Date() + + storage: (callback) -> + child_process.exec "df -h", (err, stdout) -> + disks = {} + + for line in stdout.split('\n') + [dev, size, used, available, used_per, mounted] = _.compact(line.split(' ')) + + disks[mounted] = + dev: dev + size: parseInt size?.match(/\d+/) + used: parseInt used?.match(/\d+/) + available: available + used_per: used_per + + root_disk = disks['/'] + + used = root_disk.used + total = root_disk.size + free = total - used + + used_per = (used / total * 100).toFixed() + free_per = 100 - used_per + + callback null, + used: used + free: free + total: total + + used_per: used_per + free_per: free_per + + memory: (callback) -> + fs.readFile '/proc/meminfo', (err, content) -> + mapping = {} + + for line in content.toString().split('\n') + [key, value] = line.split ':' + if value + mapping[key.trim()] = parseInt (parseInt(value.trim().match(/\d+/)) / 1024).toFixed() + + used = mapping['MemTotal'] - mapping['MemFree'] - mapping['Buffers'] - mapping['Cached'] + used_per = (used / mapping['MemTotal'] * 100).toFixed() + cached_per = (mapping['Cached'] / mapping['MemTotal'] * 100).toFixed() + buffers_per = (mapping['Buffers'] / mapping['MemTotal'] * 100).toFixed() + free_per = 100 - used_per - cached_per - buffers_per + + swap_free_per = (mapping['SwapFree'] / mapping['SwapTotal'] * 100).toFixed() + swap_used_per = 100 - swap_free_per + + callback null, + used: used + cached: mapping['Cached'] + buffers: mapping['Buffers'] + free: mapping['MemFree'] + total: mapping['MemTotal'] + swap_used: mapping['SwapTotal'] - mapping['SwapFree'] + swap_free: mapping['SwapFree'] + swap_total: mapping['SwapTotal'] + + used_per: used_per + cached_per: cached_per + buffers_per: buffers_per + free_per: free_per + + swap_used_per: swap_used_per + swap_free_per: swap_free_per + + , (err, result) -> + res.render 'public/monitor', _.extend result, + last_plist: monitor.last_plist + _: _ diff --git a/core/static/style/public/monitor.less b/core/static/style/public/monitor.less new file mode 100644 index 0000000..c829c45 --- /dev/null +++ b/core/static/style/public/monitor.less @@ -0,0 +1,39 @@ +.progress-bar { + text-align: center; +} + +.multi-progress span { + position: inherit; + display: inherit; + width: 100%; + color: inherit; +} + +body #content .row { + margin-bottom: 0px; +} + +.process-list { + overflow-x: scroll; +} + +.col-md-4 { + padding-left: 0; +} + +.col-md-8 { + padding-right: 0; +} + +.process-list td { + font-size: 14px; + font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; + white-space: nowrap; +} + +.progress-bar.blank { + background-color: #f5f5f5; + color: black; + -webkit-box-shadow: none; + box-shadow: none; +} diff --git a/core/view/public/monitor.jade b/core/view/public/monitor.jade new file mode 100644 index 0000000..24b59fe --- /dev/null +++ b/core/view/public/monitor.jade @@ -0,0 +1,124 @@ +extends ../layout + +prepend head + title 服务器状态 | #{t('app.name')} + +append head + link(rel='stylesheet', href='/style/panel.css') + link(rel='stylesheet', href='/style/public/monitor.css') + +block content + #content.container + .row + .col-md-4 + .panel.panel-default + .panel-heading + h3.panel-title System + table.table + tbody + tr + td Hostname + td= system.hostname + tr + td System + td= system.system + tr + td CPU + td= system.cpu + tr + td Time + td= system.time + tr + td Uptime + td= system.uptime + tr + td Loadavg + td= system.loadavg + tr + td Address + td= system.address + + .col-md-8 + .panel.panel-default + .panel-heading + h3.panel-title Memory & Storage + .panel-body + p Memory + .progress.multi-progress + .progress-bar.progress-bar-danger(role='progressbar', style='width: #{memory.used_per}%;') + span(title= '#{memory.used}M') #{memory.used_per}% + .progress-bar.progress-bar-info(role='progressbar', style='width: #{memory.cached_per}%;') + span(title= '#{memory.cached}M') #{memory.cached_per}% + .progress-bar.progress-bar-success(role='progressbar', style='width: #{memory.buffers_per}%;') + span(title= '#{memory.buffers}M') #{memory.buffers_per}% + .progress-bar.blank(role='progressbar', style='width: #{memory.free_per}%;') + span(title= '#{memory.free}M') #{memory.free_per}% + p SAWP + .progress.multi-progress + .progress-bar.progress-bar-warning(role='progressbar', style='width: #{memory.swap_used_per}%;') + span(title= '#{memory.swap_used_per}M') #{memory.swap_used_per}% + .progress-bar.blank(role='progressbar', style='width: #{memory.swap_free_per}%;') + span(title= '#{memory.swap_free}M') #{memory.swap_free_per}% + p Storage + .progress.multi-progress + .progress-bar.progress-bar-warning(role='progressbar', style='width: #{storage.used_per}%;') + span(title= '#{storage.used}%M') #{storage.used_per}% + .progress-bar.blank(role='progressbar', style='width: #{memory.free_per}%;') + span(title= '#{memory.free}M') #{memory.free_per}% + + .row + .panel.panel-default + .panel-heading + h3.panel-title Users + table.table.table-hover + thead + tr + th User + th Process + th CPU 1 Hour + th Memory 1 Hour + th Storage + tbody + for item in resources_usage + tr + td= item.username + td= item.process + td #{item.cpu}s + td #{item.memory.toFixed(1)}M + td #{(item.storage / 1024).toFixed(1)}M + + .row + .panel.panel-default.process-list + .panel-heading + h3.panel-title Process + table.table.table-hover + thead + tr + th USER + th PID + th %CPU + th %MEM + th VSZ + th RSS + th TTY + th STAT + th START + th TIME + th COMMAND + tbody + for process in last_plist + tr + td= process.user + td= process.pid + td= process.cpu_per + td= process.mem_per + td= process.vsz + td= process.rss + td= process.tty + td= process.stat + td= process.start + td= process.time + td!= _.escape(process.command).replace(/ /g, ' ') + +append footer + script(src='/script/panel.js') diff --git a/plugin/linux/monitor.coffee b/plugin/linux/monitor.coffee index 6821774..fefb927 100644 --- a/plugin/linux/monitor.coffee +++ b/plugin/linux/monitor.coffee @@ -9,10 +9,11 @@ REDIS_KEY = 'rp:linux:resources_usage' REDIS_OVERVIEW = 'rp:linux:overview' ITEM_IN_RESOURCES_LIST = 3600 * 1000 / config.plugins.linux.monitor_cycle -last_plist = [] +exports.last_plist = [] passwd_cache = {} exports.resources_usage = {} +exports.storage_usage = {} exports.run = -> exports.monitoring() @@ -77,7 +78,7 @@ exports.monitoring = -> exports.loadPasswd -> exports.getProcessList (plist) -> plist = _.reject plist, (item) -> - return item.user == 'root' + return item.rss == 0 async.parallel cpu: (callback) -> @@ -129,7 +130,7 @@ exports.monitoring = -> callback() , -> app.redis.set REDIS_KEY, JSON.stringify(resources_usage_list), -> - last_plist = plist + exports.last_plist = plist exports.monitoringStorage = (callback) -> app.redis.get 'rp:storage_usage', (err, result) -> @@ -153,19 +154,19 @@ exports.monitoringStorage = (callback) -> inode_used: inode_used } - exports.resources_usage.storage = {} + exports.storage_usage = {} for item in lines - exports.resources_usage.storage[item.username] = item + exports.storage_usage[item.username] = item - app.redis.setex 'rp:storage_usage', 3, JSON.stringify(exports.resources_usage.storage), -> + app.redis.setex 'rp:storage_usage', 3, JSON.stringify(exports.storage_usage), -> callback() exports.monitoringCpu = (plist, callback) -> total_time = {} findLastProcess = (process) -> - return _.find last_plist, (i) -> + return _.find exports.last_plist, (i) -> return i.pid == process.pid and i.user == process.user and i.command == process.command addTime = (account_name, time) -> diff --git a/plugin/linux/service.coffee b/plugin/linux/service.coffee index 6734f71..14b1f28 100644 --- a/plugin/linux/service.coffee +++ b/plugin/linux/service.coffee @@ -27,7 +27,7 @@ module.exports = } storage_usage: do -> - usage = monitor.resources_usage['storage'][account.username] + usage = monitor.storage_usage[account.username] now_per = (usage.size_used / 1000 / account.attribute.resources_limit.storage * 100).toFixed() return { now_per: now_per