Skip to content

Commit

Permalink
23.12.0-DEV
Browse files Browse the repository at this point in the history
  • Loading branch information
spacemanspiff2007 committed Dec 1, 2023
1 parent a91eb84 commit feb5a2f
Show file tree
Hide file tree
Showing 16 changed files with 551 additions and 13 deletions.
17 changes: 14 additions & 3 deletions docs/logging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ but the format should be pretty straight forward.
| That way even if the HABApp configuration is invalid HABApp can still log the errors that have occurred.
| e.g.: ``/HABApp/logs/habapp.log`` or ``c:\HABApp\logs\habapp.log``
Provided loggers
======================================

The ``HABApp.config.logging`` module provides additional loggers which can be used


.. autoclass:: HABApp.config.logging.MidnightRotatingFileHandler

.. autoclass:: HABApp.config.logging.CompressedMidnightRotatingFileHandler


Example
======================================

Expand All @@ -42,7 +53,7 @@ to the file configuration under ``handlers`` in the ``logging.yml``.
...
MyRuleHandler: # <-- This is the name of the handler
class: HABApp.core.lib.handler.MidnightRotatingFileHandler
class: HABApp.config.logging.MidnightRotatingFileHandler
filename: 'c:\HABApp\Logs\MyRule.log'
maxBytes: 10_000_000
backupCount: 3
Expand Down Expand Up @@ -84,7 +95,7 @@ Full Example configuration
# -----------------------------------------------------------------------------------
handlers:
HABApp_default:
class: HABApp.core.lib.handler.MidnightRotatingFileHandler
class: HABApp.config.logging.MidnightRotatingFileHandler
filename: 'HABApp.log'
maxBytes: 10_000_000
backupCount: 3
Expand All @@ -93,7 +104,7 @@ Full Example configuration
level: DEBUG
MyRuleHandler:
class: HABApp.core.lib.handler.MidnightRotatingFileHandler
class: HABApp.config.logging.MidnightRotatingFileHandler
filename: 'c:\HABApp\Logs\MyRule.log' # absolute filename is recommended
maxBytes: 10_000_000
backupCount: 3
Expand Down
6 changes: 3 additions & 3 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Packages required to build the documentation
sphinx == 7.2.5
sphinx-autodoc-typehints == 1.24.0
sphinx_rtd_theme == 1.3.0
sphinx == 7.2.6
sphinx-autodoc-typehints == 1.25.2
sphinx_rtd_theme == 2.0.0
sphinx-exec-code == 0.10
autodoc_pydantic == 2.0.1
sphinx-copybutton == 0.5.2
82 changes: 82 additions & 0 deletions docs/util.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,88 @@ Converts a hsb value to the rgb color space
.. autofunction:: HABApp.util.functions.hsb_to_rgb


Rate limiter
------------------------------
A simple rate limiter implementation which can be used in rules.
The limiter is not rule bound so the same limiter can be used in multiples files.
It also works as expected across rule reloads.

Defining limits
^^^^^^^^^^^^^^^^^^
Limits can either be explicitly added or through a textual description.
If the limit does already exist it will not be added again.
It's possible to explicitly create the limits or through some small textual description with the following syntax:

.. code-block:: text
[count] [per|in|/] [count (optional)] [s|sec|second|m|min|minute|hour|h|day|month|year] [s (optional)]
Whitespaces are ignored and can be added as desired

Examples:

* ``5 per minute``
* ``20 in 15 mins``
* ``300 / hour``


Elastic expiry
^^^^^^^^^^^^^^^^^^

The rate limiter implements a fixed window with elastic expiry.
That means if the limit is hit the interval time will be increased by the expiry time.

For example ``3 per minute``:

* First hit comes ``00:00:00``. Two more hits at ``00:00:59``.
All three pass, intervall goes from ``00:00:00`` - ``00:01:00``.
Another hit comes at ``00:01:01`` an passes. The intervall now goes from ``00:01:01`` - ``00:02:01``.

* First hit comes ``00:00:00``. Two more hits at ``00:00:30``. All three pass.
Another hit comes at ``00:00:45``, which gets rejected and the intervall now goes from ``00:00:00`` - ``00:01:45``.
A rejected hit makes the interval time longer by expiry time. If another hit comes at ``00:01:30`` it
will also get rejected and the intervall now goes from ``00:00:00`` - ``00:02:30``.


Example
^^^^^^^^^^^^^^^^^^

.. exec_code::

from HABApp.util import RateLimiter

# Create or get existing, name is case insensitive
limiter = RateLimiter('MyRateLimiterName')

# define limits, duplicate limits will only be added once
limiter.add_limit(5, 60) # add limits explicitly
limiter.parse_limits('5 per minute').parse_limits('5 in 60s', '5/60seconds') # add limits through text


# Test the limit without increasing the hits
for _ in range(100):
assert limiter.test_allow()

# the limiter will allow 5 calls ...
for _ in range(5):
assert limiter.allow()

