diff --git a/Assets/EditorTests/Logger/LoggerServiceConfigTests.cs b/Assets/EditorTests/Logger/LoggerServiceConfigTests.cs index 70da1b1..d069964 100644 --- a/Assets/EditorTests/Logger/LoggerServiceConfigTests.cs +++ b/Assets/EditorTests/Logger/LoggerServiceConfigTests.cs @@ -10,10 +10,10 @@ public void Init_WhenJsonExists_LoadsBasicProperties() { LoggerServiceConfig config = new LoggerServiceConfig(); config.Init(); - Assert.IsTrue(config.ServiceEnabled); - Assert.IsTrue(config.EnableConsole); - Assert.AreEqual("Logger/log4net.xml", config.Log4NetConfigXml); - Assert.AreEqual("DEBUG", config.MinLogLevel); + Assert.IsTrue(config.Settings.ServiceEnabled); + Assert.IsTrue(config.Settings.ConsoleEnabled); + Assert.AreEqual("Logger/log4net.xml", config.Settings.Log4NetConfigXml); + Assert.AreEqual("DEBUG", config.Settings.MinConsoleLogLevel); } } } diff --git a/Assets/EditorTests/Logger/LoggerServiceIntegrationTests.cs b/Assets/EditorTests/Logger/LoggerServiceIntegrationTests.cs index 4ac8c8c..4f9ebe6 100644 --- a/Assets/EditorTests/Logger/LoggerServiceIntegrationTests.cs +++ b/Assets/EditorTests/Logger/LoggerServiceIntegrationTests.cs @@ -13,8 +13,8 @@ public void DebugLog_WritesToInfoLogFile() LoggerServiceConfig config = new LoggerServiceConfig(); config.Init(); - Assert.IsTrue(config.ServiceEnabled, "ServiceEnabled must be true for this integration test."); - Assert.IsFalse(string.IsNullOrEmpty(config.Log4NetConfigXml), "Log4NetConfigXml must point to a valid XML file."); + Assert.IsTrue(config.Settings.ServiceEnabled, "ServiceEnabled must be true for this integration test."); + Assert.IsFalse(string.IsNullOrEmpty(config.Settings.Log4NetConfigXml), "Log4NetConfigXml must point to a valid XML file."); string logsDir = config.GetSystemLogsDirectory(); Directory.CreateDirectory(logsDir); diff --git a/Assets/StreamingAssets/DataManagement/app_settings.json b/Assets/StreamingAssets/DataManagement/app_settings.json index 1e4495e..9520fc1 100644 --- a/Assets/StreamingAssets/DataManagement/app_settings.json +++ b/Assets/StreamingAssets/DataManagement/app_settings.json @@ -3,6 +3,12 @@ "offlineConfigurationFile": "DataManagement/offline_config.json", "workspacesDirectory": "DataManagement/workspaces", "remoteConfigurationUrl": "https://example.com/config/getConfig?v=10", + "loggerSettings": { + "serviceEnabled": true, + "minFileLogLevel": "WARN", + "minHttpLogLevel": "WARN", + "forceMinLogLevel": false + }, "translationSettings": { "locale": "he-IL", "localFilePath": "Translations/Translations.json", diff --git a/Assets/StreamingAssets/Logger/LoggerConfig.json b/Assets/StreamingAssets/Logger/LoggerConfig.json index 207eeeb..aaed958 100644 --- a/Assets/StreamingAssets/Logger/LoggerConfig.json +++ b/Assets/StreamingAssets/Logger/LoggerConfig.json @@ -1,8 +1,11 @@ { - "Enabled": true, + "ServiceEnabled": true, "ConsoleEnabled": true, "Log4NetConfigXml": "Logger/log4net.xml", - "MinLogLevel": "DEBUG", + "MinConsoleLogLevel": "DEBUG", + "MinFileLogLevel": "DEBUG", + "MinHttpLogLevel": "DEBUG", + "ForceMinLogLevel": false, "StackTraceRowLimit": 0, "HttpEndpointUrl": "https://TODO/logs", "HttpPersistenceDirectory": "C:/Yahalom/logs/offline" diff --git a/Assets/com.mapcolonies.core/Services/LoggerService/Config.cs b/Assets/com.mapcolonies.core/Services/LoggerService/Config.cs deleted file mode 100644 index d277cc1..0000000 --- a/Assets/com.mapcolonies.core/Services/LoggerService/Config.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace com.mapcolonies.core.Services.LoggerService -{ - [System.Serializable] - public class Config - { - public string Log4NetConfigXml; - public int StackTraceRowLimit; - public bool Enabled; - public bool ConsoleEnabled; - public string MinLogLevel; - public string HttpEndpointUrl; - public string HttpPersistenceDirectory; - } -} diff --git a/Assets/com.mapcolonies.core/Services/LoggerService/LoggerInitializer.cs b/Assets/com.mapcolonies.core/Services/LoggerService/LoggerInitializer.cs index d3b4f34..f341da9 100644 --- a/Assets/com.mapcolonies.core/Services/LoggerService/LoggerInitializer.cs +++ b/Assets/com.mapcolonies.core/Services/LoggerService/LoggerInitializer.cs @@ -7,7 +7,12 @@ namespace com.mapcolonies.core.Services.LoggerService public static class LoggerInitializer { private const string AdditionalRevisionSeparator = "f"; - private static LoggerService Logger; + + public static LoggerService Logger + { + get; + private set; + } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)] public static void Init() diff --git a/Assets/com.mapcolonies.core/Services/LoggerService/LoggerService.cs b/Assets/com.mapcolonies.core/Services/LoggerService/LoggerService.cs index 83fbfc9..f6d4e33 100644 --- a/Assets/com.mapcolonies.core/Services/LoggerService/LoggerService.cs +++ b/Assets/com.mapcolonies.core/Services/LoggerService/LoggerService.cs @@ -1,12 +1,15 @@ using System; using System.IO; using com.mapcolonies.core.Services.LoggerService.CustomAppenders; +using Cysharp.Threading.Tasks; using log4net; +using log4net.Appender; using log4net.Config; using log4net.Core; using log4net.Layout; using log4net.Repository.Hierarchy; using UnityEngine; +using ConsoleAppender = com.mapcolonies.core.Services.LoggerService.CustomAppenders.ConsoleAppender; using Debug = UnityEngine.Debug; namespace com.mapcolonies.core.Services.LoggerService @@ -17,6 +20,10 @@ public class LoggerService : IDisposable private const string HttpEndpointUrl = "HttpEndpointUrl"; private const string HttpPersistenceDirectory = "HttpPersistenceDirectory"; private const string Pattern = "%date %-5level %logger - %message%newline"; + private const string ConsoleAppenderName = "Console"; + private const string FileAppenderName = "File"; + private const string HttpAppenderName = "Http"; + private readonly LoggerServiceConfig _config; private ILogHandler _originalUnityLogHandler; @@ -26,7 +33,7 @@ public LoggerService(LoggerServiceConfig config) { _config = config; - if (!config.ServiceEnabled) return; + if (!_config.Settings.ServiceEnabled) return; bool success = InitializeLog4Net(); @@ -47,7 +54,7 @@ private bool InitializeLog4Net() try { _originalUnityLogHandler = Debug.unityLogger.logHandler; - string logConfigFilePath = Path.Combine(Application.streamingAssetsPath, _config.Log4NetConfigXml); + string logConfigFilePath = Path.Combine(Application.streamingAssetsPath, _config.Settings.Log4NetConfigXml); if (!File.Exists(logConfigFilePath)) { @@ -62,13 +69,36 @@ private bool InitializeLog4Net() } GlobalContext.Properties[LogFilePath] = logDirectory; - GlobalContext.Properties[HttpEndpointUrl] = _config.HttpEndpointUrl; + GlobalContext.Properties[HttpEndpointUrl] = _config.Settings.HttpEndpointUrl; GlobalContext.Properties[HttpPersistenceDirectory] = _config.GetHttpPersistenceDirectory(); FileInfo logConfigFile = new FileInfo(logConfigFilePath); XmlConfigurator.Configure(logConfigFile); Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository(); + bool isDev = Application.isEditor || Debug.isDebugBuild; + + if (!isDev) + { + foreach (IAppender appender in hierarchy.GetAppenders()) + { + if (appender is AppenderSkeleton sk) + { + if (appender.Name.Contains(ConsoleAppenderName, StringComparison.OrdinalIgnoreCase)) + { + sk.Threshold = hierarchy.LevelMap[_config.Settings.MinConsoleLogLevel] ?? Level.Debug; + } + else + { + sk.Threshold = Level.Debug; + } + + sk.ActivateOptions(); + } + } + + ApplyFileAndHttpThresholds(hierarchy, _config.Settings); + } ConsoleAppender consoleAppender = new ConsoleAppender(_originalUnityLogHandler); @@ -77,13 +107,12 @@ private bool InitializeLog4Net() layout.ActivateOptions(); consoleAppender.Layout = layout; - Level consoleThreshold = hierarchy.LevelMap[_config.MinLogLevel] ?? Level.Debug; - consoleAppender.Threshold = consoleThreshold; + consoleAppender.Threshold = hierarchy.LevelMap[_config.Settings.MinConsoleLogLevel] ?? Level.Debug; consoleAppender.ActivateOptions(); hierarchy.Root.AddAppender(consoleAppender); - if (!_config.EnableConsole) + if (!_config.Settings.ConsoleEnabled) { hierarchy.Root.RemoveAppender(consoleAppender); } @@ -128,7 +157,7 @@ private bool TryInitializeLogDirectory(out string logDirectory) success = false; } - string logConfigFilePath = Path.Combine(Application.streamingAssetsPath, _config.Log4NetConfigXml); + string logConfigFilePath = Path.Combine(Application.streamingAssetsPath, _config.Settings.Log4NetConfigXml); if (!success || !File.Exists(logConfigFilePath)) { @@ -160,5 +189,40 @@ public void Dispose() _originalUnityLogHandler = null; } } + + private void ApplyFileAndHttpThresholds(Hierarchy hierarchy, LoggerSettings settings) + { + foreach (IAppender appender in hierarchy.GetAppenders()) + { + if (appender is AppenderSkeleton sk) + { + if (appender.Name.Contains(FileAppenderName, StringComparison.OrdinalIgnoreCase) || + appender is RollingFileAppender) + { + sk.Threshold = hierarchy.LevelMap[settings.MinFileLogLevel] ?? Level.Debug; + } + else if (appender.Name.Contains(HttpAppenderName, StringComparison.OrdinalIgnoreCase) || + appender is HttpAppender) + { + sk.Threshold = hierarchy.LevelMap[settings.MinHttpLogLevel] ?? Level.Debug; + } + + sk.ActivateOptions(); + } + } + } + + public async UniTask UpdateLoggerSettings(LoggerSettings settings) + { + if (!settings.ForceMinLogLevel) return false; + + Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository(); + bool isDev = Application.isEditor || Debug.isDebugBuild; + + if (isDev) return false; + + ApplyFileAndHttpThresholds(hierarchy, settings); + return true; + } } } diff --git a/Assets/com.mapcolonies.core/Services/LoggerService/LoggerServiceConfig.cs b/Assets/com.mapcolonies.core/Services/LoggerService/LoggerServiceConfig.cs index e423f93..12fdaec 100644 --- a/Assets/com.mapcolonies.core/Services/LoggerService/LoggerServiceConfig.cs +++ b/Assets/com.mapcolonies.core/Services/LoggerService/LoggerServiceConfig.cs @@ -1,4 +1,7 @@ using System.IO; +using com.mapcolonies.core.Utilities; +using Cysharp.Threading.Tasks; +using Newtonsoft.Json; using UnityEngine; namespace com.mapcolonies.core.Services.LoggerService @@ -7,39 +10,7 @@ public class LoggerServiceConfig { private const string JsonFileName = "Logger/LoggerConfig.json"; - private Config _config; - - public string Log4NetConfigXml - { - get; - private set; - } - - public bool ServiceEnabled - { - get; - private set; - } - - public bool EnableConsole - { - get; - private set; - } - - public string MinLogLevel - { - get; - private set; - } - - public string HttpEndpointUrl - { - get; - private set; - } - - public string HttpPersistenceDirectory + public LoggerSettings Settings { get; private set; @@ -47,38 +18,22 @@ public string HttpPersistenceDirectory public void Init() { - string filePath = Path.Combine(Application.streamingAssetsPath, JsonFileName); - - if (!File.Exists(filePath)) - { - Debug.LogError($"File {JsonFileName} not found at: " + filePath); - return; - } - try { - string jsonContent = File.ReadAllText(filePath); - _config = JsonUtility.FromJson(jsonContent); + Settings = JsonUtilityEx.LoadJson(JsonFileName); - if (_config == null) + if (Settings == null) { Debug.LogError($"Failed to deserialize {JsonFileName} JSON content."); - return; } - - Log4NetConfigXml = _config.Log4NetConfigXml; - ServiceEnabled = _config.Enabled; - EnableConsole = _config.ConsoleEnabled; - MinLogLevel = _config.MinLogLevel; - HttpEndpointUrl = _config.HttpEndpointUrl; - HttpPersistenceDirectory = _config.HttpPersistenceDirectory; } catch (System.Exception ex) { - Debug.LogError($"Error parsing file: {ex.Message}"); + Debug.LogError($"Error loading {JsonFileName}: {ex.Message}"); } } + public string GetSystemLogsDirectory() { if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor) diff --git a/Assets/com.mapcolonies.core/Services/LoggerService/LoggerSettings.cs b/Assets/com.mapcolonies.core/Services/LoggerService/LoggerSettings.cs new file mode 100644 index 0000000..cf54a5f --- /dev/null +++ b/Assets/com.mapcolonies.core/Services/LoggerService/LoggerSettings.cs @@ -0,0 +1,68 @@ +using System; + +namespace com.mapcolonies.core.Services.LoggerService +{ + [Serializable] + public record LoggerSettings + { + public string Log4NetConfigXml + { + get; + set; + } + + public int StackTraceRowLimit + { + get; + set; + } + + public bool ServiceEnabled + { + get; + set; + } + + public bool ConsoleEnabled + { + get; + set; + } + + public string MinConsoleLogLevel + { + get; + set; + } + + public string MinFileLogLevel + { + get; + set; + } + + public string MinHttpLogLevel + { + get; + set; + } + + public bool ForceMinLogLevel + { + get; + set; + } + + public string HttpEndpointUrl + { + get; + set; + } + + public string HttpPersistenceDirectory + { + get; + set; + } + } +} diff --git a/Assets/com.mapcolonies.core/Services/LoggerService/Config.cs.meta b/Assets/com.mapcolonies.core/Services/LoggerService/LoggerSettings.cs.meta similarity index 100% rename from Assets/com.mapcolonies.core/Services/LoggerService/Config.cs.meta rename to Assets/com.mapcolonies.core/Services/LoggerService/LoggerSettings.cs.meta diff --git a/Assets/com.mapcolonies.core/Utilities/JsonUtilityEx.cs b/Assets/com.mapcolonies.core/Utilities/JsonUtilityEx.cs index ae00e6e..d00272c 100644 --- a/Assets/com.mapcolonies.core/Utilities/JsonUtilityEx.cs +++ b/Assets/com.mapcolonies.core/Utilities/JsonUtilityEx.cs @@ -84,5 +84,29 @@ public static async UniTask DoesJsonExistAsync(string relativePath, FileLo return await FileIOUtility.FileExistsAsync(path); } + + public static T LoadJson(string relativePath, FileLocation location = FileLocation.StreamingAssets) + { + string path; + + switch (location) + { + case FileLocation.PersistentData: + path = Path.Combine(Application.persistentDataPath, relativePath); + break; + + case FileLocation.StreamingAssets: + path = Path.Combine(Application.streamingAssetsPath, relativePath); + break; + + default: + Debug.LogWarning($"Unknown file location {location}. Using streaming assets as default."); + path = Path.Combine(Application.streamingAssetsPath, relativePath); + break; + } + + string json = File.ReadAllText(path); + return JsonConvert.DeserializeObject(json); + } } } diff --git a/Assets/com.mapcolonies.yahalom/DataManagement/AppSettings/AppSettingsSelectors.cs b/Assets/com.mapcolonies.yahalom/DataManagement/AppSettings/AppSettingsSelectors.cs index 8c0e468..0d22f61 100644 --- a/Assets/com.mapcolonies.yahalom/DataManagement/AppSettings/AppSettingsSelectors.cs +++ b/Assets/com.mapcolonies.yahalom/DataManagement/AppSettings/AppSettingsSelectors.cs @@ -1,4 +1,4 @@ -using com.mapcolonies.core.Localization; +using com.mapcolonies.core.Services.LoggerService; using com.mapcolonies.core.Localization.Models; using Unity.AppUI.Redux; @@ -10,6 +10,7 @@ public class AppSettingsSelectors public static readonly Selector OfflineConfigurationPathSelector = (state) => state.OfflineConfigurationFile; public static readonly Selector RemoteConfigurationUrlSelector = (state) => state.RemoteConfigurationUrl; public static readonly Selector WorkspacesPathSelector = (state) => state.WorkspacesDirectory; + public static readonly Selector LoggerSettings = (state) => state.LoggerSettings; public static readonly Selector TranslationSettings = (state) => state.TranslationSettings; } } diff --git a/Assets/com.mapcolonies.yahalom/DataManagement/AppSettings/AppSettingsState.cs b/Assets/com.mapcolonies.yahalom/DataManagement/AppSettings/AppSettingsState.cs index 00db4dc..e0ee964 100644 --- a/Assets/com.mapcolonies.yahalom/DataManagement/AppSettings/AppSettingsState.cs +++ b/Assets/com.mapcolonies.yahalom/DataManagement/AppSettings/AppSettingsState.cs @@ -1,4 +1,5 @@ using System; +using com.mapcolonies.core.Services.LoggerService; using com.mapcolonies.core.Localization.Models; namespace com.mapcolonies.yahalom.DataManagement.AppSettings @@ -30,6 +31,12 @@ public string WorkspacesDirectory set; } + public LoggerSettings LoggerSettings + { + get; + set; + } = new LoggerSettings(); + public TranslationSettings TranslationSettings { get; diff --git a/Assets/com.mapcolonies.yahalom/EntryPoint/AppStartUpController.cs b/Assets/com.mapcolonies.yahalom/EntryPoint/AppStartUpController.cs index 7022f26..fdc5f52 100644 --- a/Assets/com.mapcolonies.yahalom/EntryPoint/AppStartUpController.cs +++ b/Assets/com.mapcolonies.yahalom/EntryPoint/AppStartUpController.cs @@ -15,6 +15,7 @@ using VContainer; using VContainer.Unity; using com.mapcolonies.core.Localization; +using com.mapcolonies.core.Services.LoggerService; using com.mapcolonies.core.Localization.Models; using com.mapcolonies.yahalom.AppMode; using com.mapcolonies.yahalom.AppMode.Modes; @@ -97,7 +98,15 @@ public AppStartUpController(InitializationPipeline initializationPipeline, Lifet AppModeSwitcher appModeSwitcher = _scope.Container.Resolve(); appModeSwitcher.RegisterChildScope(resolver); return default; - }) + }), + new ActionUnit("Logger Service", 0.1f, InitPolicy.Fail, + () => + { + ReduxStoreManager reduxStoreManager = scope.Container.Resolve(); + LoggerSettings loggerSettings = reduxStoreManager.Store.GetState(AppSettingsReducer.SliceName, AppSettingsSelectors.LoggerSettings); + LoggerService loggerService = scope.Container.Resolve(); + return loggerService.UpdateLoggerSettings(loggerSettings); + }), }) }; } diff --git a/Assets/com.mapcolonies.yahalom/EntryPoint/MainAppLifetimeScope.cs b/Assets/com.mapcolonies.yahalom/EntryPoint/MainAppLifetimeScope.cs index 7e2d076..b559499 100644 --- a/Assets/com.mapcolonies.yahalom/EntryPoint/MainAppLifetimeScope.cs +++ b/Assets/com.mapcolonies.yahalom/EntryPoint/MainAppLifetimeScope.cs @@ -12,6 +12,7 @@ using VContainer; using VContainer.Unity; using com.mapcolonies.core.Localization; +using com.mapcolonies.core.Services.LoggerService; using com.mapcolonies.yahalom.AppMode; namespace com.mapcolonies.yahalom.EntryPoint @@ -47,6 +48,7 @@ protected override void Configure(IContainerBuilder builder) builder.Register(Lifetime.Singleton).As().As(); builder.Register(Lifetime.Singleton).AsSelf().As().As(); builder.Register(Lifetime.Singleton); + builder.RegisterInstance(LoggerInitializer.Logger).As().As(); #endregion }