From 5cfe29b247d08f805aa28e955269246285b43eb0 Mon Sep 17 00:00:00 2001 From: "Calvin A. Allen" Date: Tue, 6 Jan 2026 09:42:48 -0500 Subject: [PATCH 1/2] feat(telemetry): add Otel4Vsix integration - Add CodingWithCalvin.Otel4Vsix package reference - Configure telemetry in SuperCleanPackage with Honeycomb export - Add HoneycombConfig.cs for API key placeholder - Instrument SuperClean commands with activities and logging - Track project counts and deletion status in telemetry - Add proper telemetry shutdown in Dispose - Remove explicit DeployExtension (VsixSdk handles this) --- .../CodingWithCalvin.SuperClean.csproj | 5 +- .../Commands/SuperCleanCommand.cs | 48 ++++++++++++++++++- .../HoneycombConfig.cs | 7 +++ .../SuperCleanPackage.cs | 27 ++++++++++- 4 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 src/CodingWithCalvin.SuperClean/HoneycombConfig.cs diff --git a/src/CodingWithCalvin.SuperClean/CodingWithCalvin.SuperClean.csproj b/src/CodingWithCalvin.SuperClean/CodingWithCalvin.SuperClean.csproj index 322ec51..a19c06d 100644 --- a/src/CodingWithCalvin.SuperClean/CodingWithCalvin.SuperClean.csproj +++ b/src/CodingWithCalvin.SuperClean/CodingWithCalvin.SuperClean.csproj @@ -8,11 +8,8 @@ bin/$(Configuration)/ - - True - - + diff --git a/src/CodingWithCalvin.SuperClean/Commands/SuperCleanCommand.cs b/src/CodingWithCalvin.SuperClean/Commands/SuperCleanCommand.cs index 90f9081..6a083f1 100644 --- a/src/CodingWithCalvin.SuperClean/Commands/SuperCleanCommand.cs +++ b/src/CodingWithCalvin.SuperClean/Commands/SuperCleanCommand.cs @@ -1,8 +1,10 @@ -using System; +using System; +using System.Collections.Generic; using System.ComponentModel.Design; using System.IO; using System.Text; using System.Threading.Tasks; +using CodingWithCalvin.Otel4Vsix; using Community.VisualStudio.Toolkit; using Microsoft.VisualStudio.Shell; using MessageBox = System.Windows.Forms.MessageBox; @@ -42,12 +44,20 @@ public static void Initialize(Package package) private static void OpenPathWrapper(object sender, EventArgs e) { + using var activity = VsixTelemetry.StartCommandActivity("SuperClean.OpenPathWrapper"); + try { _ = OpenPathAsync(sender, e); } catch (Exception ex) { + activity?.RecordError(ex); + VsixTelemetry.TrackException(ex, new Dictionary + { + { "operation.name", "OpenPathWrapper" } + }); + MessageBox.Show( $@" Fatal Error! Unable to invoke Super Clean! @@ -60,13 +70,19 @@ Fatal Error! Unable to invoke Super Clean! private static async Task OpenPathAsync(object sender, EventArgs e) { + using var activity = VsixTelemetry.StartCommandActivity("SuperClean.OpenPathAsync"); + var activeItem = await VS.Solutions.GetActiveItemAsync(); if (activeItem == null) { + VsixTelemetry.LogInformation("No active item found"); return; } + activity?.SetTag("item.type", activeItem.Type.ToString()); + activity?.SetTag("item.name", activeItem.Name); + switch (activeItem.Type) { case SolutionItemType.Solution: @@ -78,9 +94,17 @@ private static async Task OpenPathAsync(object sender, EventArgs e) { throw new ApplicationException(errors); } + + VsixTelemetry.LogInformation("Solution super cleaned successfully"); } catch (Exception ex) { + activity?.RecordError(ex); + VsixTelemetry.TrackException(ex, new Dictionary + { + { "operation.name", "SuperCleanSolution" } + }); + MessageBox.Show( $@" Unable to Super Clean solution @@ -95,9 +119,17 @@ Unable to Super Clean solution try { SuperCleanProject(activeItem); + VsixTelemetry.LogInformation("Project {ProjectName} super cleaned successfully", activeItem.Name); } catch (Exception ex) { + activity?.RecordError(ex); + VsixTelemetry.TrackException(ex, new Dictionary + { + { "operation.name", "SuperCleanProject" }, + { "project.name", activeItem.Name } + }); + MessageBox.Show( $@" Unable to Super Clean project ${activeItem.Name} @@ -112,27 +144,39 @@ Unable to Super Clean project ${activeItem.Name} async Task<(bool, string)> SuperCleanSolution() { + using var solutionActivity = VsixTelemetry.StartCommandActivity("SuperClean.SuperCleanSolution"); + var success = true; var errors = new StringBuilder(); + var projectCount = 0; foreach (var project in await VS.Solutions.GetAllProjectsAsync()) { try { SuperCleanProject(project); + projectCount++; } catch (Exception ex) { errors.AppendLine(ex.Message); success = false; + solutionActivity?.RecordError(ex); } } + solutionActivity?.SetTag("projects.cleaned", projectCount); + solutionActivity?.SetTag("success", success); + return (success, errors.ToString()); } void SuperCleanProject(SolutionItem project) { + using var projectActivity = VsixTelemetry.StartCommandActivity("SuperClean.SuperCleanProject"); + + projectActivity?.SetTag("project.name", project.Name); + var projectPath = Path.GetDirectoryName(project.FullPath) ?? throw new InvalidOperationException(); @@ -143,11 +187,13 @@ void SuperCleanProject(SolutionItem project) if (Directory.Exists(binPath)) { Directory.Delete(binPath, true); + projectActivity?.SetTag("bin.deleted", true); } if (Directory.Exists(objPath)) { Directory.Delete(objPath, true); + projectActivity?.SetTag("obj.deleted", true); } } } diff --git a/src/CodingWithCalvin.SuperClean/HoneycombConfig.cs b/src/CodingWithCalvin.SuperClean/HoneycombConfig.cs new file mode 100644 index 0000000..99eb0fb --- /dev/null +++ b/src/CodingWithCalvin.SuperClean/HoneycombConfig.cs @@ -0,0 +1,7 @@ +namespace CodingWithCalvin.SuperClean +{ + internal static class HoneycombConfig + { + public const string ApiKey = "PLACEHOLDER"; + } +} diff --git a/src/CodingWithCalvin.SuperClean/SuperCleanPackage.cs b/src/CodingWithCalvin.SuperClean/SuperCleanPackage.cs index a064cea..2328fe2 100644 --- a/src/CodingWithCalvin.SuperClean/SuperCleanPackage.cs +++ b/src/CodingWithCalvin.SuperClean/SuperCleanPackage.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Runtime.InteropServices; using System.Threading; +using CodingWithCalvin.Otel4Vsix; using CodingWithCalvin.SuperClean.Commands; using Microsoft.VisualStudio.Shell; using Task = System.Threading.Tasks.Task; @@ -20,7 +21,31 @@ IProgress progress { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + var builder = VsixTelemetry.Configure() + .WithServiceName(VsixInfo.DisplayName) + .WithServiceVersion(VsixInfo.Version) + .WithVisualStudioAttributes(this) + .WithEnvironmentAttributes(); + +#if !DEBUG + builder + .WithOtlpHttp("https://api.honeycomb.io") + .WithHeader("x-honeycomb-team", HoneycombConfig.ApiKey); +#endif + + builder.Initialize(); + SuperCleanCommand.Initialize(this); } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + VsixTelemetry.Shutdown(); + } + + base.Dispose(disposing); + } } } From 3dd5e7f63d32458ef3137ec4d947765de4d4de65 Mon Sep 17 00:00:00 2001 From: "Calvin A. Allen" Date: Tue, 6 Jan 2026 10:57:53 -0500 Subject: [PATCH 2/2] fix(telemetry): remove sensitive data from telemetry Remove project names and item names from telemetry tags and logs to avoid sending potentially sensitive information. --- .../Commands/SuperCleanCommand.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/CodingWithCalvin.SuperClean/Commands/SuperCleanCommand.cs b/src/CodingWithCalvin.SuperClean/Commands/SuperCleanCommand.cs index 6a083f1..9834d79 100644 --- a/src/CodingWithCalvin.SuperClean/Commands/SuperCleanCommand.cs +++ b/src/CodingWithCalvin.SuperClean/Commands/SuperCleanCommand.cs @@ -81,9 +81,7 @@ private static async Task OpenPathAsync(object sender, EventArgs e) } activity?.SetTag("item.type", activeItem.Type.ToString()); - activity?.SetTag("item.name", activeItem.Name); - - switch (activeItem.Type) + switch (activeItem.Type) { case SolutionItemType.Solution: try @@ -119,7 +117,7 @@ Unable to Super Clean solution try { SuperCleanProject(activeItem); - VsixTelemetry.LogInformation("Project {ProjectName} super cleaned successfully", activeItem.Name); + VsixTelemetry.LogInformation("Project super cleaned successfully"); } catch (Exception ex) { @@ -127,8 +125,7 @@ Unable to Super Clean solution VsixTelemetry.TrackException(ex, new Dictionary { { "operation.name", "SuperCleanProject" }, - { "project.name", activeItem.Name } - }); + }); MessageBox.Show( $@" @@ -175,9 +172,7 @@ void SuperCleanProject(SolutionItem project) { using var projectActivity = VsixTelemetry.StartCommandActivity("SuperClean.SuperCleanProject"); - projectActivity?.SetTag("project.name", project.Name); - - var projectPath = + var projectPath = Path.GetDirectoryName(project.FullPath) ?? throw new InvalidOperationException();