Skip to content

Commit

Permalink
Add instrumentation to search & show commands
Browse files Browse the repository at this point in the history
  • Loading branch information
calebkiage committed Feb 24, 2025
1 parent bb18ead commit 3fc11c2
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@

namespace kiota.Extension;

internal static class TagListExtensions
internal static class CollectionExtensions
{
public static TagList AddAll(this TagList tagList, IEnumerable<KeyValuePair<string, object?>> tags)
{
foreach (var tag in tags) tagList.Add(tag);
return tagList;
}

public static T[] OrEmpty<T>(this T[]? source)
{
return source ?? [];
}
public static List<T> OrEmpty<T>(this List<T>? source)
{
return source ?? [];
}
}
4 changes: 0 additions & 4 deletions src/kiota/Extension/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,4 @@ public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T>? source)
{
return source ?? [];
}
public static T[] OrEmpty<T>(this T[]? source)
{
return source ?? [];
}
}
6 changes: 3 additions & 3 deletions src/kiota/Handlers/KiotaInfoCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
using System.CommandLine.Rendering;
using System.CommandLine.Rendering.Views;
using System.Diagnostics;
using Kiota.Builder;
using Kiota.Builder.Configuration;
using kiota.Extension;
using kiota.Telemetry;
using Kiota.Builder;
using Kiota.Builder.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Writers;

namespace kiota.Handlers;
internal class
internal class
KiotaInfoCommandHandler : KiotaSearchBasedCommandHandler
{
private readonly KeyValuePair<string, object?>[] _commonTags =
Expand Down
59 changes: 58 additions & 1 deletion src/kiota/Handlers/KiotaSearchCommandHandler.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
using System.CommandLine;
using System.CommandLine.Hosting;
using System.CommandLine.Invocation;
using System.CommandLine.IO;
using System.CommandLine.Rendering;
using System.CommandLine.Rendering.Views;
using System.Diagnostics;
using System.Text.Json;
using Kiota.Builder;
using Kiota.Builder.SearchProviders;
using kiota.Extension;
using kiota.Telemetry;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace kiota.Handlers;

internal class KiotaSearchCommandHandler : BaseKiotaCommandHandler
{
private readonly KeyValuePair<string, object?>[] _commonTags =
[
new(TelemetryLabels.TagCommandName, "search"),
new(TelemetryLabels.TagCommandRevision, 1)
];
public required Argument<string> SearchTermArgument
{
get; init;
Expand All @@ -26,11 +36,33 @@ public required Option<string> VersionOption
}
public override async Task<int> InvokeAsync(InvocationContext context)
{
// Span start time
Stopwatch? stopwatch = Stopwatch.StartNew();
var startTime = DateTimeOffset.UtcNow;
// Get options
string searchTerm = context.ParseResult.GetValueForArgument(SearchTermArgument);
string version = context.ParseResult.GetValueForOption(VersionOption) ?? string.Empty;
string? version0 = context.ParseResult.GetValueForOption(VersionOption);
bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption);
var logLevel = context.ParseResult.FindResultFor(LogLevelOption)?.GetValueOrDefault() as LogLevel?;
CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None;

var host = context.GetHost();
var instrumentation = host.Services.GetService<Instrumentation>();
var activitySource = instrumentation?.ActivitySource;

CreateTelemetryTags(activitySource, version0, clearCache, logLevel, out var tags);
// Start span
using var invokeActivity = activitySource?.StartActivity(ActivityKind.Internal, name: TelemetryLabels.SpanSearchCommand,
startTime: startTime,
tags: _commonTags.ConcatNullable(tags)?.Concat(Telemetry.Telemetry.GetThreadTags()));
// Command duration meter
var meterRuntime = instrumentation?.CreateCommandDurationHistogram();
if (meterRuntime is null) stopwatch = null;
// Add this run to the command execution counter
var tl = new TagList(_commonTags.AsSpan()).AddAll(tags.OrEmpty());
instrumentation?.CreateCommandExecutionCounter().Add(1, tl);

string version = version0.OrEmpty();
Configuration.Search.ClearCache = clearCache;


Expand All @@ -45,10 +77,13 @@ public override async Task<int> InvokeAsync(InvocationContext context)
var searcher = await GetKiotaSearcherAsync(loggerFactory, cancellationToken).ConfigureAwait(false);
var results = await searcher.SearchAsync(searchTerm, version, cancellationToken);
await DisplayResultsAsync(searchTerm, version, results, logger, cancellationToken);
invokeActivity?.SetStatus(ActivityStatusCode.Ok);
return 0;
}
catch (Exception ex)
{
invokeActivity?.SetStatus(ActivityStatusCode.Error);
invokeActivity?.AddException(ex);
#if DEBUG
logger.LogCritical(ex, "error searching for a description: {exceptionMessage}", ex.Message);
throw; // so debug tools go straight to the source of the exception when attached
Expand All @@ -57,6 +92,10 @@ public override async Task<int> InvokeAsync(InvocationContext context)
return 1;
#endif
}
finally
{
if (stopwatch is not null) meterRuntime?.Record(stopwatch.Elapsed.TotalSeconds, tl);
}
}
}
private async Task DisplayResultsAsync(string searchTerm, string version, IDictionary<string, SearchResult> results, ILogger logger, CancellationToken cancellationToken)
Expand Down Expand Up @@ -91,6 +130,24 @@ private async Task DisplayResultsAsync(string searchTerm, string version, IDicti
DisplaySearchAddHint();
}
}

