diff --git a/NEWS b/NEWS index d8600e7552240aac954439ae46904ec66c13faa8..15d68d23ff9eb97586752c7dd5631128a9f3e6a2 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ Version 1.3.7 ============= + * Display (if terminal space is available) an alerts history (logs) + * Add a limits classe to manage stats limits * Manage black and white console (issue #31) Version 1.3.6 diff --git a/README b/README index c2afdc1e8c97f54008b65a640caf9ca06d370176..3ccb7acce7d6d3b2a3e2b5274dae9ea2a4348846 100644 --- a/README +++ b/README @@ -23,12 +23,12 @@ Packages exist for Arch, Fedora, Redhat ... Get the latest version: - $ wget https://github.com/downloads/nicolargo/glances/glances-1.3.6.tar.gz + $ wget https://github.com/downloads/nicolargo/glances/glances-1.3.7.tar.gz Glances use a standard GNU style installer: - $ tar zxvf glances-1.3.6.tar.gz - $ cd glances-1.3.6 + $ tar zxvf glances-1.3.7.tar.gz + $ cd glances-1.3.7 $ ./configure $ make $ sudo make install @@ -85,6 +85,7 @@ When Glances is running, you can press: * 'c' to sort the processes list by CPU consumption * 'd' Disable or enable the disk IO stats * 'f' Disable or enable the file system stats +* 'l' Disable or enable the logs * 'm' to sort the processes list by process size * 'n' Disable or enable the network interfaces stats * 'q' Exit @@ -208,6 +209,22 @@ sorted by CPU of memory consumption. The number of processes in the list is adapted to the screen size. +### Logs + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/logs.png) + +A logs list is displayed in the bottom of the screen if (an only if): + +* at least one WARNING or CRITICAL alert was occured. +* space is available in the bottom of the console/terminal + +There is one line per alert with the following information: + +* start date +* end date +* alert name +* (min/avg/max) values + ### Footer ![screenshot](https://github.com/nicolargo/glances/raw/master/doc/footer.png) diff --git a/README-fr b/README-fr index 370cc62236e6775d16bf021fc791699bcf316e77..1507bb21f49781b1c9bfdcca4894abb852412e16 100644 --- a/README-fr +++ b/README-fr @@ -27,14 +27,14 @@ Le projet Glances est hébergé sur GitHUB: https://github.com/nicolargo/glances Pour l'installer, il suffit de suivre les instructions suivantes depuis un terminal. -Récupération de la dernière version (1.3.6): +Récupération de la dernière version (1.3.7): - $ wget https://github.com/downloads/nicolargo/glances/glances-1.3.6.tar.gz + $ wget https://github.com/downloads/nicolargo/glances/glances-1.3.7.tar.gz Procédez ensuite à l'installation: - $ tar zxvf glances-1.3.6.tar.gz - $ cd glances-1.3.6 + $ tar zxvf glances-1.3.7.tar.gz + $ cd glances-1.3.7 $ ./configure $ make $ sudo make install @@ -104,6 +104,7 @@ Quand Glances est lancé, il est possible d'utiliser les touches suivantes: * 'c' pour forcer le tri par consommation CPU * 'd' pour desactiver ou activer l'affichage des entrées/sorties disques * 'f' pour desactiver ou activer l'affichage de l'occupation des FS +* 'l' pour desactiver ou activer l'affichage des logs * 'm' pour forcer le tri par consommation MEMOIRE * 'n' pour desactiver ou activer l'affichage des interfaces réseau * 'q' sortir de Glances (il est également possible d'utiliser CTRL-C) @@ -229,6 +230,22 @@ détaillée. Le nombre des processus affichés est adapté à la taille de la fenêtre. +### Logs + +![screenshot](https://github.com/nicolargo/glances/raw/master/doc/logs.png) + +Une liste des dernières alertes relevées par Glances est affichée si: + +* au moins une alerte de type WARNING ou CRITICAL est arrivée +* la console/fenêtre dispose de la place nécessaire + +Glances affiche une ligne par alerte: + +* date de début +* date de fin +* description de l'alerte +* valeur (min/moyenne/max) lors de l'alerte + ### Pied de page ![screenshot](https://github.com/nicolargo/glances/raw/master/doc/footer.png) @@ -254,6 +271,4 @@ Notamment: * Packaging pour Debian, Ubuntu, BSD et toutes autres distributions * Controle de la présence des librairie dans le fichier configure.ac -* Inclure les stats de FS directement dans python-statgrab -* Ajout d'une fenêtre d'aide * Optimisation du code diff --git a/README.md b/README.md deleted file mode 100644 index c2afdc1e8c97f54008b65a640caf9ca06d370176..0000000000000000000000000000000000000000 --- a/README.md +++ /dev/null @@ -1,231 +0,0 @@ -[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=nicolargo&url=https://github.com/nicolargo/glances&title=Glances&language=&tags=github&category=software) - -============================= -Glances -- Eye on your system -============================= - -## Description - -Glances is a CLI curses based monitoring tool for GNU/Linux or BSD OS. - -Glances uses the libstatgrab library to get information from your system. -It is developed in Python and uses the python-statgrab lib. - -![screenshot](https://github.com/nicolargo/glances/raw/master/screenshot.png) - -## Installation - -### From package manager - -Packages exist for Arch, Fedora, Redhat ... - -### From source - -Get the latest version: - - $ wget https://github.com/downloads/nicolargo/glances/glances-1.3.6.tar.gz - -Glances use a standard GNU style installer: - - $ tar zxvf glances-1.3.6.tar.gz - $ cd glances-1.3.6 - $ ./configure - $ make - $ sudo make install - -Pre-requisites: - -* Python 2.6+ (not tested with Python 3+) -* python-statgrab 0.5+ (did NOT work with python-statgrab 0.4) - -Notes: For Debian. -The Debian Squeeze repos only include the python-statgrab 0.4. -You had to install the version 0.5 using the following commands: - - $ sudo apt-get install libstatgrab-dev pkg-config python-dev make - $ wget http://ftp.uk.i-scream.org/sites/ftp.i-scream.org/pub/i-scream/pystatgrab/pystatgrab-0.5.tar.gz - $ tar zxvf pystatgrab-0.5.tar.gz - $ cd pystatgrab-0.5/ - $ ./setup.py build - $ sudo ./setup.py install - -Notes: For Ubuntu 10.04 and 10.10. -The instruction to install the version 0.5 are here: -https://github.com/nicolargo/glances/issues/5#issuecomment-3033194 - -## Running - -Easy: - - $ glances.py - -## User guide - -By default, stats are refreshed every second, to change this setting, you can -use the -t option. For exemple to set the refrech rate to 5 seconds: - - $ glances.py -t 5 - -Importants stats are colored: - -* GREEN: stat counter is "OK" -* BLUE: stat counter is "CAREFUL" -* MAGENTA: stat counter is "WARNING" -* RED: stat counter is "CRITICAL" - -When Glances is running, you can press: - -* 'h' to display an help message whith the keys you can press -* 'a' to set the automatic mode. The processes are sorted automatically - - If CPU > 70%, sort by process "CPU consumption" - - If MEM > 70%, sort by process "memory size" - -* 'c' to sort the processes list by CPU consumption -* 'd' Disable or enable the disk IO stats -* 'f' Disable or enable the file system stats -* 'm' to sort the processes list by process size -* 'n' Disable or enable the network interfaces stats -* 'q' Exit - -### Header - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/header.png) - -The header shows the Glances version, the host name and the operating -system name, version and architecture. - -### CPU - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/cpu.png) - -The CPU states are shown as a percentage and for the configured refresh -time. - -If user|kernel|nice CPU is < 50%, then status is set to "OK". - -If user|kernel|nice CPU is > 50%, then status is set to "CAREFUL". - -If user|kernel|nice CPU is > 70%, then status is set to "WARNING". - -If user|kernel|nice CPU is > 90%, then status is set to "CRITICAL". - -### Load - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/load.png) - -On the Nosheep blog, Zach defines the average load: "In short it is the -average sum of the number of processes waiting in the run-queue plus the -number currently executing over 1, 5, and 15 minute time periods." - -Glances gets the number of CPU cores to adapt the alerts. With Glances, -alerts on average load are only set on 5 and 15 mins. - -If average load is < O.7*Core, then status is set to "OK". - -If average load is > O.7*Core, then status is set to "CAREFUL". - -If average load is > 1*Core, then status is set to "WARNING". - -If average load is > 5*Core, then status is set to "CRITICAL". - -### Memory - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/mem.png) - -Glances uses tree columns: memory (RAM), swap and "real". - -Real used memory is: used - cache. - -Real free memory is: free + cache. - -With Glances, alerts are only set for on used swap and real memory. - -If memory is < 50%, then status is set to "OK". - -If memory is > 50%, then status is set to "CAREFUL". - -If memory is > 70%, then status is set to "WARNING". - -If memory is > 90%, then status is set to "CRITICAL". - -### Network bit rate - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/network.png) - -Glances display the network interface bit rate. The unit is adapted -dynamicaly (bits per second, Kbits per second, Mbits per second...). - -Alerts are set only if the network interface maximum speed is available. - -If bitrate is < 50%, then status is set to "OK". - -If bitrate is > 50%, then status is set to "CAREFUL". - -If bitrate is > 70%, then status is set to "WARNING". - -If bitrate is > 90%, then status is set to "CRITICAL". - -For exemple, on a 100 Mbps Ethernet interface, the warning status is set -if the bit rate is higher than 70 Mbps. - -### Disk I/O - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/diskio.png) - -Glances display the disk I/O throughput. The unit is adapted dynamicaly -(bytes per second, Kbytes per second, Mbytes per second...). - -There is no alert on this information. - -### Filesystem - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/fs.png) - -Glances display the total and used filesytem disk space. The unit is -adapted dynamicaly (bytes per second, Kbytes per second, Mbytes per -second...). - -Alerts are set for used disk space: - -If disk used is < 50%, then status is set to "OK". - -If disk used is > 50%, then status is set to "CAREFUL". - -If disk used is > 70%, then status is set to "WARNING". - -If disk used is > 90%, then status is set to "CRITICAL". - -### Processes - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/processlist.png) - -Glances displays a summary and a list of processes. - -By default (or if you hit the 'a' key) the process list is automaticaly -sorted by CPU of memory consumption. - -The number of processes in the list is adapted to the screen size. - -### Footer - -![screenshot](https://github.com/nicolargo/glances/raw/master/doc/footer.png) - -Glances displays a caption and the current time/date. - -## Localisation - -To generate french locale execute as root or sudo : -i18n_francais_generate.sh - -To generate spanish locale execute as root or sudo : -i18n_espanol_generate.sh - -## Todo - -You are welcome to contribute to this software. - -* Packaging for Debian, Ubuntu, BSD... -* Check the needed Python library in the configure.ac -* Add file system stats when the python-statgrab is corrected diff --git a/README.md b/README.md new file mode 120000 index 0000000000000000000000000000000000000000..100b93820ade4c16225673b4ca62bb3ade63c313 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +README \ No newline at end of file diff --git a/TODO b/TODO index 9e665f4286e64109205669e5c18cd1c89f155822..0531dad3fb1f2f87814d30414828e0170f32bc36 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ - Packaging for .deb (Debian|Ubuntu|Mint) Linux distributions (contributors needed) - Test/coding for xBSD - Add control to check the libstatgrab version in the script -- Add thermal information for CPU (issue #26) +- Add a limit class to manage the OK ,CAREFUL,WARNING,CRITICAL limits diff --git a/buildout.cfg b/buildout.cfg index d87898f5f9f3db926ba4fb1c4452fa6d4b6cbc6c..dfdf6b0fb63ca7bac23b70a16e906bb53a311dcd 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -30,7 +30,7 @@ include-site-packages = false allowed-eggs-from-site-packages = false [config] -glances_version = 1.3.6 +glances_version = 1.3.7 pystatgrab_version = 0.5 pystatgrab_download_url = http://ftp.uk.i-scream.org/sites/ftp.i-scream.org/pub/i-scream/pystatgrab diff --git a/doc/logs.png b/doc/logs.png new file mode 100644 index 0000000000000000000000000000000000000000..d51f2be8c85d6d9ae6fd478b2a901b81040d72bb Binary files /dev/null and b/doc/logs.png differ diff --git a/man/glances.1 b/man/glances.1 index 7022971980790a02120a701ead3b4cfd69d7681e..5fd60e39a2f12d41b6790cd1862cc40f63663a59 100644 --- a/man/glances.1 +++ b/man/glances.1 @@ -1,4 +1,4 @@ -.TH glances 1 "January, 2012" "version 1.3.6" "USER COMMANDS" +.TH glances 1 "January, 2012" "version 1.3.7" "USER COMMANDS" .SH NAME glances \- CLI curses based monitoring tool .SH SYNOPSIS @@ -22,6 +22,8 @@ You can use the following keys to sort the processesi list: 'f' Disable or enable the file system stats .PP 'h' Hide or show the help message +.PP + 'l' Hide or show the logs .PP 'm' the processes list is sorted by process size .PP diff --git a/screenshot.png b/screenshot.png index 04d5faf3e0023af50cdc6bfe7eeec118cd8c0b33..a9efb2b59bb31b5180c97ba2942236ddf8f33df6 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/setup.py b/setup.py index 308efbe30943451f8ad5d870ce478ada95a5af55..cdcc6434de4ec6f70d29cbf481919569d3f172ab 100644 --- a/setup.py +++ b/setup.py @@ -11,8 +11,8 @@ def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() setup( name='Glances', - version='1.3.6', - download_url='https://github.com/downloads/nicolargo/glances/glances-1.3.6.tar.gz', + version='1.3.7', + download_url='https://github.com/downloads/nicolargo/glances/glances-1.3.7.tar.gz', url='https://github.com/nicolargo/glances', description='CLI curses-based monitoring tool', author='Nicolas Hennion', diff --git a/src/glances.py b/src/glances.py index 52f5278385499c28eaaaf0322f1176447b44d70c..8ff0b8186e82198a10bd1fc97ab024f90d5413be 100755 --- a/src/glances.py +++ b/src/glances.py @@ -10,7 +10,7 @@ # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # -# glances is distributed in the hope that it will be useful, but +# Glances is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU Lesser General Public License for more details. @@ -19,26 +19,33 @@ # along with this program. If not, see ."; # -import os -import getopt -import sys -import signal -import time -import datetime -import multiprocessing -import gettext +from __future__ import generators -#i18n -#==== +try: + import os + import getopt + import sys + import signal + import time + import datetime + import multiprocessing + import gettext +except KeyboardInterrupt: + pass + + +# i18n +#===== application = 'glances' +__version__ = "1.3.7" gettext.install(application) try: import statgrab except: - print 'Statgrab initialization failed, Glances cannot start.' - print '' + print _('Statgrab initialization failed, Glances cannot start.') + print sys.exit(1) try: @@ -49,14 +56,9 @@ except: print sys.exit(1) -# Globals variables -#================== - -# The glances version id -__version__ = "1.3.6" -# Class -#====== +# Classes +#======== class Timer(): """ @@ -64,12 +66,147 @@ class Timer(): """ def __init__(self, duration): + self.started(duration) + + def started(self, duration): self.target = time.time() + duration def finished(self): return time.time() > self.target +class glancesLimits(): + """ + Manage the limit OK,CAREFUL,WARNING,CRITICAL for each stats + """ + + # The limit list is stored in an hash table: + # limits_list[STAT] = [ CAREFUL , WARNING , CRITICAL ] + # Exemple: + # limits_list['STD'] = [ 50, 70 , 90 ] + + __limits_list = { # CAREFUL WARNING CRITICAL + 'STD': [50, 70, 90], + 'LOAD': [0.7, 1.0, 5.0] + } + + def getSTDCareful(self): + return self.__limits_list['STD'][0] + + def getSTDWarning(self): + return self.__limits_list['STD'][1] + + def getSTDCritical(self): + return self.__limits_list['STD'][2] + + def getLOADCareful(self, core = 1): + return self.__limits_list['LOAD'][0] * core + + def getLOADWarning(self, core = 1): + return self.__limits_list['LOAD'][1] * core + + def getLOADCritical(self, core = 1): + return self.__limits_list['LOAD'][2] * core + + +class glancesLogs(): + """ + The main class to manage logs inside the Glances software + Logs is a list of list: + [["begin", "end", "WARNING|CRITICAL", "CPU|LOAD|MEM", MAX, AVG, MIN, SUM, COUNT],...] + """ + + def __init__(self): + """ + Init the logs classe + """ + # Maximum size of the logs list + self.logs_max = 10 + + # Init the logs list + self.logs_list = [] + + def get(self): + """ + Return the logs list (RAW) + """ + return self.logs_list + + def len(self): + """ + Return the number of item in the log list + """ + return self.logs_list.__len__() + + def __itemexist__(self, item_type): + """ + An item exist in the list if: + * end is < 0 + * item_type is matching + """ + for i in range(self.len()): + if ((self.logs_list[i][1] < 0) and + (self.logs_list[i][3] == item_type)): + return i + return -1 + + def add(self, item_state, item_type, item_value): + """ + item_state = "OK|CAREFUL|WARNING|CRITICAL" + item_type = "CPU|LOAD|MEM" + item_value = value + Item is defined by: + ["begin", "end", "WARNING|CRITICAL", "CPU|LOAD|MEM", MAX, AVG, MIN, SUM, COUNT] + If item is a 'new one': + Add the new item at the beginning of the logs list + Else: + Update the existing item + """ + item_index = self.__itemexist__(item_type) + if (item_index < 0): + # Item did not exist, add if WARNING or CRITICAL + if ((item_state == "WARNING") or + (item_state == "CRITICAL")): + # Time is stored in Epoch format + # Epoch -> DMYHMS = datetime.datetime.fromtimestamp(epoch) + item = [] + item.append(time.mktime(datetime.datetime.now().timetuple())) + item.append(-1) + item.append(item_state) # STATE: WARNING|CRITICAL + item.append(item_type) # TYPE: CPU, LOAD, MEM... + item.append(item_value) # MAX + item.append(item_value) # AVG + item.append(item_value) # MIN + item.append(item_value) # SUM + item.append(1) # COUNT + self.logs_list.insert(0, item) + if (self.len() > self.logs_max): + self.logs_list.pop() + else: + # Item exist, update + if ((item_state == "OK") or + (item_state == "CAREFUL")): + # Close the item + self.logs_list[item_index][1] = time.mktime(datetime.datetime.now().timetuple()) + else: + # Update the item + # State + if (item_state == "CRITICAL"): + self.logs_list[item_index][2] = item_state + # Value + if (item_value > self.logs_list[item_index][4]): + # MAX + self.logs_list[item_index][4] = item_value + elif (item_value < self.logs_list[item_index][6]): + # MIN + self.logs_list[item_index][6] = item_value + # AVG + self.logs_list[item_index][7] += item_value + self.logs_list[item_index][8] += 1 + self.logs_list[item_index][5] = self.logs_list[item_index][7] / self.logs_list[item_index][8] + return self.len() + + class glancesGrabFs(): """ Get FS stats: idem as structure http://www.i-scream.org/libstatgrab/docs/sg_get_fs_stats.3.html @@ -281,7 +418,7 @@ class glancesStats(): # Else sort by cpu comsoption sortedby = 'cpu_percent' if ( self.mem['total'] != 0): - if ( ( (self.mem['used'] - self.mem['cache']) * 100 / self.mem['total']) > 70): + if ( ( (self.mem['used'] - self.mem['cache']) * 100 / self.mem['total']) > limits.getSTDWarning()): sortedby = 'proc_size' return sorted(self.process, key=lambda process: process[sortedby], reverse=True) @@ -296,8 +433,8 @@ class glancesScreen(): """ # By default the process list is automaticaly sorted - # If global CPU > 75% => Sorted by process Cpu consomption - # If global used MEM > 75% => Sorted by process size + # If global CPU > WANRING => Sorted by process Cpu consomption + # If global used MEM > WARINING => Sorted by process size __process_sortedby = 'auto' def __init__(self, refresh_time = 1): @@ -315,6 +452,7 @@ class glancesScreen(): self.diskio_x = 0 ; self.diskio_y = -1 self.fs_x = 0 ; self.fs_y = -1 self.process_x = 30; self.process_y = 9 + self.log_x = 0 ; self.log_y = -1 self.help_x = 30; self.help_y = 12 self.now_x = 79; self.now_y = 3 self.caption_x = 0 ; self.caption_y = 3 @@ -355,29 +493,45 @@ class glancesScreen(): # Colors text styles self.no_color = curses.color_pair(1) self.default_color = curses.color_pair(3)|curses.A_BOLD - self.if50pc_color = curses.color_pair(4)|curses.A_BOLD - self.if70pc_color = curses.color_pair(5)|curses.A_BOLD - self.if90pc_color = curses.color_pair(2)|curses.A_BOLD + self.ifCAREFUL_color = curses.color_pair(4)|curses.A_BOLD + self.ifWARNING_color = curses.color_pair(5)|curses.A_BOLD + self.ifCRITICAL_color = curses.color_pair(2)|curses.A_BOLD else: # B&W text styles self.no_color = curses.A_NORMAL self.default_color = curses.A_NORMAL - self.if50pc_color = curses.A_UNDERLINE - self.if70pc_color = curses.A_BOLD - self.if90pc_color = curses.A_REVERSE + self.ifCAREFUL_color = curses.A_UNDERLINE + self.ifWARNING_color = curses.A_BOLD + self.ifCRITICAL_color = curses.A_REVERSE + + # Define the colors list (hash table) + self.__colors_list = { + # CAREFUL WARNING CRITICAL + 'DEFAULT': self.no_color, + 'OK': self.default_color, + 'CAREFUL': self.ifCAREFUL_color, + 'WARNING': self.ifWARNING_color, + 'CRITICAL': self.ifCRITICAL_color + } # By default all the stats are displayed self.network_tag = True self.diskio_tag = True self.fs_tag = True + self.log_tag = True # Init main window self.term_window = self.screen.subwin(0, 0) # Init help panel - term_help = self.screen.subwin(self.term_h-self.help_y-2, self.term_w-self.help_x, self.help_y, self.help_x) - self.panel_help = curses.panel.new_panel(term_help) - self.hideHelp() + # TODO: pb when size of the screen < 22 lines + screen_x = self.screen.getmaxyx()[1] + screen_y = self.screen.getmaxyx()[0] + if (screen_x > (self.term_w-self.help_x) and + (screen_y > (self.term_h-self.help_y-2))): + term_help = self.screen.subwin(self.term_h-self.help_y-2, self.term_w-self.help_x, self.help_y, self.help_x) + self.panel_help = curses.panel.new_panel(term_help) + self.hideHelp() # Init refresh time self.__refresh_time = refresh_time @@ -413,69 +567,76 @@ class glancesScreen(): else: return str(int(val)) - def __getColor(self, current = 0, max = 100): - # If current > 50% of max then color = self.if50pc_color / A_DIM - # If current > 70% of max then color = self.if70pc_color / A_BOLD - # If current > 90% of max then color = self.if90pc_color / A_REVERSE - # By default: color = self.default_color / 0 + def __getAlert(self, current = 0, max = 100): + # If current < CAREFUL of max then alert = OK + # If current > CAREFUL of max then alert = CAREFUL + # If current > WARNING of max then alert = WARNING + # If current > CRITICAL of max then alert = CRITICAL try: (current * 100) / max except ZeroDivisionError: - return 0 + return 'DEFAULT' variable = (current * 100) / max - if variable > 90: - if self.hascolors: - return self.if90pc_color - else: - return curses.A_REVERSE - elif variable > 70: - if self.hascolors: - return self.if70pc_color - else: - return curses.A_BOLD - elif variable > 50: - if self.hascolors: - return self.if50pc_color - else: - return curses.A_DIM - else: - if self.hascolors: - return self.default_color - else: - return 0 + if variable > limits.getSTDCritical(): + return 'CRITICAL' + elif variable > limits.getSTDWarning(): + return 'WARNING' + elif variable > limits.getSTDCareful(): + return 'CAREFUL' + + return 'OK' + + + def __getColor(self, current = 0, max = 100): + return self.__colors_list[self.__getAlert(current, max)] + + + def __getCpuAlert(self, current = 0, max = 100): + return self.__getAlert(current, max) + + + def __getCpuColor(self, current = 0, max = 100): + return self.__getColor(current, max) + + + def __getLoadAlert(self, current = 0, core = 1): + # If current < CAREFUL*core of max then alert = OK + # If current > CAREFUL*core of max then alert = CAREFUL + # If current > WARNING*core of max then alert = WARNING + # If current > CRITICAL*core of max then alert = CRITICAL + + if current > limits.getLOADCritical(core): + return 'CRITICAL' + elif current > limits.getLOADWarning(core): + return 'WARNING' + elif current > limits.getLOADCareful(core): + return 'CAREFUL' + + return 'OK' def __getLoadColor(self, current = 0, core = 1): - # core is the number of CPU core - # If current > 0.7*core then color = self.if50pc_color / A_DIM - # If current > 1.0*core then color = self.if70pc_color / A_BOLD - # If current > 5.0*core then color = self.if90pc_color / A_REVERSE - # By default: color = self.default_color / 0 - - if current > (5.0 * core): - if self.hascolors: - return self.if90pc_color - else: - return curses.A_REVERSE - elif current > (1.0 * core): - if self.hascolors: - return self.if70pc_color - else: - return curses.A_BOLD - elif current > (0.7 * core): - if self.hascolors: - return self.if50pc_color - else: - return curses.A_DIM - else: - if self.hascolors: - return self.default_color - else: - return 0 + return self.__colors_list[self.__getLoadAlert(current, core)] + def __getMemAlert(self, current = 0, max = 100): + return self.__getAlert(current, max) + + + def __getMemColor(self, current = 0, max = 100): + return self.__getColor(current, max) + + + def __getNetColor(self, current = 0, max = 100): + return self.__getColor(current, max) + + + def __getFsColor(self, current = 0, max = 100): + return self.__getColor(current, max) + + def __catchKey(self): # Get key self.pressedkey = self.term_window.getch(); @@ -504,6 +665,9 @@ class glancesScreen(): self.showHelp() else: self.hideHelp() + elif (self.pressedkey == 108): + # 'l' > Enable/Disable logs list + self.log_tag = not self.log_tag elif (self.pressedkey == 109): # 'm' > Sort process list by Mem usage self.setProcessSortedBy('proc_size') @@ -530,8 +694,9 @@ class glancesScreen(): self.displayMem(stats.getMem(), stats.getMemSwap()) network_count = self.displayNetwork(stats.getNetwork(), stats.getNetworkInterface()) diskio_count = self.displayDiskIO(stats.getDiskIO(), self.network_y + network_count) - self.displayFs(stats.getFs(), self.network_y + network_count + diskio_count) - self.displayProcess(stats.getProcessCount(), stats.getProcessList(screen.getProcessSortedBy())) + fs_count = self.displayFs(stats.getFs(), self.network_y + network_count + diskio_count) + log_count = self.displayLog(self.network_y + network_count + diskio_count + fs_count) + self.displayProcess(stats.getProcessCount(), stats.getProcessList(screen.getProcessSortedBy()), log_count) self.displayCaption() self.displayNow(stats.getNow()) @@ -580,11 +745,11 @@ class glancesScreen(): helpWindow.resize(self.term_h-self.help_y-2, self.term_w-self.help_x) helpWindow.clear() msg = _("Glances help (press 'h' to hide)") - helpWindow.addnstr(1, 2,_("'h'\tto display|hide this help message"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(2, 2,_("'a'\tto sort processes automatically"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(3, 2, _("'c'\tto sort processes by CPU consumption"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(4, 2, _("'d'\tto disable|enable the disk IO stats"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) - helpWindow.addnstr(5, 2, _("'f'\tto disable|enable the file system stats"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) + helpWindow.addnstr(1, 2, _("'a'\tto sort processes automatically"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) + helpWindow.addnstr(2, 2, _("'c'\tto sort processes by CPU consumption"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) + helpWindow.addnstr(3, 2, _("'d'\tto disable|enable the disk IO stats"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) + helpWindow.addnstr(4, 2, _("'f'\tto disable|enable the file system stats"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) + helpWindow.addnstr(5, 2, _("'l'\tto display|hide the logs messages"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) helpWindow.addnstr(6, 2, _("'m'\tto sort processes by process size"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) helpWindow.addnstr(7, 2, _("'n'\tto disable|enable the network interfaces stats"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) helpWindow.addnstr(8, 2, _("'q'\tto exit Glances"), self.term_w-self.help_x-4, self.help_color if self.hascolors else 0) @@ -643,9 +808,19 @@ class glancesScreen(): self.term_window.addnstr(self.cpu_y+2, self.cpu_x, _("Kernel:"), 8) self.term_window.addnstr(self.cpu_y+3, self.cpu_x, _("Nice:"), 8) self.term_window.addnstr(self.cpu_y+4, self.cpu_x, _("Idle:"), 8) - self.term_window.addnstr(self.cpu_y+1, self.cpu_x+10, "%.1f" % cpu['user'], 8, self.__getColor(cpu['user'])) - self.term_window.addnstr(self.cpu_y+2, self.cpu_x+10, "%.1f" % cpu['kernel'], 8, self.__getColor(cpu['kernel'])) - self.term_window.addnstr(self.cpu_y+3, self.cpu_x+10, "%.1f" % cpu['nice'], 8, self.__getColor(cpu['nice'])) + + alert = self.__getCpuAlert(cpu['user']) + logs.add(alert, "CPU user", cpu['user']) + self.term_window.addnstr(self.cpu_y+1, self.cpu_x+10, "%.1f" % cpu['user'], 8, self.__colors_list[alert]) + + alert = self.__getCpuAlert(cpu['kernel']) + logs.add(alert, "CPU kernel", cpu['kernel']) + self.term_window.addnstr(self.cpu_y+2, self.cpu_x+10, "%.1f" % cpu['kernel'], 8, self.__colors_list[alert]) + + alert = self.__getCpuAlert(cpu['nice']) + logs.add(alert, "CPU nice", cpu['nice']) + self.term_window.addnstr(self.cpu_y+3, self.cpu_x+10, "%.1f" % cpu['nice'], 8, self.__colors_list[alert]) + self.term_window.addnstr(self.cpu_y+4, self.cpu_x+10, "%.1f" % cpu['idle'], 8) @@ -662,9 +837,16 @@ class glancesScreen(): self.term_window.addnstr(self.load_y+1, self.load_x, _("1 min:"), 8) self.term_window.addnstr(self.load_y+2, self.load_x, _("5 mins:"), 8) self.term_window.addnstr(self.load_y+3, self.load_x, _("15 mins:"), 8) + self.term_window.addnstr(self.load_y+1, self.load_x+10, str(load['min1']), 8) - self.term_window.addnstr(self.load_y+2, self.load_x+10, str(load['min5']), 8, self.__getLoadColor(load['min5'], core)) - self.term_window.addnstr(self.load_y+3, self.load_x+10, str(load['min15']), 8, self.__getLoadColor(load['min15'], core)) + + alert = self.__getLoadAlert(load['min5'], core) + logs.add(alert, "LOAD 5-mins", load['min5']) + self.term_window.addnstr(self.load_y+2, self.load_x+10, str(load['min5']), 8, self.__colors_list[alert]) + + alert = self.__getLoadAlert(load['min15'], core) + logs.add(alert, "LOAD 15-mins", load['min15']) + self.term_window.addnstr(self.load_y+3, self.load_x+10, str(load['min15']), 8, self.__colors_list[alert]) def displayMem(self, mem, memswap): @@ -682,14 +864,21 @@ class glancesScreen(): self.term_window.addnstr(self.mem_y+1, self.mem_x, _("Total:"), 8) self.term_window.addnstr(self.mem_y+2, self.mem_x, _("Used:"), 8) self.term_window.addnstr(self.mem_y+3, self.mem_x, _("Free:"), 8) + self.term_window.addnstr(self.mem_y+1, self.mem_x+10, str(mem['total']/1048576), 8) self.term_window.addnstr(self.mem_y+2, self.mem_x+10, str(mem['used']/1048576), 8) self.term_window.addnstr(self.mem_y+3, self.mem_x+10, str(mem['free']/1048576), 8) + + alert = self.__getMemAlert(memswap['used'], memswap['total']) + logs.add(alert, "MEM swap", memswap['used']/1048576) self.term_window.addnstr(self.mem_y+1, self.mem_x+20, str(memswap['total']/1048576), 8) - self.term_window.addnstr(self.mem_y+2, self.mem_x+20, str(memswap['used']/1048576), 8, self.__getColor(memswap['used'], memswap['total'])) + self.term_window.addnstr(self.mem_y+2, self.mem_x+20, str(memswap['used']/1048576), 8, self.__colors_list[alert]) self.term_window.addnstr(self.mem_y+3, self.mem_x+20, str(memswap['free']/1048576), 8) + + alert = self.__getMemAlert(mem['used']-mem['cache'], mem['total']) + logs.add(alert, "MEM real", (mem['used']-mem['cache'])/1048576) self.term_window.addnstr(self.mem_y+1, self.mem_x+30, "-", 8) - self.term_window.addnstr(self.mem_y+2, self.mem_x+30, str((mem['used']-mem['cache'])/1048576), 8, self.__getColor(mem['used']-mem['cache'], mem['total'])) + self.term_window.addnstr(self.mem_y+2, self.mem_x+30, str((mem['used']-mem['cache'])/1048576), 8, self.__colors_list[alert]) self.term_window.addnstr(self.mem_y+3, self.mem_x+30, str((mem['free']+mem['cache'])/1048576), 8) @@ -727,8 +916,8 @@ class glancesScreen(): break elapsed_time = max (1, network[i]['systime']) self.term_window.addnstr(self.network_y+1+i, self.network_x, network[i]['interface_name']+':', 8) - self.term_window.addnstr(self.network_y+1+i, self.network_x+10, self.__autoUnit(network[i]['rx']/elapsed_time*8) + "b", 8, self.__getColor(network[i]['rx']/elapsed_time*8, speed[network[i]['interface_name']])) - self.term_window.addnstr(self.network_y+1+i, self.network_x+20, self.__autoUnit(network[i]['tx']/elapsed_time*8) + "b", 8, self.__getColor(network[i]['tx']/elapsed_time*8, speed[network[i]['interface_name']])) + self.term_window.addnstr(self.network_y+1+i, self.network_x+10, self.__autoUnit(network[i]['rx']/elapsed_time*8) + "b", 8, self.__getNetColor(network[i]['rx']/elapsed_time*8, speed[network[i]['interface_name']])) + self.term_window.addnstr(self.network_y+1+i, self.network_x+20, self.__autoUnit(network[i]['tx']/elapsed_time*8) + "b", 8, self.__getNetColor(network[i]['tx']/elapsed_time*8, speed[network[i]['interface_name']])) ret = ret + 1 return ret return 0 @@ -774,12 +963,47 @@ class glancesScreen(): for mounted in range(0, min(screen_y-self.fs_y-3, len(fs))): self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x, fs[mounted]['mnt_point'], 8) self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+10, self.__autoUnit(fs[mounted]['size']), 8) - self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+20, self.__autoUnit(fs[mounted]['used']), 8, self.__getColor(fs[mounted]['used'], fs[mounted]['size'])) + self.term_window.addnstr(self.fs_y+1+mounted, self.fs_x+20, self.__autoUnit(fs[mounted]['used']), 8, self.__getFsColor(fs[mounted]['used'], fs[mounted]['size'])) return mounted+3 return 0 - def displayProcess(self, processcount, processlist): + def displayLog(self, offset_y = 0): + # Logs + if ((logs.len() == 0) or not self.log_tag): + return 0 + screen_x = self.screen.getmaxyx()[1] + screen_y = self.screen.getmaxyx()[0] + self.log_y = offset_y + if ((screen_y > self.log_y+3) + and (screen_x > self.log_x+79)): + self.log_y = max(offset_y, screen_y-3-min(offset_y-3, screen_y-self.log_y, logs.len())) + logtodisplay_count = min(screen_y-self.log_y-3, logs.len()) + logmsg = _("Warning and Critical logs for CPU|LOAD|MEM") + if (logtodisplay_count > 1): + logmsg += _(" (lasts ") + str(logtodisplay_count) + _(" entries)") + else: + logmsg += _(" (one entry)") + self.term_window.addnstr(self.log_y, self.log_x, logmsg, 79, self.title_color if self.hascolors else curses.A_UNDERLINE) + # Adapt the maximum log to the screen + logcount = 0 + log = logs.get() + for logcount in range(0, logtodisplay_count): + logmsg = " "+str(datetime.datetime.fromtimestamp(log[logcount][0])) + if (log[logcount][1] > 0): + logmark = ' ' + logmsg += " > " +str(datetime.datetime.fromtimestamp(log[logcount][1])) + else: + logmark = '~' + logmsg += " > " +"%19s" % "___________________" + logmsg += " " +log[logcount][3] + " (%.1f/" % log[logcount][6] + "%.1f/" % log[logcount][5] + "%.1f)" % log[logcount][4] + self.term_window.addnstr(self.log_y+1+logcount, self.log_x, logmsg, 79) + self.term_window.addnstr(self.log_y+1+logcount, self.log_x, logmark, 1, self.__colors_list[log[logcount][2]]) + return logcount+3 + return 0 + + + def displayProcess(self, processcount, processlist, log_count = 0): # Process if (not processcount or not processlist): return 0 @@ -821,7 +1045,7 @@ class glancesScreen(): self.term_window.addnstr(self.process_y+3, process_x+10,_("Size MB")+sortchar, 8) self.term_window.addnstr(self.process_y+3, process_x+20,_("Res MB"), 8) self.term_window.addnstr(self.process_y+3, process_x+30,_("Name"), 8) - for processes in range(0, min(screen_y-self.term_h+self.process_y, len(processlist))): + for processes in range(0, min(screen_y-self.term_h+self.process_y-log_count, len(processlist))): self.term_window.addnstr(self.process_y+4+processes, process_x, "%.1f" % processlist[processes]['cpu_percent'], 8, self.__getColor(processlist[processes]['cpu_percent'])) self.term_window.addnstr(self.process_y+4+processes, process_x+10, str((processlist[processes]['proc_size'])/1048576), 8) self.term_window.addnstr(self.process_y+4+processes, process_x+20, str((processlist[processes]['proc_resident'])/1048576), 8) @@ -842,9 +1066,9 @@ class glancesScreen(): if ((screen_y > self.caption_y) and (screen_x > self.caption_x+32)): self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x, _(" OK "), 8, self.default_color) - self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x+8, _("CAREFUL "), 8, self.if50pc_color) - self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x+16, _("WARNING "), 8, self.if70pc_color) - self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x+24, _("CRITICAL"), 8, self.if90pc_color) + self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x+8, _("CAREFUL "), 8, self.ifCAREFUL_color) + self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x+16, _("WARNING "), 8, self.ifWARNING_color) + self.term_window.addnstr(max(self.caption_y, screen_y-1), self.caption_x+24, _("CRITICAL"), 8, self.ifCRITICAL_color) def displayNow(self, now): @@ -880,6 +1104,7 @@ def printSyntax(): print _("'d' to disable or enable the disk IO stats") print _("'f' to disable or enable the file system stats") print _("'h' to hide or show the help message") + print _("'l' to hide or show the logs messages") print _("'m' to sort the processes list by process size") print _("'n' to disable or enable the network interfaces stats") print _("'q' to exit") @@ -887,7 +1112,7 @@ def printSyntax(): def init(): - global stats, screen + global limits, logs, stats, screen global refresh_time refresh_time = 1 @@ -917,6 +1142,12 @@ def init(): # Catch CTRL-C signal.signal(signal.SIGINT, signal_handler) + # Init Limits + limits = glancesLimits() + + # Init Logs + logs = glancesLogs() + # Init stats stats = glancesStats() @@ -925,6 +1156,7 @@ def init(): def main(): + # Init stuff init() @@ -940,6 +1172,7 @@ def main(): def end(): stats.end() screen.end() + sys.exit(0)