Skip to content

Commit 75d9381

Browse files
Add logic to connect to Azure Log Analytics (#2787)
## Why make this change? Solves issue #2778 ## What is this change? This change adds logic to capture logs that DAB produces in order to send them to the users' Azure Log Analytics tables. It does this by creating a logger provider that is activated when the feature is enabled and sends the logs that are produced to a custom log collector. This custom log collector then flushes all of the saved logs periodically every certain amount of time to Azure Log Analytics. It is also important to note, that the name of one of the properties in the feature was changed from `workspace-id` to `custom-table-name` as I found that the feature did not need the `workspace-id` to connect to the table, but it needed the name of custom log that is created as a resource by the user. Files Created: - `AzureLogAnalyticsFlusherService` uploads the logs periodically to the Azure Log Analytics Workspace Table. - `AzureLogAnalyticsCustomLogCollector` it collects the logs from the rest of the services and pushes them to `AzureLogAnalyticsFlusherService`. - `AzureLogAnalyticsLoggerProvider` creates the `AzureLogAnalyticsLogger` to start the population of logs inside the `AzureLogAnalyticsCustomLogCollector`. - `AzureLogAnalyticsLog` is the base object that defines the structure of the logs that will be sent to Azure Log Analytics. ## How was this tested? - [ ] Integration Tests - [X] Unit Tests - [X] Manual Tests For the manual testing, I had to create various resources inside of Azure: - Create Azure Log Analytics Workspace and creating a table inside of it and decide on the `custom-table-name`. - Create `DCE Endpoint` which is used the entry point for DAB to send its logs - Create DCR or Data Collection Rule and set rules on the structure of the logs that it will receive, and set the workspace table and DCE Endpoint to which it will connect. - Create a VM that has a system assigned managed identity. - Assign permission on DCR to allow VM to write telemetry data. After creating all the necessary resources, you just need to run DAB inside of the VM to have Azure Log Analytics logs sent. You can follow the steps more in detail in the following link: https://learn.microsoft.com/en-us/azure/azure-monitor/logs/tutorial-logs-ingestion-api?tabs=dce#create-data-collection-rule ## Sample Request(s) <img width="1105" height="400" alt="image" src="https://github.com/user-attachments/assets/6cd3a597-69e8-4777-8c6d-bc167b6fd424" />
1 parent 37343c0 commit 75d9381

21 files changed

+581
-123
lines changed

schemas/dab.draft.schema.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -423,9 +423,9 @@
423423
"type": "object",
424424
"additionalProperties": false,
425425
"properties": {
426-
"workspace-id": {
426+
"custom-table-name": {
427427
"type": [ "string", "null" ],
428-
"description": "Azure Log Analytics Workspace ID"
428+
"description": "Azure Log Analytics Custom Table Name for entra-id mode"
429429
},
430430
"dcr-immutable-id": {
431431
"type": [ "string", "null" ],
@@ -459,9 +459,9 @@
459459
"properties": {
460460
"auth": {
461461
"properties": {
462-
"workspace-id": {
462+
"custom-table-name": {
463463
"type": "string",
464-
"description": "Azure Log Analytics Workspace ID"
464+
"description": "Azure Log Analytics Custom Table Name for entra-id mode"
465465
},
466466
"dcr-immutable-id": {
467467
"type": "string",
@@ -472,7 +472,7 @@
472472
"description": "DCE endpoint for entra-id mode"
473473
}
474474
},
475-
"required": [ "workspace-id", "dcr-immutable-id", "dce-endpoint" ]
475+
"required": [ "custom-table-name", "dcr-immutable-id", "dce-endpoint" ]
476476
}
477477
},
478478
"required": [ "auth" ]

src/Cli.Tests/ConfigureOptionsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ public void TestAddAzureLogAnalyticsOptions()
164164
azureLogAnalyticsEnabled: CliBool.True,
165165
azureLogAnalyticsLogType: "log-type-test",
166166
azureLogAnalyticsFlushIntervalSeconds: 1,
167-
azureLogAnalyticsWorkspaceId: "workspace-id-test",
167+
azureLogAnalyticsCustomTableName: "custom-table-name-test",
168168
azureLogAnalyticsDcrImmutableId: "dcr-immutable-id-test",
169169
azureLogAnalyticsDceEndpoint: "dce-endpoint-test",
170170
config: TEST_RUNTIME_CONFIG_FILE
@@ -183,7 +183,7 @@ public void TestAddAzureLogAnalyticsOptions()
183183
Assert.AreEqual("log-type-test", config.Runtime.Telemetry.AzureLogAnalytics.LogType);
184184
Assert.AreEqual(1, config.Runtime.Telemetry.AzureLogAnalytics.FlushIntervalSeconds);
185185
Assert.IsNotNull(config.Runtime.Telemetry.AzureLogAnalytics.Auth);
186-
Assert.AreEqual("workspace-id-test", config.Runtime.Telemetry.AzureLogAnalytics.Auth.WorkspaceId);
186+
Assert.AreEqual("custom-table-name-test", config.Runtime.Telemetry.AzureLogAnalytics.Auth.CustomTableName);
187187
Assert.AreEqual("dcr-immutable-id-test", config.Runtime.Telemetry.AzureLogAnalytics.Auth.DcrImmutableId);
188188
Assert.AreEqual("dce-endpoint-test", config.Runtime.Telemetry.AzureLogAnalytics.Auth.DceEndpoint);
189189
}

src/Cli/Commands/ConfigureOptions.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public ConfigureOptions(
5151
CliBool? azureLogAnalyticsEnabled = null,
5252
string? azureLogAnalyticsLogType = null,
5353
int? azureLogAnalyticsFlushIntervalSeconds = null,
54-
string? azureLogAnalyticsWorkspaceId = null,
54+
string? azureLogAnalyticsCustomTableName = null,
5555
string? azureLogAnalyticsDcrImmutableId = null,
5656
string? azureLogAnalyticsDceEndpoint = null,
5757
string? config = null)
@@ -95,7 +95,7 @@ public ConfigureOptions(
9595
AzureLogAnalyticsEnabled = azureLogAnalyticsEnabled;
9696
AzureLogAnalyticsLogType = azureLogAnalyticsLogType;
9797
AzureLogAnalyticsFlushIntervalSeconds = azureLogAnalyticsFlushIntervalSeconds;
98-
AzureLogAnalyticsWorkspaceId = azureLogAnalyticsWorkspaceId;
98+
AzureLogAnalyticsCustomTableName = azureLogAnalyticsCustomTableName;
9999
AzureLogAnalyticsDcrImmutableId = azureLogAnalyticsDcrImmutableId;
100100
AzureLogAnalyticsDceEndpoint = azureLogAnalyticsDceEndpoint;
101101
}
@@ -184,17 +184,17 @@ public ConfigureOptions(
184184
[Option("azure-key-vault.retry-policy.network-timeout-seconds", Required = false, HelpText = "Configure the network timeout for requests in seconds. Default: 60.")]
185185
public int? AzureKeyVaultRetryPolicyNetworkTimeoutSeconds { get; }
186186

187-
[Option("runtime.telemetry.azure-log-analytics.enabled", Default = CliBool.False, Required = false, HelpText = "Enable/Disable Azure Log Analytics.")]
187+
[Option("runtime.telemetry.azure-log-analytics.enabled", Required = false, HelpText = "Enable/Disable Azure Log Analytics. Default: False (boolean)")]
188188
public CliBool? AzureLogAnalyticsEnabled { get; }
189189

190-
[Option("runtime.telemetry.azure-log-analytics.log-type", Required = false, HelpText = "Configure Log Type for Azure Log Analytics to find table to send telemetry data")]
190+
[Option("runtime.telemetry.azure-log-analytics.log-type", Required = false, HelpText = "Configure Log Type for Azure Log Analytics to find table to send telemetry data. Default: DABLogs")]
191191
public string? AzureLogAnalyticsLogType { get; }
192192

193-
[Option("runtime.telemetry.azure-log-analytics.flush-interval-seconds", Required = false, HelpText = "Configure Flush Interval in seconds for Azure Log Analytics to specify the time interval to send the telemetry data")]
193+
[Option("runtime.telemetry.azure-log-analytics.flush-interval-seconds", Required = false, HelpText = "Configure Flush Interval in seconds for Azure Log Analytics to specify the time interval to send the telemetry data. Default: 5")]
194194
public int? AzureLogAnalyticsFlushIntervalSeconds { get; }
195195

196-
[Option("runtime.telemetry.azure-log-analytics.auth.workspace-id", Required = false, HelpText = "Configure Workspace ID for Azure Log Analytics used to find workspace to connect")]
197-
public string? AzureLogAnalyticsWorkspaceId { get; }
196+
[Option("runtime.telemetry.azure-log-analytics.auth.custom-table-name", Required = false, HelpText = "Configure Custom Table Name for Azure Log Analytics used to find table to connect")]
197+
public string? AzureLogAnalyticsCustomTableName { get; }
198198

199199
[Option("runtime.telemetry.azure-log-analytics.auth.dcr-immutable-id", Required = false, HelpText = "Configure DCR Immutable ID for Azure Log Analytics to find the data collection rule that defines how data is collected")]
200200
public string? AzureLogAnalyticsDcrImmutableId { get; }

src/Cli/ConfigGenerator.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ private static bool TryUpdateConfiguredRuntimeOptions(
782782
if (options.AzureLogAnalyticsEnabled is not null ||
783783
options.AzureLogAnalyticsLogType is not null ||
784784
options.AzureLogAnalyticsFlushIntervalSeconds is not null ||
785-
options.AzureLogAnalyticsWorkspaceId is not null ||
785+
options.AzureLogAnalyticsCustomTableName is not null ||
786786
options.AzureLogAnalyticsDcrImmutableId is not null ||
787787
options.AzureLogAnalyticsDceEndpoint is not null)
788788
{
@@ -1157,13 +1157,13 @@ private static bool TryUpdateConfiguredAzureLogAnalyticsOptions(
11571157
_logger.LogInformation($"Updated configuration with runtime.telemetry.azure-log-analytics.flush-interval-seconds as '{options.AzureLogAnalyticsFlushIntervalSeconds}'");
11581158
}
11591159

1160-
// Runtime.Telemetry.AzureLogAnalytics.Auth.WorkspaceId
1161-
if (options.AzureLogAnalyticsWorkspaceId is not null)
1160+
// Runtime.Telemetry.AzureLogAnalytics.Auth.CustomTableName
1161+
if (options.AzureLogAnalyticsCustomTableName is not null)
11621162
{
11631163
updatedAuthOptions = updatedAuthOptions is not null
1164-
? updatedAuthOptions with { WorkspaceId = options.AzureLogAnalyticsWorkspaceId, UserProvidedWorkspaceId = true }
1165-
: new AzureLogAnalyticsAuthOptions { WorkspaceId = options.AzureLogAnalyticsWorkspaceId, UserProvidedWorkspaceId = true };
1166-
_logger.LogInformation($"Updated configuration with runtime.telemetry.azure-log-analytics.auth.workspace-id as '{options.AzureLogAnalyticsWorkspaceId}'");
1164+
? updatedAuthOptions with { CustomTableName = options.AzureLogAnalyticsCustomTableName, UserProvidedCustomTableName = true }
1165+
: new AzureLogAnalyticsAuthOptions { CustomTableName = options.AzureLogAnalyticsCustomTableName, UserProvidedCustomTableName = true };
1166+
_logger.LogInformation($"Updated configuration with runtime.telemetry.azure-log-analytics.auth.custom-table-name as '{options.AzureLogAnalyticsCustomTableName}'");
11671167
}
11681168

11691169
// Runtime.Telemetry.AzureLogAnalytics.Auth.DcrImmutableId

src/Config/Converters/AzureLogAnalyticsAuthOptionsConverter.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,26 @@ public AzureLogAnalyticsAuthOptionsConverter(bool replaceEnvVar)
2929
{
3030
if (reader.TokenType is JsonTokenType.StartObject)
3131
{
32-
string? workspaceId = null;
32+
string? customTableName = null;
3333
string? dcrImmutableId = null;
3434
string? dceEndpoint = null;
3535

3636
while (reader.Read())
3737
{
3838
if (reader.TokenType == JsonTokenType.EndObject)
3939
{
40-
return new AzureLogAnalyticsAuthOptions(workspaceId, dcrImmutableId, dceEndpoint);
40+
return new AzureLogAnalyticsAuthOptions(customTableName, dcrImmutableId, dceEndpoint);
4141
}
4242

4343
string? propertyName = reader.GetString();
4444

4545
reader.Read();
4646
switch (propertyName)
4747
{
48-
case "workspace-id":
48+
case "custom-table-name":
4949
if (reader.TokenType is not JsonTokenType.Null)
5050
{
51-
workspaceId = reader.DeserializeString(_replaceEnvVar);
51+
customTableName = reader.DeserializeString(_replaceEnvVar);
5252
}
5353

5454
break;
@@ -88,10 +88,10 @@ public override void Write(Utf8JsonWriter writer, AzureLogAnalyticsAuthOptions v
8888
{
8989
writer.WriteStartObject();
9090

91-
if (value?.UserProvidedWorkspaceId is true)
91+
if (value?.UserProvidedCustomTableName is true)
9292
{
93-
writer.WritePropertyName("workspace-id");
94-
JsonSerializer.Serialize(writer, value.WorkspaceId, options);
93+
writer.WritePropertyName("custom-table-name");
94+
JsonSerializer.Serialize(writer, value.CustomTableName, options);
9595
}
9696

9797
if (value?.UserProvidedDcrImmutableId is true)

src/Config/Converters/AzureLogAnalyticsOptionsConverterFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public override void Write(Utf8JsonWriter writer, AzureLogAnalyticsOptions value
141141
JsonSerializer.Serialize(writer, value.Enabled, options);
142142
}
143143

144-
if (value?.Auth is not null && (value.Auth.UserProvidedWorkspaceId || value.Auth.UserProvidedDcrImmutableId || value.Auth.UserProvidedDceEndpoint))
144+
if (value?.Auth is not null && (value.Auth.UserProvidedCustomTableName || value.Auth.UserProvidedDcrImmutableId || value.Auth.UserProvidedDceEndpoint))
145145
{
146146
AzureLogAnalyticsAuthOptionsConverter authOptionsConverter = options.GetConverter(typeof(AzureLogAnalyticsAuthOptions)) as AzureLogAnalyticsAuthOptionsConverter ??
147147
throw new JsonException("Failed to get azure-log-analytics.auth options converter");

src/Config/ObjectModel/AzureLogAnalyticsAuthOptions.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public record AzureLogAnalyticsAuthOptions
1414
/// <summary>
1515
/// Whether Azure Log Analytics is enabled.
1616
/// </summary>
17-
public string? WorkspaceId { get; init; }
17+
public string? CustomTableName { get; init; }
1818

1919
/// <summary>
2020
/// Authentication options for Azure Log Analytics.
@@ -27,12 +27,12 @@ public record AzureLogAnalyticsAuthOptions
2727
public string? DceEndpoint { get; init; }
2828

2929
[JsonConstructor]
30-
public AzureLogAnalyticsAuthOptions(string? workspaceId = null, string? dcrImmutableId = null, string? dceEndpoint = null)
30+
public AzureLogAnalyticsAuthOptions(string? customTableName = null, string? dcrImmutableId = null, string? dceEndpoint = null)
3131
{
32-
if (workspaceId is not null)
32+
if (customTableName is not null)
3333
{
34-
WorkspaceId = workspaceId;
35-
UserProvidedWorkspaceId = true;
34+
CustomTableName = customTableName;
35+
UserProvidedCustomTableName = true;
3636
}
3737

3838
if (dcrImmutableId is not null)
@@ -51,12 +51,12 @@ public AzureLogAnalyticsAuthOptions(string? workspaceId = null, string? dcrImmut
5151
/// <summary>
5252
/// Flag which informs CLI and JSON serializer whether to write workspace-id
5353
/// property and value to the runtime config file.
54-
/// When user doesn't provide the workspace-id property/value, which signals DAB to not write anything,
54+
/// When user doesn't provide the custom-table-name property/value, which signals DAB to not write anything,
5555
/// the DAB CLI should not write the current value to a serialized config.
5656
/// </summary>
5757
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
58-
[MemberNotNullWhen(true, nameof(WorkspaceId))]
59-
public bool UserProvidedWorkspaceId { get; init; } = false;
58+
[MemberNotNullWhen(true, nameof(CustomTableName))]
59+
public bool UserProvidedCustomTableName { get; init; } = false;
6060

6161
/// <summary>
6262
/// Flag which informs CLI and JSON serializer whether to write dcr-immutable-id
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
namespace Azure.DataApiBuilder.Config.ObjectModel;
5+
6+
/// <summary>
7+
/// Class used to save the components for the logs that are sent to Azure Log Analytics
8+
/// </summary>
9+
public class AzureLogAnalyticsLogs
10+
{
11+
public string Time { get; set; }
12+
public string LogLevel { get; set; }
13+
public string? Message { get; set; }
14+
public string? Component { get; set; }
15+
public string? LogType { get; set; }
16+
17+
public AzureLogAnalyticsLogs(string time, string logLevel, string? message, string? component, string? logType = null)
18+
{
19+
Time = time;
20+
LogLevel = logLevel;
21+
Message = message;
22+
Component = component;
23+
LogType = logType;
24+
}
25+
}

src/Core/Configurations/RuntimeConfigValidator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,11 @@ public void ValidateAzureLogAnalyticsAuth(RuntimeConfig runtimeConfig)
166166
{
167167
AzureLogAnalyticsOptions azureLogAnalyticsOptions = runtimeConfig.Runtime.Telemetry.AzureLogAnalytics;
168168
AzureLogAnalyticsAuthOptions? azureLogAnalyticsAuthOptions = azureLogAnalyticsOptions.Auth;
169-
if (azureLogAnalyticsOptions.Enabled && (azureLogAnalyticsAuthOptions is null || string.IsNullOrWhiteSpace(azureLogAnalyticsAuthOptions.WorkspaceId) ||
169+
if (azureLogAnalyticsOptions.Enabled && (azureLogAnalyticsAuthOptions is null || string.IsNullOrWhiteSpace(azureLogAnalyticsAuthOptions.CustomTableName) ||
170170
string.IsNullOrWhiteSpace(azureLogAnalyticsAuthOptions.DcrImmutableId) || string.IsNullOrWhiteSpace(azureLogAnalyticsAuthOptions.DceEndpoint)))
171171
{
172172
HandleOrRecordException(new DataApiBuilderException(
173-
message: "Azure Log Analytics Auth options 'workspace-id', 'dcr-immutable-id', and 'dce-endpoint' cannot be null or empty if enabled.",
173+
message: "Azure Log Analytics Auth options 'custom-table-name', 'dcr-immutable-id', and 'dce-endpoint' cannot be null or empty if enabled.",
174174
statusCode: HttpStatusCode.ServiceUnavailable,
175175
subStatusCode: DataApiBuilderException.SubStatusCodes.ConfigValidationError));
176176
}

0 commit comments

Comments
 (0)