Skip to content

Commit 968b322

Browse files
committed
ensuring Loggers are set up after host.json is parsed
1 parent 39e1287 commit 968b322

File tree

7 files changed

+286
-33
lines changed

7 files changed

+286
-33
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using Microsoft.Azure.WebJobs.Logging.ApplicationInsights;
5+
using Microsoft.Azure.WebJobs.Script.Config;
6+
using Microsoft.Azure.WebJobs.Script.Host;
7+
using Microsoft.Extensions.Logging;
8+
9+
namespace Microsoft.Azure.WebJobs.Script.Diagnostics
10+
{
11+
/// <summary>
12+
/// Provides ways to plug into the ScriptHost ILoggerFactory initialization.
13+
/// </summary>
14+
public class DefaultLoggerFactoryBuilder : ILoggerFactoryBuilder
15+
{
16+
/// <summary>
17+
/// Adds additional <see cref="ILoggerProvider"/>s to the <see cref="ILoggerFactory"/>.
18+
/// </summary>
19+
/// <param name="factory">The <see cref="ILoggerFactory"/>.</param>
20+
/// <param name="scriptConfig">The configuration.</param>
21+
public void AddLoggerProviders(ILoggerFactory factory, ScriptHostConfiguration scriptConfig, ScriptSettingsManager settingsManager)
22+
{
23+
IMetricsLogger metricsLogger = scriptConfig.HostConfig.GetService<IMetricsLogger>();
24+
25+
// Automatically register App Insights if the key is present
26+
string instrumentationKey = settingsManager?.GetSetting(ScriptConstants.AppInsightsInstrumentationKey);
27+
if (!string.IsNullOrEmpty(instrumentationKey))
28+
{
29+
metricsLogger?.LogEvent(MetricEventNames.ApplicationInsightsEnabled);
30+
31+
ITelemetryClientFactory clientFactory = scriptConfig.HostConfig.GetService<ITelemetryClientFactory>() ??
32+
new ScriptTelemetryClientFactory(instrumentationKey, scriptConfig.ApplicationInsightsSamplingSettings, scriptConfig.LogFilter.Filter);
33+
34+
scriptConfig.HostConfig.LoggerFactory.AddApplicationInsights(clientFactory);
35+
}
36+
else
37+
{
38+
metricsLogger?.LogEvent(MetricEventNames.ApplicationInsightsDisabled);
39+
}
40+
}
41+
}
42+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using Microsoft.Azure.WebJobs.Script.Config;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace Microsoft.Azure.WebJobs.Script.Diagnostics
8+
{
9+
/// <summary>
10+
/// Provides ways to plug into the ScriptHost ILoggerFactory initialization.
11+
/// </summary>
12+
public interface ILoggerFactoryBuilder
13+
{
14+
/// <summary>
15+
/// Adds additional <see cref="ILoggerProvider"/>s to the <see cref="ILoggerFactory"/>.
16+
/// </summary>
17+
/// <param name="factory">The <see cref="ILoggerFactory"/>.</param>
18+
/// <param name="scriptConfig">The <see cref="ScriptHostConfiguration"/> used by the current <see cref="ScriptHost"/>.</param>
19+
/// <param name="settingsManager">The <see cref="ScriptSettingsManager"/> used by the current <see cref="ScriptHost"/>.</param>
20+
void AddLoggerProviders(ILoggerFactory factory, ScriptHostConfiguration scriptConfig, ScriptSettingsManager settingsManager);
21+
}
22+
}

src/WebJobs.Script/Diagnostics/MetricEventNames.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ public static class MetricEventNames
88
// host level events
99
public const string ApplicationStartLatency = "host.application.start";
1010
public const string HostStartupLatency = "host.startup.latency";
11-
public const string ApplicationInsightsEnabled = "host.applicationinsights";
11+
public const string ApplicationInsightsEnabled = "host.applicationinsights.enabled";
12+
public const string ApplicationInsightsDisabled = "host.applicationinsights.disabled";
1213

1314
// function level events
1415
public const string FunctionInvokeLatency = "function.invoke.latency";

src/WebJobs.Script/GlobalSuppressions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,5 @@
187187
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.FunctionTraceWriterFactory.#Create()")]
188188
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.Description.FunctionInvokerBase.#.ctor(Microsoft.Azure.WebJobs.Script.ScriptHost,Microsoft.Azure.WebJobs.Script.Description.FunctionMetadata)")]
189189
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.FunctionTraceWriterFactory.#CreateTraceWriter(Microsoft.Azure.WebJobs.Script.ScriptHostConfiguration,System.String)")]
190-
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.ScriptHost.#ConfigureLoggerFactory(Microsoft.Azure.WebJobs.Script.ScriptHostConfiguration,Microsoft.Azure.WebJobs.Script.IFunctionTraceWriterFactory,Microsoft.Azure.WebJobs.Script.Config.ScriptSettingsManager,Microsoft.Azure.WebJobs.Script.Diagnostics.IMetricsLogger,System.Func`1<System.Boolean>)")]
190+
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.Diagnostics.DefaultLoggerFactoryBuilder.#AddLoggerProviders(Microsoft.Extensions.Logging.ILoggerFactory,Microsoft.Azure.WebJobs.Script.ScriptHostConfiguration,Microsoft.Azure.WebJobs.Script.Config.ScriptSettingsManager)")]
191+
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.ScriptHost.#ConfigureLoggerFactory(Microsoft.Azure.WebJobs.Script.ScriptHostConfiguration,Microsoft.Azure.WebJobs.Script.IFunctionTraceWriterFactory,Microsoft.Azure.WebJobs.Script.Config.ScriptSettingsManager,System.Func`1<System.Boolean>)")]

src/WebJobs.Script/Host/ScriptHost.cs

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,13 @@
2424
using Microsoft.Azure.WebJobs.Host.Indexers;
2525
using Microsoft.Azure.WebJobs.Host.Listeners;
2626
using Microsoft.Azure.WebJobs.Logging;
27-
using Microsoft.Azure.WebJobs.Logging.ApplicationInsights;
2827
using Microsoft.Azure.WebJobs.Script.Binding;
2928
using Microsoft.Azure.WebJobs.Script.Config;
3029
using Microsoft.Azure.WebJobs.Script.Description;
3130
using Microsoft.Azure.WebJobs.Script.Diagnostics;
3231
using Microsoft.Azure.WebJobs.Script.Eventing;
3332
using Microsoft.Azure.WebJobs.Script.Eventing.File;
3433
using Microsoft.Azure.WebJobs.Script.Extensibility;
35-
using Microsoft.Azure.WebJobs.Script.Host;
3634
using Microsoft.Azure.WebJobs.Script.IO;
3735
using Microsoft.Extensions.Logging;
3836
using Newtonsoft.Json;
@@ -318,22 +316,13 @@ protected virtual void Initialize()
318316
TraceWriter = new ConsoleTraceWriter(hostTraceLevel);
319317
}
320318

