/// <summary> /// Entry point for the timer reloading the configuration file. /// </summary> /// <param name="state">Some state object (not used).</param> private void TimerProc(object state) { lock (Sync) { if (mReloadingTimer == null) { return; // object has been disposed } try { // load file (always replace mFile, do not modify existing instance for threading reasons) File = LogConfigurationFile.LoadFrom(FullPath); } catch (FileNotFoundException) { // file does not exist, should be handled using file system notifications // => don't do anything here... } catch (Exception ex) { sLog.ForceWrite(LogLevel.Error, "Reloading configuration file failed. Exception: {0}", ex); mReloadingTimer.Change(500, -1); // try again later... } } }
/// <summary> /// Is called by the file system watcher when a file is renamed in the watched directory. /// </summary> /// <param name="sender">The file system watcher.</param> /// <param name="e">Event arguments.</param> private void EH_FileSystemWatcher_Renamed(object sender, RenamedEventArgs e) { // called by worker thread... lock (Sync) { if (mReloadingTimer == null) { return; // object has been disposed } bool vanished = IsConfigurationFile(e.OldName); if (IsConfigurationFile(e.Name)) { // configuration file is present now // => schedule loading the file... mReloadingTimer.Change(500, -1); } else if (vanished) { // configuration file was removed // => create a default configuration... File = new LogConfigurationFile(); } } }
/// <summary> /// Initializes a new instance of the <see cref="LogConfigurationFile"/> by copying another instance. /// </summary> /// <param name="other">Log configuration file to copy.</param> public LogConfigurationFile(LogConfigurationFile other) { if (other == null) { throw new ArgumentNullException(nameof(other)); } mGlobalSettings = new Dictionary <string, string>(other.mGlobalSettings); ApplicationName = other.ApplicationName; LogWriterSettings.AddRange(other.LogWriterSettings); // log writer settings are immutable, so no copy needed foreach (var kvp in other.ProcessingPipelineStageSettings) { ProcessingPipelineStageSettings.Add(kvp.Key, new Dictionary <string, string>(kvp.Value)); } }
/// <summary> /// Is called by the file system watcher when a file is deleted in the watched directory. /// </summary> /// <param name="sender">The file system watcher.</param> /// <param name="e">Event arguments.</param> private void EH_FileSystemWatcher_Removed(object sender, FileSystemEventArgs e) { // called by worker thread... lock (Sync) { if (mReloadingTimer == null) { return; // object has been disposed } if (IsConfigurationFile(e.Name)) { // configuration file was removed // => create a default configuration... File = new LogConfigurationFile(); } } }
/// <summary> /// Tries to open the configured configuration file. /// </summary> private void OpenConfigurationFile() { lock (Sync) { // dispose resources associated with the loaded configuration file CloseConfigurationFile(); // load configuration file const int maxRetryCount = 5; for (int retry = 0; retry < maxRetryCount; retry++) { try { File = LogConfigurationFile.LoadFrom(FullPath); break; } catch (FileNotFoundException) { // file does not exist // => that's ok, use a default configuration file... File = new LogConfigurationFile(); break; } catch (IOException) { // there is something wrong at a lower level, most probably a sharing violation // => just try again... if (retry + 1 >= maxRetryCount) { throw; } Thread.Sleep(10); } catch (Exception ex) { // a severe error that cannot be fixed here // => abort sLog.ForceWrite( LogLevel.Error, "Loading log configuration file ({0}) failed. Exception: {1}", FullPath, ex); throw; } } // set up the file system watcher to get notified of changes to the file // (notifications about the creation of files with zero-length do not contain // valuable information, so renaming/deleting is sufficient) mFileSystemWatcher = new FileSystemWatcher { Path = System.IO.Path.GetDirectoryName(FullPath), Filter = "*" + System.IO.Path.GetExtension(mFileName) }; mFileSystemWatcher.Changed += EH_FileSystemWatcher_Changed; mFileSystemWatcher.Deleted += EH_FileSystemWatcher_Removed; mFileSystemWatcher.Renamed += EH_FileSystemWatcher_Renamed; mFileSystemWatcher.EnableRaisingEvents = true; // set up timer that will handle reloading the configuration file mReloadingTimer = new Timer(TimerProc, null, -1, -1); // do not start immediately } }
/// <summary> /// Saves the configuration. /// </summary> /// <param name="includeDefaults"> /// <c>true</c> to include the default value of settings that have not been explicitly set; /// <c>false</c> to save only settings that have not been explicitly set. /// </param> public override void Save(bool includeDefaults = false) { lock (Sync) { // save the configuration file before making modifications var oldFile = File; try { if (includeDefaults) { // create a temporary configuration file and add the default value of the settings that don't // have an explicitly set value File = new LogConfigurationFile(oldFile); foreach (var stageSettings in mProcessingPipelineConfiguration.Stages) { foreach (var setting in stageSettings.Values) { if (!setting.HasValue) { setting.Value = setting.DefaultValue; } } } } const int maxRetryCount = 5; for (int retry = 0; retry < maxRetryCount; retry++) { try { File.Save(FullPath); } catch (IOException) { // there is something wrong at a lower level, most probably a sharing violation // => just try again... if (retry + 1 >= maxRetryCount) { throw; } Thread.Sleep(10); } catch (Exception ex) { // a severe error that cannot be fixed here // => abort sLog.ForceWrite( LogLevel.Error, "Loading log configuration file ({0}) failed. Exception: {1}", FullPath, ex); throw; } } } finally { // revert to using the old configuration file File = oldFile; } } }