debugger-help 4.2.1__tar.gz → 4.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: debugger-help
3
- Version: 4.2.1
3
+ Version: 4.2.3
4
4
  Summary: debugger.help VPS Agent — Deep system monitoring for logs, GPU, PM2, Docker, and more
5
5
  Author: debugger.help
6
6
  License: MIT
@@ -1,2 +1,2 @@
1
1
  """debugger.help VPS Agent — Deep system monitoring + ComfyUI workflow management."""
2
- __version__ = "4.2.1"
2
+ __version__ = "4.2.2"
@@ -79,7 +79,7 @@ INGEST_URL = os.environ.get("DEBUGGER_INGEST_URL", "")
79
79
  SOURCE_NAME = os.environ.get("DEBUGGER_SOURCE", "vps-{}".format(socket.gethostname()))
80
80
  PLATFORM = os.environ.get("DEBUGGER_PLATFORM", "Python (VPS)")
81
81
  INTERVAL = int(os.environ.get("DEBUGGER_INTERVAL", "10"))
82
- VERSION = "4.2.0"
82
+ VERSION = "4.2.3"
83
83
 
84
84
  # Derive poll-commands URL from ingest URL
85
85
  POLL_COMMANDS_URL = INGEST_URL.replace("/ingest", "/poll-commands") if INGEST_URL else ""
@@ -226,17 +226,30 @@ class StreamCapture(io.TextIOBase):
226
226
  self._sending = False
227
227
  self._recent_hashes = deque(maxlen=200)
228
228
 
229
+ # Patterns that identify the agent's own log output — never re-send these
230
+ _SELF_PATTERNS = ("debugger-agent", "debugger_agent", "[info]", "[warning]", "[error]",
231
+ "send failed", "send error", "command poll", "executing action",
232
+ "action ", "main loop error")
233
+
229
234
  def write(self, text):
230
235
  self.original.write(text)
231
- if text.strip() and not self._sending:
232
- msg_hash = hash(text.strip()[:200])
236
+ stripped = text.strip()
237
+ if stripped and not self._sending:
238
+ lower = stripped.lower()
239
+ # Filter out the agent's own log lines to prevent feedback loops
240
+ if any(p in lower for p in self._SELF_PATTERNS):
241
+ self.buffer.append(stripped)
242
+ return len(text)
243
+
244
+ # Normalize: strip timestamps/numbers for smarter dedup
245
+ normalized = re.sub(r'\d{2,4}[:\-/\.]\d{2}[:\-/\.]\d{2,4}[\sT]?\d{0,2}:?\d{0,2}:?\d{0,2}\.?\d*|\b\d{4,}\b', '_', stripped[:300])
246
+ msg_hash = hash(normalized)
233
247
  with self.lock:
234
- # Deduplicate: skip if we've seen this exact message recently
248
+ # Deduplicate: skip if we've seen a similar message recently
235
249
  if msg_hash in self._recent_hashes:
236
250
  return len(text)
237
251
  self._recent_hashes.append(msg_hash)
238
- self.buffer.append(text.strip())
239
- lower = text.lower()
252
+ self.buffer.append(stripped)
240
253
  detected_level = self.level
241
254
  if any(kw in lower for kw in [
242
255
  "error", "exception", "traceback", "failed", "critical",
@@ -245,7 +258,7 @@ class StreamCapture(io.TextIOBase):
245
258
  detected_level = "error"
246
259
  elif any(kw in lower for kw in ["warning", "warn", "deprecat"]):
247
260
  detected_level = "warn"
248
- self.pending.append((detected_level, text.strip()[:2000]))
261
+ self.pending.append((detected_level, stripped[:2000]))
249
262
  return len(text)
250
263
 
251
264
  def flush(self):
@@ -713,11 +726,20 @@ def get_firewall_rules():
713
726
  # =============================================================================
714
727
 
715
728
  class LogFileWatcher(threading.Thread):
729
+ # Normalize timestamps/numbers for smarter dedup
730
+ _NORMALIZE_RE = re.compile(r'\d{2,4}[:\-/\.]\d{2}[:\-/\.]\d{2,4}[\sT]?\d{0,2}:?\d{0,2}:?\d{0,2}\.?\d*|\b\d{4,}\b')
731
+
716
732
  def __init__(self, files):
717
733
  super().__init__(daemon=True)
718
734
  self.files = files
719
735
  self.positions = {}
720
736
  self._recent_hashes = deque(maxlen=500)
737
+ self._file_rate = {} # filepath -> (last_send_time, skip_count)
738
+ self._MIN_INTERVAL = 10 # min seconds between sends per file for similar content
739
+
740
+ def _normalize(self, text):
741
+ """Strip timestamps and long numbers so similar lines produce the same hash."""
742
+ return self._NORMALIZE_RE.sub("_", text.strip()[:300])
721
743
 
722
744
  def run(self):
723
745
  for f in self.files:
@@ -737,12 +759,20 @@ class LogFileWatcher(threading.Thread):
737
759
  self.positions[filepath] = fh.tell()
738
760
 
739
761
  if new_lines.strip():
740
- # Deduplicate
741
- msg_hash = hash(new_lines.strip()[:300])
762
+ # Deduplicate with normalized content (ignores timestamps)
763
+ normalized = self._normalize(new_lines)
764
+ msg_hash = hash(normalized)
742
765
  if msg_hash in self._recent_hashes:
743
766
  continue
744
767
  self._recent_hashes.append(msg_hash)
745
768
 
769
+ # Per-file rate limiting — max 1 log per file per 10s
770
+ now = time.time()
771
+ last_send, skip_count = self._file_rate.get(filepath, (0, 0))
772
+ if now - last_send < self._MIN_INTERVAL:
773
+ self._file_rate[filepath] = (last_send, skip_count + 1)
774
+ continue
775
+
746
776
  level = "info"
747
777
  lower = new_lines.lower()
748
778
  if any(kw in lower for kw in ["error", "exception", "traceback", "failed", "critical"]):
@@ -750,13 +780,19 @@ class LogFileWatcher(threading.Thread):
750
780
  elif "warn" in lower:
751
781
  level = "warn"
752
782
 
783
+ msg = new_lines.strip()[:2000]
784
+ if skip_count > 0:
785
+ msg = "[+{} similar skipped] {}".format(skip_count, msg)
786
+
787
+ self._file_rate[filepath] = (now, 0)
788
+
753
789
  send({
754
790
  "type": "log",
755
791
  "source": SOURCE_NAME,
756
792
  "platform": PLATFORM,
757
793
  "version": VERSION,
758
794
  "level": level,
759
- "message": "[file:{}] {}".format(os.path.basename(filepath), new_lines.strip()[:2000]),
795
+ "message": "[file:{}] {}".format(os.path.basename(filepath), msg),
760
796
  "context": {"capturedFrom": "file_watcher", "file": filepath},
761
797
  })
762
798
  elif size < self.positions.get(filepath, 0):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: debugger-help
3
- Version: 4.2.1
3
+ Version: 4.2.3
4
4
  Summary: debugger.help VPS Agent — Deep system monitoring for logs, GPU, PM2, Docker, and more
5
5
  Author: debugger.help
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "debugger-help"
7
- version = "4.2.1"
7
+ version = "4.2.3"
8
8
  description = "debugger.help VPS Agent — Deep system monitoring for logs, GPU, PM2, Docker, and more"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes