telemetry: pluggable Unity sender; add MCP_DISABLE_TELEMETRY; server reads version file; locks for milestones
parent
f6a5568865
commit
2abca24e9d
|
|
@ -12,6 +12,7 @@ namespace MCPForUnity.Editor.Helpers
|
||||||
{
|
{
|
||||||
private const string TELEMETRY_DISABLED_KEY = "MCPForUnity.TelemetryDisabled";
|
private const string TELEMETRY_DISABLED_KEY = "MCPForUnity.TelemetryDisabled";
|
||||||
private const string CUSTOMER_UUID_KEY = "MCPForUnity.CustomerUUID";
|
private const string CUSTOMER_UUID_KEY = "MCPForUnity.CustomerUUID";
|
||||||
|
private static Action<Dictionary<string, object>> s_sender;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if telemetry is enabled (can be disabled via Environment Variable or EditorPrefs)
|
/// Check if telemetry is enabled (can be disabled via Environment Variable or EditorPrefs)
|
||||||
|
|
@ -35,6 +36,14 @@ namespace MCPForUnity.Editor.Helpers
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Honor protocol-wide opt-out as well
|
||||||
|
var mcpDisable = Environment.GetEnvironmentVariable("MCP_DISABLE_TELEMETRY");
|
||||||
|
if (!string.IsNullOrEmpty(mcpDisable) &&
|
||||||
|
(mcpDisable.Equals("true", StringComparison.OrdinalIgnoreCase) || mcpDisable == "1"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check EditorPrefs
|
// Check EditorPrefs
|
||||||
return !UnityEditor.EditorPrefs.GetBool(TELEMETRY_DISABLED_KEY, false);
|
return !UnityEditor.EditorPrefs.GetBool(TELEMETRY_DISABLED_KEY, false);
|
||||||
}
|
}
|
||||||
|
|
@ -110,6 +119,14 @@ namespace MCPForUnity.Editor.Helpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows the bridge to register a concrete sender for telemetry payloads.
|
||||||
|
/// </summary>
|
||||||
|
public static void RegisterTelemetrySender(Action<Dictionary<string, object>> sender)
|
||||||
|
{
|
||||||
|
s_sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Record bridge startup event
|
/// Record bridge startup event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -162,15 +179,28 @@ namespace MCPForUnity.Editor.Helpers
|
||||||
|
|
||||||
private static void SendTelemetryToPythonServer(Dictionary<string, object> telemetryData)
|
private static void SendTelemetryToPythonServer(Dictionary<string, object> telemetryData)
|
||||||
{
|
{
|
||||||
// This would integrate with the existing bridge communication system
|
var sender = s_sender;
|
||||||
// For now, we'll just log it when debug is enabled
|
if (sender != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sender(telemetryData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (IsDebugEnabled())
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Telemetry sender error (non-blocking): {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: log when debug is enabled
|
||||||
if (IsDebugEnabled())
|
if (IsDebugEnabled())
|
||||||
{
|
{
|
||||||
Debug.Log($"<b><color=#2EA3FF>MCP-TELEMETRY</color></b>: {telemetryData["event_type"]}");
|
Debug.Log($"<b><color=#2EA3FF>MCP-TELEMETRY</color></b>: {telemetryData["event_type"]}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Integrate with MCPForUnityBridge command system
|
|
||||||
// We would send this as a special telemetry command to the Python server
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsDebugEnabled()
|
private static bool IsDebugEnabled()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
3.3.0
|
||||||
|
|
@ -27,8 +27,15 @@ async def server_lifespan(server: FastMCP) -> AsyncIterator[Dict[str, Any]]:
|
||||||
|
|
||||||
# Record server startup telemetry
|
# Record server startup telemetry
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
start_clk = time.perf_counter()
|
||||||
|
try:
|
||||||
|
from pathlib import Path
|
||||||
|
ver_path = Path(__file__).parent / "server-version.txt"
|
||||||
|
server_version = ver_path.read_text(encoding="utf-8").strip()
|
||||||
|
except Exception:
|
||||||
|
server_version = "unknown"
|
||||||
record_telemetry(RecordType.STARTUP, {
|
record_telemetry(RecordType.STARTUP, {
|
||||||
"server_version": "3.0.2",
|
"server_version": server_version,
|
||||||
"startup_time": start_time
|
"startup_time": start_time
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -45,15 +52,23 @@ async def server_lifespan(server: FastMCP) -> AsyncIterator[Dict[str, Any]]:
|
||||||
"connection_time_ms": (time.time() - start_time) * 1000
|
"connection_time_ms": (time.time() - start_time) * 1000
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except ConnectionError as e:
|
||||||
logger.warning(f"Could not connect to Unity on startup: {str(e)}")
|
logger.warning("Could not connect to Unity on startup: %s", e)
|
||||||
_unity_connection = None
|
_unity_connection = None
|
||||||
|
|
||||||
# Record connection failure
|
# Record connection failure
|
||||||
record_telemetry(RecordType.UNITY_CONNECTION, {
|
record_telemetry(RecordType.UNITY_CONNECTION, {
|
||||||
"status": "failed",
|
"status": "failed",
|
||||||
"error": str(e)[:200],
|
"error": str(e)[:200],
|
||||||
"connection_time_ms": (time.time() - start_time) * 1000
|
"connection_time_ms": (time.perf_counter() - start_clk) * 1000
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("Unexpected error connecting to Unity on startup: %s", e)
|
||||||
|
_unity_connection = None
|
||||||
|
record_telemetry(RecordType.UNITY_CONNECTION, {
|
||||||
|
"status": "failed",
|
||||||
|
"error": str(e)[:200],
|
||||||
|
"connection_time_ms": (time.perf_counter() - start_clk) * 1000
|
||||||
})
|
})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ class TelemetryCollector:
|
||||||
self.config = TelemetryConfig()
|
self.config = TelemetryConfig()
|
||||||
self._customer_uuid: Optional[str] = None
|
self._customer_uuid: Optional[str] = None
|
||||||
self._milestones: Dict[str, Dict[str, Any]] = {}
|
self._milestones: Dict[str, Dict[str, Any]] = {}
|
||||||
|
self._lock: threading.Lock = threading.Lock()
|
||||||
self._load_persistent_data()
|
self._load_persistent_data()
|
||||||
|
|
||||||
def _load_persistent_data(self):
|
def _load_persistent_data(self):
|
||||||
|
|
@ -199,18 +200,16 @@ class TelemetryCollector:
|
||||||
"""Record a milestone event, returns True if this is the first occurrence"""
|
"""Record a milestone event, returns True if this is the first occurrence"""
|
||||||
if not self.config.enabled:
|
if not self.config.enabled:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
milestone_key = milestone.value
|
milestone_key = milestone.value
|
||||||
if milestone_key in self._milestones:
|
with self._lock:
|
||||||
return False # Already recorded
|
if milestone_key in self._milestones:
|
||||||
|
return False # Already recorded
|
||||||
milestone_data = {
|
milestone_data = {
|
||||||
"timestamp": time.time(),
|
"timestamp": time.time(),
|
||||||
"data": data or {}
|
"data": data or {},
|
||||||
}
|
}
|
||||||
|
self._milestones[milestone_key] = milestone_data
|
||||||
self._milestones[milestone_key] = milestone_data
|
self._save_milestones()
|
||||||
self._save_milestones()
|
|
||||||
|
|
||||||
# Also send as telemetry record
|
# Also send as telemetry record
|
||||||
self.record(
|
self.record(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue