Skip to content

Commit 6479d9f

Browse files
authored
adding more properties to Azure Monitor logs (#6679)
1 parent 8ed990c commit 6479d9f

File tree

4 files changed

+103
-16
lines changed

4 files changed

+103
-16
lines changed

src/WebJobs.Script.WebHost/Diagnostics/AzureMonitorDiagnosticLogger.cs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using System.IO;
78
using Microsoft.Azure.WebJobs.Logging;
9+
using Microsoft.Azure.WebJobs.Script.Configuration;
810
using Microsoft.Extensions.Logging;
11+
using Microsoft.Extensions.Options;
912
using Newtonsoft.Json;
1013

1114
namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics
@@ -15,24 +18,31 @@ public class AzureMonitorDiagnosticLogger : ILogger
1518
internal const string AzureMonitorCategoryName = "FunctionAppLogs";
1619
internal const string AzureMonitorOperationName = "Microsoft.Web/sites/functions/log";
1720

21+
private static int _processId = Process.GetCurrentProcess().Id;
22+
1823
private readonly string _hostVersion = ScriptHost.Version;
1924
private readonly string _regionName;
2025
private readonly string _category;
2126
private readonly string _hostInstanceId;
22-
27+
private readonly string _roleInstance;
2328
private readonly HostNameProvider _hostNameProvider;
29+
private readonly IOptionsMonitor<AppServiceOptions> _appServiceOptions;
2430
private readonly IEventGenerator _eventGenerator;
2531
private readonly IEnvironment _environment;
2632
private readonly IExternalScopeProvider _scopeProvider;
2733

28-
public AzureMonitorDiagnosticLogger(string category, string hostInstanceId, IEventGenerator eventGenerator, IEnvironment environment, IExternalScopeProvider scopeProvider, HostNameProvider hostNameProvider)
34+
public AzureMonitorDiagnosticLogger(string category, string hostInstanceId, IEventGenerator eventGenerator, IEnvironment environment, IExternalScopeProvider scopeProvider,
35+
HostNameProvider hostNameProvider, IOptionsMonitor<AppServiceOptions> appServiceOptions)
2936
{
3037
_category = category ?? throw new ArgumentNullException(nameof(category));
3138
_hostInstanceId = hostInstanceId ?? throw new ArgumentNullException(nameof(hostInstanceId));
3239
_eventGenerator = eventGenerator ?? throw new ArgumentNullException(nameof(eventGenerator));
3340
_environment = environment ?? throw new ArgumentNullException(nameof(environment));
3441
_scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider));
3542
_hostNameProvider = hostNameProvider ?? throw new ArgumentNullException(nameof(hostNameProvider));
43+
_appServiceOptions = appServiceOptions ?? throw new ArgumentNullException(nameof(appServiceOptions));
44+
45+
_roleInstance = _environment.GetInstanceId();
3646

3747
_regionName = _environment.GetEnvironmentVariable(EnvironmentSettingNames.RegionName) ?? string.Empty;
3848
}
@@ -61,6 +71,10 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
6171
}
6272

6373
(string exceptionType, string exceptionMessage, string exceptionDetails) = exception.GetExceptionDetails();
74+
if (exception != null)
75+
{
76+
formattedMessage = Sanitizer.Sanitize(formattedMessage);
77+
}
6478

