liblogging 0.1.16__tar.gz → 0.2.3__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.
- {liblogging-0.1.16 → liblogging-0.2.3}/PKG-INFO +1 -1
- liblogging-0.2.3/liblogging/collector/__init__.py +1 -0
- liblogging-0.2.3/liblogging/collector/mongo.py +118 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging/logger.py +1 -1
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging/sending/kafka_service.py +10 -3
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging.egg-info/PKG-INFO +1 -1
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging.egg-info/SOURCES.txt +2 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging.egg-info/entry_points.txt +1 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/setup.py +4 -2
- {liblogging-0.1.16 → liblogging-0.2.3}/LICENSE +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/README.md +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging/__init__.py +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging/sending/__init__.py +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging/sending/log_collector.py +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging/util.py +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging.egg-info/dependency_links.txt +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging.egg-info/requires.txt +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging.egg-info/top_level.txt +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/liblogging.egg-info/zip-safe +0 -0
- {liblogging-0.1.16 → liblogging-0.2.3}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
__author__ = "xi"
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from queue import Empty
|
|
9
|
+
from queue import Queue
|
|
10
|
+
from threading import Thread
|
|
11
|
+
from time import time
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from libdata import LazyMongoClient
|
|
15
|
+
from libdata.url import URL
|
|
16
|
+
from libentry import ArgumentParser
|
|
17
|
+
from pydantic import BaseModel
|
|
18
|
+
from pydantic import Field
|
|
19
|
+
|
|
20
|
+
from ..util import split_trace_id
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CollectorConfig(BaseModel):
|
|
24
|
+
mongo_url: str | URL
|
|
25
|
+
max_queue_size: int = Field(default=100_000, ge=1, le=100_000_000)
|
|
26
|
+
batch_size: int = Field(default=100, ge=1, le=10000)
|
|
27
|
+
wait_time: float = 0.2
|
|
28
|
+
max_wait_time: float = 0.5
|
|
29
|
+
|
|
30
|
+
def model_post_init(self, context: Any, /) -> None:
|
|
31
|
+
self.mongo_url = URL.ensure_url(self.mongo_url)
|
|
32
|
+
db, coll = self.mongo_url.get_database_and_table()
|
|
33
|
+
if db is None or coll is None:
|
|
34
|
+
raise ValueError("Both database and collection name should be given in the URL.")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def sending_loop(queue: Queue[dict], config: CollectorConfig):
|
|
38
|
+
client = LazyMongoClient(config.mongo_url)
|
|
39
|
+
|
|
40
|
+
batch_size = config.batch_size
|
|
41
|
+
wait_time = config.wait_time
|
|
42
|
+
max_wait_time = config.max_wait_time
|
|
43
|
+
|
|
44
|
+
while True:
|
|
45
|
+
buffer = [queue.get()]
|
|
46
|
+
|
|
47
|
+
t = time()
|
|
48
|
+
for _ in range(batch_size):
|
|
49
|
+
try:
|
|
50
|
+
buffer.append(queue.get(timeout=wait_time))
|
|
51
|
+
except Empty:
|
|
52
|
+
break
|
|
53
|
+
if time() - t > max_wait_time:
|
|
54
|
+
break
|
|
55
|
+
|
|
56
|
+
if buffer[-1] is not None:
|
|
57
|
+
client.insert_many(buffer)
|
|
58
|
+
else:
|
|
59
|
+
client.insert_many(buffer[:-1])
|
|
60
|
+
break
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def process_message(line):
|
|
64
|
+
message = json.loads(line)
|
|
65
|
+
trace_id = message.get("trace_id", "")
|
|
66
|
+
if trace_id:
|
|
67
|
+
_, chat_dict = split_trace_id(trace_id)
|
|
68
|
+
message.update({
|
|
69
|
+
"uid": chat_dict.get("uid", ""),
|
|
70
|
+
"session_id": chat_dict.get("session_id", ""),
|
|
71
|
+
"turn": chat_dict.get("turn", 0)
|
|
72
|
+
})
|
|
73
|
+
create_time = message.get("create_time")
|
|
74
|
+
create_date = datetime.strptime(create_time, "%Y-%m-%d %H:%M:%S.%f").strftime("%Y-%m-%d")
|
|
75
|
+
message.update({"create_date": create_date})
|
|
76
|
+
return trace_id, message
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def main():
|
|
80
|
+
parser = ArgumentParser()
|
|
81
|
+
parser.add_schema("config", CollectorConfig)
|
|
82
|
+
config: CollectorConfig = parser.parse_args().config
|
|
83
|
+
|
|
84
|
+
queue = Queue(config.max_queue_size)
|
|
85
|
+
|
|
86
|
+
sending_thread = Thread(
|
|
87
|
+
target=sending_loop,
|
|
88
|
+
kwargs=dict(queue=queue, config=config)
|
|
89
|
+
)
|
|
90
|
+
sending_thread.start()
|
|
91
|
+
|
|
92
|
+
while True:
|
|
93
|
+
try:
|
|
94
|
+
line = sys.stdin.readline()
|
|
95
|
+
if not line:
|
|
96
|
+
break
|
|
97
|
+
line = line.strip()
|
|
98
|
+
try:
|
|
99
|
+
trace_id, message = process_message(line)
|
|
100
|
+
queue.put(message)
|
|
101
|
+
print(message)
|
|
102
|
+
except ValueError:
|
|
103
|
+
print(line)
|
|
104
|
+
except EOFError:
|
|
105
|
+
print("End of input reached. Exiting...", file=sys.stderr)
|
|
106
|
+
break
|
|
107
|
+
except Exception as e:
|
|
108
|
+
print(f"Unexpected error: {e}", file=sys.stderr)
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
queue.put(None)
|
|
112
|
+
sending_thread.join()
|
|
113
|
+
print("Mongo collector exited.", file=sys.stderr)
|
|
114
|
+
return 0
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
raise SystemExit(main())
|
|
@@ -167,7 +167,7 @@ class ContextJSONFormatter(Formatter):
|
|
|
167
167
|
except TypeError:
|
|
168
168
|
log_data["message"] = str(log_data["message"])
|
|
169
169
|
output_log = json.dumps(log_data, ensure_ascii=False)
|
|
170
|
-
return output_log
|
|
170
|
+
return f"\n{output_log}\n"
|
|
171
171
|
|
|
172
172
|
def formatTime(self, record):
|
|
173
173
|
"""to match mysql 'datetime(3)' format"""
|
|
@@ -18,6 +18,8 @@ class KafkaService:
|
|
|
18
18
|
def __init__(self, config):
|
|
19
19
|
bootstrap_servers = config["bootstrap_servers"].split(",")
|
|
20
20
|
print(f"kafka config: {config}")
|
|
21
|
+
self.default_send_timeout = config.get("send_timeout_seconds", 2)
|
|
22
|
+
self.send_timeout_increment = config.get("send_timeout_increment", 1)
|
|
21
23
|
self.producer = KafkaProducer(
|
|
22
24
|
bootstrap_servers=bootstrap_servers,
|
|
23
25
|
key_serializer=lambda k: json.dumps(k).encode(),
|
|
@@ -44,22 +46,27 @@ class KafkaService:
|
|
|
44
46
|
message: Dict,
|
|
45
47
|
source: str,
|
|
46
48
|
key: str = None,
|
|
47
|
-
topic: str = None
|
|
49
|
+
topic: str = None,
|
|
50
|
+
timeout: float = None
|
|
48
51
|
):
|
|
49
52
|
if topic is None:
|
|
50
53
|
topic = self.topic
|
|
51
54
|
message["source"] = source
|
|
52
|
-
|
|
55
|
+
current_timeout = timeout or self.default_send_timeout
|
|
56
|
+
for attempt in range(1, 4):
|
|
53
57
|
try:
|
|
54
58
|
future = self.producer.send(
|
|
55
59
|
topic,
|
|
56
60
|
value=message,
|
|
57
61
|
key=key
|
|
58
62
|
)
|
|
59
|
-
future.get(timeout=
|
|
63
|
+
future.get(timeout=current_timeout)
|
|
64
|
+
print(f"Kafka send succeeded on attempt {attempt} (timeout={current_timeout}s)")
|
|
60
65
|
return True
|
|
61
66
|
except Exception as e:
|
|
62
67
|
print(traceback.format_exc())
|
|
68
|
+
current_timeout += self.send_timeout_increment
|
|
69
|
+
print(f"Kafka send failed after retries (final timeout={current_timeout}s)")
|
|
63
70
|
return False
|
|
64
71
|
|
|
65
72
|
|
|
@@ -11,6 +11,8 @@ liblogging.egg-info/entry_points.txt
|
|
|
11
11
|
liblogging.egg-info/requires.txt
|
|
12
12
|
liblogging.egg-info/top_level.txt
|
|
13
13
|
liblogging.egg-info/zip-safe
|
|
14
|
+
liblogging/collector/__init__.py
|
|
15
|
+
liblogging/collector/mongo.py
|
|
14
16
|
liblogging/sending/__init__.py
|
|
15
17
|
liblogging/sending/kafka_service.py
|
|
16
18
|
liblogging/sending/log_collector.py
|
|
@@ -12,13 +12,15 @@ if __name__ == '__main__':
|
|
|
12
12
|
packages=[
|
|
13
13
|
'liblogging',
|
|
14
14
|
'liblogging.sending',
|
|
15
|
+
'liblogging.collector',
|
|
15
16
|
],
|
|
16
17
|
entry_points={
|
|
17
18
|
'console_scripts': [
|
|
18
|
-
'liblogging_collector = liblogging.sending.log_collector:main'
|
|
19
|
+
'liblogging_collector = liblogging.sending.log_collector:main',
|
|
20
|
+
"liblogging_mongo_collector = liblogging.collector.mongo:main"
|
|
19
21
|
]
|
|
20
22
|
},
|
|
21
|
-
version='0.
|
|
23
|
+
version='0.2.3',
|
|
22
24
|
description='Utilities for logging and sending logs.',
|
|
23
25
|
long_description_content_type='text/markdown',
|
|
24
26
|
long_description=long_description,
|
|
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
|