@@ -48,7 +48,7 @@ public class ScriptHost : JobHost
48
48
private static readonly string [ ] WellKnownHostJsonProperties = new [ ]
49
49
{
50
50
"id" , "functionTimeout" , "http" , "watchDirectories" , "functions" , "queues" , "serviceBus" ,
51
- "eventHub" , "tracing" , "singleton" , "logger" , "aggregator" , "applicationInsights"
51
+ "eventHub" , "tracing" , "singleton" , "logger" , "aggregator" , "applicationInsights" , "healthMonitor"
52
52
} ;
53
53
54
54
private string _instanceId ;
@@ -100,6 +100,8 @@ protected internal ScriptHost(IScriptHostEnvironment environment,
100
100
_proxyClient = proxyClient ;
101
101
}
102
102
103
+ public event EventHandler HostInitializing ;
104
+
103
105
public event EventHandler HostInitialized ;
104
106
105
107
public event EventHandler HostStarted ;
@@ -269,7 +271,10 @@ internal static void AddFunctionError(Dictionary<string, Collection<string>> fun
269
271
await base . CallAsync ( method , arguments , cancellationToken ) ;
270
272
}
271
273
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 ( )
273
278
{
274
279
FileUtility . EnsureDirectoryExists ( ScriptConfig . RootScriptPath ) ;
275
280
string hostLogPath = Path . Combine ( ScriptConfig . RootLogPath , "Host" ) ;
@@ -298,19 +303,6 @@ protected virtual void Initialize()
298
303
_hostConfig . UseDevelopmentSettings ( ) ;
299
304
}
300
305
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
-
314
306
Func < string , FunctionDescriptor > funcLookup = ( name ) => this . GetFunctionOrNull ( name ) ;
315
307
_hostConfig . AddService ( funcLookup ) ;
316
308
@@ -349,6 +341,16 @@ protected virtual void Initialize()
349
341
TraceWriter = new ConsoleTraceWriter ( hostTraceLevel ) ;
350
342
}
351
343
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
+
352
354
string readingFileMessage = string . Format ( CultureInfo . InvariantCulture , "Reading host configuration file '{0}'" , hostConfigFilePath ) ;
353
355
TraceWriter . Info ( readingFileMessage ) ;
354
356
@@ -360,41 +362,29 @@ protected virtual void Initialize()
360
362
}
361
363
catch ( JsonException ex )
362
364
{
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 ) ;
370
368
throw new FormatException ( string . Format ( "Unable to parse {0} file." , ScriptConstants . HostMetadataFileName ) , ex ) ;
371
369
}
372
370
373
371
string sanitizedJson = SanitizeHostJson ( hostConfigObject ) ;
374
372
string readFileMessage = $ "Host configuration file read:{ Environment . NewLine } { sanitizedJson } ";
375
373
TraceWriter . Info ( readFileMessage ) ;
376
374
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 ) ;
388
382
389
383
// Allow tests to modify anything initialized by host.json
390
384
ScriptConfig . OnConfigurationApplied ? . Invoke ( ScriptConfig ) ;
391
385
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 ) ;
398
388
399
389
// Do not log these until after all the configuration is done so the proper filters are applied.
400
390
_startupLogger . LogInformation ( readingFileMessage ) ;
@@ -645,8 +635,21 @@ private IMetricsLogger CreateMetricsLogger()
645
635
return metricsLogger ;
646
636
}
647
637
648
- private void ConfigureDefaultLoggerFactory ( )
638
+ private void ConfigureLoggerFactory ( bool recreate = false )
649
639
{
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
+
650
653
ConfigureLoggerFactory ( ScriptConfig , FunctionTraceWriterFactory , _settingsManager , ( ) => FileLoggingEnabled ) ;
651
654
}
652
655
@@ -801,28 +804,6 @@ private void PurgeOldLogDirectories()
801
804
}
802
805
}
803
806
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
-
826
807
private static Collection < ScriptBindingProvider > LoadBindingProviders ( ScriptHostConfiguration config , JObject hostMetadata , TraceWriter traceWriter , ILogger logger , IEnumerable < string > usedBindingTypes )
827
808
{
828
809
JobHostConfiguration hostConfig = config . HostConfig ;
@@ -1401,12 +1382,6 @@ internal static void ApplyConfiguration(JObject config, ScriptHostConfiguration
1401
1382
}
1402
1383
}
1403
1384
1404
- JToken hostHealthMonitorEnabled = ( JToken ) config [ "hostHealthMonitorEnabled" ] ;
1405
- if ( hostHealthMonitorEnabled != null && hostHealthMonitorEnabled . Type == JTokenType . Boolean )
1406
- {
1407
- scriptConfig . HostHealthMonitorEnabled = ( bool ) hostHealthMonitorEnabled ;
1408
- }
1409
-
1410
1385
// Apply Singleton configuration
1411
1386
JObject configSection = ( JObject ) config [ "singleton" ] ;
1412
1387
JToken value = null ;
@@ -1434,6 +1409,33 @@ internal static void ApplyConfiguration(JObject config, ScriptHostConfiguration
1434
1409
}
1435
1410
}
1436
1411
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
+
1437
1439
// Apply Tracing/Logging configuration
1438
1440
configSection = ( JObject ) config [ "tracing" ] ;
1439
1441
if ( configSection != null )
0 commit comments