/// <summary> /// Ensures that the <see cref="Default"/> GrandOutput is created and that any <see cref="ActivityMonitor"/> that will be created in this /// application domain will automatically have a <see cref="GrandOutputClient"/> registered for this Default GrandOutput. /// If the Default is already initialized, the <paramref name="configuration"/> is applied. /// </summary> /// <param name="configuration"> /// Configuration to apply to the default GrandOutput. /// When null, a default configuration with a <see cref="Handlers.TextFileConfiguration"/> in a "Text" path is configured. /// </param> /// <param name="clearExistingTraceListeners"> /// If the <see cref="Default"/> is actually instantiated, existing <see cref="Trace.Listeners"/> /// are cleared before registering a <see cref="MonitorTraceListener"/> associated to this default grand output. /// See remarks. /// </param> /// <returns>The Default GrandOutput that has been created or reconfigured.</returns> /// <remarks> /// <para> /// This method is thread-safe (a simple lock protects it) and uses a <see cref="ActivityMonitor.AutoConfiguration"/> action /// that uses <see cref="EnsureGrandOutputClient(IActivityMonitor)"/> on newly created ActivityMonitor. /// </para> /// <para> /// The Default GrandOutput also adds a <see cref="MonitorTraceListener"/> in the <see cref="Trace.Listeners"/> collection that /// has <see cref="MonitorTraceListener.FailFast"/> sets to false: <see cref="MonitoringFailFastException"/> are thrown instead of /// calling <see cref="Environment.FailFast(string)"/>. /// If this behavior must be changed, please exploit the <see cref="Trace.Listeners"/> that is a <see cref="TraceListenerCollection"/>, wide open /// to any modifications, and the fact that <see cref="MonitorTraceListener"/> exposes its associated grand output and /// that <see cref="MonitorTraceListener.FailFast"/> property can be changed at any time. /// </para> /// <para> /// The GrandOutput.Default can safely be <see cref="Dispose()"/> at any time: disposing the Default /// sets it to null. /// </para> /// </remarks> static public GrandOutput EnsureActiveDefault(GrandOutputConfiguration?configuration = null, bool clearExistingTraceListeners = true) { lock ( _defaultLock ) { if (_default == null) { if (configuration == null) { configuration = new GrandOutputConfiguration() .AddHandler(new Handlers.TextFileConfiguration() { Path = "Text" }); configuration.InternalClone = true; } _default = new GrandOutput(true, configuration); ActivityMonitor.AutoConfiguration += AutoRegisterDefault; _traceListener = new MonitorTraceListener(_default, failFast: false); if (clearExistingTraceListeners) { Trace.Listeners.Clear(); } Trace.Listeners.Add(_traceListener); } else if (configuration != null) { _default.ApplyConfiguration(configuration, true); } } return(_default); }
public void ConfigObjectAttributeRequired() { GrandOutputConfiguration c = new GrandOutputConfiguration(); Assert.That( c.Load( XDocument.Parse( @"<GrandOutputConfiguration><Add /></GrandOutputConfiguration>" ).Root, TestHelper.ConsoleMonitor ), Is.False ); Assert.That( c.Load( XDocument.Parse( @"<GrandOutputConfiguration><Channel><Add Type=""BinaryFile"" /></Channel></GrandOutputConfiguration>" ).Root, TestHelper.ConsoleMonitor ), Is.False ); Assert.That( c.Load( XDocument.Parse( @"<GrandOutputConfiguration><Channel><Add Type=""BinaryFile"" Name=""GlobalCatch"" /></Channel></GrandOutputConfiguration>" ).Root, TestHelper.ConsoleMonitor ), Is.False ); // This is okay: Type, Name and Path for BinaryFile. Assert.That( c.Load( XDocument.Parse( @"<GrandOutputConfiguration><Channel><Add Type=""BinaryFile"" Name=""GlobalCatch"" Path=""In-Root-Log-Path"" /></Channel></GrandOutputConfiguration>" ).Root, TestHelper.ConsoleMonitor ) ); }
/// <summary> /// Applies a configuration. /// This is thread safe and can be called at any moment. /// </summary> /// <param name="configuration">The configuration to apply.</param> /// <param name="waitForApplication"> /// True to block until this configuration has been applied. /// Note that another (new) configuration may have already replaced the given configuration /// once this call ends. /// </param> public void ApplyConfiguration(GrandOutputConfiguration configuration, bool waitForApplication = false) { Throw.CheckNotNullArgument(configuration); if (!configuration.InternalClone) { configuration = configuration.Clone(); configuration.InternalClone = true; } _sink.ApplyConfiguration(configuration, waitForApplication); }
/// <summary> /// Ensures that the <see cref="Default"/> GrandOutput is created (see <see cref="EnsureActiveDefault"/>) and configured with default settings: /// only one one channel with its minimal filter sets to Debug with one text file handler that writes .txt files in "<see cref="SystemActivityMonitor.RootLogPath"/>\GrandOutputDefault" directory. /// The <see cref="SystemActivityMonitor.RootLogPath"/> must be valid and if a GrandOutput.config file exists inside, it is loaded as the configuration. /// If it exists, it must be valid (otherwise an exception is thrown). /// Once loaded, the file is monitored and any change that occurs to it dynamically triggers a <see cref="SetConfiguration"/> with the new file. /// </summary> /// <param name="monitor">An optional monitor.</param> static public GrandOutput EnsureActiveDefaultWithDefaultSettings(IActivityMonitor monitor = null) { lock ( _defaultLock ) { if (_default == null) { if (monitor == null) { monitor = new SystemActivityMonitor(true, "GrandOutput") { MinimalFilter = GrandOutputMinimalFilter } } ; using (monitor.OpenGroup(LogLevel.Info, "Attempting Default GrandOutput configuration.", null)) { try { SystemActivityMonitor.AssertRootLogPathIsSet(); _configPath = SystemActivityMonitor.RootLogPath + "GrandOutput.config"; GrandOutputConfiguration def = CreateDefaultConfig(); if (!File.Exists(_configPath)) { File.WriteAllText(_configPath, _defaultConfig); } if (!def.LoadFromFile(_configPath, monitor)) { throw new CKException("Unable to load Configuration file: '{0}'.", _configPath); } GrandOutput output = new GrandOutput(); if (!output.SetConfiguration(def, monitor)) { throw new CKException("Failed to set Configuration."); } StartMonitoring(monitor); _default = output; ActivityMonitor.AutoConfiguration += m => _default.Register(m); } catch (Exception ex) { monitor.SendLine(LogLevel.Fatal, null, ex); throw; } } } } return(_default); } const string _defaultConfig = @"<GrandOutputConfiguration> <Channel MinimalFilter=""Debug""> <Add Type=""TextFile"" Name=""All"" Path=""GrandOutputDefault"" MaxCountPerFile=""20000"" /> </Channel> </GrandOutputConfiguration>";
static GrandOutputConfiguration CreateDefaultConfig() { GrandOutputConfiguration def = new GrandOutputConfiguration(); Debug.Assert( def.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.None ); Debug.Assert( def.GlobalDefaultFilter == null ); var route = new RouteConfiguration(); route.ConfigData = new GrandOutputChannelConfigData() { MinimalFilter = LogFilter.Debug }; route.AddAction( new TextFileConfiguration( "All" ) { Path = "GrandOutputDefault" } ); def.ChannelsConfiguration = route; return def; }
/// <summary> /// Clones this configuration. /// </summary> /// <returns>Clone of this configuration.</returns> public GrandOutputConfiguration Clone() { var c = new GrandOutputConfiguration { TimerDuration = TimerDuration, ExternalLogLevelFilter = ExternalLogLevelFilter, MinimalFilter = MinimalFilter, TrackUnhandledExceptions = TrackUnhandledExceptions }; c.Handlers.AddRange(Handlers.Select(h => h.Clone())); return(c); }
static GrandOutputConfiguration CreateDefaultConfig() { GrandOutputConfiguration def = new GrandOutputConfiguration(); Debug.Assert(def.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.None); Debug.Assert(def.GlobalDefaultFilter == null); var route = new RouteConfiguration(); route.ConfigData = new GrandOutputChannelConfigData() { MinimalFilter = LogFilter.Debug }; route.AddAction(new TextFileConfiguration("All") { Path = "GrandOutputDefault" }); def.ChannelsConfiguration = route; return(def); }
public void ApplyConfigWithError() { GrandOutputConfiguration c = new GrandOutputConfiguration(); Assert.That( c.Load( XDocument.Parse( @" <GrandOutputConfiguration AppDomainDefaultFilter=""Release"" > <Channel> <Add Type=""BinaryFile"" Name=""GlobalCatch"" Path=""Configuration/ invalid path? (? is forbidden)"" /> </Channel> </GrandOutputConfiguration>" ).Root, TestHelper.ConsoleMonitor ) ); Assert.That( c.ChannelsConfiguration.Configurations.Count, Is.EqualTo( 1 ) ); GrandOutput g = new GrandOutput(); Assert.That( g.SetConfiguration( c, TestHelper.ConsoleMonitor ), Is.False ); Assert.That( g.IsDisposed, Is.False ); g.Dispose( TestHelper.ConsoleMonitor ); Assert.That( g.IsDisposed ); }
/// <summary> /// Attempts to set a new configuration. /// </summary> /// <param name="config">The configuration that must be set.</param> /// <param name="monitor">Optional monitor.</param> /// <param name="millisecondsBeforeForceClose">Optional timeout to wait before forcing the close of the currently active configuration.</param> /// <returns>True on success.</returns> public bool SetConfiguration(GrandOutputConfiguration config, IActivityMonitor monitor = null, int millisecondsBeforeForceClose = Timeout.Infinite) { if (config == null) { throw new ArgumentNullException("config"); } if (monitor == null) { monitor = new SystemActivityMonitor(true, "GrandOutput") { MinimalFilter = GrandOutputMinimalFilter } } ; using (monitor.OpenGroup(LogLevel.Info, this == Default ? "Applying Default GrandOutput configuration." : "Applying GrandOutput configuration.", null)) { if (_channelHost.SetConfiguration(monitor, config.ChannelsConfiguration ?? new RouteConfiguration(), millisecondsBeforeForceClose)) { if (this == _default && config.GlobalDefaultFilter.HasValue) { ActivityMonitor.DefaultFilter = config.GlobalDefaultFilter.Value; } if (config.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.Clear || config.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.ClearThenApply) { ActivityMonitor.SourceFilter.ClearOverrides(); } if (config.SourceOverrideFilter != null && (config.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.Apply || config.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.ClearThenApply)) { foreach (var k in config.SourceOverrideFilter) { ActivityMonitor.SourceFilter.SetOverrideFilter(k.Value, k.Key); } } monitor.CloseGroup("Success."); return(true); } return(false); } }
GrandOutput(bool isDefault, GrandOutputConfiguration config) { if (config == null) { throw new ArgumentNullException(nameof(config)); } // Creates the identity card and the client list first. _identityCard = new IdentityCard(); _clients = new List <WeakReference <GrandOutputClient> >(); _minimalFilter = LogFilter.Undefined; // Starts the pump thread. Its monitor will be registered // in this GrandOutput. _sink = new DispatcherSink(m => DoEnsureGrandOutputClient(m), _identityCard, config.TimerDuration ?? TimeSpan.FromMilliseconds(500), TimeSpan.FromMinutes(5), DoGarbageDeadClients, OnFiltersChanged, isDefault); ApplyConfiguration(config, waitForApplication: true); ActivityMonitor.StaticLogger.OnStaticLog += _sink.ExternalOrStaticLog; }
/// <summary> /// Attempts to set a new configuration. /// </summary> /// <param name="config">The configuration that must be set.</param> /// <param name="monitor">Optional monitor.</param> /// <param name="millisecondsBeforeForceClose">Optional timeout to wait before forcing the close of the currently active configuration.</param> /// <returns>True on success.</returns> public bool SetConfiguration( GrandOutputConfiguration config, IActivityMonitor monitor = null, int millisecondsBeforeForceClose = Timeout.Infinite ) { if( config == null ) throw new ArgumentNullException( "config" ); if( monitor == null ) monitor = new SystemActivityMonitor( true, "GrandOutput" ) { MinimalFilter = GrandOutputMinimalFilter }; using( monitor.OpenGroup( LogLevel.Info, this == Default ? "Applying Default GrandOutput configuration." : "Applying GrandOutput configuration.", null ) ) { if( _channelHost.SetConfiguration( monitor, config.ChannelsConfiguration ?? new RouteConfiguration(), millisecondsBeforeForceClose ) ) { if( this == _default && config.GlobalDefaultFilter.HasValue ) ActivityMonitor.DefaultFilter = config.GlobalDefaultFilter.Value; if( config.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.Clear || config.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.ClearThenApply ) { ActivityMonitor.SourceFilter.ClearOverrides(); } if( config.SourceOverrideFilter != null && (config.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.Apply || config.SourceOverrideFilterApplicationMode == SourceFilterApplyMode.ClearThenApply) ) { foreach( var k in config.SourceOverrideFilter ) ActivityMonitor.SourceFilter.SetOverrideFilter( k.Value, k.Key ); } monitor.CloseGroup( "Success." ); return true; } return false; } }
public void ApplyConfigSimple() { GrandOutputConfiguration c = new GrandOutputConfiguration(); Assert.That( c.Load( XDocument.Parse( @" <GrandOutputConfiguration AppDomainDefaultFilter=""Release"" > <Channel MinimalFilter=""{Trace,Info}""> <Add Type=""BinaryFile"" Name=""GlobalCatch"" Path=""Configuration/ApplyConfig"" /> </Channel> </GrandOutputConfiguration>" ).Root, TestHelper.ConsoleMonitor ) ); Assert.That( c.ChannelsConfiguration.Configurations.Count, Is.EqualTo( 1 ) ); ActivityMonitor m = new ActivityMonitor( false ); using( GrandOutput g = new GrandOutput() ) { m.Info().Send( "Before Registering - NOSHOW" ); g.Register( m ); m.Info().Send( "Before configuration - NOSHOW" ); Assert.That( g.SetConfiguration( c, TestHelper.ConsoleMonitor ) ); m.Info().Send( "After configuration. INFO1" ); Assert.That( m.ActualFilter, Is.EqualTo( new LogFilter( LogLevelFilter.Trace, LogLevelFilter.Info ) ) ); m.Trace().Send( "TRACE1-NOSHOW (MinimalFilter of the Channel)." ); Assert.That( g.SetConfiguration( new GrandOutputConfiguration(), TestHelper.ConsoleMonitor ) ); g.Dispose( TestHelper.ConsoleMonitor ); m.Info().Send( "After disposing - NOSHOW." ); Assert.That( m.ActualFilter, Is.EqualTo( LogFilter.Undefined ) ); } }
public void InvalidRootNode() { GrandOutputConfiguration c = new GrandOutputConfiguration(); Assert.That( c.Load( XDocument.Parse( @"<root><Add Type=""BinaryFile"" /></root>" ).Root, TestHelper.ConsoleMonitor ), Is.False ); }
/// <summary> /// Initializes a new <see cref="GrandOutput"/>. /// </summary> /// <param name="config">The configuration.</param> public GrandOutput(GrandOutputConfiguration config) : this(false, config) { }