Skip to content

Commit 68988dd

Browse files
Add diagnostic events for Portal visibility (GA requirement)
Emit MS_DiagnosticEvent structured log entries for: - AZFK0001: Connection string not found - AZFK0002: Ingestion failed/partial success - AZFK0003: Query execution failed These are picked up by the Functions runtime and surfaced in the Portal Overview section for customers.
1 parent 3076cac commit 68988dd

File tree

5 files changed

+70
-3
lines changed

5 files changed

+70
-3
lines changed

src/Bindings/KustoAsyncCollector.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public async Task FlushAsync(CancellationToken cancellationToken = default)
9292
if (ingestionStatus.Status == Status.Failed || ingestionStatus.Status == Status.PartiallySucceeded)
9393
{
9494
string errorMessage = $"Ingestion status reported failure/partial success for {ingestSourceId}. Ingest detail {this._contextdetail.Value}, and status reported was {ingestionStatus.Status}";
95-
this._logger.LogError(errorMessage);
95+
this._logger.Log(LogLevel.Error, new EventId(0), KustoDiagnosticEvent.Create(KustoConstants.IngestionErrorCode, errorMessage, KustoConstants.KustoBindingHelpLink), null, (state, ex) => state.ToString());
9696
throw new FunctionInvocationException(errorMessage);
9797
}
9898
this._rows.Clear();

src/Bindings/KustoQueryConverters.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public async Task<IEnumerable<T>> ConvertAsync(KustoAttribute attribute, Cancell
7272
catch (Exception ex)
7373
{
7474
string logMessage = $"Error in Query/Conversion. Attributes [DB='{attribute?.Database}', Query='{attribute?.KqlCommand}',Parameters='{attribute?.KqlParameters}',CRP='{attribute?.ClientRequestProperties}']";
75-
this._logger.LogError(ex, logMessage);
75+
this._logger.Log(LogLevel.Error, new EventId(0), KustoDiagnosticEvent.Create(KustoConstants.QueryErrorCode, logMessage, KustoConstants.KustoBindingHelpLink), ex, (state, e) => state.ToString());
7676
throw new InvalidOperationException(logMessage, ex);
7777
}
7878
}

src/Config/KustoDiagnosticEvent.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Collections;
5+
using System.Collections.Generic;
6+
7+
namespace Microsoft.Azure.WebJobs.Extensions.Kusto
8+
{
9+
/// <summary>
10+
/// Creates structured log state dictionaries that the Azure Functions runtime
11+
/// recognizes as diagnostic events. These events are aggregated and surfaced
12+
/// in the Portal Overview section for customers.
13+
/// </summary>
14+
internal sealed class KustoDiagnosticEvent : IReadOnlyList<KeyValuePair<string, object>>
15+
{
16+
private readonly List<KeyValuePair<string, object>> _state;
17+
private readonly string _message;
18+
19+
private KustoDiagnosticEvent(string errorCode, string message, string helpLink)
20+
{
21+
this._message = message;
22+
this._state = new List<KeyValuePair<string, object>>
23+
{
24+
new KeyValuePair<string, object>(KustoConstants.DiagnosticEventKey, true),
25+
new KeyValuePair<string, object>(KustoConstants.ErrorCodeKey, errorCode),
26+
new KeyValuePair<string, object>(KustoConstants.HelpLinkKey, helpLink),
27+
new KeyValuePair<string, object>("{OriginalFormat}", message),
28+
};
29+
}
30+
31+
public static KustoDiagnosticEvent Create(string errorCode, string message, string helpLink)
32+
{
33+
return new KustoDiagnosticEvent(errorCode, message, helpLink);
34+
}
35+
36+
public int Count => this._state.Count;
37+
38+
public KeyValuePair<string, object> this[int index] => this._state[index];
39+
40+
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
41+
{
42+
return this._state.GetEnumerator();
43+
}
44+
45+
IEnumerator IEnumerable.GetEnumerator()
46+
{
47+
return this.GetEnumerator();
48+
}
49+
50+
public override string ToString()
51+
{
52+
return this._message;
53+
}
54+
}
55+
}

src/Config/KustoExtensionConfigProvider.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ internal void ValidateConnection(KustoAttribute attribute, Type paramType)
8383
if (string.IsNullOrEmpty(resolvedConnectionString))
8484
{
8585
string attributeProperty = $"{nameof(KustoAttribute)}.{nameof(KustoAttribute.Connection)}";
86-
throw new InvalidOperationException($"Parameter {attributeProperty} should be passed as an environment variable. This value resolved to null");
86+
string errorMessage = $"Parameter {attributeProperty} should be passed as an environment variable. This value resolved to null";
87+
this._logger.Log(LogLevel.Error, new EventId(0), KustoDiagnosticEvent.Create(KustoConstants.ConnectionErrorCode, errorMessage, KustoConstants.KustoBindingHelpLink), null, (state, ex) => state.ToString());
88+
throw new InvalidOperationException(errorMessage);
8789
}
8890
// Empty database check is added right when the KustoAttribute is constructed. This however is deferred here.
8991
// TODO : Add check based on parameters and parameter indexes ?

src/KustoConstants.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ internal static class KustoConstants
2222
public const string InputBindingType = "InputBinding";
2323
public const string OutputBindingType = "OutputBinding";
2424
public const string FunctionsRuntimeHostKey = "FUNCTIONS_WORKER_RUNTIME";
25+
// Diagnostic event keys used by the Functions runtime to surface events in the Portal
26+
public const string DiagnosticEventKey = "MS_DiagnosticEvent";
27+
public const string HelpLinkKey = "MS_HelpLink";
28+
public const string ErrorCodeKey = "MS_ErrorCode";
29+
// Kusto extension error codes
30+
public const string ConnectionErrorCode = "AZFK0001";
31+
public const string IngestionErrorCode = "AZFK0002";
32+
public const string QueryErrorCode = "AZFK0003";
33+
// Help links for documentation
34+
public const string KustoBindingHelpLink = "https://learn.microsoft.com/azure/azure-functions/functions-bindings-azure-data-explorer";
2535
public static readonly string AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
2636
public static readonly string ClientDetailForTracing = $"{AzFunctionsClientName}:{AssemblyVersion}";
2737
public static readonly string ClientRequestId = $"AzFunctions.InputBinding;{AssemblyVersion}";

0 commit comments

Comments
 (0)