userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
logreader.py
1
import
pathlib
2
import
sys
3
import
threading
4
import
typing
5
6
7
class
LogFile
:
8
def
__init__(self, path: pathlib.Path):
9
self.
_path
= path
10
self.
_position
= 0
11
12
@property
13
def
path(self):
14
return
self.
_path
15
16
@property
17
def
position(self):
18
return
self.
_position
19
20
def
filesize(self) -> int:
21
try
:
22
return
self.
_path
.stat().st_size
23
except
FileNotFoundError:
24
return
0
25
26
def
update_position(self):
27
self.
_position
= self.
filesize
()
28
return
self.
_position
29
30
def
readlines(
31
self,
32
eof_handler: typing.Callable[[], bool] |
None
=
None
,
33
limit_position: bool =
False
,
34
):
35
if
limit_position:
36
max_position = self.
filesize
()
37
else
:
38
max_position =
None
39
first_skipped =
False
40
for
line, position
in
_raw_line_reader(
41
self.
_path
,
42
self.
_position
,
43
eof_handler=eof_handler,
44
):
45
self.
_position
= position
46
yield
line
47
if
max_position
is
not
None
and
position >= max_position:
48
break
49
50
51
class
LiveLogHandler
:
52
def
__init__(self, *, delay: float = 0.05):
53
self.
_threads
= {}
54
self.
_exiting
=
False
55
self.
_delay
= delay
56
self.
_condition
= threading.Condition()
57
58
def
register_logfile(self, path: pathlib.Path, *, formatter):
59
if
path
in
self.
_threads
:
60
return
61
thread = threading.Thread(
62
target=self.
_logreader_thread
,
63
args=(path,),
64
kwargs={
'formatter'
: formatter},
65
)
66
self.
_threads
[path] = thread
67
thread.start()
68
69
def
join(self, timeout: float = 10):
70
with
self.
_condition
:
71
self.
_exiting
=
True
72
self.
_condition
.notify_all()
73
for
thread
in
self.
_threads
.values():
74
thread.join(timeout=timeout)
75
76
def
_logreader_thread(self, path: pathlib.Path, formatter):
77
while
not
path.exists():
78
# wait for file to appear
79
if
self.
_eof_handler
():
80
return
81
logfile =
LogFile
(path)
82
for
line
in
logfile.readlines(eof_handler=self.
_eof_handler
):
83
line = line.rstrip(b
'\r\n'
)
84
line = formatter(line)
85
if
line:
86
self.
_write_logline
(line)
87
88
def
_write_logline(self, line: str):
89
print(line, file=sys.stderr)
90
91
def
_eof_handler(self) -> bool:
92
with
self.
_condition
:
93
if
self.
_condition
.wait_for(
94
lambda
: self.
_exiting
, timeout=self.
_delay
95
):
96
return
True
97
return
False
98
99
100
def
_raw_line_reader(
101
path: pathlib.Path,
102
position: int = 0,
103
eof_handler: typing.Callable[[], bool] |
None
=
None
,
104
) -> typing.Iterator[tuple[bytes, int]]:
105
if
not
path.exists():
106
return
107
108
with
path.open(
'rb'
)
as
fp:
109
position = fp.seek(position)
110
partial =
None
111
while
True
:
112
for
line
in
fp:
113
if
partial:
114
line = partial + line
115
partial =
None
116
if
line.endswith(b
'\n'
):
117
position += len(line)
118
yield
line, position
119
else
:
120
partial = line
121
if
not
eof_handler
or
eof_handler():
122
break
en
testsuite
_internal
logreader.py
Generated on
for userver by
Doxygen
1.17.0