LinuxMonitor 1.4.9__tar.gz → 1.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: LinuxMonitor
3
- Version: 1.4.9
3
+ Version: 1.5.1
4
4
  Summary: Get information and warning status of Linux server like service, port, ping, ssl certificate, disk/folder/cpu/ram/swap usage, ip connection, ... (Python and shell library, Linux ONLY)
5
5
  Home-page: https://github.com/QuentinCG/Linux-Monitor-Python-Library
6
6
  Author: Quentin Comte-Gaz
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: LinuxMonitor
3
- Version: 1.4.9
3
+ Version: 1.5.1
4
4
  Summary: Get information and warning status of Linux server like service, port, ping, ssl certificate, disk/folder/cpu/ram/swap usage, ip connection, ... (Python and shell library, Linux ONLY)
5
5
  Home-page: https://github.com/QuentinCG/Linux-Monitor-Python-Library
6
6
  Author: Quentin Comte-Gaz
@@ -71,6 +71,7 @@ def main() -> None:
71
71
  out_msg += msg
72
72
 
73
73
  out_msg += "\n"
74
+ out_msg += monitoring.check_load_average(display_only_if_critical=False) + "\n"
74
75
  out_msg += monitoring.check_cpu_usage(display_only_if_critical=False) + "\n"
75
76
  out_msg += monitoring.check_ram_usage(display_only_if_critical=False) + "\n"
76
77
  out_msg += monitoring.check_swap_usage(display_only_if_critical=False) + "\n"
@@ -33,7 +33,7 @@ __email__ = "quentin@comte-gaz.com"
33
33
  __license__ = "MIT License"
34
34
  __copyright__ = "Copyright Quentin Comte-Gaz (2024)"
35
35
  __python_version__ = "3.+"
36
- __version__ = "1.4.9 (2024/10/27)"
36
+ __version__ = "1.5.1 (2024/10/27)"
37
37
  __status__ = "Usable for any Linux project"
38
38
 
39
39
  import json
@@ -106,6 +106,14 @@ class LinuxMonitor:
106
106
  if not isinstance(basic_config, dict):
107
107
  raise ValueError("The basic configuration must be a dictionary")
108
108
 
109
+ if 'warning_load_average_percent' not in basic_config:
110
+ raise ValueError("The basic configuration must contain the 'warning_load_average_percent' key")
111
+ self.warning_load_average_percent: float = basic_config.get('warning_load_average_percent') # type: ignore
112
+
113
+ if 'critical_load_average_percent' not in basic_config:
114
+ raise ValueError("The basic configuration must contain the 'critical_load_average_percent' key")
115
+ self.critical_load_average_percent: float = basic_config.get('critical_load_average_percent') # type: ignore
116
+
109
117
  if 'warning_cpu_percent' not in basic_config:
110
118
  raise ValueError("The basic configuration must contain the 'warning_cpu_percent' key")
111
119
  self.warning_cpu_percent: float = basic_config.get('warning_cpu_percent') # type: ignore
@@ -587,7 +595,7 @@ class LinuxMonitor:
587
595
  cpu_cores: int = psutil.cpu_count(logical=False)
588
596
  cpu_name: str = self._get_cpu_name()
589
597
 
590
- if cpu_percent > self.critical_cpu_percent:
598
+ if cpu_percent >= self.critical_cpu_percent:
591
599
  out_msg = f"- 🚨 **Critical CPU usage**:\n- **{cpu_percent:.2f}%** used on {cpu_cores} core of {cpu_info:.2f}GHz ({cpu_name})\n⚠️ **Check what is using so much CPU power** ⚠️"
592
600
 
593
601
  # If there is a critical CPU usage, we also display the top 10 processes consuming the most CPU
@@ -595,7 +603,7 @@ class LinuxMonitor:
595
603
 
596
604
  logging.warning(msg=out_msg)
597
605
  elif not display_only_if_critical:
598
- if cpu_percent > self.warning_cpu_percent:
606
+ if cpu_percent >= self.warning_cpu_percent:
599
607
  icon = "⚠️"
