diff --git a/Assets/Cryville.EEW.Unity/App.cs b/Assets/Cryville.EEW.Unity/App.cs index dc1095f..1344699 100644 --- a/Assets/Cryville.EEW.Unity/App.cs +++ b/Assets/Cryville.EEW.Unity/App.cs @@ -1,19 +1,60 @@ using Cryville.Common.Font; +using Cryville.Common.Logging; using Cryville.Common.Unity.UI; using Cryville.Culture; +using System; +using System.Globalization; using System.IO; using System.Text; using System.Xml; using System.Xml.Linq; using UnityEngine; +using Logger = Cryville.Common.Logging.Logger; namespace Cryville.EEW.Unity { class App { + public static string AppDataPath { get; private set; } + + public static Logger MainLogger { get; private set; } + static FileStream _logFileStream; + static StreamLoggerListener _logWriter; + static bool _init; public static void Init() { if (_init) return; _init = true; + AppDataPath = Application.persistentDataPath; + + var logPath = Directory.CreateDirectory(Path.Combine(AppDataPath, "logs")); + _logFileStream = new FileStream( + Path.Combine( + logPath.FullName, + string.Format( + CultureInfo.InvariantCulture, + "{0}.log", + DateTimeOffset.UtcNow.ToString("yyyyMMddHHmmssfff", CultureInfo.InvariantCulture) + ) + ), + FileMode.Create, FileAccess.Write, FileShare.Read + ); + _logWriter = new StreamLoggerListener(_logFileStream) { AutoFlush = true }; + MainLogger = new Logger(); + var listener = new InstantLoggerListener(); + listener.Log += MainLogger.Log; + MainLogger.AddListener(_logWriter); + Application.logMessageReceivedThreaded += OnInternalLog; + + MainLogger.Log(1, "App", null, "App Version: {0}", Application.version); + MainLogger.Log(1, "App", null, "Unity Version: {0}", Application.unityVersion); + MainLogger.Log(1, "App", null, "Operating System: {0}, Unity = {1}, Family = {2}", Environment.OSVersion, SystemInfo.operatingSystem, SystemInfo.operatingSystemFamily); + MainLogger.Log(1, "App", null, "Platform: Build = {0}, Unity = {1}", PlatformConfig.Name, Application.platform); + MainLogger.Log(1, "App", null, "Culture: {0}, UI = {1}, Unity = {2}", SharedCultures.CurrentCulture, SharedCultures.CurrentUICulture, Application.systemLanguage); + MainLogger.Log(1, "App", null, "Device: Model = {0}, Type = {1}", SystemInfo.deviceModel, SystemInfo.deviceType); + MainLogger.Log(1, "App", null, "Graphics: Name = {0}, Type = {1}, Vendor = {2}, Version = {3}", SystemInfo.graphicsDeviceName, SystemInfo.graphicsDeviceType, SystemInfo.graphicsDeviceVendor, SystemInfo.graphicsDeviceVersion); + MainLogger.Log(1, "App", null, "Processor: Count = {0}, Frequency = {1}MHz, Type = {2}", SystemInfo.processorCount, SystemInfo.processorFrequency, SystemInfo.processorType); + + MainLogger.Log(1, "App", null, "Initializing font manager"); foreach (var res in Resources.LoadAll("cldr/common/validity")) { IdValidity.Load(LoadXmlDocument(res)); } @@ -25,7 +66,10 @@ namespace Cryville.EEW.Unity { }; TMPLocalizedText.DefaultShader = Resources.Load(PlatformConfig.TextShader); + MainLogger.Log(1, "App", null, "Loading config"); SharedSettings.Instance.Init(); + + MainLogger.Log(1, "App", null, "Initialized"); } static readonly Encoding _encoding = new UTF8Encoding(false, true); @@ -40,5 +84,16 @@ namespace Cryville.EEW.Unity { using var reader = XmlReader.Create(stream, _xmlSettings); return XDocument.Load(reader); } + + static void OnInternalLog(string condition, string stackTrace, LogType type) { + var l = type switch { + LogType.Log => 1, + LogType.Assert => 2, + LogType.Warning => 3, + LogType.Error or LogType.Exception => 4, + _ => 1, + }; + MainLogger.Log(l, "Internal", null, "{0}\n{1}", condition, stackTrace); + } } } diff --git a/Assets/Cryville.EEW.Unity/Map/MapTileBitmapHolderBehaviour.cs b/Assets/Cryville.EEW.Unity/Map/MapTileBitmapHolderBehaviour.cs index fc619c6..53b69e5 100644 --- a/Assets/Cryville.EEW.Unity/Map/MapTileBitmapHolderBehaviour.cs +++ b/Assets/Cryville.EEW.Unity/Map/MapTileBitmapHolderBehaviour.cs @@ -40,7 +40,7 @@ namespace Cryville.EEW.Unity.Map { _req.SendWebRequest(); } catch (Exception ex) { - Debug.LogException(ex); + App.MainLogger.Log(4, "Map", null, "An error occurred when loading map tile {0}: {1}", _localFile, ex); } _isReady = false; } @@ -51,7 +51,7 @@ namespace Cryville.EEW.Unity.Map { _sprite = Sprite.Create(_tex, new Rect(0, 0, _tex.width, _tex.height), Vector2.zero, _tex.height, 0, SpriteMeshType.FullRect, Vector4.zero, false); } else { - Debug.LogError(_texHandler.error); + App.MainLogger.Log(4, "Map", null, "An error occurred when loading map tile {0}: {1}", _localFile, _texHandler.error); _localFile.Delete(); } _req.Dispose(); diff --git a/Assets/Cryville.EEW.Unity/SoundPlayer.cs b/Assets/Cryville.EEW.Unity/SoundPlayer.cs index 1e5926c..afdb910 100644 --- a/Assets/Cryville.EEW.Unity/SoundPlayer.cs +++ b/Assets/Cryville.EEW.Unity/SoundPlayer.cs @@ -13,6 +13,7 @@ namespace Cryville.EEW.Unity { }; protected override Stream Open(string path) { + App.MainLogger.Log(0, "Audio", null, "Opening audio file {0}", path); path = Path.Combine(Application.streamingAssetsPath, "Sounds", path + ".ogg"); if (!File.Exists(path)) return null; return new FileStream(path, FileMode.Open, FileAccess.Read); diff --git a/Assets/Cryville.EEW.Unity/TTSWorker.cs b/Assets/Cryville.EEW.Unity/TTSWorker.cs index 7c3e1f3..92575d0 100644 --- a/Assets/Cryville.EEW.Unity/TTSWorker.cs +++ b/Assets/Cryville.EEW.Unity/TTSWorker.cs @@ -9,6 +9,7 @@ namespace Cryville.EEW.Unity { readonly ISpVoice _voice; public TTSWorker() : base(CreateSoundPlayer()) { + App.MainLogger.Log(1, "Audio", null, "Initializing TTS worker"); try { _voice = new SpVoiceClass(); } @@ -16,10 +17,12 @@ namespace Cryville.EEW.Unity { } static SoundPlayer CreateSoundPlayer() { + App.MainLogger.Log(1, "Audio", null, "Creating sound player"); try { return new SoundPlayer(); } - catch (InvalidOperationException) { + catch (InvalidOperationException ex) { + App.MainLogger.Log(3, "Audio", null, "An error occurred when creating sound player: {0}", ex); return null; } } @@ -37,11 +40,13 @@ namespace Cryville.EEW.Unity { (uint)(SpeechVoiceSpeakFlags.SVSFlagsAsync | SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak), out _ ); + App.MainLogger.Log(0, "Audio", null, "TTS ({0}): {1}", culture, content); return Task.CompletedTask; } protected override void StopCurrent() { if (_voice == null) return; + App.MainLogger.Log(0, "Audio", null, "TTS stopping current"); _voice.Skip("SENTENCE", int.MaxValue, out _); } } diff --git a/Assets/Cryville.EEW.Unity/Worker.cs b/Assets/Cryville.EEW.Unity/Worker.cs index 99578c7..04c291b 100644 --- a/Assets/Cryville.EEW.Unity/Worker.cs +++ b/Assets/Cryville.EEW.Unity/Worker.cs @@ -60,6 +60,7 @@ namespace Cryville.EEW.Unity { } void Start() { + App.MainLogger.Log(1, "App", null, "Initializing localized resources manager"); LocalizedResources.Init(new LocalizedResourcesManager()); RegisterViewModelGenerators(_worker); RegisterTTSMessageGenerators(_worker); @@ -73,6 +74,7 @@ namespace Cryville.EEW.Unity { _worker.Reported += OnReported; _grouper.GroupUpdated += OnGroupUpdated; _grouper.GroupRemoved += OnGroupRemoved; + App.MainLogger.Log(1, "App", null, "Worker ready"); Task.Run(() => GatewayVerify(_cancellationTokenSource.Token)).ContinueWith(task => { if (task.IsFaulted) { OnReported(this, new() { Title = task.Exception.Message }); @@ -133,6 +135,7 @@ namespace Cryville.EEW.Unity { bool _verified; void BuildWorkers() { + App.MainLogger.Log(1, "App", null, "Building workers"); #if UNITY_EDITOR _worker.AddWorker(new WolfxWorker(new Uri("ws://localhost:9995/wolfx"))); _worker.AddWorker(new JMAAtomWorker(new Uri("http://localhost:9095/eqvol.xml"))); @@ -215,7 +218,7 @@ namespace Cryville.EEW.Unity { ReportViewModel _latestHistoryReport; void OnReported(object sender, ReportViewModel e) { if (e.Model is Exception && e.Model is not SourceWorkerNetworkException) - Debug.LogError(e); + App.MainLogger.Log(4, "Map", null, "Received an error from {0}: {1}", sender.GetType(), e.Model); _grouper.Report(e); _ongoingReportManager.Report(e); _uiActionQueue.Enqueue(() => { diff --git a/Assets/Plugins/Cryville.Common.Logging.dll b/Assets/Plugins/Cryville.Common.Logging.dll new file mode 100644 index 0000000..59183eb Binary files /dev/null and b/Assets/Plugins/Cryville.Common.Logging.dll differ diff --git a/Assets/Plugins/Cryville.Common.Logging.dll.meta b/Assets/Plugins/Cryville.Common.Logging.dll.meta new file mode 100644 index 0000000..476e6bd --- /dev/null +++ b/Assets/Plugins/Cryville.Common.Logging.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7aa0c56ccfdaf9443b58f26bf40eed01 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Cryville.Common.Logging.xml b/Assets/Plugins/Cryville.Common.Logging.xml new file mode 100644 index 0000000..5b2c752 --- /dev/null +++ b/Assets/Plugins/Cryville.Common.Logging.xml @@ -0,0 +1,185 @@ + + + + Cryville.Common.Logging + + + + + A logger. + + + + + Attaches a listener to the logger. + + The logger listener. + + + + Detaches a listener from the logger. + + The logger listener. + + + + Logs to the logger. + + The severity level. + The category. + The format string. + The arguments for formatting. + + + + Logs to the logger. + + The severity level. + The category. + The format provider. + The format string. + The arguments for formatting. + + + + Logs to the logger. + + The severity level. + The category. + The message. + + + + Logs to the logger. + + The severity level. + The category. + An array of containing the message. + + + + Logs to the logger. + + The severity level. + The category. + An array of containing the message. + A zero-based index of the first character of the message within . + The length of the message. + + + + Logs to the logger. + + The severity level. + The category. + A pointer to the first character of the message. + The length of the message. + + + + A logger listener. + + + + + Closes the logger listener and cleans up all the resources. + + Whether to clean up managed resources. + + + + Closes the logger listener. + + + + + Handles an incoming log. + + The severity level. + The category. + The message. + + + + Handles an incoming log. + + The severity level. + The category. + An array of containing the message. + A zero-based index of the first character of the message within . + The length of the message. + + + + Handles an incoming log. + + The severity level. + The category. + A pointer to the first character of the message. + The length of the message. + + + + A that calls a callback function on log. + + + + + Occurs when a log is logged to the logger. + + + + + + + + A that buffers the logs for enumeration. + + + + + + + + Enumerates the buffered logs. + + The callback function to receive the logs. + + + + A that writes logs into a stream. + + The stream. + The encoding. + + + + A that writes logs into a stream. + + The stream. + The encoding. + + + + Creates an instance of the class. + + The stream. + + + + Whether to flush the stream every time a log is written. + + + + + + + + Represents the method that will handle a log. + + The severity level. + The category. + The message. + + + diff --git a/Assets/Plugins/Cryville.Common.Logging.xml.meta b/Assets/Plugins/Cryville.Common.Logging.xml.meta new file mode 100644 index 0000000..2a0c9e9 --- /dev/null +++ b/Assets/Plugins/Cryville.Common.Logging.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 72ded0675457e0348809193a9c1092b5 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: