Skip to content

Commit d4bc3d2

Browse files
committed
Allow user to change the logger server port. This fixes the issue #2 where user app did not exit cleanly at previous run
1 parent 4d8c512 commit d4bc3d2

File tree

8 files changed

+107
-16
lines changed

8 files changed

+107
-16
lines changed

README.md

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,13 @@ setup_logging(config_path="", log_path="",
127127
use_multiprocessing=False,
128128
limit_line_length=1000,
129129
analyze_raise_statement=False,
130+
host="",
131+
port=0,
130132
)
131133
```
132134

133135
This function also return a `LogConfig` object.
134-
Except `config_path`, `log_path` and `use_multiprocessing`,
136+
Except `config_path`, `log_path`, `use_multiprocessing`, `host` and `port`,
135137
other parameters are attributes of this object and can be changed on the fly.
136138

137139
Except `config_path`, `log_path`, all other parameters can be defined in `logger_tt` section in the config file
@@ -507,6 +509,18 @@ The content of `log.txt` should be similar to below:
507509
**Note**: Under linux, to use `queueHandler`, you must pass `use_multiprocessing="fork"` to `setup_logging`.<br>
508510
Other options `True`, `spawn`, `forkserver` will use `socketHandler` by default.<br>
509511
This is to prevent you `set_start_method` as `spawn` under linux and thus `queueHandler` won't work.
512+
513+
514+
**Socket Address**: `socketHandler` will use tcp `localhost` and port `9020` by default.
515+
In the rare cases where you run multiple multiprocessing applications with `logger_tt`,
516+
the `Address already in use` error will be raised. In such cases, you have to set the address manually.
517+
518+
```python
519+
setup_logging(host='localhost', port=6789)
520+
```
521+
You can omit the `host` if you use `"localhost"`.
522+
You can also set this in the log config file for each application.
523+
510524

511525
### 8. Temporary disable logging:
512526

@@ -875,10 +889,14 @@ logger_tt:
875889
## 1.7.0
876890
* Fixed:
877891
* multiprocessing: log file rollover fails as child process keep opening the file.
878-
* multiprocessing: if the log path is set by variable with time,
892+
* multiprocessing: if the log path is set by a variable with time,
879893
child process creates a new redundant log path.
880-
* New functionality: Added `StreamHandlerWithBuffer`.
881-
GUI app could use this handler to keep the app responsive while having a tremendous log output.
894+
895+
* New functionality: Added `StreamHandlerWithBuffer`. Buffer the log output by time or by line number.
896+
GUI app could use this handler to keep the app responsive while having a tremendous log output.
897+
898+
* Usability: In multiprocessing logging,
899+
users can set the log server address themselves through `setup_logging` or log config file.
882900

883901
## 1.6.1
884902
* Added `limit_line_length` parameter: log only maximum `n` characters for each traceback line.
@@ -933,7 +951,7 @@ logger_tt:
933951
**Pre-existing loggers:**<br>
934952
Before this version, if you import submodules before importing `logger_tt` and
935953
there are loggers in submodules, these loggers do not inspect exception when you call `logger.exception()`.
936-
That is because there class was different than loggers created after importing `logger_tt`.
954+
That is because there class was different from the loggers created after importing `logger_tt`.
937955
Now all loggers have the same new class regardless the point of importing `logger_tt`.
938956

939957
**setup_logging()**: This function should only be called once.

logger_tt/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def merge_config(from_file: dict, from_func: dict) -> dict:
111111
full_context=0, suppress=None,
112112
suppress_level_below=logging.WARNING, use_multiprocessing=False,
113113
limit_line_length=1000, analyze_raise_statement=False,
114+
host=None, port=None,
114115
)
115116
merged = {}
116117
for key, val in defaults.items():
@@ -149,6 +150,8 @@ def setup_logging(config_path: str = "", log_path: str = "", **logger_tt_config)
149150
queue.Queue to multiprocessing.Queue . This option can only be used here.
150151
:key limit_line_length : int, define how long should one log line be. 0: unlimited; n: n character
151152
:key analyze_raise_statement: bool, should the variables in `raise` exception line be shown or not.
153+
:key host: str, default to 'localhost'. Used in multiprocessing logging
154+
:key port: int, default to logging.handlers.DEFAULT_TCP_LOGGING_PORT. Used in multiprocessing logging
152155
"""
153156
if internal_config.initialized:
154157
logger.warning('Re-initializing logger_tt. "setup_logging()" should only be called one.')

logger_tt/__init__.pyi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ def setup_logging(config_path: str = "", log_path: str = "",
2121
suppress_level_below: int = logging.WARNING,
2222
use_multiprocessing: bool = False,
2323
limit_line_length: int = 1000,
24-
analyze_raise_statement: bool = False) -> LogConfig: ...
24+
analyze_raise_statement: bool = False,
25+
host: str = None,
26+
port: int = None) -> LogConfig: ...

logger_tt/core.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ def __init__(self):
2626
self.q_listeners = []
2727

2828
# tcp port for socket handler
29-
self.host = 'localhost'
30-
self.port = handlers.DEFAULT_TCP_LOGGING_PORT
29+
self._host = 'localhost'
30+
self._port = handlers.DEFAULT_TCP_LOGGING_PORT
3131
self.tcp_server = None
3232

3333
# other settings
@@ -74,6 +74,10 @@ def from_dict(self, odict: dict):
7474
# backup root handler:
7575
self.root_handlers = root_logger.handlers
7676

77+
# host and port for multiprocessing logging
78+
self._host = odict.get('host') or 'localhost'
79+
self._port = odict.get('port') or handlers.DEFAULT_TCP_LOGGING_PORT
80+
7781
# set logging mode accordingly
7882
self._set_mode(odict['use_multiprocessing'])
7983

@@ -137,13 +141,13 @@ def _replace_with_socket_handler(self):
137141
logger.handlers = []
138142

139143
# add socket handler
140-
socket_handler = logging.handlers.SocketHandler(self.host, self.port)
144+
socket_handler = logging.handlers.SocketHandler(self._host, self._port)
141145
atexit.register(socket_handler.close)
142146
logger.addHandler(socket_handler)
143147

144148
# initiate server
145149
if current_process().name == 'MainProcess':
146-
self.tcp_server = LogRecordSocketReceiver(self.host, self.port, all_handlers)
150+
self.tcp_server = LogRecordSocketReceiver(self._host, self._port, all_handlers)
147151
serving = Thread(target=self.tcp_server.serve_until_stopped)
148152
serving.start()
149153
root_logger.debug('Logging server started!')

logger_tt/log_config.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
"use_multiprocessing": false,
6666
"limit_line_length": 1000,
6767
"analyze_raise_statement": false,
68+
"host": "",
69+
"port": 0,
6870
"default_logger_formats": {
6971
"normal": ["%(name)s", "%(filename)s"],
7072
"thread": ["%(message)s", "%(threadName)s %(message)s"],

logger_tt/log_config.yaml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ handlers:
2929
delay: True
3030

3131
buffer_stream_handler:
32-
class: logger_tt.handlers.StreamHandlerWithBuffer,
33-
level: DEBUG,
34-
formatter: brief,
35-
stream: ext://sys.stdout,
36-
buffer_time: 0.5,
37-
buffer_lines: 0,
32+
class: logger_tt.handlers.StreamHandlerWithBuffer
33+
level: DEBUG
34+
formatter: brief
35+
stream: ext://sys.stdout
36+
buffer_time: 0.5
37+
buffer_lines: 0
3838
debug: False
3939

4040
loggers:
@@ -57,6 +57,8 @@ logger_tt:
5757
use_multiprocessing: False
5858
limit_line_length: 1000
5959
analyze_raise_statement: False
60+
host: ""
61+
port: 0
6062
default_logger_formats:
6163
normal: ["%(name)s", "%(filename)s"]
6264
thread: ["%(message)s", "%(threadName)s %(message)s"]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import time
2+
import sys
3+
from random import randint
4+
5+
from multiprocessing import Process
6+
from logger_tt import setup_logging
7+
from logging import getLogger
8+
9+
10+
__author__ = "Duc Tin"
11+
logger = getLogger(__name__)
12+
config = setup_logging(config_path="multiprocessing_change_port.yaml")
13+
14+
15+
def worker(arg):
16+
logger.info(f'child process {arg}: started')
17+
time.sleep(randint(3, 10))
18+
logger.info(f'child process {arg}: stopped')
19+
20+
21+
if __name__ == '__main__':
22+
if len(sys.argv) > 1:
23+
proc_no = int(sys.argv[1])
24+
else:
25+
proc_no = 7
26+
27+
all_processes = []
28+
logger.info('Parent process is ready to spawn child')
29+
logger.info(f'current logging tcp_server is: {config.tcp_server.server_address}')
30+
for i in range(proc_no):
31+
p = Process(target=worker, args=(i,))
32+
all_processes.append(p)
33+
p.daemon = True
34+
p.start()
35+
36+
for p in all_processes:
37+
p.join()
38+
39+
print('__finished__')

tests/test_multiprocessing.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,24 @@ def test_multiprocessing_issue5():
100100
logfile = sub_folders[0] / 'info.log'
101101
assert logfile.exists()
102102
assert 'Test for issue 5' in logfile.read_text()
103+
104+
105+
def test_multiprocessing_port_change():
106+
"""Change the tcp server's port to a user selected one"""
107+
yaml = YAML(typ='safe')
108+
config_file = Path("../logger_tt/log_config.yaml")
109+
log_config = yaml.load(config_file.read_text())
110+
log_config['logger_tt']['use_multiprocessing'] = True
111+
log_config['logger_tt']['port'] = 6789
112+
113+
# write the config out
114+
test_config = Path("multiprocessing_change_port.yaml")
115+
yaml.dump(data=log_config, stream=test_config)
116+
117+
cmd = [sys.executable, "multiprocessing_change_port.py", "3"]
118+
result = run(cmd, stdout=PIPE, universal_newlines=True)
119+
120+
test_config.unlink()
121+
assert '6789' in result.stdout, "Port failed to change"
122+
assert result.stdout.count("stopped") == 3, "Child process failed to log"
123+

0 commit comments

Comments
 (0)