600
608
  else:
601
609
  icon = "✅"
@@ -626,7 +634,7 @@ class LinuxMonitor:
626
634
  used_ram: float = ram.used / (2**30)
627
635
  free_ram: float = total_ram - used_ram
628
636
  percent_ram: float = ram.percent
629
- if percent_ram > self.critical_ram_percent:
637
+ if percent_ram >= self.critical_ram_percent:
630
638
  out_msg = f"- 🚨 **Critical RAM usage**:\n- Total: {total_ram:.2f}GB\n- Used: {used_ram:.2f}GB ({percent_ram:.2f}%)\n- Free: {free_ram:.2f}GB\n⚠️ **Check what is using so much RAM** ⚠️"
631
639
 
632
640
  # If there is a critical RAM usage, we also display the top 10 processes consuming the most RAM
@@ -634,7 +642,7 @@ class LinuxMonitor:
634
642
 
635
643
  logging.warning(msg=out_msg)
636
644
  elif not display_only_if_critical:
637
- if percent_ram > self.warning_ram_percent:
645
+ if percent_ram >= self.warning_ram_percent:
638
646
  icon = "⚠️"
639
647
  else:
640
648
  icon = "✅"
@@ -649,6 +657,48 @@ class LinuxMonitor:
649
657
 
650
658
  return out_msg
651
659
 
660
+ def check_load_average(self, display_only_if_critical: bool=False) -> str:
661
+ """
662
+ Check the load average.
663
+
664
+ :param display_only_if_critical: If True, the string result will only be returned if there is an error during execution.
665
+
666
+ :return: A string containing the result message.
667
+ """
668
+ out_msg: str = ""
669
+ try:
670
+ # Number of CPU cores
671
+ num_cores: int = psutil.cpu_count()
672
+
673
+ # Getting load average
674
+ load_avg: Tuple[float] = psutil.getloadavg()
675
+
676
+ # Getting an average of all 3 values
677
+ avg_load_avg: float = 100 * sum(load_avg) / (len(load_avg) * num_cores)
678
+
679
+ if avg_load_avg >= self.critical_load_average_percent:
680
+ out_msg = f"- 🚨 **High load average**: **{avg_load_avg:.2f}%**\n⚠️ **Check what is causing the high load average** ⚠️"
681
+
682
+ # If there is a critical load average, we also display the top 10 processes consuming the most CPU
683
+ out_msg += "\n" + self.get_ordered_processes(get_non_consuming_processes=False, order_by_ram=False, max_processes=10)
684
+
685
+ logging.warning(msg=out_msg)
686
+ elif not display_only_if_critical:
687
+ if avg_load_avg >= self.warning_load_average_percent:
688
+ icon = "⚠️"
689
+ else:
690
+ icon = "✅"
691
+ out_msg = f"- {icon} **{avg_load_avg:.2f}%**"
692
+ logging.info(msg=out_msg)
693
+ except Exception as e:
694
+ out_msg = f"- ⚠️ **Error getting load average**:\n```sh\n{e}\n```"
695
+ logging.exception(msg=out_msg)
696
+
697
+ if out_msg != "":
698
+ out_msg = f"# 📈 Load Average 📈\n{out_msg}"
699
+
700
+ return out_msg
701
+
652
702
  def check_swap_usage(self, display_only_if_critical: bool=False) -> str:
653
703
  """
654
704
  Check the SWAP usage.
@@ -665,11 +715,11 @@ class LinuxMonitor:
665
715
  used_swap: float = swap.used / (2**30)
666
716
  free_swap: float = swap.free / (2**30)
667
717
  percent_swap: float = swap.percent
668
- if percent_swap > self.critical_swap_percent:
718
+ if percent_swap >= self.critical_swap_percent:
669
719
  out_msg = f"- 🚨 **Critical SWAP usage**\n- Total: {total_swap:.2f}GB\n- Used: {used_swap:.2f}GB ({percent_swap:.2f}%)\n- Free: {free_swap:.2f}GB\n⚠️ **Check what is using so much SWAP** ⚠️"
670
720
  logging.warning(msg=out_msg)
671
721
  elif not display_only_if_critical:
672
- if percent_swap > self.warning_swap_percent:
722
+ if percent_swap >= self.warning_swap_percent:
673
723
  icon = "⚠️"
674
724
  else:
675
725
  icon = "✅"
@@ -715,11 +765,11 @@ class LinuxMonitor:
715
765
  max_temp = max(temp.current for temp in cpu_temps) # type: ignore
716
766
 
717
767
  # Vérifier la température par rapport au seuil critique
718
- if max_temp > self.critical_temperature_celsius:
768
+ if max_temp >= self.critical_temperature_celsius:
719
769
  out_msg = f"- 🚨 **Critical CPU temperature**:\n- {max_temp:.2f}°C\n- Critical threshold: {self.critical_temperature_celsius}°C\n⚠️ **Check the CPU cooling system** ⚠️"
720
770
  logging.warning(msg=out_msg)
721
771
  elif not display_only_if_critical:
722
- if max_temp > self.warning_temperature_celsius:
772
+ if max_temp >= self.warning_temperature_celsius:
723
773
  icon = "⚠️"
724
774
  else:
725
775
  icon = "✅"
@@ -2048,6 +2098,7 @@ class LinuxMonitor:
2048
2098
 
2049
2099
  datetime_last_disk_usage_error_displayed: Optional[datetime] = None
2050
2100
  datetime_last_folder_usage_error_displayed: Optional[datetime] = None
2101
+ datetime_last_load_average_error_displayed: Optional[datetime] = None
2051
2102
  datetime_last_cpu_usage_error_displayed: Optional[datetime] = None
2052
2103
  datetime_last_ram_usage_error_displayed: Optional[datetime] = None
2053
2104
  datetime_last_swap_usage_error_displayed: Optional[datetime] = None
@@ -2071,6 +2122,28 @@ class LinuxMonitor:
2071
2122
  logging.info(msg="-----------------------------------------------")
2072
2123
  logging.info(msg="Checking services status and all disk usage, CPU, RAM, Swap, CPU temperature and ping of websites periodically...")
2073
2124
  try:
2125
+ # Load average
2126
+ if is_private:
2127
+ msg = self.check_load_average(display_only_if_critical=True)
2128
+ if msg != "":
2129
+ logging.warning(msg=msg)
2130
+ if datetime_last_load_average_error_displayed is None or ((datetime.now() - datetime_last_load_average_error_displayed).total_seconds() > self.max_duration_seconds_showing_same_error_again_in_scheduled_tasks):
2131
+ if out_msg != "":
2132
+ out_msg += "\n"
2133
+ out_msg += msg
2134
+ datetime_last_load_average_error_displayed = datetime.now()
2135
+ else:
2136
+ logging.warning(msg="Load average critical but already notified less than 12 hours ago, not notifying...")
2137
+ elif datetime_last_load_average_error_displayed is not None:
2138
+ msg = "✅ **Load average returned to normal state**"
2139
+ logging.info(msg=msg)
2140
+ if out_msg != "":
2141
+ out_msg += "\n"
2142
+ out_msg += msg
2143
+ datetime_last_load_average_error_displayed = None
2144
+ else:
2145
+ logging.info(msg="- ✅ Load average is OK.")
2146
+
2074
2147
  # CPU
2075
2148
  if is_private:
2076
2149
  msg = self.check_cpu_usage(display_only_if_critical=True)
@@ -6,7 +6,7 @@ with io.open(file='README.md', mode='r', encoding='utf-8') as readme_file:
6
6
 
7
7
  setup(
8
8
  name="LinuxMonitor",
9
- version="1.4.9",
9
+ version="1.5.1",
10
10
  description="Get information and warning status of Linux server like service, port, ping, ssl certificate, disk/folder/cpu/ram/swap usage, ip connection, ... (Python and shell library, Linux ONLY)",
11
11
  long_description=readme,
12
12
  long_description_content_type="text/markdown",
File without changes
File without changes
File without changes