321-
ConfigureLoggerFactory(ScriptConfig, FunctionTraceWriterFactory, _settingsManager, metricsLogger, () => FileLoggingEnabled);
322-
323-
// Use the startupLogger in this class as it is concerned with startup. The public Logger is used
324-
// for all other logging after startup.
325-
_startupLogger = hostConfig.LoggerFactory.CreateLogger(LogCategories.Startup);
326-
Logger = hostConfig.LoggerFactory.CreateLogger(ScriptConstants.LogCategoryHostGeneral);
327-
328-
string message = string.Format(CultureInfo.InvariantCulture, "Reading host configuration file '{0}'", hostConfigFilePath);
329-
TraceWriter.Info(message);
330-
_startupLogger?.LogInformation(message);
319+
string readingFileMessage = string.Format(CultureInfo.InvariantCulture, "Reading host configuration file '{0}'", hostConfigFilePath);
320+
TraceWriter.Info(readingFileMessage);
331321

332322
string json = File.ReadAllText(hostConfigFilePath);
333323

334-
message = $"Host configuration file read:{Environment.NewLine}{json}";
335-
TraceWriter.Info(message);
336-
_startupLogger.LogInformation(message);
324+
string readFileMessage = $"Host configuration file read:{Environment.NewLine}{json}";
325+
TraceWriter.Info(readFileMessage);
337326

338327
JObject hostConfigObject;
339328
try
@@ -342,11 +331,30 @@ protected virtual void Initialize()
342331
}
343332
catch (JsonException ex)
344333
{
334+
// If there's a parsing error, set up the logger and write out the previous messages so that they're
335+
// discoverable in Application Insights. There's no filter, but that's okay since we cannot parse host.json to
336+
// determine how to build the filtler.
337+
ConfigureLoggerFactory(ScriptConfig, FunctionTraceWriterFactory, _settingsManager, () => FileLoggingEnabled);
338+
ILogger startupErrorLogger = hostConfig.LoggerFactory.CreateLogger(LogCategories.Startup);
339+
startupErrorLogger.LogInformation(readingFileMessage);
340+
startupErrorLogger.LogInformation(readFileMessage);
341+
345342
throw new FormatException(string.Format("Unable to parse {0} file.", ScriptConstants.HostMetadataFileName), ex);
346343
}
347344

