Skip to content

Commit 5bac761

Browse files
Add support for identity-based connections to Diagnostic Events (#10438)
* Add support for identity-based connections to Diagnostic Events * Updated release notes * Removed unused namespace * Ensure that the factory takes into account a configuration section and not only the connection string * Logging failure to create the client as a warning and adapted message * Update release_notes Co-authored-by: Lilian Kasem <[email protected]> * Address PR feedback * Update release notes --------- Co-authored-by: Lilian Kasem <[email protected]>
1 parent a61ce6c commit 5bac761

File tree

4 files changed

+37
-45
lines changed

4 files changed

+37
-45
lines changed

release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
- Implement host configuration property to allow configuration of the metadata provider timeout period (#10526)
1212
- The value can be set via `metadataProviderTimeout` in host.json and defaults to "00:00:30" (30 seconds).
1313
- For logic apps, unless configured via the host.json, the timeout is disabled by default.
14+
- Added support for identity-based connections to Diagnostic Events (#10438)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using Microsoft.Extensions.Configuration;
66
using Microsoft.Extensions.DependencyInjection;
7-
using Microsoft.Extensions.Logging;
87

98
namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics
109
{
@@ -21,11 +20,12 @@ public DiagnosticEventRepositoryFactory(IServiceProvider serviceProvider, IConfi
2120

2221
public IDiagnosticEventRepository Create()
2322
{
24-
// Using this to break ciruclar dependency for ILoggers. Typically you cannot log errors within the logging pipeline because it creates infinte loop.
23+
// Using this to break circular dependency for ILoggers. Typically you cannot log errors within the logging pipeline because it creates infinite loop.
2524
// However in this case that loop is broken because of the filtering in the DiagnosticEventLogger
2625

27-
string storageConnectionString = _configuration.GetWebJobsConnectionString(ConnectionStringNames.Storage);
28-
if (string.IsNullOrEmpty(storageConnectionString))
26+
IConfigurationSection storageConnectionSection = _configuration.GetWebJobsConnectionSection(ConnectionStringNames.Storage);
27+
28+
if (storageConnectionSection is null || !storageConnectionSection.Exists())
2929
{
3030
return new DiagnosticEventNullRepository();
3131
}

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

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
using Microsoft.Azure.WebJobs.Hosting;
1313
using Microsoft.Azure.WebJobs.Logging;
1414
using Microsoft.Azure.WebJobs.Script.WebHost.Helpers;
15-
using Microsoft.Extensions.Configuration;
1615
using Microsoft.Extensions.DependencyInjection;
1716
using Microsoft.Extensions.Logging;
1817

@@ -25,9 +24,9 @@ public class DiagnosticEventTableStorageRepository : IDiagnosticEventRepository,
2524
private const int TableCreationMaxRetryCount = 5;
2625

2726
private readonly Timer _flushLogsTimer;
28-
private readonly IConfiguration _configuration;
2927
private readonly IHostIdProvider _hostIdProvider;
3028
private readonly IEnvironment _environment;
29+
private readonly IAzureTableStorageProvider _azureTableStorageProvider;
3130
private readonly ILogger<DiagnosticEventTableStorageRepository> _logger;
3231
private readonly IServiceProvider _serviceProvider;
3332
private readonly object _syncLock = new object();
@@ -41,43 +40,30 @@ public class DiagnosticEventTableStorageRepository : IDiagnosticEventRepository,
4140
private string _tableName;
4241
private bool _isEnabled = true;
4342

44-
internal DiagnosticEventTableStorageRepository(IConfiguration configuration, IHostIdProvider hostIdProvider, IEnvironment environment, IScriptHostManager scriptHostManager,
45-
ILogger<DiagnosticEventTableStorageRepository> logger, int logFlushInterval)
43+
internal DiagnosticEventTableStorageRepository(IHostIdProvider hostIdProvider, IEnvironment environment, IScriptHostManager scriptHostManager,
44+
IAzureTableStorageProvider azureTableStorageProvider, ILogger<DiagnosticEventTableStorageRepository> logger, int logFlushInterval)
4645
{
47-
_configuration = configuration;
4846
_hostIdProvider = hostIdProvider;
4947
_environment = environment;
5048
_serviceProvider = scriptHostManager as IServiceProvider;
5149
_logger = logger;
5250
_flushLogsTimer = new Timer(OnFlushLogs, null, logFlushInterval, logFlushInterval);
51+
_azureTableStorageProvider = azureTableStorageProvider;
5352
}
5453

55-
public DiagnosticEventTableStorageRepository(IConfiguration configuration, IHostIdProvider hostIdProvider, IEnvironment environment, IScriptHostManager scriptHost,
56-
ILogger<DiagnosticEventTableStorageRepository> logger)
57-
: this(configuration, hostIdProvider, environment, scriptHost, logger, LogFlushInterval) { }
54+
public DiagnosticEventTableStorageRepository(IHostIdProvider hostIdProvider, IEnvironment environment, IScriptHostManager scriptHost,
55+
IAzureTableStorageProvider azureTableStorageProvider, ILogger<DiagnosticEventTableStorageRepository> logger)
56+
: this(hostIdProvider, environment, scriptHost, azureTableStorageProvider, logger, LogFlushInterval) { }
5857

5958
internal TableServiceClient TableClient
6059
{
6160
get
6261
{
63-
if (!_environment.IsPlaceholderModeEnabled() && _tableClient == null)
62+
if (!_environment.IsPlaceholderModeEnabled() && _tableClient == null && !_azureTableStorageProvider.TryCreateHostingTableServiceClient(out _tableClient))
6463
{
65-
string storageConnectionString = _configuration.GetWebJobsConnectionString(ConnectionStringNames.Storage);
66-
67-
try
68-
{
69-
_tableClient = new TableServiceClient(storageConnectionString);
70-
71-
// The TableServiceClient only verifies the format of the connection string.
72-
// To ensure the storage account exists and supports Table storage, validate the connection string by retrieving the properties of the table service.
73-
_ = _tableClient.GetProperties();
74-
}
75-
catch (Exception ex)
76-
{
77-
_logger.LogError(ex, "The Azure Storage connection string is either empty or invalid. Unable to record diagnostic events, so the diagnostic logging service is being stopped.");
78-
_isEnabled = false;
79-
StopTimer();
80-
}
64+
_logger.LogWarning("An error occurred initializing the Table Storage Client. We are unable to record diagnostic events, so the diagnostic logging service is being stopped.");
65+
_isEnabled = false;
66+
StopTimer();
8167
}
8268

8369
return _tableClient;

test/WebJobs.Script.Tests.Integration/Diagnostics/DiagnosticEventTableStorageRepositoryTests.cs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ public class DiagnosticEventTableStorageRepositoryTests
2727
private const string TestHostId = "testhostid";
2828
private readonly IHostIdProvider _hostIdProvider;
2929
private readonly TestLoggerProvider _loggerProvider;
30-
private IConfiguration _configuration;
30+
private readonly IAzureTableStorageProvider _azureTableStorageProvider;
31+
3132
private ILogger<DiagnosticEventTableStorageRepository> _logger;
3233
private Mock<IScriptHostManager> _scriptHostMock;
3334

3435
public DiagnosticEventTableStorageRepositoryTests()
3536
{
36-
_configuration = new ConfigurationBuilder().AddEnvironmentVariables().Build();
3737
_hostIdProvider = new FixedHostIdProvider(TestHostId);
3838

3939
var mockPrimaryHostStateProvider = new Mock<IPrimaryHostStateProvider>(MockBehavior.Strict);
@@ -46,6 +46,9 @@ public DiagnosticEventTableStorageRepositoryTests()
4646
ILoggerFactory loggerFactory = new LoggerFactory();
4747
loggerFactory.AddProvider(_loggerProvider);
4848
_logger = loggerFactory.CreateLogger<DiagnosticEventTableStorageRepository>();
49+
50+
var configuration = new ConfigurationBuilder().AddEnvironmentVariables().Build();
51+
_azureTableStorageProvider = TestHelpers.GetAzureTableStorageProvider(configuration);
4952
}
5053

5154
[Fact]
@@ -54,7 +57,7 @@ public async Task TimerFlush_CalledOnExpectedInterval()
5457
IEnvironment testEnvironment = new TestEnvironment();
5558
int flushInterval = 10;
5659
Mock<DiagnosticEventTableStorageRepository> mockDiagnosticEventTableStorageRepository = new Mock<DiagnosticEventTableStorageRepository>
57-
(_configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, NullLogger<DiagnosticEventTableStorageRepository>.Instance, flushInterval);
60+
(_hostIdProvider, testEnvironment, _scriptHostMock.Object, _azureTableStorageProvider, NullLogger<DiagnosticEventTableStorageRepository>.Instance, flushInterval);
5861

5962
DiagnosticEventTableStorageRepository repository = mockDiagnosticEventTableStorageRepository.Object;
6063

@@ -76,7 +79,7 @@ public void WriteDiagnostic_LogsError_WhenHostIdNotSet()
7679
IEnvironment testEnvironment = new TestEnvironment();
7780

7881
DiagnosticEventTableStorageRepository repository =
79-
new DiagnosticEventTableStorageRepository(_configuration, null, testEnvironment, _scriptHostMock.Object, _logger);
82+
new DiagnosticEventTableStorageRepository(null, testEnvironment, _scriptHostMock.Object, _azureTableStorageProvider, _logger);
8083

8184
repository.WriteDiagnosticEvent(DateTime.UtcNow, "eh1", LogLevel.Information, "This is the message", "https://fwlink/", new Exception("exception message"));
8285

@@ -91,7 +94,7 @@ public async Task WriteDiagnostic_HasTheCorrectHitCount_WhenCalledFromMultipleTh
9194
testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
9295

9396
DiagnosticEventTableStorageRepository repository =
94-
new DiagnosticEventTableStorageRepository(_configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, _logger);
97+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, _scriptHostMock.Object, _azureTableStorageProvider, _logger);
9598

9699
var eventTask1 = Task.Run(() =>
97100
{
@@ -136,7 +139,7 @@ public void GetDiagnosticEventsTable_ReturnsExpectedValue_WhenSpecialized()
136139
testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
137140

138141
DiagnosticEventTableStorageRepository repository =
139-
new DiagnosticEventTableStorageRepository(_configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, _logger);
142+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, _scriptHostMock.Object, _azureTableStorageProvider, _logger);
140143
DateTime dateTime = new DateTime(2021, 1, 1);
141144
var cloudTable = repository.GetDiagnosticEventsTable(dateTime);
142145
Assert.NotNull(cloudTable);
@@ -151,16 +154,17 @@ public void GetDiagnosticEventsTable_LogsError_StorageConnectionStringIsNotPrese
151154
testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
152155

153156
var configuration = new ConfigurationBuilder().Build();
157+
var localStorageProvider = TestHelpers.GetAzureTableStorageProvider(configuration);
154158
DiagnosticEventTableStorageRepository repository =
155-
new DiagnosticEventTableStorageRepository(configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, _logger);
159+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, _scriptHostMock.Object, localStorageProvider, _logger);
156160

157161
// The repository should be initially enabled and then disabled after TableServiceClient failure.
158162
Assert.True(repository.IsEnabled());
159163
DateTime dateTime = new DateTime(2021, 1, 1);
160164
var cloudTable = repository.GetDiagnosticEventsTable(dateTime);
161165
Assert.Null(cloudTable);
162166
var messages = _loggerProvider.GetAllLogMessages();
163-
Assert.Equal(messages[0].FormattedMessage, "The Azure Storage connection string is either empty or invalid. Unable to record diagnostic events, so the diagnostic logging service is being stopped.");
167+
Assert.Equal(messages[0].FormattedMessage, "An error occurred initializing the Table Storage Client. We are unable to record diagnostic events, so the diagnostic logging service is being stopped.");
164168
Assert.False(repository.IsEnabled());
165169
}
166170

@@ -171,7 +175,7 @@ public async Task QueueBackgroundDiagnosticsEventsTablePurge_PurgesTables()
171175
testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
172176

173177
DiagnosticEventTableStorageRepository repository =
174-
new DiagnosticEventTableStorageRepository(_configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, _logger);
178+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, _scriptHostMock.Object, _azureTableStorageProvider, _logger);
175179

176180
// delete any existing non-current diagnostics events tables
177181
string tablePrefix = DiagnosticEventTableStorageRepository.TableNamePrefix;
@@ -212,7 +216,7 @@ public async Task QueueBackgroundDiagnosticsEventsTablePurge_PurgesOnlyDiagnosti
212216
testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
213217

214218
DiagnosticEventTableStorageRepository repository =
215-
new DiagnosticEventTableStorageRepository(_configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, _logger);
219+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, _scriptHostMock.Object, _azureTableStorageProvider, _logger);
216220

217221
// delete any existing non-current diagnostics events tables
218222
string tablePrefix = DiagnosticEventTableStorageRepository.TableNamePrefix;
@@ -267,8 +271,9 @@ public async Task FlushLogs_LogsErrorAndClearsEvents_WhenTableCreatingFails()
267271
.AddInMemoryCollection(testData)
268272
.Build();
269273

274+
var localStorageProvider = TestHelpers.GetAzureTableStorageProvider(configuration);
270275
DiagnosticEventTableStorageRepository repository =
271-
new DiagnosticEventTableStorageRepository(configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, _logger);
276+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, _scriptHostMock.Object, localStorageProvider, _logger);
272277

273278
// Act
274279
repository.WriteDiagnosticEvent(DateTime.UtcNow, "eh1", LogLevel.Information, "This is the message", "https://fwlink/", new Exception("exception message"));
@@ -278,7 +283,7 @@ public async Task FlushLogs_LogsErrorAndClearsEvents_WhenTableCreatingFails()
278283
var logMessage = _loggerProvider.GetAllLogMessages().SingleOrDefault(m => m.FormattedMessage.Contains("Unable to get table reference"));
279284
Assert.NotNull(logMessage);
280285

281-
var messagePresent = _loggerProvider.GetAllLogMessages().Any(m => m.FormattedMessage.Contains("The Azure Storage connection string is either empty or invalid. Unable to record diagnostic events, so the diagnostic logging service is being stopped."));
286+
var messagePresent = _loggerProvider.GetAllLogMessages().Any(m => m.FormattedMessage.Contains("An error occurred initializing the Table Storage Client. We are unable to record diagnostic events, so the diagnostic logging service is being stopped."));
282287
Assert.True(messagePresent);
283288

284289
Assert.Equal(0, repository.Events.Values.Count());
@@ -302,7 +307,7 @@ public async Task FlushLogs_OnPrimaryHost_PurgesPreviousEventVersionTables(strin
302307
scriptHostMock.As<IServiceProvider>().Setup(m => m.GetService(typeof(IPrimaryHostStateProvider))).Returns(mockPrimaryHostStateProvider.Object);
303308

304309
DiagnosticEventTableStorageRepository repository =
305-
new DiagnosticEventTableStorageRepository(_configuration, _hostIdProvider, testEnvironment, scriptHostMock.Object, _logger);
310+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, scriptHostMock.Object, _azureTableStorageProvider, _logger);
306311

307312
// delete existing tables
308313
string tablePrefix = DiagnosticEventTableStorageRepository.TableNamePrefix;
@@ -352,7 +357,7 @@ public async Task ExecuteBatchAsync_WritesToTableStorage()
352357
testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
353358

354359
DiagnosticEventTableStorageRepository repository =
355-
new DiagnosticEventTableStorageRepository(_configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, _logger);
360+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, _scriptHostMock.Object, _azureTableStorageProvider, _logger);
356361

357362
var table = repository.GetDiagnosticEventsTable();
358363
await TableStorageHelpers.CreateIfNotExistsAsync(table, repository.TableClient, 2);
@@ -375,7 +380,7 @@ public async Task FlushLogs_WritesToTableStorage()
375380
testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
376381

377382
DiagnosticEventTableStorageRepository repository =
378-
new DiagnosticEventTableStorageRepository(_configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, _logger);
383+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, _scriptHostMock.Object, _azureTableStorageProvider, _logger);
379384

380385
var table = repository.GetDiagnosticEventsTable();
381386
await TableStorageHelpers.CreateIfNotExistsAsync(table, repository.TableClient, 2);
@@ -397,7 +402,7 @@ public async Task ExecuteBatchAsync_LogsError()
397402
testEnvironment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebsitePlaceholderMode, "0");
398403

399404
DiagnosticEventTableStorageRepository repository =
400-
new DiagnosticEventTableStorageRepository(_configuration, _hostIdProvider, testEnvironment, _scriptHostMock.Object, _logger);
405+
new DiagnosticEventTableStorageRepository(_hostIdProvider, testEnvironment, _scriptHostMock.Object, _azureTableStorageProvider, _logger);
401406

402407
var tableClient = repository.TableClient;
403408
var table = tableClient.GetTableClient("aa");

0 commit comments

Comments
 (0)