6579
var scopeProps = _scopeProvider.GetScopeDictionary();
6680
var stateProps = state as IEnumerable<KeyValuePair<string, object>> ?? new Dictionary<string, object>();
@@ -70,30 +84,47 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
7084
using (JsonTextWriter writer = new JsonTextWriter(sw) { Formatting = Formatting.None })
7185
{
7286
writer.WriteStartObject();
73-
WritePropertyIfNotNull(writer, "message", Sanitizer.Sanitize(formattedMessage));
87+
WritePropertyIfNotNull(writer, "appName", _appServiceOptions.CurrentValue.AppName);
88+
WritePropertyIfNotNull(writer, "roleInstance", _roleInstance);
89+
WritePropertyIfNotNull(writer, "message", formattedMessage);
7490
WritePropertyIfNotNull(writer, "category", _category);
7591
WritePropertyIfNotNull(writer, "hostVersion", _hostVersion);
7692
WritePropertyIfNotNull(writer, "functionInvocationId", Utility.GetValueFromScope(scopeProps, ScopeKeys.FunctionInvocationId));
7793
WritePropertyIfNotNull(writer, "functionName", Utility.ResolveFunctionName(stateProps, scopeProps));
7894
WritePropertyIfNotNull(writer, "hostInstanceId", _hostInstanceId);
7995
WritePropertyIfNotNull(writer, "activityId", Utility.GetValueFromScope(scopeProps, ScriptConstants.LogPropertyActivityIdKey));
8096
WritePropertyIfNotNull(writer, "level", logLevel.ToString());
97+
WritePropertyIfNotNull(writer, "levelId", (int)logLevel);
98+
WritePropertyIfNotNull(writer, "processId", _processId);
8199
WritePropertyIfNotNull(writer, nameof(exceptionDetails), exceptionDetails);
82100
WritePropertyIfNotNull(writer, nameof(exceptionMessage), exceptionMessage);
83101
WritePropertyIfNotNull(writer, nameof(exceptionType), exceptionType);
102+
103+
// Only write the event if it's relevant
104+
if (eventId.Id != 0 || !string.IsNullOrEmpty(eventId.Name))
105+
{
106+
WriteProperty(writer, "eventId", eventId.Id);
107+
WriteProperty(writer, "eventName", eventId.Name);
108+
}
109+
84110
writer.WriteEndObject();
85111
}
86112

87113
_eventGenerator.LogAzureMonitorDiagnosticLogEvent(logLevel, _hostNameProvider.Value, AzureMonitorOperationName, AzureMonitorCategoryName, _regionName, sw.ToString());
88114
}
89115

90-
private static void WritePropertyIfNotNull(JsonTextWriter writer, string propertyName, string propertyValue)
116+
private static void WritePropertyIfNotNull<T>(JsonTextWriter writer, string propertyName, T propertyValue)
91117
{
92118
if (propertyValue != null)
93119
{
94-
writer.WritePropertyName(propertyName);
95-
writer.WriteValue(propertyValue);
120+
WriteProperty(writer, propertyName, propertyValue);
96121
}
97122
}
123+
124+
private static void WriteProperty<T>(JsonTextWriter writer, string propertyName, T propertyValue)
125+
{
126+
writer.WritePropertyName(propertyName);
127+
writer.WriteValue(propertyValue);
128+
}
98129
}
99130
}

src/WebJobs.Script.WebHost/Diagnostics/AzureMonitorDiagnosticLoggerProvider.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
5+
using Microsoft.Azure.WebJobs.Script.Configuration;
56
using Microsoft.Extensions.Logging;
67
using Microsoft.Extensions.Options;
78

@@ -13,24 +14,28 @@ public class AzureMonitorDiagnosticLoggerProvider : ILoggerProvider, ISupportExt
1314
private readonly IEventGenerator _eventGenerator;
1415
private readonly IEnvironment _environment;
1516
private readonly HostNameProvider _hostNameProvider;
17+
private readonly IOptionsMonitor<AppServiceOptions> _appServiceOptions;
1618
private IExternalScopeProvider _scopeProvider;
1719

18-
public AzureMonitorDiagnosticLoggerProvider(IOptions<ScriptJobHostOptions> scriptOptions, IEventGenerator eventGenerator, IEnvironment environment, HostNameProvider hostNameProvider)
19-
: this(scriptOptions.Value.InstanceId, eventGenerator, environment, hostNameProvider)
20+
public AzureMonitorDiagnosticLoggerProvider(IOptions<ScriptJobHostOptions> scriptOptions, IEventGenerator eventGenerator, IEnvironment environment,
21+
HostNameProvider hostNameProvider, IOptionsMonitor<AppServiceOptions> appServiceOptions)
22+
: this(scriptOptions.Value.InstanceId, eventGenerator, environment, hostNameProvider, appServiceOptions)
2023
{
2124
}
2225

23-
public AzureMonitorDiagnosticLoggerProvider(string hostInstanceId, IEventGenerator eventGenerator, IEnvironment environment, HostNameProvider hostNameProvider)
26+
public AzureMonitorDiagnosticLoggerProvider(string hostInstanceId, IEventGenerator eventGenerator, IEnvironment environment,
27+
HostNameProvider hostNameProvider, IOptionsMonitor<AppServiceOptions> appServiceOptions)
2428
{
2529
_hostInstanceId = hostInstanceId ?? throw new ArgumentNullException(hostInstanceId);
2630
_eventGenerator = eventGenerator ?? throw new ArgumentNullException(nameof(eventGenerator));
2731
_environment = environment ?? throw new ArgumentException(nameof(environment));
2832
_hostNameProvider = hostNameProvider ?? throw new ArgumentException(nameof(hostNameProvider));
33+
_appServiceOptions = appServiceOptions ?? throw new ArgumentNullException(nameof(appServiceOptions));
2934
}
3035

