Skip to content

Commit c925bcd

Browse files
committed
Add basic diagnostics with categorization
1 parent bf53223 commit c925bcd

10 files changed

+679
-178
lines changed

.claude/settings.local.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"allow": [
44
"Bash(ls:*)",
55
"Bash(find:*)",
6-
"Bash(grep:*)"
6+
"Bash(grep:*)",
7+
"mcp__ide__getDiagnostics"
78
],
89
"deny": []
910
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
using System;
2+
using UnityEngine;
3+
4+
namespace BetaHub
5+
{
6+
/// <summary>
7+
/// Interface for contextual diagnostics providing error handling and logging capabilities.
8+
/// </summary>
9+
public interface IContextualDiagnostics
10+
{
11+
/// <summary>
12+
/// Logs an informational message for an operation.
13+
/// </summary>
14+
/// <param name="operation">The operation name</param>
15+
/// <param name="message">The message to log</param>
16+
void LogInfo(string operation, string message);
17+
18+
/// <summary>
19+
/// Logs a successful operation completion.
20+
/// </summary>
21+
/// <param name="operation">The operation name</param>
22+
/// <param name="message">The success message</param>
23+
void LogSuccess(string operation, string message);
24+
25+
/// <summary>
26+
/// Logs progress information for a long-running operation.
27+
/// </summary>
28+
/// <param name="operation">The operation name</param>
29+
/// <param name="message">The progress message</param>
30+
void LogProgress(string operation, string message);
31+
32+
/// <summary>
33+
/// Logs an error with automatic exception categorization and retry analysis.
34+
/// </summary>
35+
/// <param name="operation">The operation name</param>
36+
/// <param name="ex">The exception that occurred</param>
37+
void LogError(string operation, Exception ex);
38+
39+
/// <summary>
40+
/// Logs an error with explicit categorization and retry information.
41+
/// </summary>
42+
/// <param name="operation">The operation name</param>
43+
/// <param name="message">The error message</param>
44+
/// <param name="category">The error category</param>
45+
/// <param name="isRetryable">Whether the error is retryable</param>
46+
void LogError(string operation, string message, BetaHubErrorCategory category = BetaHubErrorCategory.Unknown, bool isRetryable = false);
47+
}
48+
49+
/// <summary>
50+
/// Main entry point for BetaHub diagnostics. Provides contextual diagnostics services
51+
/// for consistent error handling and logging across the plugin.
52+
/// </summary>
53+
public static class BetaHubDiagnostics
54+
{
55+
/// <summary>
56+
/// Creates a contextual diagnostics service for a specific component or operation context.
57+
/// </summary>
58+
/// <param name="contextName">The name of the context (e.g., "Issue(abc123)", "GameRecorder", etc.)</param>
59+
/// <returns>A contextual diagnostics service instance</returns>
60+
public static IContextualDiagnostics ForContext(string contextName)
61+
{
62+
return new ContextualDiagnostics(contextName);
63+
}
64+
}
65+
66+
/// <summary>
67+
/// Implementation of contextual diagnostics that provides consistent logging and error handling
68+
/// for a specific context (component, operation, or scope).
69+
/// </summary>
70+
internal class ContextualDiagnostics : IContextualDiagnostics
71+
{
72+
private readonly string _contextName;
73+
74+
/// <summary>
75+
/// Initializes a new instance of contextual diagnostics for the specified context.
76+
/// </summary>
77+
/// <param name="contextName">The context name to include in all log messages</param>
78+
public ContextualDiagnostics(string contextName)
79+
{
80+
_contextName = contextName ?? throw new ArgumentNullException(nameof(contextName));
81+
}
82+
83+
/// <summary>
84+
/// Logs an informational message for an operation.
85+
/// </summary>
86+
/// <param name="operation">The operation name</param>
87+
/// <param name="message">The message to log</param>
88+
public void LogInfo(string operation, string message)
89+
{
90+
Debug.Log($"[INFO] {_contextName}.{operation}: {message}");
91+
}
92+
93+
/// <summary>
94+
/// Logs a successful operation completion.
95+
/// </summary>
96+
/// <param name="operation">The operation name</param>
97+
/// <param name="message">The success message</param>
98+
public void LogSuccess(string operation, string message)
99+
{
100+
Debug.Log($"[SUCCESS] {_contextName}.{operation}: {message}");
101+
}
102+
103+
/// <summary>
104+
/// Logs progress information for a long-running operation.
105+
/// </summary>
106+
/// <param name="operation">The operation name</param>
107+
/// <param name="message">The progress message</param>
108+
public void LogProgress(string operation, string message)
109+
{
110+
Debug.Log($"[PROGRESS] {_contextName}.{operation}: {message}");
111+
}
112+
113+
/// <summary>
114+
/// Logs an error with automatic exception categorization and retry analysis.
115+
/// </summary>
116+
/// <param name="operation">The operation name</param>
117+
/// <param name="ex">The exception that occurred</param>
118+
public void LogError(string operation, Exception ex)
119+
{
120+
var category = BetaHubErrorHelper.CategorizeException(ex);
121+
var isRetryable = BetaHubErrorHelper.IsRetryable(ex);
122+
LogError(operation, ex.Message, category, isRetryable);
123+
}
124+
125+
/// <summary>
126+
/// Logs an error with explicit categorization and retry information.
127+
/// </summary>
128+
/// <param name="operation">The operation name</param>
129+
/// <param name="message">The error message</param>
130+
/// <param name="category">The error category</param>
131+
/// <param name="isRetryable">Whether the error is retryable</param>
132+
public void LogError(string operation, string message, BetaHubErrorCategory category = BetaHubErrorCategory.Unknown, bool isRetryable = false)
133+
{
134+
string retryInfo = isRetryable ? " (Retryable)" : " (Not retryable)";
135+
Debug.LogError($"[ERROR] {_contextName}.{operation} [{category}]: {message}{retryInfo}");
136+
}
137+
}
138+
139+
/// <summary>
140+
/// Configuration class for BetaHub diagnostics behavior.
141+
/// Allows customization of logging levels and formatting.
142+
/// </summary>
143+
public static class BetaHubDiagnosticsConfig
144+
{
145+
/// <summary>
146+
/// Gets or sets whether to include timestamps in log messages.
147+
/// Default: false (Unity Console already includes timestamps)
148+
/// </summary>
149+
public static bool IncludeTimestamps { get; set; } = false;
150+
151+
/// <summary>
152+
/// Gets or sets whether to include stack traces in error logs.
153+
/// Default: false (Unity Console shows stack traces separately)
154+
/// </summary>
155+
public static bool IncludeStackTraces { get; set; } = false;
156+
157+
/// <summary>
158+
/// Gets or sets the minimum log level to output.
159+
/// Can be used to reduce log verbosity in production builds.
160+
/// </summary>
161+
public static BetaHubLogLevel MinimumLogLevel { get; set; } = BetaHubLogLevel.Info;
162+
}
163+
164+
/// <summary>
165+
/// Log levels for BetaHub diagnostics.
166+
/// </summary>
167+
public enum BetaHubLogLevel
168+
{
169+
/// <summary>
170+
/// Progress and detailed information
171+
/// </summary>
172+
Progress = 0,
173+
174+
/// <summary>
175+
/// General information
176+
/// </summary>
177+
Info = 1,
178+
179+
/// <summary>
180+
/// Success notifications
181+
/// </summary>
182+
Success = 2,
183+
184+
/// <summary>
185+
/// Error messages only
186+
/// </summary>
187+
Error = 3,
188+
189+
/// <summary>
190+
/// No logging
191+
/// </summary>
192+
None = 4
193+
}
194+
}

Runtime/Scripts/ErrorHandler.cs.meta renamed to Runtime/Scripts/BetaHubDiagnostics.cs.meta

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
using System;
2+
using System.IO;
3+
using UnityEngine;
4+
5+
namespace BetaHub
6+
{
7+
/// <summary>
8+
/// Categories of errors that can occur in the BetaHub plugin operations.
9+
/// Used for consistent error handling and retry logic across all components.
10+
/// </summary>
11+
public enum BetaHubErrorCategory
12+
{
13+
/// <summary>
14+
/// Unknown or uncategorized error type
15+
/// </summary>
16+
Unknown,
17+
18+
/// <summary>
19+
/// Network-related errors (HTTP requests, connectivity issues)
20+
/// Generally retryable with appropriate backoff
21+
/// </summary>
22+
Network,
23+
24+
/// <summary>
25+
/// Authentication and authorization failures
26+
/// Usually not retryable without user intervention
27+
/// </summary>
28+
Authentication,
29+
30+
/// <summary>
31+
/// File system access errors (file not found, permission denied, etc.)
32+
/// Usually not retryable unless temporary lock issues
33+
/// </summary>
34+
FileAccess,
35+
36+
/// <summary>
37+
/// Input validation errors (invalid parameters, malformed data)
38+
/// Not retryable without fixing the input
39+
/// </summary>
40+
Validation,
41+
42+
/// <summary>
43+
/// Timeout errors (operation took too long)
44+
/// Often retryable with longer timeout or retry logic
45+
/// </summary>
46+
Timeout,
47+
48+
/// <summary>
49+
/// Server-side errors (5xx HTTP status codes)
50+
/// May be retryable depending on the specific error
51+
/// </summary>
52+
ServerError
53+
}
54+
55+
/// <summary>
56+
/// Utility class for error categorization and retry logic determination.
57+
/// </summary>
58+
public static class BetaHubErrorHelper
59+
{
60+
/// <summary>
61+
/// Categorizes an exception into a BetaHubErrorCategory for consistent error handling.
62+
/// </summary>
63+
/// <param name="ex">The exception to categorize</param>
64+
/// <returns>The appropriate error category</returns>
65+
public static BetaHubErrorCategory CategorizeException(Exception ex)
66+
{
67+
return ex switch
68+
{
69+
TimeoutException => BetaHubErrorCategory.Timeout,
70+
UnauthorizedAccessException => BetaHubErrorCategory.Authentication,
71+
FileNotFoundException or DirectoryNotFoundException => BetaHubErrorCategory.FileAccess,
72+
IOException ioEx when ioEx.Message.Contains("being used by another process") => BetaHubErrorCategory.FileAccess,
73+
IOException => BetaHubErrorCategory.FileAccess,
74+
ArgumentException or ArgumentNullException => BetaHubErrorCategory.Validation,
75+
InvalidOperationException invalidOp when invalidOp.Message.Contains("HTTP") => BetaHubErrorCategory.Network,
76+
InvalidOperationException invalidOp when invalidOp.Message.Contains("401") => BetaHubErrorCategory.Authentication,
77+
InvalidOperationException invalidOp when invalidOp.Message.Contains("403") => BetaHubErrorCategory.Authentication,
78+
InvalidOperationException invalidOp when invalidOp.Message.Contains("5") && invalidOp.Message.Contains("HTTP") => BetaHubErrorCategory.ServerError,
79+
_ => BetaHubErrorCategory.Unknown
80+
};
81+
}
82+
83+
/// <summary>
84+
/// Determines if an error category is generally retryable.
85+
/// </summary>
86+
/// <param name="category">The error category to check</param>
87+
/// <returns>True if the error type is generally retryable</returns>
88+
public static bool IsRetryable(BetaHubErrorCategory category)
89+
{
90+
return category switch
91+
{
92+
BetaHubErrorCategory.Network => true,
93+
BetaHubErrorCategory.Timeout => true,
94+
BetaHubErrorCategory.ServerError => true,
95+
BetaHubErrorCategory.FileAccess => false, // Usually permanent unless lock issues
96+
BetaHubErrorCategory.Authentication => false,
97+
BetaHubErrorCategory.Validation => false,
98+
BetaHubErrorCategory.Unknown => false,
99+
_ => false
100+
};
101+
}
102+
103+
/// <summary>
104+
/// Gets a human-readable description of an error category.
105+
/// </summary>
106+
/// <param name="category">The error category</param>
107+
/// <returns>A descriptive string for the category</returns>
108+
public static string GetCategoryDescription(BetaHubErrorCategory category)
109+
{
110+
return category switch
111+
{
112+
BetaHubErrorCategory.Network => "Network connectivity or HTTP error",
113+
BetaHubErrorCategory.Authentication => "Authentication or authorization failure",
114+
BetaHubErrorCategory.FileAccess => "File system access error",
115+
BetaHubErrorCategory.Validation => "Input validation error",
116+
BetaHubErrorCategory.Timeout => "Operation timeout",
117+
BetaHubErrorCategory.ServerError => "Server-side error",
118+
BetaHubErrorCategory.Unknown => "Unknown error",
119+
_ => "Unrecognized error category"
120+
};
121+
}
122+
123+
/// <summary>
124+
/// Determines if a specific exception instance is retryable based on both category and content.
125+
/// </summary>
126+
/// <param name="ex">The exception to analyze</param>
127+
/// <returns>True if this specific exception instance should be retried</returns>
128+
public static bool IsRetryable(Exception ex)
129+
{
130+
var category = CategorizeException(ex);
131+
132+
// Special cases where category-level retry logic might be overridden
133+
switch (category)
134+
{
135+
case BetaHubErrorCategory.FileAccess:
136+
// File access might be retryable if it's a temporary lock
137+
return ex.Message.Contains("being used by another process") ||
138+
ex.Message.Contains("access denied") ||
139+
ex is UnauthorizedAccessException;
140+
141+
case BetaHubErrorCategory.ServerError:
142+
// Some server errors shouldn't be retried (e.g., 501 Not Implemented)
143+
return !ex.Message.Contains("501") && !ex.Message.Contains("505");
144+
145+
default:
146+
return IsRetryable(category);
147+
}
148+
}
149+
}
150+
}

Runtime/Scripts/BetaHubErrorCategory.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)