mirror of https://github.com/nicolargo/glances.git
Add a focus option for the processlist plugin #3293
This commit is contained in:
parent
df4b312bec
commit
ab68fcbe28
|
|
@ -487,6 +487,8 @@ 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
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ 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)
|
||||
|
|
@ -175,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
|
||||
-------------
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
|
@ -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" "Nov 10, 2025" "4.4.2_dev1" "Glances"
|
||||
.TH "GLANCES" "1" "Nov 11, 2025" "4.4.2_dev1" "Glances"
|
||||
.SH NAME
|
||||
glances \- An eye on your system
|
||||
.SH SYNOPSIS
|
||||
|
|
|
|||
|
|
@ -152,6 +152,10 @@ Examples of use:
|
|||
if not self.args.process_filter and not self.is_standalone():
|
||||
logger.debug("Process filter is only available in standalone mode")
|
||||
|
||||
# Focus filter is only available in standalone mode
|
||||
if not self.args.process_focus and not self.is_standalone():
|
||||
logger.debug("Process focus is only available in standalone mode")
|
||||
|
||||
# Cursor option is only available in standalone mode
|
||||
if not self.args.disable_cursor and not self.is_standalone():
|
||||
logger.debug("Cursor is only available in standalone mode")
|
||||
|
|
@ -496,6 +500,14 @@ Examples of use:
|
|||
dest='process_filter',
|
||||
help='set the process filter pattern (regular expression)',
|
||||
)
|
||||
# Process will focus on some process (comma-separated list of Glances filter)
|
||||
parser.add_argument(
|
||||
'--process-focus',
|
||||
default=None,
|
||||
type=str,
|
||||
dest='process_focus',
|
||||
help='set a process list to focus on (comma-separated list of Glances filter)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--process-short-name',
|
||||
action='store_true',
|
||||
|
|
|
|||
|
|
@ -208,6 +208,16 @@ class GlancesRestfulApi:
|
|||
status.HTTP_401_UNAUTHORIZED, "Incorrect username or password", {"WWW-Authenticate": "Basic"}
|
||||
)
|
||||
|
||||
def _logo(self):
|
||||
return rf"""
|
||||
_____ _
|
||||
/ ____| |
|
||||
| | __| | __ _ _ __ ___ ___ ___
|
||||
| | |_ | |/ _` | '_ \ / __/ _ \/ __|
|
||||
| |__| | | (_| | | | | (_| __/\__
|
||||
\_____|_|\__,_|_| |_|\___\___||___/ {__version__}
|
||||
"""
|
||||
|
||||
def _router(self) -> APIRouter:
|
||||
"""Define a custom router for Glances path."""
|
||||
base_path = f'/api/{self.API_VERSION}'
|
||||
|
|
@ -267,6 +277,9 @@ class GlancesRestfulApi:
|
|||
for path, endpoint in route_mapping.items():
|
||||
router.add_api_route(path, endpoint)
|
||||
|
||||
# Logo
|
||||
print(self._logo())
|
||||
|
||||
# Browser WEBUI
|
||||
if hasattr(self.args, 'browser') and self.args.browser:
|
||||
# Template for the root browser.html file
|
||||
|
|
|
|||
|
|
@ -169,10 +169,10 @@ body {
|
|||
}
|
||||
|
||||
.button {
|
||||
color: #99CCFF; /* Bleu clair high-tech */
|
||||
background: rgba(0, 0, 0, 0.4); /* Fond légèrement transparent */
|
||||
border: 1px solid #99CCFF; /* Bordure discrète */
|
||||
padding: 5px 10px;
|
||||
color: #99CCFF;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid #99CCFF;
|
||||
padding: 1px 5px;
|
||||
border-radius: 5px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
|
|
@ -182,14 +182,14 @@ body {
|
|||
}
|
||||
|
||||
.button:hover {
|
||||
background: rgba(153, 204, 255, 0.15); /* Légère coloration au survol */
|
||||
background: rgba(183, 214, 255, 0.30);
|
||||
border-color: #B0D0FF;
|
||||
color: #B0D0FF;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: scale(0.95); /* Légère réduction pour effet de pression */
|
||||
box-shadow: 0 0 8px rgba(153, 204, 255, 0.5); /* Flash léger */
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 8px rgba(153, 204, 255, 0.5);
|
||||
}
|
||||
|
||||
.frequency {
|
||||
|
|
@ -413,6 +413,8 @@ body {
|
|||
#processlist {
|
||||
overflow-y: auto;
|
||||
height: 600px;
|
||||
margin-top: 1em;
|
||||
|
||||
.table {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
<div v-if="!args.disable_ip" class="d-none d-lg-block"><glances-plugin-ip
|
||||
:data="data"></glances-plugin-ip>
|
||||
</div>
|
||||
<div v-if="!args.disable_now" class="d-none d-xl-block"><glances-plugin-now
|
||||
:data="data"></glances-plugin-now></div>
|
||||
<div v-if="!args.disable_uptime" class="d-none d-md-block"><glances-plugin-uptime
|
||||
:data="data"></glances-plugin-uptime></div>
|
||||
<div v-if="!args.disable_now" class="d-none d-xl-block"><glances-plugin-now
|
||||
:data="data"></glances-plugin-now></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex d-none d-sm-block">
|
||||
|
|
|
|||
|
|
@ -101,7 +101,9 @@ export default {
|
|||
return this.store.config || {};
|
||||
},
|
||||
available_args() {
|
||||
return this.config.mem.available || false;
|
||||
return this.config !== undefined && this.config.mem !== undefined && this.config.available !== undefined
|
||||
? this.config.mem.available || false
|
||||
: false
|
||||
},
|
||||
stats() {
|
||||
return this.data.stats['mem'];
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<div>
|
||||
<span>CPU Min/Max/Mean: </span>
|
||||
<span class="careful">{{ $filters.number(extended_stats.cpu_min, 1)
|
||||
}}% / {{
|
||||
}}% / {{
|
||||
$filters.number(extended_stats.cpu_max, 1) }}% / {{ $filters.number(extended_stats.cpu_mean, 1)
|
||||
}}%</span>
|
||||
<span>Affinity: </span>
|
||||
|
|
@ -29,7 +29,8 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive d-lg-none">
|
||||
<div v-show="is_focus">Focus on following processes: {{ focus.join(', ') }}</div>
|
||||
<div class="table-responsive d-lg-none" id="processlist-table">
|
||||
<table class="table table-sm table-borderless table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -520,6 +521,16 @@ export default {
|
|||
return this.config.outputs !== undefined
|
||||
? this.config.outputs.max_processes_display
|
||||
: undefined;
|
||||
},
|
||||
focus() {
|
||||
return this.args !== undefined && this.args.process_focus !== undefined && this.args.process_focus !== null
|
||||
? this.args.process_focus.split(',')
|
||||
: this.config.processlist !== undefined && this.config.processlist.focus !== undefined && this.config.processlist.focus !== null
|
||||
? this.config.processlist.focus.split(',')
|
||||
: [];
|
||||
},
|
||||
is_focus() {
|
||||
return this.focus.length > 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -215,6 +215,11 @@ class ProcesslistPlugin(GlancesPluginModel):
|
|||
glances_processes.export_process_filter = config.as_dict()['processlist']['export']
|
||||
if args.export:
|
||||
logger.info("Export process filter is set to: {}".format(config.as_dict()['processlist']['export']))
|
||||
if 'focus' in config.as_dict()['processlist']:
|
||||
glances_processes.process_focus = config.as_dict()['processlist']['focus']
|
||||
logger.info(
|
||||
"Focus process filter (in glances.conf) is set to: {}".format(config.as_dict()['processlist']['focus'])
|
||||
)
|
||||
if 'disable_stats' in config.as_dict()['processlist']:
|
||||
logger.info(
|
||||
'Followings processes stats wil not be displayed: {}'.format(
|
||||
|
|
@ -727,6 +732,11 @@ class ProcesslistPlugin(GlancesPluginModel):
|
|||
"""Build the header and add it to the ret dict."""
|
||||
sort_style = 'SORT'
|
||||
|
||||
if glances_processes.process_focus and glances_processes.process_focus != []:
|
||||
msg = 'Focus on following processes: ' + ', '.join([i.filter for i in glances_processes.process_focus])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
display_stats = [i for i in self.enable_stats if i not in glances_processes.disable_stats]
|
||||
|
||||
if 'cpu_percent' in display_stats:
|
||||
|
|
|
|||
|
|
@ -137,6 +137,10 @@ class ProgramlistPlugin(ProcesslistPlugin):
|
|||
"""Init the plugin."""
|
||||
super().__init__(args=args, config=config)
|
||||
|
||||
def load(self, args, config):
|
||||
"""Load already done in processlist"""
|
||||
pass
|
||||
|
||||
def get_key(self):
|
||||
"""Return the key of the list."""
|
||||
return 'name'
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ class GlancesProcesses:
|
|||
# Cache is a dict with key=pid and value = dict of cached value
|
||||
self.processlist_cache = {}
|
||||
|
||||
# List of processes to focus on
|
||||
self._filter_focus = GlancesFilterList()
|
||||
|
||||
# List of processes stats to export
|
||||
# Only process matching one of the filter will be exported
|
||||
self._filter_export = GlancesFilterList()
|
||||
|
|
@ -138,6 +141,10 @@ class GlancesProcesses:
|
|||
"""Set args."""
|
||||
self.args = args
|
||||
|
||||
if self.args.process_focus is not None:
|
||||
logger.info(f"Focus process filter (--process-focus option) is set to: {self.args.process_focus}")
|
||||
self.process_focus = self.args.process_focus
|
||||
|
||||
def reset_internal_cache(self):
|
||||
"""Reset the internal cache."""
|
||||
self.cache_timer = Timer(0)
|
||||
|
|
@ -267,7 +274,21 @@ class GlancesProcesses:
|
|||
"""Get the process regular expression compiled."""
|
||||
return self._filter.filter_re
|
||||
|
||||
# Process focus filter
|
||||
# List of Glances filter
|
||||
|
||||
@property
|
||||
def process_focus(self):
|
||||
"""Get the focus process filter."""
|
||||
return self._filter_focus.filter
|
||||
|
||||
@process_focus.setter
|
||||
def process_focus(self, value):
|
||||
"""Set the focus process filter list."""
|
||||
self._filter_focus.filter = value
|
||||
|
||||
# Export filter
|
||||
# List of Glances filter
|
||||
|
||||
@property
|
||||
def export_process_filter(self):
|
||||
|
|
@ -642,6 +663,9 @@ class GlancesProcesses:
|
|||
|
||||
def update_list(self, processlist):
|
||||
"""Return the process list after filtering and transformation (namedtuple to dict)."""
|
||||
if self._filter_focus.filter is not None and self._filter_focus.filter != []:
|
||||
ret = list(filter(lambda p: self._filter_focus.is_filtered(p), processlist))
|
||||
return list_of_namedtuple_to_list_of_dict(ret)
|
||||
if self._filter.filter is None:
|
||||
return list_of_namedtuple_to_list_of_dict(processlist)
|
||||
ret = list(filter(lambda p: self._filter.is_filtered(p), processlist))
|
||||
|
|
|
|||
Loading…
Reference in New Issue