22using System . Collections . Generic ;
33using System . Linq ;
44using Microsoft . Extensions . Configuration ;
5+ using NLog . Common ;
56using NLog . Config ;
67
78namespace NLog . Extensions . Logging
@@ -11,10 +12,12 @@ namespace NLog.Extensions.Logging
1112 /// </summary>
1213 public class NLogLoggingConfiguration : LoggingConfigurationParser
1314 {
14- private readonly Action < object > _reloadConfiguration ;
15+ private readonly IConfigurationSection _originalConfigSection ;
16+ private bool _autoReload ;
17+ private Action < object > _reloadConfiguration ;
1518
1619 /// <summary>
17- /// Initializes a new instance of the <see cref="NLogLoggingConfiguration"/> class.
20+ /// Initializes a new instance of the <see cref="NLogLoggingConfiguration" /> class.
1821 /// </summary>
1922 /// <param name="nlogConfig">Configuration section to be read</param>
2023 public NLogLoggingConfiguration ( IConfigurationSection nlogConfig )
@@ -23,106 +26,294 @@ public NLogLoggingConfiguration(IConfigurationSection nlogConfig)
2326 }
2427
2528 /// <summary>
26- /// Initializes a new instance of the <see cref="NLogLoggingConfiguration"/> class.
29+ /// Initializes a new instance of the <see cref="NLogLoggingConfiguration" /> class.
2730 /// </summary>
2831 /// <param name="nlogConfig">Configuration section to be read</param>
29- /// <param name="logFactory">The <see cref="LogFactory"/> to which to apply any applicable configuration values.</param>
32+ /// <param name="logFactory">The <see cref="LogFactory" /> to which to apply any applicable configuration values.</param>
3033 public NLogLoggingConfiguration ( IConfigurationSection nlogConfig , LogFactory logFactory )
3134 : base ( logFactory )
3235 {
33- _reloadConfiguration = ( state ) => LoadConfigurationSection ( ( IConfigurationSection ) state , true ) ;
34- LoadConfigurationSection ( nlogConfig , null ) ;
36+ _originalConfigSection = nlogConfig ;
37+ _autoReload = LoadConfigurationSection ( nlogConfig ) ;
3538 }
3639
37- private void LoadConfigurationSection ( IConfigurationSection nlogConfig , bool ? autoReload )
40+ /// <summary>
41+ /// Gets the collection of file names which should be watched for changes by NLog.
42+ /// </summary>
43+ public override IEnumerable < string > FileNamesToWatch
44+ {
45+ get
46+ {
47+ if ( _autoReload && _reloadConfiguration == null )
48+ {
49+ // Prepare for setting up reload notification handling
50+ _reloadConfiguration = state => ReloadConfigurationSection ( ( IConfigurationSection ) state ) ;
51+ LogFactory . ConfigurationChanged += LogFactory_ConfigurationChanged ;
52+ }
53+
54+ return Enumerable . Empty < string > ( ) ;
55+ }
56+ }
57+
58+ /// <inheritdoc />
59+ public override LoggingConfiguration Reload ( )
60+ {
61+ return new NLogLoggingConfiguration ( _originalConfigSection , LogFactory ) ;
62+ }
63+
64+ private bool LoadConfigurationSection ( IConfigurationSection nlogConfig )
3865 {
39- var configElement = new LoggingConfigurationElement ( nlogConfig , true ) ;
66+ var configElement = new LoggingConfigurationElement ( nlogConfig , new LoggingConfigurationElementContext ( ) , true ) ;
4067 LoadConfig ( configElement , null ) ;
41- if ( autoReload ?? configElement . AutoReload )
68+ return configElement . AutoReload ;
69+ }
70+
71+ private void LogFactory_ConfigurationChanged ( object sender , LoggingConfigurationChangedEventArgs e )
72+ {
73+ if ( ReferenceEquals ( e . DeactivatedConfiguration , this ) )
74+ {
75+ if ( _autoReload )
76+ {
77+ _autoReload = false ; // Cannot unsubscribe to reload event, but we can stop reacting to it
78+ LogFactory . ConfigurationChanged -= LogFactory_ConfigurationChanged ;
79+ }
80+ }
81+ else if ( ReferenceEquals ( e . ActivatedConfiguration , this ) && _autoReload && _reloadConfiguration != null )
82+ {
83+ // Setup reload notification
84+ LogFactory . ConfigurationChanged += LogFactory_ConfigurationChanged ;
85+ MonitorForReload ( _originalConfigSection ) ;
86+ }
87+ }
88+
89+ private void ReloadConfigurationSection ( IConfigurationSection nlogConfig )
90+ {
91+ try
92+ {
93+ if ( ! _autoReload )
94+ {
95+ return ; // Should no longer react to reload events
96+ }
97+
98+ InternalLogger . Info ( "Reloading NLogLoggingConfiguration..." ) ;
99+ var newConfig = new NLogLoggingConfiguration ( nlogConfig , LogFactory ) ;
100+ if ( LogFactory . Configuration != null )
101+ {
102+ LogFactory . Configuration = newConfig ;
103+ }
104+ }
105+ catch ( Exception ex )
42106 {
43- nlogConfig . GetReloadToken ( ) . RegisterChangeCallback ( _reloadConfiguration , nlogConfig ) ;
107+ InternalLogger . Warn ( ex , "NLogLoggingConfiguration failed to reload" ) ;
108+ MonitorForReload ( nlogConfig ) ; // Continue watching this file
44109 }
45110 }
46111
112+ private void MonitorForReload ( IConfigurationSection nlogConfig )
113+ {
114+ nlogConfig . GetReloadToken ( ) . RegisterChangeCallback ( _reloadConfiguration , nlogConfig ) ;
115+ }
116+
117+ private class LoggingConfigurationElementContext
118+ {
119+ public IConfigurationSection DefaultTargetParametersSection ;
120+ public IConfigurationSection DefaultWrapperSection ;
121+ }
122+
47123 private class LoggingConfigurationElement : ILoggingConfigurationElement
48124 {
49- private const string VariablesKey = "Variables" ;
50- private const string VariableKey = "Variable" ;
51125 private const string TargetKey = "target" ;
126+ private const string DefaultTargetParameters = "Default-target-parameters" ;
127+ private const string VariableKey = "Variable" ;
128+ private const string DefaultWrapper = "Default-wrapper" ;
52129 private readonly IConfigurationSection _configurationSection ;
130+ private readonly LoggingConfigurationElementContext _context ;
53131 private readonly string _nameOverride ;
54132 private readonly bool _topElement ;
55133
56- public string Name => _nameOverride ?? _configurationSection . Key ;
57- public IEnumerable < KeyValuePair < string , string > > Values => GetValues ( ) ;
58- public IEnumerable < ILoggingConfigurationElement > Children => GetChildren ( ) ;
59- public bool AutoReload { get ; }
60-
61- public LoggingConfigurationElement ( IConfigurationSection configurationSection , bool topElement , string nameOverride = null )
134+ public LoggingConfigurationElement ( IConfigurationSection configurationSection , LoggingConfigurationElementContext context , bool topElement , string nameOverride = null )
62135 {
63136 _configurationSection = configurationSection ;
137+ _context = context ;
64138 _nameOverride = nameOverride ;
65139 _topElement = topElement ;
66- if ( topElement )
140+ if ( topElement && bool . TryParse ( configurationSection [ "autoreload" ] , out var autoreload ) )
67141 {
68- if ( bool . TryParse ( configurationSection [ "autoreload" ] , out var autoreload ) )
69- {
70- AutoReload = autoreload ;
71- }
142+ AutoReload = autoreload ;
72143 }
73144 }
74145
146+ public bool AutoReload { get ; }
147+
148+ public string Name => _nameOverride ?? _configurationSection . Key ;
149+ public IEnumerable < KeyValuePair < string , string > > Values => GetValues ( ) ;
150+ public IEnumerable < ILoggingConfigurationElement > Children => GetChildren ( ) ;
151+
75152 private IEnumerable < KeyValuePair < string , string > > GetValues ( )
76153 {
77154 var children = _configurationSection . GetChildren ( ) ;
78155 foreach ( var child in children )
79156 {
80157 if ( ! child . GetChildren ( ) . Any ( ) )
158+ {
81159 yield return new KeyValuePair < string , string > ( child . Key , child . Value ) ;
160+ }
82161 }
162+
83163 if ( _nameOverride != null )
84164 {
85- yield return new KeyValuePair < string , string > ( "name" , _configurationSection . Key ) ;
165+ if ( ReferenceEquals ( _nameOverride , DefaultTargetParameters ) )
166+ {
167+ yield return new KeyValuePair < string , string > ( "type" , _configurationSection . Key ) ;
168+ }
169+ else
170+ {
171+ yield return new KeyValuePair < string , string > ( "name" , _configurationSection . Key ) ;
172+ }
173+
86174 if ( ReferenceEquals ( _nameOverride , VariableKey ) )
175+ {
87176 yield return new KeyValuePair < string , string > ( "value" , _configurationSection . Value ) ;
177+ }
88178 }
89179 }
90180
91181 private IEnumerable < ILoggingConfigurationElement > GetChildren ( )
92182 {
93- var variables = _topElement ? _configurationSection . GetSection ( VariablesKey ) : null ;
183+ var variables = GetVariablesSection ( ) ;
94184 if ( variables != null )
95185 {
96186 foreach ( var variable in variables . GetChildren ( ) )
97- yield return new LoggingConfigurationElement ( variable , false , VariableKey ) ;
187+ {
188+ yield return new LoggingConfigurationElement ( variable , _context , false , VariableKey ) ;
189+ }
190+ }
191+
192+ var targetsSection = ! _topElement && _nameOverride == null && _configurationSection . Key . EqualsOrdinalIgnoreCase ( "targets" ) ;
193+ var defaultWrapper = GetDefaultWrapperSection ( ) ;
194+ if ( defaultWrapper != null )
195+ {
196+ _context . DefaultWrapperSection = defaultWrapper ;
197+ }
198+
199+ var defaultTargetParameters = GetDefaultTargetParametersSection ( ) ;
200+ if ( defaultTargetParameters != null )
201+ {
202+ _context . DefaultTargetParametersSection = defaultTargetParameters ;
203+ }
204+ if ( targetsSection )
205+ {
206+ foreach ( var loggingConfigurationElement in YieldCapturedContextSections ( ) )
207+ {
208+ yield return loggingConfigurationElement ;
209+ }
98210 }
99211
100212 var children = _configurationSection . GetChildren ( ) ;
101213 foreach ( var child in children )
102214 {
103215 var firstChildValue = child ? . GetChildren ( ) ? . FirstOrDefault ( ) ;
104216 if ( firstChildValue == null )
105- continue ; // Simple value without children
217+ {
218+ continue ; // Simple value without children
219+ }
106220
107- if ( _nameOverride == TargetKey && child . Key . EqualsOrdinalIgnoreCase ( TargetKey ) && child . GetChildren ( ) . Count ( ) == 1 )
221+ if ( IsTargetWithinWrapper ( child ) )
108222 {
109- // Target-config inside Wrapper-Target
110- yield return new LoggingConfigurationElement ( firstChildValue , false , TargetKey ) ;
223+ yield return new LoggingConfigurationElement ( firstChildValue , _context , false , TargetKey ) ;
111224 }
112225 else
113226 {
114- if ( variables != null && string . Equals ( child . Key , VariablesKey , StringComparison . OrdinalIgnoreCase ) )
227+ string nameOverride = null ;
228+ if ( AlreadReadChild ( child , variables , defaultWrapper , defaultTargetParameters ) )
229+ {
115230 continue ;
231+ }
116232
117- string nameOverride = null ;
118- if ( _configurationSection . Key . EqualsOrdinalIgnoreCase ( "targets" ) )
233+ if ( targetsSection )
119234 {
120235 nameOverride = TargetKey ;
121236 }
122- yield return new LoggingConfigurationElement ( child , false , nameOverride ) ;
237+
238+ yield return new LoggingConfigurationElement ( child , _context , false , nameOverride ) ;
123239 }
124240 }
125241 }
242+
243+ private IEnumerable < ILoggingConfigurationElement > YieldCapturedContextSections ( )
244+ {
245+ if ( _context . DefaultWrapperSection != null )
246+ {
247+ yield return new LoggingConfigurationElement ( _context . DefaultWrapperSection , _context , true , DefaultWrapper ) ;
248+ _context . DefaultWrapperSection = null ;
249+ }
250+
251+ if ( _context . DefaultTargetParametersSection != null )
252+ {
253+ foreach ( var targetParameters in _context . DefaultTargetParametersSection . GetChildren ( ) )
254+ {
255+ yield return new LoggingConfigurationElement ( targetParameters , _context , true , DefaultTargetParameters ) ;
256+ }
257+
258+ _context . DefaultTargetParametersSection = null ;
259+ }
260+ }
261+
262+ /// <summary>
263+ /// Target-config inside Wrapper-Target
264+ /// </summary>
265+ /// <param name="child"></param>
266+ /// <returns></returns>
267+ private bool IsTargetWithinWrapper ( IConfigurationSection child )
268+ {
269+ return _nameOverride == TargetKey && child . Key . EqualsOrdinalIgnoreCase ( TargetKey ) && child . GetChildren ( ) . Count ( ) == 1 ;
270+ }
271+
272+ private bool AlreadReadChild ( IConfigurationSection child , IConfigurationSection variables , IConfigurationSection defaultWrapper , IConfigurationSection defaultTargetParameters )
273+ {
274+ if ( variables != null && child . Key . EqualsOrdinalIgnoreCase ( variables . Key ) )
275+ {
276+ return true ;
277+ }
278+
279+ if ( _topElement )
280+ {
281+ if ( defaultWrapper != null && child . Key . EqualsOrdinalIgnoreCase ( defaultWrapper . Key ) )
282+ {
283+ return true ;
284+ }
285+
286+ if ( defaultTargetParameters != null && child . Key . EqualsOrdinalIgnoreCase ( defaultTargetParameters . Key ) )
287+ {
288+ return true ;
289+ }
290+ }
291+
292+ return false ;
293+ }
294+
295+ private IConfigurationSection GetVariablesSection ( )
296+ {
297+ var variables = _topElement ? _configurationSection . GetSection ( "Variables" ) : null ;
298+ return variables ;
299+ }
300+
301+ private IConfigurationSection GetDefaultTargetParametersSection ( )
302+ {
303+ var defaultTargetParameters = _topElement ? _configurationSection . GetSection ( DefaultTargetParameters ) : null ;
304+ return defaultTargetParameters ;
305+ }
306+
307+ private IConfigurationSection GetDefaultWrapperSection ( )
308+ {
309+ var defaultWrapper = _topElement ? _configurationSection . GetSection ( DefaultWrapper ) : null ;
310+ if ( defaultWrapper != null && defaultWrapper . GetChildren ( ) . Any ( ) )
311+ {
312+ return defaultWrapper ;
313+ }
314+
315+ return null ;
316+ }
126317 }
127318 }
128- }
319+ }
0 commit comments