Compare commits

...

581 Commits

Author SHA1 Message Date
nicolargo 17266144c8 Merge branch 'issue3400' into develop 2026-01-10 16:23:13 +01:00
nicolargo 7da3111b76 Process widget is broken in Homepage since a few dev release #3400 2026-01-10 16:23:01 +01:00
nicolargo 131d0aec8d Merge branch 'issue3127' into develop 2026-01-04 18:58:34 +01:00
nicolargo 3f060c684b Improve CPU name in Quicklook plugin for ARM prcessors #3127 2026-01-04 18:58:21 +01:00
nicolargo 37d2fe13d0 Make public IP address retreiving call async 2026-01-03 15:42:31 +01:00
nicolargo 0d8352c9c7 Update README 2026-01-03 15:21:24 +01:00
nicolargo ad9d52b5b0 Remove Netifaces2 from requirements file 2026-01-03 15:15:15 +01:00
nicolargo 22f7be01cb Merge branch 'issue3130' into develop 2026-01-03 15:14:20 +01:00
nicolargo 326c2fef51 Remove Netifaces dependency and use a pure Python to get Default Gateway.
Ip address and Net mask are now retreived thanks to PSutil.
2026-01-03 15:12:53 +01:00
nicolargo a3195463a0 Add system test for NATS exporter 2026-01-03 14:21:26 +01:00
nicolargo 84324615c5 Align conf file between default and the one used in Docker compose 2026-01-03 09:32:34 +01:00
nicolargo fa9bb2cdab Add Ruff formater in pre-hook 2026-01-03 09:25:27 +01:00
nicolargo d97c5cd5b9 Format and lint the code 2026-01-03 09:21:43 +01:00
nicolargo 420305a24a Merge branch 'issue3385' into develop 2026-01-03 09:18:45 +01:00
nicolargo 603f1ee8a4 Option to revert to using arrows to switch sorting? #3385 2026-01-03 09:18:25 +01:00
nicolargo 62e0b784bd Format/Lint code 2026-01-03 09:03:32 +01:00
nicolargo 1e25b4b130 Merge branch 'drake7707-develop' into develop 2026-01-03 09:00:39 +01:00
nicolargo 8b76cd458e Reduced cyclomatic complexity - Each function now does one thing
Better readability - Descriptive function names explain intent
Easier testing - Helper functions can be tested independently
Consistent style - f-strings used throughout
Removed code smells - No more pass statements in conditionals, no assert for validation
2026-01-03 09:00:05 +01:00
nicolargo 5cbbe91e1f Regenerate the WebUI and lint the code 2026-01-03 08:52:19 +01:00
Drakarah 862ac41b58 Merge branch 'develop' of https://github.com/nicolargo/glances into develop 2026-01-03 07:33:53 +00:00
Drakarah c8b2a90f3b Format the smart data in the web ui the same way as curses output 2026-01-03 07:33:09 +00:00
nicolargo 6e844f3c8b Update doc 2026-01-02 18:10:00 +01:00
nicolargo 91279b9144 Lint and format the code 2026-01-02 18:07:28 +01:00
nicolargo 7160cd8ae4 Merge branch 'issue3341-NATS' into develop 2026-01-02 18:00:30 +01:00
nicolargo 4bd36a34a1 Refactor NATS export to use async base class
Replace custom event loop threading implementation with GlancesExportAsyncio
base class. This simplifies the code by delegating async lifecycle management
to the parent class and using standardized _async_init(), _async_exit(), and
_async_export() methods instead of manual thread and event loop handling.
2026-01-02 17:28:52 +01:00
nicolargo 6cedd16753 Add test for NATS in the Makefile 2026-01-02 17:02:49 +01:00
nicolargo 12083f33fc Remove debug message 2026-01-02 09:42:03 +01:00
nicolargo b7c6cce373 First version ok. Log message should be removed. Code should be tested. 2025-12-30 19:04:07 +01:00
nicolargo ba932e72bd Read only container (with docker-compose) #3382 2025-12-30 10:40:36 +01:00
nicolargo 5d9593b393 Lint the code 2025-12-26 18:47:01 +01:00
nicolargo 5886321156 Lint code 2025-12-26 18:27:13 +01:00
nicolargo f72b3e226e Merge branch 'issue3387' into develop 2025-12-26 18:22:31 +01:00
nicolargo b9a89006a2 Sort by time do not work anymore #3387 2025-12-26 18:22:20 +01:00
nicolargo 1353b22445 Revert "Switch Docker Alpine based images to hardened"
This reverts commit f3b3b42e29.
2025-12-23 14:33:55 +01:00
nicolargo f058360b52 Merge branch 'issue3280' into develop 2025-12-23 14:15:16 +01:00
nicolargo f3b3b42e29 Switch Docker Alpine based images to hardened 2025-12-23 14:14:58 +01:00
nicolargo be199ef044 Merge branch 'issue3359_Improve_DockerFile' into develop 2025-12-22 10:58:30 +01:00
nicolargo 15d252a40b Some imporovement regarding Dockerfile - #3359 2025-12-22 10:58:09 +01:00
Drakarah 0b141ff92d update doc 2025-12-22 09:42:32 +00:00
Drakarah 60f304c313 auto unit on curses output 2025-12-22 09:38:14 +00:00
Drakarah 29d7a15dab Add a hide_attributes to well hide attributes 2025-12-22 09:17:20 +00:00
Drakarah f837614a69 Use proper labels instead of attribute keys (if possible) 2025-12-22 07:43:52 +00:00
Drakarah 3247b0dc33 Change proposed by @webdjoe (https://github.com/nicolargo/glances/pull/3355?notification_referrer_id=NT_kwDOAFGpVLMyMDY1Mjk5NjMyMTo1MzUxNzY0#issuecomment-3679451101) 2025-12-22 07:30:12 +00:00
Drakarah 507079491a Merge branch 'develop' of https://github.com/nicolargo/glances into develop 2025-12-22 07:27:41 +00:00
nicolargo d5ce8a7461 Lint code for Python 3.10 2025-12-21 14:40:39 +01:00
nicolargo f587b281c0 Catch error on ADM GPU plugin and Python 3.9 2025-12-20 15:33:17 +01:00
nicolargo e74dcc0060 Add comment for Git action tests 2025-12-20 15:19:58 +01:00
nicolargo 59d54b8e2b Correct issue with test on Python 3.9 2025-12-20 10:36:00 +01:00
nicolargo 184e490660 Bump to Python 3.10 as minimal version 2025-12-20 10:22:03 +01:00
nicolargo cd1f9348c2 Change load method name 2025-12-20 10:08:26 +01:00
nicolargo 68b5215e94 Lint quicklook 2025-12-20 10:01:01 +01:00
nicolargo 928e577d8c Update webUI 2025-12-20 09:53:29 +01:00
nicolargo 1b6d63fd2f Update deps 2025-12-20 09:44:49 +01:00
nicolargo fafdef615d Lint the code and correct issue with List (replaced by list) 2025-12-20 09:40:09 +01:00
nicolargo 8ab188bc04 Lint code 2025-12-20 09:33:01 +01:00
Nicolas Hennion cd9360f637
Merge pull request #3379 from okulev/cpu_freq_nomax
Fix quicklook in case psutil.cpu_freq().max=0.0
2025-12-20 09:22:22 +01:00
Nicolas Hennion be5a92c7e6
Merge pull request #3381 from ruben-arts/patch-1
Update license specification to SPDX format
2025-12-20 09:20:22 +01:00
Nicolas Hennion 9907e7242f
Merge pull request #3383 from kelvinvelasquez-SDE/fix/security-bandit
fix(security): resolve B701 (Jinja2) and B113 (timeout) vulnerabilities
2025-12-20 09:19:09 +01:00
Kelvin Velasquez 9b5cc01b06 fix(security): resolve B701 and B113 bandit issues (#3353)
Detailed changes:
- Enable autoescape=True for Jinja2 environment to prevent XSS (B701).
- Add timeout=15s to requests.get in nginx amp (B113).
- Add timeout=15s to post in restful export (B113).
2025-12-19 13:23:51 -06:00
Ruben Arts 89753198b3
Remove LGPL-3.0 license from pyproject.toml
License classifiers have been superseded by license expressions (see https://peps.python.org/pep-0639/).
2025-12-19 10:17:28 +01:00
Ruben Arts 8408a35100
Update license specification to SPDX format
Using the format specified in: https://spdx.org/licenses/
2025-12-19 10:04:07 +01:00
Ognyan Kulev bd246fe966 Fix quicklook in case psutil.cpu_freq().max=0.0 2025-12-16 20:57:46 +02:00
Nicolas Hennion 7b22761c75
Merge pull request #3377 from okulev/amdgpu_gtt
Include GTT to mem% for integrated AMD GPUs
2025-12-15 09:05:57 +01:00
Drakarah 9d4bc76304 Don't crash if any attribute fails to serialize 2025-12-15 08:03:08 +00:00
Drakarah 74771d0949 Merge branch 'develop' of https://github.com/nicolargo/glances into develop 2025-12-15 07:57:47 +00:00
Ognyan Kulev 2a508d7641 Use glob.glob also for device list 2025-12-14 23:57:41 +02:00
Ognyan Kulev 41db9c86f9 Include GTT to mem% for integrated AMD GPUs 2025-12-14 22:39:02 +02:00
nicolargo eb21408b72 Lint the code 2025-12-14 17:27:53 +01:00
Nicolas Hennion 4f48d33709
Merge pull request #3376 from okulev/amdgpu_ids
Get amdgpu name from `amdgpu.ids`
2025-12-14 17:24:33 +01:00
Ognyan Kulev 2800e81cb0 Add pci id files to tests-data 2025-12-14 14:19:18 +02:00
Ognyan Kulev bc629d12b2 Remove 'AMD' prefix and 'Graphics' suffix. 2025-12-14 12:44:30 +02:00
Ognyan Kulev ec11dff2cd Get amdgpu name from amdgpu.ids 2025-12-13 21:33:22 +02:00
nicolargo 71b0d4b606 Dependencies update 2025-12-06 17:55:24 +01:00
Drakarah d009335ba0 Merge branch 'develop' of https://github.com/nicolargo/glances into develop 2025-12-02 07:51:49 +00:00
nicolargo f84baaa8df Update requirements 2025-11-29 09:50:26 +01:00
Drakarah bbb733d6cb Fix output for curses 2025-11-23 10:47:10 +00:00
Drakarah 73f45dd9a8 remove old comment 2025-11-23 10:17:53 +00:00
Drakarah 394f94fb27 use pySMART instead of the old outdated pySMART.smartx 2025-11-23 10:15:53 +00:00
Drakarah 2146673841 Merge branch 'develop' of https://github.com/nicolargo/glances into develop 2025-11-23 10:09:11 +00:00
Drakarah fcc5061d8c smart nvme support 2025-11-23 10:05:55 +00:00
nicolargo f2641c80e2 Glances won't start when using snmp discovery with parameter -c #3354 2025-11-23 10:06:08 +01:00
nicolargo b9c451ce7a Merge branch 'issue3342-precommit_hooks' into develop 2025-11-22 19:08:00 +01:00
nicolargo ca2d729211 Add bandit in Makefile 2025-11-22 19:07:45 +01:00
nicolargo 50818213b1 What a sexy pre-commit config file ! 2025-11-22 18:48:13 +01:00
nicolargo 868aa0f4b1 Merge branch 'issue3352-fetch_client_server' into develop 2025-11-22 16:38:21 +01:00
nicolargo f7ab7efdd4 Fetch option is not compliant with client/server mode #3352 2025-11-22 16:37:03 +01:00
nicolargo 9797fc795e Correct lint issue 2025-11-21 18:36:50 +01:00
nicolargo fc2e2c71af Merge branch 'issue3350-jupiter-notebook' into develop 2025-11-21 18:32:36 +01:00
nicolargo 45bdcc92c7 Add a first and simple Jupyter notebook 2025-11-21 18:32:23 +01:00
nicolargo 1cf7683c1d Update Glances API documentation 2025-11-21 18:03:36 +01:00
nicolargo 1b3ee3e035 Update zero 2025-11-21 17:57:05 +01:00
nicolargo 3de1a25305 Revert some change on hide zero feature (need additional tests 2025-11-16 11:32:21 +01:00
nicolargo 1567c2db49 Update docs for PR #3346 2025-11-16 10:54:39 +01:00
nicolargo 1171d0b4d3 Merge branch 'ciherrera20-issue3345' into develop 2025-11-16 10:52:11 +01:00
nicolargo df684c64c2 Remove unuse parameter in split_esc 2025-11-16 10:51:49 +01:00
Chris Herrera c70321c3fb Formatting changes from running make format 2025-11-16 10:43:46 +01:00
Chris Herrera 93120b9779 Use split_esc in read_alias
Allows for ':' to be used in sensor names or alias names
2025-11-16 10:43:46 +01:00
Chris Herrera 89c8fc125c Add unit test for split_esc 2025-11-16 10:43:46 +01:00
Chris Herrera 11aac6ca4d Add split_esc function globals
Extends the functionality of str.split by adding an escape character. This allows ignoring separators preceded by the escape character.
2025-11-16 10:43:46 +01:00
nicolargo e7bd8d4768 Correct issue with hide on diskio 2025-11-16 10:41:35 +01:00
nicolargo e660520eb2 Correct typo in Makefile 2025-11-16 10:03:19 +01:00
nicolargo 0ae24bade0 Correct an issue with Wifi Alert following implementation of action for all plugins 2025-11-16 10:01:48 +01:00
nicolargo 728b36a8c9 Correct an issue with FS Alert following implementation of action for all plugins 2025-11-16 09:55:32 +01:00
nicolargo e2a1606479 Remove test from config file 2025-11-16 09:41:54 +01:00
Nicolas Hennion 8c9822ed26
Integrate ruff pre-commit hooks
Added ruff pre-commit hooks for linting and formatting.
2025-11-13 14:20:57 +01:00
Nicolas Hennion c4f6c04eb4
Refactor export options in README
Consolidate export options into categories for clarity.
2025-11-12 15:14:30 +01:00
nicolargo 7c13d53cb8 Make WebUI and Restful API SSL compliant #3340 2025-11-11 18:35:15 +01:00
nicolargo ccb00ec252 Make WebUI and Restful API SSL compliant #3340 2025-11-11 18:34:51 +01:00
nicolargo ab3b386e10 Improve Docker build pipeline#3336 2025-11-11 17:45:31 +01:00
nicolargo 5cbd9e030a Improve Docker build pipeline #3336 2025-11-11 17:45:16 +01:00
nicolargo c89effd602 Add a "focus" option for the processlist plugin #3293 2025-11-11 10:40:23 +01:00
nicolargo ab68fcbe28 Add a focus option for the processlist plugin #3293 2025-11-11 10:40:06 +01:00
nicolargo df4b312bec Update requirements files 2025-11-10 11:58:41 +01:00
nicolargo 25da171479 Add some fetch templates as an example 2025-11-10 11:45:33 +01:00
nicolargo 34c3c11725 Make unit test with Python 3.13 on Windows 2025-11-10 11:16:07 +01:00
nicolargo de0b1ef2da Make unit test with Python 3.14 on Windows and MacOS 2025-11-10 11:08:37 +01:00
nicolargo c0dad78189 Unittest now take into account the default glances.conf file 2025-11-10 10:53:52 +01:00
nicolargo d9abfca7fb Remove test because it breaks the CI test 2025-11-10 10:27:13 +01:00
nicolargo cb377055f2 Remove test because it breaks the CI test 2025-11-10 10:26:22 +01:00
nicolargo b2e3ea283d Remove test because it breaks the CI test 2025-11-10 07:48:03 +01:00
nicolargo a74b1867d7 Merge branch 'issue3324-log_and_alert_for_all' into develop 2025-11-09 17:28:42 +01:00
nicolargo afff3e2ba6 Add a simple unit test for get_alert 2025-11-09 17:27:00 +01:00
nicolargo fb17066692 Update docs 2025-11-09 13:05:07 +01:00
nicolargo e5e476d255 Ok for containers and folders 2025-11-09 13:02:59 +01:00
nicolargo ea10748e07 Fs is OK 2025-11-09 12:58:12 +01:00
nicolargo 7c19d01c21 DiskIO is OK 2025-11-09 12:54:29 +01:00
nicolargo 8bb2a8a91b Network IO is OK 2025-11-09 12:49:44 +01:00
nicolargo b75d1f24c3 Correct an issue with events type name (unattended _ in the name) 2025-11-08 19:34:31 +01:00
nicolargo 5efa3a01a3 Update docs 2025-11-08 19:22:49 +01:00
nicolargo e11cb80492 First issue corrected. It is now possible to generate alarm for plugins returning a list (tested only with sensors). To be done for all plugins (add header adn actions_keys) 2025-11-08 18:40:20 +01:00
nicolargo e2d37ee482 Change the way uv is installed #3338 2025-11-08 16:18:22 +01:00
nicolargo 930745b5f3 Package generation issue on Pypi because of README.rst #3337 2025-11-08 09:38:01 +01:00
nicolargo ba1b898a3e Add info in F.A.Q about free memory 2025-11-08 09:25:26 +01:00
nicolargo 9de127cce9 Update documentation for API 2025-11-08 09:16:16 +01:00
nicolargo 55ff04437f Update documentation for API 2025-11-08 08:58:19 +01:00
nicolargo ba6be768d0 Update documentation 2025-11-08 08:56:43 +01:00
nicolargo 4e669d0bc7 Merge branch 'develop' of github.com:nicolargo/glances into develop 2025-11-08 08:31:40 +01:00
Nicolas Hennion 3e7a49eb3b
Fix badge formatting in README.rst 2025-11-05 10:05:47 +01:00
nicolargo f1bd18a3a5 Update requirements file and add some tests 2025-11-04 21:41:15 +01:00
Nicolas Hennion ef5ef9eb8a
Replace logo and update README formatting
Updated logo image and modified title formatting.
2025-11-04 12:45:50 +01:00
Nicolas Hennion 89a10d9633
Enhance README with HTML and logo alignment
Added HTML formatting for logo and summary section.
2025-11-04 12:43:23 +01:00
Nicolas Hennion 6692e00a0e
Update README to include sponsors badge 2025-11-04 10:49:55 +01:00
Nicolas Hennion a1edfc735a
Fix twitter badge syntax in README.rst 2025-11-04 10:49:12 +01:00
Nicolas Hennion 5101512320
Update Twitter badge in README 2025-11-04 10:46:59 +01:00
Nicolas Hennion 244f13634f
Fix Reddit badge formatting in README
Updated badge formatting for Reddit in README.
2025-11-04 10:45:49 +01:00
Nicolas Hennion 7e3116a4e1
Update README with new social media badges
Added Reddit badge to README and updated Twitter badge.
2025-11-04 10:41:46 +01:00
nicolargo 579049cec5 Generate Snap for Glances 4.4.2_dev1 2025-11-03 15:32:11 +01:00
nicolargo 3646e011cf Generate Snap for Glances 4.4.1 2025-11-03 15:02:24 +01:00
nicolargo ff5e33d856 version 4.4.1 2025-11-03 14:47:16 +01:00
nicolargo de6fae584e Merge branch 'support/v4.4.x' 2025-11-03 14:44:57 +01:00
nicolargo 1c6a9e1412 Restful API issue after a while (stats are no more updated) #3333 2025-11-03 14:44:11 +01:00
nicolargo a7ba827cc5 On the road of Glances 4.4.1 2025-11-02 17:22:06 +01:00
nicolargo c523d20648 Merge branch 'master' into develop 2025-11-02 17:19:04 +01:00
nicolargo ef36eb9ab6 version 4.4.0 2025-11-02 17:07:24 +01:00
nicolargo f38ff27faa Merge branch 'develop' 2025-11-02 17:02:14 +01:00
nicolargo f87783689b Docs and requirements for Glances 4.4.0 2025-11-02 17:01:32 +01:00
nicolargo 28098f1555 Snap rplace architectures per platforms 2025-11-01 17:04:00 +01:00
nicolargo 0cec111764 Snap rplace architectures per platforms 2025-11-01 16:56:23 +01:00
nicolargo 54068209b3 Add limitation to TimescaleDB export for diskio, not working for the moment 2025-11-01 16:43:56 +01:00
nicolargo bdd66c56d3 Correct an issue when containers ports as no destination 2025-11-01 15:54:30 +01:00
nicolargo 10111cc7f6 Add check in pre-commit 2025-11-01 15:32:54 +01:00
nicolargo 02752f90ea Remove duplicate line at the end of file and update doc for Glances 4.4.0RC1 2025-11-01 15:29:34 +01:00
nicolargo d9d4507bd4 Merge branch 'issue2054' into develop 2025-11-01 10:27:50 +01:00
nicolargo 133f97efd8 Show used port in container section #2054 2025-11-01 10:27:34 +01:00
nicolargo c9fc7d4bb4 Resolve conflict 2025-11-01 09:50:47 +01:00
nicolargo 7f34578cea Show used port in container section - First try, ok for one port... #2054 2025-11-01 09:49:49 +01:00
nicolargo 7b827954e0 Update dep 2025-11-01 09:31:52 +01:00
nicolargo 3a3ae4eddb Solve issue with multiprocessing exception with Snap package 2025-10-31 19:15:06 +01:00
nicolargo aef78c3a10 Use develop branch for snap 2025-10-31 17:58:45 +01:00
nicolargo db13c8380b Update docs and add some system test for CPU consumption 2025-10-31 17:43:44 +01:00
nicolargo 7f2f69985c Update .gitignore 2025-10-24 19:24:03 +02:00
nicolargo 6361217258 Correct issue with 2 digit instead of one in Glances UI MEM 2025-10-24 19:22:57 +02:00
nicolargo 7edbab35d8 Highlight zero for proces CPU and RAM 2025-10-24 18:54:24 +02:00
nicolargo 067eb918ad Sensors plugin refresh by default every 10 seconds 2025-10-24 18:35:30 +02:00
nicolargo c8ed977c9d Add a test script for identify CPU consumption of sensor plugin 2025-10-23 11:47:54 +02:00
nicolargo 1ac760c55b Refactor port to take into account netifaces2 2025-10-23 11:43:22 +02:00
nicolargo c2e038d244 Optimize LRU caching for API requests with plugins list (ex: Homepage plugin) 2025-10-23 11:30:03 +02:00
nicolargo 02fe3aa12e Merge branch 'issue1553' into develop 2025-10-23 10:33:12 +02:00
nicolargo f3a76caa64 Show long command line with arrow key #1553 2025-10-23 10:33:01 +02:00
Nicolas Hennion 729532713f Quick and dirty patch for discussion - Last dev build broke Homepage API calls ? only 1 widget still working #3322 2025-10-20 13:21:18 +00:00
nicolargo 2b1d6f4309 Update memory profiling file 2025-10-19 17:47:16 +02:00
nicolargo 6dd33e3317 Correct issue with Chrome driver in WebUI unit test 2025-10-19 17:28:00 +02:00
nicolargo 1114a9ab87 Merge branch 'issue3033' into develop 2025-10-19 17:19:53 +02:00
nicolargo 4361df8f84 Do not call update if a call is done to a specific plugin through the API #3033 2025-10-19 17:19:34 +02:00
nicolargo 1bc8df0a13 Patch api to make fetch compliant with Windows 2025-10-18 16:12:02 +02:00
nicolargo 1b93758b65 [UI] Process virtual memory display can be disable by configuration #3299 2025-10-18 16:03:23 +02:00
nicolargo 8fa0677538 Update .pre-commit-config.yaml file #3300 2025-10-18 14:25:33 +02:00
nicolargo 074a01efc2 Update .pre-commit-config.yaml file #3300 2025-10-18 14:17:37 +02:00
nicolargo 227dedb41b Try to use Python 3.14 in the CI 2025-10-18 14:00:18 +02:00
nicolargo bcd48844b5 Support for Python 14 #3319 2025-10-18 13:51:15 +02:00
nicolargo f01008f430 Support for Python 14 #3319 2025-10-18 12:03:18 +02:00
nicolargo a41a67edf7 Cloud plugin always generate communication with 169.254.169.254, even if the plugin is disabled #3316 2025-10-18 10:52:18 +02:00
nicolargo 6fc887839e Cloud plugin always generate communication with 169.254.169.254, even if the plugin is disabled #3316 2025-10-18 09:38:17 +02:00
nicolargo 8c70a55488 API response delay (3+ minutes) when VMs are running due to 'virsh domstats' – Suggest adding '--nowait' option #3317 2025-10-18 09:30:55 +02:00
nicolargo 8a748d15e3 Upgrade export test with InfluxDB 1.12 2025-10-18 09:25:39 +02:00
nicolargo bcfb3675a6 Update dep 2025-10-18 09:06:24 +02:00
nicolargo e3b016e6f3 Update dep and change Fetch screenshot for documentation 2025-10-18 08:59:21 +02:00
Nicolas Hennion a64474f43f
Merge pull request #3314 from syohex/typo-option-document
Fix typo of --export-process-filter help message
2025-10-14 07:48:07 +02:00
Shohei YOSHIDA c2b59d0a74
Fix typo of --export-process-filter help message 2025-10-14 12:08:21 +09:00
Nicolas Hennion d6a820d2bb Remove unused lib 2025-10-13 11:54:33 +00:00
Nicolas Hennion ee50f4e8e2 In the outdated feature, catch error message is Pypi server not reachable 2025-10-13 11:49:19 +00:00
Nicolas Hennion eead34b0d0 Glances do not display CPU stat correctly #3155 2025-10-13 11:43:32 +00:00
Nicolas Hennion 29e3947577 Revert "[WINDOWS] Glances do not display CPU stat correctly #3155"
This reverts commit bb0116a198.
2025-10-13 11:38:17 +00:00
Nicolas Hennion bb0116a198 [WINDOWS] Glances do not display CPU stat correctly #3155 2025-10-13 11:11:53 +00:00
Nicolas Hennion 349c33fd27 Refactor Fetch template 2025-10-13 09:28:21 +00:00
Nicolas Hennion 52c5042e37 Add --no-emit-workspace in the Makefile 2025-10-13 09:25:31 +00:00
Nicolas Hennion 853b71998c Add --no-emit-workspace to the requirements exports tasks 2025-10-13 09:22:32 +00:00
nicolargo 03d4d6cf65 Add TOML conf file to Docker in order to build 2025-10-12 18:33:24 +02:00
nicolargo d02aed76a6 Remove hashes from requirements file because it causes some issue in Gitlab pipeline (with pip) 2025-10-12 17:24:04 +02:00
nicolargo 97ec09dfc3 Correct an issue when Netifaces is not installed on the system 2025-10-12 17:05:19 +02:00
nicolargo 0c2b9e5e8e Requirements files are now generated dynamically with the make requirements or requirements-upgrade target 2025-10-12 17:00:46 +02:00
nicolargo 0134487f5e Merge branch 'issue3025-uv' into develop 2025-10-12 15:20:34 +02:00
nicolargo f1328a84e9 Use uv instead of venv in Makefile #3304 2025-10-12 15:19:32 +02:00
Nicolas Hennion 547d98599a
Update README.rst 2025-10-10 11:59:59 +02:00
Nicolas Hennion 031d5d6485
Update README.rst 2025-10-08 18:09:40 +02:00
Nicolas Hennion 62c6c9df4e
Update README.rst
Add Brew as a standard installation method.
2025-10-07 09:47:39 +02:00
Nicolas Hennion 6302389073
Update find-duplicate-lines.sh
Make the code return an error code if a file ended with a duplicated line is founded.
2025-10-06 14:14:35 +02:00
Nicolas Hennion 83d5886a35
Update __init__.py
Remove dupliate line
2025-10-06 14:06:19 +02:00
nicolargo 350ed125d3 Store nothing is history is not enable. 2025-10-05 17:08:27 +02:00
nicolargo 7382089129 Disable history when history size is set to 0 by configuration. 2025-10-05 17:04:27 +02:00
nicolargo 2254f899b6 Add unit test for Prometheus exporter aand refactor the code to not use the API (not useful in this case because stats instance already exist) 2025-10-04 09:42:00 +02:00
nicolargo d03295a977 Merge branch 'develop' of github.com:nicolargo/glances into develop 2025-09-28 17:03:55 +02:00
nicolargo 9f1eb9496a Add unit test for auto_unit 2025-09-28 17:03:44 +02:00
Nicolas Hennion d28f3056a7
Merge pull request #3287 from nicolargo/renovate/macos-15.x
chore(deps): update dependency macos to v15
2025-09-28 16:40:10 +02:00
Nicolas Hennion e2c65c6687
Merge pull request #3278 from nicolargo/renovate/actions-stale-10.x
chore(deps): update actions/stale action to v10
2025-09-28 16:39:30 +02:00
Nicolas Hennion e721b66488
Merge pull request #3277 from nicolargo/renovate/actions-setup-python-6.x
chore(deps): update actions/setup-python action to v6
2025-09-28 16:39:25 +02:00
Nicolas Hennion 26a0c3ef87
Merge pull request #3276 from nicolargo/renovate/actions-setup-node-5.x
chore(deps): update actions/setup-node action to v5
2025-09-28 16:39:19 +02:00
nicolargo 0bf79775b6 Remove duplicate line at the end of files 2025-09-28 16:28:05 +02:00
nicolargo c970932e96 Set default timeout to 2 second 2025-09-28 16:10:07 +02:00
nicolargo 38c7bfccba Make the issue3290 compliant with non Linux OS 2025-09-28 15:09:19 +02:00
nicolargo 48674ee71f Clean unused decorator 2025-09-28 15:00:37 +02:00
Nicolas Hennion 6539979498
Merge pull request #3291 from nicolargo/issue3290-cleanup
Fix: Glances stalling on broken NFS connections
2025-09-28 14:17:24 +02:00
Nicolas Hennion 3652dc339f chg: plugin(fs) - use timeout wrappers for psutil calls
Why? Broken NFS connections can stall psutil calls indefinitely

Co-authored-by: Raz Crimson <52282402+razcrimson@users.noreply.github.com>
2025-09-28 05:55:56 +05:30
Nicolas Hennion da700c8cfc add: generic timeout wrappers
For use with blocking functions and utilities

Co-authored-by: Raz Crimson <52282402+razcrimson@users.noreply.github.com>
2025-09-28 05:55:34 +05:30
nicolargo 420cf2bdde Lint code 2025-09-27 16:02:48 +02:00
Nicolas Hennion ce27d795db
Merge pull request #3283 from DeepSpace2/fix-prometheus-export-format
Fix prometheus export format
2025-09-27 15:58:41 +02:00
Adi f53dbe748e getting object key dynamically instead of maintaining an hardcoded mapping 2025-09-27 12:34:40 +03:00
nicolargo 3931457495 Merge branch 'issue3288-mem-available' into develop 2025-09-25 22:20:38 +02:00
nicolargo d0137830c2 Choose between used or available in the mem plugin #3288 2025-09-25 22:20:23 +02:00
Nicolas Hennion 48296a5de9
Merge pull request #3289 from bkanuka/issue3979-zfs-cache
Issue #3279 zfs cache and memory math issues
2025-09-25 20:48:49 +02:00
Bennett Kanuka 6339393271 ruff format 2025-09-25 14:03:27 -04:00
Bennett Kanuka 2b59ad71fe fix zfs cache math 2025-09-25 13:49:08 -04:00
nicolargo ff6dcd5cb0 Update documentation 2025-09-25 13:49:06 -04:00
nicolargo b26dac4dd5 ZFS ARC/cache not reported as cache memory usage #3279 2025-09-25 13:48:43 -04:00
renovate[bot] b6db99cbb8
Update dependency macos to v15 2025-09-25 16:35:06 +00:00
renovate[bot] 86e198310d
Update actions/stale action to v10 2025-09-25 16:35:04 +00:00
renovate[bot] 55c3b9c0ab
Update actions/setup-python action to v6 2025-09-25 16:35:01 +00:00
renovate[bot] 9eaef1da36
Update actions/setup-node action to v5 2025-09-25 16:34:58 +00:00
Nicolas Hennion de9d46297c Merge pull request #3286 from jmwallach/patch-1
Label error in docs
2025-09-25 13:59:02 +02:00
jmwallach 36c9658912
Update network.rst
Fix labeling error
2025-09-24 16:40:09 -07:00
Adi afdd09d648 fix bug in construction of metric name 2025-09-23 22:27:33 +03:00
Adi c8e67d07c1 add missing metrics to labels mappings 2025-09-23 18:00:29 +03:00
Adi 63f0aacb7c add missing metrics to labels mappings 2025-09-23 13:19:11 +03:00
Adi 2c1abd666e spliting labels on the first ':' only as a label value might contain ':' 2025-09-23 12:51:54 +03:00
Adi 9834e204c4 fix nonetype is not iterable error 2025-09-23 12:50:37 +03:00
Adi 39c1b9e13c using object as label 2025-09-23 12:47:01 +03:00
nicolargo 6631b37d67 Revert "First version done. Need to be tested and documented."
This reverts commit 17eca1f6e6.
2025-09-21 16:44:28 +02:00
nicolargo 95620bf584 Merge branch 'issue3205-duckdb' into develop 2025-09-21 15:13:03 +02:00
nicolargo fcdd792722 Update docs for DuckDB export 2025-09-21 15:09:28 +02:00
nicolargo 3de48a4f56 First version done. Need to be tested and documented. 2025-09-21 15:08:25 +02:00
nicolargo 17eca1f6e6 First version done. Need to be tested and documented. 2025-09-21 15:08:25 +02:00
nicolargo 3db5f13d8c Correct duplicate line in plugin model 2025-09-21 15:00:59 +02:00
nicolargo 11bab0fb5e Udate readme file 2025-09-21 14:41:34 +02:00
nicolargo ef45c6def2 Merge branch 'issue3281-fetch' into develop 2025-09-21 14:37:35 +02:00
nicolargo 32e17004b6 Update readme file 2025-09-21 14:33:00 +02:00
nicolargo be29f069d6 First release ok, need to add some tests 2025-09-21 10:03:33 +02:00
nicolargo d5d38af356 First version of the (neo)fetch feature for OS 2025-09-20 19:38:33 +02:00
nicolargo 0abc39f7df First rush to make the top menu configurable 2025-09-16 19:05:38 +02:00
nicolargo 8f240afec1 Put WebUI conf generator in a dedicated script 2025-09-14 15:30:55 +02:00
nicolargo 8c8120fb90 Put WebUI conf generator in a dedicated script 2025-09-14 15:30:45 +02:00
nicolargo 54216c6580 Rafactor the Makefile to generate WebUI config file for all webui targets 2025-09-14 14:59:51 +02:00
nicolargo 605f3a9da3 Correct typo in comment 2025-09-14 14:55:49 +02:00
nicolargo 3ad154b4f8 Merge branch 'issue3100' into develop 2025-09-14 14:44:03 +02:00
nicolargo af082ee2e0 Glances not showing Processes on MacOS #3100 2025-09-14 14:43:42 +02:00
Nicolas Hennion ba68e3d8f4
Merge pull request #3275 from timrettop/sensors-docs-alias
Update sensors.rst
2025-08-30 08:49:12 +02:00
Nicolas Hennion 48641b5d1a
Merge pull request #3273 from lawrence-c/patch-1
Update docker compose env quote
2025-08-30 08:48:02 +02:00
Tim Potter fa82d8ed1c
Merge branch 'nicolargo:develop' into sensors-docs-alias 2025-08-28 13:46:57 -05:00
Tim Potter 81a16075bd
Update sensors.rst
Add alias example
2025-08-28 13:43:11 -05:00
Lawrence 05080765cc
Update docker env quote 2025-08-27 21:06:10 +01:00
nicolargo 580af978f4 [MACOS] Glances crashes when I try to filter #3266 2025-08-24 11:40:05 +02:00
nicolargo d99d3684c4 [MACOS] Glances crashes when I try to filter #3266 2025-08-23 18:24:33 +02:00
nicolargo 26a41d0c21 Display hash for Pypi release 2025-08-23 14:55:54 +02:00
nicolargo 435b250a84 Glances 4.4.0-dev2 2025-08-23 14:34:22 +02:00
nicolargo a31b60b1ab Glances crashes when I try to filter #3266 2025-08-23 10:06:23 +02:00
nicolargo cb8003c1df Update API doc generation 2025-08-23 09:52:02 +02:00
nicolargo 973dd8978e Update API docs 2025-08-17 18:34:31 +02:00
nicolargo 06c6f8408e Update README with nice icons #3236 2025-08-17 17:04:13 +02:00
nicolargo 948bc90b58 Merge branch 'develop' of github.com:nicolargo/glances into develop 2025-08-17 16:55:30 +02:00
nicolargo a7683caa71 Update README with nice icons #3236 2025-08-17 16:55:19 +02:00
Nicolas Hennion 3f5c1f478a
Merge pull request #3261 from nicolargo/renovate/major-github-artifact-actions
Update actions/download-artifact action to v5
2025-08-17 16:43:52 +02:00
Nicolas Hennion f65ae16ea2
Merge pull request #3263 from nicolargo/renovate/actions-checkout-5.x
Update actions/checkout action to v5
2025-08-17 16:43:27 +02:00
nicolargo e88f33ca05 Clear-text logging of sensitive information 2025-08-16 10:20:59 +02:00
nicolargo 2a3046796b Glances hang when killing process with muliple CTRL-C #3264 2025-08-15 18:53:03 +02:00
nicolargo 89e9ad1284 Merge branch 'issue1070' into develop 2025-08-13 21:36:09 +02:00
renovate[bot] dbba6e0673
Update actions/checkout action to v5 2025-08-11 18:55:38 +00:00
nicolargo 1daac0e6d0 Add Disk I/O Latency stats #1070 2025-08-10 19:08:02 +02:00
renovate[bot] f173c8d6b4
Update actions/download-artifact action to v5 2025-08-10 13:45:47 +00:00
nicolargo 5f3e1de01e Merge branch 'issue3248' into develop 2025-08-10 15:37:58 +02:00
nicolargo 9a58715a2f Issues after disabling system and processcount plugins #3248 2025-08-10 15:37:46 +02:00
nicolargo afaab7190b Merge branch 'issue3250' into develop 2025-08-10 15:00:42 +02:00
nicolargo c63f2404aa Headers missing from predefined fields in TUI browser machine list - Part 1 #3250 2025-08-03 16:53:51 +02:00
nicolargo ae31d29c75 Remove test in glances.conf 2025-08-01 17:54:51 +02:00
nicolargo 04fee963cc Merge branch 'issue3528' into develop 2025-08-01 17:20:08 +02:00
nicolargo f1ae3bb535 Filter fields to export #3258 2025-08-01 17:19:43 +02:00
nicolargo f5ec34cdf9 Add docuùentation for WebUI test 2025-07-28 17:52:26 +02:00
nicolargo 11890f31b1 Update docs 2025-07-27 16:18:30 +02:00
nicolargo c1c0d4038f Add another check for the famous Netifaces issue - Related to #3219 2025-07-27 15:57:05 +02:00
nicolargo 2cf92f93a9 Add another check for the famous Netifaces issue - Related to #3219 2025-07-27 15:41:15 +02:00
nicolargo 89118e53de Key error 'type' in server_list_static.py (load_server_list) #3247 2025-07-26 17:08:03 +02:00
Nicolas Hennion 1e8ea943ba
Merge pull request #3249 from korn3r/patch-1
Update docker-compose.yml
2025-07-26 16:41:24 +02:00
Nicolas Hennion 6ffcaf4a94
Merge pull request #3253 from Boris-Okassa/Dictkeys
remove `.keys()` from loops over dicts
2025-07-26 16:40:41 +02:00
Nicolas Hennion cba28407cb
Merge pull request #3252 from ariel-anieli/fix-python2-iterators
Remove iterator helpers
2025-07-26 16:39:08 +02:00
Boris Okassa 463d0af666 remove .keys() from loops over dicts
By default dict loops use keys.

Signed-off-by: Boris Okassa <borisokassa@yahoo.fr>
2025-07-23 23:16:20 +02:00
Ariel Otilibili 3eb09d7fc5
Remove iterator helpers
Related to #3181.

Co-authored-by: Jatin Singh <fithacker97@gmail.com>
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-22 23:46:39 +02:00
korn3r 202289599d
Update docker-compose.yml
added commented out by default mount point for /etc/os-release. without it Glances container shows distibution information from container itself and not host system.
2025-07-21 02:05:09 +03:00
nicolargo 3a1e188d58 Merge branch 'issue3237_GlancesAPI' into develop 2025-07-12 18:17:21 +02:00
nicolargo 3d263bc7d4 Create a Glances API in order to use Glances as a Python lib #3237 2025-07-12 18:16:43 +02:00
nicolargo 365a670c12 Processlist ok 2025-07-09 17:40:08 +02:00
nicolargo 660b94e99d First version of the API 2025-07-09 17:40:08 +02:00
nicolargo 65393b0771 First version of the API 2025-07-09 17:40:08 +02:00
nicolargo 45c3c489a0 On the road of Glances 4.4.0 (after 4.3.3) 2025-07-09 17:37:24 +02:00
nicolargo 73a01d4c52 version 4.3.3 2025-07-09 17:35:33 +02:00
nicolargo 218831ecb9 Align glances.conf file 2025-07-09 17:28:19 +02:00
nicolargo e3bbd0dc2a Something in 4.3.2 broke the home assistant add-on for Glances #3238 2025-07-09 17:26:54 +02:00
nicolargo b340b5fc30 Something in 4.3.2 broke the home assistant add-on for Glances #3238 2025-07-07 07:41:25 +02:00
nicolargo fca11995cb Revert "Something in 4.3.2 broke the home assistant add-on for Glances - Try 1 #3238"
This reverts commit 76b070f3de.
2025-07-06 22:47:52 +02:00
nicolargo 76b070f3de Something in 4.3.2 broke the home assistant add-on for Glances - Try 1 #3238 2025-07-06 22:06:56 +02:00
nicolargo 5b2b7325fc Update README 2025-07-05 18:17:18 +02:00
nicolargo 94ae566eb7 On the orad of Glances 4.4.0 2025-07-05 17:58:24 +02:00
nicolargo a9c3770851 On the orad of Glances 4.4.0 2025-07-05 17:57:05 +02:00
nicolargo 4c31611f2f version 4.3.2 - Update Snap 2025-07-05 10:44:29 +02:00
nicolargo e46a38f141 version 4.3.2 2025-07-05 10:43:46 +02:00
nicolargo c6a48bf424 Update docs and correct an issue with UTC import 2025-07-05 10:32:11 +02:00
nicolargo bf2c6d1bda Add unit test for time zone in history 2025-07-05 10:32:11 +02:00
nicolargo 455888e0fa API history endpoints shows times without timezone #3218 2025-07-05 10:32:11 +02:00
nicolargo 0db81b71c6 Correct a crash in client/server mode 2025-07-05 10:32:11 +02:00
nicolargo c1d67556c7 Make --stdout (csv and json) compliant with client/server mode #3235 2025-07-05 10:32:11 +02:00
nicolargo e46eb765ff Update documentation lint 2025-07-05 10:32:11 +02:00
nicolargo 82a829d0c4 Update documentation 2025-07-05 10:32:11 +02:00
nicolargo 155452e223 Glances 4.3.2_rc01 2025-07-05 10:32:11 +02:00
nicolargo 15a94f04da Improve Restful API documentation embeded in FastAPI #2632 2025-07-05 10:32:11 +02:00
nicolargo 813785ff82 Change the way Docker start Glances 2025-07-05 10:32:11 +02:00
nicolargo 28e82eb237 Lint Dockerfile 2025-07-05 10:32:11 +02:00
nicolargo 1268b58bbc Glances breaks if Podman container is started while it is running #3199 2025-07-05 10:32:11 +02:00
nicolargo 1434d3ad5c Avoid none in rate computation 2025-07-05 10:32:11 +02:00
nicolargo 28ae053e4b Glances Central Browser should use name instead of IP adress for redirection #3103 2025-07-05 10:32:11 +02:00
nicolargo cd71b45089 Add container inactive_file & limit to InfluxDB2 export #3206 2025-07-05 10:32:11 +02:00
nicolargo 1365d600a3 (postgre)SQL export support / TimeScaleDB #2814 2025-07-05 10:32:11 +02:00
nicolargo 7c13ae17fa Implement a basic memory cache with TTL for API call (set to ~1 second) #3202 2025-07-05 10:32:11 +02:00
nicolargo b888dc55e8 Correct key() by keys() 2025-07-05 10:32:11 +02:00
Boris Okassa ed2e1cdefb exports: outputs: Removed iterator helpers
Related to #3181
Signed-off-by: Boris Okassa <borisokassa@yahoo.fr>
2025-07-05 10:32:11 +02:00
renovate[bot] 505a90f8da fix(deps): update dependency pysnmp-lextudio to <6.3.1 2025-07-05 10:32:11 +02:00
snyk-bot cac2e0b884 fix: dev-requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-H11-10293728
2025-07-05 10:32:11 +02:00
snyk-bot df93b5bf4c fix: optional-requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-NUMPY-2321964
- https://snyk.io/vuln/SNYK-PYTHON-NUMPY-2321966
- https://snyk.io/vuln/SNYK-PYTHON-NUMPY-2321970
- https://snyk.io/vuln/SNYK-PYTHON-PYARROW-6052811
- https://snyk.io/vuln/SNYK-PYTHON-SETUPTOOLS-3180412
- https://snyk.io/vuln/SNYK-PYTHON-SETUPTOOLS-7448482
- https://snyk.io/vuln/SNYK-PYTHON-SETUPTOOLS-9964606
- https://snyk.io/vuln/SNYK-PYTHON-URLLIB3-7267250
2025-07-05 10:32:11 +02:00
nicolargo e277908118 Commit issue following #3186 - glances.js not found 2025-07-05 10:32:11 +02:00
fr4nc0is ea12008e10 Update plugin containers display and order #3186 2025-07-05 10:32:11 +02:00
ia74 bcf3d42097 Change "Pinned thread" to "Pinned task" and "Upin" to "Unpin" 2025-07-05 10:32:11 +02:00
Ariel Otilibili 0ffb08dd2a glances: Refactor _build_amps_list
Used a list comprehension. Moreover None and empty list are considered
as false values in Python.

Part-of: #2801
Link: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
Ariel Otilibili 75b7998185 glances: amps: remove iterator helpers
No more needed in Python 3.

Moreover in update(), the keys are not used: the call to items() is
replaced with values().

Related to #3181
2025-07-05 10:32:11 +02:00
nicolargo f45900b450 Add test for JSON export 2025-07-05 10:32:11 +02:00
nicolargo 96660cae16 Add Star history to Readme file 2025-07-05 10:32:11 +02:00
nicolargo 83a5847ff1 Add Star history to Readme file 2025-07-05 10:32:11 +02:00
renovate[bot] 51166fe1df chore(deps): update alpine docker tag to v3.22 2025-07-05 10:32:11 +02:00
Ariel Otilibili 1375573a46 glances: remove iterator helpers
No more needed in Python 3. Following commit 72e1511d ("globals: Fix a
race condition in namedtuple_to_dict"), the dynamic view on the keys is
as well transformed into a list.

Related to #3181
2025-07-05 10:32:11 +02:00
Ariel Otilibili 239d5f7db6 glances: Refactor
Instead of a loop, used a generator for the search.

Part-of: #2801
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
Ariel Otilibili 81150246e5 glances: remove iterator helpers
No more needed in Python 3.

Related to #3181
2025-07-05 10:32:11 +02:00
Ariel Otilibili 39ac435218 plugins: containers: Refactor update()
Commit 445d20dc ("plugins: containers: Refactor") changed update() so
that, stats are added if any of is_key_in_container_and_not_hidden()
or is_key_absent_in_container() is true.

This could be replaced by the negation of is_key_in_container_and_hidden()

Part-of: #2801
Suggested-by: Jasmin Blackshaw <105jassen@gmail.com>
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
Ariel Otilibili 016799cbfa outputs: glances_curses: Refactor
In catch_other_actions_maybe_return_to_browser(), replaced if-statements
by a dict. If no key is found, a null function is executed.

Part-of: #2801
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
nicolargo 719094edf7 Update Glances dev version 2025-07-05 10:32:11 +02:00
nicolargo c343351072 Update Glances dev version 2025-07-05 10:32:11 +02:00
nicolargo d79c74df25 Update Glances dev version 2025-07-05 10:32:11 +02:00
Ariel Otilibili 3488760ae5 globals: Fix a race condition in namedtuple_to_dict
namedtuple_to_dict() loops over a dynamic view of the dict of processes;
other threads might be editing the same dict.

For instance in webserver mode, when a race condition happens, the
following error shows up in the console:

>   File "C:\Users\ryabtsev\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\glances\processes.py", line 628, in update_list
>     return list_of_namedtuple_to_list_of_dict(processlist)
>   File "C:\Users\ryabtsev\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\glances\globals.py", line 495, in list_of_namedtuple_to_list_of_dict
>     return [namedtuple_to_dict(d) for d in data]
>             ^^^^^^^^^^^^^^^^^^^^^
>   File "/app/glances/globals.py", line 490, in namedtuple_to_dict
>     return {k: (v._asdict() if hasattr(v, '_asdict') else v) for k, v in data.items()}
>                                                                          ^^^^^^^^^^^^
> RuntimeError: dictionary changed size during iteration

Instead of a dict view, namedtuple_to_dict() will use a list of tuples.

Performances are unchanged: on average, for 100 runs, namedtuple_to_dict()
completes in 0.2ms

GH issue: #3097
Link: https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
nicolargo 7c0dee3a63 Add support for InfluxDB 3 Core #3182 2025-07-05 10:32:11 +02:00
Ariel Otilibili a5b2a97e6c podman: remove iterator helpers
No more needed in Python 3.

Related to #3181
2025-07-05 10:32:11 +02:00
Ariel Otilibili b57b26e1b5 docker: remove iterator helpers
No more needed in Python 3.

Related to #3181
2025-07-05 10:32:11 +02:00
nicolargo 31834a366f Related to iteritems, itervalues, and iterkeys are not more needed in Python 3 #3181 2025-07-05 10:32:11 +02:00
nicolargo aea1b67862 Related to iteritems, itervalues, and iterkeys are not more needed in Python 3 #3181 2025-07-05 10:32:11 +02:00
nicolargo e28bf6496f Add a new field in the process list to identifie Zombie process #3178 2025-07-05 10:32:11 +02:00
Ariel Otilibili a78e778b36 plugins: containers: Refactor
1. update()
Two branches share the same logic for updating stats. Moreover, intermediate
values where used in nested loops: for filtering keys, and for extending stats.

This could be done at once with chain.from_iterable().

Therefore four helpers are introduced:
- add_engine_into_container
- is_key_in_container_and_not_hidden
- is_key_absent_in_container
- get_containers_from_updated_watcher

A profiling shows the stats build time is the same after the refactor,

$ sudo make run-webserver 2>&1 | grep primitive
         31631 function calls (31566 primitive calls) in 0.028 seconds
         23537 function calls (23487 primitive calls) in 0.025 seconds
         23559 function calls (23509 primitive calls) in 0.014 seconds
         23549 function calls (23499 primitive calls) in 0.024 seconds
         23549 function calls (23499 primitive calls) in 0.026 seconds
         23559 function calls (23509 primitive calls) in 0.016 seconds
         23559 function calls (23509 primitive calls) in 0.022 seconds
         23544 function calls (23494 primitive calls) in 0.015 seconds
         23549 function calls (23499 primitive calls) in 0.023 seconds
         23544 function calls (23494 primitive calls) in 0.018 seconds

[snapshot of top three calls]
         31881 function calls (31816 primitive calls) in 0.029 seconds

   Ordered by: cumulative time
   List reduced from 536 to 3 due to restriction <3>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        3    0.000    0.000    0.029    0.010 __init__.py:250(<genexpr>)
        2    0.000    0.000    0.029    0.015 __init__.py:242(get_containers_from_updated_watcher)
        1    0.000    0.000    0.029    0.029 docker.py:248(update)

And before,

$ sudo make run-webserver 2>&1 | grep primitive
         31620 function calls (31555 primitive calls) in 0.021 seconds
         23526 function calls (23476 primitive calls) in 0.019 seconds
         23533 function calls (23483 primitive calls) in 0.024 seconds
         23538 function calls (23488 primitive calls) in 0.015 seconds
         23528 function calls (23478 primitive calls) in 0.023 seconds
         23528 function calls (23478 primitive calls) in 0.022 seconds
         23533 function calls (23483 primitive calls) in 0.016 seconds
         23538 function calls (23488 primitive calls) in 0.025 seconds
         23538 function calls (23488 primitive calls) in 0.029 seconds
         23538 function calls (23488 primitive calls) in 0.013 seconds

[snapshot of top three calls]
         31865 function calls (31800 primitive calls) in 0.024 seconds

   Ordered by: cumulative time
   List reduced from 531 to 3 due to restriction <3>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.024    0.024 docker.py:248(update)
       13    0.000    0.000    0.022    0.002 decorators.py:38(inner)
       13    0.000    0.000    0.022    0.002 client.py:244(_get)

2. msg_curse()
The conditions are directly used in any().

3. exit()
Minimum supported version is Python 3.9; loops use iterators by default.

itervalues() was introduced for compatility with Python 2. Commit 76ea71f2
("Remove Python 2 in docs and README files") removed Python 2 support.

Part-of: #2801
Link: https://docs.python.org/3/library/itertools.html#itertools.chain.from_iterable
Link: https://docs.python.org/3/library/profile.html#profile.Profile
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
Ariel Otilibili 91902f0ffa plugins: containers: Refactor build_container_data
All the if-statements do the same actions:
- check the key is not disabled
- and add the corresponding callback to the building steps.

There could be done with a list comprehension on a dict.

Part-of: #2801
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
Ariel Otilibili f747bdfa73 containers: Fix sorting
sort_keys() uses sort_by_these_keys() on the stats. So doing, one of the
retrieved values might be absent; and sort_keys() will fall back to the
default sorting method, which is the alphabetical order.

This is the case for dead or exited containers.

Instead of a key lookup, the get method of dict could be used.

GH issue: #3098
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
Ariel Otilibili e4cdc14cd0 containers: processes: Refactor
Refactored sort_docker_stats() and sort_stats().

Useful for an upcoming commit.

Part-of: #2801
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
nicolargo dc165930b4 Refactor code to limit the complexity of update_views method in plugins #3171 2025-07-05 10:32:11 +02:00
nicolargo 03f23a582d Code refactoring - Rename plugin class to <Plugin name>Plugin instead of PluginModel #3169 2025-07-05 10:32:11 +02:00
Ariel Otilibili 1e92365c5e plugins: connections: refactor, version 2
Extracted update_with_net_connections_method and
update_with_nf_conntrack_method from update.

Part-of: #2801
Link: https://scrutinizer-ci.com/g/nicolargo/glances/code-structure/develop/py-function/glances.plugins.connections.PluginModel.update
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
Nicolas Hennion a3e55145b8 Revert "plugins: connections: refactor" 2025-07-05 10:32:11 +02:00
Ariel Otilibili 72a1466e9f plugins: connections: refactor
Extracted update_with_net_connections_method and
update_with_nf_conntrack_method from update.

Part-of: #2801
Link: https://scrutinizer-ci.com/g/nicolargo/glances/code-structure/develop/py-function/glances.plugins.connections.PluginModel.update
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
nguuuquaaa c612899612 fix display checking logic 2025-07-05 10:32:11 +02:00
nguuuquaaa b86b201e6a fix broken matching when fs config has show value 2025-07-05 10:32:11 +02:00
nguuuquaaa c59931cb80 fix broken implementation of plugin.is_display/is_hide/is_show 2025-07-05 10:32:11 +02:00
nicolargo bfda524b4e [WEBUI] Irix mode (per core instead of per CPU percentage) not togglable #3158 2025-07-05 10:32:11 +02:00
nicolargo 0bbb20a127 Done bu display 0 on the WebUI CPU column in IRIS mode... Why ??? 2025-07-05 10:32:11 +02:00
Ariel Otilibili 54e57317be plugins: mem: reduce code complexity
Part of #2801.

Link: https://scrutinizer-ci.com/g/nicolargo/glances/code-structure/develop/py-function/glances.plugins.mem.PluginModel.update
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:11 +02:00
wuhgit c473060740 Revision of confusing descriptions in the SMART section of the document 2025-07-05 10:32:11 +02:00
nicolargo 4e8b1f5508 Correct an issue with VMs bin test 2025-07-05 10:32:11 +02:00
nicolargo 82e59dbfda Add stats about running VMS (qemu/libvirt/kvm support through virsh) #1531 2025-07-05 10:32:11 +02:00
nicolargo fb81a2a5f7 Add stats about running VMS (qemu/libvirt/kvm support through virsh) #1531 2025-07-05 10:32:11 +02:00
Nicolas Hennion 50c440f739 Update faq.rst 2025-07-05 10:32:11 +02:00
Nicolas Hennion 9db02dc7ec Update faq.rst 2025-07-05 10:32:11 +02:00
Nicolas Hennion ae5eb61307 Container memory stats not displayed #3142 2025-07-05 10:32:11 +02:00
Tyler Kerr 080be4d845 strip unsupported line endings from password file 2025-07-05 10:32:11 +02:00
renovate[bot] c738f482e9 Update docker/build-push-action action to v6 2025-07-05 10:32:11 +02:00
nicolargo a550afbd25 Take into account @amard33p comment - https://github.com/nicolargo/glances/issues/2394#issuecomment-2794522951 2025-07-05 10:32:11 +02:00
nicolargo 941baba5b4 Improve unittest for CSV export #3150 2025-07-05 10:32:11 +02:00
nicolargo 91c132fe08 Remove quicklook from the exportable list 2025-07-05 10:32:11 +02:00
nicolargo 009212a04d Do not store data during InfluxDB test 2025-07-05 10:32:11 +02:00
nicolargo 43626d9d69 Improve unittest for InfluxDB plugin #3149 2025-07-05 10:32:11 +02:00
nicolargo ec8365817f Improve unittest for InfluxDB plugin #3149 2025-07-05 10:32:11 +02:00
nicolargo bdef92da4f Correct NoneType in update_view method 2025-07-05 10:32:11 +02:00
renovate[bot] 16dee18c52 chore(deps): update actions/stale action to v9 2025-07-05 10:32:11 +02:00
renovate[bot] d2de1424fb Update dependency windows to v2025 2025-07-05 10:32:10 +02:00
renovate[bot] daf6964f14 Update dependency ubuntu to v24 2025-07-05 10:32:10 +02:00
nicolargo cfc34e7cd4 Upgrade JS libs #3147 2025-07-05 10:32:10 +02:00
nicolargo 0d2f5e4ba3 Commit before lint 2025-07-05 10:32:10 +02:00
nicolargo 7de582fdb0 Update precommit hook 2025-07-05 10:32:10 +02:00
nicolargo 1378eb80ba Correct README file 2025-07-05 10:32:10 +02:00
nicolargo fe59850c28 Make the shtab optional 2025-07-05 10:32:10 +02:00
nicolargo 558ebbaec7 Add a new option --print-completion to generate shell tab completion - #3111 2025-07-05 10:32:10 +02:00
nicolargo 9e2504b056 Update documentation 2025-07-05 10:32:10 +02:00
nicolargo b4d5de96fc Add shtab as a dependency 2025-07-05 10:32:10 +02:00
nicolargo 0e181d9059 In the FS module, do not display threshold for volume mounted in 'ro' (read-only) #3143 2025-07-05 10:32:10 +02:00
kenrmayfield 25b550834a Update README.rst
Update Line: 
Glances Binary Package Versions per System Architecture for FreeBSD as of: 08 Mar 2025 04:05:21

Changed to:
Glances Binary Package Versions and Python Versions( pyXY-glances) per System Architecture for FreeBSD as of: 08 Mar 2025 04:05:21
2025-07-05 10:32:10 +02:00
kenrmayfield b30a29897e Update README.rst
Remove CHECK Link.  Not working correctly.  Need to wait until Main Repository has been Updated.  Then I can make a Link to the FreeBSD Glances Binary Package Version Picture.
2025-07-05 10:32:10 +02:00
kenrmayfield 451b1a2294 Update README.rst
Updated CHECK Link again due to Error in Syntax
2025-07-05 10:32:10 +02:00
kenrmayfield b23a4f6a3d Update README.rst
Updated CHECK Link for Python Version corresponding to Glances Binary Package
2025-07-05 10:32:10 +02:00
kenrmayfield 415abe2284 Update README.rst
Capitalize the words eye and system in the Title to Eye and System
2025-07-05 10:32:10 +02:00
kenrmayfield 7f5f2fd2b1 Update README.rst
Changed spelling of ports to Ports
2025-07-05 10:32:10 +02:00
kenrmayfield 49aa4e21ab Update README.rst
Bold the word NOTE
2025-07-05 10:32:10 +02:00
kenrmayfield 11a1aa6250 Update README.rst
Update FreeBSD Instructions
1. Add Glances Binary Package Versions Picture
2. Update Install
3. Add Note to Check Glances Binary Package Version with System 
    Architecture that corresponds to Python Version.
4.  Add Link to Glances Binary Package Versions Picture called 
     CHECK
2025-07-05 10:32:10 +02:00
nicolargo a1eb6cbacf [GPU] AMD Plugin: Operation not permitted #3125 2025-07-05 10:32:10 +02:00
nicolargo 343c933e15 FR: Sort Sensors my name in proper number order #3132 2025-07-05 10:32:10 +02:00
nicolargo e8b6fd7641 [Discussion] FreeBSD - AMD64 Install: Package py39-glances not showing a Version Available for AMD64 but for Package py311-glances #3139 2025-07-05 10:32:10 +02:00
Ariel Otilibili 5d4596ed41 glances: refactor start()
Part of #2801: start() is split into start_main_loop(),
check_memleak(), setup_server_mode(), & maybe_trace_memleak().

Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-07-05 10:32:10 +02:00
nicolargo 0d542550a4 Update docs and correct an issue with UTC import 2025-07-05 10:19:59 +02:00
nicolargo ba67e55a0f Add unit test for time zone in history 2025-07-05 09:57:54 +02:00
nicolargo 4cf6cf8ac2 API history endpoints shows times without timezone #3218 2025-07-05 09:38:02 +02:00
nicolargo 3eb67a32bb Correct a crash in client/server mode 2025-07-05 09:18:59 +02:00
nicolargo fe48f7df94 Make --stdout (csv and json) compliant with client/server mode #3235 2025-07-05 08:17:58 +02:00
nicolargo ca65324976 Update documentation lint 2025-06-30 08:06:25 +02:00
nicolargo d563c68b4c Update documentation 2025-06-30 08:04:14 +02:00
nicolargo d978d545ed Glances 4.3.2_rc01 2025-06-29 17:33:31 +02:00
nicolargo 4f743287b6 Merge branch 'issue2632' into develop 2025-06-29 14:33:25 +02:00
nicolargo 12eecf47f6 Improve Restful API documentation embeded in FastAPI #2632 2025-06-29 14:32:58 +02:00
nicolargo 7b8724244a Change the way Docker start Glances 2025-06-29 13:40:52 +02:00
nicolargo fcbad49771 Lint Dockerfile 2025-06-29 13:22:35 +02:00
nicolargo 4b15e6693c Glances breaks if Podman container is started while it is running #3199 2025-06-29 11:00:28 +02:00
nicolargo 6db3672c50 Avoid none in rate computation 2025-06-28 20:14:12 +02:00
nicolargo b56b231ad8 Glances Central Browser should use name instead of IP adress for redirection #3103 2025-06-28 16:10:34 +02:00
nicolargo eec89ccd90 Merge branch 'issue3206' into develop 2025-06-22 22:17:22 +02:00
nicolargo 95ff4506d1 Add container inactive_file & limit to InfluxDB2 export #3206 2025-06-22 22:17:02 +02:00
nicolargo 4e5eff834a Merge branch 'issue2814-TimeScaleDB' into develop 2025-06-22 18:26:43 +02:00
nicolargo 4f03be737b (postgre)SQL export support / TimeScaleDB #2814 2025-06-22 18:26:00 +02:00
nicolargo f799911058 Merge branch 'issue3202' into develop 2025-06-14 10:47:23 +02:00
nicolargo 0dbd4c58d4 Implement a basic memory cache with TTL for API call (set to ~1 second) #3202 2025-06-14 10:47:00 +02:00
nicolargo ecb3e6e0ea Correct key() by keys() 2025-06-10 19:24:42 +02:00
Boris Okassa 4ef03ddac2 exports: outputs: Removed iterator helpers
Related to #3181
Signed-off-by: Boris Okassa <borisokassa@yahoo.fr>
2025-06-10 00:13:05 +02:00
Nicolas Hennion ed400246fd
Merge pull request #3107 from nicolargo/renovate/pysnmp-lextudio-6.x
fix(deps): update dependency pysnmp-lextudio to <6.3.1
2025-06-09 13:10:57 +02:00
Nicolas Hennion efa636b7a7
Merge pull request #3195 from nicolargo/snyk-fix-b29587102aaf5d35b3590a9d7407596b
[Snyk] Security upgrade h11 from 0.14.0 to 0.16.0
2025-06-08 09:44:46 +02:00
Nicolas Hennion a3556a4004
Merge pull request #3184 from nicolargo/snyk-fix-7fbd5db6108cdc150d044d238786b513
[Snyk] Fix for 8 vulnerabilities
2025-06-08 09:44:23 +02:00
nicolargo 4dd312229f Commit issue following #3186 - glances.js not found 2025-06-08 09:40:46 +02:00
snyk-bot 36e0b46903
fix: dev-requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-H11-10293728
2025-06-08 07:34:24 +00:00
Nicolas Hennion b31aa7234e
Merge pull request #3192 from fr4nc0is/FIX-3186
Update plugin containers display and order #3186
2025-06-08 09:31:38 +02:00
Nicolas Hennion f854ea31d8
Merge pull request #3191 from ia74/patch-1
Change "Pinned thread" to "Pinned task" and "Upin" to "Unpin"
2025-06-08 09:30:41 +02:00
Nicolas Hennion 1f8e63ec3d
Merge pull request #3190 from ariel-anieli/issue-3181
glances: amps: Removal of iterator helpers, refactor
2025-06-08 09:30:07 +02:00
fr4nc0is 0e288cdc97 Update plugin containers display and order #3186 2025-06-03 09:18:14 +02:00
ia74 6ed7bee526
Change "Pinned thread" to "Pinned task" and "Upin" to "Unpin" 2025-06-02 16:23:22 -05:00
Ariel Otilibili 5a661a295c
glances: Refactor _build_amps_list
Used a list comprehension. Moreover None and empty list are considered
as false values in Python.

Part-of: #2801
Link: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-06-02 14:20:22 +02:00
Ariel Otilibili 9e29d8aa45
glances: amps: remove iterator helpers
No more needed in Python 3.

Moreover in update(), the keys are not used: the call to items() is
replaced with values().

Related to #3181
2025-06-02 14:20:13 +02:00
nicolargo a6de55bfca Add test for JSON export 2025-06-01 17:37:21 +02:00
nicolargo fe5edd6e60 Add Star history to Readme file 2025-05-31 15:31:18 +02:00
nicolargo 71c1c34ddd Add Star history to Readme file 2025-05-31 15:28:17 +02:00
Nicolas Hennion eff75ebeed
Merge pull request #3189 from nicolargo/renovate/alpine-3.x
chore(deps): update alpine docker tag to v3.22
2025-05-31 14:44:31 +02:00
Nicolas Hennion 07daacd35f
Merge pull request #3187 from ariel-anieli/issue-3181-processes-client-snmp
glances: removal of iterators helpers in `processes.py` & `stats_client_snmp.py`
2025-05-31 14:44:01 +02:00
Nicolas Hennion 7440f6df9b
Merge pull request #3188 from ariel-anieli/issue-2801-glances-curses
outputs: containers: Refactor
2025-05-31 14:43:45 +02:00
Ariel Otilibili d6802b77d7
plugins: containers: Refactor update()
Commit 445d20dc ("plugins: containers: Refactor") changed update() so
that, stats are added if any of is_key_in_container_and_not_hidden()
or is_key_absent_in_container() is true.

This could be replaced by the negation of is_key_in_container_and_hidden()

Part-of: #2801
Suggested-by: Jasmin Blackshaw <105jassen@gmail.com>
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-05-31 13:46:44 +02:00
renovate[bot] 3462a48081
chore(deps): update alpine docker tag to v3.22 2025-05-30 22:02:58 +00:00
Ariel Otilibili c5acae327b
outputs: glances_curses: Refactor
In catch_other_actions_maybe_return_to_browser(), replaced if-statements
by a dict. If no key is found, a null function is executed.

Part-of: #2801
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-05-30 14:12:11 +02:00
Ariel Otilibili 73663ef5c7
glances: remove iterator helpers
No more needed in Python 3. Following commit 72e1511d ("globals: Fix a
race condition in namedtuple_to_dict"), the dynamic view on the keys is
as well transformed into a list.

Related to #3181
2025-05-29 13:59:09 +02:00
Ariel Otilibili 003ba392c0
glances: Refactor
Instead of a loop, used a generator for the search.

Part-of: #2801
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-05-29 13:59:09 +02:00
Ariel Otilibili b28d793d72
glances: remove iterator helpers
No more needed in Python 3.

Related to #3181
2025-05-29 13:39:51 +02:00
nicolargo 1c6485fda6 Update Glances dev version 2025-05-28 10:01:16 +02:00
nicolargo 649562ca37 Update Glances dev version 2025-05-28 09:54:07 +02:00
nicolargo 17e43c1271 Update Glances dev version 2025-05-28 09:39:25 +02:00
Nicolas Hennion c1a772d7f1
Merge pull request #3185 from ariel-anieli/issue-3097-dict-changed-size-runtime-error
globals: Fix a race condition in `namedtuple_to_dict`
2025-05-27 17:32:55 +02:00
Ariel Otilibili 72e1511dc4
globals: Fix a race condition in namedtuple_to_dict
namedtuple_to_dict() loops over a dynamic view of the dict of processes;
other threads might be editing the same dict.

For instance in webserver mode, when a race condition happens, the
following error shows up in the console:

>   File "C:\Users\ryabtsev\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\glances\processes.py", line 628, in update_list
>     return list_of_namedtuple_to_list_of_dict(processlist)
>   File "C:\Users\ryabtsev\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\glances\globals.py", line 495, in list_of_namedtuple_to_list_of_dict
>     return [namedtuple_to_dict(d) for d in data]
>             ^^^^^^^^^^^^^^^^^^^^^
>   File "/app/glances/globals.py", line 490, in namedtuple_to_dict
>     return {k: (v._asdict() if hasattr(v, '_asdict') else v) for k, v in data.items()}
>                                                                          ^^^^^^^^^^^^
> RuntimeError: dictionary changed size during iteration

Instead of a dict view, namedtuple_to_dict() will use a list of tuples.

Performances are unchanged: on average, for 100 runs, namedtuple_to_dict()
completes in 0.2ms

GH issue: #3097
Link: https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-05-27 14:55:41 +02:00
snyk-bot 3278027281
fix: optional-requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-NUMPY-2321964
- https://snyk.io/vuln/SNYK-PYTHON-NUMPY-2321966
- https://snyk.io/vuln/SNYK-PYTHON-NUMPY-2321970
- https://snyk.io/vuln/SNYK-PYTHON-PYARROW-6052811
- https://snyk.io/vuln/SNYK-PYTHON-SETUPTOOLS-3180412
- https://snyk.io/vuln/SNYK-PYTHON-SETUPTOOLS-7448482
- https://snyk.io/vuln/SNYK-PYTHON-SETUPTOOLS-9964606
- https://snyk.io/vuln/SNYK-PYTHON-URLLIB3-7267250
2025-05-27 08:51:43 +00:00
nicolargo ad5e7f8479 Merge branch 'issue3182-InfluxDB3' into develop 2025-05-27 10:46:54 +02:00
nicolargo 3f124d3596 Add support for InfluxDB 3 Core #3182 2025-05-27 10:46:39 +02:00
Nicolas Hennion 24eb6cf21a
Merge pull request #3183 from ariel-anieli/issue-2801-refactor
podman: docker: Remove iterator helpers
2025-05-26 19:24:48 +02:00
Ariel Otilibili 52bd26f03e
podman: remove iterator helpers
No more needed in Python 3.

Related to #3181
2025-05-26 17:34:58 +02:00
Ariel Otilibili 75afbe16c9
docker: remove iterator helpers
No more needed in Python 3.

Related to #3181
2025-05-26 17:34:48 +02:00
nicolargo 6af2aa092b Related to iteritems, itervalues, and iterkeys are not more needed in Python 3 #3181 2025-05-26 15:12:42 +02:00
nicolargo 95d38a1392 Related to iteritems, itervalues, and iterkeys are not more needed in Python 3 #3181 2025-05-26 15:12:03 +02:00
nicolargo dde335696a Add a new field in the process list to identifie Zombie process #3178 2025-05-25 18:52:25 +02:00
Nicolas Hennion ef83c404ff
Merge pull request #3180 from ariel-anieli/issue-2801-refactor
plugins: containers; Refactor
2025-05-25 17:30:57 +02:00
Ariel Otilibili 445d20dcad
plugins: containers: Refactor
1. update()
Two branches share the same logic for updating stats. Moreover, intermediate
values where used in nested loops: for filtering keys, and for extending stats.

This could be done at once with chain.from_iterable().

Therefore four helpers are introduced:
- add_engine_into_container
- is_key_in_container_and_not_hidden
- is_key_absent_in_container
- get_containers_from_updated_watcher

A profiling shows the stats build time is the same after the refactor,

$ sudo make run-webserver 2>&1 | grep primitive
         31631 function calls (31566 primitive calls) in 0.028 seconds
         23537 function calls (23487 primitive calls) in 0.025 seconds
         23559 function calls (23509 primitive calls) in 0.014 seconds
         23549 function calls (23499 primitive calls) in 0.024 seconds
         23549 function calls (23499 primitive calls) in 0.026 seconds
         23559 function calls (23509 primitive calls) in 0.016 seconds
         23559 function calls (23509 primitive calls) in 0.022 seconds
         23544 function calls (23494 primitive calls) in 0.015 seconds
         23549 function calls (23499 primitive calls) in 0.023 seconds
         23544 function calls (23494 primitive calls) in 0.018 seconds

[snapshot of top three calls]
         31881 function calls (31816 primitive calls) in 0.029 seconds

   Ordered by: cumulative time
   List reduced from 536 to 3 due to restriction <3>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        3    0.000    0.000    0.029    0.010 __init__.py:250(<genexpr>)
        2    0.000    0.000    0.029    0.015 __init__.py:242(get_containers_from_updated_watcher)
        1    0.000    0.000    0.029    0.029 docker.py:248(update)

And before,

$ sudo make run-webserver 2>&1 | grep primitive
         31620 function calls (31555 primitive calls) in 0.021 seconds
         23526 function calls (23476 primitive calls) in 0.019 seconds
         23533 function calls (23483 primitive calls) in 0.024 seconds
         23538 function calls (23488 primitive calls) in 0.015 seconds
         23528 function calls (23478 primitive calls) in 0.023 seconds
         23528 function calls (23478 primitive calls) in 0.022 seconds
         23533 function calls (23483 primitive calls) in 0.016 seconds
         23538 function calls (23488 primitive calls) in 0.025 seconds
         23538 function calls (23488 primitive calls) in 0.029 seconds
         23538 function calls (23488 primitive calls) in 0.013 seconds

[snapshot of top three calls]
         31865 function calls (31800 primitive calls) in 0.024 seconds

   Ordered by: cumulative time
   List reduced from 531 to 3 due to restriction <3>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.024    0.024 docker.py:248(update)
       13    0.000    0.000    0.022    0.002 decorators.py:38(inner)
       13    0.000    0.000    0.022    0.002 client.py:244(_get)

2. msg_curse()
The conditions are directly used in any().

3. exit()
Minimum supported version is Python 3.9; loops use iterators by default.

itervalues() was introduced for compatility with Python 2. Commit 76ea71f2
("Remove Python 2 in docs and README files") removed Python 2 support.

Part-of: #2801
Link: https://docs.python.org/3/library/itertools.html#itertools.chain.from_iterable
Link: https://docs.python.org/3/library/profile.html#profile.Profile
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-05-24 19:56:59 +02:00
Ariel Otilibili e732ba37be
plugins: containers: Refactor build_container_data
All the if-statements do the same actions:
- check the key is not disabled
- and add the corresponding callback to the building steps.

There could be done with a list comprehension on a dict.

Part-of: #2801
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-05-23 18:17:33 +02:00
Nicolas Hennion e118d7d171
Merge pull request #3179 from ariel-anieli/issue-3098-display-containers
Fix the sorting of containers
2025-05-23 11:33:36 +02:00
Ariel Otilibili b414bafc94
containers: Fix sorting
sort_keys() uses sort_by_these_keys() on the stats. So doing, one of the
retrieved values might be absent; and sort_keys() will fall back to the
default sorting method, which is the alphabetical order.

This is the case for dead or exited containers.

Instead of a key lookup, the get method of dict could be used.

GH issue: #3098
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-05-22 17:40:42 +02:00
Ariel Otilibili 8e23bd661f
containers: processes: Refactor
Refactored sort_docker_stats() and sort_stats().

Useful for an upcoming commit.

Part-of: #2801
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-05-22 17:40:42 +02:00
nicolargo c8d70fb343 Merge branch 'issue3171' into develop 2025-05-02 15:02:33 +02:00
nicolargo 21f0fd0eb4 Refactor code to limit the complexity of update_views method in plugins #3171 2025-05-02 15:01:29 +02:00
nicolargo 5f31106ac1 Merge branch 'issue3169' into develop 2025-05-02 11:17:04 +02:00
nicolargo 6e466c7d14 Code refactoring - Rename plugin class to <Plugin name>Plugin instead of PluginModel #3169 2025-05-02 09:41:32 +02:00
renovate[bot] 1be8d528ed
fix(deps): update dependency pysnmp-lextudio to <6.3.1 2025-04-30 12:09:19 +00:00
Nicolas Hennion 1e7e0c056b
Merge pull request #3163 from ariel-anieli/refactor-plugin-connections-v2
plugins: connections: refactor, version 2
2025-04-27 19:56:10 +02:00
Ariel Otilibili ebd8f766b0
plugins: connections: refactor, version 2
Extracted update_with_net_connections_method and
update_with_nf_conntrack_method from update.

Part-of: #2801
Link: https://scrutinizer-ci.com/g/nicolargo/glances/code-structure/develop/py-function/glances.plugins.connections.PluginModel.update
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-04-27 17:16:43 +02:00
Nicolas Hennion 270ff52363
Merge pull request #3162 from nicolargo/revert-3161-refactor-plugin-connections
Revert "plugins: connections: refactor"
2025-04-27 15:21:54 +02:00
Nicolas Hennion f933d9f1ef
Revert "plugins: connections: refactor" 2025-04-27 15:21:06 +02:00
Nicolas Hennion c69a5a9307
Merge pull request #3161 from ariel-anieli/refactor-plugin-connections
plugins: connections: refactor
2025-04-27 15:02:36 +02:00
Nicolas Hennion b747c87796
Merge pull request #3160 from nguuuquaaa/fix-fs
Fix matching problem when fs config has "show" value
2025-04-27 14:58:38 +02:00
nicolargo f77e039f21 Merge branch 'issue3158' into develop 2025-04-27 14:53:30 +02:00
nicolargo 1901fcad05 [WEBUI] Irix mode (per core instead of per CPU percentage) not togglable #3158 2025-04-27 14:53:02 +02:00
nguuuquaaa 2ef24c7b2d fix display checking logic 2025-04-27 04:35:24 +07:00
Ariel Otilibili 1c93368561
plugins: connections: refactor
Extracted update_with_net_connections_method and
update_with_nf_conntrack_method from update.

Part-of: #2801
Link: https://scrutinizer-ci.com/g/nicolargo/glances/code-structure/develop/py-function/glances.plugins.connections.PluginModel.update
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-04-26 19:07:32 +02:00
nicolargo 55258748ff Done bu display 0 on the WebUI CPU column in IRIS mode... Why ??? 2025-04-26 19:06:56 +02:00
nguuuquaaa 133b97bcaf fix broken matching when fs config has show value 2025-04-26 01:12:13 +07:00
nguuuquaaa 840670ea77 fix broken implementation of plugin.is_display/is_hide/is_show 2025-04-26 00:52:15 +07:00
Nicolas Hennion 5e1a08af79
Merge pull request #3159 from ariel-anieli/reduce-complexity-mem-plugin-update
plugins: mem: reduce code complexity
2025-04-24 07:59:43 +02:00
Ariel Otilibili 4531aac37f
plugins: mem: reduce code complexity
Part of #2801.

Link: https://scrutinizer-ci.com/g/nicolargo/glances/code-structure/develop/py-function/glances.plugins.mem.PluginModel.update
Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-04-23 22:01:27 +02:00
Nicolas Hennion 829ae981b5
Merge pull request #3152 from wuhgit/develop
Revision of confusing descriptions in the SMART section of the document
2025-04-21 11:13:53 +02:00
nicolargo 8d1039d4ac Correct an issue with VMs bin test 2025-04-21 10:56:04 +02:00
nicolargo 2cbf7a7fba Merge branch 'issue1531' into develop 2025-04-21 10:40:03 +02:00
nicolargo 0f10ffc245 Add stats about running VMS (qemu/libvirt/kvm support through virsh) #1531 2025-04-21 10:39:48 +02:00
nicolargo f9250c9985 Add stats about running VMS (qemu/libvirt/kvm support through virsh) #1531 2025-04-21 10:39:20 +02:00
wuhgit bdccb0b196
Revision of confusing descriptions in the SMART section of the document 2025-04-17 07:19:26 +08:00
Nicolas Hennion 7218a22002
Update faq.rst 2025-04-15 09:04:40 +02:00
Nicolas Hennion 1feb1ec6f8
Update faq.rst 2025-04-15 09:04:14 +02:00
Nicolas Hennion 4b21d0a18f
Container memory stats not displayed #3142 2025-04-15 09:03:41 +02:00
Nicolas Hennion bead0ae13d
Merge pull request #3151 from Destarianon/bug/discussion2274
Ignore unsupported line endings in password file
2025-04-15 08:54:09 +02:00
Tyler Kerr 1c7a824821
strip unsupported line endings from password file 2025-04-14 21:54:36 +00:00
Nicolas Hennion 48543cab8f
Merge pull request #3070 from nicolargo/renovate/docker-build-push-action-6.x
Update docker/build-push-action action to v6
2025-04-14 16:54:29 +02:00
nicolargo 01b4ca1cf5 Take into account @amard33p comment - https://github.com/nicolargo/glances/issues/2394#issuecomment-2794522951 2025-04-13 16:36:29 +02:00
nicolargo de90b4fd54 Improve unittest for CSV export #3150 2025-04-13 16:28:49 +02:00
nicolargo a91b187069 Remove quicklook from the exportable list 2025-04-13 15:53:52 +02:00
nicolargo eeb7db8e82 Do not store data during InfluxDB test 2025-04-13 15:41:02 +02:00
nicolargo d22f3e16fe Merge branch 'issue3149' into develop 2025-04-13 14:41:32 +02:00
nicolargo 7cc4e86639 Improve unittest for InfluxDB plugin #3149 2025-04-13 14:41:08 +02:00
nicolargo 3cdededa78 Improve unittest for InfluxDB plugin #3149 2025-04-13 14:40:56 +02:00
nicolargo 3016717208 Correct NoneType in update_view method 2025-04-12 16:40:26 +02:00
Nicolas Hennion 9fd1efce21
Merge pull request #3108 from nicolargo/renovate/actions-stale-9.x
Update actions/stale action to v9
2025-04-12 16:12:51 +02:00
Nicolas Hennion 4db6aaeb3d
Merge pull request #3148 from nicolargo/renovate/windows-2025.x
Update dependency windows to v2025
2025-04-12 16:11:06 +02:00
Nicolas Hennion d4202339d9
Merge pull request #3066 from nicolargo/renovate/ubuntu-24.x
Update dependency ubuntu to v24
2025-04-12 16:10:52 +02:00
renovate[bot] 551ec80be6
Update dependency windows to v2025 2025-04-12 12:55:54 +00:00
nicolargo 4c9685f0bf Merge branch 'issue3147' into develop 2025-04-12 14:55:16 +02:00
nicolargo 90ebe27f7e Upgrade JS libs #3147 2025-04-12 14:54:39 +02:00
nicolargo 0907700843 Commit before lint 2025-04-12 14:51:38 +02:00
nicolargo ca2b7a9907 Update precommit hook 2025-04-11 19:19:44 +02:00
nicolargo c1beee40a2 Correct README file 2025-04-11 19:05:30 +02:00
nicolargo acf54a508e Make the shtab optional 2025-04-11 18:54:25 +02:00
nicolargo 50aa49a497 Merge branch 'issue3111' into develop 2025-04-11 18:45:24 +02:00
nicolargo 17553fac8f Add a new option --print-completion to generate shell tab completion - #3111 2025-04-11 18:44:55 +02:00
nicolargo 060457c2ae Update documentation 2025-04-11 18:43:35 +02:00
nicolargo bf54140b87 Add shtab as a dependency 2025-04-11 18:18:21 +02:00
nicolargo 18d6beebe1 Merge branch 'issue3143' into develop 2025-04-11 17:59:31 +02:00
nicolargo bc1211a672 In the FS module, do not display threshold for volume mounted in 'ro' (read-only) #3143 2025-04-11 17:58:00 +02:00
Nicolas Hennion 9cd51fc060
Merge pull request #3141 from kenrmayfield/develop
Updates 4/6/2025: FreeBSD Section - Glances - An Eye on your System
2025-04-10 13:42:21 +02:00
kenrmayfield 041a9852a9
Update README.rst
Update Line: 
Glances Binary Package Versions per System Architecture for FreeBSD as of: 08 Mar 2025 04:05:21

Changed to:
Glances Binary Package Versions and Python Versions( pyXY-glances) per System Architecture for FreeBSD as of: 08 Mar 2025 04:05:21
2025-04-06 23:42:31 -05:00
kenrmayfield aa8f3abab3
Update README.rst
Remove CHECK Link.  Not working correctly.  Need to wait until Main Repository has been Updated.  Then I can make a Link to the FreeBSD Glances Binary Package Version Picture.
2025-04-06 23:33:44 -05:00
kenrmayfield 96aa6987ab
Update README.rst
Updated CHECK Link again due to Error in Syntax
2025-04-06 23:20:20 -05:00
kenrmayfield f29af964ef
Update README.rst
Updated CHECK Link for Python Version corresponding to Glances Binary Package
2025-04-06 22:44:53 -05:00
kenrmayfield aee1ea6acd
Update README.rst
Capitalize the words eye and system in the Title to Eye and System
2025-04-06 22:27:05 -05:00
kenrmayfield 6f93c1bad9
Update README.rst
Changed spelling of ports to Ports
2025-04-06 22:00:29 -05:00
kenrmayfield 20d08c0cb6
Update README.rst
Bold the word NOTE
2025-04-06 21:58:44 -05:00
kenrmayfield 4d7300ab96
Update README.rst
Update FreeBSD Instructions
1. Add Glances Binary Package Versions Picture
2. Update Install
3. Add Note to Check Glances Binary Package Version with System 
    Architecture that corresponds to Python Version.
4.  Add Link to Glances Binary Package Versions Picture called 
     CHECK
2025-04-06 21:47:56 -05:00
nicolargo 0b62a9f785 [GPU] AMD Plugin: Operation not permitted #3125 2025-04-06 18:49:38 +02:00
nicolargo cb22a0c56a Merge branch 'issue3132' into develop 2025-04-06 18:39:26 +02:00
nicolargo fcbcbbfb4d FR: Sort Sensors my name in proper number order #3132 2025-04-06 18:37:45 +02:00
nicolargo c5954c9d99 [Discussion] FreeBSD - AMD64 Install: Package py39-glances not showing a Version Available for AMD64 but for Package py311-glances #3139 2025-04-06 17:51:36 +02:00
nicolargo ad0be97653 Merge branch 'develop' of github.com:nicolargo/glances into develop 2025-04-06 17:49:08 +02:00
nicolargo 6ccd4430e9 Merge branch 'issue2394' into develop 2025-04-06 17:48:54 +02:00
nicolargo b06b0d5c7c Make all results from amps plugins exportable #2394 2025-04-06 17:48:34 +02:00
nicolargo 3a96cfd0cc CSV column name now include the plugin name - Related to #2394 2025-04-06 17:33:20 +02:00
Nicolas Hennion 584fbba311
Merge pull request #3131 from ariel-anieli/glances-start-refactor
glances: refactor start()
2025-03-25 07:37:59 +01:00
Ariel Otilibili fc4dc38028 glances: refactor start()
Part of #2801: start() is split into start_main_loop(),
check_memleak(), setup_server_mode(), & maybe_trace_memleak().

Signed-off-by: Ariel Otilibili <otilibil@eurecom.fr>
2025-03-23 21:19:31 +01:00
nicolargo aa428b51c9 Create a new init method for export in order to init fields in some export module 2025-03-23 18:53:19 +01:00
nicolargo 3ba5946974 Remove <plugin>_disable field from export 2025-03-23 17:49:32 +01:00
nicolargo b7cdf66eb0 On the road of Glances 4.3.2 2025-03-22 17:53:04 +01:00
renovate[bot] 6a265be9d5
chore(deps): update actions/stale action to v9 2025-02-19 09:49:45 +00:00
renovate[bot] f1054c5eea
Update docker/build-push-action action to v6 2024-12-30 14:11:07 +00:00
renovate[bot] 4c3e8f9448
Update dependency ubuntu to v24 2024-12-30 12:42:00 +00:00
264 changed files with 27435 additions and 22074 deletions

View File

@ -8,10 +8,8 @@
!/glances/outputs/static
# Include Requirements files
!/requirements.txt
!/all-requirements.txt
!/docker-requirements.txt
!/webui-requirements.txt
!/optional-requirements.txt
# Include Config file
!/docker-compose/glances.conf
@ -19,3 +17,6 @@
# Include Binary file
!/docker-bin.sh
# Include TOML file
!/pyproject.toml

View File

@ -12,9 +12,9 @@ jobs:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Install pypa/build
@ -45,7 +45,7 @@ jobs:
id-token: write
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: python-package-distributions
path: dist/
@ -54,6 +54,7 @@ jobs:
with:
skip-existing: true
attestations: false
print-hash: true
pypi_test:
name: Publish Python 🐍 distribution 📦 to TestPyPI
@ -69,7 +70,7 @@ jobs:
id-token: write
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: python-package-distributions
path: dist/

View File

@ -4,7 +4,7 @@ name: build_docker
env:
DEFAULT_DOCKER_IMAGE: nicolargo/glances
PUSH_BRANCH: ${{ 'refs/heads/develop' == github.ref || 'refs/heads/master' == github.ref || startsWith(github.ref, 'refs/tags/v') }}
PUSH_BRANCH: ${{ 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/v') }}
# Alpine image platform: https://hub.docker.com/_/alpine
# linux/arm/v6,linux/arm/v7 do not work (timeout during the build)
DOCKER_PLATFORMS: linux/amd64,linux/arm64/v8
@ -36,15 +36,11 @@ jobs:
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
TAG_ARRAY="[{ \"target\": \"minimal\", \"tag\": \"${VERSION}\" },"
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"${VERSION}-full\" }]"
TAG_ARRAY="$TAG_ARRAY { \"target\": \"minimal\", \"tag\": \"latest\" },"
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"${VERSION}-full\" },"
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"latest-full\" }]"
elif [[ $GITHUB_REF == refs/heads/develop ]]; then
TAG_ARRAY="[{ \"target\": \"dev\", \"tag\": \"dev\" }]"
elif [[ $GITHUB_REF == refs/heads/master ]]; then
TAG_ARRAY="[{ \"target\": \"minimal\", \"tag\": \"latest\" },"
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"latest-full\" }]"
elif [[ $GITHUB_REF == refs/heads/main ]]; then
TAG_ARRAY="[{ \"target\": \"minimal\", \"tag\": \"latest\" },"
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"latest-full\" }]"
else
TAG_ARRAY="[]"
fi
@ -63,7 +59,7 @@ jobs:
tag: ${{ fromJson(needs.create_docker_images_list.outputs.tags) }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Retrieve Repository Docker metadata
id: docker_meta
@ -92,7 +88,7 @@ jobs:
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
push: ${{ env.PUSH_BRANCH == 'true' }}
tags: "${{ env.DEFAULT_DOCKER_IMAGE }}:${{ matrix.os != 'alpine' && format('{0}-', matrix.os) || '' }}${{ matrix.tag.tag }}"

View File

@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Run Trivy vulnerability scanner in repo mode
uses: aquasecurity/trivy-action@master

View File

@ -10,7 +10,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
- uses: actions/stale@v10
with:
days-before-issue-stale: 90
days-before-issue-close: -1

View File

@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@ -8,10 +8,10 @@ on:
jobs:
source-code-checks:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Check formatting with Ruff
uses: chartboost/ruff-action@v1
@ -37,14 +37,16 @@ jobs:
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
# Python EOL version are note tested
# Multiple Python version only tested for Linux
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
@ -58,26 +60,22 @@ jobs:
run: |
python -m pytest ./tests/test_core.py
# Error appear with h11, not related to Glances
# Should be tested if correction is done
# Installed c:\hostedtoolcache\windows\python\3.9.13\x64\lib\site-packages\exceptiongroup-1.2.1-py3.9.egg
# error: h11 0.14.0 is installed but h11<0.13,>=0.11 is required by {'httpcore'}
# Error: Process completed with exit code 1.
test-windows:
needs: source-code-checks
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
runs-on: windows-2022
runs-on: windows-2025
strategy:
matrix:
# Windows-curses not available for Python 3.13 for the moment
python-version: ["3.9", "3.10", "3.11", "3.12"]
# Windows-curses not available for Python 3.14 for the moment
# See https://github.com/zephyrproject-rtos/windows-curses/issues/76
python-version: ["3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
@ -96,18 +94,18 @@ jobs:
needs: source-code-checks
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
runs-on: macos-14
runs-on: macos-15
strategy:
matrix:
# Only test the latest stable version
python-version: ["3.13"]
python-version: ["3.14"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'

View File

@ -14,9 +14,9 @@ jobs:
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Glances will be build with Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

10
.gitignore vendored
View File

@ -23,6 +23,7 @@ local.properties
.classpath
.settings/
.loadpath
.ipynb_checkpoints/
# External tool builders
.externalToolBuilders/
@ -63,7 +64,14 @@ bower_components/
/*_source.tar.bz2
# Virtual env
/venv*/
.venv-uv/
.venv/
uv.lock
.python-version
# Test
.coverage
tests-data/issues/*/config/
# Local SSL certificates
glances.local*.pem

View File

@ -1,22 +1,107 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: check-ast
- id: check-docstring-first
- id: check-json
- id: check-merge-conflict
- id: check-shebang-scripts-are-executable
- id: check-toml
- id: check-yaml
- id: debug-statements
- id: detect-private-key
- id: mixed-line-ending
- id: requirements-txt-fixer
- id: gitleaks
name: "🔒 security · Detect hardcoded secrets"
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
rev: v0.14.10
hooks:
- id: ruff-format
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
name: "🐍 python · Formatter with Ruff"
types_or: [ python, pyi ]
args: [ --config, './pyproject.toml' ]
- id: ruff-check
name: "🐍 python · Linter with Ruff"
types_or: [ python, pyi ]
args: [ --fix, --exit-non-zero-on-fix, --config, './pyproject.toml' ]
# - repo: https://github.com/RobertCraigie/pyright-python
# rev: v1.1.391
# hooks:
# - id: pyright
# name: "🐍 python · Check types"
# - repo: https://github.com/biomejs/pre-commit
# rev: "v2.3.7"
# hooks:
# - id: biome-check
# name: "🟨 javascript · Lint, format, and safe fixes with Biome"
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.35.0
hooks:
- id: check-github-workflows
name: "🐙 github-actions · Validate gh workflow files"
args: ["--verbose"]
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1
hooks:
- id: shellcheck
name: "🐚 shell · Lint shell scripts"
- repo: https://github.com/openstack/bashate
rev: 2.1.1
hooks:
- id: bashate
name: "🐚 shell · Check shell script code style"
entry: bashate --error . --ignore=E006
# - repo: https://github.com/mrtazz/checkmake.git
# rev: 0.2.2
# hooks:
# - id: checkmake
# name: "🐮 Makefile · Lint Makefile"
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-executables-have-shebangs
name: "📁 filesystem/⚙️ exec · Verify shebang presence"
- id: check-shebang-scripts-are-executable
name: "📁 filesystem/⚙️ exec · Verify script permissions"
- id: check-case-conflict
name: "📁 filesystem/📝 names · Check case sensitivity"
- id: destroyed-symlinks
name: "📁 filesystem/🔗 symlink · Detect broken symlinks"
- id: check-merge-conflict
name: "🌳 git · Detect conflict markers"
- id: forbid-new-submodules
name: "🌳 git · Prevent submodule creation"
- id: no-commit-to-branch
name: "🌳 git · Protect main branches"
args: ["--branch", "main", "--branch", "master"]
- id: check-added-large-files
name: "🌳 git · Block large file commits"
args: ['--maxkb=5000']
- id: check-ast
name: "🐍 python/🔍 quality · Validate Python AST"
- id: check-docstring-first
name: "🐍 python/📝 style · Enforce docstring at top"
- id: check-json
name: "📄 formats/json · Validate JSON files"
- id: check-shebang-scripts-are-executable
name: "📁 filesystem/⚙️ exec · Ensure scripts are executable"
- id: check-toml
name: "📄 formats/toml · Validate TOML files"
- id: check-yaml
name: "📄 formats/yaml · Validate YAML syntax"
- id: debug-statements
name: "🐍 python/🪲 debug · Detect debug statements"
- id: detect-private-key
name: "🔐 security · Detect private keys"
- id: mixed-line-ending
name: "📄 text/↩️ newline · Normalize line endings"
- id: requirements-txt-fixer
name: "🐍 python/📦 deps · Sort requirements.txt"
- repo: local
hooks:
- id: find-duplicate-lines
name: "❗local script · Find duplicate lines at the end of file"
entry: bash tests-data/tools/find-duplicate-lines.sh
language: system
types: [python]
pass_filenames: false

View File

@ -31,4 +31,4 @@ sphinx:
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
python:
install:
- requirements: doc-requirements.txt
- requirements: dev-requirements.txt

View File

@ -13,6 +13,9 @@ PGP Public key: gpg --keyserver pgp.mit.edu --recv-keys 0xba43c11f2c8b4347
RazCrimson (maintainer of the Glances project)
https://github.com/RazCrimson
Ariel Otibili (aka) ariel-anieli (for the huge work on code quality)
https://github.com/ariel-anieli
Alessio Sergi (aka) Al3hex (thanks you for the great job on this project)
https://twitter.com/al3hex
https://github.com/asergi

View File

@ -3,9 +3,12 @@ include CONTRIBUTING.md
include COPYING
include NEWS.rst
include README.rst
include README-pypi.rst
include SECURITY.md
include conf/glances.conf
include conf/fetch-templates/*.jinja
include requirements.txt
include all-requirements.txt
recursive-include docs *
recursive-include glances *.py
recursive-include glances/outputs/static *

253
Makefile
View File

@ -1,17 +1,6 @@
PORT ?= 8008
venv_full:= venv/bin
venv_min := venv-min/bin
CONF := conf/glances.conf
PIP := $(venv_full)/pip
PYTHON := $(venv_full)/python
PYTEST := $(venv_full)/python -m pytest
LASTTAG = $(shell git describe --tags --abbrev=0)
VENV_TYPES := full min
VENV_PYTHON := $(VENV_TYPES:%=venv-%-python)
VENV_UPG := $(VENV_TYPES:%=venv-%-upgrade)
VENV_DEPS := $(VENV_TYPES:%=venv-%)
VENV_INST_UPG := $(VENV_DEPS) $(VENV_UPG)
PORT ?= 8008
CONF := conf/glances.conf
LASTTAG = $(shell git describe --tags --abbrev=0)
IMAGES_TYPES := full minimal
DISTROS := alpine ubuntu
@ -26,109 +15,155 @@ PODMAN_SOCK ?= /run/user/$(shell id -u)/podman/podman.sock
DOCKER_SOCK ?= /var/run/docker.sock
DOCKER_SOCKS := -v $(PODMAN_SOCK):$(PODMAN_SOCK):ro -v $(DOCKER_SOCK):$(DOCKER_SOCK):ro
DOCKER_OPTS := --rm -e TZ="${TZ}" -e GLANCES_OPT="" --pid host --network host
UV_RUN := .venv-uv/bin/uv
# if the command is only `make`, the default tasks will be the printing of the help.
.DEFAULT_GOAL := help
.PHONY: help test docs docs-server venv venv-min
.PHONY: help test docs docs-server venv requirements profiling docker all clean all test
help: ## List all make commands available
@grep -E '^[\.a-zA-Z_%-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk -F ":" '{print $1}' | \
grep -v % | \
sed 's/\\//g' | \
sort | \
grep -v % | sed 's/\\//g' | sort | \
awk 'BEGIN {FS = ":[^:]*?##"}; {printf "\033[1;34mmake %-50s\033[0m %s\n", $$1, $$2}'
# ===================================================================
# Virtualenv
# ===================================================================
venv-%-upgrade: UPGRADE = --upgrade
# install-uv: ## Instructions to install the UV tool
# @echo "Install the UV tool (https://astral.sh/uv/)"
# @echo "Please install the UV tool manually"
# @echo "For example with: curl -LsSf https://astral.sh/uv/install.sh | sh"
# @echo "Or via a package manager of your distribution"
# @echo "For example for Snap: snap install astral-uv"
define DEFINE_VARS_FOR_TYPE
venv-$(TYPE) venv-$(TYPE)-upgrade: VIRTUAL_ENV = $(venv_$(TYPE))
endef
install-uv: ## Install UV tool in a specific virtualenv
python3 -m venv .venv-uv
.venv-uv/bin/pip install uv
$(foreach TYPE,$(VENV_TYPES),$(eval $(DEFINE_VARS_FOR_TYPE)))
upgrade-uv: ## Upgrade the UV tool
.venv-uv/bin/pip install --upgrade pip
.venv-uv/bin/pip install --upgrade uv
$(VENV_PYTHON): venv-%-python:
virtualenv -p python3 $(if $(filter full,$*),venv,venv-$*)
venv: ## Create the virtualenv with all dependencies
$(UV_RUN) sync --all-extras --no-group dev
$(VENV_INST_UPG): venv-%:
$(if $(UPGRADE),$(VIRTUAL_ENV)/pip install --upgrade pip,)
$(foreach REQ,$(REQS), $(VIRTUAL_ENV)/pip install $(UPGRADE) -r $(REQ);)
$(if $(PRE_COMMIT),$(VIRTUAL_ENV)/pre-commit install --hook-type pre-commit,)
venv-upgrade venv-switch-to-full: ## Upgrade the virtualenv with all dependencies
$(UV_RUN) sync --upgrade --all-extras
venv-python: $(VENV_PYTHON) ## Install all Python 3 venv
venv: $(VENV_DEPS) ## Install all Python 3 dependencies
venv-upgrade: $(VENV_UPG) ## Upgrade all Python 3 dependencies
venv-min: ## Create the virtualenv with minimal dependencies
$(UV_RUN) sync
# For full installation (with optional dependencies)
venv-upgrade-min venv-switch-to-min: ## Upgrade the virtualenv with minimal dependencies
$(UV_RUN) sync --upgrade
venv-full venv-full-upgrade: REQS = requirements.txt optional-requirements.txt dev-requirements.txt doc-requirements.txt
venv-clean: ## Remove the virtualenv
rm -rf .venv
venv-full-python: ## Install Python 3 venv
venv-full: venv-python ## Install Python 3 run-time
venv-full-upgrade: ## Upgrade Python 3 run-time dependencies
venv-full: PRE_COMMIT = 1
venv-dev: ## Create the virtualenv with dev dependencies
$(UV_RUN) sync --dev --all-extras
$(UV_RUN) run pre-commit install --hook-type pre-commit
# For minimal installation (without optional dependencies)
# ===================================================================
# Requirements
#
# Note: the --no-hashes option should be used because pip (in CI) has
# issues with hashes.
# ===================================================================
venv-min venv-min-upgrade: REQS = requirements.txt dev-requirements.txt doc-requirements.txt
requirements-min: ## Generate the requirements.txt files (minimal dependencies)
$(UV_RUN) export --no-emit-workspace --no-hashes --no-group dev --output-file requirements.txt
venv-min-python: ## Install Python 3 venv minimal
venv-min: venv-min-python ## Install Python 3 minimal run-time dependencies
venv-min-upgrade: ## Upgrade Python 3 minimal run-time dependencies
requirements-all: ## Generate the all-requirements.txt files (all dependencies)
$(UV_RUN) export --no-emit-workspace --no-hashes --all-extras --no-group dev --output-file all-requirements.txt
requirements-docker: ## Generate the docker-requirements.txt files (Docker specific dependencies)
$(UV_RUN) export --no-emit-workspace --no-hashes --no-group dev --extra containers --extra web --output-file docker-requirements.txt
requirements-dev: ## Generate the dev-requirements.txt files (dev dependencies)
$(UV_RUN) export --no-hashes --only-dev --output-file dev-requirements.txt
requirements: requirements-min requirements-all requirements-dev requirements-docker ## Generate all the requirements files
requirements-upgrade: venv-upgrade requirements ## Upgrade the virtualenv and regenerate all the requirements files
# ===================================================================
# Tests
# ===================================================================
test: ## Run All unit tests
$(PYTEST)
$(UV_RUN) run pytest
test-core: ## Run Core unit tests
$(PYTEST) tests/test_core.py
$(UV_RUN) run pytest tests/test_core.py
test-api: ## Run API unit tests
$(UV_RUN) run pytest tests/test_api.py
test-memoryleak: ## Run Memory-leak unit tests
$(PYTEST) tests/test_memoryleak.py
$(UV_RUN) run pytest tests/test_memoryleak.py
test-perf: ## Run Perf unit tests
$(PYTEST) tests/test_perf.py
$(UV_RUN) run pytest tests/test_perf.py
test-restful: ## Run Restful API unit tests
$(PYTEST) tests/test_restful.py
$(UV_RUN) run pytest tests/test_restful.py
test-webui: ## Run WebUI unit tests
$(PYTEST) tests/test_webui.py
$(UV_RUN) run pytest tests/test_webui.py
test-xmlrpc: ## Run XMLRPC API unit tests
$(PYTEST) tests/test_xmlrpc.py
$(UV_RUN) run pytest tests/test_xmlrpc.py
test-with-upgrade: venv-upgrade test ## Upgrade deps and run unit tests
test-min: ## Run core unit tests in minimal environment
$(venv_min)/python -m pytest tests/test_core.py
test-export-csv: ## Run interface tests with CSV
/bin/bash ./tests/test_export_csv.sh
test-min-with-upgrade: venv-min-upgrade ## Upgrade deps and run unit tests in minimal environment
$(venv_min)/python -m pytest tests/test_core.py
test-export-json: ## Run interface tests with JSON
/bin/bash ./tests/test_export_json.sh
test-export-influxdb-v1: ## Run interface tests with InfluxDB version 1 (Legacy)
/bin/bash ./tests/test_export_influxdb_v1.sh
test-export-influxdb-v3: ## Run interface tests with InfluxDB version 3 (Core)
/bin/bash ./tests/test_export_influxdb_v3.sh
test-export-timescaledb: ## Run interface tests with TimescaleDB
/bin/bash ./tests/test_export_timescaledb.sh
test-export-nats: ## Run interface tests with NATS
/bin/bash ./tests/test_export_nats.sh
test-exports: test-export-csv test-export-json test-export-influxdb-v1 test-export-influxdb-v3 test-export-timescaledb test-export-nats ## Tests all exports
# ===================================================================
# Linters, profilers and cyber security
# ===================================================================
pre-commit: ## Run pre-commit hooks
$(UV_RUN) run pre-commit run --all-files
find-duplicate-lines: ## Search for duplicate lines in files
/bin/bash tests-data/tools/find-duplicate-lines.sh
format: ## Format the code
$(venv_full)/python -m ruff format .
$(UV_RUN) run ruff format .
lint: ## Lint the code.
$(venv_full)/python -m ruff check . --fix
$(UV_RUN) run ruff check . --fix
lint-readme: ## Lint the main README.rst file
$(UV_RUN) run rstcheck README.rst
$(UV_RUN) run rstcheck README-pypi.rst
codespell: ## Run codespell to fix common misspellings in text files
$(venv_full)/codespell -S .git,./docs/_build,./Glances.egg-info,./venv*,./glances/outputs,*.svg -L hart,bu,te,statics -w
$(UV_RUN) run codespell -S .git,./docs/_build,./Glances.egg-info,./venv*,./glances/outputs,*.svg -L hart,bu,te,statics -w
semgrep: ## Run semgrep to find bugs and enforce code standards
$(venv_full)/semgrep scan --config=auto
$(UV_RUN) run semgrep scan --config=auto
profiling-%: SLEEP = 3
profiling-%: TIMES = 30
@ -142,27 +177,27 @@ endef
profiling-gprof: CPROF = glances.cprof
profiling-gprof: ## Callgraph profiling (need "apt install graphviz")
$(DISPLAY-BANNER)
$(PYTHON) -m cProfile -o $(CPROF) run-venv.py -C $(CONF) --stop-after $(TIMES)
$(venv_full)/gprof2dot -f pstats $(CPROF) | dot -Tsvg -o $(OUT_DIR)/glances-cgraph.svg
$(UV_RUN) run python -m cProfile -o $(CPROF) run-venv.py -C $(CONF) --stop-after $(TIMES)
$(UV_RUN) run gprof2dot -f pstats $(CPROF) | dot -Tsvg -o $(OUT_DIR)/glances-cgraph.svg
rm -f $(CPROF)
profiling-pyinstrument: ## PyInstrument profiling
$(DISPLAY-BANNER)
$(PIP) install pyinstrument
$(PYTHON) -m pyinstrument -r html -o $(OUT_DIR)/glances-pyinstrument.html -m glances -C $(CONF) --stop-after $(TIMES)
$(UV_RUN) add pyinstrument
$(UV_RUN) run pyinstrument -r html -o $(OUT_DIR)/glances-pyinstrument.html -m glances -C $(CONF) --stop-after $(TIMES)
profiling-pyspy: ## Flame profiling
$(DISPLAY-BANNER)
$(venv_full)/py-spy record -o $(OUT_DIR)/glances-flame.svg -d 60 -s -- $(PYTHON) run-venv.py -C $(CONF) --stop-after $(TIMES)
$(UV_RUN) run py-spy record -o $(OUT_DIR)/glances-flame.svg -d 60 -s -- .venv-uv/bin/uvrun python run-venv.py -C $(CONF) --stop-after $(TIMES)
profiling: profiling-gprof profiling-pyinstrument profiling-pyspy ## Profiling of the Glances software
trace-malloc: ## Trace the malloc() calls
@echo "Malloc test is running, please wait ~30 secondes..."
$(PYTHON) -m glances -C $(CONF) --trace-malloc --stop-after 15 --quiet
$(UV_RUN) run python -m glances -C $(CONF) --trace-malloc --stop-after 15 --quiet
memory-leak: ## Profile memory leaks
$(PYTHON) -m glances -C $(CONF) --memory-leak
$(UV_RUN) run python -m glances -C $(CONF) --memory-leak
memory-profiling: TIMES = 2400
memory-profiling: PROFILE = mprofile_*.dat
@ -171,29 +206,37 @@ memory-profiling: ## Profile memory usage
@echo "It's a very long test (~4 hours)..."
rm -f $(PROFILE)
@echo "1/2 - Start memory profiling with the history option enable"
$(venv_full)/mprof run -T 1 -C run-venv.py -C $(CONF) --stop-after $(TIMES) --quiet
$(venv_full)/mprof plot --output $(OUT_DIR)/glances-memory-profiling-with-history.png
$(UV_RUN) run mprof run -T 1 -C run-venv.py -C $(CONF) --stop-after $(TIMES) --quiet
$(UV_RUN) run mprof plot --output $(OUT_DIR)/glances-memory-profiling-with-history.png
rm -f $(PROFILE)
@echo "2/2 - Start memory profiling with the history option disable"
$(venv_full)/mprof run -T 1 -C run-venv.py -C $(CONF) --disable-history --stop-after $(TIMES) --quiet
$(venv_full)/mprof plot --output $(OUT_DIR)/glances-memory-profiling-without-history.png
$(UV_RUN) run mprof run -T 1 -C run-venv.py -C $(CONF) --disable-history --stop-after $(TIMES) --quiet
$(UV_RUN) run mprof plot --output $(OUT_DIR)/glances-memory-profiling-without-history.png
rm -f $(PROFILE)
# Trivy installation: https://aquasecurity.github.io/trivy/latest/getting-started/installation/
trivy: ## Run Trivy to find vulnerabilities in container images
trivy fs .
trivy: ## Run Trivy to find vulnerabilities
$(UV_RUN) run trivy fs ./glances/
bandit: ## Run Bandit to find vulnerabilities
$(UV_RUN) run bandit glances -r
# ===================================================================
# Docs
# ===================================================================
docs: ## Create the documentation
$(PYTHON) -m glances -C $(CONF) --api-doc > ./docs/api.rst
$(UV_RUN) run python -m glances -C $(CONF) --api-doc > ./docs/api/python.rst
$(UV_RUN) run python ./generate_openapi.py
$(UV_RUN) run python -m glances -C $(CONF) --api-restful-doc > ./docs/api/restful.rst
cd docs && ./build.sh && cd ..
docs-server: docs ## Start a Web server to serve the documentation
(sleep 2 && sensible-browser "http://localhost:$(PORT)") &
cd docs/_build/html/ && ../../../venv/bin/python -m http.server $(PORT)
cd docs/_build/html/ && .venv-uv/bin/uvrun python -m http.server $(PORT)
docs-jupyter: ## Start Jupyter Notebook
$(UV_RUN) run --with jupyter jupyter lab
release-note: ## Generate release note
git --no-pager log $(LASTTAG)..HEAD --first-parent --pretty=format:"* %s"
@ -210,23 +253,28 @@ install: ## Open a Web Browser to the installation procedure
webui webui%: DIR = glances/outputs/static/
webui: ## Build the Web UI
$(PYTHON) -c 'import json; from glances.outputs.glances_curses import _GlancesCurses; print(json.dumps({ "leftMenu": [p for p in _GlancesCurses._left_sidebar if p != "now"]}, indent=4))' > ./glances/outputs/static/js/uiconfig.json
webui-gen-config: ## Generate the Web UI config file
$(UV_RUN) run python ./generate_webui_conf.py > ./glances/outputs/static/js/uiconfig.json
webui: webui-gen-config ## Build the Web UI
cd $(DIR) && npm ci && npm run build
webui-audit: ## Audit the Web UI
cd $(DIR) && npm audit
webui-audit-fix: ## Fix audit the Web UI
webui-audit-fix: webui-gen-config ## Fix audit the Web UI
cd $(DIR) && npm audit fix && npm ci && npm run build
webui-update: webui-gen-config ## Update JS dependencies
cd $(DIR) && npm update --save && npm ci && npm run build
# ===================================================================
# Packaging
# ===================================================================
flatpak: venv-upgrade ## Generate FlatPack JSON file
git clone https://github.com/flatpak/flatpak-builder-tools.git
$(PYTHON) ./flatpak-builder-tools/pip/flatpak-pip-generator glances
$(UV_RUN) run python ./flatpak-builder-tools/pip/flatpak-pip-generator glances
rm -rf ./flatpak-builder-tools
@echo "Now follow: https://github.com/flathub/flathub/wiki/App-Submission"
@ -260,33 +308,33 @@ docker-ubuntu-full: ## Generate local docker image (Ubuntu full)
docker-ubuntu-minimal: ## Generate local docker image (Ubuntu minimal)
docker-ubuntu-dev: ## Generate local docker image (Ubuntu dev)
trivy-docker: ## Run Trivy to find vulnerabilities in Docker images
$(UV_RUN) run trivy image glances:local-alpine-full
$(UV_RUN) run trivy image glances:local-alpine-minimal
$(UV_RUN) run trivy image glances:local-ubuntu-full
$(UV_RUN) run trivy image glances:local-ubuntu-minimal
# ===================================================================
# Run
# ===================================================================
run: ## Start Glances in console mode (also called standalone)
$(PYTHON) -m glances -C $(CONF)
$(UV_RUN) run python -m glances -C $(CONF)
run-debug: ## Start Glances in debug console mode (also called standalone)
$(PYTHON) -m glances -C $(CONF) -d
$(UV_RUN) run python -m glances -C $(CONF) -d
run-local-conf: ## Start Glances in console mode with the system conf file
$(PYTHON) -m glances
$(UV_RUN) run python -m glances
run-local-conf-hide-public: ## Start Glances in console mode with the system conf file and hide public information
$(PYTHON) -m glances --hide-public-info
run-min: ## Start minimal Glances in console mode (also called standalone)
$(venv_min)/python -m glances -C $(CONF)
run-min-debug: ## Start minimal Glances in debug console mode (also called standalone)
$(venv_min)/python -m glances -C $(CONF) -d
run-min-local-conf: ## Start minimal Glances in console mode with the system conf file
$(venv_min)/python -m glances
$(UV_RUN) run python -m glances --hide-public-info
run-like-htop: ## Start Glances with the same features than Htop
$(venv_min)/python -m glances --disable-plugin network,ports,wifi,connections,diskio,fs,irq,folders,raid,smart,sensors,vms,containers,ip,amps --disable-left-sidebar
$(UV_RUN) run python -m glances --disable-plugin network,ports,wifi,connections,diskio,fs,irq,folders,raid,smart,sensors,vms,containers,ip,amps --disable-left-sidebar
run-fetch: ## Start Glances in fetch mode
$(UV_RUN) run python -m glances --fetch
$(DOCKER_RUNTIMES): run-docker-%:
$(DOCKER_RUN) $(DOCKER_OPTS) $(DOCKER_SOCKS) -it glances:local-$*
@ -298,32 +346,35 @@ run-docker-ubuntu-minimal: ## Start Glances Ubuntu Docker minimal in console mod
run-docker-ubuntu-full: ## Start Glances Ubuntu Docker full in console mode
run-docker-ubuntu-dev: ## Start Glances Ubuntu Docker dev in console mode
generate-ssl: ## Generate local and sel signed SSL certificates for dev (need mkcert)
mkcert glances.local localhost 120.0.0.1 0.0.0.0
run-webserver: ## Start Glances in Web server mode
$(PYTHON) -m glances -C $(CONF) -w
$(UV_RUN) run python -m glances -C $(CONF) -w
run-webserver-local-conf: ## Start Glances in Web server mode with the system conf file
$(PYTHON) -m glances -w
$(UV_RUN) run python -m glances -w
run-webserver-local-conf-hide-public: ## Start Glances in Web server mode with the system conf file and hide public info
$(PYTHON) -m glances -w --hide-public-info
$(UV_RUN) run python -m glances -w --hide-public-info
run-restapiserver: ## Start Glances in REST API server mode
$(PYTHON) -m glances -C $(CONF) -w --disable-webui
$(UV_RUN) run python -m glances -C $(CONF) -w --disable-webui
run-server: ## Start Glances in server mode (RPC)
$(PYTHON) -m glances -C $(CONF) -s
$(UV_RUN) run python -m glances -C $(CONF) -s
run-client: ## Start Glances in client mode (RPC)
$(PYTHON) -m glances -C $(CONF) -c localhost
$(UV_RUN) run python -m glances -C $(CONF) -c localhost
run-browser: ## Start Glances in browser mode (RPC)
$(PYTHON) -m glances -C $(CONF) --browser
$(UV_RUN) run python -m glances -C $(CONF) --browser
run-web-browser: ## Start Web Central Browser
$(PYTHON) -m glances -C $(CONF) -w --browser
$(UV_RUN) run python -m glances -C $(CONF) -w --browser
run-issue: ## Start Glances in issue mode
$(PYTHON) -m glances -C $(CONF) --issue
$(UV_RUN) run python -m glances -C $(CONF) --issue
run-multipass: ## Install and start Glances in a VM (only available on Ubuntu with multipass already installed)
multipass launch -n glances-on-lts lts
@ -333,4 +384,4 @@ run-multipass: ## Install and start Glances in a VM (only available on Ubuntu wi
multipass delete glances-on-lts
show-version: ## Show Glances version number
$(PYTHON) -m glances -C $(CONF) -V
$(UV_RUN) run python -m glances -C $(CONF) -V

162
NEWS.rst
View File

@ -1,6 +1,150 @@
==============================================================================
Glances ChangeLog
============================================================================
==============================================================================
=============
Version 4.4.1
=============
Bug corrected:
* Restful API issue after a while (stats are no more updated) #3333
=============
Version 4.4.0
=============
Breaking changes:
* A new Python API is now available to use Glances as a Python lib in your hown development #3237
* In the process list, the long command line is now truncated by default. Use the arrow keys to show the full command line. SHIFT + arrow keys are used to switch between column sorts (TUI).
* Prometheus export format is now more user friendly (see detail in #3283)
Enhancements:
* Make a Glances API in order to use Glances as a Python lib #3237
* Add a new --fetch (neofetch like) option to display a snapshot of the current system status #3281
* Show used port in container section #2054
* Show long command line with arrow key #1553
* Sensors plugin refresh by default every 10 seconds
* Do not call update if a call is done to a specific plugin through the API #3033
* [UI] Process virtual memory display can be disable by configuration #3299
* Choose between used or available in the mem plugin #3288
* [Experimental] Add export to DuckDB database #3205
* Add Disk I/O Latency stats #1070
* Filter fields to export #3258
* Remove .keys() from loops over dicts #3253
* Remove iterator helpers #3252
Bug corrected:
* [MACOS] Glances not showing Processes on MacOS #3100
* Last dev build broke Homepage API calls ? only 1 widget still working #3322
* Cloud plugin always generate communication with 169.254.169.254, even if the plugin is disabled #3316
* API response delay (3+ minutes) when VMs are running #3317
* [WINDOWS] Glances do not display CPU stat correctly #3155
* Glances hangs if network device (NFS) is no available #3290
* Fix prometheus export format #3283
* Issue #3279 zfs cache and memory math issues #3289
* [MACOS] Glances crashes when I try to filter #3266
* Glances hang when killing process with muliple CTRL-C #3264
* Issues after disabling system and processcount plugins #3248
* Headers missing from predefined fields in TUI browser machine list #3250
* Add another check for the famous Netifaces issue - Related to #3219
* Key error 'type' in server_list_static.py (load_server_list) #3247
Continious integration and documentation:
* Glances now use uv for the dev environment #3025
* Glances is compatible with Python 3.14 #3319
* Glances provides requirements files with specific versions for each release
* Requirements files are now generated dynamically with the make requirements or requirements-upgrade target
* Add duplicate line check in pre-commit (strange behavor with some VScode extension)
* Solve issue with multiprocessing exception with Snap package
* Add a test script for identify CPU consumption of sensor plugin
* Refactor port to take into account netifaces2
* Correct issue with Chrome driver in WebUI unit test
* Upgrade export test with InfluxDB 1.12
* Fix typo of --export-process-filter help message #3314
* In the outdated feature, catch error message if Pypi server not reachable
* Add unit test for auto_unit
* Label error in docs #3286
* Put WebUI conf generator in a dedicated script
* Refactor the Makefile to generate WebUI config file for all webui targets
* Update sensors documentation #3275
* Update docker compose env quote #3273
* Update docker-compose.yml #3249
* Update API doc generation
* Update README with nice icons #3236
* Add documentation for WebUI test
Thanks to all contributors and bug reporters !
Special thanks to:
- Adi
- Bennett Kanuka
- Tim Potter
- Ariel Otilibili
- Boris Okassa
- Lawrence
- Shohei YOSHIDA
- jmwallach
- korn3r
=============
Version 4.3.3
=============
Bug corrected:
* Something in 4.3.2 broke the home assistant add-on for Glances #3238
Thanks to the FastAPI and Home Assistant community for the support.
=============
Version 4.3.2
=============
Enhancements:
* Add stats about running VMS (qemu/libvirt/kvm support through virsh) #1531
* Add support for InfluxDB 3 Core #3182
* (postgre)SQL export support / TimeScaleDB #2814
* CSV column name now include the plugin name - Related to #2394
* Make all results from amps plugins exportable #2394
* Make --stdout (csv and json) compliant with client/server mode #3235
* API history endpoints shows times without timezone #3218
* FR: Sort Sensors my name in proper number order #3132
* In the FS module, do not display threshold for volume mounted in 'ro' (read-only) #3143
* Add a new field in the process list to identifie Zombie process #3178
* Update plugin containers display and order #3186
* Implement a basic memory cache with TTL for API call (set to ~1 second) #3202
* Add container inactive_file & limit to InfluxDB2 export #3206
Bug corrected:
* [GPU] AMD Plugin: Operation not permitted #3125
* Container memory stats not displayed #3142
* [WEBUI] Irix mode (per core instead of per CPU percentage) not togglable #3158
* Related to iteritems, itervalues, and iterkeys are not more needed in Python 3 #3181
* Glances Central Browser should use name instead of IP adress for redirection #3103
* Glances breaks if Podman container is started while it is running #3199
Continious integration and documentation:
* Add a new option --print-completion to generate shell tab completion - #3111
* Improve Restful API documentation embeded in FastAPI #2632
* Upgrade JS libs #3147
* Improve unittest for CSV export #3150
* Improve unittest for InfluxDB plugin #3149
* Code refactoring - Rename plugin class to <Plugin name>Plugin instead of PluginModel #3169
* Refactor code to limit the complexity of update_views method in plugins #3171
Thanks to all contributors and bug reporters !
Special thanks to:
- Ariel Otilibili
- kenrmayfield
=============
Version 4.3.1
@ -369,7 +513,7 @@ See release note in Wiki format: https://github.com/nicolargo/glances/wiki/Glanc
**BREAKING CHANGES:**
* The minimal Python version is 3.8
* The Glances API version 3 is replaced by the version 4. So Restfull API URL is now /api/4/ #2610
* The Glances API version 3 is replaced by the version 4. So Restful API URL is now /api/4/ #2610
* Alias definition change in the configuration file #1735
Glances version 3.x and lower:
@ -394,9 +538,9 @@ Minimal requirements for Glances version 4 are:
* packaging
* ujson
* pydantic
* fastapi (for WebUI / RestFull API)
* uvicorn (for WebUI / RestFull API)
* jinja2 (for WebUI / RestFull API)
* fastapi (for WebUI / RestFul API)
* uvicorn (for WebUI / RestFul API)
* jinja2 (for WebUI / RestFul API)
Majors changes between Glances version 3 and version 4:
@ -456,7 +600,7 @@ Bug corrected:
CI and documentation:
* New logo for Glances version 4.0 #2713
* Update api.rst documentation #2496
* Update api-restful.rst documentation #2496
* Change Renovate config #2729
* Docker compose password unrecognized arguments when applying docs #2698
* Docker includes OS Release Volume mount info #2473
@ -834,7 +978,7 @@ Bugs corrected:
* Threading.Event.isSet is deprecated in Python 3.10 #2017
* Fix code scanning alert - Clear-text logging of sensitive information security #2006
* The gpu temperature unit are displayed incorrectly in web ui bug #2002
* Doc for 'alert' Restfull/JSON API response documentation #1994
* Doc for 'alert' Restful/JSON API response documentation #1994
* Show the spinning state of a disk documentation #1993
* Web server status check endpoint enhancement #1988
* --time parameter being ignored for client/server mode bug #1978
@ -929,7 +1073,7 @@ Bugs corrected:
* [3.2.0/3.2.1] keybinding not working anymore #1904
* InfluxDB/InfluxDB2 Export object has no attribute hostname #1899
Documentation: The "make docs" generate RestFull/API documentation file.
Documentation: The "make docs" generate RestFul/API documentation file.
===============
Version 3.2.1
@ -1956,7 +2100,7 @@ Version 2.1
* Add Glances log message (in the /tmp/glances.log file)
The default log level is INFO, you can switch to the DEBUG mode using the -d option on the command line.
* Add RESTful API to the Web server mode
RESTful API doc: https://github.com/nicolargo/glances/wiki/The-Glances-RESTFULL-JSON-API
RESTful API doc: https://github.com/nicolargo/glances/wiki/The-Glances-RESTFUL-JSON-API
* Improve SNMP fallback mode for Cisco IOS, VMware ESXi
* Add --theme-white feature to optimize display for white background
* Experimental history feature (--enable-history option on the command line)

385
README-pypi.rst Normal file
View File

@ -0,0 +1,385 @@
Glances 🌟
==========
**Glances** is an open-source system cross-platform monitoring tool.
It allows real-time monitoring of various aspects of your system such as
CPU, memory, disk, network usage etc. It also allows monitoring of running processes,
logged in users, temperatures, voltages, fan speeds etc.
It also supports container monitoring, it supports different container management
systems such as Docker, LXC. The information is presented in an easy to read dashboard
and can also be used for remote monitoring of systems via a web interface or command
line interface. It is easy to install and use and can be customized to show only
the information that you are interested in.
In client/server mode, remote monitoring could be done via terminal,
Web interface or API (XML-RPC and RESTful).
Stats can also be exported to files or external time/value databases, CSV or direct
output to STDOUT.
Glances is written in Python and uses libraries to grab information from
your system. It is based on an open architecture where developers can
add new plugins or exports modules.
Usage 👋
========
For the standalone mode, just run:
.. code-block:: console
$ glances
.. image:: https://github.com/nicolargo/glances/raw/refs/heads/master/docs/_static/glances-responsive-webdesign.png
For the Web server mode, run:
.. code-block:: console
$ glances -w
and enter the URL ``http://<ip>:61208`` in your favorite web browser.
In this mode, a HTTP/Restful API is exposed, see document `RestfulApi`_ for more details.
.. image:: https://github.com/nicolargo/glances/raw/refs/heads/master/docs/_static/screenshot-web.png
For the client/server mode (remote monitoring through XML-RPC), run the following command on the server:
.. code-block:: console
$ glances -s
and this one on the client:
.. code-block:: console
$ glances -c <ip>
You can also detect and display all Glances servers available on your
network (or defined in the configuration file) in TUI:
.. code-block:: console
$ glances --browser
or WebUI:
.. code-block:: console
$ glances -w --browser
It possible to display raw stats on stdout:
.. code-block:: console
$ glances --stdout cpu.user,mem.used,load
cpu.user: 30.7
mem.used: 3278204928
load: {'cpucore': 4, 'min1': 0.21, 'min5': 0.4, 'min15': 0.27}
cpu.user: 3.4
mem.used: 3275251712
load: {'cpucore': 4, 'min1': 0.19, 'min5': 0.39, 'min15': 0.27}
...
or in a CSV format thanks to the stdout-csv option:
.. code-block:: console
$ glances --stdout-csv now,cpu.user,mem.used,load
now,cpu.user,mem.used,load.cpucore,load.min1,load.min5,load.min15
2018-12-08 22:04:20 CEST,7.3,5948149760,4,1.04,0.99,1.04
2018-12-08 22:04:23 CEST,5.4,5949136896,4,1.04,0.99,1.04
...
or in a JSON format thanks to the stdout-json option (attribute not supported in this mode in order to have a real JSON object in output):
.. code-block:: console
$ glances --stdout-json cpu,mem
cpu: {"total": 29.0, "user": 24.7, "nice": 0.0, "system": 3.8, "idle": 71.4, "iowait": 0.0, "irq": 0.0, "softirq": 0.0, "steal": 0.0, "guest": 0.0, "guest_nice": 0.0, "time_since_update": 1, "cpucore": 4, "ctx_switches": 0, "interrupts": 0, "soft_interrupts": 0, "syscalls": 0}
mem: {"total": 7837949952, "available": 2919079936, "percent": 62.8, "used": 4918870016, "free": 2919079936, "active": 2841214976, "inactive": 3340550144, "buffers": 546799616, "cached": 3068141568, "shared": 788156416}
...
Last but not least, you can use the fetch mode to get a quick look of a machine:
.. code-block:: console
$ glances --fetch
Results look like this:
.. image:: https://github.com/nicolargo/glances/raw/refs/heads/master/docs/_static/screenshot-fetch.png
Use Glances as a Python library 📚
==================================
You can access the Glances API by importing the `glances.api` module and creating an
instance of the `GlancesAPI` class. This instance provides access to all Glances plugins
and their fields. For example, to access the CPU plugin and its total field, you can
use the following code:
.. code-block:: python
>>> from glances import api
>>> gl = api.GlancesAPI()
>>> gl.cpu
{'cpucore': 16,
'ctx_switches': 1214157811,
'guest': 0.0,
'idle': 91.4,
'interrupts': 991768733,
'iowait': 0.3,
'irq': 0.0,
'nice': 0.0,
'soft_interrupts': 423297898,
'steal': 0.0,
'syscalls': 0,
'system': 5.4,
'total': 7.3,
'user': 3.0}
>>> gl.cpu["total"]
7.3
>>> gl.mem["used"]
12498582144
>>> gl.auto_unit(gl.mem["used"])
11.6G
If the stats return a list of items (like network interfaces or processes), you can
access them by their name:
.. code-block:: python
>>> gl.network.keys()
['wlp0s20f3', 'veth33b370c', 'veth19c7711']
>>> gl.network["wlp0s20f3"]
{'alias': None,
'bytes_all': 362,
'bytes_all_gauge': 9242285709,
'bytes_all_rate_per_sec': 1032.0,
'bytes_recv': 210,
'bytes_recv_gauge': 7420522678,
'bytes_recv_rate_per_sec': 599.0,
'bytes_sent': 152,
'bytes_sent_gauge': 1821763031,
'bytes_sent_rate_per_sec': 433.0,
'interface_name': 'wlp0s20f3',
'key': 'interface_name',
'speed': 0,
'time_since_update': 0.3504955768585205}
For a complete example of how to use Glances as a library, have a look to the `PythonApi`_.
Documentation 📜
================
For complete documentation have a look at the readthedocs_ website.
If you have any question (after RTFM! and the `FAQ`_), please post it on the official Reddit `forum`_ or in GitHub `Discussions`_.
Gateway to other services 🌐
============================
Glances can export stats to:
- ``CSV`` file
- ``JSON`` file
- ``InfluxDB`` server
- ``Cassandra`` server
- ``CouchDB`` server
- ``OpenTSDB`` server
- ``Prometheus`` server
- ``StatsD`` server
- ``ElasticSearch`` server
- ``PostgreSQL/TimeScale`` server
- ``RabbitMQ/ActiveMQ`` broker
- ``ZeroMQ`` broker
- ``Kafka`` broker
- ``Riemann`` server
- ``Graphite`` server
- ``RESTful`` endpoint
Installation 🚀
===============
There are several methods to test/install Glances on your system. Choose your weapon!
PyPI: Pip, the standard way
---------------------------
Glances is on ``PyPI``. By using PyPI, you will be using the latest stable version.
To install Glances, simply use the ``pip`` command line.
Warning: on modern Linux operating systems, you may have an externally-managed-environment
error message when you try to use ``pip``. In this case, go to the the PipX section below.
.. code-block:: console
pip install --user glances
*Note*: Python headers are required to install `psutil`_, a Glances
dependency. For example, on Debian/Ubuntu **the simplest** is
``apt install python3-psutil`` or alternatively need to install first
the *python-dev* package and gcc (*python-devel* on Fedora/CentOS/RHEL).
For Windows, just install psutil from the binary installation file.
By default, Glances is installed **without** the Web interface dependencies.
To install it, use the following command:
.. code-block:: console
pip install --user 'glances[web]'
For a full installation (with all features, see features list bellow):
.. code-block:: console
pip install --user 'glances[all]'
Features list:
- all: install dependencies for all features
- action: install dependencies for action feature
- browser: install dependencies for Glances centram browser
- cloud: install dependencies for cloud plugin
- containers: install dependencies for container plugin
- export: install dependencies for all exports modules
- gpu: install dependencies for GPU plugin
- graph: install dependencies for graph export
- ip: install dependencies for IP public option
- raid: install dependencies for RAID plugin
- sensors: install dependencies for sensors plugin
- smart: install dependencies for smart plugin
- snmp: install dependencies for SNMP
- sparklines: install dependencies for sparklines option
- web: install dependencies for Webserver (WebUI) and Web API
- wifi: install dependencies for Wifi plugin
To upgrade Glances to the latest version:
.. code-block:: console
pip install --user --upgrade glances
The current develop branch is published to the test.pypi.org package index.
If you want to test the develop version (could be instable), enter:
.. code-block:: console
pip install --user -i https://test.pypi.org/simple/ Glances
PyPI: PipX, the alternative way
-------------------------------
Install PipX on your system (apt install pipx on Ubuntu).
Install Glances (with all features):
.. code-block:: console
pipx install 'glances[all]'
The glances script will be installed in the ~/.local/bin folder.
Shell tab completion 🔍
=======================
Glances 4.3.2 and higher includes shell tab autocompletion thanks to the --print-completion option.
For example, on a Linux operating system with bash shell:
.. code-block:: console
$ mkdir -p ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion
$ glances --print-completion bash > ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion/glances
$ source ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion/glances
Following shells are supported: bash, zsh and tcsh.
Requirements 🧩
===============
Glances is developed in Python. A minimal Python version 3.10 or higher
should be installed on your system.
*Note for Python 2 users*
Glances version 4 or higher do not support Python 2 (and Python 3 < 3.10).
Please uses Glances version 3.4.x if you need Python 2 support.
Dependencies:
- ``psutil`` (better with latest version)
- ``defusedxml`` (in order to monkey patch xmlrpc)
- ``packaging`` (for the version comparison)
- ``windows-curses`` (Windows Curses implementation) [Windows-only]
- ``shtab`` (Shell autocompletion) [All but Windows]
- ``jinja2`` (for fetch mode and templating)
Extra dependencies:
- ``batinfo`` (for battery monitoring)
- ``bernhard`` (for the Riemann export module)
- ``cassandra-driver`` (for the Cassandra export module)
- ``chevron`` (for the action script feature)
- ``docker`` (for the Containers Docker monitoring support)
- ``elasticsearch`` (for the Elastic Search export module)
- ``FastAPI`` and ``Uvicorn`` (for Web server mode)
- ``graphitesender`` (For the Graphite export module)
- ``hddtemp`` (for HDD temperature monitoring support) [Linux-only]
- ``influxdb`` (for the InfluxDB version 1 export module)
- ``influxdb-client`` (for the InfluxDB version 2 export module)
- ``kafka-python`` (for the Kafka export module)
- ``nvidia-ml-py`` (for the GPU plugin)
- ``pycouchdb`` (for the CouchDB export module)
- ``pika`` (for the RabbitMQ/ActiveMQ export module)
- ``podman`` (for the Containers Podman monitoring support)
- ``potsdb`` (for the OpenTSDB export module)
- ``prometheus_client`` (for the Prometheus export module)
- ``psycopg[binary]`` (for the PostgreSQL/TimeScale export module)
- ``pygal`` (for the graph export module)
- ``pymdstat`` (for RAID support) [Linux-only]
- ``pymongo`` (for the MongoDB export module)
- ``pysnmp-lextudio`` (for SNMP support)
- ``pySMART.smartx`` (for HDD Smart support) [Linux-only]
- ``pyzmq`` (for the ZeroMQ export module)
- ``requests`` (for the Ports, Cloud plugins and RESTful export module)
- ``sparklines`` (for the Quick Plugin sparklines option)
- ``statsd`` (for the StatsD export module)
- ``wifi`` (for the wifi plugin) [Linux-only]
- ``zeroconf`` (for the autodiscover mode)
Project sponsorship 🙌
======================
You can help me to achieve my goals of improving this open-source project
or just say "thank you" by:
- sponsor me using one-time or monthly tier Github sponsors_ page
- send me some pieces of bitcoin: 185KN9FCix3svJYp7JQM7hRMfSKyeaJR4X
- buy me a gift on my wishlist_ page
Any and all contributions are greatly appreciated.
Authors and Contributors 🔥
===========================
Nicolas Hennion (@nicolargo) <nicolas@nicolargo.com>
.. image:: https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40nicolargo
:target: https://twitter.com/nicolargo
License 📜
==========
Glances is distributed under the LGPL version 3 license. See ``COPYING`` for more details.
.. _psutil: https://github.com/giampaolo/psutil
.. _readthedocs: https://glances.readthedocs.io/
.. _forum: https://www.reddit.com/r/glances/
.. _sponsors: https://github.com/sponsors/nicolargo
.. _wishlist: https://www.amazon.fr/hz/wishlist/ls/BWAAQKWFR3FI?ref_=wl_share
.. _PythonApi: https://glances.readthedocs.io/en/develop/api/python.html
.. _RestfulApi: https://glances.readthedocs.io/en/develop/api/restful.html
.. _FAQ: https://github.com/nicolargo/glances/blob/develop/docs/faq.rst
.. _Discussions: https://github.com/nicolargo/glances/discussions

View File

@ -1,10 +1,18 @@
===============================
Glances - An eye on your system
===============================
.. raw:: html
<div align="center">
.. image:: ./docs/_static/glances-responsive-webdesign.png
.. raw:: html
<h1>Glances</h1>
An Eye on your System
| |pypi| |test| |contributors| |quality|
| |starts| |docker| |pypistat| |ossrank|
| |sponsors| |twitter|
| |starts| |docker| |pypistat| |sponsors|
| |reddit|
.. |pypi| image:: https://img.shields.io/pypi/v/glances.svg
:target: https://pypi.python.org/pypi/Glances
@ -21,10 +29,6 @@ Glances - An eye on your system
:target: https://pepy.tech/project/glances
:alt: Pypi downloads
.. |ossrank| image:: https://shields.io/endpoint?url=https://ossrank.com/shield/3689
:target: https://ossrank.com/p/3689
:alt: OSSRank
.. |test| image:: https://github.com/nicolargo/glances/actions/workflows/ci.yml/badge.svg?branch=develop
:target: https://github.com/nicolargo/glances/actions
:alt: Linux tests (GitHub Actions)
@ -41,12 +45,20 @@ Glances - An eye on your system
:target: https://github.com/sponsors/nicolargo
:alt: Sponsors
.. |twitter| image:: https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40nicolargo
.. |twitter| image:: https://img.shields.io/badge/X-000000?style=for-the-badge&logo=x&logoColor=white
:target: https://twitter.com/nicolargo
:alt: @nicolargo
Summary
=======
.. |reddit| image:: https://img.shields.io/badge/Reddit-FF4500?style=for-the-badge&logo=reddit&logoColor=white
:target: https://www.reddit.com/r/glances/
:alt: @reddit
.. raw:: html
</div>
Summary 🌟
==========
**Glances** is an open-source system cross-platform monitoring tool.
It allows real-time monitoring of various aspects of your system such as
@ -58,102 +70,199 @@ and can also be used for remote monitoring of systems via a web interface or com
line interface. It is easy to install and use and can be customized to show only
the information that you are interested in.
.. image:: https://raw.githubusercontent.com/nicolargo/glances/develop/docs/_static/glances-summary.png
In client/server mode, remote monitoring could be done via terminal,
Web interface or API (XML-RPC and RESTful).
Stats can also be exported to files or external time/value databases, CSV or direct
output to STDOUT.
.. image:: https://raw.githubusercontent.com/nicolargo/glances/develop/docs/_static/glances-responsive-webdesign.png
Glances is written in Python and uses libraries to grab information from
your system. It is based on an open architecture where developers can
add new plugins or exports modules.
Project sponsorship
===================
Usage 👋
========
You can help me to achieve my goals of improving this open-source project
or just say "thank you" by:
For the standalone mode, just run:
- sponsor me using one-time or monthly tier Github sponsors_ page
- send me some pieces of bitcoin: 185KN9FCix3svJYp7JQM7hRMfSKyeaJR4X
- buy me a gift on my wishlist_ page
.. code-block:: console
Any and all contributions are greatly appreciated.
$ glances
Requirements
============
.. image:: ./docs/_static/glances-summary.png
Glances is developed in Python. A minimal Python version 3.9 or higher
should be installed on your system.
For the Web server mode, run:
*Note for Python 2 users*
.. code-block:: console
Glances version 4 or higher do not support Python 2 (and Python 3 < 3.9).
Please uses Glances version 3.4.x if you need Python 2 support.
$ glances -w
Dependencies:
and enter the URL ``http://<ip>:61208`` in your favorite web browser.
- ``psutil`` (better with latest version)
- ``defusedxml`` (in order to monkey patch xmlrpc)
- ``packaging`` (for the version comparison)
- ``windows-curses`` (Windows Curses implementation) [Windows-only]
In this mode, a HTTP/Restful API is exposed, see document `RestfulApi`_ for more details.
Optional dependencies:
.. image:: ./docs/_static/screenshot-web.png
- ``batinfo`` (for battery monitoring)
- ``bernhard`` (for the Riemann export module)
- ``cassandra-driver`` (for the Cassandra export module)
- ``chevron`` (for the action script feature)
- ``docker`` (for the Containers Docker monitoring support)
- ``elasticsearch`` (for the Elastic Search export module)
- ``FastAPI`` and ``Uvicorn`` (for Web server mode)
- ``graphitesender`` (For the Graphite export module)
- ``hddtemp`` (for HDD temperature monitoring support) [Linux-only]
- ``influxdb`` (for the InfluxDB version 1 export module)
- ``influxdb-client`` (for the InfluxDB version 2 export module)
- ``jinja2`` (for templating, used under the hood by FastAPI)
- ``kafka-python`` (for the Kafka export module)
- ``netifaces2`` (for the IP plugin)
- ``nvidia-ml-py`` (for the GPU plugin)
- ``pycouchdb`` (for the CouchDB export module)
- ``pika`` (for the RabbitMQ/ActiveMQ export module)
- ``podman`` (for the Containers Podman monitoring support)
- ``potsdb`` (for the OpenTSDB export module)
- ``prometheus_client`` (for the Prometheus export module)
- ``pygal`` (for the graph export module)
- ``pymdstat`` (for RAID support) [Linux-only]
- ``pymongo`` (for the MongoDB export module)
- ``pysnmp-lextudio`` (for SNMP support)
- ``pySMART.smartx`` (for HDD Smart support) [Linux-only]
- ``pyzmq`` (for the ZeroMQ export module)
- ``requests`` (for the Ports, Cloud plugins and RESTful export module)
- ``sparklines`` (for the Quick Plugin sparklines option)
- ``statsd`` (for the StatsD export module)
- ``wifi`` (for the wifi plugin) [Linux-only]
- ``zeroconf`` (for the autodiscover mode)
For the client/server mode (remote monitoring through XML-RPC), run the following command on the server:
Installation
============
.. code-block:: console
$ glances -s
and this one on the client:
.. code-block:: console
$ glances -c <ip>
You can also detect and display all Glances servers available on your
network (or defined in the configuration file) in TUI:
.. code-block:: console
$ glances --browser
or WebUI:
.. code-block:: console
$ glances -w --browser
It possible to display raw stats on stdout:
.. code-block:: console
$ glances --stdout cpu.user,mem.used,load
cpu.user: 30.7
mem.used: 3278204928
load: {'cpucore': 4, 'min1': 0.21, 'min5': 0.4, 'min15': 0.27}
cpu.user: 3.4
mem.used: 3275251712
load: {'cpucore': 4, 'min1': 0.19, 'min5': 0.39, 'min15': 0.27}
...
or in a CSV format thanks to the stdout-csv option:
.. code-block:: console
$ glances --stdout-csv now,cpu.user,mem.used,load
now,cpu.user,mem.used,load.cpucore,load.min1,load.min5,load.min15
2018-12-08 22:04:20 CEST,7.3,5948149760,4,1.04,0.99,1.04
2018-12-08 22:04:23 CEST,5.4,5949136896,4,1.04,0.99,1.04
...
or in a JSON format thanks to the stdout-json option (attribute not supported in this mode in order to have a real JSON object in output):
.. code-block:: console
$ glances --stdout-json cpu,mem
cpu: {"total": 29.0, "user": 24.7, "nice": 0.0, "system": 3.8, "idle": 71.4, "iowait": 0.0, "irq": 0.0, "softirq": 0.0, "steal": 0.0, "guest": 0.0, "guest_nice": 0.0, "time_since_update": 1, "cpucore": 4, "ctx_switches": 0, "interrupts": 0, "soft_interrupts": 0, "syscalls": 0}
mem: {"total": 7837949952, "available": 2919079936, "percent": 62.8, "used": 4918870016, "free": 2919079936, "active": 2841214976, "inactive": 3340550144, "buffers": 546799616, "cached": 3068141568, "shared": 788156416}
...
Last but not least, you can use the fetch mode to get a quick look of a machine:
.. code-block:: console
$ glances --fetch
Results look like this:
.. image:: ./docs/_static/screenshot-fetch.png
Use Glances as a Python library 📚
==================================
You can access the Glances API by importing the `glances.api` module and creating an
instance of the `GlancesAPI` class. This instance provides access to all Glances plugins
and their fields. For example, to access the CPU plugin and its total field, you can
use the following code:
.. code-block:: python
>>> from glances import api
>>> gl = api.GlancesAPI()
>>> gl.cpu
{'cpucore': 16,
'ctx_switches': 1214157811,
'guest': 0.0,
'idle': 91.4,
'interrupts': 991768733,
'iowait': 0.3,
'irq': 0.0,
'nice': 0.0,
'soft_interrupts': 423297898,
'steal': 0.0,
'syscalls': 0,
'system': 5.4,
'total': 7.3,
'user': 3.0}
>>> gl.cpu.get("total")
7.3
>>> gl.mem.get("used")
12498582144
>>> gl.auto_unit(gl.mem.get("used"))
11.6G
If the stats return a list of items (like network interfaces or processes), you can
access them by their name:
.. code-block:: python
>>> gl.network.keys()
['wlp0s20f3', 'veth33b370c', 'veth19c7711']
>>> gl.network.get("wlp0s20f3")
{'alias': None,
'bytes_all': 362,
'bytes_all_gauge': 9242285709,
'bytes_all_rate_per_sec': 1032.0,
'bytes_recv': 210,
'bytes_recv_gauge': 7420522678,
'bytes_recv_rate_per_sec': 599.0,
'bytes_sent': 152,
'bytes_sent_gauge': 1821763031,
'bytes_sent_rate_per_sec': 433.0,
'interface_name': 'wlp0s20f3',
'key': 'interface_name',
'speed': 0,
'time_since_update': 0.3504955768585205}
For a complete example of how to use Glances as a library, have a look to the `PythonApi`_.
Documentation 📜
================
For complete documentation have a look at the readthedocs_ website.
If you have any question (after RTFM! and the `FAQ`_), please post it on the official Reddit `forum`_ or in GitHub `Discussions`_.
Gateway to other services 🌐
============================
Glances can export stats to:
- files: ``CSV`` and ``JSON``
- databases: ``InfluxDB``, ``ElasticSearch``, ``PostgreSQL/TimeScale``, ``Cassandra``, ``CouchDB``, ``OpenTSDB``, ``Prometheus``, ``StatsD``, ``Riemann`` and ``Graphite``
- brokers: ``RabbitMQ/ActiveMQ``, ``NATS``, ``ZeroMQ`` and ``Kafka``
- others: ``RESTful`` endpoint
Installation 🚀
===============
There are several methods to test/install Glances on your system. Choose your weapon!
PyPI: Pip, the standard way
---------------------------
Glances is on ``PyPI``. By using PyPI, you will be using the latest
stable version.
Glances is on ``PyPI``. By using PyPI, you will be using the latest stable version.
To install Glances, simply use the ``pip`` command line.
Warning: on modern Linux operating systems, you may have an externally-managed-environment
error message when you try to use ``pip``. In this case, go to the the PipX section below.
To install Glances, simply use the ``pip`` command line in an virtual environment.
.. code-block:: console
pip install --user glances
cd ~
python3 -m venv ~/.venv
source ~/.venv/bin/activate
pip install glances
*Note*: Python headers are required to install `psutil`_, a Glances
dependency. For example, on Debian/Ubuntu **the simplest** is
@ -162,17 +271,18 @@ the *python-dev* package and gcc (*python-devel* on Fedora/CentOS/RHEL).
For Windows, just install psutil from the binary installation file.
By default, Glances is installed **without** the Web interface dependencies.
To install it, use the following command:
.. code-block:: console
pip install --user 'glances[web]'
pip install 'glances[web]'
For a full installation (with all features, see features list bellow):
.. code-block:: console
pip install --user 'glances[all]'
pip install 'glances[all]'
Features list:
@ -197,21 +307,18 @@ To upgrade Glances to the latest version:
.. code-block:: console
pip install --user --upgrade glances
The current develop branch is published to the test.pypi.org package index.
If you want to test the develop version (could be instable), enter:
.. code-block:: console
pip install --user -i https://test.pypi.org/simple/ Glances
pip install --upgrade glances
PyPI: PipX, the alternative way
-------------------------------
Install PipX on your system (apt install pipx on Ubuntu).
Install PipX on your system. For example on Ubuntu/Debian:
Install Glances (with all features):
.. code-block:: console
sudo apt install pipx
Then install Glances (with all features):
.. code-block:: console
@ -219,18 +326,18 @@ Install Glances (with all features):
The glances script will be installed in the ~/.local/bin folder.
To upgrade Glances to the latest version:
.. code-block:: console
pipx upgrade glances
Docker: the cloudy way
----------------------
Glances Docker images are available. You can use it to monitor your
server and all your containers !
Get the Glances container:
.. code-block:: console
docker pull nicolargo/glances:latest-full
The following tags are available:
- *latest-full* for a full Alpine Glances image (latest release) with all dependencies
@ -275,13 +382,32 @@ Run the container in *Web server mode*:
For a full list of options, see the Glances `Docker`_ documentation page.
It is also possible to use a simple Docker compose file (see in ./docker-compose/docker-compose.yml):
.. code-block:: console
cd ./docker-compose
docker-compose up
It will start a Glances server with WebUI.
Brew: The missing package manager
---------------------------------
For Linux and Mac OS, it is also possible to install Glances with `Brew`_:
.. code-block:: console
brew install glances
GNU/Linux package
-----------------
`Glances` is available on many Linux distributions, so you should be
able to install it using your favorite package manager. Be aware that
when you use this method the operating system `package`_ for `Glances`
may not be the latest version and only basics plugins are enabled.
able to install it using your favorite package manager. Nevetheless,
i do not recommend it. Be aware that when you use this method the operating
system `package`_ for `Glances`may not be the latest version and only basics
plugins are enabled.
Note: The Debian package (and all other Debian-based distributions) do
not include anymore the JS statics files used by the Web interface
@ -292,13 +418,30 @@ higher, the path to the statics file is configurable (see ``issue2612``).
FreeBSD
-------
To install the binary package:
On FreeBSD, package name depends on the Python version.
Check for Python version:
.. code-block:: console
# pkg install py39-glances
# python --version
To install Glances from ports:
Install the Glances package:
.. code-block:: console
# pkg install pyXY-glances
Where X and Y are the Major and Minor Values of your Python System.
.. code-block:: console
# Example for Python 3.11.3: pkg install py311-glances
**NOTE:** Check Glances Binary Package Version for your System Architecture.
You must have the Correct Python Version Installed which corresponds to the Glances Binary Package.
To install Glances from Ports:
.. code-block:: console
@ -308,9 +451,7 @@ To install Glances from ports:
macOS
-----
If you do not want to use the glancesautoinstall script, follow this procedure.
macOS users can install Glances using ``Homebrew`` or ``MacPorts``.
MacOS users can install Glances using ``Homebrew`` or ``MacPorts``.
Homebrew
````````
@ -330,11 +471,7 @@ Windows
-------
Install `Python`_ for Windows (Python 3.4+ ship with pip) and
then run the following command:
.. code-block:: console
$ pip install glances
follow the Glances Pip install procedure.
Android
-------
@ -388,94 +525,77 @@ Ansible
A Glances ``Ansible`` role is available: https://galaxy.ansible.com/zaxos/glances-ansible-role/
Usage
=====
Shell tab completion 🔍
=======================
For the standalone mode, just run:
Glances 4.3.2 and higher includes shell tab autocompletion thanks to the --print-completion option.
For example, on a Linux operating system with bash shell:
.. code-block:: console
$ glances
$ mkdir -p ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion
$ glances --print-completion bash > ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion/glances
$ source ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion/glances
For the Web server mode, run:
Following shells are supported: bash, zsh and tcsh.
.. code-block:: console
Requirements 🧩
===============
$ glances -w
Glances is developed in Python. A minimal Python version 3.10 or higher
should be installed on your system.
and enter the URL ``http://<ip>:61208`` in your favorite web browser.
*Note for Python 2 users*
For the client/server mode, run:
Glances version 4 or higher do not support Python 2 (and Python 3 < 3.10).
Please uses Glances version 3.4.x if you need Python 2 support.
.. code-block:: console
Dependencies:
$ glances -s
- ``psutil`` (better with latest version)
- ``defusedxml`` (in order to monkey patch xmlrpc)
- ``packaging`` (for the version comparison)
- ``windows-curses`` (Windows Curses implementation) [Windows-only]
- ``shtab`` (Shell autocompletion) [All but Windows]
- ``jinja2`` (for fetch mode and templating)
on the server side and run:
Extra dependencies:
.. code-block:: console
- ``batinfo`` (for battery monitoring)
- ``bernhard`` (for the Riemann export module)
- ``cassandra-driver`` (for the Cassandra export module)
- ``chevron`` (for the action script feature)
- ``docker`` (for the Containers Docker monitoring support)
- ``elasticsearch`` (for the Elastic Search export module)
- ``FastAPI`` and ``Uvicorn`` (for Web server mode)
- ``graphitesender`` (For the Graphite export module)
- ``hddtemp`` (for HDD temperature monitoring support) [Linux-only]
- ``influxdb`` (for the InfluxDB version 1 export module)
- ``influxdb-client`` (for the InfluxDB version 2 export module)
- ``kafka-python`` (for the Kafka export module)
- ``nats-py`` (for the NATS export module)
- ``nvidia-ml-py`` (for the GPU plugin)
- ``pycouchdb`` (for the CouchDB export module)
- ``pika`` (for the RabbitMQ/ActiveMQ export module)
- ``podman`` (for the Containers Podman monitoring support)
- ``potsdb`` (for the OpenTSDB export module)
- ``prometheus_client`` (for the Prometheus export module)
- ``psycopg[binary]`` (for the PostgreSQL/TimeScale export module)
- ``pygal`` (for the graph export module)
- ``pymdstat`` (for RAID support) [Linux-only]
- ``pymongo`` (for the MongoDB export module)
- ``pysnmp-lextudio`` (for SNMP support)
- ``pySMART.smartx`` (for HDD Smart support) [Linux-only]
- ``pyzmq`` (for the ZeroMQ export module)
- ``requests`` (for the Ports, Cloud plugins and RESTful export module)
- ``sparklines`` (for the Quick Plugin sparklines option)
- ``statsd`` (for the StatsD export module)
- ``wifi`` (for the wifi plugin) [Linux-only]
- ``zeroconf`` (for the autodiscover mode)
$ glances -c <ip>
on the client one.
You can also detect and display all Glances servers available on your
network or defined in the configuration file:
.. code-block:: console
$ glances --browser
You can also display raw stats on stdout:
.. code-block:: console
$ glances --stdout cpu.user,mem.used,load
cpu.user: 30.7
mem.used: 3278204928
load: {'cpucore': 4, 'min1': 0.21, 'min5': 0.4, 'min15': 0.27}
cpu.user: 3.4
mem.used: 3275251712
load: {'cpucore': 4, 'min1': 0.19, 'min5': 0.39, 'min15': 0.27}
...
or in a CSV format thanks to the stdout-csv option:
.. code-block:: console
$ glances --stdout-csv now,cpu.user,mem.used,load
now,cpu.user,mem.used,load.cpucore,load.min1,load.min5,load.min15
2018-12-08 22:04:20 CEST,7.3,5948149760,4,1.04,0.99,1.04
2018-12-08 22:04:23 CEST,5.4,5949136896,4,1.04,0.99,1.04
...
or in a JSON format thanks to the stdout-json option (attribute not supported in this mode in order to have a real JSON object in output):
.. code-block:: console
$ glances --stdout-json cpu,mem
cpu: {"total": 29.0, "user": 24.7, "nice": 0.0, "system": 3.8, "idle": 71.4, "iowait": 0.0, "irq": 0.0, "softirq": 0.0, "steal": 0.0, "guest": 0.0, "guest_nice": 0.0, "time_since_update": 1, "cpucore": 4, "ctx_switches": 0, "interrupts": 0, "soft_interrupts": 0, "syscalls": 0}
mem: {"total": 7837949952, "available": 2919079936, "percent": 62.8, "used": 4918870016, "free": 2919079936, "active": 2841214976, "inactive": 3340550144, "buffers": 546799616, "cached": 3068141568, "shared": 788156416}
...
and RTFM, always.
Documentation
=============
For complete documentation have a look at the readthedocs_ website.
If you have any question (after RTFM!), please post it on the official Q&A `forum`_.
Gateway to other services
=========================
Glances can export stats to: ``CSV`` file, ``JSON`` file, ``InfluxDB``, ``Cassandra``, ``CouchDB``,
``OpenTSDB``, ``Prometheus``, ``StatsD``, ``ElasticSearch``, ``RabbitMQ/ActiveMQ``,
``ZeroMQ``, ``Kafka``, ``Riemann``, ``Graphite`` and ``RESTful`` server.
How to contribute ?
===================
How to contribute ? 🤝
======================
If you want to contribute to the Glances project, read this `wiki`_ page.
@ -484,30 +604,53 @@ There is also a chat dedicated to the Glances developers:
.. image:: https://badges.gitter.im/Join%20Chat.svg
:target: https://gitter.im/nicolargo/glances?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
Author
======
Project sponsorship 🙌
======================
You can help me to achieve my goals of improving this open-source project
or just say "thank you" by:
- sponsor me using one-time or monthly tier Github sponsors_ page
- send me some pieces of bitcoin: 185KN9FCix3svJYp7JQM7hRMfSKyeaJR4X
- buy me a gift on my wishlist_ page
Any and all contributions are greatly appreciated.
Authors and Contributors 🔥
===========================
Nicolas Hennion (@nicolargo) <nicolas@nicolargo.com>
.. image:: https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40nicolargo
:target: https://twitter.com/nicolargo
License
=======
License 📜
==========
Glances is distributed under the LGPL version 3 license. See ``COPYING`` for more details.
More stars ! 🌟
===============
Please give us a star on `GitHub`_ if you like this project.
.. image:: https://api.star-history.com/svg?repos=nicolargo/glances&type=Date
:target: https://www.star-history.com/#nicolargo/glances&Date
:alt: Star history
.. _psutil: https://github.com/giampaolo/psutil
.. _glancesautoinstall: https://github.com/nicolargo/glancesautoinstall
.. _Brew: https://formulae.brew.sh/formula/glances
.. _Python: https://www.python.org/getit/
.. _Termux: https://play.google.com/store/apps/details?id=com.termux
.. _readthedocs: https://glances.readthedocs.io/
.. _forum: https://groups.google.com/forum/?hl=en#!forum/glances-users
.. _forum: https://www.reddit.com/r/glances/
.. _wiki: https://github.com/nicolargo/glances/wiki/How-to-contribute-to-Glances-%3F
.. _package: https://repology.org/project/glances/versions
.. _sponsors: https://github.com/sponsors/nicolargo
.. _wishlist: https://www.amazon.fr/hz/wishlist/ls/BWAAQKWFR3FI?ref_=wl_share
.. _issue2021: https://github.com/nicolargo/glances/issues/2021
.. _issue2021comment: https://github.com/nicolargo/glances/issues/2021#issuecomment-1197831157
.. _issue2612: https://github.com/nicolargo/glances/issues/2612
.. _Docker: https://github.com/nicolargo/glances/blob/develop/docs/docker.rst
.. _Docker: https://github.com/nicolargo/glances/blob/master/docs/docker.rst
.. _GitHub: https://github.com/nicolargo/glances
.. _PythonApi: https://glances.readthedocs.io/en/develop/api/python.html
.. _RestfulApi: https://glances.readthedocs.io/en/develop/api/restful.html
.. _FAQ: https://github.com/nicolargo/glances/blob/develop/docs/faq.rst
.. _Discussions: https://github.com/nicolargo/glances/discussions

237
all-requirements.txt Normal file
View File

@ -0,0 +1,237 @@
# This file was autogenerated by uv via the following command:
# uv export --no-emit-workspace --no-hashes --all-extras --no-group dev --output-file all-requirements.txt
annotated-doc==0.0.4
# via fastapi
annotated-types==0.7.0
# via pydantic
anyio==4.12.0
# via
# elasticsearch
# starlette
batinfo==0.4.2 ; sys_platform == 'linux'
# via glances
bernhard==0.2.6
# via glances
cassandra-driver==3.29.3
# via glances
certifi==2025.11.12
# via
# elastic-transport
# influxdb-client
# influxdb3-python
# requests
cffi==2.0.0 ; implementation_name == 'pypy' or platform_python_implementation != 'PyPy'
# via
# cryptography
# pyzmq
chardet==5.2.0
# via pysmart
charset-normalizer==3.4.4
# via requests
chevron==0.14.0
# via glances
click==8.1.8
# via
# geomet
# uvicorn
colorama==0.4.6 ; sys_platform == 'win32'
# via click
cryptography==46.0.3
# via pysnmpcrypto
defusedxml==0.7.1
# via glances
dnspython==2.8.0
# via pymongo
docker==7.1.0
# via glances
elastic-transport==9.2.1
# via elasticsearch
elasticsearch==9.2.1
# via glances
exceptiongroup==1.2.2 ; python_full_version < '3.11'
# via anyio
fastapi==0.128.0
# via glances
geomet==1.1.0
# via cassandra-driver
graphitesender==0.11.2
# via glances
h11==0.16.0
# via uvicorn
humanfriendly==10.0
# via pysmart
ibm-cloud-sdk-core==3.24.2
# via ibmcloudant
ibmcloudant==0.11.2
# via glances
idna==3.11
# via
# anyio
# requests
ifaddr==0.2.0
# via zeroconf
importlib-metadata==8.7.1
# via pygal
influxdb==5.3.2
# via glances
influxdb-client==1.49.0
# via glances
influxdb3-python==0.16.0
# via glances
jinja2==3.1.6
# via
# glances
# pysmi-lextudio
kafka-python==2.3.0
# via glances
markupsafe==3.0.3
# via jinja2
msgpack==1.1.2
# via influxdb
nats-py==2.12.0
# via glances
nvidia-ml-py==13.590.44
# via glances
packaging==25.0
# via glances
paho-mqtt==2.1.0
# via glances
pbkdf2==1.3
# via wifi
pika==1.3.2
# via glances
ply==3.11
# via pysmi-lextudio
podman==5.6.0
# via glances
potsdb==1.0.3
# via glances
prometheus-client==0.23.1
# via glances
protobuf==6.33.2
# via bernhard
psutil==7.2.1
# via glances
psycopg==3.3.2
# via glances
psycopg-binary==3.3.2 ; implementation_name != 'pypy'
# via psycopg
pyarrow==22.0.0
# via influxdb3-python
pyasn1==0.6.1
# via pysnmp-lextudio
pycparser==2.23 ; (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') or (implementation_name == 'pypy' and platform_python_implementation == 'PyPy')
# via cffi
pydantic==2.12.5
# via fastapi
pydantic-core==2.41.5
# via pydantic
pygal==3.1.0
# via glances
pyjwt==2.10.1
# via
# ibm-cloud-sdk-core
# ibmcloudant
pymdstat==0.4.3
# via glances
pymongo==4.15.5
# via glances
pyreadline3==3.5.4 ; sys_platform == 'win32'
# via humanfriendly
pysmart==1.4.2
# via glances
pysmi-lextudio==1.4.3
# via pysnmp-lextudio
pysnmp-lextudio==6.1.2
# via glances
pysnmpcrypto==0.0.4
# via pysnmp-lextudio
python-dateutil==2.9.0.post0
# via
# elasticsearch
# glances
# ibm-cloud-sdk-core
# ibmcloudant
# influxdb
# influxdb-client
# influxdb3-python
pytz==2025.2
# via influxdb
pywin32==311 ; sys_platform == 'win32'
# via docker
pyzmq==27.1.0
# via glances
reactivex==4.1.0
# via
# influxdb-client
# influxdb3-python
requests==2.32.5
# via
# docker
# glances
# ibm-cloud-sdk-core
# ibmcloudant
# influxdb
# podman
# pysmi-lextudio
setuptools==80.9.0
# via
# influxdb-client
# wifi
shtab==1.8.0 ; sys_platform != 'win32'
# via glances
six==1.17.0
# via
# glances
# influxdb
# python-dateutil
sniffio==1.3.1
# via
# elastic-transport
# elasticsearch
sparklines==0.7.0
# via glances
starlette==0.50.0
# via fastapi
statsd==4.0.1
# via glances
termcolor==3.3.0
# via sparklines
tomli==2.0.2 ; python_full_version < '3.11'
# via podman
typing-extensions==4.15.0
# via
# anyio
# cryptography
# elasticsearch
# fastapi
# psycopg
# pydantic
# pydantic-core
# reactivex
# starlette
# typing-inspection
# uvicorn
typing-inspection==0.4.2
# via pydantic
tzdata==2025.3 ; sys_platform == 'win32'
# via psycopg
urllib3==2.6.2
# via
# docker
# elastic-transport
# ibm-cloud-sdk-core
# influxdb-client
# influxdb3-python
# podman
# requests
uvicorn==0.40.0
# via glances
wifi==0.3.8
# via glances
windows-curses==2.4.1 ; sys_platform == 'win32'
# via glances
zeroconf==0.148.0
# via glances
zipp==3.23.0
# via importlib-metadata

View File

@ -0,0 +1,9 @@
✨ {{ gl.system['hostname'] }}{{ ' - ' + gl.ip['address'] if gl.ip['address'] else '' }}
⚙️ {{ gl.system['hr_name'] }} | Uptime: {{ gl.uptime }}
💡 LOAD {{ '%0.2f'| format(gl.load['min1']) }} {{ '%0.2f'| format(gl.load['min5']) }} {{ '%0.2f'| format(gl.load['min15']) }}
⚡ CPU {{ gl.bar(gl.cpu['total']) }} {{ gl.cpu['total'] }}% of {{ gl.core['log'] }} cores
🧠 MEM {{ gl.bar(gl.mem['percent']) }} {{ gl.mem['percent'] }}% ({{ gl.auto_unit(gl.mem['used']) }} {{ gl.auto_unit(gl.mem['total']) }})
{% for fs in gl.fs.keys() %}💾 {% if loop.index == 1 %}DISK{% else %} {% endif %} {{ gl.bar(gl.fs[fs]['percent']) }} {{ gl.fs[fs]['percent'] }}% ({{ gl.auto_unit(gl.fs[fs]['used']) }} {{ gl.auto_unit(gl.fs[fs]['size']) }}) for {{ fs }}
{% endfor %}{% for net in gl.network.keys() %}📡 {% if loop.index == 1 %}NET{% else %} {% endif %} ↓ {{ gl.auto_unit(gl.network[net]['bytes_recv_rate_per_sec']) }}b/s ↑ {{ gl.auto_unit(gl.network[net]['bytes_sent_rate_per_sec']) }}b/s for {{ net }}
{% endfor %}

View File

@ -0,0 +1,23 @@
_____ _
/ ____| |
| | __| | __ _ _ __ ___ ___ ___
| | |_ | |/ _` | '_ \ / __/ _ \/ __|
| |__| | | (_| | | | | (_| __/\__
\_____|_|\__,_|_| |_|\___\___||___/
✨ {{ gl.system['hostname'] }}{{ ' - ' + gl.ip['address'] if gl.ip['address'] else '' }}
⚙️ {{ gl.system['hr_name'] }} | Uptime: {{ gl.uptime }}
💡 LOAD {{ '%0.2f'| format(gl.load['min1']) }} {{ '%0.2f'| format(gl.load['min5']) }} {{ '%0.2f'| format(gl.load['min15']) }}
⚡ CPU {{ gl.bar(gl.cpu['total']) }} {{ gl.cpu['total'] }}% of {{ gl.core['log'] }} cores
🧠 MEM {{ gl.bar(gl.mem['percent']) }} {{ gl.mem['percent'] }}% ({{ gl.auto_unit(gl.mem['used']) }} {{ gl.auto_unit(gl.mem['total']) }})
{% for fs in gl.fs.keys() %}💾 {% if loop.index == 1 %}DISK{% else %} {% endif %} {{ gl.bar(gl.fs[fs]['percent']) }} {{ gl.fs[fs]['percent'] }}% ({{ gl.auto_unit(gl.fs[fs]['used']) }} {{ gl.auto_unit(gl.fs[fs]['size']) }}) for {{ fs }}
{% endfor %}{% for net in gl.network.keys() %}📡 {% if loop.index == 1 %}NET{% else %} {% endif %} ↓ {{ gl.auto_unit(gl.network[net]['bytes_recv_rate_per_sec']) }}b/s ↑ {{ gl.auto_unit(gl.network[net]['bytes_sent_rate_per_sec']) }}b/s for {{ net }}
{% endfor %}
🔥 TOP PROCESS by CPU
{% for process in gl.top_process() %}{{ loop.index }}️⃣ {{ process['name'][:20] }}{{ ' ' * (20 - process['name'][:20] | length) }} ⚡ {{ process['cpu_percent'] }}% CPU{{ ' ' * (8 - (gl.auto_unit(process['cpu_percent']) | length)) }} 🧠 {{ gl.auto_unit(process['memory_info']['rss']) }}B MEM
{% endfor %}
🔥 TOP PROCESS by MEM
{% for process in gl.top_process(sorted_by='memory_percent', sorted_by_secondary='cpu_percent') %}{{ loop.index }}️⃣ {{ process['name'][:20] }}{{ ' ' * (20 - process['name'][:20] | length) }} 🧠 {{ gl.auto_unit(process['memory_info']['rss']) }}B MEM{{ ' ' * (7 - (gl.auto_unit(process['memory_info']['rss']) | length)) }} ⚡ {{ process['cpu_percent'] }}% CPU
{% endfor %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,7 @@ history_size=1200
# You can download it in a specific folder
# thanks to https://github.com/nicolargo/glances/issues/2021
# then configure this folder with the webui_root_path key
# Default is folder where glances_restfull_api.py is hosted
# Default is folder where glances_restful_api.py is hosted
#webui_root_path=
# CORS options
# Comma separated list of origins that should be permitted to make cross-origin requests.
@ -64,6 +64,10 @@ history_size=1200
# Comma separated list of HTTP request headers that should be supported for cross-origin requests.
# Default is *
#cors_headers=*
# Define SSL files (keyfile_password is optional)
#ssl_keyfile_password=kfp
#ssl_keyfile=./glances.local+3-key.pem
#ssl_certfile=./glances.local+3.pem
##############################################################################
# Plugins
@ -127,7 +131,7 @@ user_careful=50
user_warning=70
user_critical=90
user_log=False
#user_critical_action=echo {{user}} {{value}} {{max}} > /tmp/cpu.alert
#user_critical_action=echo "{{time}} User CPU {{user}} higher than {{critical}}" > /tmp/cpu.alert
#
system_careful=50
system_warning=70
@ -181,12 +185,14 @@ temperature_critical=80
[mem]
disable=False
# Display available memory instead of used memory
#available=True
# Define RAM thresholds in %
# Default values if not defined: 50/70/90
careful=50
#careful_action_repeat=echo {{percent}} >> /tmp/memory.alert
warning=70
critical=90
#critical_action_repeat=echo "{{time}} {{percent}} higher than {{critical}}"" >> /tmp/memory.alert
[memswap]
disable=False
@ -195,6 +201,7 @@ disable=False
careful=50
warning=70
critical=90
#warning_action=echo "{{time}} {{percent}} higher than {{warning}}"" > /tmp/memory.alert
[load]
disable=False
@ -241,8 +248,9 @@ hide_zero=False
#wlan0_tx_warning=900000
#wlan0_tx_critical=1000000
#wlan0_tx_log=True
#wlan0_rx_critical_action=echo "{{time}} {{interface_name}} RX {{bytes_recv_rate_per_sec}}Bps" > /tmp/network.alert
# Alias for network interface name
#alias=wlp2s0:WIFI
#alias=wlp0s20f3:WIFI
[ip]
# Disable display of private IP address
@ -300,15 +308,32 @@ hide_zero=False
#show=sda.*
# Alias for sda1 and sdb1
#alias=sda1:SystemDisk,sdb1:DataDisk
# Set thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
# Default latency thresholds (in ms) (rx = read / tx = write)
rx_latency_careful=10
rx_latency_warning=20
rx_latency_critical=50
tx_latency_careful=10
tx_latency_warning=20
tx_latency_critical=50
# Set latency thresholds (latency in ms) for a given disk name (rx = read / tx = write)
# dm-0_rx_latency_careful=10
# dm-0_rx_latency_warning=20
# dm-0_rx_latency_critical=50
# dm-0_rx_latency_log=False
# dm-0_tx_latency_careful=10
# dm-0_tx_latency_warning=20
# dm-0_tx_latency_critical=50
# dm-0_tx_latency_log=False
# There is no default bitrate thresholds for disk (because it is not possible to know the disk speed)
# Set bitrate thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
#dm-0_rx_careful=4000000000
#dm-0_rx_warning=5000000000
#dm-0_rx_critical=6000000000
#dm-0_rx_log=True
#dm-0_rx_log=False
#dm-0_tx_careful=700000000
#dm-0_tx_warning=900000000
#dm-0_tx_critical=1000000000
#dm-0_tx_log=True
#dm-0_tx_log=False
[fs]
disable=False
@ -318,15 +343,19 @@ hide=/boot.*,.*/snap.*
#show=/,/srv
# Define filesystem space thresholds in %
# Default values if not defined: 50/70/90
# It is also possible to define per mount point value
# Example: /_careful=40
careful=50
warning=70
critical=90
# It is also possible to define per mount point value
# Example: /_careful=40
#/_careful=1
#/_warning=5
#/_critical=10
#/_critical_action=echo "{{time}} {{mnt_point}} filesystem space {{percent}}% higher than {{critical}}%" > /tmp/fs.alert
# Allow additional file system types (comma-separated FS type)
#allow=shm
# Alias for root file system
#alias=/:Root,/zsfpool:ZSF
#alias=/:Root,/zfspool:ZFS
[irq]
# Documentation: https://glances.readthedocs.io/en/latest/aoa/irq.html
@ -373,6 +402,8 @@ disable=True
#hide=.*Hide_this_driver.*
# Define the list of sensors to show (comma-separated regexp)
#show=.*Drive_Temperature.*
# List of attributes to hide (comma separated)
#hide_attributes=Self-tests,Errors
[hddtemp]
disable=False
@ -384,8 +415,8 @@ port=7634
# Documentation: https://glances.readthedocs.io/en/latest/aoa/sensors.html
disable=False
# Set the refresh multiplicator for the sensors
# By default refresh every Glances refresh * 3 (increase to reduce CPU consumption)
#refresh=3
# By default refresh every Glances refresh * 5 (increase to reduce CPU consumption)
#refresh=5
# Hide some sensors (comma separated list of regexp)
hide=unknown.*
# Show only the following sensors (comma separated list of regexp)
@ -393,10 +424,11 @@ hide=unknown.*
# Sensors core thresholds (in Celsius...)
# By default values are grabbed from the system
# Overwrite thresholds for a specific sensor
#temperature_core_Ambient_careful=45
#temperature_core_Ambient_warning=65
#temperature_core_Ambient_critical=80
#temperature_core_Ambient_log=False
# temperature_core_Ambient_careful=40
# temperature_core_Ambient_warning=60
# temperature_core_Ambient_critical=85
# temperature_core_Ambient_log=True
# temperature_core_Ambient_critical_action=echo "{{time}} {{label}} temperature {{value}}{{unit}} higher than {{critical}}{{unit}}" > /tmp/temperature.alert
# Overwrite thresholds for a specific type of sensor
#temperature_core_careful=45
#temperature_core_warning=65
@ -431,6 +463,8 @@ disable=False
# Stats that can be disabled: cpu_percent,memory_info,memory_percent,username,cpu_times,num_threads,nice,status,io_counters,cmdline
# Stats that can not be disable: pid,name
#disable_stats=cpu_percent,memory_info,memory_percent,username,cpu_times,num_threads,nice,status,io_counters,cmdline
# Disable display of virtual memory
#disable_virtual_memory=True
# Define CPU/MEM (per process) thresholds in %
# Default values if not defined: 50/70/90
cpu_careful=50
@ -447,13 +481,20 @@ mem_critical=90
nice_warning=-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19
#
# Nice: Example 2, low priority processes escalate from careful to critical
#nice_ok=O
#nice_careful=1,2,3,4,5,6,7,8,9
#nice_warning=10,11,12,13,14
#nice_critical=15,16,17,18,19
#
# Status: define threshold regarding the process status (first letter of process status)
# R: Running, S: Sleeping, Z: Zombie (complete list here https://psutil.readthedocs.io/en/latest/#process-status-constants)
status_ok=R,W,P,I
status_critical=Z,D
# Define the list of processes to export using:
# a comma-separated list of Glances filter
#export=.*firefox.*,pid:1234
# Define a list of process to focus on (comma-separated list of Glances filter)
#focus=.*firefox.*,.*python.*
[ports]
disable=False
@ -505,7 +546,8 @@ port_default_gateway=True
disable=True
# Define the maximum VMs size name (default is 20 chars)
max_name_size=20
# By default, Glances only display running VMs with states: 'Running', 'Starting' or 'Restarting'
# By default, Glances only display running VMs with states:
# 'Running', 'Paused', 'Starting' or 'Restarting'
# Set the following key to True to display all VMs regarding their states
all=False
@ -520,8 +562,8 @@ disable=False
# Define the maximum docker size name (default is 20 chars)
max_name_size=20
# List of stats to disable (not display)
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,command
; disable_stats=diskio,networkio
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,ports,command
disable_stats=command
# Thresholds for CPU and MEM (in %)
; cpu_careful=50
; cpu_warning=70
@ -599,6 +641,11 @@ disable=False
# Exports
##############################################################################
[export]
# Common section for all exporters
# Do not export following fields (comma separated list of regex)
#exclude_fields=.*_critical,.*_careful,.*_warning,.*\.key$
[graph]
# Configuration for the --export graph option
# Set the path where the graph (.svg files) will be created
@ -617,7 +664,7 @@ style=DarkStyle
[influxdb]
# !!!
# Will be DEPRECATED in future release.
# Please have a look on the new influxdb2 export module (compatible with InfluxDB 1.8.x and 2.x)
# Please have a look on the new influxdb3 export module
# !!!
# Configuration for the --export influxdb option
# https://influxdb.com/
@ -646,7 +693,28 @@ port=8086
protocol=http
org=nicolargo
bucket=glances
token=EjFUTWe8U-MIseEAkaVIgVnej_TrnbdvEcRkaB1imstW7gapSqy6_6-8XD-yd51V0zUUpDy-kAdVD1purDLuxA==
token=PUT_YOUR_INFLUXDB2_TOKEN_HERE
# Set the interval between two exports (in seconds)
# If the interval is set to 0, the Glances refresh time is used (default behavor)
#interval=0
# Prefix will be added for all measurement name
# Ex: prefix=foo
# => foo.cpu
# => foo.mem
# You can also use dynamic values
#prefix=foo
# Following tags will be added for all measurements
# You can also use dynamic values.
# Note: hostname and name (for process) are always added as a tag
#tags=foo:bar,spam:eggs,domain:`domainname`
[influxdb3]
# Configuration for the --export influxdb3 option
# https://influxdb.com/
host=http://localhost:8181
org=nicolargo
database=glances
token=PUT_YOUR_INFLUXDB3_TOKEN_HERE
# Set the interval between two exports (in seconds)
# If the interval is set to 0, the Glances refresh time is used (default behavor)
#interval=0
@ -812,6 +880,26 @@ prefix=glances
# By default, system_name = FQDN
#system_name=mycomputer
[timescaledb]
# Configuration for the --export timescaledb option
# https://www.timescale.com/
host=localhost
port=5432
db=glances
user=postgres
password=password
# Overwrite device name (default is the FQDN)
# Most of the time, you should not overwrite this value
#hostname=mycomputer
[nats]
# Configuration for the --export nats option
# https://nats.io/
# Host is a separated list of NATS nodes
host=nats://localhost:4222
# Prefix for the subjects (default is 'glances')
prefix=glances
##############################################################################
# AMPS
# * enable: Enable (true) or disable (false) the AMP

View File

@ -1,18 +1,395 @@
codespell
coverage
fonttools>=4.43.0 # not directly required, pinned by Snyk to avoid a vulnerability
gprof2dot
matplotlib
memory-profiler
numpy>=1.22.2 # not directly required, pinned by Snyk to avoid a vulnerability
pillow>=10.0.1 # not directly required, pinned by Snyk to avoid a vulnerability
pre-commit
py-spy
pyright
pytest
requirements-parser
ruff
selenium
semgrep; platform_system == 'Linux'
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
webdriver-manager
# This file was autogenerated by uv via the following command:
# uv export --no-hashes --only-dev --output-file dev-requirements.txt
alabaster==1.0.0
# via sphinx
annotated-types==0.7.0
# via pydantic
anyio==4.12.0
# via
# httpx
# mcp
# sse-starlette
# starlette
attrs==25.4.0
# via
# glom
# jsonschema
# outcome
# referencing
# reuse
# semgrep
# trio
babel==2.17.0
# via sphinx
boltons==21.0.0
# via
# face
# glom
# semgrep
boolean-py==5.0
# via license-expression
bracex==2.6
# via wcmatch
certifi==2025.11.12
# via
# httpcore
# httpx
# requests
# selenium
cffi==2.0.0 ; (implementation_name != 'pypy' and os_name == 'nt') or platform_python_implementation != 'PyPy'
# via
# cryptography
# trio
cfgv==3.5.0
# via pre-commit
charset-normalizer==3.4.4
# via
# python-debian
# requests
click==8.1.8
# via
# click-option-group
# reuse
# semgrep
# typer
# uvicorn
click-option-group==0.5.9
# via semgrep
codespell==2.4.1
colorama==0.4.6
# via
# click
# pytest
# semgrep
# sphinx
contourpy==1.3.2 ; python_full_version < '3.11'
# via matplotlib
contourpy==1.3.3 ; python_full_version >= '3.11'
# via matplotlib
cryptography==46.0.3
# via pyjwt
cycler==0.12.1
# via matplotlib
distlib==0.4.0
# via virtualenv
docutils==0.21.2
# via
# rstcheck-core
# sphinx
# sphinx-rtd-theme
exceptiongroup==1.2.2
# via
# anyio
# pytest
# semgrep
# trio
# trio-websocket
face==24.0.0
# via glom
filelock==3.20.2
# via virtualenv
fonttools==4.61.1
# via matplotlib
glom==22.1.0
# via semgrep
googleapis-common-protos==1.72.0
# via opentelemetry-exporter-otlp-proto-http
gprof2dot==2025.4.14
h11==0.16.0
# via
# httpcore
# uvicorn
# wsproto
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via mcp
httpx-sse==0.4.3
# via mcp
identify==2.6.15
# via pre-commit
idna==3.11
# via
# anyio
# httpx
# requests
# trio
imagesize==1.4.1
# via sphinx
importlib-metadata==8.7.1
# via opentelemetry-api
iniconfig==2.3.0
# via pytest
jinja2==3.1.6
# via
# reuse
# sphinx
jsonschema==4.25.1
# via
# mcp
# semgrep
jsonschema-specifications==2025.9.1
# via jsonschema
kiwisolver==1.4.9
# via matplotlib
license-expression==30.4.4
# via reuse
markdown-it-py==4.0.0
# via rich
markupsafe==3.0.3
# via jinja2
matplotlib==3.10.8
mcp==1.23.3
# via semgrep
mdurl==0.1.2
# via markdown-it-py
memory-profiler==0.61.0
nodeenv==1.10.0
# via
# pre-commit
# pyright
numpy==2.2.6 ; python_full_version < '3.11'
# via
# contourpy
# matplotlib
numpy==2.4.0 ; python_full_version >= '3.11'
# via
# contourpy
# matplotlib
opentelemetry-api==1.37.0
# via
# opentelemetry-exporter-otlp-proto-http
# opentelemetry-instrumentation
# opentelemetry-instrumentation-requests
# opentelemetry-sdk
# opentelemetry-semantic-conventions
# semgrep
opentelemetry-exporter-otlp-proto-common==1.37.0
# via opentelemetry-exporter-otlp-proto-http
opentelemetry-exporter-otlp-proto-http==1.37.0
# via semgrep
opentelemetry-instrumentation==0.58b0
# via opentelemetry-instrumentation-requests
opentelemetry-instrumentation-requests==0.58b0
# via semgrep
opentelemetry-proto==1.37.0
# via
# opentelemetry-exporter-otlp-proto-common
# opentelemetry-exporter-otlp-proto-http
opentelemetry-sdk==1.37.0
# via
# opentelemetry-exporter-otlp-proto-http
# semgrep
opentelemetry-semantic-conventions==0.58b0
# via
# opentelemetry-instrumentation
# opentelemetry-instrumentation-requests
# opentelemetry-sdk
opentelemetry-util-http==0.58b0
# via opentelemetry-instrumentation-requests
outcome==1.3.0.post0
# via
# trio
# trio-websocket
packaging==25.0
# via
# matplotlib
# opentelemetry-instrumentation
# pytest
# requirements-parser
# semgrep
# sphinx
# webdriver-manager
peewee==3.18.3
# via semgrep
pillow==12.1.0
# via matplotlib
platformdirs==4.5.1
# via virtualenv
pluggy==1.6.0
# via pytest
pre-commit==4.5.1
protobuf==6.33.2
# via
# googleapis-common-protos
# opentelemetry-proto
psutil==7.2.1
# via memory-profiler
py-spy==0.4.1
pycparser==2.23 ; (implementation_name != 'PyPy' and implementation_name != 'pypy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy')
# via cffi
pydantic==2.12.5
# via
# mcp
# pydantic-settings
# rstcheck-core
pydantic-core==2.41.5
# via pydantic
pydantic-settings==2.12.0
# via mcp
pygments==2.19.2
# via
# pytest
# rich
# sphinx
pyinstrument==5.1.1
pyjwt==2.10.1
# via mcp
pyparsing==3.3.1
# via matplotlib
pyright==1.1.407
pysocks==1.7.1
# via urllib3
pytest==9.0.2
python-dateutil==2.9.0.post0
# via matplotlib
python-debian==1.0.1
# via reuse
python-dotenv==1.2.1
# via
# pydantic-settings
# webdriver-manager
python-magic==0.4.27
# via reuse
python-multipart==0.0.21
# via mcp
pywin32==311 ; sys_platform == 'win32'
# via
# mcp
# semgrep
pyyaml==6.0.3
# via pre-commit
referencing==0.37.0
# via
# jsonschema
# jsonschema-specifications
requests==2.32.5
# via
# opentelemetry-exporter-otlp-proto-http
# semgrep
# sphinx
# webdriver-manager
requirements-parser==0.13.0
reuse==6.2.0
rich==13.5.3
# via
# semgrep
# typer
roman-numerals==4.1.0 ; python_full_version >= '3.11'
# via roman-numerals-py
roman-numerals-py==4.1.0 ; python_full_version >= '3.11'
# via sphinx
rpds-py==0.30.0
# via
# jsonschema
# referencing
rstcheck==6.2.5
rstcheck-core==1.2.2
# via rstcheck
ruamel-yaml==0.19.1
# via semgrep
ruamel-yaml-clib==0.2.14
# via semgrep
ruff==0.14.10
selenium==4.39.0
semgrep==1.146.0
setuptools==80.9.0
shellingham==1.5.4
# via typer
six==1.17.0
# via python-dateutil
sniffio==1.3.1
# via trio
snowballstemmer==3.0.1
# via sphinx
sortedcontainers==2.4.0
# via trio
sphinx==8.1.3 ; python_full_version < '3.11'
# via
# sphinx-rtd-theme
# sphinxcontrib-jquery
sphinx==8.2.3 ; python_full_version >= '3.11'
# via
# sphinx-rtd-theme
# sphinxcontrib-jquery
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
# via sphinx
sphinxcontrib-devhelp==2.0.0
# via sphinx
sphinxcontrib-htmlhelp==2.1.0
# via sphinx
sphinxcontrib-jquery==4.1
# via sphinx-rtd-theme
sphinxcontrib-jsmath==1.0.1
# via sphinx
sphinxcontrib-qthelp==2.0.0
# via sphinx
sphinxcontrib-serializinghtml==2.0.0
# via sphinx
sse-starlette==3.1.2
# via mcp
starlette==0.50.0
# via
# mcp
# sse-starlette
tomli==2.0.2
# via
# pytest
# semgrep
# sphinx
tomlkit==0.13.3
# via reuse
trio==0.32.0
# via
# selenium
# trio-websocket
trio-websocket==0.12.2
# via selenium
typer==0.21.0
# via rstcheck
typing-extensions==4.15.0
# via
# anyio
# cryptography
# mcp
# opentelemetry-api
# opentelemetry-exporter-otlp-proto-http
# opentelemetry-sdk
# opentelemetry-semantic-conventions
# pydantic
# pydantic-core
# pyright
# referencing
# selenium
# semgrep
# starlette
# typer
# typing-inspection
# uvicorn
# virtualenv
typing-inspection==0.4.2
# via
# mcp
# pydantic
# pydantic-settings
urllib3==2.6.2
# via
# requests
# selenium
# semgrep
uvicorn==0.40.0 ; sys_platform != 'emscripten'
# via mcp
virtualenv==20.35.4
# via pre-commit
wcmatch==8.5.2
# via semgrep
webdriver-manager==4.0.2
websocket-client==1.9.0
# via selenium
wrapt==1.17.3
# via opentelemetry-instrumentation
wsproto==1.3.2
# via trio-websocket
zipp==3.23.0
# via importlib-metadata

View File

@ -1,7 +0,0 @@
psutil
defusedxml
orjson
reuse
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
sphinx
sphinx_rtd_theme

View File

@ -1,3 +0,0 @@
FROM glances:local-alpine-minimal as glancesminimal
COPY glances.conf /glances/conf/glances.conf
CMD python -m glances -C /glances/conf/glances.conf $GLANCES_OPT

View File

@ -1,40 +0,0 @@
version: "3.9"
services:
reverse-proxy:
image: traefik
command: --api --docker
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
whoami:
image: emilevauge/whoami
labels:
- "traefik.frontend.rule=Host:whoami.docker.localhost"
monitoring:
image: nicolargo/glances:dev
restart: unless-stopped
pid: host
privileged: true
network_mode: "host"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "/run/user/1000/podman/podman.sock:/run/user/1000/podman/podman.sock:ro"
- "./glances.conf:/glances/conf/glances.conf"
environment:
- TZ=${TZ}
- "GLANCES_OPT=-C /glances/conf/glances.conf -w"
# Uncomment for GPU compatibility (Nvidia) inside the container
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: 1
# capabilities: [gpu]
labels:
- "traefik.port=61208"
- "traefik.frontend.rule=Host:glances.docker.localhost"

View File

@ -1,20 +1,41 @@
version: '3.9'
services:
glances:
build:
context: ./
dockerfile: Dockerfile
# See all images tags here: https://hub.docker.com/r/nicolargo/glances/tags
image: nicolargo/glances:latest-full
restart: always
pid: "host"
privileged: true
network_mode: "host"
read_only: true
privileged: false
# Uncomment next line for SATA or NVME smartctl monitoring
# cap_add:
# Uncomment next line for SATA smartctl monitoring
# - SYS_RAWIO
# Uncomment next line for NVME smartctl monitoring
# - SYS_ADMIN
# devices:
# - "/dev/nvme0"
volumes:
- "/:/rootfs:ro"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "/run/user/1000/podman/podman.sock:/run/user/1000/podman/podman.sock:ro"
- "./glances.conf:/glances/conf/glances.conf"
# Uncomment for proper distro information in upper panel.
# # Works only for distros that do have this file (most of distros do).
# - "/etc/os-release:/etc/os-release:ro"
tmpfs:
- /tmp
environment:
- TZ=${TZ}
- GLANCES_OPT=-C /glances/conf/glances.conf -w
# Please set to your local timezone (or use local ${TZ} environment variable if set on your host)
- TZ=Europe/Paris
- GLANCES_OPT=-C /glances/conf/glances.conf -w --enable-plugin smart
- PYTHONPYCACHEPREFIX=/tmp/py_caches
# # Uncomment for GPU compatibility (Nvidia) inside the container
# deploy:
# resources:
@ -23,10 +44,11 @@ services:
# - driver: nvidia
# count: 1
# capabilities: [gpu]
# # Uncomment to protect Glances WebUI by a login/password (add --password to GLANCES_OPT)
# secrets:
# - source: glances_password
# target: /root/.config/glances/<login>.pwd
# Uncomment to protect Glances WebUI by a login/password (add --password to GLANCES_OPT)
# secrets:
# - source: glances_password
# target: /root/.config/glances/<login>.pwd
# secrets:
# glances_password:

130
docker-compose/glances.conf Executable file → Normal file
View File

@ -13,7 +13,7 @@ check_update=False
# Default is 1200 values (~1h with the default refresh rate)
history_size=1200
# Set the way Glances should display the date (default is %Y-%m-%d %H:%M:%S %Z)
# strftime_format=%Y-%m-%d %H:%M:%S %Z
#strftime_format=%Y-%m-%d %H:%M:%S %Z
# Define external directory for loading additional plugins
# The layout follows the glances standard for plugin definitions
#plugin_dir=/home/user/dev/plugins
@ -49,7 +49,7 @@ max_processes_display=25
# You can download it in a specific folder
# thanks to https://github.com/nicolargo/glances/issues/2021
# then configure this folder with the webui_root_path key
# Default is folder where glances_restfull_api.py is hosted
# Default is folder where glances_restful_api.py is hosted
#webui_root_path=
# CORS options
# Comma separated list of origins that should be permitted to make cross-origin requests.
@ -64,6 +64,10 @@ max_processes_display=25
# Comma separated list of HTTP request headers that should be supported for cross-origin requests.
# Default is *
#cors_headers=*
# Define SSL files (keyfile_password is optional)
#ssl_keyfile_password=kfp
#ssl_keyfile=./glances.local+3-key.pem
#ssl_certfile=./glances.local+3.pem
##############################################################################
# Plugins
@ -127,7 +131,7 @@ user_careful=50
user_warning=70
user_critical=90
user_log=False
#user_critical_action=echo {{user}} {{value}} {{max}} > /tmp/cpu.alert
#user_critical_action=echo "{{time}} User CPU {{user}} higher than {{critical}}" > /tmp/cpu.alert
#
system_careful=50
system_warning=70
@ -181,12 +185,14 @@ temperature_critical=80
[mem]
disable=False
# Display available memory instead of used memory
#available=True
# Define RAM thresholds in %
# Default values if not defined: 50/70/90
careful=50
#careful_action_repeat=echo {{percent}} >> /tmp/memory.alert
warning=70
critical=90
#critical_action_repeat=echo "{{time}} {{percent}} higher than {{critical}}"" >> /tmp/memory.alert
[memswap]
disable=False
@ -195,6 +201,7 @@ disable=False
careful=50
warning=70
critical=90
#warning_action=echo "{{time}} {{percent}} higher than {{warning}}"" > /tmp/memory.alert
[load]
disable=False
@ -241,8 +248,9 @@ hide_zero=False
#wlan0_tx_warning=900000
#wlan0_tx_critical=1000000
#wlan0_tx_log=True
#wlan0_rx_critical_action=echo "{{time}} {{interface_name}} RX {{bytes_recv_rate_per_sec}}Bps" > /tmp/network.alert
# Alias for network interface name
#alias=wlp2s0:WIFI
#alias=wlp0s20f3:WIFI
[ip]
# Disable display of private IP address
@ -300,15 +308,32 @@ hide_zero=False
#show=sda.*
# Alias for sda1 and sdb1
#alias=sda1:SystemDisk,sdb1:DataDisk
# Set thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
# Default latency thresholds (in ms) (rx = read / tx = write)
rx_latency_careful=10
rx_latency_warning=20
rx_latency_critical=50
tx_latency_careful=10
tx_latency_warning=20
tx_latency_critical=50
# Set latency thresholds (latency in ms) for a given disk name (rx = read / tx = write)
# dm-0_rx_latency_careful=10
# dm-0_rx_latency_warning=20
# dm-0_rx_latency_critical=50
# dm-0_rx_latency_log=False
# dm-0_tx_latency_careful=10
# dm-0_tx_latency_warning=20
# dm-0_tx_latency_critical=50
# dm-0_tx_latency_log=False
# There is no default bitrate thresholds for disk (because it is not possible to know the disk speed)
# Set bitrate thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
#dm-0_rx_careful=4000000000
#dm-0_rx_warning=5000000000
#dm-0_rx_critical=6000000000
#dm-0_rx_log=True
#dm-0_rx_log=False
#dm-0_tx_careful=700000000
#dm-0_tx_warning=900000000
#dm-0_tx_critical=1000000000
#dm-0_tx_log=True
#dm-0_tx_log=False
[fs]
disable=False
@ -318,11 +343,15 @@ hide=/boot.*,.*/snap.*
#show=/,/srv
# Define filesystem space thresholds in %
# Default values if not defined: 50/70/90
# It is also possible to define per mount point value
# Example: /_careful=40
careful=50
warning=70
critical=90
# It is also possible to define per mount point value
# Example: /_careful=40
#/_careful=1
#/_warning=5
#/_critical=10
#/_critical_action=echo "{{time}} {{mnt_point}} filesystem space {{percent}}% higher than {{critical}}%" > /tmp/fs.alert
# Allow additional file system types (comma-separated FS type)
#allow=shm
# Alias for root file system
@ -373,6 +402,8 @@ disable=True
#hide=.*Hide_this_driver.*
# Define the list of sensors to show (comma-separated regexp)
#show=.*Drive_Temperature.*
# List of attributes to hide (comma separated)
#hide_attributes=Self-tests,Errors
[hddtemp]
disable=False
@ -384,8 +415,8 @@ port=7634
# Documentation: https://glances.readthedocs.io/en/latest/aoa/sensors.html
disable=False
# Set the refresh multiplicator for the sensors
# By default refresh every Glances refresh * 3 (increase to reduce CPU consumption)
#refresh=3
# By default refresh every Glances refresh * 5 (increase to reduce CPU consumption)
#refresh=5
# Hide some sensors (comma separated list of regexp)
hide=unknown.*
# Show only the following sensors (comma separated list of regexp)
@ -393,10 +424,11 @@ hide=unknown.*
# Sensors core thresholds (in Celsius...)
# By default values are grabbed from the system
# Overwrite thresholds for a specific sensor
#temperature_core_Ambient_careful=45
#temperature_core_Ambient_warning=65
#temperature_core_Ambient_critical=80
#temperature_core_Ambient_log=False
# temperature_core_Ambient_careful=40
# temperature_core_Ambient_warning=60
# temperature_core_Ambient_critical=85
# temperature_core_Ambient_log=True
# temperature_core_Ambient_critical_action=echo "{{time}} {{label}} temperature {{value}}{{unit}} higher than {{critical}}{{unit}}" > /tmp/temperature.alert
# Overwrite thresholds for a specific type of sensor
#temperature_core_careful=45
#temperature_core_warning=65
@ -431,6 +463,8 @@ disable=False
# Stats that can be disabled: cpu_percent,memory_info,memory_percent,username,cpu_times,num_threads,nice,status,io_counters,cmdline
# Stats that can not be disable: pid,name
#disable_stats=cpu_percent,memory_info,memory_percent,username,cpu_times,num_threads,nice,status,io_counters,cmdline
# Disable display of virtual memory
#disable_virtual_memory=True
# Define CPU/MEM (per process) thresholds in %
# Default values if not defined: 50/70/90
cpu_careful=50
@ -447,13 +481,20 @@ mem_critical=90
nice_warning=-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19
#
# Nice: Example 2, low priority processes escalate from careful to critical
#nice_ok=O
#nice_careful=1,2,3,4,5,6,7,8,9
#nice_warning=10,11,12,13,14
#nice_critical=15,16,17,18,19
#
# Status: define threshold regarding the process status (first letter of process status)
# R: Running, S: Sleeping, Z: Zombie (complete list here https://psutil.readthedocs.io/en/latest/#process-status-constants)
status_ok=R,W,P,I
status_critical=Z,D
# Define the list of processes to export using:
# a comma-separated list of Glances filter
#export=.*firefox.*,pid:1234
# Define a list of process to focus on (comma-separated list of Glances filter)
#focus=.*firefox.*,.*python.*
[ports]
disable=False
@ -505,7 +546,8 @@ port_default_gateway=False
disable=True
# Define the maximum VMs size name (default is 20 chars)
max_name_size=20
# By default, Glances only display running VMs with states: 'Running', 'Starting' or 'Restarting'
# By default, Glances only display running VMs with states:
# 'Running', 'Paused', 'Starting' or 'Restarting'
# Set the following key to True to display all VMs regarding their states
all=False
@ -520,8 +562,8 @@ disable=False
# Define the maximum docker size name (default is 20 chars)
max_name_size=20
# List of stats to disable (not display)
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,command
; disable_stats=diskio,networkio
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,ports,command
disable_stats=command
# Thresholds for CPU and MEM (in %)
; cpu_careful=50
; cpu_warning=70
@ -599,6 +641,11 @@ disable=False
# Exports
##############################################################################
[export]
# Common section for all exporters
# Do not export following fields (comma separated list of regex)
#exclude_fields=.*_critical,.*_careful,.*_warning,.*\.key$
[graph]
# Configuration for the --export graph option
# Set the path where the graph (.svg files) will be created
@ -617,7 +664,7 @@ style=DarkStyle
[influxdb]
# !!!
# Will be DEPRECATED in future release.
# Please have a look on the new influxdb2 export module (compatible with InfluxDB 1.8.x and 2.x)
# Please have a look on the new influxdb3 export module
# !!!
# Configuration for the --export influxdb option
# https://influxdb.com/
@ -646,7 +693,28 @@ port=8086
protocol=http
org=nicolargo
bucket=glances
token=EjFUTWe8U-MIseEAkaVIgVnej_TrnbdvEcRkaB1imstW7gapSqy6_6-8XD-yd51V0zUUpDy-kAdVD1purDLuxA==
token=PUT_YOUR_INFLUXDB2_TOKEN_HERE
# Set the interval between two exports (in seconds)
# If the interval is set to 0, the Glances refresh time is used (default behavor)
#interval=0
# Prefix will be added for all measurement name
# Ex: prefix=foo
# => foo.cpu
# => foo.mem
# You can also use dynamic values
#prefix=foo
# Following tags will be added for all measurements
# You can also use dynamic values.
# Note: hostname and name (for process) are always added as a tag
#tags=foo:bar,spam:eggs,domain:`domainname`
[influxdb3]
# Configuration for the --export influxdb3 option
# https://influxdb.com/
host=http://localhost:8181
org=nicolargo
database=glances
token=PUT_YOUR_INFLUXDB3_TOKEN_HERE
# Set the interval between two exports (in seconds)
# If the interval is set to 0, the Glances refresh time is used (default behavor)
#interval=0
@ -812,6 +880,26 @@ prefix=glances
# By default, system_name = FQDN
#system_name=mycomputer
[timescaledb]
# Configuration for the --export timescaledb option
# https://www.timescale.com/
host=localhost
port=5432
db=glances
user=postgres
password=password
# Overwrite device name (default is the FQDN)
# Most of the time, you should not overwrite this value
#hostname=mycomputer
[nats]
# Configuration for the --export nats option
# https://nats.io/
# Host is a separated list of NATS nodes
host=nats://localhost:4222
# Prefix for the subjects (default is 'glances')
prefix=glances
##############################################################################
# AMPS
# * enable: Enable (true) or disable (false) the AMP

View File

@ -9,12 +9,12 @@
# WARNING: the Alpine image version and Python version should be set.
# Alpine 3.18 tag is a link to the latest 3.18.x version.
# Be aware that if you change the Alpine version, you may have to change the Python version.
ARG IMAGE_VERSION=3.21
ARG IMAGE_VERSION=3.23
ARG PYTHON_VERSION=3.12
##############################################################################
# Base layer to be used for building dependencies and the release images
FROM alpine:${IMAGE_VERSION} as base
FROM alpine:${IMAGE_VERSION} AS base
# Upgrade the system
RUN apk update \
@ -34,7 +34,7 @@ RUN apk add --no-cache \
# BUILD Stages
##############################################################################
# BUILD: Base image shared by all build images
FROM base as build
FROM base AS build
ARG PYTHON_VERSION
RUN apk add --no-cache \
@ -61,26 +61,21 @@ RUN apk add --no-cache \
RUN python${PYTHON_VERSION} -m venv venv-build
RUN /venv-build/bin/python${PYTHON_VERSION} -m pip install --upgrade pip
RUN python${PYTHON_VERSION} -m venv venv-build
RUN /venv-build/bin/python${PYTHON_VERSION} -m pip install --upgrade pip
RUN python${PYTHON_VERSION} -m venv --without-pip venv
COPY requirements.txt docker-requirements.txt webui-requirements.txt optional-requirements.txt ./
COPY pyproject.toml docker-requirements.txt all-requirements.txt ./
##############################################################################
# BUILD: Install the minimal image deps
FROM build as buildMinimal
FROM build AS buildminimal
ARG PYTHON_VERSION
RUN /venv-build/bin/python${PYTHON_VERSION} -m pip install --target="/venv/lib/python${PYTHON_VERSION}/site-packages" \
-r requirements.txt \
-r docker-requirements.txt \
-r webui-requirements.txt
-r docker-requirements.txt
##############################################################################
# BUILD: Install all the deps
FROM build as buildFull
FROM build AS buildfull
ARG PYTHON_VERSION
# Required for optional dependency cassandra-driver
@ -89,14 +84,13 @@ ARG CASS_DRIVER_NO_CYTHON=1
ARG CARGO_NET_GIT_FETCH_WITH_CLI=true
RUN /venv-build/bin/python${PYTHON_VERSION} -m pip install --target="/venv/lib/python${PYTHON_VERSION}/site-packages" \
-r requirements.txt \
-r optional-requirements.txt
-r all-requirements.txt
##############################################################################
# RELEASE Stages
##############################################################################
# Base image shared by all releases
FROM base as release
FROM base AS release
ARG PYTHON_VERSION
# Copy source code and config file
@ -108,40 +102,48 @@ COPY docker-bin.sh /usr/local/bin/glances
RUN chmod a+x /usr/local/bin/glances
ENV PATH="/venv/bin:$PATH"
# Copy binary and update PATH
COPY docker-bin.sh /usr/local/bin/glances
RUN chmod a+x /usr/local/bin/glances
ENV PATH="/venv/bin:$PATH"
# EXPOSE PORT (XMLRPC / WebUI)
EXPOSE 61209 61208
# Add glances user
# RUN addgroup -g 1000 glances && \
# adduser -D -u 1000 -G glances glances && \
# chown -R glances:glances /app
# Define default command.
WORKDIR /app
CMD /venv/bin/python3 -m glances $GLANCES_OPT
ENV PYTHON_VERSION=${PYTHON_VERSION}
CMD ["/bin/sh", "-c", "/venv/bin/python${PYTHON_VERSION} -m glances ${GLANCES_OPT}"]
################################################################################
# RELEASE: minimal
FROM release as minimal
FROM release AS minimal
COPY --from=buildMinimal /venv /venv
COPY --from=buildminimal /venv /venv
# USER glances
################################################################################
# RELEASE: full
FROM release as full
FROM release AS full
RUN apk add --no-cache libzmq
COPY --from=buildFull /venv /venv
COPY --from=buildfull /venv /venv
# USER glances
################################################################################
# RELEASE: dev - to be compatible with CI
FROM full as dev
FROM full AS dev
# Add the specific logger configuration file for Docker dev
# All logs will be forwarded to stdout
COPY ./docker-files/docker-logger.json /app
ENV LOG_CFG=/app/docker-logger.json
# USER glances
WORKDIR /app
CMD /venv/bin/python3 -m glances $GLANCES_OPT
ENV PYTHON_VERSION=${PYTHON_VERSION}
CMD ["/bin/sh", "-c", "/venv/bin/python${PYTHON_VERSION} -m glances ${GLANCES_OPT}"]

View File

@ -1,22 +1,24 @@
{
"version": 1,
"disable_existing_loggers": "False",
"root": {"level": "INFO", "handlers": ["console"]},
"formatters": {
"standard": {"format": "%(asctime)s -- %(levelname)s -- %(message)s"},
"short": {"format": "%(levelname)s -- %(message)s"},
"long": {"format": "%(asctime)s -- %(levelname)s -- %(message)s (%(funcName)s in %(filename)s)"},
"free": {"format": "%(message)s"}
},
"handlers": {
"console": {"class": "logging.StreamHandler", "formatter": "standard"}
},
"loggers": {
"debug": {"handlers": ["console"], "level": "DEBUG"},
"verbose": {"handlers": ["console"], "level": "INFO"},
"standard": {"handlers": ["console"], "level": "INFO"},
"requests": {"handlers": ["console"], "level": "ERROR"},
"elasticsearch": {"handlers": ["console"], "level": "ERROR"},
"elasticsearch.trace": {"handlers": ["console"], "level": "ERROR"}
}
}
"version": 1,
"disable_existing_loggers": "False",
"root": { "level": "INFO", "handlers": ["console"] },
"formatters": {
"standard": { "format": "%(asctime)s -- %(levelname)s -- %(message)s" },
"short": { "format": "%(levelname)s -- %(message)s" },
"long": {
"format": "%(asctime)s -- %(levelname)s -- %(message)s (%(funcName)s in %(filename)s)"
},
"free": { "format": "%(message)s" }
},
"handlers": {
"console": { "class": "logging.StreamHandler", "formatter": "standard" }
},
"loggers": {
"debug": { "handlers": ["console"], "level": "DEBUG" },
"verbose": { "handlers": ["console"], "level": "INFO" },
"standard": { "handlers": ["console"], "level": "INFO" },
"requests": { "handlers": ["console"], "level": "ERROR" },
"elasticsearch": { "handlers": ["console"], "level": "ERROR" },
"elasticsearch.trace": { "handlers": ["console"], "level": "ERROR" }
}
}

View File

@ -13,7 +13,7 @@ ARG PYTHON_VERSION=3.12
##############################################################################
# Base layer to be used for building dependencies and the release images
FROM ubuntu:${IMAGE_VERSION} as base
FROM ubuntu:${IMAGE_VERSION} AS base
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
@ -32,7 +32,7 @@ RUN apt-get update \
# BUILD Stages
##############################################################################
# BUILD: Base image shared by all build images
FROM base as build
FROM base AS build
ARG PYTHON_VERSION
ARG DEBIAN_FRONTEND=noninteractive
@ -55,32 +55,29 @@ RUN apt-get clean \
RUN python3 -m venv --without-pip venv
COPY requirements.txt docker-requirements.txt webui-requirements.txt optional-requirements.txt ./
COPY pyproject.toml docker-requirements.txt all-requirements.txt ./
##############################################################################
# BUILD: Install the minimal image deps
FROM build as buildMinimal
FROM build AS buildminimal
ARG PYTHON_VERSION
RUN python3 -m pip install --target="/venv/lib/python${PYTHON_VERSION}/site-packages" \
-r requirements.txt \
-r docker-requirements.txt \
-r webui-requirements.txt
-r docker-requirements.txt
##############################################################################
# BUILD: Install all the deps
FROM build as buildFull
FROM build AS buildfull
ARG PYTHON_VERSION
RUN python3 -m pip install --target="/venv/lib/python${PYTHON_VERSION}/site-packages" \
-r requirements.txt \
-r optional-requirements.txt
-r all-requirements.txt
##############################################################################
# RELEASE Stages
##############################################################################
# Base image shared by all releases
FROM base as release
FROM base AS release
ARG PYTHON_VERSION
# Copy Glances source code and config file
@ -92,28 +89,34 @@ COPY docker-bin.sh /usr/local/bin/glances
RUN chmod a+x /usr/local/bin/glances
ENV PATH="/venv/bin:$PATH"
# Copy binary and update PATH
COPY docker-bin.sh /usr/local/bin/glances
RUN chmod a+x /usr/local/bin/glances
ENV PATH="/venv/bin:$PATH"
# EXPOSE PORT (XMLRPC / WebUI)
EXPOSE 61209 61208
# Add glances user
# NOTE: If used, the Glances Docker plugin do not work...
# UID and GUID 1000 are already configured for the ubuntu user
# Create anew one with UID and GUID 1001
# RUN groupadd -g 1001 glances && \
# useradd -u 1001 -g glances glances && \
# chown -R glances:glances /app
# Define default command.
WORKDIR /app
CMD /venv/bin/python3 -m glances $GLANCES_OPT
ENV PYTHON_VERSION=${PYTHON_VERSION}
CMD ["/bin/sh", "-c", "/venv/bin/python${PYTHON_VERSION} -m glances ${GLANCES_OPT}"]
################################################################################
# RELEASE: minimal
FROM release as minimal
FROM release AS minimal
ARG PYTHON_VERSION
COPY --from=buildMinimal /venv /venv
# USER glances
################################################################################
# RELEASE: full
FROM release as full
FROM release AS full
ARG PYTHON_VERSION
RUN apt-get update \
@ -121,11 +124,13 @@ RUN apt-get update \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY --from=buildFull /venv /venv
COPY --from=buildfull /venv /venv
# USER glances
################################################################################
# RELEASE: dev - to be compatible with CI
FROM full as dev
FROM full AS dev
ARG PYTHON_VERSION
# Add the specific logger configuration file for Docker dev
@ -133,5 +138,8 @@ ARG PYTHON_VERSION
COPY ./docker-files/docker-logger.json /app
ENV LOG_CFG=/app/docker-logger.json
# USER glances
WORKDIR /app
CMD /venv/bin/python3 -m glances $GLANCES_OPT
ENV PYTHON_VERSION=${PYTHON_VERSION}
CMD ["/bin/sh", "-c", "/venv/bin/python${PYTHON_VERSION} -m glances ${GLANCES_OPT}"]

View File

@ -1,10 +1,83 @@
# install with base requirements file
-r requirements.txt
docker>=6.1.1
orjson # JSON Serialization speedup
podman
python-dateutil
requests
six
urllib3
# This file was autogenerated by uv via the following command:
# uv export --no-emit-workspace --no-hashes --no-group dev --extra containers --extra web --output-file docker-requirements.txt
annotated-doc==0.0.4
# via fastapi
annotated-types==0.7.0
# via pydantic
anyio==4.12.0
# via starlette
certifi==2025.11.12
# via requests
charset-normalizer==3.4.4
# via requests
click==8.1.8
# via uvicorn
colorama==0.4.6 ; sys_platform == 'win32'
# via click
defusedxml==0.7.1
# via glances
docker==7.1.0
# via glances
exceptiongroup==1.2.2 ; python_full_version < '3.11'
# via anyio
fastapi==0.128.0
# via glances
h11==0.16.0
# via uvicorn
idna==3.11
# via
# anyio
# requests
jinja2==3.1.6
# via glances
markupsafe==3.0.3
# via jinja2
packaging==25.0
# via glances
podman==5.6.0
# via glances
psutil==7.2.1
# via glances
pydantic==2.12.5
# via fastapi
pydantic-core==2.41.5
# via pydantic
python-dateutil==2.9.0.post0
# via glances
pywin32==311 ; sys_platform == 'win32'
# via docker
requests==2.32.5
# via
# docker
# glances
# podman
shtab==1.8.0 ; sys_platform != 'win32'
# via glances
six==1.17.0
# via
# glances
# python-dateutil
starlette==0.50.0
# via fastapi
tomli==2.0.2 ; python_full_version < '3.11'
# via podman
typing-extensions==4.15.0
# via
# anyio
# fastapi
# pydantic
# pydantic-core
# starlette
# typing-inspection
# uvicorn
typing-inspection==0.4.2
# via pydantic
urllib3==2.6.2
# via
# docker
# podman
# requests
uvicorn==0.40.0
# via glances
windows-curses==2.4.1 ; sys_platform == 'win32'
# via glances

View File

@ -3,7 +3,7 @@
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = ../venv/bin/sphinx-build
SPHINXBUILD = ../.venv/bin/sphinx-build
PAPER =
BUILDDIR = _build

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 76 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because one or more lines are too long

BIN
docs/_static/screenshot-fetch.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -3,7 +3,7 @@
Actions
=======
Glances can trigger actions on events.
Glances can trigger actions on events for warning and critical thresholds.
By ``action``, we mean all shell command line. For example, if you want
to execute the ``foo.py`` script if the last 5 minutes load are critical
@ -18,6 +18,13 @@ then add the ``_action`` line to the Glances configuration file:
All the stats are available in the command line through the use of the
`Mustache`_ syntax. `Chevron`_ is required to render the mustache's template syntax.
Additionaly to the stats of the current plugin, the following variables are
also available:
- ``{{time}}``: current time in ISO format
- ``{{critical}}``: critical threshold value
- ``{{warning}}``: warning threshold value
- ``{{careful}}``: careful threshold value
Another example would be to create a log file
containing used vs total disk space if a space trigger warning is
reached:
@ -26,7 +33,7 @@ reached:
[fs]
warning=70
warning_action=echo {{mnt_point}} {{used}}/{{size}} > /tmp/fs.alert
warning_action=echo "{{time}} {{mnt_point}} {{used}}/{{size}}" > /tmp/fs.alert
A last example would be to create a log file containing the total user disk
space usage for a device and notify by email each time a space trigger
@ -36,13 +43,11 @@ critical is reached:
[fs]
critical=90
critical_action_repeat=echo {{device_name}} {{percent}} > /tmp/fs.alert && python /etc/glances/actions.d/fs-critical.py
critical_action_repeat=echo "{{time}} {{device_name}} {{percent}}" > /tmp/fs.alert && python /etc/glances/actions.d/fs-critical.py
.. note::
Use && as separator for multiple commands
Within ``/etc/glances/actions.d/fs-critical.py``:
.. code-block:: python
@ -63,7 +68,7 @@ Within ``/etc/glances/actions.d/fs-critical.py``:
.. note::
You can use all the stats for the current plugin. See
https://github.com/nicolargo/glances/wiki/The-Glances-RESTFULL-JSON-API
https://github.com/nicolargo/glances/wiki/The-Glances-RESTFUL-JSON-API
for the stats list.
It is also possible to repeat action until the end of the alert.

View File

@ -32,8 +32,8 @@ under the ``[containers]`` section:
# Define the maximum containers size name (default is 20 chars)
max_name_size=20
# List of stats to disable (not display)
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,command
disable_stats=diskio,networkio
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,ports,command
disable_stats=command
# Global containers' thresholds for CPU and MEM (in %)
cpu_careful=50
cpu_warning=70

View File

@ -5,21 +5,16 @@ Disk I/O
.. image:: ../_static/diskio.png
Glances displays the disk I/O throughput. The unit is adapted
dynamically.
You can display:
Glances displays the disk I/O throughput, count and mean latency:
- bytes per second (default behavior / Bytes/s, KBytes/s, MBytes/s, etc)
- requests per second (using --diskio-iops option or *B* hotkey)
- mean latency (using --diskio-latency option or *L* hotkey)
There is no alert on this information.
It's possible to define:
It's also possible to define:
- a list of disk to show (white list)
- a list of disks to hide
- aliases for disk name
- aliases for disk name (use \ to espace special characters)
under the ``[diskio]`` section in the configuration file.
@ -42,13 +37,20 @@ Filtering is based on regular expression. Please be sure that your regular
expression works as expected. You can use an online tool like `regex101`_ in
order to test your regular expression.
It is also possible to define thesholds for bytes read and write per second:
It is also possible to define thesholds for latency and bytes read and write per second:
.. code-block:: ini
[diskio]
# Alias for sda1 and sdb1
#alias=sda1:SystemDisk,sdb1:DataDisk
# Default latency thresholds (in ms) (rx = read / tx = write)
rx_latency_careful=10
rx_latency_warning=20
rx_latency_critical=50
tx_latency_careful=10
tx_latency_warning=20
tx_latency_critical=50
# Set thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
dm-0_rx_careful=4000000000
dm-0_rx_warning=5000000000

View File

@ -35,6 +35,11 @@ system:
[fs]
allow=shm
With the above configuration key, it is also possible to monitor NFS
mount points (allow=nfs). Be aware that this can slow down the
performance of the plugin if the NFS server is not reachable. In this
case, the plugin will wait for a 2 seconds timeout.
Also, you can hide mount points using regular expressions.
To hide all mount points starting with /boot and /snap:

View File

@ -41,6 +41,7 @@ Legend:
hddtemp
ps
containers
vms
amps
events
actions

View File

@ -27,7 +27,7 @@ Stats description:
is in RAM.
- **inactive**: (UNIX): memory that is marked as not used.
- **buffers**: (Linux, BSD): cache for things like file system metadata.
- **cached**: (Linux, BSD): cache for various things.
- **cached**: (Linux, BSD): cache for various things (including ZFS cache).
Additional stats available in through the API:
@ -41,6 +41,10 @@ Additional stats available in through the API:
- **shared**: (BSD): memory that may be simultaneously accessed by multiple
processes.
It is possible to display the available memory instead of the used memory
by setting the ``available`` option to ``True`` in the configuration file
under the ``[mem]`` section.
A character is also displayed just after the MEM header and shows the
trend value:

View File

@ -20,7 +20,7 @@ Additionally, you can define:
- automatically hide interfaces not up
- automatically hide interfaces without IP address
- per-interface limit values
- aliases for interface name
- aliases for interface name (use \ to espace special characters)
The configuration should be done in the ``[network]`` section of the
Glances configuration file.
@ -72,7 +72,7 @@ can also be used to set a threshold higher than zero.
.. code-block:: ini
[diskio]
[network]
hide_zero=True
hide_threshold_bytes=0

View File

@ -102,7 +102,8 @@ Columns display
``CPU%`` % of CPU used by the process
If Irix/Solaris mode is off ('0' key), the value
is divided by logical core number
is divided by logical core number (the column
name became CPUi)
``MEM%`` % of MEM used by the process (RES divided by
the total RAM you have)
``VIRT`` Virtual Memory Size
@ -148,12 +149,24 @@ Columns display
pressing on the ``'/'`` key
========================= ==============================================
Disable display of virtual memory
---------------------------------
It's possible to disable the display of the VIRT column (virtual memory) by adding the
``disable_virtual_memory=True`` option in the ``[processlist]`` section of the configuration
file (glances.conf):
.. code-block:: ini
[processlist]
disable_virtual_memory=True
Process filtering
-----------------
It's possible to filter the processes list using the ``ENTER`` key.
Filter syntax is the following (examples):
Glances filter syntax is the following (examples):
- ``python``: Filter processes name or command line starting with
*python* (regexp)
@ -162,6 +175,25 @@ Filter syntax is the following (examples):
- ``username:nicolargo``: Processes of nicolargo user (key:regexp)
- ``cmdline:\/usr\/bin.*``: Processes starting by */usr/bin*
Process focus
-------------
It's also possible to select a processes list to focus on.
A list of Glances filters (see upper) can be define from the command line:
.. code-block:: bash
glances --process-focus .*python.*,.*firefox.*
or the glances.conf file:
.. code-block:: ini
[processlist]
focus=.*python.*,.*firefox.*
Extended info
-------------

View File

@ -33,6 +33,7 @@ thresholds (default behavor).
#temperature_core_careful=45
#temperature_core_warning=65
#temperature_core_critical=80
#alias=temp1:Motherboard 0,core 0:CPU Core 0
.. note 1::
The support for multiple batteries is only available if

View File

@ -23,25 +23,32 @@ How to read the information:
.. warning::
This plugin needs administrator rights. Please run Glances as root/admin.
Also, you can hide sensors using regular expressions.
Also, you can hide driver using regular expressions.
To hide sensors you should use the hide option:
To hide device you should use the hide option:
.. code-block:: ini
[smart]
hide=.*Hide_this_driver.*
hide=.*Hide_this_device.*
It is also possible to configure a white list of devices to display.
Example to only show .*Drive_Temperature.* sensors:
Example to show only the specified drive:
.. code-block:: ini
[smart]
show=.*Drive_Temperature.*
show=.*Show_this_device.*
Filtering is based on regular expression. Please be sure that your regular
expression works as expected. You can use an online tool like `regex101`_ in
order to test your regular expression.
.. _regex101: https://regex101.com/
.. _regex101: https://regex101.com/
You can also hide attributes, for example Self-tests, Errors, etc. Use a comma separated list.
.. code-block:: ini
[smart]
hide_attributes=attribute_name1,attribute_name2

36
docs/aoa/vms.rst Normal file
View File

@ -0,0 +1,36 @@
.. _vms:
VMs
===
Glances ``vms`` plugin is designed to display stats about VMs ran on the host.
It's actually support two engines: `Multipass` and `Virsh`.
No Python dependency is needed but Multipass and Virsh binary should be available:
- multipass should be executable from /snap/bin/multipass
- virsh should be executable from /usr/bin/virsh
Note: CPU information is not availble for Multipass VM. Load is not available for Virsh VM.
Configuration file options:
.. code-block:: ini
[vms]
disable=True
# Define the maximum VMs size name (default is 20 chars)
max_name_size=20
# By default, Glances only display running VMs with states:
# 'Running', 'Paused', 'Starting' or 'Restarting'
# Set the following key to True to display all VMs regarding their states
all=False
You can use all the variables ({{foo}}) available in the containers plugin.
Filtering (for hide or show) is based on regular expression. Please be sure that your regular
expression works as expected. You can use an online tool like `regex101`_ in
order to test your regular expression.
.. _Multipass: https://canonical.com/multipass
.. _Virsh: https://www.libvirt.org/manpages/virsh.html

1
docs/api/openapi.json Normal file

File diff suppressed because one or more lines are too long

1570
docs/api/python.rst Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,5 @@
#!/bin/sh
make clean
make html
LC_ALL=C make man

View File

@ -18,6 +18,10 @@ Command-Line Options
enable debug mode
.. option:: --print-completion
generate shell tab completion scripts for Glances CLI
.. option:: -C CONF_FILE, --config CONF_FILE
path to the configuration file
@ -348,7 +352,7 @@ The following commands (key pressed) are supported while in Glances:
Show/hide RAID plugin
``s``
Show/hide sensors stats
Show/hide sensors plugin
``S``
Enable/disable spark lines
@ -365,6 +369,9 @@ The following commands (key pressed) are supported while in Glances:
``U``
View cumulative network I/O
``V``
Show/hide VMS plugin
``w``
Delete finished warning log messages
@ -409,12 +416,18 @@ The following commands (key pressed) are supported while in Glances:
``F5`` or ``CTRL-R``
Refresh user interface
``LEFT``
``SHIFT-LEFT``
Navigation left through the process sort
``RIGHT``
``SHIFT-RIGHT``
Navigation right through the process sort
``LEFT``
Navigation left through the process name
``RIGHT``
Navigation right through the process name
``UP``
Up in the processes list

View File

@ -80,7 +80,7 @@ than a second one concerning the user interface:
# You can download it in a specific folder
# thanks to https://github.com/nicolargo/glances/issues/2021
# then configure this folder with the webui_root_path key
# Default is folder where glances_restfull_api.py is hosted
# Default is folder where glances_restful_api.py is hosted
#webui_root_path=
# CORS options
# Comma separated list of origins that should be permitted to make cross-origin requests.
@ -95,6 +95,10 @@ than a second one concerning the user interface:
# Comma separated list of HTTP request headers that should be supported for cross-origin requests.
# Default is *
#cors_headers=*
# Define SSL files (keyfile_password is optional)
#ssl_keyfile=./glances.local+3-key.pem
#ssl_keyfile_password=kfp
#ssl_certfile=./glances.local+3.pem
Each plugin, export module, and application monitoring process (AMP) can
have a section. Below is an example for the CPU plugin:

View File

@ -187,7 +187,7 @@ and make it visible to your container by adding it to ``docker-compose.yml`` as
image: nicolargo/glances:latest
restart: always
environment:
- GLANCES_OPT="-w --password"
- "GLANCES_OPT=-w --password"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# Uncomment the below line if you want glances to display host OS detail instead of container's

View File

@ -3,8 +3,51 @@
F.A.Q
=====
*Any encoding issue ?*
Any encoding issue ?
--------------------
Try to run Glances with the following command line:
LANG=en_US.UTF-8 LC_ALL= glances
Container memory stats not displayed ?
--------------------------------------
On ARM64, Docker needs to be configured to allow access to the memory stats.
Edit the /boot/firmware/cmdline.txt and add the following configuration key:
cgroup_enable=memory
Netifaces issue ?
-----------------
Previously, Glances uses Netifaces to get network interfaces information.
Now, Glances uses Netifaces2.
Please uninstall Netifaces and install Netifaces2 instead.
Extra note: Glances 4.5 or higher do not use Netifaces/Netifaces2 anymore.
On Debian/Ubuntu Operating Systems, Webserver display a blank screen ?
----------------------------------------------------------------------
For some reason, the Glances Debian/Ubuntu packages do not include the Web UI static files.
Please read: https://github.com/nicolargo/glances/issues/2021 for workaround and more information.
Glances said that my computer has no free memory, is it normal ?
----------------------------------------------------------------
On Linux, Glances shows by default the free memory.
Free memory can be low, it's a "normal" behavior because Linux uses free memory for disk caching
to improve performance. More information can be found here: https://linuxatemyram.com/.
If you want to display the "available" memory instead of the "free" memory, you can uses the
the following configuration key in the Glances configuration file:
[mem]
# Display available memory instead of used memory
available=True

46
docs/fetch.rst Normal file
View File

@ -0,0 +1,46 @@
.. _fetch:
Fetch
=====
The fetch mode is used to get and share a quick look of a machine using the
``fetch`` option. In this mode, current stats are displayed on the console in
a fancy way.
.. code-block:: console
$ glances --fetch
Results look like this:
.. image:: _static/screenshot-fetch.png
It is also possible to use a custom template with the ``--fetch-template </path/to/template.jinja>`` option.
Some examples are provided in the ``conf/fetch-templates/`` directory. Please feel free to
customize them or create your own template (contribution via PR are welcome).
The format of the template is based on the Jinja2 templating engine and can use all the stats
available in Glances through the ``gl`` variable (an instance of the :ref:`Glances Python API<api>`).
For example, the default template is define as:
.. code-block:: jinja
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✨ {{ gl.system['hostname'] }}{{ ' - ' + gl.ip['address'] if gl.ip['address'] else '' }}
⚙️ {{ gl.system['hr_name'] }} | Uptime: {{ gl.uptime }}
💡 LOAD {{ '%0.2f'| format(gl.load['min1']) }} {{ '%0.2f'| format(gl.load['min5']) }} {{ '%0.2f'| format(gl.load['min15']) }}
⚡ CPU {{ gl.bar(gl.cpu['total']) }} {{ gl.cpu['total'] }}% of {{ gl.core['log'] }} cores
🧠 MEM {{ gl.bar(gl.mem['percent']) }} {{ gl.mem['percent'] }}% ({{ gl.auto_unit(gl.mem['used']) }} {{ gl.auto_unit(gl.mem['total']) }})
{% for fs in gl.fs.keys() %}💾 {% if loop.index == 1 %}DISK{% else %} {% endif %} {{ gl.bar(gl.fs[fs]['percent']) }} {{ gl.fs[fs]['percent'] }}% ({{ gl.auto_unit(gl.fs[fs]['used']) }} {{ gl.auto_unit(gl.fs[fs]['size']) }}) for {{ fs }}
{% endfor %}{% for net in gl.network.keys() %}📡 {% if loop.index == 1 %}NET{% else %} {% endif %} ↓ {{ gl.auto_unit(gl.network[net]['bytes_recv_rate_per_sec']) }}b/s ↑ {{ gl.auto_unit(gl.network[net]['bytes_sent_rate_per_sec']) }}b/s for {{ net }}
{% endfor %}
🔥 TOP PROCESS by CPU
{% for process in gl.top_process() %}{{ loop.index }}️⃣ {{ process['name'][:20] }}{{ ' ' * (20 - process['name'][:20] | length) }} ⚡ {{ process['cpu_percent'] }}% CPU{{ ' ' * (8 - (gl.auto_unit(process['cpu_percent']) | length)) }} 🧠 {{ gl.auto_unit(process['memory_info']['rss']) }}B MEM
{% endfor %}
🔥 TOP PROCESS by MEM
{% for process in gl.top_process(sorted_by='memory_percent', sorted_by_secondary='cpu_percent') %}{{ loop.index }}️⃣ {{ process['name'][:20] }}{{ ' ' * (20 - process['name'][:20] | length) }} 🧠 {{ gl.auto_unit(process['memory_info']['rss']) }}B MEM{{ ' ' * (7 - (gl.auto_unit(process['memory_info']['rss']) | length)) }} ⚡ {{ process['cpu_percent'] }}% CPU
{% endfor %}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

110
docs/gw/duckdb.rst Normal file
View File

@ -0,0 +1,110 @@
.. _duckdb:
DuckDB
===========
DuckDB is an in-process SQL OLAP database management system.
You can export statistics to a ``DuckDB`` server.
The connection should be defined in the Glances configuration file as
following:
.. code-block:: ini
[duckdb]
# database defines where data are stored, can be one of:
# /path/to/glances.db (see https://duckdb.org/docs/stable/clients/python/dbapi#file-based-connection)
# :memory:glances (see https://duckdb.org/docs/stable/clients/python/dbapi#in-memory-connection)
# Or anyone else supported by the API (see https://duckdb.org/docs/stable/clients/python/dbapi)
database=/tmp/glances.db
and run Glances with:
.. code-block:: console
$ glances --export duckdb
Data model
-----------
The data model is composed of one table per Glances plugin.
Example:
.. code-block:: python
>>> import duckdb
>>> db = duckdb.connect(database='/tmp/glances.db', read_only=True)
>>> db.sql("SELECT * from cpu")
┌─────────────────────┬─────────────────┬────────┬────────┬────────┬───┬────────────────────┬─────────────────────┬──────────────────────┬──────────────────────┬──────────────────────┐
│ time │ hostname_id │ total │ user │ nice │ … │ cpu_iowait_warning │ cpu_iowait_critical │ cpu_ctx_switches_c… │ cpu_ctx_switches_w… │ cpu_ctx_switches_c… │
│ time with time zone │ varchar │ double │ double │ double │ │ double │ double │ double │ double │ double │
├─────────────────────┼─────────────────┼────────┼────────┼────────┼───┼────────────────────┼─────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┤
│ 11:50:25+00 │ nicolargo-xps15 │ 8.0 │ 5.6 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:50:27+00 │ nicolargo-xps15 │ 4.3 │ 3.2 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:50:29+00 │ nicolargo-xps15 │ 4.3 │ 3.2 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:50:31+00 │ nicolargo-xps15 │ 14.9 │ 15.7 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:50:33+00 │ nicolargo-xps15 │ 14.9 │ 15.7 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:50:35+00 │ nicolargo-xps15 │ 8.2 │ 7.8 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:50:37+00 │ nicolargo-xps15 │ 8.2 │ 7.8 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:50:39+00 │ nicolargo-xps15 │ 12.7 │ 10.3 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:50:41+00 │ nicolargo-xps15 │ 12.7 │ 10.3 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:50:43+00 │ nicolargo-xps15 │ 12.2 │ 10.3 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ 11:51:29+00 │ nicolargo-xps15 │ 10.1 │ 7.4 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:51:32+00 │ nicolargo-xps15 │ 10.1 │ 7.4 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:51:34+00 │ nicolargo-xps15 │ 6.6 │ 4.9 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:51:36+00 │ nicolargo-xps15 │ 6.6 │ 4.9 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:51:38+00 │ nicolargo-xps15 │ 9.9 │ 7.5 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:51:40+00 │ nicolargo-xps15 │ 9.9 │ 7.5 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:51:42+00 │ nicolargo-xps15 │ 4.0 │ 3.1 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:51:44+00 │ nicolargo-xps15 │ 4.0 │ 3.1 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:51:46+00 │ nicolargo-xps15 │ 11.1 │ 8.8 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
│ 11:51:48+00 │ nicolargo-xps15 │ 11.1 │ 8.8 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
├─────────────────────┴─────────────────┴────────┴────────┴────────┴───┴────────────────────┴─────────────────────┴──────────────────────┴──────────────────────┴──────────────────────┤
│ 41 rows (20 shown) 47 columns (10 shown) │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
>>> db.sql("SELECT * from cpu").fetchall()[0]
(datetime.time(11, 50, 25, tzinfo=datetime.timezone.utc), 'nicolargo-xps15', 8.0, 5.6, 0.0, 2.3, 91.9, 0.1, 0.0, 0.0, 0.0, 0, 0, 0, 0, 16, 2.4103684425354004, 90724823, 0, 63323797, 0, 30704572, 0, 0, 0, 1200.0, 65.0, 75.0, 85.0, True, 50.0, 70.0, 90.0, True, 50.0, 70.0, 90.0, True, 50.0, 70.0, 90.0, 5.0, 5.625, 6.25, 640000.0, 720000.0, 800000.0)
>>> db.sql("SELECT * from network")
┌─────────────────────┬─────────────────┬────────────────┬────────────┬────────────┬───┬─────────────────────┬────────────────┬────────────────────┬────────────────────┬───────────────────┐
│ time │ hostname_id │ key_id │ bytes_sent │ bytes_recv │ … │ network_tx_critical │ network_hide │ network_hide_no_up │ network_hide_no_ip │ network_hide_zero │
│ time with time zone │ varchar │ varchar │ int64 │ int64 │ │ double │ varchar │ boolean │ boolean │ boolean │
├─────────────────────┼─────────────────┼────────────────┼────────────┼────────────┼───┼─────────────────────┼────────────────┼────────────────────┼────────────────────┼───────────────────┤
│ 11:50:25+00 │ nicolargo-xps15 │ interface_name │ 407761 │ 32730 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:50:27+00 │ nicolargo-xps15 │ interface_name │ 2877 │ 4857 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:50:29+00 │ nicolargo-xps15 │ interface_name │ 44504 │ 32555 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:50:31+00 │ nicolargo-xps15 │ interface_name │ 1092285 │ 48600 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:50:33+00 │ nicolargo-xps15 │ interface_name │ 150119 │ 43805 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:50:35+00 │ nicolargo-xps15 │ interface_name │ 34424 │ 14825 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:50:37+00 │ nicolargo-xps15 │ interface_name │ 19382 │ 33614 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:50:39+00 │ nicolargo-xps15 │ interface_name │ 53060 │ 39780 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:50:41+00 │ nicolargo-xps15 │ interface_name │ 371914 │ 78626 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:50:43+00 │ nicolargo-xps15 │ interface_name │ 82356 │ 60612 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
│ 11:51:29+00 │ nicolargo-xps15 │ interface_name │ 3766 │ 9977 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:51:32+00 │ nicolargo-xps15 │ interface_name │ 188036 │ 18668 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:51:34+00 │ nicolargo-xps15 │ interface_name │ 543 │ 2451 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:51:36+00 │ nicolargo-xps15 │ interface_name │ 8247 │ 7275 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:51:38+00 │ nicolargo-xps15 │ interface_name │ 7252 │ 986 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:51:40+00 │ nicolargo-xps15 │ interface_name │ 172 │ 132 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:51:42+00 │ nicolargo-xps15 │ interface_name │ 8080 │ 6640 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:51:44+00 │ nicolargo-xps15 │ interface_name │ 19660 │ 17830 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:51:46+00 │ nicolargo-xps15 │ interface_name │ 1007030 │ 84170 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
│ 11:51:48+00 │ nicolargo-xps15 │ interface_name │ 128947 │ 18087 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
├─────────────────────┴─────────────────┴────────────────┴────────────┴────────────┴───┴─────────────────────┴────────────────┴────────────────────┴────────────────────┴───────────────────┤
│ 41 rows (20 shown) 28 columns (10 shown) │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
.. _duckdb: https://duckdb.org/

View File

@ -3,8 +3,24 @@
Gateway To Other Services
=========================
Glances can exports stats to a CSV file. Also, it can act as a gateway
to providing stats to multiple services (see list below).
Glances can exports stats in files or to other services like databases, message queues, etc.
Each exporter has its own configuration options, which can be set in the Glances
configuration file (`glances.conf`).
A common options section is also available:
is the `exclude_fields` option, which allows you to specify
.. code-block:: ini
[export]
# Common section for all exporters
# Do not export following fields (comma separated list of regex)
exclude_fields=.*_critical,.*_careful,.*_warning,.*\.key$
This section describes the available exporters and how to configure them:
.. toctree::
:maxdepth: 2
@ -14,15 +30,18 @@ to providing stats to multiple services (see list below).
couchdb
elastic
graph
graphite
influxdb
json
kafka
mqtt
mongodb
nats
opentsdb
prometheus
rabbitmq
restful
riemann
statsd
timescaledb
zeromq

View File

@ -17,19 +17,19 @@ Glances InfluxDB data model:
+---------------+-----------------------+-----------------------+
| Measurement | Fields | Tags |
+===============+=======================+=======================+
| cpu | user | hostname |
| cpu | user | hostname |
| | system | |
| | iowait... | |
+---------------+-----------------------+-----------------------+
| network | read_bytes | hostname |
| | write_bytes | disk_name |
| | time_since_update... | |
|  | | |
| | | |
+---------------+-----------------------+-----------------------+
| diskio | rx | hostname |
| | tx | interface_name |
| | time_since_update... | |
|  | | |
| | | |
+---------------+-----------------------+-----------------------+
| docker | cpu_percent | hostname |
| | memory_usage... | name |
@ -78,7 +78,7 @@ configuration file (no limit on columns number).
Note: if you want to use SSL, please set 'protocol=https'.
InfluxDB v2 (from InfluxDB v1.8.x/Flux and InfluxDB v2.x)
InfluxDB v2 (from InfluxDB v1.8.x/Flux and InfluxDB <v3.x)
---------------------------------------------------------
Note: The InfluxDB v2 client (https://pypi.org/project/influxdb-client/)
@ -90,12 +90,14 @@ following:
.. code-block:: ini
[influxdb2]
# Configuration for the --export influxdb2 option
# https://influxdb.com/
host=localhost
port=8086
protocol=http
org=nicolargo
bucket=glances
token=EjFUTWe8U-MIseEAkaVIgVnej_TrnbdvEcRkaB1imstW7gapSqy6_6-8XD-yd51V0zUUpDy-kAdVD1purDLuxA==
token=PUT_YOUR_INFLUXDB2_TOKEN_HERE
# Set the interval between two exports (in seconds)
# If the interval is set to 0, the Glances refresh time is used (default behavor)
#interval=0
@ -107,7 +109,7 @@ following:
#prefix=foo
# Following tags will be added for all measurements
# You can also use dynamic values.
# Note: hostname is always added as a tag
# Note: hostname and name (for process) are always added as a tag
#tags=foo:bar,spam:eggs,domain:`domainname`
and run Glances with:
@ -118,6 +120,46 @@ and run Glances with:
Note: if you want to use SSL, please set 'protocol=https'.
InfluxDB v3 (for InfluxDB 3.x)
------------------------------
Note: The InfluxDB v3 client (https://pypi.org/project/influxdb3-python/)
is only available for Python 3.8 or higher.
The connection should be defined in the Glances configuration file as
following:
.. code-block:: ini
[influxdb3]
# Configuration for the --export influxdb3 option
# https://influxdb.com/
host=http://localhost:8181
org=nicolargo
database=glances
token=PUT_YOUR_INFLUXDB3_TOKEN_HERE
# Set the interval between two exports (in seconds)
# If the interval is set to 0, the Glances refresh time is used (default behavor)
#interval=0
# Prefix will be added for all measurement name
# Ex: prefix=foo
# => foo.cpu
# => foo.mem
# You can also use dynamic values
#prefix=foo
# Following tags will be added for all measurements
# You can also use dynamic values.
# Note: hostname and name (for process) are always added as a tag
#tags=foo:bar,spam:eggs,domain:`domainname`
and run Glances with:
.. code-block:: console
$ glances --export influxdb3
Note: if you want to use SSL, please set host with 'https' scheme instead of 'http'.
Grafana
-------

68
docs/gw/nats.rst Normal file
View File

@ -0,0 +1,68 @@
.. _nats:
NATS
====
NATS is a message broker.
You can export statistics to a ``NATS`` server.
The connection should be defined in the Glances configuration file as
following:
.. code-block:: ini
[nats]
host=nats://localhost:4222
prefix=glances
and run Glances with:
.. code-block:: console
$ glances --export nats
Data model
-----------
Glances stats are published as JSON messagesto the following subjects:
<prefix>.<plugin>
Example:
CPU stats are published to glances.cpu
So a simple Python client will subscribe to this subject with:
import asyncio
import nats
async def main():
nc = nats.NATS()
await nc.connect(servers=["nats://localhost:4222"])
future = asyncio.Future()
async def cb(msg):
nonlocal future
future.set_result(msg)
await nc.subscribe("glances.cpu", cb=cb)
# Wait for message to come in
print("Waiting (max 30 seconds) for a message on 'glances' subject...")
msg = await asyncio.wait_for(future, 30)
print(msg.subject, msg.data)
if __name__ == '__main__':
asyncio.run(main())
To subscribe to all Glannces stats use wildcard:
await nc.subscribe("glances.*", cb=cb)

48
docs/gw/timescaledb.rst Normal file
View File

@ -0,0 +1,48 @@
.. _timescale:
TimeScaleDB
===========
TimescaleDB is a time-series database built on top of PostgreSQL.
You can export statistics to a ``TimescaleDB`` server.
The connection should be defined in the Glances configuration file as
following:
.. code-block:: ini
[timescaledb]
host=localhost
port=5432
db=glances
user=postgres
password=password
and run Glances with:
.. code-block:: console
$ glances --export timescaledb
Data model
-----------
Each plugin will create an `hypertable`_ in the TimescaleDB database.
Tables are partitionned by time (using the ``time`` column).
Tables are segmented by hostname (in order to have multiple host stored in the Glances database).
For plugin with a key (example network where the key is the interface name), the key will
be added as a column in the table (named key_id) and added to the timescaledb.segmentby option.
Current limitations
-------------------
Sensors, Fs and DiskIO plugins are not supported by the TimescaleDB exporter.
In the cpu plugin, the user field is exported as user_cpu (user_percpu in the percpu plugin)
because user is a reserved keyword in PostgreSQL.
.. _hypertable: https://docs.tigerdata.com/use-timescale/latest/hypertables/

View File

@ -11,12 +11,11 @@ information depending on the terminal size.
It can also work in client/server mode. Remote monitoring can be
done via terminal, Web interface, or API (XMLRPC and RESTful).
Glances is written in Python and uses the `psutil`_ library to get
information from your system.
Stats can also be exported to :ref:`files or external databases<gw>`.
Stats can also be exported to external time/value databases.
.. _psutil: https://github.com/giampaolo/psutil
It is also possible to use it in your own Python scripts thanks to
the :ref:`Glances API<api>` or in any other application through
the :ref:`RESTful API<api_restful>`.
Table of Contents
=================
@ -30,7 +29,11 @@ Table of Contents
config
aoa/index
gw/index
api
api/python
api/restful
docker
faq
support
.. _psutil: https://github.com/giampaolo/psutil

View File

@ -34,5 +34,19 @@ To upgrade Glances and all its dependencies to the latest versions:
For additional installation methods, read the official `README`_ file.
Shell tab completion
====================
Glances 4.3.2 and higher includes shell tab autocompletion thanks to the --print-completion option.
For example, on a Linux operating system with Bash shell:
.. code-block:: console
$ glances --print-completion bash | sudo tee -a /etc/bash_completion.d/glances
$ source /etc/bash_completion.d/glances
Following shells are supported: bash, zsh and tcsh.
.. _psutil: https://github.com/giampaolo/psutil
.. _README: https://github.com/nicolargo/glances/blob/master/README.rst

View File

@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "GLANCES" "1" "Mar 22, 2025" "4.3.1" "Glances"
.TH "GLANCES" "1" "Jan 03, 2026" "4.4.2_dev1" "Glances"
.SH NAME
glances \- An eye on your system
.SH SYNOPSIS
@ -64,6 +64,11 @@ enable debug mode
.UNINDENT
.INDENT 0.0
.TP
.B \-\-print\-completion
generate shell tab completion scripts for Glances CLI
.UNINDENT
.INDENT 0.0
.TP
.B \-C CONF_FILE, \-\-config CONF_FILE
path to the configuration file
.UNINDENT
@ -453,7 +458,7 @@ Reset history
Show/hide RAID plugin
.TP
.B \fBs\fP
Show/hide sensors stats
Show/hide sensors plugin
.TP
.B \fBS\fP
Enable/disable spark lines
@ -470,6 +475,9 @@ Sort processes by USER
.B \fBU\fP
View cumulative network I/O
.TP
.B \fBV\fP
Show/hide VMS plugin
.TP
.B \fBw\fP
Delete finished warning log messages
.TP
@ -514,12 +522,18 @@ Switch between process command line or command name
.B \fBF5\fP or \fBCTRL\-R\fP
Refresh user interface
.TP
.B \fBLEFT\fP
.B \fBSHIFT\-LEFT\fP
Navigation left through the process sort
.TP
.B \fBRIGHT\fP
.B \fBSHIFT\-RIGHT\fP
Navigation right through the process sort
.TP
.B \fBLEFT\fP
Navigation left through the process name
.TP
.B \fBRIGHT\fP
Navigation right through the process name
.TP
.B \fBUP\fP
Up in the processes list
.TP
@ -658,7 +672,7 @@ max_processes_display=25
# You can download it in a specific folder
# thanks to https://github.com/nicolargo/glances/issues/2021
# then configure this folder with the webui_root_path key
# Default is folder where glances_restfull_api.py is hosted
# Default is folder where glances_restful_api.py is hosted
#webui_root_path=
# CORS options
# Comma separated list of origins that should be permitted to make cross\-origin requests.
@ -673,6 +687,10 @@ max_processes_display=25
# Comma separated list of HTTP request headers that should be supported for cross\-origin requests.
# Default is *
#cors_headers=*
# Define SSL files (keyfile_password is optional)
#ssl_keyfile=./glances.local+3\-key.pem
#ssl_keyfile_password=kfp
#ssl_certfile=./glances.local+3.pem
.EE
.UNINDENT
.UNINDENT
@ -945,6 +963,6 @@ If you do not want to see the local Glances Web Server in the browser list pleas
.sp
Nicolas Hennion aka Nicolargo <\X'tty: link mailto:contact@nicolargo.com'\fI\%contact@nicolargo.com\fP\X'tty: link'>
.SH COPYRIGHT
2025, Nicolas Hennion
2026, Nicolas Hennion
.\" Generated by docutils manpage writer.
.

View File

@ -4,11 +4,12 @@ Quickstart
==========
This page gives a good introduction to how to get started with Glances.
Glances offers three modes:
Glances offers multiple modes:
- Standalone
- Client/Server
- Web server
- Fetch
Standalone Mode
---------------
@ -196,7 +197,7 @@ Here's a screenshot from Chrome on Android:
.. image:: _static/screenshot-web2.png
How do you protect your server (or Web server) with a login/password ?
------------------------------------------------------------------
----------------------------------------------------------------------
You can set a password to access the server using the ``--password``.
By default, the login is ``glances`` but you can change it with
@ -222,3 +223,22 @@ file:
# Additionally (and optionally) a default password could be defined
localhost=mylocalhostpassword
default=mydefaultpassword
Fetch mode
----------
It is also possible to get and share a quick look of a machine using the
``fetch`` mode. In this mode, current stats are display on the console in
a fancy way.
.. code-block:: console
$ glances --fetch
Results look like this:
.. image:: _static/screenshot-fetch.png
It is also possible to use a custom template with the ``--fetch-template </path/to/template.jinja>`` option.
Have a look to the :ref:`fetch documentation page<fetch>` to learn how to create your own template.

33
generate_openapi.py Normal file
View File

@ -0,0 +1,33 @@
import json
from unittest.mock import patch
from fastapi.openapi.utils import get_openapi
from glances.main import GlancesMain
# sys.path.append('./glances/outputs')
from glances.outputs.glances_restful_api import GlancesRestfulApi
# Init Glances core
testargs = ["glances", "-C", "./conf/glances.conf"]
with patch('sys.argv', testargs):
core = GlancesMain()
test_config = core.get_config()
test_args = core.get_args()
app = GlancesRestfulApi(config=test_config, args=test_args)._app
with open('./docs/api/openapi.json', 'w') as f:
json.dump(
get_openapi(
title=app.title,
version=app.version,
# Set the OenAPI version
# It's an hack to make openapi.json compatible with tools like https://editor.swagger.io/
# Please read https://fastapi.tiangolo.com/reference/fastapi/?h=openapi#fastapi.FastAPI.openapi_version
openapi_version="3.0.2",
description=app.description,
routes=app.routes,
),
f,
)

13
generate_webui_conf.py Normal file
View File

@ -0,0 +1,13 @@
import json
from glances.outputs.glances_curses import _GlancesCurses
print(
json.dumps(
{
"topMenu": list(_GlancesCurses._top),
"leftMenu": [p for p in _GlancesCurses._left_sidebar if p != "now"],
},
indent=4,
)
)

523
glances.ipynb Normal file
View File

@ -0,0 +1,523 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "592b8135-c06b-41b7-895e-9dd70787f6ac",
"metadata": {},
"source": [
"# Use Glances API in your Python code"
]
},
{
"cell_type": "markdown",
"id": "e5ec86ae-ce2b-452f-b715-54e746026a96",
"metadata": {},
"source": [
"## Init the Glances API"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ba9b3546-65a0-4eec-942b-1855ff5c5d32",
"metadata": {},
"outputs": [],
"source": [
"from glances import api"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e81ad928-3b61-4654-8589-13cb29e7f292",
"metadata": {},
"outputs": [],
"source": [
"gl = api.GlancesAPI()"
]
},
{
"cell_type": "markdown",
"id": "6ec912a3-0875-4cdb-8539-e84ffb27768a",
"metadata": {},
"source": [
"## Get plugins list"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "1ce57a13-a90d-4d65-b4a4-2bc45112697e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['alert',\n",
" 'ports',\n",
" 'diskio',\n",
" 'containers',\n",
" 'processcount',\n",
" 'programlist',\n",
" 'gpu',\n",
" 'percpu',\n",
" 'vms',\n",
" 'system',\n",
" 'network',\n",
" 'cpu',\n",
" 'amps',\n",
" 'processlist',\n",
" 'load',\n",
" 'sensors',\n",
" 'uptime',\n",
" 'now',\n",
" 'connections',\n",
" 'fs',\n",
" 'wifi',\n",
" 'ip',\n",
" 'help',\n",
" 'version',\n",
" 'psutilversion',\n",
" 'core',\n",
" 'mem',\n",
" 'folders',\n",
" 'quicklook',\n",
" 'memswap',\n",
" 'raid']"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.plugins()"
]
},
{
"cell_type": "markdown",
"id": "d5be2964-7a28-4b93-9dd0-1481afd2ee50",
"metadata": {},
"source": [
"## Get CPU stats"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "0d1636d2-3f3e-44d4-bb67-45487384f79f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'total': 3.8, 'user': 3.0, 'nice': 0.0, 'system': 0.8, 'idle': 96.1, 'iowait': 0.1, 'irq': 0.0, 'steal': 0.0, 'guest': 0.0, 'ctx_switches': 0, 'interrupts': 0, 'soft_interrupts': 0, 'syscalls': 0, 'cpucore': 16, 'time_since_update': 141.46278643608093, 'ctx_switches_gauge': 12830371, 'ctx_switches_rate_per_sec': 0, 'interrupts_gauge': 9800040, 'interrupts_rate_per_sec': 0, 'soft_interrupts_gauge': 3875931, 'soft_interrupts_rate_per_sec': 0, 'syscalls_gauge': 0, 'syscalls_rate_per_sec': 0}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.cpu"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "99681a33-045e-43bf-927d-88b15872fad0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3.1"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.cpu.get('total')"
]
},
{
"cell_type": "markdown",
"id": "07e30de4-8f2a-4110-9c43-2a87d91dbf24",
"metadata": {},
"source": [
"## Get MEMORY stats"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "33502d93-acf9-49c5-8bcd-0a0404b47829",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'total': 16422858752, 'available': 6726169136, 'percent': 59.0, 'used': 9696689616, 'free': 541847552, 'active': 8672595968, 'inactive': 5456875520, 'buffers': 354791424, 'cached': 6520318384, 'shared': 729960448}"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.mem"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "404cd8d6-ac38-4830-8ead-4b747e0ca7b1",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"6779998768"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.mem.get('available')"
]
},
{
"cell_type": "markdown",
"id": "74e27e9f-3240-4827-a754-3538b7d68119",
"metadata": {},
"source": [
"Display it in a user friendly way:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "fa83b40a-51e8-45fa-b478-d0fcc9de4639",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'6.28G'"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.auto_unit(gl.mem.get('available'))"
]
},
{
"cell_type": "markdown",
"id": "bfaf5b94-7c9c-4fdc-8a91-71f543cafa4b",
"metadata": {},
"source": [
"## Get NETWORK stats"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "a0ab2ce7-e9bd-4a60-9b90-095a9023dac7",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'wlp0s20f3': {'bytes_sent': 1130903, 'bytes_recv': 2213272, 'speed': 0, 'key': 'interface_name', 'interface_name': 'wlp0s20f3', 'alias': 'WIFI', 'bytes_all': 3344175, 'time_since_update': 354.35748958587646, 'bytes_recv_gauge': 1108380679, 'bytes_recv_rate_per_sec': 6245.0, 'bytes_sent_gauge': 21062113, 'bytes_sent_rate_per_sec': 3191.0, 'bytes_all_gauge': 1129442792, 'bytes_all_rate_per_sec': 9437.0}}"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.network"
]
},
{
"cell_type": "markdown",
"id": "b65f7280-d9f0-4719-9e10-8b78dc414bae",
"metadata": {},
"source": [
"Get the list of networks interfaces:"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "1a55d32a-bd7d-4dfa-b239-8875c01f205e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['wlp0s20f3']"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.network.keys()"
]
},
{
"cell_type": "markdown",
"id": "8c7e0215-e96a-4f7e-a187-9b7bee1abcf9",
"metadata": {},
"source": [
"Get stats for a specific network interface:"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "9aacfb32-c0e3-4fc7-b1d2-d216e46088cd",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'bytes_sent': 118799,\n",
" 'bytes_recv': 275052,\n",
" 'speed': 0,\n",
" 'key': 'interface_name',\n",
" 'interface_name': 'wlp0s20f3',\n",
" 'alias': 'WIFI',\n",
" 'bytes_all': 393851,\n",
" 'time_since_update': 46.24822926521301,\n",
" 'bytes_recv_gauge': 1108795793,\n",
" 'bytes_recv_rate_per_sec': 5947.0,\n",
" 'bytes_sent_gauge': 21268464,\n",
" 'bytes_sent_rate_per_sec': 2568.0,\n",
" 'bytes_all_gauge': 1130064257,\n",
" 'bytes_all_rate_per_sec': 8516.0}"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.network.get('wlp0s20f3')"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "4f5ae513-6022-4a52-8d6c-e8b62afacc24",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5105.0"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.network.get('wlp0s20f3').get('bytes_recv_rate_per_sec')"
]
},
{
"cell_type": "markdown",
"id": "8b0bdbf4-e386-44aa-9585-1d042f0ded5d",
"metadata": {},
"source": [
"## Additional information"
]
},
{
"cell_type": "markdown",
"id": "5c52a0c7-06fb-432a-bdb7-9921f432d5a6",
"metadata": {},
"source": [
"Example for the LOAD plugin."
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "99303a2b-52a3-440f-a896-ad4951a9de34",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'min1': 1.01123046875, 'min5': 0.83447265625, 'min15': 0.76171875, 'cpucore': 16}"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.load"
]
},
{
"cell_type": "markdown",
"id": "7a560824-2787-4436-b39b-63de0c455536",
"metadata": {},
"source": [
"Get the limit configured in the glances.conf:"
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "cbbc6a81-623f-4eff-9d08-e6a8b5981660",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'min1': {'description': 'Average sum of the number of processes waiting in the run-queue plus the number currently executing over 1 minute.',\n",
" 'unit': 'float'},\n",
" 'min5': {'description': 'Average sum of the number of processes waiting in the run-queue plus the number currently executing over 5 minutes.',\n",
" 'unit': 'float'},\n",
" 'min15': {'description': 'Average sum of the number of processes waiting in the run-queue plus the number currently executing over 15 minutes.',\n",
" 'unit': 'float'},\n",
" 'cpucore': {'description': 'Total number of CPU core.', 'unit': 'number'}}"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.load.fields_description"
]
},
{
"cell_type": "markdown",
"id": "2bd51d13-77e3-48f0-aa53-af86df6425f8",
"metadata": {},
"source": [
"Get field description and unit:"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "8682edcf-a8b9-424c-976f-2a301a05be6a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'history_size': 1200.0,\n",
" 'load_disable': ['False'],\n",
" 'load_careful': 0.7,\n",
" 'load_warning': 1.0,\n",
" 'load_critical': 5.0}"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.load.get_limits()"
]
},
{
"cell_type": "raw",
"id": "3c671ff8-3a0c-48d3-8247-6081c69c19a9",
"metadata": {},
"source": [
"Get current stats views regarding limits:"
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "45e03e9b-233c-4359-bcbc-7d2f06aca1c6",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'min1': {'decoration': 'DEFAULT',\n",
" 'optional': False,\n",
" 'additional': False,\n",
" 'splittable': False,\n",
" 'hidden': False},\n",
" 'min5': {'decoration': 'OK',\n",
" 'optional': False,\n",
" 'additional': False,\n",
" 'splittable': False,\n",
" 'hidden': False},\n",
" 'min15': {'decoration': 'OK_LOG',\n",
" 'optional': False,\n",
" 'additional': False,\n",
" 'splittable': False,\n",
" 'hidden': False},\n",
" 'cpucore': {'decoration': 'DEFAULT',\n",
" 'optional': False,\n",
" 'additional': False,\n",
" 'splittable': False,\n",
" 'hidden': False}}"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gl.load.get_views()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@ -19,7 +19,8 @@ import tracemalloc
# Global name
# Version should start and end with a numerical char
# See https://packaging.python.org/specifications/core-metadata/#version
__version__ = "4.3.1"
# Examples: 1.0.0, 1.0.0rc1, 1.1.0_dev1
__version__ = "4.4.2_dev1"
__apiversion__ = '4'
__author__ = 'Nicolas Hennion <nicolas@nicolargo.com>'
__license__ = 'LGPLv3'
@ -52,10 +53,10 @@ if psutil_version_info < psutil_min_version:
# Trac malloc is only available on Python 3.4 or higher
def __signal_handler(signal, frame):
logger.debug(f"Signal {signal} caught")
def __signal_handler(sig, frame):
logger.debug(f"Signal {sig} caught")
# Avoid Glances hang when killing process with muliple CTRL-C See #3264
signal.signal(signal.SIGINT, signal.SIG_IGN)
end()
@ -74,6 +75,53 @@ def end():
sys.exit(0)
def start_main_loop(args, start_duration):
logger.debug(f"Glances started in {start_duration.get()} seconds")
if args.stop_after:
logger.info(f'Glances will be stopped in ~{args.stop_after * args.time} seconds')
def check_memleak(args, mode):
if args.memory_leak:
wait = args.stop_after * args.time * args.memory_leak * 2
print(f'Memory leak detection, please wait ~{wait} seconds...')
# First run without dump to fill the memory
mode.serve_n(args.stop_after)
# Then start the memory-leak loop
snapshot_begin = tracemalloc.take_snapshot()
else:
snapshot_begin = None
return snapshot_begin
def setup_server_mode(args, mode):
if args.stdout_issue or args.stdout_api_restful_doc or args.stdout_api_doc:
# Serve once for issue and API documentation modes
mode.serve_issue()
else:
# Serve forever
mode.serve_forever()
def maybe_trace_memleak(args, snapshot_begin):
if args.trace_malloc or args.memory_leak:
snapshot_end = tracemalloc.take_snapshot()
if args.memory_leak:
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
memory_leak = sum([s.size_diff for s in snapshot_diff])
print(f"Memory consumption: {memory_leak / 1000:.1f}KB (see log for details)")
logger.info("Memory consumption (top 5):")
for stat in snapshot_diff[:5]:
logger.info(stat)
if args.trace_malloc:
# See more options here: https://docs.python.org/3/library/tracemalloc.html
top_stats = snapshot_end.statistics("filename")
print("[ Trace malloc - Top 10 ]")
for stat in top_stats[:10]:
print(stat)
def start(config, args):
"""Start Glances."""
@ -101,40 +149,10 @@ def start(config, args):
logger.info(f"Start {GlancesMode.__name__} mode")
mode = GlancesMode(config=config, args=args)
# Start the main loop
logger.debug(f"Glances started in {start_duration.get()} seconds")
if args.stop_after:
logger.info(f'Glances will be stopped in ~{args.stop_after * args.time} seconds')
if args.memory_leak:
print(f'Memory leak detection, please wait ~{args.stop_after * args.time * args.memory_leak * 2} seconds...')
# First run without dump to fill the memory
mode.serve_n(args.stop_after)
# Then start the memory-leak loop
snapshot_begin = tracemalloc.take_snapshot()
if args.stdout_issue or args.stdout_apidoc:
# Serve once for issue/test mode
mode.serve_issue()
else:
# Serve forever
mode.serve_forever()
if args.memory_leak:
snapshot_end = tracemalloc.take_snapshot()
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
memory_leak = sum([s.size_diff for s in snapshot_diff])
print(f"Memory consumption: {memory_leak / 1000:.1f}KB (see log for details)")
logger.info("Memory consumption (top 5):")
for stat in snapshot_diff[:5]:
logger.info(stat)
elif args.trace_malloc:
# See more options here: https://docs.python.org/3/library/tracemalloc.html
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics("filename")
print("[ Trace malloc - Top 10 ]")
for stat in top_stats[:10]:
print(stat)
start_main_loop(args, start_duration)
snapshot_begin = check_memleak(args, mode)
setup_server_mode(args, mode)
maybe_trace_memleak(args, snapshot_begin)
# Shutdown
mode.end()
@ -171,3 +189,6 @@ def main():
# Glances can be ran in standalone, client or server mode
start(config=core.get_config(), args=core.get_args())
# End of glances/__init__.py

View File

@ -66,7 +66,7 @@ class Amp(GlancesAmp):
"""Update the AMP"""
# Get the Nginx status
logger.debug('{}: Update stats using status URL {}'.format(self.NAME, self.get('status_url')))
res = requests.get(self.get('status_url'))
res = requests.get(self.get('status_url'), timeout=15)
if res.ok:
# u'Active connections: 1 \nserver accepts handled requests\n 1 1 1 \nReading: 0 Writing: 1 Waiting: 0 \n'
self.set_result(res.text.rstrip())

View File

@ -37,7 +37,7 @@ systemctl_cmd=/usr/bin/systemctl --plain
from subprocess import CalledProcessError, check_output
from glances.amps.amp import GlancesAmp
from glances.globals import iteritems, to_ascii
from glances.globals import to_ascii
from glances.logger import logger
@ -45,7 +45,7 @@ class Amp(GlancesAmp):
"""Glances' Systemd AMP."""
NAME = 'Systemd'
VERSION = '1.0'
VERSION = '1.1'
DESCRIPTION = 'Get services list from systemctl (systemd)'
AUTHOR = 'Nicolargo'
EMAIL = 'contact@nicolargo.com'
@ -77,7 +77,7 @@ class Amp(GlancesAmp):
status[column[c]] = 1
# Build the output (string) message
output = 'Services\n'
for k, v in iteritems(status):
for k, v in status.items():
output += f'{k}: {v}\n'
self.set_result(output, separator=' ')

View File

@ -34,7 +34,6 @@ service_cmd=/usr/bin/service --status-all
"""
from glances.amps.amp import GlancesAmp
from glances.globals import iteritems
from glances.logger import logger
from glances.secure import secure_popen
@ -43,7 +42,7 @@ class Amp(GlancesAmp):
"""Glances' Systemd AMP."""
NAME = 'SystemV'
VERSION = '1.0'
VERSION = '1.1'
DESCRIPTION = 'Get services list from service (initd)'
AUTHOR = 'Nicolargo'
EMAIL = 'contact@nicolargo.com'
@ -77,7 +76,7 @@ class Amp(GlancesAmp):
status['upstart'] += 1
# Build the output (string) message
output = 'Services\n'
for k, v in iteritems(status):
for k, v in status.items():
output += f'{k}: {v}\n'
self.set_result(output, separator=' ')

View File

@ -12,7 +12,7 @@ import os
import re
import threading
from glances.globals import amps_path, iteritems, listkeys
from glances.globals import amps_path, listkeys
from glances.logger import logger
from glances.processes import glances_processes
@ -90,7 +90,7 @@ class AmpsList:
processlist = glances_processes.get_list()
# Iter upon the AMPs dict
for k, v in iteritems(self.get()):
for k, v in self.get().items():
if not v.enable():
# Do not update if the enable tag is set
continue
@ -125,18 +125,14 @@ class AmpsList:
Search application monitored processes by a regular expression
"""
ret = []
try:
# Search in both cmdline and name (for kernel thread, see #1261)
for p in processlist:
if (re.search(amp_value.regex(), p['name']) is not None) or (
p['cmdline'] is not None
and p['cmdline'] != []
and re.search(amp_value.regex(), ' '.join(p['cmdline'])) is not None
):
ret.append(
{'pid': p['pid'], 'cpu_percent': p['cpu_percent'], 'memory_percent': p['memory_percent']}
)
ret = [
{'pid': p['pid'], 'cpu_percent': p['cpu_percent'], 'memory_percent': p['memory_percent']}
for p in processlist
if re.search(amp_value.regex(), p['name'])
or ((cmdline := p.get('cmdline')) and re.search(amp_value.regex(), ' '.join(cmdline)))
]
except (TypeError, KeyError) as e:
logger.debug(f"Can not build AMPS list ({e})")

117
glances/api.py Normal file
View File

@ -0,0 +1,117 @@
#
# Glances - An eye on your system
#
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
from glances import __version__ as glances_version
from glances.globals import auto_unit, weak_lru_cache
from glances.main import GlancesMain
from glances.outputs.glances_bars import Bar
from glances.processes import sort_stats
from glances.stats import GlancesStats
plugin_dependencies_tree = {
'processlist': ['processcount'],
}
class GlancesAPI:
ttl = 2.0 # Default cache TTL in seconds
def __init__(self, config=None, args=None):
self.__version__ = glances_version.split('.')[0] # Get the major version
core = GlancesMain()
self.args = args if args is not None else core.get_args()
self.config = config if config is not None else core.get_config()
self._stats = GlancesStats(config=self.config, args=self.args)
# Set the cache TTL for the API
self.ttl = self.args.time if self.args.time is not None else self.ttl
# Init the stats of all plugins in order to ensure that rate are computed
self._stats.update()
@weak_lru_cache(maxsize=1, ttl=ttl)
def __getattr__(self, item):
"""Fallback to the stats object for any missing attributes."""
if item in self._stats.getPluginsList():
if item in plugin_dependencies_tree:
# Ensure dependencies are updated before accessing the plugin
for dependency in plugin_dependencies_tree[item]:
self._stats.get_plugin(dependency).update()
# Update the plugin stats
self._stats.get_plugin(item).update()
return self._stats.get_plugin(item)
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'")
def plugins(self):
"""Return the list of available plugins."""
return self._stats.getPluginsList()
def auto_unit(self, number, low_precision=False, min_symbol='K', none_symbol='-'):
"""
Converts a numeric value into a human-readable string with appropriate units.
Args:
number (float or int): The numeric value to be converted.
low_precision (bool, optional): If True, use lower precision for the output. Defaults to False.
min_symbol (str, optional): The minimum unit symbol to use (e.g., 'K' for kilo). Defaults to 'K'.
none_symbol (str, optional): The symbol to display if the number is None. Defaults to '-'.
Returns:
str: A human-readable string representation of the number with units.
"""
return auto_unit(number, low_precision, min_symbol, none_symbol)
def bar(self, value, size=18, bar_char='', empty_char='', pre_char='', post_char=''):
"""
Generate a progress bar representation for a given value.
Args:
value (float): The percentage value to represent in the bar (typically between 0 and 100).
size (int, optional): The total length of the bar in characters. Defaults to 18.
bar_char (str, optional): The character used to represent the filled portion of the bar. Defaults to ''.
empty_char (str, optional): The character used to represent the empty portion of the bar. Defaults to ''.
pre_char (str, optional): A string to prepend to the bar. Defaults to ''.
post_char (str, optional): A string to append to the bar. Defaults to ''.
Returns:
str: A string representing the progress bar.
"""
b = Bar(
size, bar_char=bar_char, empty_char=empty_char, pre_char=pre_char, post_char=post_char, display_value=False
)
b.percent = value
return b.get()
def top_process(self, limit=3, sorted_by='cpu_percent', sorted_by_secondary='memory_percent'):
"""
Returns a list of the top processes sorted by specified criteria.
Args:
limit (int, optional): The maximum number of top processes to return. Defaults to 3.
sorted_by (str, optional): The primary key to sort processes by (e.g., 'cpu_percent').
Defaults to 'cpu_percent'.
sorted_by_secondary (str, optional): The secondary key to sort processes by if primary keys are equal
(e.g., 'memory_percent'). Defaults to 'memory_percent'.
Returns:
list: A list of dictionaries representing the top processes, excluding those with 'glances' in their
command line.
Note:
The 'glances' process is excluded from the returned list to avoid self-generated CPU load affecting
the results.
"""
# Exclude glances process from the top list
# because in fetch mode, Glances generate a CPU load
all_but_glances = [
p
for p in self._stats.get_plugin('processlist').get_raw()
if p['cmdline'] and 'glances' not in (p['cmdline'] or ())
]
return sort_stats(all_but_glances, sorted_by=sorted_by, sorted_by_secondary=sorted_by_secondary)[:limit]

View File

@ -10,6 +10,14 @@
from datetime import datetime
# Ugly hack waiting for Python 3.10 deprecation
try:
from datetime import UTC
except ImportError:
from datetime import timezone
UTC = timezone.utc
class GlancesAttribute:
def __init__(self, name, description='', history_max_size=None):
@ -73,7 +81,7 @@ class GlancesAttribute:
Value is a tuple: (<timestamp>, <new_value>)
"""
self._value = (datetime.now(), new_value)
self._value = (datetime.now(UTC), new_value)
self.history_add(self._value)
"""

View File

@ -17,6 +17,10 @@ from glances import __version__
from glances.globals import json_loads
from glances.logger import logger
from glances.outputs.glances_curses import GlancesCursesClient
from glances.outputs.glances_stdout import GlancesStdout
from glances.outputs.glances_stdout_csv import GlancesStdoutCsv
from glances.outputs.glances_stdout_fetch import GlancesStdoutFetch
from glances.outputs.glances_stdout_json import GlancesStdoutJson
from glances.stats_client import GlancesStatsClient
from glances.timer import Counter
@ -73,7 +77,7 @@ class GlancesClient:
def log_and_exit(self, msg=''):
"""Log and exit."""
if not self.return_to_browser:
logger.critical(msg)
logger.critical(f"Error when connecting to Glances server: {msg}")
sys.exit(2)
else:
logger.error(msg)
@ -172,6 +176,21 @@ class GlancesClient:
if self.quiet:
# In quiet mode, nothing is displayed
logger.info("Quiet mode is ON: Nothing will be displayed")
elif self.args.stdout:
logger.info(f"Stdout mode is ON, following stats will be displayed: {self.args.stdout}")
# Init screen
self.screen = GlancesStdout(config=self.config, args=self.args)
elif self.args.stdout_json:
logger.info(f"Stdout JSON mode is ON, following stats will be displayed: {self.args.stdout_json}")
# Init screen
self.screen = GlancesStdoutJson(config=self.config, args=self.args)
elif self.args.stdout_csv:
logger.info(f"Stdout CSV mode is ON, following stats will be displayed: {self.args.stdout_csv}")
# Init screen
self.screen = GlancesStdoutCsv(config=self.config, args=self.args)
elif self.args.stdout_fetch:
logger.info("Fetch mode is ON")
self.screen = GlancesStdoutFetch(config=self.config, args=self.args)
else:
self.screen = GlancesCursesClient(config=self.config, args=self.args)
@ -237,6 +256,7 @@ class GlancesClient:
return self.client_mode
exit_key = False
try:
while True and not exit_key:
# Update the stats
@ -264,8 +284,8 @@ class GlancesClient:
else:
# In quiet mode, we only wait adapated_refresh seconds
time.sleep(adapted_refresh)
except Exception as e:
logger.critical(e)
except Exception:
logger.critical("Critical error in client serve_forever loop")
self.end()
return self.client_mode

View File

@ -9,7 +9,7 @@
"""CPU percent stats shared between CPU and Quicklook plugins."""
import platform
from typing import Optional, TypedDict
from typing import TypedDict
import psutil
@ -18,11 +18,184 @@ from glances.timer import Timer
__all__ = ["cpu_percent"]
CPU_IMPLEMENTERS = {
0x41: 'ARM Limited',
0x42: 'Broadcom',
0x43: 'Cavium',
0x44: 'DEC',
0x46: 'Fujitsu',
0x48: 'HiSilicon',
0x49: 'Infineon Technologies',
0x4D: 'Motorola/Freescale',
0x4E: 'NVIDIA',
0x50: 'Applied Micro (APM)',
0x51: 'Qualcomm',
0x53: 'Samsung',
0x56: 'Marvell',
0x61: 'Apple',
0x66: 'Faraday',
0x69: 'Intel',
0x6D: 'Microsoft',
0x70: 'Phytium',
0xC0: 'Ampere Computing',
}
CPU_PARTS = {
# ARM Limited (0x41)
0x41: {
0xD03: 'Cortex-A53',
0xD04: 'Cortex-A35',
0xD05: 'Cortex-A55',
0xD06: 'Cortex-A65',
0xD07: 'Cortex-A57',
0xD08: 'Cortex-A72',
0xD09: 'Cortex-A73',
0xD0A: 'Cortex-A75',
0xD0B: 'Cortex-A76',
0xD0C: 'Neoverse N1',
0xD0D: 'Cortex-A77',
0xD0E: 'Cortex-A76AE',
0xD13: 'Cortex-R52',
0xD20: 'Cortex-M23',
0xD21: 'Cortex-M33',
0xD40: 'Neoverse V1',
0xD41: 'Cortex-A78',
0xD42: 'Cortex-A78AE',
0xD43: 'Cortex-A65AE',
0xD44: 'Cortex-X1',
0xD46: 'Cortex-A510',
0xD47: 'Cortex-A710',
0xD48: 'Cortex-X2',
0xD49: 'Neoverse N2',
0xD4A: 'Neoverse E1',
0xD4B: 'Cortex-A78C',
0xD4C: 'Cortex-X1C',
0xD4D: 'Cortex-A715',
0xD4E: 'Cortex-X3',
0xD4F: 'Neoverse V2',
0xD80: 'Cortex-A520',
0xD81: 'Cortex-A720',
0xD82: 'Cortex-X4',
0xD84: 'Neoverse V3',
0xD85: 'Cortex-X925',
0xD87: 'Cortex-A725',
},
# Apple (0x61)
0x61: {
0x000: 'Swift',
0x001: 'Cyclone',
0x002: 'Typhoon',
0x003: 'Twister',
0x004: 'Hurricane',
0x005: 'Monsoon/Mistral',
0x006: 'Vortex/Tempest',
0x007: 'Lightning/Thunder',
0x008: 'Firestorm/Icestorm (M1)',
0x009: 'Avalanche/Blizzard (M2)',
0x00E: 'Everest/Sawtooth (M3)',
0x010: 'Blizzard/Avalanche (A16)',
0x011: 'Coll (M4)',
},
# Qualcomm (0x51)
0x51: {
0x00F: 'Scorpion',
0x02D: 'Scorpion',
0x04D: 'Krait',
0x06F: 'Krait',
0x201: 'Kryo',
0x205: 'Kryo',
0x211: 'Kryo',
0x800: 'Kryo 260/280 Gold (Cortex-A73)',
0x801: 'Kryo 260/280 Silver (Cortex-A53)',
0x802: 'Kryo 385 Gold (Cortex-A75)',
0x803: 'Kryo 385 Silver (Cortex-A55)',
0x804: 'Kryo 485 Gold (Cortex-A76)',
0x805: 'Kryo 485 Silver (Cortex-A55)',
0xC00: 'Falkor',
0xC01: 'Saphira',
},
# Samsung (0x53)
0x53: {
0x001: 'Exynos M1/M2',
0x002: 'Exynos M3',
0x003: 'Exynos M4',
0x004: 'Exynos M5',
},
# NVIDIA (0x4e)
0x4E: {
0x000: 'Denver',
0x003: 'Denver 2',
0x004: 'Carmel',
},
# Marvell (0x56)
0x56: {
0x131: 'Feroceon 88FR131',
0x581: 'PJ4/PJ4b',
0x584: 'PJ4B-MP',
},
# Cavium (0x43)
0x43: {
0x0A0: 'ThunderX',
0x0A1: 'ThunderX 88XX',
0x0A2: 'ThunderX 81XX',
0x0A3: 'ThunderX 83XX',
0x0AF: 'ThunderX2 99xx',
0x0B0: 'OcteonTX2',
0x0B1: 'OcteonTX2 T98',
0x0B2: 'OcteonTX2 T96',
0x0B3: 'OcteonTX2 F95',
0x0B4: 'OcteonTX2 F95N',
0x0B5: 'OcteonTX2 F95MM',
},
# Broadcom (0x42)
0x42: {
0x00F: 'Brahma B15',
0x100: 'Brahma B53',
0x516: 'Vulcan',
},
# HiSilicon (0x48)
0x48: {
0xD01: 'Kunpeng-920',
0xD40: 'Cortex-A76 (Kirin)',
},
# Ampere (0xc0)
0xC0: {
0xAC3: 'Ampere-1',
0xAC4: 'Ampere-1a',
},
# Fujitsu (0x46)
0x46: {
0x001: 'A64FX',
},
# Intel (0x69) - ARM-based chips
0x69: {
0x200: 'i80200',
0x210: 'PXA250A',
0x212: 'PXA210A',
0x242: 'i80321-400',
0x243: 'i80321-600',
0x290: 'PXA250B/PXA26x',
0x292: 'PXA210B',
0x2C2: 'i80321-400-B0',
0x2C3: 'i80321-600-B0',
0x2D0: 'PXA250C/PXA255/PXA26x',
0x2D2: 'PXA210C',
0x411: 'PXA27x',
0x41C: 'IPX425-533',
0x41D: 'IPX425-400',
0x41F: 'IPX425-266',
0x682: 'PXA32x',
0x683: 'PXA930/PXA935',
0x688: 'PXA30x',
0x689: 'PXA31x',
},
}
class CpuInfo(TypedDict):
cpu_name: str
cpu_hz: Optional[float]
cpu_hz_current: Optional[float]
cpu_hz: float | None
cpu_hz_current: float | None
class PerCpuPercentInfo(TypedDict):
@ -32,15 +205,15 @@ class PerCpuPercentInfo(TypedDict):
user: float
system: float
idle: float
nice: Optional[float]
iowait: Optional[float]
irq: Optional[float]
softirq: Optional[float]
steal: Optional[float]
guest: Optional[float]
guest_nice: Optional[float]
dpc: Optional[float]
interrupt: Optional[float]
nice: float | None
iowait: float | None
irq: float | None
softirq: float | None
steal: float | None
guest: float | None
guest_nice: float | None
dpc: float | None
interrupt: float | None
class CpuPercent:
@ -84,7 +257,7 @@ class CpuPercent:
self.cpu_info['cpu_hz_current'] = cpu_freq.current
else:
self.cpu_info['cpu_hz_current'] = None
if hasattr(cpu_freq, 'max'):
if hasattr(cpu_freq, 'max') and cpu_freq.max != 0.0:
self.cpu_info['cpu_hz'] = cpu_freq.max
else:
self.cpu_info['cpu_hz'] = None
@ -103,9 +276,18 @@ class CpuPercent:
logger.debug("No permission to read '/proc/cpuinfo'")
return ret
cpu_implementer = None
for line in cpuinfo_lines:
# Look for the CPU name
if line.startswith('model name') or line.startswith('Model') or line.startswith('cpu model'):
return line.split(':')[1].strip()
# Look for the CPU name on ARM architecture (see #3127)
if line.startswith('CPU implementer'):
cpu_implementer = CPU_IMPLEMENTERS.get(int(line.split(':')[1].strip(), 16), ret)
ret = cpu_implementer
if line.startswith('CPU part') and cpu_implementer in CPU_PARTS:
cpu_part = CPU_PARTS[cpu_implementer].get(int(line.split(':')[1].strip(), 16), 'Unknown')
ret = f'{cpu_implementer} {cpu_part}'
return ret

View File

@ -271,6 +271,7 @@ class GlancesEventsList:
event_time, event_index, event_state, event_type, event_value, proc_list, proc_desc, global_message
)
# logger.info(self.events_list)
return self.len()
def _create_event(self, event_time, event_state, event_type, event_value, proc_desc, global_message):

View File

@ -1,18 +1,19 @@
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <nicolas@nicolargo.com>
# SPDX-FileCopyrightText: 2026 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
"""
I am your father...
...for all Glances exports IF.
"""
from glances.globals import NoOptionError, NoSectionError, iteritems, iterkeys, json_dumps
import re
from glances.globals import NoOptionError, NoSectionError, json_dumps
from glances.logger import logger
from glances.timer import Counter
@ -20,18 +21,14 @@ from glances.timer import Counter
class GlancesExport:
"""Main class for Glances export IF."""
# List of non exportable plugins
# @TODO: remove this part and make all plugins exportable (see issue #1556)
# @TODO: also make this list configurable by the user (see issue #1443)
# List of non exportable internal plugins
non_exportable_plugins = [
'alert',
'amps',
'help',
'now',
'plugin',
'psutilversion',
'quicklook',
'version',
"alert",
"help",
"plugin",
"psutilversion",
"quicklook",
"version",
]
def __init__(self, config=None, args=None):
@ -55,6 +52,13 @@ class GlancesExport:
# Save last export list
self._last_exported_list = None
# Fields description
self._fields_description = None
# Load the default common export configuration
if self.config is not None:
self.load_common_conf()
def _log_result_decorator(fct):
"""Log (DEBUG) the result of the function fct."""
@ -73,7 +77,25 @@ class GlancesExport:
"""Close the export module."""
logger.debug(f"Finalise export interface {self.export_name}")
def load_conf(self, section, mandatories=['host', 'port'], options=None):
def load_common_conf(self):
"""Load the common export configuration in the Glances configuration file.
:returns: Boolean -- True if section is found
"""
# Read the common [export] section
section = "export"
opt = "exclude_fields"
try:
setattr(self, opt, self.config.get_list_value(section, opt))
except NoOptionError:
logger.debug(f"{opt} option not found in the {section} configuration section")
logger.debug(f"Load common {section} from the Glances configuration file")
return True
def load_conf(self, section, mandatories=["host", "port"], options=None):
"""Load the export <section> configuration in the Glances configuration file.
:param section: name of the export section to load
@ -103,7 +125,7 @@ class GlancesExport:
try:
setattr(self, opt, self.config.get_value(section, opt))
except NoOptionError:
pass
logger.debug(f"{opt} option not found in the {section} configuration section")
logger.debug(f"Load {section} from the Glances configuration file")
logger.debug(f"{section} parameters: { ({opt: getattr(self, opt) for opt in mandatories + options}) }")
@ -114,7 +136,7 @@ class GlancesExport:
"""Return the value of the item 'key'."""
ret = None
try:
ret = item[item['key']]
ret = item[item["key"]]
except KeyError:
logger.error(f"No 'key' available in {item}")
if isinstance(ret, list):
@ -130,14 +152,81 @@ class GlancesExport:
d_tags = {}
if tags:
try:
d_tags = dict([x.split(':') for x in tags.split(',')])
d_tags = dict(x.split(":", 1) for x in tags.split(","))
except ValueError:
# one of the 'key:value' pairs was missing
logger.info('Invalid tags passed: %s', tags)
logger.info("Invalid tags passed: %s", tags)
d_tags = {}
return d_tags
def normalize_for_influxdb(self, name, columns, points):
"""Normalize data for the InfluxDB's data model.
:return: a list of measurements.
"""
FIELD_TO_TAG = ["name", "cmdline", "type"]
ret = []
# Build initial dict by crossing columns and point
data_dict = dict(zip(columns, points))
# issue1871 - Check if a key exist. If a key exist, the value of
# the key should be used as a tag to identify the measurement.
keys_list = [k.split(".")[0] for k in columns if k.endswith(".key")]
if not keys_list:
keys_list = [None]
for measurement in keys_list:
# Manage field
if measurement is not None:
fields = {
k.replace(f"{measurement}.", ""): data_dict[k] for k in data_dict if k.startswith(f"{measurement}.")
}
else:
fields = data_dict
# Transform to InfluxDB data model
# https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_reference/
for k in fields:
# Do not export empty (None) value
if fields[k] is None:
continue
# Convert numerical to float
try:
fields[k] = float(fields[k])
except (TypeError, ValueError):
# Convert others to string
try:
fields[k] = str(fields[k])
except (TypeError, ValueError):
pass
# Manage tags
tags = self.parse_tags(self.tags)
# Add the hostname as a tag
tags["hostname"] = self.hostname
if "hostname" in fields:
fields.pop("hostname")
# Others tags...
if "key" in fields and fields["key"] in fields:
# Create a tag from the key
# Tag should be an string (see InfluxDB data model)
tags[fields["key"]] = str(fields[fields["key"]])
# Remove it from the field list (can not be a field and a tag)
fields.pop(fields["key"])
# Add name as a tag (example for the process list)
for k in FIELD_TO_TAG:
if k in fields:
tags[k] = str(fields[k])
# Remove it from the field list (can not be a field and a tag)
fields.pop(k)
# Add the measurement to the list
ret.append({"measurement": name, "tags": tags, "fields": fields})
return ret
def is_excluded(self, field):
"""Return true if the field is excluded."""
return any(re.fullmatch(i, field, re.I) for i in (getattr(self, 'exclude_fields') or ()))
def plugins_to_export(self, stats):
"""Return the list of plugins to export.
@ -150,12 +239,21 @@ class GlancesExport:
"""Return the list of plugins last exported."""
return self._last_exported_list
def init_fields(self, stats):
"""Return fields description in order to init stats in a server."""
if not self.export_enable:
return False
self._last_exported_list = self.plugins_to_export(stats)
self._fields_description = stats.getAllFieldsDescriptionAsDict(plugin_list=self.last_exported_list())
return self._fields_description
def update(self, stats):
"""Update stats to a server.
The method builds two lists: names and values and calls the export method to export the stats.
Note: this class can be overwritten (for example in CSV and Graph).
Note: if needed this class can be overwritten.
"""
if not self.export_enable:
return False
@ -169,10 +267,14 @@ class GlancesExport:
for plugin in self.last_exported_list():
if isinstance(all_stats[plugin], dict):
all_stats[plugin].update(all_limits[plugin])
# Remove the <plugin>_disable field
all_stats[plugin].pop(f"{plugin}_disable", None)
elif isinstance(all_stats[plugin], list):
# TypeError: string indices must be integers (Network plugin) #1054
for i in all_stats[plugin]:
i.update(all_limits[plugin])
# Remove the <plugin>_disable field
i.pop(f"{plugin}_disable", None)
else:
continue
export_names, export_values = self.build_export(all_stats[plugin])
@ -181,24 +283,28 @@ class GlancesExport:
return True
def build_export(self, stats):
"""Build the export lists."""
"""Build the export lists.
This method builds two lists: names and values.
"""
# Initialize export lists
export_names = []
export_values = []
if isinstance(stats, dict):
# Stats is a dict
# Is there a key ?
if 'key' in iterkeys(stats) and stats['key'] in iterkeys(stats):
pre_key = '{}.'.format(stats[stats['key']])
if "key" in stats and stats["key"] in stats:
pre_key = "{}.".format(stats[stats["key"]])
else:
pre_key = ''
pre_key = ""
# Walk through the dict
for key, value in sorted(iteritems(stats)):
for key, value in sorted(stats.items()):
if isinstance(value, bool):
value = json_dumps(value)
value = json_dumps(value).decode()
if isinstance(value, list):
value = ' '.join([str(v) for v in value])
value = " ".join([str(v) for v in value])
if isinstance(value, dict):
item_names, item_values = self.build_export(value)
@ -206,6 +312,9 @@ class GlancesExport:
export_names += item_names
export_values += item_values
else:
# We are on a simple value
if self.is_excluded(pre_key + key.lower()):
continue
export_names.append(pre_key + key.lower())
export_values.append(value)
elif isinstance(stats, list):

View File

@ -0,0 +1,165 @@
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2026 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
"""
I am your son...
...abstract class for AsyncIO-based Glances exports.
"""
import asyncio
import threading
import time
from abc import abstractmethod
from glances.exports.export import GlancesExport
from glances.logger import logger
class GlancesExportAsyncio(GlancesExport):
"""Abstract class for AsyncIO-based export modules.
This class manages a persistent event loop in a background thread,
allowing child classes to use AsyncIO operations for exporting data.
Child classes must implement:
- async _async_init(): AsyncIO initialization (e.g., connection setup)
- async _async_exit(): AsyncIO cleanup (e.g., disconnection)
- async _async_export(name, columns, points): AsyncIO export operation
"""
def __init__(self, config=None, args=None):
"""Init the AsyncIO export interface."""
super().__init__(config=config, args=args)
# AsyncIO event loop management
self.loop = None
self._loop_ready = threading.Event()
self._loop_exception = None
self._shutdown = False
# Start the background event loop thread
self._loop_thread = threading.Thread(target=self._run_event_loop, daemon=True)
self._loop_thread.start()
# Wait for the loop to be ready
if not self._loop_ready.wait(timeout=10):
raise RuntimeError("AsyncIO event loop failed to start within timeout")
if self._loop_exception:
raise RuntimeError(f"AsyncIO event loop creation failed: {self._loop_exception}")
if self.loop is None:
raise RuntimeError("AsyncIO event loop is None after initialization")
# Call child class AsyncIO initialization
future = asyncio.run_coroutine_threadsafe(self._async_init(), self.loop)
try:
future.result(timeout=10)
logger.debug(f"{self.export_name} AsyncIO export initialized successfully")
except Exception as e:
logger.warning(f"{self.export_name} AsyncIO initialization failed: {e}. Will retry in background.")
def _run_event_loop(self):
"""Run event loop in background thread."""
try:
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self._loop_ready.set()
self.loop.run_forever()
except Exception as e:
self._loop_exception = e
self._loop_ready.set()
logger.error(f"{self.export_name} AsyncIO event loop thread error: {e}")
finally:
# Clean up pending tasks
pending = asyncio.all_tasks(self.loop)
for task in pending:
task.cancel()
if pending:
self.loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
self.loop.close()
@abstractmethod
async def _async_init(self):
"""AsyncIO initialization method.
Child classes should implement this method to perform AsyncIO-based
initialization such as connecting to servers, setting up clients, etc.
This method is called once during __init__ after the event loop is ready.
"""
pass
@abstractmethod
async def _async_exit(self):
"""AsyncIO cleanup method.
Child classes should implement this method to perform AsyncIO-based
cleanup such as disconnecting from servers, closing clients, etc.
This method is called during exit() before stopping the event loop.
"""
pass
@abstractmethod
async def _async_export(self, name, columns, points):
"""AsyncIO export method.
Child classes must implement this method to perform the actual
export operation using AsyncIO.
:param name: plugin name
:param columns: list of column names
:param points: list of values corresponding to columns
"""
pass
def exit(self):
"""Close the AsyncIO export module."""
super().exit()
self._shutdown = True
logger.info(f"{self.export_name} AsyncIO export shutting down")
# Call child class cleanup
if self.loop:
future = asyncio.run_coroutine_threadsafe(self._async_exit(), self.loop)
try:
future.result(timeout=5)
except Exception as e:
logger.error(f"{self.export_name} Error in AsyncIO cleanup: {e}")
# Stop the event loop
if self.loop:
self.loop.call_soon_threadsafe(self.loop.stop)
time.sleep(0.5)
logger.debug(f"{self.export_name} AsyncIO export shutdown complete")
def export(self, name, columns, points):
"""Export data using AsyncIO.
This method bridges the synchronous export() interface with
the AsyncIO _async_export() implementation.
"""
if self._shutdown:
logger.debug(f"{self.export_name} Export called during shutdown, skipping")
return
if not self.loop or not self.loop.is_running():
logger.error(f"{self.export_name} AsyncIO event loop is not running")
return
# Submit the export operation to the background event loop
try:
future = asyncio.run_coroutine_threadsafe(self._async_export(name, columns, points), self.loop)
# Don't block forever - use a short timeout
future.result(timeout=1)
except asyncio.TimeoutError:
logger.warning(f"{self.export_name} AsyncIO export timeout for {name}")
except Exception as e:
logger.error(f"{self.export_name} AsyncIO export error for {name}: {e}", exc_info=True)

View File

@ -105,7 +105,7 @@ class Export(GlancesExport):
logger.debug(f"Export {name} stats to Cassandra")
# Remove non number stats and convert all to float (for Boolean)
data = {k: float(v) for (k, v) in dict(zip(columns, points)).iteritems() if isinstance(v, Number)}
data = {k: float(v) for k, v in zip(columns, points) if isinstance(v, Number)}
# Write input to the Cassandra table
try:

View File

@ -12,7 +12,7 @@
# How to test ?
#
# 1) docker run -d -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=admin -p 5984:5984 --name my-couchdb couchdb
# 2) ./venv/bin/python -m glances -C ./conf/glances.conf --export couchdb --quiet
# 2) .venv/bin/python -m glances -C ./conf/glances.conf --export couchdb --quiet
# 3) Result can be seen at: http://127.0.0.1:5984/_utils
#

View File

@ -80,6 +80,8 @@ class Export(GlancesExport):
# Loop over plugins to export
for plugin in self.plugins_to_export(stats):
export_names, export_values = self.build_export(all_stats[plugin])
# Add the plugin name in the field
export_names = [plugin + '.' + n for n in export_names]
if self.first_line:
csv_header += export_names
csv_data += export_values

View File

@ -0,0 +1,195 @@
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
"""DuckDB interface class."""
import sys
import time
from datetime import datetime
from platform import node
import duckdb
from glances.exports.export import GlancesExport
from glances.logger import logger
# Define the type conversions for DuckDB
# https://duckdb.org/docs/stable/clients/python/conversion
convert_types = {
'bool': 'BOOLEAN',
'int': 'BIGINT',
'float': 'DOUBLE',
'str': 'VARCHAR',
'tuple': 'VARCHAR', # Store tuples as VARCHAR (comma-separated)
'list': 'VARCHAR', # Store lists as VARCHAR (comma-separated)
'NoneType': 'VARCHAR',
}
class Export(GlancesExport):
"""This class manages the DuckDB export module."""
def __init__(self, config=None, args=None):
"""Init the DuckDB export IF."""
super().__init__(config=config, args=args)
# Mandatory configuration keys (additional to host and port)
self.db = None
# Optional configuration keys
self.user = None
self.password = None
self.hostname = None
# Load the configuration file
self.export_enable = self.load_conf(
'duckdb', mandatories=['database'], options=['user', 'password', 'hostname']
)
if not self.export_enable:
exit('Missing DuckDB config')
# The hostname is always add as an identifier in the DuckDB table
# so we can filter the stats by hostname
self.hostname = self.hostname or node().split(".")[0]
# Init the DuckDB client
self.client = self.init()
def init(self):
"""Init the connection to the DuckDB server."""
if not self.export_enable:
return None
try:
db = duckdb.connect(database=self.database)
except Exception as e:
logger.critical(f"Cannot connect to DuckDB {self.database} ({e})")
sys.exit(2)
else:
logger.info(f"Stats will be exported to DuckDB: {self.database}")
return db
def normalize(self, value):
# Nothing to do...
if isinstance(value, list) and len(value) == 1 and value[0] in ['True', 'False']:
return bool(value[0])
return value
def update(self, stats):
"""Update the DuckDB export module."""
if not self.export_enable:
return False
# Get all the stats & limits
# Current limitation with sensors and fs plugins because fields list is not the same
self._last_exported_list = [p for p in self.plugins_to_export(stats) if p not in ['sensors', 'fs']]
all_stats = stats.getAllExportsAsDict(plugin_list=self.last_exported_list())
all_limits = stats.getAllLimitsAsDict(plugin_list=self.last_exported_list())
# Loop over plugins to export
for plugin in self.last_exported_list():
# Remove some fields
if isinstance(all_stats[plugin], dict):
all_stats[plugin].update(all_limits[plugin])
# Remove the <plugin>_disable field
all_stats[plugin].pop(f"{plugin}_disable", None)
elif isinstance(all_stats[plugin], list):
for i in all_stats[plugin]:
i.update(all_limits[plugin])
# Remove the <plugin>_disable field
i.pop(f"{plugin}_disable", None)
else:
continue
plugin_stats = all_stats[plugin]
creation_list = [] # List used to create the DuckDB table
values_list = [] # List of values to insert (list of lists, one list per row)
if isinstance(plugin_stats, dict):
# Create the list to create the table
creation_list.append('time TIMETZ')
creation_list.append('hostname_id VARCHAR')
for key, value in plugin_stats.items():
creation_list.append(f"{key} {convert_types[type(self.normalize(value)).__name__]}")
# Create the list of values to insert
item_list = []
item_list.append(self.normalize(datetime.now().replace(microsecond=0)))
item_list.append(self.normalize(f"{self.hostname}"))
item_list.extend([self.normalize(value) for value in plugin_stats.values()])
values_list = [item_list]
elif isinstance(plugin_stats, list) and len(plugin_stats) > 0 and 'key' in plugin_stats[0]:
# Create the list to create the table
creation_list.append('time TIMETZ')
creation_list.append('hostname_id VARCHAR')
creation_list.append('key_id VARCHAR')
for key, value in plugin_stats[0].items():
creation_list.append(f"{key} {convert_types[type(self.normalize(value)).__name__]}")
# Create the list of values to insert
for plugin_item in plugin_stats:
item_list = []
item_list.append(self.normalize(datetime.now().replace(microsecond=0)))
item_list.append(self.normalize(f"{self.hostname}"))
item_list.append(self.normalize(f"{plugin_item.get('key')}"))
item_list.extend([self.normalize(value) for value in plugin_item.values()])
values_list.append(item_list)
else:
continue
# Export stats to DuckDB
self.export(plugin, creation_list, values_list)
return True
def export(self, plugin, creation_list, values_list):
"""Export the stats to the DuckDB server."""
logger.debug(f"Export {plugin} stats to DuckDB")
# Create the table if it does not exist
table_list = [t[0] for t in self.client.sql("SHOW TABLES").fetchall()]
if plugin not in table_list:
# Execute the create table query
create_query = f"""
CREATE TABLE {plugin} (
{', '.join(creation_list)}
);"""
logger.debug(f"Create table: {create_query}")
try:
self.client.execute(create_query)
except Exception as e:
logger.error(f"Cannot create table {plugin}: {e}")
return
# Commit the changes
self.client.commit()
# Insert values into the table
for values in values_list:
insert_query = f"""
INSERT INTO {plugin} VALUES (
{', '.join(['?' for _ in values])}
);"""
logger.debug(f"Insert values into table {plugin}: {values}")
try:
self.client.execute(insert_query, values)
except Exception as e:
logger.error(f"Cannot insert data into table {plugin}: {e}")
# Commit the changes
self.client.commit()
def exit(self):
"""Close the DuckDB export module."""
# Force last write
self.client.commit()
# Close the DuckDB client
time.sleep(3) # Wait a bit to ensure all data is written
self.client.close()
# Call the father method
super().exit()

View File

@ -17,7 +17,7 @@ import pygal.style
from pygal import DateTimeLine
from glances.exports.export import GlancesExport
from glances.globals import iteritems, time_series_subsample
from glances.globals import time_series_subsample
from glances.logger import logger
from glances.timer import Timer
@ -120,7 +120,7 @@ class Export(GlancesExport):
x_label_rotation=20,
x_value_formatter=lambda dt: dt.strftime('%Y/%m/%d %H:%M:%S'),
)
for k, v in iteritems(time_series_subsample(data, self.width)):
for k, v in time_series_subsample(data, self.width).items():
chart.add(k, v)
chart.render_to_file(os.path.join(self.path, title + '.svg'))
return True

View File

@ -17,8 +17,6 @@ from influxdb.client import InfluxDBClientError
from glances.exports.export import GlancesExport
from glances.logger import logger
FIELD_TO_TAG = ['name', 'cmdline', 'type']
class Export(GlancesExport):
"""This class manages the InfluxDB export module."""
@ -33,20 +31,22 @@ class Export(GlancesExport):
self.db = None
# Optional configuration keys
self.protocol = 'http'
self.protocol = "http"
self.prefix = None
self.tags = None
self.hostname = None
# Load the InfluxDB configuration file
self.export_enable = self.load_conf(
'influxdb', mandatories=['host', 'port', 'user', 'password', 'db'], options=['protocol', 'prefix', 'tags']
"influxdb",
mandatories=["host", "port", "user", "password", "db"],
options=["protocol", "prefix", "tags"],
)
if not self.export_enable:
exit('Missing INFLUXDB version 1 config')
exit("Missing influxdb config")
# The hostname is always add as a tag
self.hostname = node().split('.')[0]
self.hostname = node().split(".")[0]
# Init the InfluxDB client
self.client = self.init()
@ -57,7 +57,7 @@ class Export(GlancesExport):
return None
# Correct issue #1530
if self.protocol is not None and (self.protocol.lower() == 'https'):
if self.protocol is not None and (self.protocol.lower() == "https"):
ssl = True
else:
ssl = False
@ -72,7 +72,7 @@ class Export(GlancesExport):
password=self.password,
database=self.db,
)
get_all_db = [i['name'] for i in db.get_list_database()]
get_all_db = [i["name"] for i in db.get_list_database()]
except InfluxDBClientError as e:
logger.critical(f"Cannot connect to InfluxDB database '{self.db}' ({e})")
sys.exit(2)
@ -85,76 +85,20 @@ class Export(GlancesExport):
return db
def _normalize(self, name, columns, points):
"""Normalize data for the InfluxDB's data model.
:return: a list of measurements.
"""
ret = []
# Build initial dict by crossing columns and point
data_dict = dict(zip(columns, points))
# issue1871 - Check if a key exist. If a key exist, the value of
# the key should be used as a tag to identify the measurement.
keys_list = [k.split('.')[0] for k in columns if k.endswith('.key')]
if not keys_list:
keys_list = [None]
for measurement in keys_list:
# Manage field
if measurement is not None:
fields = {
k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.')
}
else:
fields = data_dict
# Transform to InfluxDB data model
# https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_reference/
for k in fields:
# Do not export empty (None) value
if fields[k] is None:
continue
# Convert numerical to float
try:
fields[k] = float(fields[k])
except (TypeError, ValueError):
# Convert others to string
try:
fields[k] = str(fields[k])
except (TypeError, ValueError):
pass
# Manage tags
tags = self.parse_tags(self.tags)
if 'key' in fields and fields['key'] in fields:
# Create a tag from the key
# Tag should be an string (see InfluxDB data model)
tags[fields['key']] = str(fields[fields['key']])
# Remove it from the field list (can not be a field and a tag)
fields.pop(fields['key'])
# Add the hostname as a tag
tags['hostname'] = self.hostname
# Add name as a tag (example for the process list)
for k in FIELD_TO_TAG:
if k in fields:
tags[k] = str(fields[k])
# Remove it from the field list (can not be a field and a tag)
fields.pop(k)
# Add the measurement to the list
ret.append({'measurement': name, 'tags': tags, 'fields': fields})
return ret
def export(self, name, columns, points):
"""Write the points to the InfluxDB server."""
# Manage prefix
if self.prefix is not None:
name = self.prefix + '.' + name
name = self.prefix + "." + name
# Write input to the InfluxDB database
if not points:
logger.debug(f"Cannot export empty {name} stats to InfluxDB")
else:
try:
self.client.write_points(self._normalize(name, columns, points), time_precision="s")
self.client.write_points(
self.normalize_for_influxdb(name, columns, points),
time_precision="s",
)
except Exception as e:
# Log level set to warning instead of error (see: issue #1561)
logger.warning(f"Cannot export {name} stats to InfluxDB ({e})")

View File

@ -6,7 +6,7 @@
# SPDX-License-Identifier: LGPL-3.0-only
#
"""InfluxDB (from to InfluxDB 1.8+) interface class."""
"""InfluxDB (from to InfluxDB 1.8+ to <3.0) interface class."""
import sys
from platform import node
@ -16,8 +16,6 @@ from influxdb_client import InfluxDBClient, WriteOptions
from glances.exports.export import GlancesExport
from glances.logger import logger
FIELD_TO_TAG = ['name', 'cmdline', 'type']
class Export(GlancesExport):
"""This class manages the InfluxDB export module."""
@ -32,7 +30,7 @@ class Export(GlancesExport):
self.token = None
# Optional configuration keys
self.protocol = 'http'
self.protocol = "http"
self.prefix = None
self.tags = None
self.hostname = None
@ -40,12 +38,12 @@ class Export(GlancesExport):
# Load the InfluxDB configuration file
self.export_enable = self.load_conf(
'influxdb2',
mandatories=['host', 'port', 'user', 'password', 'org', 'bucket', 'token'],
options=['protocol', 'prefix', 'tags', 'interval'],
"influxdb2",
mandatories=["host", "port", "user", "password", "org", "bucket", "token"],
options=["protocol", "prefix", "tags", "interval"],
)
if not self.export_enable:
exit('Missing influxdb2 config')
exit("Missing influxdb2 config")
# Interval between two exports (in seconds)
if self.interval is None:
@ -60,7 +58,7 @@ class Export(GlancesExport):
logger.debug(f"InfluxDB export interval is set to {self.interval} seconds")
# The hostname is always add as a tag
self.hostname = node().split('.')[0]
self.hostname = node().split(".")[0]
# Init the InfluxDB client
self.client = self.init()
@ -70,10 +68,16 @@ class Export(GlancesExport):
if not self.export_enable:
return None
url = f'{self.protocol}://{self.host}:{self.port}'
url = f"{self.protocol}://{self.host}:{self.port}"
try:
# See docs: https://influxdb-client.readthedocs.io/en/stable/api.html#influxdbclient
client = InfluxDBClient(url=url, enable_gzip=False, verify_ssl=False, org=self.org, token=self.token)
client = InfluxDBClient(
url=url,
enable_gzip=False,
verify_ssl=False,
org=self.org,
token=self.token,
)
except Exception as e:
logger.critical(f"Cannot connect to InfluxDB server '{url}' ({e})")
sys.exit(2)
@ -93,76 +97,22 @@ class Export(GlancesExport):
)
)
def _normalize(self, name, columns, points):
"""Normalize data for the InfluxDB's data model.
:return: a list of measurements.
"""
ret = []
# Build initial dict by crossing columns and point
data_dict = dict(zip(columns, points))
# issue1871 - Check if a key exist. If a key exist, the value of
# the key should be used as a tag to identify the measurement.
keys_list = [k.split('.')[0] for k in columns if k.endswith('.key')]
if not keys_list:
keys_list = [None]
for measurement in keys_list:
# Manage field
if measurement is not None:
fields = {
k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.')
}
else:
fields = data_dict
# Transform to InfluxDB datamodel
# https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/
for k in fields:
# Do not export empty (None) value
if fields[k] is None:
continue
# Convert numerical to float
try:
fields[k] = float(fields[k])
except (TypeError, ValueError):
# Convert others to string
try:
fields[k] = str(fields[k])
except (TypeError, ValueError):
pass
# Manage tags
tags = self.parse_tags(self.tags)
if 'key' in fields and fields['key'] in fields:
# Create a tag from the key
# Tag should be an string (see InfluxDB data model)
tags[fields['key']] = str(fields[fields['key']])
# Remove it from the field list (can not be a field and a tag)
fields.pop(fields['key'])
# Add the hostname as a tag
tags['hostname'] = self.hostname
# Add name as a tag (example for the process list)
for k in FIELD_TO_TAG:
if k in fields:
tags[k] = str(fields[k])
# Remove it from the field list (can not be a field and a tag)
fields.pop(k)
# Add the measurement to the list
ret.append({'measurement': name, 'tags': tags, 'fields': fields})
return ret
def export(self, name, columns, points):
"""Write the points to the InfluxDB server."""
# Manage prefix
if self.prefix is not None:
name = self.prefix + '.' + name
name = self.prefix + "." + name
# Write input to the InfluxDB database
if not points:
logger.debug(f"Cannot export empty {name} stats to InfluxDB")
else:
try:
self.client.write(self.bucket, self.org, self._normalize(name, columns, points), time_precision="s")
self.client.write(
self.bucket,
self.org,
self.normalize_for_influxdb(name, columns, points),
time_precision="s",
)
except Exception as e:
# Log level set to warning instead of error (see: issue #1561)
logger.warning(f"Cannot export {name} stats to InfluxDB ({e})")

View File

@ -0,0 +1,98 @@
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
"""InfluxDB (for InfluxDB 3.x) interface class."""
import sys
from platform import node
from influxdb_client_3 import InfluxDBClient3
from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport):
"""This class manages the InfluxDB export module."""
def __init__(self, config=None, args=None):
"""Init the InfluxDB export IF."""
super().__init__(config=config, args=args)
# Mandatory configuration keys (additional to host and port)
self.host = None
self.port = None
self.org = None
self.database = None
self.token = None
# Optional configuration keys
self.prefix = None
self.tags = None
self.hostname = None
# Load the InfluxDB configuration file
self.export_enable = self.load_conf(
"influxdb3",
mandatories=["host", "port", "org", "database", "token"],
options=["prefix", "tags"],
)
if not self.export_enable:
exit("Missing influxdb3 config")
# The hostname is always add as a tag
self.hostname = node().split(".")[0]
# Init the InfluxDB client
self.client = self.init()
def init(self):
"""Init the connection to the InfluxDB server."""
if not self.export_enable:
return None
try:
db = InfluxDBClient3(
host=self.host,
org=self.org,
database=self.database,
token=self.token,
)
except Exception as e:
logger.critical(f"Cannot connect to InfluxDB database '{self.database}' ({e})")
sys.exit(2)
if self.database == db._database:
logger.info(
f"Stats will be exported to InfluxDB server {self.host}:{self.port} in {self.database} database"
)
else:
logger.critical(f"InfluxDB database '{self.database}' did not exist. Please create it")
sys.exit(2)
return db
def export(self, name, columns, points):
"""Write the points to the InfluxDB server."""
# Manage prefix
if self.prefix is not None:
name = self.prefix + "." + name
# Write input to the InfluxDB database
if not points:
logger.debug(f"Cannot export empty {name} stats to InfluxDB")
else:
try:
self.client.write(
record=self.normalize_for_influxdb(name, columns, points),
time_precision="s",
)
except Exception as e:
# Log level set to warning instead of error (see: issue #1561)
logger.warning(f"Cannot export {name} stats to InfluxDB ({e})")
else:
logger.debug(f"Export {name} stats to InfluxDB")

View File

@ -48,7 +48,10 @@ class Export(GlancesExport):
# Export stats to JSON file
with open(self.json_filename, "wb") as self.json_file:
self.json_file.write(json_dumps(self.buffer) + b'\n')
try:
self.json_file.write(json_dumps(self.buffer) + b'\n')
except Exception as e:
logger.error(f'Can not export data to JSON ({e})')
# Reset buffer
self.buffer = {}

0
glances/exports/glances_mqtt/__init__.py Executable file → Normal file
View File

View File

@ -0,0 +1,134 @@
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2026 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
"""NATS interface class."""
from nats.aio.client import Client as NATS
from nats.errors import ConnectionClosedError
from nats.errors import TimeoutError as NatsTimeoutError
from glances.exports.export_asyncio import GlancesExportAsyncio
from glances.globals import json_dumps
from glances.logger import logger
class Export(GlancesExportAsyncio):
"""This class manages the NATS export module."""
def __init__(self, config=None, args=None):
"""Init the NATS export IF."""
# Load the NATS configuration file before calling super().__init__
# because super().__init__ will call _async_init() which needs config
self.config = config
self.args = args
self.export_name = self.__class__.__module__
export_enable = self.load_conf(
'nats',
mandatories=['host'],
options=['prefix'],
)
if not export_enable:
exit('Missing NATS config')
self.prefix = self.prefix or 'glances'
# Host is a comma-separated list of NATS servers
self.hosts = self.host
# NATS-specific attributes
self.client = None
self._connected = False
self._publish_count = 0
# Call parent __init__ which will start event loop and call _async_init()
super().__init__(config=config, args=args)
# Restore export_enable after super().__init__() resets it to False
self.export_enable = export_enable
async def _async_init(self):
"""Connect to NATS with error handling."""
try:
if self.client:
try:
await self.client.close()
except Exception as e:
logger.debug(f"NATS Error closing existing client: {e}")
self.client = NATS()
logger.debug(f"NATS Connecting to servers: {self.hosts}")
# Configure with reconnection callbacks
await self.client.connect(
servers=[s.strip() for s in self.hosts.split(',')],
reconnect_time_wait=2,
max_reconnect_attempts=60,
error_cb=self._error_callback,
disconnected_cb=self._disconnected_callback,
reconnected_cb=self._reconnected_callback,
)
self._connected = True
logger.info(f"NATS Successfully connected to servers: {self.hosts}")
except Exception as e:
self._connected = False
logger.error(f"NATS connection error: {e}")
raise
async def _error_callback(self, e):
"""Called when NATS client encounters an error."""
logger.error(f"NATS error callback: {e}")
async def _disconnected_callback(self):
"""Called when disconnected from NATS."""
self._connected = False
logger.debug("NATS disconnected callback")
async def _reconnected_callback(self):
"""Called when reconnected to NATS."""
self._connected = True
logger.debug("NATS reconnected callback")
async def _async_exit(self):
"""Disconnect from NATS."""
try:
if self.client and self._connected:
await self.client.drain()
await self.client.close()
self._connected = False
logger.debug(f"NATS disconnected cleanly. Total messages published: {self._publish_count}")
except Exception as e:
logger.error(f"NATS Error in disconnect: {e}")
async def _async_export(self, name, columns, points):
"""Write the points to NATS using AsyncIO."""
if not self._connected:
logger.warning("NATS not connected, skipping export")
return
subject_name = f"{self.prefix}.{name}"
subject_data = dict(zip(columns, points))
# Publish data to NATS
try:
if not self._connected:
raise ConnectionClosedError("NATS Not connected to server")
await self.client.publish(subject_name, json_dumps(subject_data))
await self.client.flush(timeout=2.0)
self._publish_count += 1
except (ConnectionClosedError, NatsTimeoutError) as e:
self._connected = False
logger.error(f"NATS publish failed for {subject_name}: {e}")
raise
except Exception as e:
logger.error(f"NATS Unexpected error publishing {subject_name}: {e}", exc_info=True)
raise
# End of glances/exports/glances_nats/__init__.py

View File

@ -57,7 +57,7 @@ class Export(GlancesExport):
return db
def export(self, name, columns, points):
"""Export the stats to the Statsd server."""
"""Export the stats to the OpenTSDB server."""
for i in range(len(columns)):
if not isinstance(points[i], Number):
continue

View File

@ -14,7 +14,7 @@ from numbers import Number
from prometheus_client import Gauge, start_http_server
from glances.exports.export import GlancesExport
from glances.globals import iteritems, listkeys
from glances.globals import listkeys
from glances.logger import logger
@ -43,6 +43,9 @@ class Export(GlancesExport):
# Perhaps a better method is possible...
self._metric_dict = {}
# Keys name (compute in update() method)
self.keys_name = {}
# Init the Prometheus Exporter
self.init()
@ -56,29 +59,41 @@ class Export(GlancesExport):
else:
logger.info(f"Start Prometheus exporter on {self.host}:{self.port}")
def update(self, stats):
self.keys_name = {k: stats.get_plugin(k).get_key() for k in stats.getPluginsList()}
super().update(stats)
def export(self, name, columns, points):
"""Write the points to the Prometheus exporter using Gauge."""
logger.debug(f"Export {name} stats to Prometheus exporter")
# Remove non number stats and convert all to float (for Boolean)
data = {k: float(v) for (k, v) in iteritems(dict(zip(columns, points))) if isinstance(v, Number)}
data = {str(k): float(v) for k, v in zip(columns, points) if isinstance(v, Number)}
# Write metrics to the Prometheus exporter
for k, v in iteritems(data):
# Prometheus metric name: prefix_<glances stats name>
metric_name = self.prefix + self.METRIC_SEPARATOR + str(name) + self.METRIC_SEPARATOR + str(k)
for metric, value in data.items():
labels = self.labels
metric_name = self.prefix + self.METRIC_SEPARATOR + name + self.METRIC_SEPARATOR
try:
obj, stat = metric.split('.')
metric_name += stat
labels += f",{self.keys_name.get(name)}:{obj}"
except ValueError:
metric_name += metric
# Prometheus is very sensible to the metric name
# See: https://prometheus.io/docs/practices/naming/
for c in ' .-/:[]':
metric_name = metric_name.replace(c, self.METRIC_SEPARATOR)
# Get the labels
labels = self.parse_tags(self.labels)
labels = self.parse_tags(labels)
# Manage an internal dict between metric name and Gauge
if metric_name not in self._metric_dict:
self._metric_dict[metric_name] = Gauge(metric_name, k, labelnames=listkeys(labels))
self._metric_dict[metric_name] = Gauge(metric_name, "", labelnames=listkeys(labels))
# Write the value
if hasattr(self._metric_dict[metric_name], 'labels'):
# Add the labels (see issue #1255)
self._metric_dict[metric_name].labels(**labels).set(v)
self._metric_dict[metric_name].labels(**labels).set(value)
else:
self._metric_dict[metric_name].set(v)
self._metric_dict[metric_name].set(value)

View File

@ -54,7 +54,7 @@ class Export(GlancesExport):
# One complete loop have been done
logger.debug(f"Export stats ({listkeys(self.buffer)}) to RESTful endpoint ({self.client})")
# Export stats
post(self.client, json=self.buffer, allow_redirects=True)
post(self.client, json=self.buffer, allow_redirects=True, timeout=15)
# Reset buffer
self.buffer = {}

View File

@ -0,0 +1,223 @@
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
"""TimescaleDB interface class."""
import sys
import time
from platform import node
import psycopg
from glances.exports.export import GlancesExport
from glances.logger import logger
# Define the type conversions for TimescaleDB
# https://www.postgresql.org/docs/current/datatype.html
convert_types = {
'bool': 'BOOLEAN',
'int': 'BIGINT',
'float': 'DOUBLE PRECISION',
'str': 'TEXT',
'tuple': 'TEXT', # Store tuples as TEXT (comma-separated)
'list': 'TEXT', # Store lists as TEXT (comma-separated)
'NoneType': 'DOUBLE PRECISION', # Use DOUBLE PRECISION for NoneType to avoid issues with NULL
}
class Export(GlancesExport):
"""This class manages the TimescaleDB export module."""
def __init__(self, config=None, args=None):
"""Init the TimescaleDB export IF."""
super().__init__(config=config, args=args)
# Mandatory configuration keys (additional to host and port)
self.db = None
# Optional configuration keys
self.user = None
self.password = None
self.hostname = None
# Load the configuration file
self.export_enable = self.load_conf(
'timescaledb', mandatories=['host', 'port', 'db'], options=['user', 'password', 'hostname']
)
if not self.export_enable:
exit('Missing TimescaleDB config')
# The hostname is always add as an identifier in the TimescaleDB table
# so we can filter the stats by hostname
self.hostname = self.hostname or node().split(".")[0]
# Init the TimescaleDB client
self.client = self.init()
def init(self):
"""Init the connection to the TimescaleDB server."""
if not self.export_enable:
return None
try:
# See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
conn_str = f"host={self.host} port={self.port} dbname={self.db} user={self.user} password={self.password}"
db = psycopg.connect(conn_str)
except Exception as e:
logger.critical(f"Cannot connect to TimescaleDB server {self.host}:{self.port} ({e})")
sys.exit(2)
else:
logger.info(f"Stats will be exported to TimescaleDB server: {self.host}:{self.port}")
return db
def normalize(self, value):
"""Normalize the value to be exportable to TimescaleDB."""
if value is None:
return 'NULL'
if isinstance(value, bool):
return str(value).upper()
if isinstance(value, (list, tuple)):
# Special case for list of one boolean
if len(value) == 1 and isinstance(value[0], bool):
return str(value[0]).upper()
return ', '.join([f"'{v}'" for v in value])
if isinstance(value, str):
return f"'{value}'"
return f"{value}"
def update(self, stats):
"""Update the TimescaleDB export module."""
if not self.export_enable:
return False
# Get all the stats & limits
# @TODO: Current limitation with sensors, fs and diskio plugins because fields list is not the same
self._last_exported_list = [p for p in self.plugins_to_export(stats) if p not in ['sensors', 'fs', 'diskio']]
all_stats = stats.getAllExportsAsDict(plugin_list=self.last_exported_list())
all_limits = stats.getAllLimitsAsDict(plugin_list=self.last_exported_list())
# Loop over plugins to export
for plugin in self.last_exported_list():
if isinstance(all_stats[plugin], dict):
all_stats[plugin].update(all_limits[plugin])
# Remove the <plugin>_disable field
all_stats[plugin].pop(f"{plugin}_disable", None)
# user is a special field that should not be exported
# rename it to user_<plugin>
if 'user' in all_stats[plugin]:
all_stats[plugin][f'user_{plugin}'] = all_stats[plugin].pop('user')
elif isinstance(all_stats[plugin], list):
for i in all_stats[plugin]:
i.update(all_limits[plugin])
# Remove the <plugin>_disable field
i.pop(f"{plugin}_disable", None)
# user is a special field that should not be exported
# rename it to user_<plugin>
if 'user' in i:
i[f'user_{plugin}'] = i.pop('user')
else:
continue
plugin_stats = all_stats[plugin]
creation_list = [] # List used to create the TimescaleDB table
segmented_by = [] # List of columns used to segment the data
values_list = [] # List of values to insert (list of lists, one list per row)
if isinstance(plugin_stats, dict):
# Stats is a dict
# Create the list used to create the TimescaleDB table
creation_list.append('time TIMESTAMPTZ NOT NULL')
creation_list.append('hostname_id TEXT NOT NULL')
segmented_by.extend(['hostname_id']) # Segment by hostname
for key, value in plugin_stats.items():
creation_list.append(f"{key} {convert_types[type(value).__name__]} NULL")
values_list.append('NOW()') # Add the current time (insertion time)
values_list.append(f"'{self.hostname}'") # Add the hostname
values_list.extend([self.normalize(value) for value in plugin_stats.values()])
values_list = [values_list]
elif isinstance(plugin_stats, list) and len(plugin_stats) > 0 and 'key' in plugin_stats[0]:
# Stats is a list
# Create the list used to create the TimescaleDB table
creation_list.append('time TIMESTAMPTZ NOT NULL')
creation_list.append('hostname_id TEXT NOT NULL')
creation_list.append('key_id TEXT NOT NULL')
segmented_by.extend(['hostname_id', 'key_id']) # Segment by hostname and key
for key, value in plugin_stats[0].items():
creation_list.append(f"{key} {convert_types[type(value).__name__]} NULL")
# Create the values list (it is a list of list to have a single datamodel for all the plugins)
for plugin_item in plugin_stats:
item_list = []
item_list.append('NOW()') # Add the current time (insertion time)
item_list.append(f"'{self.hostname}'") # Add the hostname
item_list.append(f"'{plugin_item.get('key')}'")
item_list.extend([self.normalize(value) for value in plugin_item.values()])
values_list.append(item_list[:-1])
else:
continue
# Export stats to TimescaleDB
# logger.info(plugin)
# logger.info(f"Segmented by: {segmented_by}")
# logger.info(list(zip(creation_list, values_list[0])))
self.export(plugin, creation_list, segmented_by, values_list)
return True
def export(self, plugin, creation_list, segmented_by, values_list):
"""Export the stats to the TimescaleDB server."""
logger.debug(f"Export {plugin} stats to TimescaleDB")
with self.client.cursor() as cur:
# Is the table exists?
cur.execute(f"select exists(select * from information_schema.tables where table_name='{plugin}')")
if not cur.fetchone()[0]:
# Create the table if it does not exist
# https://github.com/timescale/timescaledb/blob/main/README.md#create-a-hypertable
# Execute the create table query
create_query = f"""
CREATE TABLE {plugin} (
{', '.join(creation_list)}
)
WITH (
timescaledb.hypertable,
timescaledb.partition_column='time',
timescaledb.segmentby = '{", ".join(segmented_by)}'
);"""
logger.debug(f"Create table: {create_query}")
try:
cur.execute(create_query)
except Exception as e:
logger.error(f"Cannot create table {plugin}: {e}")
return
# Insert the data
# https://github.com/timescale/timescaledb/blob/main/README.md#insert-and-query-data
insert_list = [f"({','.join(i)})" for i in values_list]
insert_query = f"INSERT INTO {plugin} VALUES {','.join(insert_list)};"
logger.debug(f"Insert data into table: {insert_query}")
try:
cur.execute(insert_query)
except Exception as e:
logger.error(f"Cannot insert data into table {plugin}: {e}")
return
# Commit the changes (for every plugin or to be done at the end ?)
self.client.commit()
def exit(self):
"""Close the TimescaleDB export module."""
# Force last write
self.client.commit()
# Close the TimescaleDB client
time.sleep(3) # Wait a bit to ensure all data is written
self.client.close()
# Call the father method
super().exit()

View File

@ -17,10 +17,12 @@ import base64
import errno
import functools
import importlib
import multiprocessing
import os
import platform
import queue
import re
import socket
import subprocess
import sys
import weakref
@ -34,6 +36,8 @@ from urllib.error import HTTPError, URLError
from urllib.parse import urlparse
from urllib.request import Request, urlopen
import psutil
# Prefer faster libs for JSON (de)serialization
# Preference Order: orjson > ujson > json (builtin)
try:
@ -96,6 +100,11 @@ viewkeys = methodcaller('keys')
viewvalues = methodcaller('values')
viewitems = methodcaller('items')
# Multiprocessing start method (on POSIX system)
if LINUX or BSD or SUNOS or MACOS:
ctx_mp_fork = multiprocessing.get_context('fork')
else:
ctx_mp_fork = multiprocessing.get_context()
###################
# GLOBALS FUNCTIONS
@ -127,18 +136,6 @@ def listvalues(d):
return list(d.values())
def iteritems(d):
return iter(d.items())
def iterkeys(d):
return iter(d.keys())
def itervalues(d):
return iter(d.values())
def u(s, errors='replace'):
if isinstance(s, text_type):
return s
@ -370,11 +367,18 @@ def json_dumps(data) -> bytes:
return b(res)
def json_loads(data: Union[str, bytes, bytearray]) -> Union[dict, list]:
def json_loads(data: str | bytes | bytearray) -> dict | list:
"""Load a JSON buffer into memory as a Python object"""
return json.loads(data)
def list_to_dict(data):
"""Convert a list of dict (with key in 'key') to a dict with key as key and value as value."""
if not isinstance(data, list):
return None
return {item[item['key']]: item for item in data if 'key' in item}
def dictlist(data, item):
if isinstance(data, dict):
try:
@ -400,7 +404,7 @@ def dictlist_json_dumps(data, item):
return json_dumps(dl)
def dictlist_first_key_value(data: list[dict], key, value) -> Optional[dict]:
def dictlist_first_key_value(data: list[dict], key, value) -> dict | None:
"""In a list of dict, return first item where key=value or none if not found."""
try:
ret = next(item for item in data if key in item and item[key] == value)
@ -409,6 +413,65 @@ def dictlist_first_key_value(data: list[dict], key, value) -> Optional[dict]:
return ret
def auto_unit(number, low_precision=False, min_symbol='K', none_symbol='-'):
"""Make a nice human-readable string out of number.
Number of decimal places increases as quantity approaches 1.
CASE: 613421788 RESULT: 585M low_precision: 585M
CASE: 5307033647 RESULT: 4.94G low_precision: 4.9G
CASE: 44968414685 RESULT: 41.9G low_precision: 41.9G
CASE: 838471403472 RESULT: 781G low_precision: 781G
CASE: 9683209690677 RESULT: 8.81T low_precision: 8.8T
CASE: 1073741824 RESULT: 1024M low_precision: 1024M
CASE: 1181116006 RESULT: 1.10G low_precision: 1.1G
:low_precision: returns less decimal places potentially (default is False)
sacrificing precision for more readability.
:min_symbol: Do not approach if number < min_symbol (default is K)
:decimal_count: if set, force the number of decimal number (default is None)
"""
if number is None:
return none_symbol
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
if min_symbol in symbols:
symbols = symbols[symbols.index(min_symbol) :]
prefix = {
'Y': 1208925819614629174706176,
'Z': 1180591620717411303424,
'E': 1152921504606846976,
'P': 1125899906842624,
'T': 1099511627776,
'G': 1073741824,
'M': 1048576,
'K': 1024,
}
if number == 0:
# Avoid 0.0
return '0'
# If a value is a float, decimal_precision is 2 else 0
decimal_precision = 2 if isinstance(number, float) else 0
for symbol in reversed(symbols):
value = float(number) / prefix[symbol]
if value > 1:
decimal_precision = 0
if value < 10:
decimal_precision = 2
elif value < 100:
decimal_precision = 1
if low_precision:
if symbol in 'MK':
decimal_precision = 0
else:
decimal_precision = min(1, decimal_precision)
elif symbol in 'K':
decimal_precision = 0
return '{:.{decimal}f}{symbol}'.format(value, decimal=decimal_precision, symbol=symbol)
return f'{number:.{decimal_precision}f}'
def string_value_to_float(s):
"""Convert a string with a value and an unit to a float.
Example:
@ -476,18 +539,32 @@ def folder_size(path, errno=0):
return ret_size, ret_err
def weak_lru_cache(maxsize=128, typed=False):
def _get_ttl_hash(ttl):
"""A simple (dummy) function to return a hash based on the current second.
TODO: Implement a real TTL mechanism.
"""
if ttl is None:
return 0
now = datetime.now()
return now.second
def weak_lru_cache(maxsize=1, typed=False, ttl=None):
"""LRU Cache decorator that keeps a weak reference to self
Warning: When used in a class, the class should implement __eq__(self, other) and __hash__(self) methods
Source: https://stackoverflow.com/a/55990799"""
def wrapper(func):
@functools.lru_cache(maxsize, typed)
def _func(_self, *args, **kwargs):
def _func(_self, *args, ttl_hash=None, **kwargs):
del ttl_hash # Unused parameter, but kept for compatibility
return func(_self(), *args, **kwargs)
@functools.wraps(func)
def inner(self, *args, **kwargs):
return _func(weakref.ref(self), *args, **kwargs)
return _func(weakref.ref(self), *args, ttl_hash=_get_ttl_hash(ttl), **kwargs)
return inner
@ -496,7 +573,7 @@ def weak_lru_cache(maxsize=128, typed=False):
def namedtuple_to_dict(data):
"""Convert a namedtuple to a dict, using the _asdict() method embedded in PsUtil stats."""
return {k: (v._asdict() if hasattr(v, '_asdict') else v) for k, v in data.items()}
return {k: (v._asdict() if hasattr(v, '_asdict') else v) for k, v in list(data.items())}
def list_of_namedtuple_to_list_of_dict(data):
@ -508,3 +585,190 @@ def replace_special_chars(input_string, by=' '):
"""Replace some special char by another in the input_string
Return: the string with the chars replaced"""
return input_string.replace('\r\n', by).replace('\n', by).replace('\t', by)
def atoi(text):
return int(text) if text.isdigit() else text
def natural_keys(text):
"""Return a text in a natural/human readable format."""
return [atoi(c) for c in re.split(r'(\d+)', text)]
def exit_after(seconds, default=None):
"""Exit the function if it takes more than 'seconds' seconds to complete.
In this case, return the value of 'default' (default: None)."""
def handler(q, func, args, kwargs):
q.put(func(*args, **kwargs))
def decorator(func):
if not LINUX:
return func
def wraps(*args, **kwargs):
try:
q = ctx_mp_fork.Queue()
except PermissionError:
# Manage an exception in Snap packages on Linux
# The strict mode prevent the use of multiprocessing.Queue()
# There is a "dirty" hack:
# https://forum.snapcraft.io/t/python-multiprocessing-permission-denied-in-strictly-confined-snap/15518/2
# But i prefer to just disable the timeout feature in this case
func(*args, **kwargs)
else:
p = ctx_mp_fork.Process(target=handler, args=(q, func, args, kwargs))
p.start()
p.join(timeout=seconds)
if not p.is_alive():
return q.get()
p.terminate()
p.join(timeout=0.1)
if p.is_alive():
# Kill in case processes doesn't terminate
# Happens with cases like broken NFS connections
p.kill()
return default
return wraps
return decorator
def split_esc(input_string, sep=None, maxsplit=-1, esc='\\'):
"""
Return a list of the substrings in the input_string, using sep as the separator char
and esc as the escape character.
sep
The separator used to split the input_string.
When set to None (the default value), will split on any whitespace
character (including \n \r \t \f and spaces) unless the character is escaped
and will discard empty strings from the result.
maxsplit
Maximum number of splits.
-1 (the default value) means no limit.
esc
The character used to escape the separator.
When set to None, this behaves equivalently to `str.split`.
Defaults to '\\\\' i.e. backslash.
Splitting starts at the front of the input_string and works to the end.
Note: escape characters in the substrings returned are removed. However, if
maxsplit is reached, escape characters in the remaining, unprocessed substring
are not removed, which allows split_esc to be called on it again.
"""
# Input validation
if not isinstance(input_string, str):
raise TypeError(f'must be str, not {input_string.__class__.__name__}')
str.split('', sep=sep, maxsplit=maxsplit) # Use str.split to validate sep and maxsplit
if esc is None:
return input_string.split(
sep=sep, maxsplit=maxsplit
) # Short circuit to default implementation if the escape character is None
if not isinstance(esc, str):
raise TypeError(f'must be str or None, not {esc.__class__.__name__}')
if len(esc) == 0:
raise ValueError('empty escape character')
if len(esc) > 1:
raise ValueError('escape must be a single character')
# Set up a simple state machine keeping track of whether we have seen an escape character
ret, esc_seen, i = [''], False, 0
while i < len(input_string) and len(ret) - 1 != maxsplit:
if not esc_seen:
if input_string[i] == esc:
# Consume the escape character and transition state
esc_seen = True
i += 1
elif sep is None and input_string[i].isspace():
# Consume as much whitespace as possible
n = 1
while i + n + 1 < len(input_string) and input_string[i + n : i + n + 1].isspace():
n += 1
ret.append('')
i += n
elif sep is not None and input_string[i : i + len(sep)] == sep:
# Consume the separator
ret.append('')
i += len(sep)
else:
# Otherwise just add the current char
ret[-1] += input_string[i]
i += 1
else:
# Add the current char and transition state back
ret[-1] += input_string[i]
esc_seen = False
i += 1
# Append any remaining string if we broke early because of maxsplit
if i < len(input_string):
ret[-1] += input_string[i:]
# If splitting on whitespace, discard empty strings from result
if sep is None:
ret = [sub for sub in ret if len(sub) > 0]
return ret
def get_ip_address(ipv6=False):
"""Get current IP address and netmask as a tuple."""
family = socket.AF_INET6 if ipv6 else socket.AF_INET
# Get IP address
stats = psutil.net_if_stats()
addrs = psutil.net_if_addrs()
ip_address = None
ip_netmask = None
for interface, stat in stats.items():
if stat.isup and interface != 'lo':
if interface in addrs:
for addr in addrs[interface]:
if addr.family == family:
ip_address = addr.address
ip_netmask = addr.netmask
break
return ip_address, ip_netmask
def get_default_gateway(ipv6=False):
"""Get the default gateway IP address."""
def convert_ipv4(gateway_hex):
"""Convert IPv4 hex (little-endian) to dotted notation."""
return '.'.join(str(int(gateway_hex[i : i + 2], 16)) for i in range(6, -1, -2))
def convert_ipv6(gateway_hex):
"""Convert IPv6 hex to colon notation."""
return ':'.join(gateway_hex[i : i + 4] for i in range(0, 32, 4))
if ipv6:
route_file = '/proc/net/ipv6_route'
default_dest = '00000000000000000000000000000000'
dest_field = 0
gateway_field = 4
converter = convert_ipv6
else:
route_file = '/proc/net/route'
default_dest = '00000000'
dest_field = 1
gateway_field = 2
converter = convert_ipv4
try:
with open(route_file) as f:
for line in f:
fields = line.strip().split()
if fields[dest_field] == default_dest:
return converter(fields[gateway_field])
except (FileNotFoundError, IndexError, ValueError):
return None
return None

Some files were not shown because too many files have changed in this diff Show More