3136
public ILogger CreateLogger(string categoryName)
3237
{
33-
return new AzureMonitorDiagnosticLogger(categoryName, _hostInstanceId, _eventGenerator, _environment, _scopeProvider, _hostNameProvider);
38+
return new AzureMonitorDiagnosticLogger(categoryName, _hostInstanceId, _eventGenerator, _environment, _scopeProvider, _hostNameProvider, _appServiceOptions);
3439
}
3540

3641
public void Dispose()

src/WebJobs.Script/Environment/EnvironmentExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,21 @@ public static string GetAzureWebsiteUniqueSlotName(this IEnvironment environment
243243
return name?.ToLowerInvariant();
244244
}
245245

246+
/// <summary>
247+
/// Gets the Instance id.
248+
/// </summary>
249+
public static string GetInstanceId(this IEnvironment environment)
250+
{
251+
if (environment.IsLinuxConsumption())
252+
{
253+
return environment.GetEnvironmentVariableOrDefault(ContainerName, string.Empty);
254+
}
255+
else
256+
{
257+
return environment.GetEnvironmentVariableOrDefault(AzureWebsiteInstanceId, string.Empty);
258+
}
259+
}
260+
246261
/// <summary>
247262
/// Gets a the subscription Id of the current site.
248263
/// </summary>

test/WebJobs.Script.Tests/Eventing/DiagnosticLoggerTests.cs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
67
using Microsoft.Azure.WebJobs.Logging;
8+
using Microsoft.Azure.WebJobs.Script.Configuration;
79
using Microsoft.Azure.WebJobs.Script.WebHost;
810
using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics;
911
using Microsoft.Extensions.Logging;
@@ -20,11 +22,14 @@ public class DiagnosticLoggerTests
2022
private static readonly string _regionName = "West US";
2123
private static readonly string _websiteHostName = "functionstest.azurewebsites.net";
2224
private static readonly string _subscriptionId = "e3235165-1600-4819-85f0-2ab362e909e4";
25+
private static readonly string _roleInstance = "e777fde04dea4eb931d5e5f06e65b4fdf5b375aed60af41dd7b491cf5792e01b";
26+
private static readonly int _processId = Process.GetCurrentProcess().Id;
2327
private readonly string _hostInstanceId = Guid.NewGuid().ToString();
2428

2529
private readonly AzureMonitorDiagnosticLogger _logger;
2630
private readonly Mock<IEventGenerator> _mockEventGenerator;
2731
private readonly IEnvironment _environment = new TestEnvironment();
32+
private readonly TestOptionsMonitor<AppServiceOptions> _appServiceOptionsWrapper;
2833
private readonly string _category = LogCategories.CreateFunctionCategory(_functionName);
2934
private readonly HostNameProvider _hostNameProvider;
3035

@@ -33,15 +38,26 @@ public DiagnosticLoggerTests()
3338
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteOwnerName, $"{_subscriptionId}+westuswebspace");
3439
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteHostName, _websiteHostName);
3540
_environment.SetEnvironmentVariable(EnvironmentSettingNames.RegionName, _regionName);
41+
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteInstanceId, _roleInstance);
3642

3743
_mockEventGenerator = new Mock<IEventGenerator>(MockBehavior.Strict);
3844

45+
var appServiceOptions = new AppServiceOptions
46+
{
47+
AppName = "TestApp",
48+
SlotName = "Production",
49+
SubscriptionId = "abc123",
50+
RuntimeSiteName = "TestApp_Runtime"
51+
};
52+
53+
_appServiceOptionsWrapper = new TestOptionsMonitor<AppServiceOptions>(appServiceOptions);
54+
3955
_category = LogCategories.CreateFunctionCategory(_functionName);
4056
var loggerProvider = new TestLoggerProvider();
4157
var loggerFactory = new LoggerFactory();
4258
loggerFactory.AddProvider(loggerProvider);
4359
_hostNameProvider = new HostNameProvider(_environment);
44-
_logger = new AzureMonitorDiagnosticLogger(_category, _hostInstanceId, _mockEventGenerator.Object, _environment, new LoggerExternalScopeProvider(), _hostNameProvider);
60+
_logger = new AzureMonitorDiagnosticLogger(_category, _hostInstanceId, _mockEventGenerator.Object, _environment, new LoggerExternalScopeProvider(), _hostNameProvider, _appServiceOptionsWrapper);
4561
}
4662

4763
[Fact]
@@ -61,22 +77,30 @@ public void Log_EmitsExpectedEvent()
6177

6278
using (CreateScope(activityId: activityId, functionName: _functionName, functionInvocationId: functionInvocationId))
6379
{
64-
_logger.LogDebug(message);
80+
_logger.LogDebug(new EventId(123, "TestEvent"), message);
6581
}
6682

6783
_mockEventGenerator.VerifyAll();
6884

6985
JObject actual = JObject.Parse(properties);
86+
87+
var level = LogLevel.Debug;
7088
JObject expected = JObject.FromObject(new
7189
{
90+
appName = _appServiceOptionsWrapper.CurrentValue.AppName,
91+
roleInstance = _roleInstance,
7292
message,
7393
category = _category,
7494
hostVersion = ScriptHost.Version,
7595
functionInvocationId,
7696
functionName = _functionName,
7797
hostInstanceId = _hostInstanceId,
7898
activityId,
79-
level = "Debug"
99+
level = level.ToString(),
100+
levelId = (int)level,
101+
processId = _processId,
102+
eventId = 123,
103+
eventName = "TestEvent"
80104
});
81105

82106
Assert.True(JToken.DeepEquals(actual, expected), $"Actual: {actual.ToString()}{Environment.NewLine}Expected: {expected.ToString()}");
@@ -107,17 +131,23 @@ public void Log_Error_EmitsExpectedEvent()
107131

108132
_mockEventGenerator.VerifyAll();
109133

134+
var level = LogLevel.Error;
135+
110136
JObject actual = JObject.Parse(properties);
111137
JObject expected = JObject.FromObject(new
112138
{
139+
appName = _appServiceOptionsWrapper.CurrentValue.AppName,
140+
roleInstance = _roleInstance,
113141
exceptionType = ex.GetType().ToString(),
114142
exceptionMessage = ex.Message,
115143
exceptionDetails = ex.ToFormattedString(),
116144
message,
117145
category = _category,
118146
hostInstanceId = _hostInstanceId,
119147
hostVersion = ScriptHost.Version,
120-
level = "Error"
148+
level = level.ToString(),
149+
levelId = (int)level,
150+
processId = _processId
121151
});
122152

123153
Assert.True(JToken.DeepEquals(actual, expected), $"Actual: {actual.ToString()}{Environment.NewLine}Expected: {expected.ToString()}");
@@ -152,9 +182,13 @@ public void Log_Sanitizes()
152182

153183
_mockEventGenerator.VerifyAll();
154184

185+
var level = LogLevel.Error;
186+
155187
JObject actual = JObject.Parse(properties);
156188
JObject expected = JObject.FromObject(new
157189
{
190+
appName = _appServiceOptionsWrapper.CurrentValue.AppName,
191+
roleInstance = _roleInstance,
158192
category = _category,
159193
exceptionDetails = sanitizedDetails,
160194
exceptionMessage = sanitizedExceptionMessage,
@@ -163,8 +197,10 @@ public void Log_Sanitizes()
163197
functionName = _functionName,
164198
hostInstanceId = _hostInstanceId,
165199
hostVersion = ScriptHost.Version,
166-
level = "Error",
200+
level = level.ToString(),
201+
levelId = (int)level,
167202
message = sanitizedString,
203+
processId = _processId
168204
});
169205

170206
Assert.True(JToken.DeepEquals(actual, expected), $"Actual: {actual.ToString()}{Environment.NewLine}Expected: {expected.ToString()}");
@@ -194,7 +230,7 @@ public void Log_DisabledIfNoSiteName()
194230
_environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteHostName, null);
195231

196232
// Recreate the logger was we cache the site name in the constructor
197-
ILogger logger = new AzureMonitorDiagnosticLogger(_category, _hostInstanceId, _mockEventGenerator.Object, _environment, new LoggerExternalScopeProvider(), _hostNameProvider);
233+
ILogger logger = new AzureMonitorDiagnosticLogger(_category, _hostInstanceId, _mockEventGenerator.Object, _environment, new LoggerExternalScopeProvider(), _hostNameProvider, _appServiceOptionsWrapper);
198234

199235
logger.LogInformation(message);
200236

0 commit comments

Comments
 (0)