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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: liblogging
3
- Version: 0.1.16
3
+ Version: 0.2.3
4
4
  Summary: Utilities for logging and sending logs.
5
5
  Home-page: https://github.com/XoriieInpottn/liblogging
6
6
  Author: xi
@@ -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
- for _ in range(3):
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=1)
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: liblogging
3
- Version: 0.1.16
3
+ Version: 0.2.3
4
4
  Summary: Utilities for logging and sending logs.
5
5
  Home-page: https://github.com/XoriieInpottn/liblogging
6
6
  Author: xi
@@ -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
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  liblogging_collector = liblogging.sending.log_collector:main
3
+ liblogging_mongo_collector = liblogging.collector.mongo:main
@@ -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.1.16',
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