Skip to content

Commit

Permalink
Allow users to override SIGHUP signal
Browse files Browse the repository at this point in the history
  • Loading branch information
okomestudio committed Feb 18, 2024
1 parent 4d791aa commit 9f3f888
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 22 deletions.
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@

# Socket Log Receiver

`socket_log_receiver` is a light-weight socket log receiver. It runs
as a server and aggregates messages from multi-process application via
socket and logs to a single file. Python's `logging` package does not
support logging to a single file from multiple processes. By pointing
`SocketHandler` to `socket_log_receiver`, the multi-process
application can log to a single file.
`socket_log_receiver` is a light-weight socket log receiving server.
It aggregates messages from multi-process application via socket and
logs to a single file. The service is intended to be used with
Python's `logging` package, which does not support multi-process
writes to a single file.


## Installation
Expand All @@ -26,13 +25,15 @@ $ pip install socket-log-receiver

## Basic Usage

The receiver service should be run as a service.
The receiver service should be run as a service:

``` bash
$ python -m socket_log_receiver # as a module
$ log_receiver # as a command-line program
```

By default, the receiver service starts listening on `localhost:9020`.

In the application, use `SocketHandler` to send logs to the receiver
service.

Expand All @@ -46,6 +47,19 @@ logging.root.addHandler(handler) # add the socket handler to the root
This way, the root logger sends logging messages to the receiver service.


### Undefined Signal

The receiver service's configuration is managed by
[resconfig](https://github.com/okomestudio/resconfig). By default, the
dynamic configuration update is trigged by `SIGHUP`. Some systems
might not make this signal available, in which case you could use a
different signal for the trigger using the command-line option, e.g.

``` bash
$ log_receiver --reload-signal SIGUSR2
```


## Development

```bash
Expand All @@ -57,5 +71,5 @@ $ pre-commit install
### Running Tests

``` bash
$ python setup.py tests
$ pytest
```
10 changes: 7 additions & 3 deletions src/socket_log_receiver/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
from .receivers import serve


def main():
def main(argv=None):
p = ArgumentParser()
p.add_argument("--conf")
p.add_argument("--reload-signal", default="SIGHUP")
p.add_argument(
"--reload-signal",
default="SIGHUP",
help="Signal to reload config.",
)
config.add_arguments_to_argparse(p)
args = p.parse_args()
args = p.parse_args(argv)
config.prepare_from_argparse(args, config_file_arg="conf")
config.register("log", configure_logging)
config.register("receiver", serve)
Expand Down
13 changes: 4 additions & 9 deletions src/socket_log_receiver/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging
import logging.handlers
import signal
import platform

from resconfig import ResConfig

Expand All @@ -28,17 +27,13 @@
def handler(*args, **kwargs):
config.load()

if platform.system() != 'Linux':
signal.SIGHUP = 1

def reloader(reload_signal):
# reloader_signal = config.get("reloader.signal", "SIGHUP")
print("FF", reload_signal)
logging.info("HERE")
def reloader(reload_signal: str):
"""Configure config reloader."""
try:
sig = getattr(signal, reload_signal)
except AttributeError:
assert isinstance(sig, signal.Signals)
except (AttributeError, AssertionError):
raise RuntimeError(f"Signal '{ reload_signal }' not recognized")
logging.info("%s will reload config", sig)
signal.signal(sig, handler)
logging.info("%s will reload config", sig)
34 changes: 32 additions & 2 deletions tests/socket_log_receiver/test_main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import sys

import pytest

import socket_log_receiver.__main__ as main
from socket_log_receiver.config import config

Expand All @@ -10,7 +12,35 @@


class TestMain:
def test(self):
def test_default(self):
with patch.object(config, "load") as load:
main.main()
load.assert_called()

load.assert_called()

def test_reload_signal_default(self):
argv = None # argv from command-line

with patch.object(config, "load") as load:
main.main(argv)

load.assert_called()

def test_reload_signal_override(self):
available_signal = "SIGTERM"
argv = ["--reload-signal", available_signal]

with patch.object(config, "load") as load:
main.main(argv)

load.assert_called()

def test_reload_signal_override_with_bad_signal(self):
bad_signal = "SIGFOO"
argv = ["--reload-signal", bad_signal]

with patch.object(config, "load"):
with pytest.raises(RuntimeError) as exc:
main.main(argv)

assert f"Signal '{ bad_signal }' not recognized" in str(exc)

0 comments on commit 9f3f888

Please sign in to comment.