echoss-common 2.0.0__tar.gz → 2.0.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.
- {echoss_common-2.0.0/echoss_common.egg-info → echoss_common-2.0.1}/PKG-INFO +1 -1
- {echoss_common-2.0.0 → echoss_common-2.0.1}/echoss_common/echoss_logger.py +23 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1/echoss_common.egg-info}/PKG-INFO +1 -1
- {echoss_common-2.0.0 → echoss_common-2.0.1}/pyproject.toml +1 -1
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/test_echoss_logger_2x.py +51 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/LICENSE +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/MANIFEST.in +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/README.md +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/echoss_common/__init__.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/echoss_common/config_fileformat.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/echoss_common.egg-info/SOURCES.txt +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/echoss_common.egg-info/dependency_links.txt +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/echoss_common.egg-info/requires.txt +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/echoss_common.egg-info/top_level.txt +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/setup.cfg +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/scripts/check_xml_samples.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/scripts/hold_file.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/scripts/spike_default_log_file.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/scripts/verify_xml_edge_cases.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/test_config_fileformat.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/test_config_fileformat_2x.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/test_echoss_logger_1.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/test_echoss_logger_2.py +0 -0
- {echoss_common-2.0.0 → echoss_common-2.0.1}/tests/test_echoss_logger_json.py +0 -0
|
@@ -52,6 +52,23 @@ class JsonLineFormatter(logging.Formatter):
|
|
|
52
52
|
return json.dumps(payload, ensure_ascii=False, default=str)
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
# Windows cannot rename a file another handle keeps open, so a replaced stream only arises
|
|
56
|
+
# there through copy-truncate, which keeps the inode. Keep the 2.0.0 behaviour on Windows.
|
|
57
|
+
_STALE_STREAM_CHECK = os.name != "nt"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# True when another handler already rotated the file this handler is still writing to.
|
|
61
|
+
def _stream_replaced(handler) -> bool:
|
|
62
|
+
if not _STALE_STREAM_CHECK or handler.stream is None:
|
|
63
|
+
return False
|
|
64
|
+
try:
|
|
65
|
+
mine = os.fstat(handler.stream.fileno())
|
|
66
|
+
disk = os.stat(handler.baseFilename)
|
|
67
|
+
except OSError:
|
|
68
|
+
return False
|
|
69
|
+
return (mine.st_dev, mine.st_ino) != (disk.st_dev, disk.st_ino)
|
|
70
|
+
|
|
71
|
+
|
|
55
72
|
# On Windows, renaming a log file that another handle keeps open fails with PermissionError (WinError 32).
|
|
56
73
|
# Fall back to copy-then-truncate so no record is lost, and always move rolloverAt forward.
|
|
57
74
|
class CopyTruncateRotatingHandler(TimedRotatingFileHandler):
|
|
@@ -73,6 +90,12 @@ class CopyTruncateRotatingHandler(TimedRotatingFileHandler):
|
|
|
73
90
|
f.truncate(0)
|
|
74
91
|
|
|
75
92
|
def doRollover(self):
|
|
93
|
+
if _stream_replaced(self):
|
|
94
|
+
self.stream.close()
|
|
95
|
+
self.stream = None # a failed _open() must leave None so the next emit reopens, as the stdlib does
|
|
96
|
+
self.stream = self._open()
|
|
97
|
+
self.rolloverAt = self.computeRollover(int(time.time()))
|
|
98
|
+
return
|
|
76
99
|
super().doRollover()
|
|
77
100
|
now = int(time.time())
|
|
78
101
|
if self.rolloverAt <= now:
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
# 패키지 메타데이터
|
|
7
7
|
[project]
|
|
8
8
|
name="echoss-common"
|
|
9
|
-
version="2.0.
|
|
9
|
+
version="2.0.1"
|
|
10
10
|
description="echoss AI Bigdata Center 공통 유틸리티 라이브러리 (Logger, JsonLogDict, config_fileformat)"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
license = "Apache-2.0"
|
|
@@ -164,6 +164,57 @@ def test_existing_dated_file_is_kept_and_rollover_moves_forward(tmp_path):
|
|
|
164
164
|
assert handler.rolloverAt > now
|
|
165
165
|
|
|
166
166
|
|
|
167
|
+
# Two loggers, one handler each, one file, rotating every second: writes A1 B1 | A2 B2 | A3 B3
|
|
168
|
+
def _write_through_two_handlers(tmp_path):
|
|
169
|
+
log_file = tmp_path / "e.log"
|
|
170
|
+
loggers = []
|
|
171
|
+
for name in ("two_a", "two_b"):
|
|
172
|
+
handler = echoss_logger.CopyTruncateRotatingHandler(str(log_file), when="S", interval=1, encoding="utf-8")
|
|
173
|
+
handler.setFormatter(logging.Formatter("%(message)s"))
|
|
174
|
+
logger = logging.getLogger(name)
|
|
175
|
+
logger.propagate = False
|
|
176
|
+
logger.setLevel(logging.INFO)
|
|
177
|
+
logger.addHandler(handler)
|
|
178
|
+
loggers.append(logger)
|
|
179
|
+
try:
|
|
180
|
+
for i in (1, 2, 3):
|
|
181
|
+
loggers[0].info("A%d", i)
|
|
182
|
+
loggers[1].info("B%d", i)
|
|
183
|
+
if i < 3:
|
|
184
|
+
time.sleep(1.2)
|
|
185
|
+
finally:
|
|
186
|
+
for logger in loggers:
|
|
187
|
+
for handler in list(logger.handlers):
|
|
188
|
+
logger.removeHandler(handler)
|
|
189
|
+
handler.close()
|
|
190
|
+
rotated = sorted(tmp_path.glob("e.log.*"))
|
|
191
|
+
return log_file.read_text(encoding="utf-8").split(), [p.read_text(encoding="utf-8").split() for p in rotated]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# T-M1
|
|
195
|
+
def test_two_handlers_on_one_file_write_current_file_after_rotation(tmp_path):
|
|
196
|
+
current, rotated = _write_through_two_handlers(tmp_path)
|
|
197
|
+
|
|
198
|
+
assert current == ["A3", "B3"]
|
|
199
|
+
assert rotated == [["A1", "B1"], ["A2", "B2"]]
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# T-M2
|
|
203
|
+
def test_two_handlers_lose_and_duplicate_no_record(tmp_path):
|
|
204
|
+
current, rotated = _write_through_two_handlers(tmp_path)
|
|
205
|
+
|
|
206
|
+
assert sorted(current + [line for lines in rotated for line in lines]) == ["A1", "A2", "A3", "B1", "B2", "B3"]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# T-M3
|
|
210
|
+
def test_stale_stream_check_off_keeps_2_0_0_layout(tmp_path, monkeypatch):
|
|
211
|
+
monkeypatch.setattr(echoss_logger, "_STALE_STREAM_CHECK", False, raising=False)
|
|
212
|
+
current, rotated = _write_through_two_handlers(tmp_path)
|
|
213
|
+
|
|
214
|
+
assert current == ["A3"]
|
|
215
|
+
assert rotated == [["A1", "B1", "B2", "B3"], ["A2"]]
|
|
216
|
+
|
|
217
|
+
|
|
167
218
|
# T-08
|
|
168
219
|
@pytest.mark.parametrize("func, params", [
|
|
169
220
|
(echoss_common.get_app_logger,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|