348345
ApplyConfiguration(hostConfigObject, ScriptConfig);
349346

347+
ConfigureLoggerFactory(ScriptConfig, FunctionTraceWriterFactory, _settingsManager, () => FileLoggingEnabled);
348+
349+
// Use the startupLogger in this class as it is concerned with startup. The public Logger is used
350+
// for all other logging after startup.
351+
_startupLogger = hostConfig.LoggerFactory.CreateLogger(LogCategories.Startup);
352+
Logger = hostConfig.LoggerFactory.CreateLogger(ScriptConstants.LogCategoryHostGeneral);
353+
354+
// Do not log these until after all the configuration is done so the proper filters are applied.
355+
_startupLogger.LogInformation(readingFileMessage);
356+
_startupLogger.LogInformation(readFileMessage);
357+
350358
if (string.IsNullOrEmpty(hostConfig.HostId))
351359
{
352360
hostConfig.HostId = Utility.GetDefaultHostId(_settingsManager, ScriptConfig);
@@ -537,23 +545,17 @@ private void LoadExtension(IExtensionConfigProvider instance, string locationHin
537545
}
538546

539547
internal static void ConfigureLoggerFactory(ScriptHostConfiguration scriptConfig, IFunctionTraceWriterFactory traceWriteFactory,
540-
ScriptSettingsManager settingsManager, IMetricsLogger metrics, Func<bool> isFileLoggingEnabled)
548+
ScriptSettingsManager settingsManager, Func<bool> isFileLoggingEnabled)
541549
{
542-
// Register a file logger that only logs user logs and only if file logging is enabled
550+
// Register a file logger that only logs user logs and only if file logging is enabled.
551+
// We don't allow this to be replaced; if you want to disable it, you can use host.json to do so.
543552
scriptConfig.HostConfig.LoggerFactory.AddProvider(new FileLoggerProvider(traceWriteFactory,
544553
(category, level) => (category == LogCategories.Function) && isFileLoggingEnabled()));
545554

546-
// Automatically register App Insights if the key is present
547-
string instrumentationKey = settingsManager?.GetSetting(ScriptConstants.AppInsightsInstrumentationKey);
548-
if (!string.IsNullOrEmpty(instrumentationKey))
549-
{
550-
metrics.LogEvent(MetricEventNames.ApplicationInsightsEnabled);
551-
552-
ITelemetryClientFactory factory = scriptConfig.HostConfig.GetService<ITelemetryClientFactory>() ??
553-
new ScriptTelemetryClientFactory(instrumentationKey, scriptConfig.ApplicationInsightsSamplingSettings, scriptConfig.LogFilter.Filter);
554-
555-
scriptConfig.HostConfig.LoggerFactory.AddApplicationInsights(factory);
556-
}
555+
// Allow a way to plug in custom LoggerProviders.
556+
ILoggerFactoryBuilder builder = scriptConfig.HostConfig.GetService<ILoggerFactoryBuilder>() ??
557+
new DefaultLoggerFactoryBuilder();
558+
builder.AddLoggerProviders(scriptConfig.HostConfig.LoggerFactory, scriptConfig, settingsManager);
557559
}
558560

559561
private void TraceFileChangeRestart(string changeType, string path, bool isShutdown)

src/WebJobs.Script/WebJobs.Script.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,8 @@
481481
<Compile Include="Diagnostics\FunctionStartedEvent.cs" />
482482
<Compile Include="Diagnostics\FunctionTraceWriterFactory.cs" />
483483
<Compile Include="Diagnostics\HostPerformanceManager.cs" />
484+
<Compile Include="Diagnostics\DefaultLoggerFactoryBuilder.cs" />
485+
<Compile Include="Diagnostics\ILoggerFactoryBuilder.cs" />
484486
<Compile Include="Diagnostics\IMetricsLogger.cs" />
485487
<Compile Include="Diagnostics\InterceptingTraceWriter.cs" />
486488
<Compile Include="Diagnostics\IFunctionTraceWriterFactory.cs" />

0 commit comments

Comments
 (0)