private static void CreateTelemetryTags(ActivitySource? activitySource, string? version, bool clearCache,
LogLevel? logLevel,
out List<KeyValuePair<string, object?>>? tags)
{
// set up telemetry tags
const string redacted = TelemetryLabels.RedactedValuePlaceholder;
tags = activitySource?.HasListeners() == true ? new List<KeyValuePair<string, object?>>(7)
{
// Search term is required
new($"{TelemetryLabels.TagCommandParams}.search_term", redacted),
new($"{TelemetryLabels.TagCommandParams}.clear_cache", clearCache),
} : null;

if (version is not null) tags?.Add(new KeyValuePair<string, object?>($"{TelemetryLabels.TagCommandParams}.version", redacted));
if (logLevel is { } ll) tags?.Add(new KeyValuePair<string, object?>($"{TelemetryLabels.TagCommandParams}.log_level", ll.ToString("G")));
}

private const int MaxDescriptionLength = 70;
private static string ShortenDescription(string description)
{
Expand Down
76 changes: 70 additions & 6 deletions src/kiota/Handlers/KiotaShowCommandHandler.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
using System.CommandLine;
using System.CommandLine.Hosting;
using System.CommandLine.Invocation;
using System.Diagnostics;
using System.Text;
using kiota.Extension;
using kiota.Telemetry;
using Kiota.Builder;
using Kiota.Builder.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Services;

namespace kiota.Handlers;
internal class KiotaShowCommandHandler : KiotaSearchBasedCommandHandler
{
private readonly KeyValuePair<string, object?>[] _commonTags =
[
new(TelemetryLabels.TagCommandName, "show"),
new(TelemetryLabels.TagCommandRevision, 1)
];
public required Option<string> DescriptionOption
{
get; init;
Expand Down Expand Up @@ -48,17 +58,44 @@ public required Option<bool> DisableSSLValidationOption

public override async Task<int> InvokeAsync(InvocationContext context)
{
string openapi = context.ParseResult.GetValueForOption(DescriptionOption) ?? string.Empty;
string manifest = context.ParseResult.GetValueForOption(ManifestOption) ?? string.Empty;
string searchTerm = context.ParseResult.GetValueForOption(SearchTermOption) ?? string.Empty;
string version = context.ParseResult.GetValueForOption(VersionOption) ?? string.Empty;
// Span start time
Stopwatch? stopwatch = Stopwatch.StartNew();
var startTime = DateTimeOffset.UtcNow;
// Get options
string? openapi0 = context.ParseResult.GetValueForOption(DescriptionOption);
string? manifest0 = context.ParseResult.GetValueForOption(ManifestOption);
string? searchTerm0 = context.ParseResult.GetValueForOption(SearchTermOption);
string? version0 = context.ParseResult.GetValueForOption(VersionOption);
uint maxDepth = context.ParseResult.GetValueForOption(MaxDepthOption);
List<string> includePatterns = context.ParseResult.GetValueForOption(IncludePatternsOption) ?? new List<string>();
List<string> excludePatterns = context.ParseResult.GetValueForOption(ExcludePatternsOption) ?? new List<string>();
List<string>? includePatterns0 = context.ParseResult.GetValueForOption(IncludePatternsOption);
List<string>? excludePatterns0 = context.ParseResult.GetValueForOption(ExcludePatternsOption);
bool clearCache = context.ParseResult.GetValueForOption(ClearCacheOption);
bool disableSSLValidation = context.ParseResult.GetValueForOption(DisableSSLValidationOption);
var logLevel = context.ParseResult.FindResultFor(LogLevelOption)?.GetValueOrDefault() as LogLevel?;
CancellationToken cancellationToken = context.BindingContext.GetService(typeof(CancellationToken)) is CancellationToken token ? token : CancellationToken.None;

var host = context.GetHost();
var instrumentation = host.Services.GetService<Instrumentation>();
var activitySource = instrumentation?.ActivitySource;

CreateTelemetryTags(activitySource, searchTerm0, version0, clearCache, includePatterns0, excludePatterns0, logLevel, out var tags);
// Start span
using var invokeActivity = activitySource?.StartActivity(ActivityKind.Internal, name: TelemetryLabels.SpanShowCommand,
startTime: startTime,
tags: _commonTags.ConcatNullable(tags)?.Concat(Telemetry.Telemetry.GetThreadTags()));
// Command duration meter
var meterRuntime = instrumentation?.CreateCommandDurationHistogram();
if (meterRuntime is null) stopwatch = null;
// Add this run to the command execution counter
var tl = new TagList(_commonTags.AsSpan()).AddAll(tags.OrEmpty());
instrumentation?.CreateCommandExecutionCounter().Add(1, tl);

string openapi = openapi0.OrEmpty();
string manifest = manifest0.OrEmpty();
string searchTerm = searchTerm0.OrEmpty();
string version = version0.OrEmpty();
var includePatterns = includePatterns0.OrEmpty();
var excludePatterns = excludePatterns0.OrEmpty();
var (loggerFactory, logger) = GetLoggerAndFactory<KiotaBuilder>(context);

Configuration.Search.ClearCache = clearCache;
Expand Down Expand Up @@ -103,6 +140,8 @@ public override async Task<int> InvokeAsync(InvocationContext context)
}
catch (Exception ex)
{
invokeActivity?.SetStatus(ActivityStatusCode.Error);
invokeActivity?.AddException(ex);
#if DEBUG
logger.LogCritical(ex, "error showing the description: {exceptionMessage}", ex.Message);
throw; // so debug tools go straight to the source of the exception when attached
Expand All @@ -111,8 +150,13 @@ public override async Task<int> InvokeAsync(InvocationContext context)
return 1;
#endif
}
finally
{
if (stopwatch is not null) meterRuntime?.Record(stopwatch.Elapsed.TotalSeconds, tl);
}

}
invokeActivity?.SetStatus(ActivityStatusCode.Ok);
return 0;
}
private const string Cross = " ├─";
Expand Down Expand Up @@ -152,4 +196,24 @@ private static void RenderChildNode(OpenApiUrlTreeNode node, uint maxDepth, Stri

RenderNode(node, maxDepth, builder, indent, nodeDepth + 1);
}

private static void CreateTelemetryTags(ActivitySource? activitySource, string? searchTerm, string? version,
bool clearCache, List<string>? includePatterns, List<string>? excludePatterns, LogLevel? logLevel,
out List<KeyValuePair<string, object?>>? tags)
{
// set up telemetry tags
const string redacted = TelemetryLabels.RedactedValuePlaceholder;
tags = activitySource?.HasListeners() == true ? new List<KeyValuePair<string, object?>>(7)
{
new($"{TelemetryLabels.TagCommandParams}.openapi", redacted),
new($"{TelemetryLabels.TagCommandParams}.clear_cache", clearCache),
new($"{TelemetryLabels.TagCommandParams}.max_depth", redacted),
} : null;

if (searchTerm is not null) tags?.Add(new KeyValuePair<string, object?>($"{TelemetryLabels.TagCommandParams}.search_key", redacted));
if (version is not null) tags?.Add(new KeyValuePair<string, object?>($"{TelemetryLabels.TagCommandParams}.version", redacted));
if (includePatterns is not null) tags?.Add(new KeyValuePair<string, object?>($"{TelemetryLabels.TagCommandParams}.include_path", redacted));
if (excludePatterns is not null) tags?.Add(new KeyValuePair<string, object?>($"{TelemetryLabels.TagCommandParams}.exclude_path", redacted));
if (logLevel is { } ll) tags?.Add(new KeyValuePair<string, object?>($"{TelemetryLabels.TagCommandParams}.log_level", ll.ToString("G")));
}
}
2 changes: 2 additions & 0 deletions src/kiota/Telemetry/TelemetryLabels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ public static class TelemetryLabels
public const string SpanGitHubLogoutCommand = "Logout/GitHub InvokeAsync()";
public const string SpanGitHubPatLoginCommand = "Login/GitHub/Pat InvokeAsync()";
public const string SpanInfoCommand = "Info InvokeAsync()";
public const string SpanSearchCommand = "Search InvokeAsync()";
public const string SpanShowCommand = "Show InvokeAsync()";
}

0 comments on commit 3fc11c2

Please sign in to comment.