/// <summary> /// Full HandleOnStartup method signature. /// <paramref name="argsRef"/> line arguments string array will be used with the (optional) initial setup of Modular.Config. /// If this parameter is non-null, it will be processed to extract the key=value items it contains, and will be replaced with a new array that has the consumed key=value items removed from it. /// <paramref name="appLogger"/> will be assigned to a new logger (this is expected to be used by the client in calls to later HandleYYY methods). /// <paramref name="nvs"/> is used to pass all directly caller configurable values that are supported by this method (see remarks section below) /// </summary> /// <remarks> /// logBaseName: optional name used in the default log file names that are generated. Also used to name the main thread if it has not already been named. /// addWPFLMH: no longer supported - ignored /// addSetLMH: pass as true to enable creating a set based log message handler. /// enableUEH: if true, this method will install an unhandled exception handler, if none has already been established. Set to false to disable this behavior. /// providerSelect: set to StandardProviderSelect value to override the default behavior of StandardProviderSelect.All. This value will be ignored if there are already any providers registered with Config.Instance when this method is called. If the given argsRef array value is null then MainArgs will be excluded from this value. /// uehFileWritePath: may be used to override the config key value (Config.UnhandledExceptionEventHandler.FilePath), and/or its initial value. Must be non-empty to enable setting up an UEH. /// mainLoggerType: may be used to override the config key value (Config.Logging.MainLogger.Type), and/or its initial value. When neither this parameter, nor the configuration key are found to give a valid FileRingLogMessageHandlerType value, the static DefaultMainLoggerType is used. /// diagTraceLMHSettingFlags, setLMHSettingFlags: may be used to override the corresponding config key values (Config.Logging.DiagnosticTrace/Set), and/or their initial values. (LogMessageHandlerSettingFlags) /// setName, setCapacity, setLogGate: may be used to override the corresponding config key values (Config.Logging.Set.Name/Capacity/LogGate), and/or their initial values. /// appEventMesgType: may be used to define the MesgType to be used for App Event messages. When present, this key's value is used to set the AppEventMesgType. /// traceLoggingGroupName: may be used to override the config key value (Config.Logging.TraceRing.LGID) and/or its initial value. /// traceRingLinkFromDefaultGroup, traceRingLinkToDefaultGroup: when present these boolean values control the corresponding function without regard to the contents of the corresponding modular config values. /// </remarks> public static void HandleOnStartup(ref string[] argsRef, ref Logging.Logger appLogger, INamedValueSet nvs = null) { System.Reflection.Assembly callerAssy = CallerAssembly; nvs = nvs.MapNullToEmpty(); string logBaseName = nvs["logBaseName"].VC.GetValue <string>(rethrow: false) ?? callerAssy.GetAssemblyNameFromFullName(); bool addWPFLMH = nvs["addWPFLMH"].VC.GetValue <bool?>(rethrow: false) ?? false; bool addSetLMH = nvs["addSetLMH"].VC.GetValue <bool?>(rethrow: false) ?? false; bool enableUEH = nvs["enableUEH"].VC.GetValue <bool?>(rethrow: false) ?? true; System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess(); System.Threading.Thread currentThread = System.Threading.Thread.CurrentThread; System.Reflection.Assembly currentExecAssy = System.Reflection.Assembly.GetExecutingAssembly(); System.Reflection.Assembly mainAssy = System.Reflection.Assembly.GetEntryAssembly(); if (currentThread.Name.IsNullOrEmpty()) { currentThread.Name = "{0}.Main".CheckedFormat(logBaseName); } IConfig config = Config.Instance; StandardProviderSelect providerSelect = nvs["providerSelect"].VC.GetValue <StandardProviderSelect>(rethrow: false, defaultValue: StandardProviderSelect.All); if (argsRef == null) { providerSelect &= ~StandardProviderSelect.MainArgs; } if (providerSelect != StandardProviderSelect.None && config.Providers.IsNullOrEmpty()) { config.AddStandardProviders(ref argsRef, providerSelect: providerSelect); } if (enableUEH && UnhandledExceptionEventHandler != null) { uehFileWritePath = nvs["uehFileWritePath"].VC.GetValue <string>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.UnhandledExceptionEventHandler.FilePath", silenceLogging: true).GetValue(uehFileWritePath); if (!uehFileWritePath.IsNullOrEmpty()) { AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.UnhandledException += UnhandledExceptionEventHandler; } } int ringQueueSize = 500; int traceQueueSize = 1000; Logging.ListMesgEmitter issueListEmitter = new Logging.ListMesgEmitter() { MesgType = Logging.MesgType.Error }; Logging.ListMesgEmitter valuesListEmitter = new Logging.ListMesgEmitter() { MesgType = Logging.MesgType.Debug }; FileRingLogMessageHandlerType mainLoggerType = nvs["mainLoggerType"].VC.GetValue <FileRingLogMessageHandlerType?>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.Logging.MainLogger.Type").GetValue <FileRingLogMessageHandlerType?>() ?? DefaultMainLoggerType; Logging.FileRotationLoggingConfig fileRotationRingConfig = new Logging.FileRotationLoggingConfig(logBaseName.MapNullToEmpty() + "LogFile") { mesgQueueSize = ringQueueSize, nameUsesDateAndTime = true, fileHeaderLines = Logging.GenerateDefaultHeaderLines(logBaseName, includeNullForDynamicLines: true, hostingAssembly: callerAssy), fileHeaderLinesDelegate = Logging.GenerateDynamicHeaderLines, logGate = Logging.LogGate.Debug, }; Logging.Handlers.TextFileDateTreeLogMessageHandler.Config dateTreeDirConfig = new Logging.Handlers.TextFileDateTreeLogMessageHandler.Config(logBaseName.MapNullToEmpty() + "Log", @".\Logs") { FileHeaderLines = Logging.GenerateDefaultHeaderLines(logBaseName, includeNullForDynamicLines: true, hostingAssembly: callerAssy), FileHeaderLinesDelegate = Logging.GenerateDynamicHeaderLines, LogGate = Logging.LogGate.Debug, }; switch (mainLoggerType) { case FileRingLogMessageHandlerType.TextFileRotationDirectoryLogMessageHandler: fileRotationRingConfig.UpdateFromModularConfig("Config.Logging.FileRing.", issueListEmitter, valuesListEmitter, configInstance: config); break; case FileRingLogMessageHandlerType.TextFileDateTreeDirectoryLogMessageHandler: dateTreeDirConfig.UpdateFromModularConfig("Config.Logging.DateTree.", issueListEmitter, valuesListEmitter, configInstance: config); break; default: case FileRingLogMessageHandlerType.None: break; } Logging.ILogMessageHandler mainLMHnoQ = null; int maxQueueSize = 1000; switch (mainLoggerType) { case FileRingLogMessageHandlerType.TextFileRotationDirectoryLogMessageHandler: mainLMHnoQ = new Logging.Handlers.TextFileRotationLogMessageHandler(fileRotationRingConfig); maxQueueSize = fileRotationRingConfig.mesgQueueSize; break; case FileRingLogMessageHandlerType.TextFileDateTreeDirectoryLogMessageHandler: mainLMHnoQ = new Logging.Handlers.TextFileDateTreeLogMessageHandler(dateTreeDirConfig); maxQueueSize = dateTreeDirConfig.MesgQueueSize; break; default: case FileRingLogMessageHandlerType.None: break; } LogMessageHandlerSettingFlags diagTraceLMHSettingFlags = nvs["diagTraceLMHSettingFlags"].VC.GetValue <LogMessageHandlerSettingFlags?>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.Logging.DiagnosticTrace").GetValue(LogMessageHandlerSettingFlags.IncludeWhenDebuggerAttached); LogMessageHandlerSettingFlags setLMHSettingFlags = nvs["setLMHSettingFlags"].VC.GetValue <LogMessageHandlerSettingFlags?>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.Logging.Set").GetValue(addSetLMH ? LogMessageHandlerSettingFlags.IncludeAlways : LogMessageHandlerSettingFlags.Disabled); bool addDiagTraceLMH = diagTraceLMHSettingFlags.IsSet(LogMessageHandlerSettingFlags.IncludeAlways) || diagTraceLMHSettingFlags.IsSet(LogMessageHandlerSettingFlags.IncludeWhenDebuggerAttached) && System.Diagnostics.Debugger.IsAttached; addSetLMH = setLMHSettingFlags.IsSet(LogMessageHandlerSettingFlags.IncludeAlways) || setLMHSettingFlags.IsSet(LogMessageHandlerSettingFlags.IncludeWhenDebuggerAttached) && System.Diagnostics.Debugger.IsAttached; Logging.ILogMessageHandler diagTraceLMH = addDiagTraceLMH ? Logging.CreateDiagnosticTraceLogMessageHandler(logGate: Logging.LogGate.Debug) : null; Logging.ILogMessageHandler setLMH = null; if (addSetLMH) { string setName = nvs["setName"].VC.GetValue <string>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.Logging.Set.Name").GetValue("LogMessageHistory"); int setCapacity = nvs["setCapacity"].VC.GetValue <int?>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.Logging.Set.Capacity").GetValue(1000); Logging.LogGate setLogGate = nvs["setLogGate"].VC.GetValue <Logging.LogGate?>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.Logging.Set.LogGate").GetValue(Logging.LogGate.Debug); if (!setName.IsNullOrEmpty()) { setLMH = new MosaicLib.Logging.Handlers.SetLogMessageHandler(setName, capacity: setCapacity, logGate: setLogGate); } } // Normally all of the standard lmh objects (main, diag, wpf) share the use of a single message queue. List <Logging.ILogMessageHandler> mainLMHList = new List <Logging.ILogMessageHandler>(); if (mainLMHnoQ != null) { mainLMHList.Add(mainLMHnoQ); } if (diagTraceLMH != null) { if (diagTraceLMHSettingFlags.IsClear(LogMessageHandlerSettingFlags.NonQueued)) { mainLMHList.Add(diagTraceLMH); } else { Logging.AddLogMessageHandlerToDefaultDistributionGroup(diagTraceLMH); } } if (setLMH != null) { if (setLMHSettingFlags.IsClear(LogMessageHandlerSettingFlags.NonQueued)) { mainLMHList.Add(setLMH); } else { Logging.AddLogMessageHandlerToDefaultDistributionGroup(setLMH); } } Logging.ILogMessageHandler mainLMHQueueLMH = new Logging.Handlers.QueueLogMessageHandler("lmhMainSet.q", mainLMHList.ToArray(), maxQueueSize: maxQueueSize); Logging.AddLogMessageHandlerToDefaultDistributionGroup(mainLMHQueueLMH); string traceLoggingGroupName = nvs["traceLoggingGroupName"].VC.GetValue <string>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.Logging.TraceRing.LGID", silenceLogging: true).GetValue("LGID.Trace"); bool traceRingEnable = config.GetConfigKeyAccessOnce("Config.Logging.TraceRing.Enable").GetValue(!traceLoggingGroupName.IsNullOrEmpty()); if (traceRingEnable) { // Setup the trace logger. This logger uses a seperate message queue. Logging.FileRotationLoggingConfig traceRingConfig = new Logging.FileRotationLoggingConfig((logBaseName ?? String.Empty) + "TraceRing") { mesgQueueSize = traceQueueSize, nameUsesDateAndTime = false, // will use 4 digit file names. Limit of 100 files total includeThreadInfo = true, fileHeaderLines = Logging.GenerateDefaultHeaderLines("{0} Trace Output".CheckedFormat(logBaseName), includeNullForDynamicLines: true, hostingAssembly: callerAssy), fileHeaderLinesDelegate = Logging.GenerateDynamicHeaderLines, logGate = Logging.LogGate.All, }.UpdateFromModularConfig("Config.Logging.TraceRing.", issueListEmitter, valuesListEmitter, configInstance: config); Logging.ILogMessageHandler traceRingLMH = Logging.CreateQueuedTextFileRotationDirectoryLogMessageHandler(traceRingConfig); Logging.AddLogMessageHandlerToDistributionGroup(traceLoggingGroupName, traceRingLMH); Logging.SetDistributionGroupGate(traceLoggingGroupName, Logging.LogGate.All); Logging.MapLoggersToDistributionGroup(Logging.LoggerNameMatchType.Regex, @"(\.Data|\.Trace)", traceLoggingGroupName); bool traceRingLinkFromDefaultGroup = nvs["traceRingLinkFromDefaultGroup"].VC.GetValue <bool?>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.Logging.TraceRing.LinkFromDefaultGroup").GetValue(true); bool traceRingLinkToDefaultGroup = nvs["traceRingLinkToDefaultGroup"].VC.GetValue <bool?>(rethrow: false) ?? config.GetConfigKeyAccessOnce("Config.Logging.TraceRing.LinkToDefaultGroup", silenceLogging: true).GetValue(false); if (traceRingLinkFromDefaultGroup) { Logging.UpdateGroupSettings(Logging.DefaultDistributionGroupName, new Logging.GroupSettings() { GroupLinkageBehavior = Logging.GroupLinkageBehavior.IncludeLinkedLMHInstancesInGroupGateEvaluation }); Logging.LinkDistributionToGroup(Logging.DefaultDistributionGroupName, traceLoggingGroupName); } if (traceRingLinkToDefaultGroup) { Logging.UpdateGroupSettings(traceLoggingGroupName, new Logging.GroupSettings() { GroupLinkageBehavior = Logging.GroupLinkageBehavior.IncludeLinkedLMHInstancesInGroupGateEvaluation }); Logging.LinkDistributionToGroup(traceLoggingGroupName, Logging.DefaultDistributionGroupName); } } if (nvs.Contains("appEventMesgType")) { AppEventMesgType = nvs["appEventMesgType"].VC.GetValue <Logging.MesgType?>(rethrow: false) ?? Logging.MesgType.None; } appLogger = new Logging.Logger("AppLogger"); appLogger.Emitter(AppEventMesgType).EmitWith("App Starting", nvs: new NamedValueSet() { { "AppEvent", "OnStartup" } }); // emit the config messages obtained above. Logging.Logger appLoggerCopy = appLogger; issueListEmitter.EmittedItemList.ForEach((item) => appLoggerCopy.Error.Emit(item.MesgStr)); valuesListEmitter.EmittedItemList.ForEach((item) => appLoggerCopy.Debug.Emit(item.MesgStr)); if (addWPFLMH) { appLogger.Warning.Emit("Use of MosaicLib.WPF.Logging is no longer supported. Please enable use of SetLogMessageHandler (addSetLMH/useSetLMH) and convert to use of set based log display controls"); } }