netra-zen 1.0.10__py3-none-any.whl → 1.0.11__py3-none-any.whl
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.
- {netra_zen-1.0.10.dist-info → netra_zen-1.0.11.dist-info}/METADATA +992 -971
- netra_zen-1.0.11.dist-info/RECORD +30 -0
- {netra_zen-1.0.10.dist-info → netra_zen-1.0.11.dist-info}/licenses/LICENSE.md +1 -1
- scripts/__init__.py +1 -1
- scripts/__main__.py +5 -5
- scripts/agent_cli.py +7179 -6948
- scripts/agent_logs.py +327 -327
- scripts/demo_log_collection.py +146 -144
- scripts/embed_release_credentials.py +75 -75
- scripts/test_apex_telemetry_debug.py +221 -0
- scripts/verify_log_transmission.py +140 -140
- zen/__init__.py +7 -7
- zen/__main__.py +11 -11
- zen/telemetry/__init__.py +14 -11
- zen/telemetry/apex_telemetry.py +259 -0
- zen/telemetry/embedded_credentials.py +59 -26
- zen/telemetry/manager.py +249 -249
- zen_orchestrator.py +3058 -3008
- netra_zen-1.0.10.dist-info/RECORD +0 -28
- {netra_zen-1.0.10.dist-info → netra_zen-1.0.11.dist-info}/WHEEL +0 -0
- {netra_zen-1.0.10.dist-info → netra_zen-1.0.11.dist-info}/entry_points.txt +0 -0
- {netra_zen-1.0.10.dist-info → netra_zen-1.0.11.dist-info}/top_level.txt +0 -0
scripts/demo_log_collection.py
CHANGED
@@ -1,144 +1,146 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
Demonstration of log collection from .claude/Projects
|
4
|
-
|
5
|
-
This script shows how the zen --apex --send-logs functionality works
|
6
|
-
"""
|
7
|
-
import sys
|
8
|
-
from pathlib import Path
|
9
|
-
import json
|
10
|
-
|
11
|
-
# Add parent to path for imports
|
12
|
-
sys.path.insert(0, str(Path(__file__).parent.parent))
|
13
|
-
|
14
|
-
from scripts.agent_logs import collect_recent_logs
|
15
|
-
|
16
|
-
|
17
|
-
def demo_log_collection():
|
18
|
-
"""Demonstrate log collection with various scenarios"""
|
19
|
-
|
20
|
-
print("=" * 60)
|
21
|
-
print("Zen Apex Log Collection Demo")
|
22
|
-
print("=" * 60)
|
23
|
-
print()
|
24
|
-
|
25
|
-
# Check if .claude/Projects exists
|
26
|
-
claude_path = Path.home() / ".claude" / "Projects"
|
27
|
-
|
28
|
-
if not claude_path.exists():
|
29
|
-
print("❌ .claude/Projects does not exist")
|
30
|
-
print(f" Expected location: {claude_path}")
|
31
|
-
print()
|
32
|
-
print("Creating test directory...")
|
33
|
-
claude_path.mkdir(parents=True, exist_ok=True)
|
34
|
-
test_project = claude_path / "demo-project"
|
35
|
-
test_project.mkdir(exist_ok=True)
|
36
|
-
|
37
|
-
# Create sample log
|
38
|
-
sample_log = {
|
39
|
-
"type": "demo_event",
|
40
|
-
"timestamp": "2025-01-08T12:00:00",
|
41
|
-
"message": "This is a demo log entry",
|
42
|
-
"data": {"key": "value"}
|
43
|
-
}
|
44
|
-
(test_project / "demo-session.jsonl").write_text(json.dumps(sample_log) + "\n")
|
45
|
-
print(f"✅ Created demo project at {test_project}")
|
46
|
-
print()
|
47
|
-
|
48
|
-
# Scenario 1: Collect with defaults
|
49
|
-
print("Scenario 1: Collect logs with defaults (limit=
|
50
|
-
print("-" * 60)
|
51
|
-
logs = collect_recent_logs(limit=
|
52
|
-
|
53
|
-
if logs:
|
54
|
-
print(f"✅ Collected {len(logs)} log entries")
|
55
|
-
print(f" Total entries: {len(logs)}")
|
56
|
-
print()
|
57
|
-
print(" Sample entry (first):")
|
58
|
-
print(f" {json.dumps(logs[0], indent=4)}")
|
59
|
-
else:
|
60
|
-
print("⚠️ No logs found")
|
61
|
-
print(" Tip: Run Claude Code with some commands to generate logs")
|
62
|
-
print()
|
63
|
-
|
64
|
-
# Scenario 2: List available projects
|
65
|
-
print("Scenario 2: List available projects")
|
66
|
-
print("-" * 60)
|
67
|
-
if claude_path.exists():
|
68
|
-
projects = [p for p in claude_path.iterdir() if p.is_dir()]
|
69
|
-
if projects:
|
70
|
-
print(f"Found {len(projects)} project(s):")
|
71
|
-
for proj in sorted(projects, key=lambda p: p.stat().st_mtime, reverse=True):
|
72
|
-
jsonl_count = len(list(proj.glob("*.jsonl")))
|
73
|
-
mtime = proj.stat().st_mtime
|
74
|
-
from datetime import datetime
|
75
|
-
mtime_str = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M:%S")
|
76
|
-
marker = " ← most recent" if proj == projects[0] else ""
|
77
|
-
print(f" • {proj.name}: {jsonl_count} .jsonl files (modified: {mtime_str}){marker}")
|
78
|
-
else:
|
79
|
-
print(" No projects found")
|
80
|
-
print()
|
81
|
-
|
82
|
-
# Scenario 3: Collect from specific project
|
83
|
-
if claude_path.exists():
|
84
|
-
projects = [p for p in claude_path.iterdir() if p.is_dir()]
|
85
|
-
if projects:
|
86
|
-
specific_project = projects[0].name
|
87
|
-
print(f"Scenario 3: Collect from specific project '{specific_project}'")
|
88
|
-
print("-" * 60)
|
89
|
-
logs = collect_recent_logs(limit=3, project_name=specific_project)
|
90
|
-
if logs:
|
91
|
-
print(f"✅ Collected {len(logs)} entries from '{specific_project}'")
|
92
|
-
print(f" Entry types: {[log.get('type', 'unknown') for log in logs[:3]]}")
|
93
|
-
else:
|
94
|
-
print(f"⚠️ No logs in '{specific_project}'")
|
95
|
-
print()
|
96
|
-
|
97
|
-
# Scenario 4: Show what would be sent with --send-logs
|
98
|
-
print("Scenario 4: What gets sent with 'zen --apex --send-logs --message \"..\"'")
|
99
|
-
print("-" * 60)
|
100
|
-
logs = collect_recent_logs(limit=
|
101
|
-
if logs:
|
102
|
-
payload_preview = {
|
103
|
-
"type": "user_message",
|
104
|
-
"payload": {
|
105
|
-
"content": "your message here",
|
106
|
-
"run_id": "cli_20250108_120000_12345",
|
107
|
-
"thread_id": "cli_thread_abc123def456",
|
108
|
-
"timestamp": "2025-01-08T12:00:00",
|
109
|
-
"jsonl_logs": logs # This is what gets attached
|
110
|
-
}
|
111
|
-
}
|
112
|
-
print("Payload structure:")
|
113
|
-
print(json.dumps(payload_preview, indent=2)[:500] + "...")
|
114
|
-
print()
|
115
|
-
print(f"✅ {len(logs)} log entries would be attached to the message")
|
116
|
-
else:
|
117
|
-
print("⚠️ No logs would be attached (none found)")
|
118
|
-
print()
|
119
|
-
|
120
|
-
# Summary
|
121
|
-
print("=" * 60)
|
122
|
-
print("Summary")
|
123
|
-
print("=" * 60)
|
124
|
-
print()
|
125
|
-
print("To use log forwarding with zen --apex:")
|
126
|
-
print()
|
127
|
-
print(" # Basic usage (
|
128
|
-
print(" zen --apex --send-logs --message \"analyze these sessions\"")
|
129
|
-
print()
|
130
|
-
print(" # Custom number of logs")
|
131
|
-
print(" zen --apex --send-logs --
|
132
|
-
print()
|
133
|
-
print("
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
print(" zen --apex --send-logs --logs-project
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Demonstration of log collection from .claude/Projects
|
4
|
+
|
5
|
+
This script shows how the zen --apex --send-logs functionality works
|
6
|
+
"""
|
7
|
+
import sys
|
8
|
+
from pathlib import Path
|
9
|
+
import json
|
10
|
+
|
11
|
+
# Add parent to path for imports
|
12
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
13
|
+
|
14
|
+
from scripts.agent_logs import collect_recent_logs
|
15
|
+
|
16
|
+
|
17
|
+
def demo_log_collection():
|
18
|
+
"""Demonstrate log collection with various scenarios"""
|
19
|
+
|
20
|
+
print("=" * 60)
|
21
|
+
print("Zen Apex Log Collection Demo")
|
22
|
+
print("=" * 60)
|
23
|
+
print()
|
24
|
+
|
25
|
+
# Check if .claude/Projects exists
|
26
|
+
claude_path = Path.home() / ".claude" / "Projects"
|
27
|
+
|
28
|
+
if not claude_path.exists():
|
29
|
+
print("❌ .claude/Projects does not exist")
|
30
|
+
print(f" Expected location: {claude_path}")
|
31
|
+
print()
|
32
|
+
print("Creating test directory...")
|
33
|
+
claude_path.mkdir(parents=True, exist_ok=True)
|
34
|
+
test_project = claude_path / "demo-project"
|
35
|
+
test_project.mkdir(exist_ok=True)
|
36
|
+
|
37
|
+
# Create sample log
|
38
|
+
sample_log = {
|
39
|
+
"type": "demo_event",
|
40
|
+
"timestamp": "2025-01-08T12:00:00",
|
41
|
+
"message": "This is a demo log entry",
|
42
|
+
"data": {"key": "value"}
|
43
|
+
}
|
44
|
+
(test_project / "demo-session.jsonl").write_text(json.dumps(sample_log) + "\n")
|
45
|
+
print(f"✅ Created demo project at {test_project}")
|
46
|
+
print()
|
47
|
+
|
48
|
+
# Scenario 1: Collect with defaults
|
49
|
+
print("Scenario 1: Collect logs with defaults (limit=1, auto-detect project)")
|
50
|
+
print("-" * 60)
|
51
|
+
logs = collect_recent_logs(limit=1)
|
52
|
+
|
53
|
+
if logs:
|
54
|
+
print(f"✅ Collected {len(logs)} log entries")
|
55
|
+
print(f" Total entries: {len(logs)}")
|
56
|
+
print()
|
57
|
+
print(" Sample entry (first):")
|
58
|
+
print(f" {json.dumps(logs[0], indent=4)}")
|
59
|
+
else:
|
60
|
+
print("⚠️ No logs found")
|
61
|
+
print(" Tip: Run Claude Code with some commands to generate logs")
|
62
|
+
print()
|
63
|
+
|
64
|
+
# Scenario 2: List available projects
|
65
|
+
print("Scenario 2: List available projects")
|
66
|
+
print("-" * 60)
|
67
|
+
if claude_path.exists():
|
68
|
+
projects = [p for p in claude_path.iterdir() if p.is_dir()]
|
69
|
+
if projects:
|
70
|
+
print(f"Found {len(projects)} project(s):")
|
71
|
+
for proj in sorted(projects, key=lambda p: p.stat().st_mtime, reverse=True):
|
72
|
+
jsonl_count = len(list(proj.glob("*.jsonl")))
|
73
|
+
mtime = proj.stat().st_mtime
|
74
|
+
from datetime import datetime
|
75
|
+
mtime_str = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M:%S")
|
76
|
+
marker = " ← most recent" if proj == projects[0] else ""
|
77
|
+
print(f" • {proj.name}: {jsonl_count} .jsonl files (modified: {mtime_str}){marker}")
|
78
|
+
else:
|
79
|
+
print(" No projects found")
|
80
|
+
print()
|
81
|
+
|
82
|
+
# Scenario 3: Collect from specific project
|
83
|
+
if claude_path.exists():
|
84
|
+
projects = [p for p in claude_path.iterdir() if p.is_dir()]
|
85
|
+
if projects:
|
86
|
+
specific_project = projects[0].name
|
87
|
+
print(f"Scenario 3: Collect from specific project '{specific_project}'")
|
88
|
+
print("-" * 60)
|
89
|
+
logs = collect_recent_logs(limit=3, project_name=specific_project)
|
90
|
+
if logs:
|
91
|
+
print(f"✅ Collected {len(logs)} entries from '{specific_project}'")
|
92
|
+
print(f" Entry types: {[log.get('type', 'unknown') for log in logs[:3]]}")
|
93
|
+
else:
|
94
|
+
print(f"⚠️ No logs in '{specific_project}'")
|
95
|
+
print()
|
96
|
+
|
97
|
+
# Scenario 4: Show what would be sent with --send-logs
|
98
|
+
print("Scenario 4: What gets sent with 'zen --apex --send-logs --message \"..\"'")
|
99
|
+
print("-" * 60)
|
100
|
+
logs = collect_recent_logs(limit=1)
|
101
|
+
if logs:
|
102
|
+
payload_preview = {
|
103
|
+
"type": "user_message",
|
104
|
+
"payload": {
|
105
|
+
"content": "your message here",
|
106
|
+
"run_id": "cli_20250108_120000_12345",
|
107
|
+
"thread_id": "cli_thread_abc123def456",
|
108
|
+
"timestamp": "2025-01-08T12:00:00",
|
109
|
+
"jsonl_logs": logs # This is what gets attached
|
110
|
+
}
|
111
|
+
}
|
112
|
+
print("Payload structure:")
|
113
|
+
print(json.dumps(payload_preview, indent=2)[:500] + "...")
|
114
|
+
print()
|
115
|
+
print(f"✅ {len(logs)} log entries would be attached to the message")
|
116
|
+
else:
|
117
|
+
print("⚠️ No logs would be attached (none found)")
|
118
|
+
print()
|
119
|
+
|
120
|
+
# Summary
|
121
|
+
print("=" * 60)
|
122
|
+
print("Summary")
|
123
|
+
print("=" * 60)
|
124
|
+
print()
|
125
|
+
print("To use log forwarding with zen --apex:")
|
126
|
+
print()
|
127
|
+
print(" # Basic usage (default: 1 log file for best results)")
|
128
|
+
print(" zen --apex --send-logs --message \"analyze these sessions\"")
|
129
|
+
print()
|
130
|
+
print(" # Custom number of logs (default: 1 for best results)")
|
131
|
+
print(" zen --apex --send-logs --message \"review recent log\" (analyzes 1 file)")
|
132
|
+
print(" # Multiple files (use with caution - keep payload under 1MB)")
|
133
|
+
print(" zen --apex --send-logs --logs-count 2 --message \"review last 2\"")
|
134
|
+
print()
|
135
|
+
print(" # Specific project")
|
136
|
+
if claude_path.exists() and list(claude_path.iterdir()):
|
137
|
+
first_project = list(p for p in claude_path.iterdir() if p.is_dir())[0].name
|
138
|
+
print(f" zen --apex --send-logs --logs-project {first_project} --message \"...\"")
|
139
|
+
else:
|
140
|
+
print(" zen --apex --send-logs --logs-project PROJECT_NAME --message \"...\"")
|
141
|
+
print()
|
142
|
+
print("=" * 60)
|
143
|
+
|
144
|
+
|
145
|
+
if __name__ == "__main__":
|
146
|
+
demo_log_collection()
|
@@ -1,75 +1,75 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""Embed telemetry credentials for release builds.
|
3
|
-
|
4
|
-
Usage:
|
5
|
-
COMMUNITY_CREDENTIALS="<base64-json>" python scripts/embed_release_credentials.py
|
6
|
-
"""
|
7
|
-
|
8
|
-
from __future__ import annotations
|
9
|
-
|
10
|
-
import base64
|
11
|
-
import json
|
12
|
-
import os
|
13
|
-
import sys
|
14
|
-
from pathlib import Path
|
15
|
-
|
16
|
-
|
17
|
-
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
18
|
-
TARGET_FILE = PROJECT_ROOT / "zen" / "telemetry" / "embedded_credentials.py"
|
19
|
-
|
20
|
-
|
21
|
-
def main() -> int:
|
22
|
-
encoded = os.getenv("COMMUNITY_CREDENTIALS", "").strip()
|
23
|
-
if not encoded:
|
24
|
-
print(
|
25
|
-
"COMMUNITY_CREDENTIALS is not set. Set the base64-encoded "
|
26
|
-
"service-account JSON before running this script.",
|
27
|
-
file=sys.stderr,
|
28
|
-
)
|
29
|
-
return 1
|
30
|
-
|
31
|
-
try:
|
32
|
-
decoded = base64.b64decode(encoded)
|
33
|
-
info = json.loads(decoded)
|
34
|
-
except Exception as exc: # pragma: no cover - defensive guard
|
35
|
-
print(f"Failed to decode telemetry credentials: {exc}", file=sys.stderr)
|
36
|
-
return 2
|
37
|
-
|
38
|
-
project_id = info.get("project_id", "netra-telemetry-public")
|
39
|
-
encoded_literal = repr(encoded)
|
40
|
-
|
41
|
-
generated = f'''"""Embedded telemetry credentials. AUTO-GENERATED - DO NOT COMMIT."""
|
42
|
-
|
43
|
-
import base64
|
44
|
-
import json
|
45
|
-
from google.oauth2 import service_account
|
46
|
-
|
47
|
-
_EMBEDDED_CREDENTIALS_B64 = {encoded_literal}
|
48
|
-
_CREDENTIALS_DICT = json.loads(
|
49
|
-
base64.b64decode(_EMBEDDED_CREDENTIALS_B64.encode("utf-8"))
|
50
|
-
)
|
51
|
-
|
52
|
-
|
53
|
-
def get_embedded_credentials():
|
54
|
-
"""Return service account credentials."""
|
55
|
-
try:
|
56
|
-
return service_account.Credentials.from_service_account_info(
|
57
|
-
_CREDENTIALS_DICT,
|
58
|
-
scopes=["https://www.googleapis.com/auth/trace.append"],
|
59
|
-
)
|
60
|
-
except Exception:
|
61
|
-
return None
|
62
|
-
|
63
|
-
|
64
|
-
def get_project_id() -> str:
|
65
|
-
"""Return GCP project ID."""
|
66
|
-
return _CREDENTIALS_DICT.get("project_id", {project_id!r})
|
67
|
-
'''
|
68
|
-
|
69
|
-
TARGET_FILE.write_text(generated)
|
70
|
-
print(f"Embedded release credentials written to {TARGET_FILE.relative_to(PROJECT_ROOT)}")
|
71
|
-
return 0
|
72
|
-
|
73
|
-
|
74
|
-
if __name__ == "__main__":
|
75
|
-
raise SystemExit(main())
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""Embed telemetry credentials for release builds.
|
3
|
+
|
4
|
+
Usage:
|
5
|
+
COMMUNITY_CREDENTIALS="<base64-json>" python scripts/embed_release_credentials.py
|
6
|
+
"""
|
7
|
+
|
8
|
+
from __future__ import annotations
|
9
|
+
|
10
|
+
import base64
|
11
|
+
import json
|
12
|
+
import os
|
13
|
+
import sys
|
14
|
+
from pathlib import Path
|
15
|
+
|
16
|
+
|
17
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
18
|
+
TARGET_FILE = PROJECT_ROOT / "zen" / "telemetry" / "embedded_credentials.py"
|
19
|
+
|
20
|
+
|
21
|
+
def main() -> int:
|
22
|
+
encoded = os.getenv("COMMUNITY_CREDENTIALS", "").strip()
|
23
|
+
if not encoded:
|
24
|
+
print(
|
25
|
+
"COMMUNITY_CREDENTIALS is not set. Set the base64-encoded "
|
26
|
+
"service-account JSON before running this script.",
|
27
|
+
file=sys.stderr,
|
28
|
+
)
|
29
|
+
return 1
|
30
|
+
|
31
|
+
try:
|
32
|
+
decoded = base64.b64decode(encoded)
|
33
|
+
info = json.loads(decoded)
|
34
|
+
except Exception as exc: # pragma: no cover - defensive guard
|
35
|
+
print(f"Failed to decode telemetry credentials: {exc}", file=sys.stderr)
|
36
|
+
return 2
|
37
|
+
|
38
|
+
project_id = info.get("project_id", "netra-telemetry-public")
|
39
|
+
encoded_literal = repr(encoded)
|
40
|
+
|
41
|
+
generated = f'''"""Embedded telemetry credentials. AUTO-GENERATED - DO NOT COMMIT."""
|
42
|
+
|
43
|
+
import base64
|
44
|
+
import json
|
45
|
+
from google.oauth2 import service_account
|
46
|
+
|
47
|
+
_EMBEDDED_CREDENTIALS_B64 = {encoded_literal}
|
48
|
+
_CREDENTIALS_DICT = json.loads(
|
49
|
+
base64.b64decode(_EMBEDDED_CREDENTIALS_B64.encode("utf-8"))
|
50
|
+
)
|
51
|
+
|
52
|
+
|
53
|
+
def get_embedded_credentials():
|
54
|
+
"""Return service account credentials."""
|
55
|
+
try:
|
56
|
+
return service_account.Credentials.from_service_account_info(
|
57
|
+
_CREDENTIALS_DICT,
|
58
|
+
scopes=["https://www.googleapis.com/auth/trace.append"],
|
59
|
+
)
|
60
|
+
except Exception:
|
61
|
+
return None
|
62
|
+
|
63
|
+
|
64
|
+
def get_project_id() -> str:
|
65
|
+
"""Return GCP project ID."""
|
66
|
+
return _CREDENTIALS_DICT.get("project_id", {project_id!r})
|
67
|
+
'''
|
68
|
+
|
69
|
+
TARGET_FILE.write_text(generated)
|
70
|
+
print(f"Embedded release credentials written to {TARGET_FILE.relative_to(PROJECT_ROOT)}")
|
71
|
+
return 0
|
72
|
+
|
73
|
+
|
74
|
+
if __name__ == "__main__":
|
75
|
+
raise SystemExit(main())
|
@@ -0,0 +1,221 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Debug script to test apex telemetry and identify why spans aren't appearing in Cloud Trace.
|
4
|
+
Run this after setting COMMUNITY_CREDENTIALS to diagnose the issue.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import sys
|
8
|
+
import logging
|
9
|
+
from pathlib import Path
|
10
|
+
|
11
|
+
# Add project to path
|
12
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
13
|
+
|
14
|
+
# Enable debug logging
|
15
|
+
logging.basicConfig(
|
16
|
+
level=logging.DEBUG,
|
17
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
18
|
+
)
|
19
|
+
|
20
|
+
def test_telemetry_manager():
|
21
|
+
"""Test that telemetry manager is properly initialized."""
|
22
|
+
print("=" * 80)
|
23
|
+
print("TEST 1: Telemetry Manager Initialization")
|
24
|
+
print("=" * 80)
|
25
|
+
|
26
|
+
from zen.telemetry import telemetry_manager
|
27
|
+
|
28
|
+
print(f"Telemetry manager type: {type(telemetry_manager)}")
|
29
|
+
print(f"Is enabled: {telemetry_manager.is_enabled()}")
|
30
|
+
|
31
|
+
if hasattr(telemetry_manager, '_tracer'):
|
32
|
+
print(f"Has _tracer: {telemetry_manager._tracer is not None}")
|
33
|
+
else:
|
34
|
+
print("❌ No _tracer attribute")
|
35
|
+
|
36
|
+
if hasattr(telemetry_manager, '_provider'):
|
37
|
+
print(f"Has _provider: {telemetry_manager._provider is not None}")
|
38
|
+
else:
|
39
|
+
print("❌ No _provider attribute")
|
40
|
+
|
41
|
+
print()
|
42
|
+
return telemetry_manager.is_enabled()
|
43
|
+
|
44
|
+
|
45
|
+
def test_apex_wrapper_import():
|
46
|
+
"""Test that apex wrapper can be imported."""
|
47
|
+
print("=" * 80)
|
48
|
+
print("TEST 2: Apex Telemetry Wrapper Import")
|
49
|
+
print("=" * 80)
|
50
|
+
|
51
|
+
try:
|
52
|
+
from zen.telemetry.apex_telemetry import ApexTelemetryWrapper
|
53
|
+
print("✅ ApexTelemetryWrapper imported successfully")
|
54
|
+
|
55
|
+
wrapper = ApexTelemetryWrapper()
|
56
|
+
print(f"✅ Wrapper instance created: {type(wrapper)}")
|
57
|
+
print()
|
58
|
+
return True
|
59
|
+
except Exception as e:
|
60
|
+
print(f"❌ Failed to import ApexTelemetryWrapper: {e}")
|
61
|
+
import traceback
|
62
|
+
traceback.print_exc()
|
63
|
+
print()
|
64
|
+
return False
|
65
|
+
|
66
|
+
|
67
|
+
def test_manual_span_emission():
|
68
|
+
"""Test manual span emission using telemetry manager."""
|
69
|
+
print("=" * 80)
|
70
|
+
print("TEST 3: Manual Span Emission")
|
71
|
+
print("=" * 80)
|
72
|
+
|
73
|
+
from zen.telemetry import telemetry_manager
|
74
|
+
|
75
|
+
if not telemetry_manager.is_enabled():
|
76
|
+
print("⚠️ Telemetry is not enabled - skipping test")
|
77
|
+
print()
|
78
|
+
return False
|
79
|
+
|
80
|
+
try:
|
81
|
+
from opentelemetry.trace import SpanKind
|
82
|
+
|
83
|
+
print("Creating test span...")
|
84
|
+
with telemetry_manager._tracer.start_as_current_span(
|
85
|
+
"test.apex.span", kind=SpanKind.INTERNAL
|
86
|
+
) as span:
|
87
|
+
span.set_attribute("test.type", "manual")
|
88
|
+
span.set_attribute("test.value", 123)
|
89
|
+
print("✅ Span created and attributes set")
|
90
|
+
|
91
|
+
print("Flushing provider...")
|
92
|
+
if hasattr(telemetry_manager, '_provider') and telemetry_manager._provider:
|
93
|
+
telemetry_manager._provider.force_flush(timeout_millis=5000)
|
94
|
+
print("✅ Provider flushed")
|
95
|
+
|
96
|
+
print("✅ Manual span test completed")
|
97
|
+
print(" Check Cloud Trace for span: 'test.apex.span'")
|
98
|
+
print()
|
99
|
+
return True
|
100
|
+
|
101
|
+
except Exception as e:
|
102
|
+
print(f"❌ Manual span emission failed: {e}")
|
103
|
+
import traceback
|
104
|
+
traceback.print_exc()
|
105
|
+
print()
|
106
|
+
return False
|
107
|
+
|
108
|
+
|
109
|
+
def test_apex_wrapper_emission():
|
110
|
+
"""Test apex wrapper span emission."""
|
111
|
+
print("=" * 80)
|
112
|
+
print("TEST 4: Apex Wrapper Span Emission")
|
113
|
+
print("=" * 80)
|
114
|
+
|
115
|
+
from zen.telemetry.apex_telemetry import ApexTelemetryWrapper
|
116
|
+
|
117
|
+
wrapper = ApexTelemetryWrapper()
|
118
|
+
wrapper.start_time = 1000.0
|
119
|
+
wrapper.end_time = 1010.0
|
120
|
+
wrapper.exit_code = 0
|
121
|
+
wrapper.message = "test apex telemetry debug"
|
122
|
+
wrapper.env = "staging"
|
123
|
+
wrapper.stdout = ""
|
124
|
+
wrapper.stderr = ""
|
125
|
+
|
126
|
+
print("Emitting telemetry with wrapper...")
|
127
|
+
try:
|
128
|
+
wrapper._emit_telemetry()
|
129
|
+
print("✅ Wrapper._emit_telemetry() completed")
|
130
|
+
print(" Check Cloud Trace for span: 'apex.instance'")
|
131
|
+
print()
|
132
|
+
return True
|
133
|
+
except Exception as e:
|
134
|
+
print(f"❌ Wrapper emission failed: {e}")
|
135
|
+
import traceback
|
136
|
+
traceback.print_exc()
|
137
|
+
print()
|
138
|
+
return False
|
139
|
+
|
140
|
+
|
141
|
+
def test_credentials():
|
142
|
+
"""Test credential loading."""
|
143
|
+
print("=" * 80)
|
144
|
+
print("TEST 5: Credential Loading")
|
145
|
+
print("=" * 80)
|
146
|
+
|
147
|
+
from zen.telemetry import get_embedded_credentials, get_project_id
|
148
|
+
|
149
|
+
creds = get_embedded_credentials()
|
150
|
+
if creds:
|
151
|
+
print(f"✅ Credentials loaded")
|
152
|
+
project_id = get_project_id()
|
153
|
+
print(f"✅ Project ID: {project_id}")
|
154
|
+
print()
|
155
|
+
return True
|
156
|
+
else:
|
157
|
+
print("❌ No credentials found")
|
158
|
+
print(" Set COMMUNITY_CREDENTIALS environment variable")
|
159
|
+
print()
|
160
|
+
return False
|
161
|
+
|
162
|
+
|
163
|
+
def main():
|
164
|
+
"""Run all diagnostic tests."""
|
165
|
+
print("\n🔍 APEX TELEMETRY DEBUG TESTS")
|
166
|
+
print("=" * 80)
|
167
|
+
|
168
|
+
results = {}
|
169
|
+
|
170
|
+
results['credentials'] = test_credentials()
|
171
|
+
results['telemetry_manager'] = test_telemetry_manager()
|
172
|
+
results['apex_wrapper_import'] = test_apex_wrapper_import()
|
173
|
+
|
174
|
+
if results['telemetry_manager']:
|
175
|
+
results['manual_span'] = test_manual_span_emission()
|
176
|
+
results['apex_wrapper'] = test_apex_wrapper_emission()
|
177
|
+
else:
|
178
|
+
print("⚠️ Skipping span tests - telemetry not enabled")
|
179
|
+
results['manual_span'] = False
|
180
|
+
results['apex_wrapper'] = False
|
181
|
+
|
182
|
+
# Summary
|
183
|
+
print("=" * 80)
|
184
|
+
print("SUMMARY")
|
185
|
+
print("=" * 80)
|
186
|
+
|
187
|
+
for test_name, passed in results.items():
|
188
|
+
status = "✅ PASS" if passed else "❌ FAIL"
|
189
|
+
print(f"{status:10} {test_name}")
|
190
|
+
|
191
|
+
print()
|
192
|
+
|
193
|
+
if all(results.values()):
|
194
|
+
print("✅ ALL TESTS PASSED!")
|
195
|
+
print("\nIf spans still don't appear in Cloud Trace:")
|
196
|
+
print(" 1. Wait 60 seconds (BatchSpanProcessor batches spans)")
|
197
|
+
print(" 2. Check Cloud Trace console")
|
198
|
+
print(" 3. Verify project ID matches your GCP setup")
|
199
|
+
print(" 4. Check service account has cloudtrace.traces.patch permission")
|
200
|
+
return 0
|
201
|
+
else:
|
202
|
+
print("❌ SOME TESTS FAILED")
|
203
|
+
print("\nTroubleshooting:")
|
204
|
+
|
205
|
+
if not results['credentials']:
|
206
|
+
print(" • Set COMMUNITY_CREDENTIALS: export COMMUNITY_CREDENTIALS='<base64-json>'")
|
207
|
+
|
208
|
+
if not results['telemetry_manager']:
|
209
|
+
print(" • Telemetry manager not initialized - check credentials")
|
210
|
+
|
211
|
+
if results['telemetry_manager'] and not results['manual_span']:
|
212
|
+
print(" • Manual span failed - check OpenTelemetry setup")
|
213
|
+
|
214
|
+
if results['telemetry_manager'] and not results['apex_wrapper']:
|
215
|
+
print(" • Apex wrapper failed - check implementation")
|
216
|
+
|
217
|
+
return 1
|
218
|
+
|
219
|
+
|
220
|
+
if __name__ == "__main__":
|
221
|
+
sys.exit(main())
|