# and reject the 6th
assert not limiter.allow()

# It's possible to get statistics about the limiter and the corresponding windows
print(limiter.info())


Documentation
^^^^^^^^^^^^^^^^^^
.. autofunction:: HABApp.util.RateLimiter


.. autoclass:: HABApp.util.rate_limiter.limiter.Limiter
:members:


Statistics
------------------------------

Expand Down
7 changes: 6 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,12 @@ MyOpenhabRule()
```

# Changelog
#### 23.11.1 (2023-11-23)
#### 23.12.0-DEV (2023-XX-XX)
- Added HABApp.util.RateLimiter
- Added CompressedMidnightRotatingFileHandler
- Updated dependencies

#### 23.11.0 (2023-11-23)
- Fix for very small float values (#425)
- Fix for writing to persistence (#424)
- Updated dependencies
Expand Down
2 changes: 1 addition & 1 deletion requirements_setup.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
aiohttp == 3.9.0
aiohttp == 3.9.1
pydantic == 2.5.2
msgspec == 0.18.4
pendulum == 2.1.2
Expand Down
8 changes: 5 additions & 3 deletions run/conf/logging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ handlers:
# There are several Handlers available:
# - logging.handlers.RotatingFileHandler:
# Will rotate when the file reaches a certain size (see python logging documentation for args)
# - HABApp.core.lib.handler.MidnightRotatingFileHandler:
# - HABApp.config.logging.handler.MidnightRotatingFileHandler:
# Will wait until the file reaches a certain size and then will rotate on midnight
# - HABApp.config.logging.handler.CompressedMidnightRotatingFileHandler:
# Same as MidnightRotatingFileHandler but will rotate to a gzipped archive
# - More handlers:
# https://docs.python.org/3/library/logging.handlers.html#rotatingfilehandler

HABApp_default:
class: HABApp.core.lib.handler.MidnightRotatingFileHandler
class: HABApp.config.logging.handler.MidnightRotatingFileHandler
filename: 'HABApp.log'
maxBytes: 1_048_576
backupCount: 3
Expand All @@ -23,7 +25,7 @@ handlers:
level: DEBUG

EventFile:
class: HABApp.core.lib.handler.MidnightRotatingFileHandler
class: HABApp.config.logging.handler.CompressedMidnightRotatingFileHandler
filename: 'events.log'
maxBytes: 1_048_576
backupCount: 3
Expand Down
2 changes: 1 addition & 1 deletion src/HABApp/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
# Development versions contain the DEV-COUNTER postfix:
# - 23.09.0.DEV-1

__version__ = '23.11.0'
__version__ = '23.12.0.DEV-1'
2 changes: 1 addition & 1 deletion src/HABApp/config/logging/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .handler import MidnightRotatingFileHandler
from .handler import MidnightRotatingFileHandler, CompressedMidnightRotatingFileHandler

# isort: split

Expand Down
2 changes: 2 additions & 0 deletions src/HABApp/config/logging/default_logfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def get_default_logfile() -> str:
# Will rotate when the file reaches a certain size (see python logging documentation for args)
# - HABApp.config.logging.MidnightRotatingFileHandler:
# Will wait until the file reaches a certain size and then will rotate on midnight
# - HABApp.config.logging.CompressedMidnightRotatingFileHandler:
# Same as MidnightRotatingFileHandler but will rotate to a gzipped archive
# - More handlers:
# https://docs.python.org/3/library/logging.handlers.html#rotatingfilehandler
Expand Down
26 changes: 26 additions & 0 deletions src/HABApp/config/logging/handler.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import gzip
import shutil
from datetime import date, datetime
from logging.handlers import RotatingFileHandler
from pathlib import Path


class MidnightRotatingFileHandler(RotatingFileHandler):
"""A rotating file handler that checks once after midnight if the configured size has been exceeded and
then rotates the file
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand All @@ -14,3 +20,23 @@ def shouldRollover(self, record):
return 0
self.last_check = date
return super().shouldRollover(record)


class CompressedMidnightRotatingFileHandler(MidnightRotatingFileHandler):
"""Same as ``MidnightRotatingFileHandler`` but rotates the file to a gzipped archive (``.gz``)
"""

def __init__(self, *args, **kwargs):
self.namer = self.compressed_namer
self.rotator = self.compressed_rotator
super().__init__(*args, **kwargs)

def compressed_namer(self, default_name: str) -> str:
return default_name + ".gz"

def compressed_rotator(self, source: str, dest: str):
src = Path(source)
with src.open('rb') as f_in, gzip.open(dest, 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
src.unlink()
1 change: 1 addition & 0 deletions src/HABApp/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .statistics import Statistics
from .listener_groups import EventListenerGroup
from .fade import Fade
from .rate_limiter import RateLimiter

from . import functions
from . import multimode
1 change: 1 addition & 0 deletions src/HABApp/util/rate_limiter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .registry import RateLimiter
Loading

0 comments on commit feb5a2f

Please sign in to comment.