diff --git a/AppScaffolding/AppScaffolding.csproj b/AppScaffolding/AppScaffolding.csproj index f882dccf..be1ba58c 100644 --- a/AppScaffolding/AppScaffolding.csproj +++ b/AppScaffolding/AppScaffolding.csproj @@ -3,7 +3,7 @@ net6.0 - 6.6.8.1 + 6.6.9.1 diff --git a/AppScaffolding/LibationScaffolding.cs b/AppScaffolding/LibationScaffolding.cs index 1d871842..a4de712c 100644 --- a/AppScaffolding/LibationScaffolding.cs +++ b/AppScaffolding/LibationScaffolding.cs @@ -56,7 +56,7 @@ namespace AppScaffolding // migrations go below here // - Migrations.migrate_to_v6_5_2(config); + Migrations.migrate_to_v6_6_9(config); } public static void PopulateMissingConfigValues(Configuration config) @@ -107,29 +107,12 @@ namespace AppScaffolding if (config.GetObject("Serilog") != null) return; - // "Serilog": { - // "MinimumLevel": "Information" - // "WriteTo": [ - // { - // "Name": "Console" - // }, - // { - // "Name": "File", - // "Args": { - // "rollingInterval": "Day", - // "outputTemplate": ... - // } - // } - // ], - // "Using": [ "Dinah.Core" ], - // "Enrich": [ "WithCaller" ] - // } var serilogObj = new JObject { { "MinimumLevel", "Information" }, { "WriteTo", new JArray { - new JObject { {"Name", "Console" } }, + // new JObject { {"Name", "Console" } }, // this has caused more problems than it's solved new JObject { { "Name", "File" }, @@ -144,14 +127,16 @@ namespace AppScaffolding // output example: 2019-11-26 08:48:40.224 -05:00 [DBG] Begin Libation // - with class and method info: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] (at {Caller}) {Message:lj}{NewLine}{Exception}"; // output example: 2019-11-26 08:48:40.224 -05:00 [DBG] (at LibationWinForms.Program.init()) Begin Libation - { "outputTemplate", "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] (at {Caller}) {Message:lj}{NewLine}{Exception}" } + // {Properties:j} needed for expanded exception logging + { "outputTemplate", "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] (at {Caller}) {Message:lj}{NewLine}{Exception} {Properties:j}" } } } } } }, - { "Using", new JArray{ "Dinah.Core" } }, // dll's name, NOT namespace - { "Enrich", new JArray{ "WithCaller" } }, + // better exception logging with: Serilog.Exceptions library -- WithExceptionDetails + { "Using", new JArray{ "Dinah.Core", "Serilog.Exceptions" } }, // dll's name, NOT namespace + { "Enrich", new JArray{ "WithCaller", "WithExceptionDetails" } }, }; config.SetObject("Serilog", serilogObj); } @@ -166,9 +151,9 @@ namespace AppScaffolding // capture most Console.WriteLine() and write to serilog. See below tests for details. // Some dependencies print helpful info via Console.WriteLine. We'd like to log it. // - // Serilog also writes to Console so this might be asking for trouble. ie: infinite loops. - // SerilogTextWriter needs to be more robust and tested. Esp the Write() methods. - // Empirical testing so far has shown no issues. + // If Serilog also writes to Console, this might be asking for trouble. ie: infinite loops. + // To use that way, SerilogTextWriter needs to be more robust and tested. Esp the Write() methods. + // However, empirical testing so far has shown no issues. Console.SetOut(new MultiTextWriter(origOut, new SerilogTextWriter())); #region Console => Serilog tests @@ -346,9 +331,51 @@ namespace AppScaffolding }; #endregion - public static void migrate_to_v6_5_2(Configuration config) + public static void migrate_to_v6_6_9(Configuration config) { - // example + var writeToPath = $"Serilog.WriteTo"; + + // remove WriteTo[].Name == Console + { + if (UNSAFE_MigrationHelper.Settings_TryGetArrayLength(writeToPath, out var length1)) + { + for (var i = length1 - 1; i >= 0; i--) + { + var exists = UNSAFE_MigrationHelper.Settings_TryGetFromJsonPath($"{writeToPath}[{i}].Name", out var value); + + if (exists && value == "Console") + UNSAFE_MigrationHelper.Settings_RemoveFromArray(writeToPath, i); + } + } + } + + // add Serilog.Exceptions -- WithExceptionDetails + { + // outputTemplate should contain "{Properties:j}" + { + // re-calculate. previous loop may have changed the length + if (UNSAFE_MigrationHelper.Settings_TryGetArrayLength(writeToPath, out var length2)) + { + var propertyName = "outputTemplate"; + for (var i = 0; i < length2; i++) + { + var jsonPath = $"{writeToPath}[{i}].Args"; + var exists = UNSAFE_MigrationHelper.Settings_TryGetFromJsonPath($"{jsonPath}.{propertyName}", out var value); + + var newValue = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] (at {Caller}) {Message:lj}{NewLine}{Exception} {Properties:j}"; + + if (exists && value != newValue) + UNSAFE_MigrationHelper.Settings_SetWithJsonPath(jsonPath, propertyName, newValue); + } + } + } + + // Serilog.Using must include "Serilog.Exceptions" + UNSAFE_MigrationHelper.Settings_AddUniqueToArray("Serilog.Using", "Serilog.Exceptions"); + + // Serilog.Enrich must include "WithExceptionDetails" + UNSAFE_MigrationHelper.Settings_AddUniqueToArray("Serilog.Enrich", "WithExceptionDetails"); + } } } } diff --git a/AppScaffolding/UNSAFE_MigrationHelper.cs b/AppScaffolding/UNSAFE_MigrationHelper.cs index cd5e332b..b44a80d1 100644 --- a/AppScaffolding/UNSAFE_MigrationHelper.cs +++ b/AppScaffolding/UNSAFE_MigrationHelper.cs @@ -113,6 +113,108 @@ namespace AppScaffolding return success; } + public static bool Settings_JsonPathIsType(string jsonPath, JTokenType jTokenType) + { + JToken val = null; + + process_SettingsJson(jObj => val = jObj.SelectToken(jsonPath), false); + + return val?.Type == jTokenType; + } + + public static bool Settings_TryGetFromJsonPath(string jsonPath, out string value) + { + JToken val = null; + + process_SettingsJson(jObj => val = jObj.SelectToken(jsonPath), false); + + if (val?.Type == JTokenType.String) + { + value = val.Value(); + return true; + } + else + { + value = null; + return false; + } + } + + public static void Settings_SetWithJsonPath(string jsonPath, string propertyName, string newValue) + { + if (!Settings_TryGetFromJsonPath($"{jsonPath}.{propertyName}", out _)) + return; + + process_SettingsJson(jObj => + { + var token = jObj.SelectToken(jsonPath); + if (token is null + || token is not JObject o + || o[propertyName] is null) + return; + + var oldValue = token.Value(propertyName); + if (oldValue != newValue) + token[propertyName] = newValue; + }); + } + + public static bool Settings_TryGetArrayLength(string jsonPath, out int length) + { + length = 0; + + if (!Settings_JsonPathIsType(jsonPath, JTokenType.Array)) + return false; + + JArray array = null; + process_SettingsJson(jObj => array = (JArray)jObj.SelectToken(jsonPath)); + + length = array.Count; + return true; + } + + public static void Settings_AddToArray(string jsonPath, string newValue) + { + if (!Settings_JsonPathIsType(jsonPath, JTokenType.Array)) + return; + + process_SettingsJson(jObj => + { + var array = (JArray)jObj.SelectToken(jsonPath); + array.Add(newValue); + }); + } + + /// Do not add if already exists + public static void Settings_AddUniqueToArray(string arrayPath, string newValue) + { + if (!Settings_TryGetArrayLength(arrayPath, out var qty)) + return; + + for (var i = 0; i < qty; i++) + { + var exists = Settings_TryGetFromJsonPath($"{arrayPath}[{i}]", out var value); + if (exists && value == newValue) + return; + } + + Settings_AddToArray(arrayPath, newValue); + } + + /// only remove if not exists + public static void Settings_RemoveFromArray(string jsonPath, int position) + { + if (!Settings_JsonPathIsType(jsonPath, JTokenType.Array)) + return; + + process_SettingsJson(jObj => + { + var array = (JArray)jObj.SelectToken(jsonPath); + if (position < array.Count) + array.RemoveAt(position); + }); + } + /// only insert if not exists public static void Settings_Insert(string key, string value) => process_SettingsJson(jObj => jObj.TryAdd(key, value)); diff --git a/LibationFileManager/Configuration.cs b/LibationFileManager/Configuration.cs index 17be2109..fa38ddc5 100644 --- a/LibationFileManager/Configuration.cs +++ b/LibationFileManager/Configuration.cs @@ -270,7 +270,7 @@ namespace LibationFileManager var valueWasChanged = persistentDictionary.SetWithJsonPath("Serilog", "MinimumLevel", value.ToString()); if (!valueWasChanged) { - Log.Logger.Information("LogLevel.set attempt. No change"); + Log.Logger.Debug("LogLevel.set attempt. No change"); return; } diff --git a/LibationFileManager/LibationFileManager.csproj b/LibationFileManager/LibationFileManager.csproj index 8142aac2..9849e96d 100644 --- a/LibationFileManager/LibationFileManager.csproj +++ b/LibationFileManager/LibationFileManager.csproj @@ -6,6 +6,7 @@ +