Logging System
QWR Utilities provides an enhanced logging system that extends Unity's built-in logging capabilities with features like log categories, filtering, file output, and performance measurement. This page explains how to use and customize the logging system.
Logging System Overview
The QWR logging system includes:
- Log levels (Debug, Info, Warning, Error, Fatal)
- Log categories for better organization
- File logging capabilities
- Conditional logging
- Performance measurement
- Custom log formatting
- Editor integration
Basic Logging
Standard Log Methods
using QWR.Utilities.Logging;
// Basic logging
QWRLogger.Log("Player initialized");
QWRLogger.LogWarning("Low memory detected");
QWRLogger.LogError("Failed to load asset");
QWRLogger.LogFatal("Critical error occurred");
// With context object (highlights in Unity console)
QWRLogger.Log("Player initialized", gameObject);
Log Levels
The logging system supports different log levels:
// Set the minimum log level
QWRLogger.SetLogLevel(LogLevel.Info);
// Logs with different levels
QWRLogger.LogDebug("Debug message"); // Won't show if level is Info or higher
QWRLogger.LogInfo("Info message");
QWRLogger.LogWarning("Warning message");
QWRLogger.LogError("Error message");
QWRLogger.LogFatal("Fatal error message");
// Check current log level
LogLevel currentLevel = QWRLogger.CurrentLogLevel;
Log Categories
You can categorize logs for better organization:
// Log with categories
QWRLogger.Log("Player movement updated", LogCategory.Gameplay);
QWRLogger.LogWarning("Frame time spike detected", LogCategory.Performance);
QWRLogger.LogError("Failed to load texture", LogCategory.AssetLoading);
// Define custom categories
public static class CustomLogCategories
{
public static readonly LogCategory AI = new LogCategory("AI");
public static readonly LogCategory Networking = new LogCategory("Networking");
public static readonly LogCategory UI = new LogCategory("UI");
}
// Use custom categories
QWRLogger.Log("AI agent initialized", CustomLogCategories.AI);
Advanced Logging Features
Conditional Logging
Log only when certain conditions are met:
// Only log if condition is true
bool isDebugBuild = Debug.isDebugBuild;
QWRLogger.LogIf(isDebugBuild, "This only logs in debug builds");
// Log with condition and category
QWRLogger.LogIf(health < 20, "Player health critical", LogCategory.Gameplay);
// Conditional warning/error
QWRLogger.LogWarningIf(memory < 100, "Low memory warning");
QWRLogger.LogErrorIf(connection == null, "Connection failed");
Format Logging
Format logs with variable data:
// Format with parameters
QWRLogger.LogFormat("Player {0} scored {1} points", playerName, score);
// Format with category
QWRLogger.LogFormat(LogCategory.Gameplay, "Level {0} completed in {1} seconds", levelId, time);
// Format warnings and errors
QWRLogger.LogWarningFormat("Object {0} has invalid position {1}", objectName, position);
QWRLogger.LogErrorFormat("Failed to load {0}: {1}", assetName, errorMessage);
Performance Logging
Measure and log performance metrics:
// Simple performance measurement
using (QWRLogger.LogPerformance("ExpensiveOperation"))
{
// Code to measure
PerformExpensiveOperation();
}
// Outputs: "ExpensiveOperation completed in X.XX ms"
// With custom category
using (QWRLogger.LogPerformance("LoadLevel", LogCategory.Performance))
{
LoadLevel();
}
// With threshold (only log if execution exceeds threshold)
using (QWRLogger.LogPerformance("RenderFrame", threshold: 16.67f)) // 60fps threshold
{
RenderFrame();
}
// Manual performance measurement
QWRPerformanceMarker marker = QWRLogger.BeginPerformanceMeasurement("ComplexOperation");
// ... code to measure ...
marker.End(); // Logs the result
Log Configuration
Global Settings
Configure global logging settings:
// Set minimum log level
QWRLogger.SetLogLevel(LogLevel.Info);
// Enable/disable specific log categories
QWRLogger.EnableCategory(LogCategory.Gameplay);
QWRLogger.DisableCategory(LogCategory.Debug);
// Check if category is enabled
bool isEnabled = QWRLogger.IsCategoryEnabled(LogCategory.Performance);
// Enable/disable all logging
QWRLogger.EnableLogging(true);
File Logging
Log to file for persistent records:
// Enable file logging
QWRLogger.EnableFileLogging(true);
// Configure file logging
QWRLogger.SetLogFilePath("Logs/game_log.txt");
QWRLogger.SetLogFileMaxSize(10 * 1024 * 1024); // 10 MB
QWRLogger.SetLogFileRotationCount(5); // Keep 5 backup files
// Flush logs to file immediately
QWRLogger.FlushLogFile();
Custom Log Handlers
Register custom log handlers to process logs:
// Create a custom log handler
public class CustomLogHandler : IQWRLogHandler
{
public void HandleLog(LogLevel level, LogCategory category, string message, UnityEngine.Object context)
{
// Custom log handling (e.g., send to server, store in database)
string formattedMessage = $"[{level}][{category}] {message}";
// Example: Send log to server
NetworkManager.SendLogToServer(formattedMessage);
}
}
// Register the custom handler
QWRLogger.RegisterLogHandler(new CustomLogHandler());
// Unregister when no longer needed
QWRLogger.UnregisterLogHandler(myCustomHandler);
Editor Integration
QWR Utilities includes editor integration for the logging system:
Log Console Window
A custom log console window with filtering capabilities:
// This code would be in an Editor script
using QWR.Utilities.Editor.Log;
using UnityEditor;
[MenuItem("QWR/Log Console")]
public static void ShowLogConsole()
{
QWRLogConsoleWindow.ShowWindow();
}
Log Interceptor
Intercept Unity logs in the editor:
// This code would be in an Editor script
using QWR.Utilities.Editor;
using UnityEditor;
[InitializeOnLoad]
public class LogInterceptorSetup
{
static LogInterceptorSetup()
{
LogInterceptor.EnableLogInterception(true);
LogInterceptor.OnLogIntercepted += HandleLog;
}
private static void HandleLog(string condition, string stackTrace, LogType type)
{
// Process intercepted logs
}
}
Best Practices
Log Level Guidelines
- Debug: Detailed information for debugging
- Info: General information about application flow
- Warning: Potential issues that don't stop execution
- Error: Errors that allow the application to continue
- Fatal: Critical errors that may cause the application to terminate
Category Organization
Organize log categories by system or feature:
public static class LogCategories
{
// Core systems
public static readonly LogCategory System = new LogCategory("System");
public static readonly LogCategory Performance = new LogCategory("Performance");
// Game features
public static readonly LogCategory Gameplay = new LogCategory("Gameplay");
public static readonly LogCategory UI = new LogCategory("UI");
public static readonly LogCategory Audio = new LogCategory("Audio");
public static readonly LogCategory Graphics = new LogCategory("Graphics");
// Development
public static readonly LogCategory Debug = new LogCategory("Debug");
public static readonly LogCategory Editor = new LogCategory("Editor");
}
Performance Considerations
- Disable debug logs in release builds
- Use conditional logging to avoid string formatting overhead
- Be mindful of file logging performance impact
// Efficient conditional logging
if (QWRLogger.IsLoggingEnabled(LogLevel.Debug, LogCategory.AI))
{
// Only format the string if the log will actually be output
string complexMessage = GenerateComplexLogMessage();
QWRLogger.LogDebug(complexMessage, LogCategory.AI);
}
Thread Safety
The logging system is thread-safe and can be used from background threads:
// Logging from a background thread
System.Threading.Tasks.Task.Run(() => {
QWRLogger.Log("Background thread operation started");
// Perform background work
QWRLogger.Log("Background thread operation completed");
});
Extending the Logging System
Creating Custom Log Formatters
You can create custom log formatters:
// Custom log formatter
public class CustomLogFormatter : IQWRLogFormatter
{
public string FormatLogMessage(LogLevel level, LogCategory category, string message)
{
string timestamp = System.DateTime.Now.ToString("HH:mm:ss.fff");
return $"[{timestamp}][{level}][{category}] {message}";
}
}
// Register the custom formatter
QWRLogger.SetLogFormatter(new CustomLogFormatter());
Creating Log Utilities
You can create utility classes for specific logging needs:
// Example: Network logging utility
public static class NetworkLogger
{
private static readonly LogCategory NetworkCategory = new LogCategory("Network");
public static void LogConnection(string serverId, string status)
{
QWRLogger.LogFormat(NetworkCategory, "Server {0}: {1}", serverId, status);
}
public static void LogPacket(string packetType, int size)
{
QWRLogger.LogDebugFormat(NetworkCategory, "Packet {0}: {1} bytes", packetType, size);
}
public static void LogError(string errorCode, string message)
{
QWRLogger.LogErrorFormat(NetworkCategory, "Error {0}: {1}", errorCode, message);
}
}
// Usage
NetworkLogger.LogConnection("Main", "Connected");
NetworkLogger.LogPacket("PlayerUpdate", 256);
Troubleshooting
Logs Not Appearing
If logs aren't appearing:
- Check that the log level is appropriate (
QWRLogger.SetLogLevel(LogLevel.Debug)) - Verify that the category is enabled (
QWRLogger.EnableCategory(myCategory)) - Ensure logging is enabled globally (
QWRLogger.EnableLogging(true))
File Logging Issues
If file logging isn't working:
- Check file path permissions
- Verify the log directory exists
- Ensure the file isn't locked by another process
Performance Impact
If logging is impacting performance:
- Reduce the log level in production builds
- Use conditional logging to avoid string formatting overhead
- Consider disabling file logging in performance-critical sections
Next Steps
Now that you understand the logging system, you can explore:
- Extension Methods: Learn about useful extensions for Unity objects
- Data Structures: Discover specialized data structures
- Singleton Patterns: Explore different singleton implementations