diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml
new file mode 100644
index 0000000..a99ad0b
--- /dev/null
+++ b/.github/workflows/contributors.yml
@@ -0,0 +1,42 @@
+name: Update Contributors
+
+on:
+ schedule:
+ - cron: '0 0 * * *' # Run daily at midnight UTC
+ workflow_dispatch: # Allow manual trigger
+
+jobs:
+ contributors:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ token: ${{ secrets.CONTRIBUTORS_TOKEN }}
+
+ - name: Update contributors
+ env:
+ GH_TOKEN: ${{ secrets.CONTRIBUTORS_TOKEN }}
+ run: |
+ # Fetch contributors from GitHub API (exclude bots)
+ contributors=$(gh api repos/CodingWithCalvin/Otel4Vsix/contributors --paginate --jq '.[] | select(.type != "Bot") | select(.login | test("\\[bot\\]$") | not) | "
"')
+
+ # Build the contributors section
+ contrib_section="
+
+ $contributors
+
+ "
+
+ # Update README between the markers
+ awk -v contrib="$contrib_section" '
+ //{found=1; print contrib; next}
+ //{found=0; next}
+ !found{print}
+ ' README.md > README.tmp && mv README.tmp README.md
+
+ - name: Commit and push
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git add README.md
+ git diff --staged --quiet || (git commit -m "docs: update contributors [skip ci]" && git push)
diff --git a/README.md b/README.md
index 75060be..3fd816a 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,40 @@
-# Otel4Vsix
+
+
+
-[](https://github.com/CodingWithCalvin/Otel4Vsix/actions/workflows/build.yml)
-[](https://www.nuget.org/packages/CodingWithCalvin.Otel4Vsix/)
-[](https://www.nuget.org/packages/CodingWithCalvin.Otel4Vsix/)
-[](https://opensource.org/licenses/MIT)
+🔭 Otel4Vsix
-OpenTelemetry support library for Visual Studio 2022+ extensions. Add distributed tracing, metrics, logging, and exception tracking to your VSIX with minimal configuration.
+
+
+
+
+
+
+
+
+ 🚀 Add OpenTelemetry observability to your Visual Studio extensions in minutes!
+
+
+Otel4Vsix is a powerful yet simple library that brings distributed tracing, metrics, logging, and exception tracking to your VSIX extensions with minimal configuration. See exactly what's happening inside your extension! 👀
---
-## Features
+## ✨ Features
-- **Distributed Tracing** - Track operations across your extension with spans and activities
-- **Metrics** - Counters, histograms, and gauges for performance monitoring
-- **Structured Logging** - OpenTelemetry-integrated logging via `ILogger`
-- **Exception Tracking** - Automatic and manual exception capture with full context
-- **Multiple Exporters** - OTLP (gRPC/HTTP) for production, Console for debugging
-- **VS-Specific Helpers** - Pre-configured spans for commands, tool windows, and documents
+| Feature | Description |
+|---------|-------------|
+| 📊 **Distributed Tracing** | Track operations across your extension with spans and activities |
+| 📈 **Metrics** | Counters, histograms, and gauges for performance monitoring |
+| 📝 **Structured Logging** | OpenTelemetry-integrated logging via `ILogger` |
+| 💥 **Exception Tracking** | Automatic and manual exception capture with full context |
+| 🔌 **Multiple Export Modes** | OTLP (gRPC/HTTP) for production, Debug output for development |
+| 🎯 **VS-Specific Helpers** | Pre-configured spans for commands, tool windows, and documents |
+| 🏗️ **Fluent Builder API** | Clean, chainable configuration |
+| 🔧 **Auto-Detection** | Automatically captures VS version, edition, OS, and architecture |
---
-## Installation
+## 📦 Installation
### Package Manager
```powershell
@@ -39,32 +53,33 @@ dotnet add package CodingWithCalvin.Otel4Vsix
---
-## Quick Start
+## 🚀 Quick Start
-### 1. Initialize Telemetry
+### 1️⃣ Initialize Telemetry
In your Visual Studio extension's `InitializeAsync` method:
```csharp
-using Otel4Vsix;
+using CodingWithCalvin.Otel4Vsix;
protected override async Task InitializeAsync(
CancellationToken cancellationToken,
IProgress progress)
{
- await base.InitializeAsync(cancellationToken, progress);
-
- VsixTelemetry.Initialize(new TelemetryConfiguration
- {
- ServiceName = "MyAwesomeExtension",
- ServiceVersion = "1.0.0",
- OtlpEndpoint = "http://localhost:4317",
- EnableConsoleExporter = true // Useful during development
- });
+ await JoinableTaskFactory.SwitchToMainThreadAsync();
+
+ VsixTelemetry.Configure()
+ .WithServiceName("MyAwesomeExtension")
+ .WithServiceVersion("1.0.0")
+ .WithVisualStudioAttributes(this) // 🪄 Auto-captures VS version & edition!
+ .WithEnvironmentAttributes() // 🖥️ Auto-captures OS & architecture!
+ .WithOtlpHttp("https://api.honeycomb.io")
+ .WithHeader("x-honeycomb-team", "your-api-key")
+ .Initialize();
}
```
-### 2. Shutdown on Dispose
+### 2️⃣ Shutdown on Dispose
```csharp
protected override void Dispose(bool disposing)
@@ -77,24 +92,60 @@ protected override void Dispose(bool disposing)
}
```
+🎉 **That's it!** Your extension is now observable!
+
---
-## Usage
+## 🎛️ Telemetry Modes
+
+Otel4Vsix supports multiple telemetry modes to fit your workflow:
+
+| Mode | Description |
+|------|-------------|
+| `Auto` | 🤖 **Default** - Uses OTLP if endpoint configured, otherwise Debug output |
+| `Debug` | 🐛 Outputs to VS Output window (visible when debugging) |
+| `Otlp` | 📡 Exports via OTLP protocol to your collector |
+| `Disabled` | 🔇 No telemetry collection |
+
+### 💡 Pro Tip: Development vs Production
+
+```csharp
+var builder = VsixTelemetry.Configure()
+ .WithServiceName(Vsix.Name)
+ .WithServiceVersion(Vsix.Version)
+ .WithVisualStudioAttributes(this)
+ .WithEnvironmentAttributes();
+
+#if !DEBUG
+// 📡 Only send to collector in Release builds
+builder
+ .WithOtlpHttp("https://api.honeycomb.io")
+ .WithHeader("x-honeycomb-team", apiKey);
+#endif
+
+builder.Initialize();
+```
-### Tracing
+In Debug builds, telemetry automatically outputs to the VS **Output** window! 🔍
+
+---
+
+## 📊 Usage
+
+### 🔍 Tracing
Create spans to track operations and their duration:
```csharp
-// Simple span
+// 🎯 Simple span
using var activity = VsixTelemetry.Tracer.StartActivity("ProcessFile");
activity?.SetTag("file.path", filePath);
activity?.SetTag("file.size", fileSize);
-// VS command span (with pre-configured attributes)
+// ⚡ VS command span (with pre-configured attributes)
using var commandSpan = VsixTelemetry.StartCommandActivity("MyExtension.DoSomething");
-// Nested spans for detailed tracing
+// 🪆 Nested spans for detailed tracing
using var outer = VsixTelemetry.Tracer.StartActivity("LoadProject");
{
using var inner = VsixTelemetry.Tracer.StartActivity("ParseProjectFile");
@@ -102,7 +153,7 @@ using var outer = VsixTelemetry.Tracer.StartActivity("LoadProject");
}
```
-#### Error Handling in Spans
+#### ⚠️ Error Handling in Spans
```csharp
using var activity = VsixTelemetry.StartActivity("RiskyOperation");
@@ -120,12 +171,12 @@ catch (Exception ex)
---
-### Metrics
+### 📈 Metrics
Record counters, histograms, and gauges:
```csharp
-// Counter - track occurrences
+// 🔢 Counter - track occurrences
var commandCounter = VsixTelemetry.GetOrCreateCounter(
"extension.commands.executed",
"{command}",
@@ -134,7 +185,7 @@ var commandCounter = VsixTelemetry.GetOrCreateCounter(
commandCounter?.Add(1,
new KeyValuePair("command.name", "FormatDocument"));
-// Histogram - track distributions (e.g., durations)
+// 📊 Histogram - track distributions (e.g., durations)
var durationHistogram = VsixTelemetry.GetOrCreateHistogram(
"extension.operation.duration",
"ms",
@@ -149,16 +200,17 @@ durationHistogram?.Record(stopwatch.ElapsedMilliseconds,
---
-### Logging
+### 📝 Logging
Structured logging with OpenTelemetry integration:
```csharp
-// Use the default logger
-VsixTelemetry.Logger.LogInformation("Processing file: {FilePath}", filePath);
-VsixTelemetry.Logger.LogWarning("File not found, using default: {DefaultPath}", defaultPath);
+// 📢 Quick logging methods
+VsixTelemetry.LogInformation("Processing file: {FilePath}", filePath);
+VsixTelemetry.LogWarning("File not found, using default: {DefaultPath}", defaultPath);
+VsixTelemetry.LogError(ex, "Failed to process {FileName}", fileName);
-// Create a typed logger for your class
+// 🏷️ Create a typed logger for your class
public class MyToolWindow
{
private readonly ILogger _logger = VsixTelemetry.CreateLogger();
@@ -167,29 +219,19 @@ public class MyToolWindow
{
_logger.LogDebug("Starting work...");
// ...
- _logger.LogInformation("Work completed successfully");
+ _logger.LogInformation("Work completed successfully! 🎉");
}
}
-
-// Log errors with exceptions
-try
-{
- // risky operation
-}
-catch (Exception ex)
-{
- VsixTelemetry.Logger.LogError(ex, "Failed to process {FileName}", fileName);
-}
```
---
-### Exception Tracking
+### 💥 Exception Tracking
Track exceptions with full context:
```csharp
-// Manual exception tracking
+// 🎯 Manual exception tracking
try
{
// risky operation
@@ -200,7 +242,7 @@ catch (Exception ex)
// Handle or rethrow
}
-// With additional context
+// 📋 With additional context
catch (Exception ex)
{
VsixTelemetry.TrackException(ex, new Dictionary
@@ -213,119 +255,148 @@ catch (Exception ex)
}
```
-> **Note**: Global unhandled exceptions are automatically captured when `EnableGlobalExceptionHandler` is `true` (default).
+> 💡 **Note**: Global unhandled exceptions are automatically captured when `EnableGlobalExceptionHandler` is `true` (default).
---
-## Configuration
-
-| Property | Type | Default | Description |
-|----------|------|---------|-------------|
-| `ServiceName` | `string` | `"VsixExtension"` | Service name for telemetry identification |
-| `ServiceVersion` | `string` | `"1.0.0"` | Service version |
-| `OtlpEndpoint` | `string` | `null` | OTLP collector endpoint (e.g., `http://localhost:4317`) |
-| `UseOtlpHttp` | `bool` | `false` | Use HTTP/protobuf instead of gRPC |
-| `OtlpHeaders` | `IDictionary` | empty | Custom headers for OTLP requests (auth, API keys) |
-| `EnableConsoleExporter` | `bool` | `false` | Output telemetry to console (for debugging) |
-| `EnableTracing` | `bool` | `true` | Enable distributed tracing |
-| `EnableMetrics` | `bool` | `true` | Enable metrics collection |
-| `EnableLogging` | `bool` | `true` | Enable structured logging |
-| `EnableGlobalExceptionHandler` | `bool` | `true` | Capture unhandled exceptions automatically |
-| `TraceSamplingRatio` | `double` | `1.0` | Trace sampling ratio (`0.0` - `1.0`) |
-| `IncludeVisualStudioContext` | `bool` | `true` | Add VS context to telemetry |
-| `ExceptionFilter` | `Func` | `null` | Filter which exceptions to track |
-| `ResourceAttributes` | `IDictionary` | empty | Custom resource attributes |
-| `ExportTimeoutMilliseconds` | `int` | `30000` | Export timeout |
-| `BatchExportScheduledDelayMilliseconds` | `int` | `5000` | Batch export delay |
-
-### Example: Production Configuration
+## ⚙️ Configuration Options
+
+### 🏗️ Fluent Builder Methods
+
+| Method | Description |
+|--------|-------------|
+| `WithServiceName(name)` | Set the service name for identification |
+| `WithServiceVersion(version)` | Set the service version |
+| `WithVisualStudioAttributes(serviceProvider)` | 🪄 Auto-capture VS version & edition |
+| `WithVisualStudioAttributes(version, edition)` | Manually set VS attributes |
+| `WithEnvironmentAttributes()` | 🖥️ Auto-capture OS version & architecture |
+| `WithResourceAttribute(key, value)` | Add custom resource attributes |
+| `WithOtlpHttp(endpoint)` | Configure OTLP HTTP export |
+| `WithOtlpGrpc(endpoint)` | Configure OTLP gRPC export |
+| `WithHeader(key, value)` | Add headers for OTLP requests |
+| `WithMode(mode)` | Set telemetry mode (Auto/Debug/Otlp/Disabled) |
+| `WithTracing(enabled)` | Enable/disable tracing |
+| `WithMetrics(enabled)` | Enable/disable metrics |
+| `WithLogging(enabled)` | Enable/disable logging |
+| `WithTraceSamplingRatio(ratio)` | Set trace sampling (0.0 - 1.0) |
+| `WithGlobalExceptionHandler(enabled)` | Enable/disable auto exception capture |
+| `WithExceptionFilter(filter)` | Filter which exceptions to track |
+| `WithExportTimeout(ms)` | Set export timeout in milliseconds |
+| `Initialize()` | 🚀 Initialize telemetry |
+
+### 📋 Auto-Captured Attributes
+
+When using the helper methods, these attributes are automatically captured:
+
+| Attribute | Source | Example |
+|-----------|--------|---------|
+| `vs.version` | `WithVisualStudioAttributes()` | `"17.12.35521.163"` |
+| `vs.edition` | `WithVisualStudioAttributes()` | `"Enterprise"` |
+| `os.version` | `WithEnvironmentAttributes()` | `"10.0.22631.0"` |
+| `host.arch` | `WithEnvironmentAttributes()` | `"X64"` or `"Arm64"` |
-```csharp
-VsixTelemetry.Initialize(new TelemetryConfiguration
-{
- ServiceName = "MyExtension",
- ServiceVersion = typeof(MyPackage).Assembly.GetName().Version.ToString(),
- OtlpEndpoint = "https://otel-collector.mycompany.com:4317",
- TraceSamplingRatio = 0.1, // Sample 10% of traces
- EnableConsoleExporter = false,
- ResourceAttributes =
- {
- { "deployment.environment", "production" },
- { "service.namespace", "visualstudio-extensions" }
- },
- ExceptionFilter = ex => !(ex is OperationCanceledException) // Ignore cancellations
-});
-```
+---
+
+## 🔌 Supported Backends
+
+Otel4Vsix exports telemetry via OTLP, which is supported by:
+
+| Backend | Link |
+|---------|------|
+| 🐝 Honeycomb | [honeycomb.io](https://www.honeycomb.io/) |
+| 🔵 Azure Monitor | [Application Insights](https://docs.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-overview) |
+| 🐕 Datadog | [datadoghq.com](https://www.datadoghq.com/) |
+| 🟡 Jaeger | [jaegertracing.io](https://www.jaegertracing.io/) |
+| 🔴 Grafana Tempo | [grafana.com/oss/tempo](https://grafana.com/oss/tempo/) |
+| 📮 Zipkin | [zipkin.io](https://zipkin.io/) |
+| ☁️ AWS X-Ray | [aws.amazon.com/xray](https://aws.amazon.com/xray/) |
+| 🌐 Google Cloud Trace | [cloud.google.com/trace](https://cloud.google.com/trace) |
+| 🔧 Any OTLP-compatible collector | — |
+
+---
-### Example: Using Custom Headers (Honeycomb, etc.)
+## 📋 Example: Full Production Setup
```csharp
-var config = new TelemetryConfiguration
-{
- ServiceName = "MyExtension",
- OtlpEndpoint = "https://api.honeycomb.io:443",
- UseOtlpHttp = true
-};
+using CodingWithCalvin.Otel4Vsix;
-// Add authentication headers
-config.OtlpHeaders["x-honeycomb-team"] = "your-api-key";
-config.OtlpHeaders["x-honeycomb-dataset"] = "your-dataset";
+public sealed class MyExtensionPackage : AsyncPackage
+{
+ protected override async Task InitializeAsync(
+ CancellationToken cancellationToken,
+ IProgress progress)
+ {
+ await JoinableTaskFactory.SwitchToMainThreadAsync();
+
+ var builder = VsixTelemetry.Configure()
+ .WithServiceName("MyExtension")
+ .WithServiceVersion(Vsix.Version)
+ .WithVisualStudioAttributes(this)
+ .WithEnvironmentAttributes()
+ .WithResourceAttribute("deployment.environment", "production")
+ .WithTraceSamplingRatio(0.1) // Sample 10% of traces
+ .WithExceptionFilter(ex => ex is not OperationCanceledException);
+
+#if !DEBUG
+ builder
+ .WithOtlpHttp("https://api.honeycomb.io")
+ .WithHeader("x-honeycomb-team", Config.HoneycombApiKey);
+#endif
+
+ builder.Initialize();
+
+ // ... rest of initialization
+ }
-VsixTelemetry.Initialize(config);
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ VsixTelemetry.Shutdown();
+ }
+ base.Dispose(disposing);
+ }
+}
```
---
-## Supported Backends
-
-Otel4Vsix exports telemetry via OTLP, which is supported by:
+## 📋 Requirements
-- [Jaeger](https://www.jaegertracing.io/)
-- [Zipkin](https://zipkin.io/)
-- [Azure Monitor / Application Insights](https://docs.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-overview)
-- [Honeycomb](https://www.honeycomb.io/)
-- [Datadog](https://www.datadoghq.com/)
-- [Grafana Tempo](https://grafana.com/oss/tempo/)
-- [AWS X-Ray](https://aws.amazon.com/xray/)
-- [Google Cloud Trace](https://cloud.google.com/trace)
-- Any OTLP-compatible collector
+| Requirement | Version |
+|-------------|---------|
+| .NET Framework | 4.8 |
+| Visual Studio | 2022 or later |
---
-## Requirements
+## 🤝 Contributing
-- **.NET Framework 4.8**
-- **Visual Studio 2022** or later
+Contributions are welcome! 🎉
-## Dependencies
-
-- OpenTelemetry (>= 1.7.0)
-- OpenTelemetry.Exporter.OpenTelemetryProtocol (>= 1.7.0)
-- OpenTelemetry.Exporter.Console (>= 1.7.0)
-- Microsoft.Extensions.Logging (>= 8.0.0)
-- Microsoft.VisualStudio.SDK (>= 17.0)
+1. 🍴 Fork the repository
+2. 🌿 Create your feature branch (`git checkout -b feature/AmazingFeature`)
+3. 💾 Commit your changes (`git commit -m 'Add some AmazingFeature'`)
+4. 📤 Push to the branch (`git push origin feature/AmazingFeature`)
+5. 🔃 Open a Pull Request
---
-## Contributing
-
-Contributions are welcome! Please feel free to submit a Pull Request.
+## 👥 Contributors
-1. Fork the repository
-2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
-3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
-4. Push to the branch (`git push origin feature/AmazingFeature`)
-5. Open a Pull Request
+
+
---
-## License
+## 📄 License
-This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
+This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details.
---
-## Acknowledgments
+## 🙏 Acknowledgments
-- Built on top of [OpenTelemetry .NET](https://github.com/open-telemetry/opentelemetry-dotnet)
-- Inspired by the need for better observability in Visual Studio extensions
+- Built on top of [OpenTelemetry .NET](https://github.com/open-telemetry/opentelemetry-dotnet) 🔭
+- Inspired by the need for better observability in Visual Studio extensions 💡
+- Made with ❤️ by [Coding with Calvin](https://github.com/CodingWithCalvin)