@@ -48,7 +48,7 @@ public class ScriptHost : JobHost
4848 private static readonly string [ ] WellKnownHostJsonProperties = new [ ]
4949 {
5050 "id" , "functionTimeout" , "http" , "watchDirectories" , "functions" , "queues" , "serviceBus" ,
51- "eventHub" , "tracing" , "singleton" , "logger" , "aggregator" , "applicationInsights"
51+ "eventHub" , "tracing" , "singleton" , "logger" , "aggregator" , "applicationInsights" , "healthMonitor"
5252 } ;
5353
5454 private string _instanceId ;
@@ -100,6 +100,8 @@ protected internal ScriptHost(IScriptHostEnvironment environment,
100100 _proxyClient = proxyClient ;
101101 }
102102
103+ public event EventHandler HostInitializing ;
104+
103105 public event EventHandler HostInitialized ;
104106
105107 public event EventHandler HostStarted ;
@@ -269,7 +271,10 @@ internal static void AddFunctionError(Dictionary<string, Collection<string>> fun
269271 await base . CallAsync ( method , arguments , cancellationToken ) ;
270272 }
271273
272- protected virtual void Initialize ( )
274+ /// <summary>
275+ /// Performs all required initialization on the host. Must be called before the host is started.
276+ /// </summary>
277+ public void Initialize ( )
273278 {
274279 FileUtility . EnsureDirectoryExists ( ScriptConfig . RootScriptPath ) ;
275280 string hostLogPath = Path . Combine ( ScriptConfig . RootLogPath , "Host" ) ;
@@ -298,19 +303,6 @@ protected virtual void Initialize()
298303 _hostConfig . UseDevelopmentSettings ( ) ;
299304 }
300305
301- // Ensure we always have an ILoggerFactory,
302- // regardless of whether AppInsights is registered or not
303- if ( _hostConfig . LoggerFactory == null )
304- {
305- _hostConfig . LoggerFactory = new LoggerFactory ( ) ;
306-
307- // If we've created the LoggerFactory, then we are responsible for
308- // disposing. Store this locally for disposal later. We can't rely
309- // on accessing this directly from ScriptConfig.HostConfig as the
310- // ScriptConfig is re-used for every host.
311- _loggerFactory = _hostConfig . LoggerFactory ;
312- }
313-
314306 Func < string , FunctionDescriptor > funcLookup = ( name ) => this . GetFunctionOrNull ( name ) ;
315307 _hostConfig . AddService ( funcLookup ) ;
316308
@@ -349,6 +341,16 @@ protected virtual void Initialize()
349341 TraceWriter = new ConsoleTraceWriter ( hostTraceLevel ) ;
350342 }
351343
344+ // Before configuration has been fully read, configure a default logger factory
345+ // to ensure we can log any configuration errors. There's no filters at this point,
346+ // but that's okay since we can't build filters until we apply configuration below.
347+ // We'll recreate the loggers after config is read. We initialize the public logger
348+ // to the startup logger until we've read configuration settings and can create the real logger.
349+ // The "startup" logger is used in this class for startup related logs. The public logger is used
350+ // for all other logging after startup.
351+ ConfigureLoggerFactory ( ) ;
352+ Logger = _startupLogger = _hostConfig . LoggerFactory . CreateLogger ( LogCategories . Startup ) ;
353+
352354 string readingFileMessage = string . Format ( CultureInfo . InvariantCulture , "Reading host configuration file '{0}'" , hostConfigFilePath ) ;
353355 TraceWriter . Info ( readingFileMessage ) ;
354356
@@ -360,41 +362,29 @@ protected virtual void Initialize()
360362 }
361363 catch ( JsonException ex )
362364 {
363- // If there's a parsing error, set up the logger and write out the previous messages so that they're
364- // discoverable in Application Insights. There's no filter, but that's okay since we cannot parse host.json to
365- // determine how to build the filtler.
366- ConfigureDefaultLoggerFactory ( ) ;
367- ILogger startupErrorLogger = _hostConfig . LoggerFactory . CreateLogger ( LogCategories . Startup ) ;
368- startupErrorLogger . LogInformation ( readingFileMessage ) ;
369-
365+ // If there's a parsing error, write out the previous messages without filters to ensure
366+ // they're logged
367+ _startupLogger . LogInformation ( readingFileMessage ) ;
370368 throw new FormatException ( string . Format ( "Unable to parse {0} file." , ScriptConstants . HostMetadataFileName ) , ex ) ;
371369 }
372370
373371 string sanitizedJson = SanitizeHostJson ( hostConfigObject ) ;
374372 string readFileMessage = $ "Host configuration file read:{ Environment . NewLine } { sanitizedJson } ";
375373 TraceWriter . Info ( readFileMessage ) ;
376374
377- try
378- {
379- ApplyConfiguration ( hostConfigObject , ScriptConfig ) ;
380- }
381- catch ( Exception )
382- {
383- // If we have an error applying the configuration (for example, a value is invalid),
384- // make sure we have a default LoggerFactory so that the error log can be written.
385- ConfigureDefaultLoggerFactory ( ) ;
386- throw ;
387- }
375+ ApplyConfiguration ( hostConfigObject , ScriptConfig ) ;
376+
377+ // now the configuration has been read and applied re-create the logger
378+ // factory and loggers ensuring that filters and settings have been applied
379+ ConfigureLoggerFactory ( recreate : true ) ;
380+ _startupLogger = _hostConfig . LoggerFactory . CreateLogger ( LogCategories . Startup ) ;
381+ Logger = _hostConfig . LoggerFactory . CreateLogger ( ScriptConstants . LogCategoryHostGeneral ) ;
388382
389383 // Allow tests to modify anything initialized by host.json
390384 ScriptConfig . OnConfigurationApplied ? . Invoke ( ScriptConfig ) ;
391385
392- ConfigureDefaultLoggerFactory ( ) ;
393-
394- // Use the startupLogger in this class as it is concerned with startup. The public Logger is used
395- // for all other logging after startup.
396- _startupLogger = _hostConfig . LoggerFactory . CreateLogger ( LogCategories . Startup ) ;
397- Logger = _hostConfig . LoggerFactory . CreateLogger ( ScriptConstants . LogCategoryHostGeneral ) ;
386+ // only after configuration has been applied and loggers have been created, raise the initializing event
387+ HostInitializing ? . Invoke ( this , EventArgs . Empty ) ;
398388
399389 // Do not log these until after all the configuration is done so the proper filters are applied.
400390 _startupLogger . LogInformation ( readingFileMessage ) ;
@@ -645,8 +635,21 @@ private IMetricsLogger CreateMetricsLogger()
645635 return metricsLogger ;
646636 }
647637
648- private void ConfigureDefaultLoggerFactory ( )
638+ private void ConfigureLoggerFactory ( bool recreate = false )
649639 {
640+ // Ensure we always have an ILoggerFactory,
641+ // regardless of whether AppInsights is registered or not
642+ if ( recreate || _hostConfig . LoggerFactory == null )
643+ {
644+ _hostConfig . LoggerFactory = new LoggerFactory ( ) ;
645+
646+ // If we've created the LoggerFactory, then we are responsible for
647+ // disposing. Store this locally for disposal later. We can't rely
648+ // on accessing this directly from ScriptConfig.HostConfig as the
649+ // ScriptConfig is re-used for every host.
650+ _loggerFactory = _hostConfig . LoggerFactory ;
651+ }
652+
650653 ConfigureLoggerFactory ( ScriptConfig , FunctionTraceWriterFactory , _settingsManager , ( ) => FileLoggingEnabled ) ;
651654 }
652655
@@ -801,28 +804,6 @@ private void PurgeOldLogDirectories()
801804 }
802805 }
803806
804- public static ScriptHost Create ( IScriptHostEnvironment environment , IScriptEventManager eventManager ,
805- ScriptHostConfiguration scriptConfig = null , ScriptSettingsManager settingsManager = null , ProxyClientExecutor proxyClient = null )
806- {
807- ScriptHost scriptHost = new ScriptHost ( environment , eventManager , scriptConfig , settingsManager , proxyClient ) ;
808- try
809- {
810- scriptHost . Initialize ( ) ;
811- }
812- catch ( Exception ex )
813- {
814- string errorMsg = "ScriptHost initialization failed" ;
815- scriptHost . TraceWriter ? . Error ( errorMsg , ex ) ;
816-
817- ILogger logger = scriptConfig ? . HostConfig ? . LoggerFactory ? . CreateLogger ( LogCategories . Startup ) ;
818- logger ? . LogError ( 0 , ex , errorMsg ) ;
819-
820- throw ;
821- }
822-
823- return scriptHost ;
824- }
825-
826807 private static Collection < ScriptBindingProvider > LoadBindingProviders ( ScriptHostConfiguration config , JObject hostMetadata , TraceWriter traceWriter , ILogger logger , IEnumerable < string > usedBindingTypes )
827808 {
828809 JobHostConfiguration hostConfig = config . HostConfig ;
@@ -1401,12 +1382,6 @@ internal static void ApplyConfiguration(JObject config, ScriptHostConfiguration
14011382 }
14021383 }
14031384
1404- JToken hostHealthMonitorEnabled = ( JToken ) config [ "hostHealthMonitorEnabled" ] ;
1405- if ( hostHealthMonitorEnabled != null && hostHealthMonitorEnabled . Type == JTokenType . Boolean )
1406- {
1407- scriptConfig . HostHealthMonitorEnabled = ( bool ) hostHealthMonitorEnabled ;
1408- }
1409-
14101385 // Apply Singleton configuration
14111386 JObject configSection = ( JObject ) config [ "singleton" ] ;
14121387 JToken value = null ;
@@ -1434,6 +1409,33 @@ internal static void ApplyConfiguration(JObject config, ScriptHostConfiguration
14341409 }
14351410 }
14361411
1412+ // Apply Host Health Montitor configuration
1413+ configSection = ( JObject ) config [ "healthMonitor" ] ;
1414+ value = null ;
1415+ if ( configSection != null )
1416+ {
1417+ if ( configSection . TryGetValue ( "enabled" , out value ) && value . Type == JTokenType . Boolean )
1418+ {
1419+ scriptConfig . HostHealthMonitor . Enabled = ( bool ) value ;
1420+ }
1421+ if ( configSection . TryGetValue ( "healthCheckInterval" , out value ) )
1422+ {
1423+ scriptConfig . HostHealthMonitor . HealthCheckInterval = TimeSpan . Parse ( ( string ) value , CultureInfo . InvariantCulture ) ;
1424+ }
1425+ if ( configSection . TryGetValue ( "healthCheckWindow" , out value ) )
1426+ {
1427+ scriptConfig . HostHealthMonitor . HealthCheckWindow = TimeSpan . Parse ( ( string ) value , CultureInfo . InvariantCulture ) ;
1428+ }
1429+ if ( configSection . TryGetValue ( "healthCheckThreshold" , out value ) )
1430+ {
1431+ scriptConfig . HostHealthMonitor . HealthCheckThreshold = ( int ) value ;
1432+ }
1433+ if ( configSection . TryGetValue ( "counterThreshold" , out value ) )
1434+ {
1435+ scriptConfig . HostHealthMonitor . CounterThreshold = ( float ) value ;
1436+ }
1437+ }
1438+
14371439 // Apply Tracing/Logging configuration
14381440 configSection = ( JObject ) config [ "tracing" ] ;
14391441 if ( configSection != null )
0 commit comments