diff --git a/Botello.csproj b/Botello.csproj
index 3bec3ae..9f00ed2 100644
--- a/Botello.csproj
+++ b/Botello.csproj
@@ -14,7 +14,7 @@
true
- MSBuild custom logger that forwards build events to Azure Application Insights via OpenTelemetry.
+ MSBuild custom logger that forwards build events to Azure Application Insights or any OTLP-compatible backend via OpenTelemetry.
Botello
@@ -30,8 +30,9 @@
-
+
+
diff --git a/Config/ConfigurationLoader.cs b/Config/ConfigurationLoader.cs
index 2ae59af..18452c2 100644
--- a/Config/ConfigurationLoader.cs
+++ b/Config/ConfigurationLoader.cs
@@ -56,8 +56,43 @@ private static void ApplyLoggerParameters(string parameters, LoggerConfig config
if (string.IsNullOrEmpty(value))
continue;
- if (key.Equals("ConnectionString", StringComparison.OrdinalIgnoreCase))
+ // ── Exporter selection ──────────────────────────────────────
+ if (key.Equals("Exporter", StringComparison.OrdinalIgnoreCase))
+ {
+ if (Enum.TryParse(value, ignoreCase: true, out ExporterType exporter))
+ config.Exporter = exporter;
+ else
+ throw new InvalidOperationException(
+ $"Botello: Unknown Exporter value '{value}'. " +
+ "Valid values: AzureMonitor, Otlp.");
+ }
+ // ── Azure Monitor ───────────────────────────────────────────
+ else if (key.Equals("ConnectionString", StringComparison.OrdinalIgnoreCase))
config.ConnectionString = value;
+ // ── OTLP ────────────────────────────────────────────────────
+ else if (key.Equals("OtlpEndpoint", StringComparison.OrdinalIgnoreCase))
+ config.OtlpEndpoint = value;
+ else if (key.Equals("OtlpProtocol", StringComparison.OrdinalIgnoreCase))
+ {
+ if (Enum.TryParse(value, ignoreCase: true, out OtlpProtocolType protocol))
+ config.OtlpProtocol = protocol;
+ else
+ throw new InvalidOperationException(
+ $"Botello: Unknown OtlpProtocol value '{value}'. " +
+ "Valid values: Grpc, HttpProtobuf.");
+ }
+ else if (key.Equals("OtlpHeaders", StringComparison.OrdinalIgnoreCase))
+ config.OtlpHeaders = value;
+ else if (key.Equals("OtlpTimeout", StringComparison.OrdinalIgnoreCase))
+ {
+ if (int.TryParse(value, out var timeout) && timeout > 0)
+ config.OtlpTimeout = timeout;
+ else
+ throw new InvalidOperationException(
+ $"Botello: Invalid OtlpTimeout value '{value}'. " +
+ "Provide a positive integer (milliseconds).");
+ }
+ // ── Common ──────────────────────────────────────────────────
else if (key.Equals("ServiceName", StringComparison.OrdinalIgnoreCase))
config.ServiceName = value;
else if (key.Equals("MinimumLevel", StringComparison.OrdinalIgnoreCase))
diff --git a/Config/LoggerConfig.cs b/Config/LoggerConfig.cs
index 464b147..7131e59 100644
--- a/Config/LoggerConfig.cs
+++ b/Config/LoggerConfig.cs
@@ -2,16 +2,80 @@
namespace Botello.Config;
+///
+/// Selects the telemetry export back-end.
+///
+public enum ExporterType
+{
+ /// Azure Application Insights via the Azure Monitor exporter.
+ AzureMonitor,
+
+ /// Any OTLP-compatible collector (Jaeger, Grafana, Seq, Aspire Dashboard, etc.).
+ Otlp,
+}
+
+///
+/// Selects the OTLP transport protocol. Only relevant when
+/// is set to .
+///
+public enum OtlpProtocolType
+{
+ /// gRPC transport (default OTLP port 4317).
+ Grpc,
+
+ /// HTTP/protobuf transport (default OTLP port 4318).
+ HttpProtobuf,
+}
+
///
/// Configuration for the Botello logger.
/// Loaded from appsettings.json, environment variables (BOTELLO__*), and Logger.Parameters.
///
public sealed class LoggerConfig
{
- /// Azure Application Insights connection string. Required.
+ // ── Exporter selection ──────────────────────────────────────────────
+
+ ///
+ /// Selects the telemetry back-end. Defaults to .
+ /// Set to to export to any OTLP-compatible collector.
+ ///
+ public ExporterType Exporter { get; set; } = ExporterType.AzureMonitor;
+
+ // ── Azure Monitor settings ──────────────────────────────────────────
+
+ /// Azure Application Insights connection string. Required when Exporter is AzureMonitor.
public string? ConnectionString { get; set; }
- /// Service name reported in Application Insights. Defaults to "Botello".
+ // ── OTLP settings ───────────────────────────────────────────────────
+
+ ///
+ /// OTLP collector endpoint. Defaults to http://localhost:4317 for gRPC
+ /// or http://localhost:4318 for HTTP/protobuf. Only used when Exporter is Otlp.
+ ///
+ public string? OtlpEndpoint { get; set; }
+
+ ///
+ /// OTLP transport protocol. Defaults to .
+ /// Only used when Exporter is Otlp.
+ ///
+ public OtlpProtocolType OtlpProtocol { get; set; } = OtlpProtocolType.Grpc;
+
+ ///
+ /// Optional headers sent with every OTLP export request (comma-separated key=value pairs).
+ /// Example: Authorization=Bearer token123,X-Custom=value.
+ /// Only used when Exporter is Otlp.
+ ///
+ public string? OtlpHeaders { get; set; }
+
+ ///
+ /// Timeout in milliseconds for OTLP export requests. Defaults to 10 000 (10 s).
+ /// Only used when Exporter is Otlp.
+ ///
+ public int OtlpTimeout { get; set; } = 10_000;
+
+ // ── Common settings ─────────────────────────────────────────────────
+
+ /// Service name reported in telemetry. Defaults to "Botello".
public string ServiceName { get; set; } = "Botello";
/// Minimum log level to emit. Defaults to .
@@ -34,13 +98,18 @@ public sealed class LoggerConfig
internal void Validate()
{
- if (string.IsNullOrWhiteSpace(ConnectionString))
- throw new InvalidOperationException(
- "Botello: A connection string for Application Insights is required. " +
- "Provide it via appsettings.json (Botello:ConnectionString), " +
- "the APPLICATIONINSIGHTS_CONNECTION_STRING or BOTELLO__CONNECTIONSTRING " +
- "environment variable, or the Logger.Parameters CLI argument " +
- "(-logger:Botello.dll;ConnectionString=...).");
+ if (Exporter == ExporterType.AzureMonitor)
+ {
+ if (string.IsNullOrWhiteSpace(ConnectionString))
+ throw new InvalidOperationException(
+ "Botello: A connection string for Application Insights is required when " +
+ "Exporter is AzureMonitor. Provide it via appsettings.json (Botello:ConnectionString), " +
+ "the APPLICATIONINSIGHTS_CONNECTION_STRING or BOTELLO__CONNECTIONSTRING " +
+ "environment variable, or the Logger.Parameters CLI argument " +
+ "(-logger:Botello.dll;ConnectionString=...).");
+ }
+
+ // OTLP does not strictly require an endpoint — it falls back to localhost defaults.
if (string.IsNullOrWhiteSpace(ServiceName))
ServiceName = "Botello";
diff --git a/README.md b/README.md
index 9b205c0..e21175b 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
# Botello📝
-A custom logger that captures build events and forwards them to Open Telemetry compatible logging system such as **Azure Application Insights** as distributed traces and structured logs.
+A custom logger that captures build events and forwards them to any OpenTelemetry-compatible backend — **Azure Application Insights**, **Jaeger**, **Grafana Tempo**, **Seq**, **.NET Aspire Dashboard**, or any **OTLP collector** — as distributed traces and structured logs.
-Drop `Botello.dll` onto any `dotnet build` or `msbuild` command and get full build observability in App Insights — no code changes to your projects required.
+Drop `Botello.dll` onto any `dotnet build` or `msbuild` command and get full build observability — no code changes to your projects required.
---
@@ -12,17 +12,20 @@ Drop `Botello.dll` onto any `dotnet build` or `msbuild` command and get full bui
- **Distributed traces** — hierarchical spans for build → project → target with pass/fail status
- **Structured logs** — errors, warnings, messages, and lifecycle events with rich `customDimensions`
+- **Two exporters** — Azure Application Insights or any OTLP-compatible backend (gRPC / HTTP)
- **Zero-code-change** — attach as an external logger; nothing in your project files changes
- **Flexible configuration** — `appsettings.json`, environment variables, or inline CLI parameters
- **Parallel-build safe** — span keys are composite to avoid collisions across MSBuild nodes
-- **Guaranteed flush** — all in-flight telemetry is flushed to Azure Monitor before the process exits
+- **Guaranteed flush** — all in-flight telemetry is flushed before the process exits
---
## Requirements🔗
- .NET 10 SDK or later
-- An Azure Application Insights resource (connection string)
+- One of the following:
+ - An Azure Application Insights resource (connection string), **or**
+ - An OTLP-compatible collector endpoint (e.g. Jaeger, Grafana Tempo, Seq, Aspire Dashboard, OTel Collector)
---
@@ -46,7 +49,9 @@ Copy the entire `net10.0/` output directory to a stable location (e.g. `~/.msbui
## Quick Start🚀
-Set your Application Insights connection string as an environment variable and pass the logger path to `dotnet build`:
+### Azure Application Insights
+
+Set your connection string as an environment variable and pass the logger path to `dotnet build`:
```bash
export APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=00000000-...;IngestionEndpoint=https://..."
@@ -62,9 +67,18 @@ $env:APPLICATIONINSIGHTS_CONNECTION_STRING = "InstrumentationKey=00000000-...;In
dotnet build MyApp.sln -logger:"C:\tools\Botello\Botello.dll"
```
+### OTLP (Jaeger, Grafana, Aspire Dashboard, etc.)
+
+Point the logger at any OTLP collector — no Azure account needed:
+
+```bash
+dotnet build MyApp.sln \
+ -logger:"Botello.dll;Exporter=Otlp;OtlpEndpoint=http://localhost:4317"
+```
+
Within seconds of the build completing you will see:
-- A **`build`** span in the App Insights Transaction Search / Application Map
+- A **`build`** span in your tracing UI
- Child **`project: `** spans for each `.csproj` built
- Log entries for warnings, errors, and messages under the corresponding categories
@@ -72,7 +86,7 @@ Within seconds of the build completing you will see:
## Usage📝
-### Minimal — connection string from environment
+### Azure Monitor — connection string from environment
```bash
dotnet build -logger:Botello.dll
@@ -83,26 +97,81 @@ Botello reads the connection string from either of these environment variables (
1. `BOTELLO__CONNECTIONSTRING`
2. `APPLICATIONINSIGHTS_CONNECTION_STRING` (the standard App Insights variable)
-### Inline parameters
-
-Pass all options directly on the command line using semicolon-separated `Key=Value` pairs after the DLL path:
+### Azure Monitor — inline parameters
```bash
dotnet build MyApp.sln \
-logger:"Botello.dll;ConnectionString=InstrumentationKey=...;ServiceName=my-app;MinimumLevel=Debug"
```
-### Override a single option while keeping appsettings.json
+### OTLP — gRPC (default protocol)
```bash
-# Enable target-level spans (off by default) without touching appsettings.json
-dotnet build -logger:"Botello.dll;IncludeTargetEvents=true"
+# Jaeger with OTLP gRPC on default port 4317
+dotnet build MyApp.sln \
+ -logger:"Botello.dll;Exporter=Otlp;OtlpEndpoint=http://localhost:4317;ServiceName=my-app"
+```
+
+### OTLP — HTTP/protobuf
+
+```bash
+# Grafana Tempo or OTel Collector with HTTP/protobuf on default port 4318
+dotnet build MyApp.sln \
+ -logger:"Botello.dll;Exporter=Otlp;OtlpProtocol=HttpProtobuf;OtlpEndpoint=http://localhost:4318"
+```
+
+### OTLP — with authentication headers
+
+```bash
+# Grafana Cloud, Honeycomb, or any backend requiring auth headers
+dotnet build MyApp.sln \
+ -logger:"Botello.dll;Exporter=Otlp;OtlpEndpoint=https://otlp.example.com:4317;OtlpHeaders=Authorization=Bearer mytoken123"
+```
+
+### OTLP — .NET Aspire Dashboard
+
+```bash
+# The Aspire Dashboard listens on OTLP gRPC port 4317 by default
+docker run -d -p 18888:18888 -p 4317:18889 mcr.microsoft.com/dotnet/aspire-dashboard:latest
+
+dotnet build MyApp.sln \
+ -logger:"Botello.dll;Exporter=Otlp;OtlpEndpoint=http://localhost:4317;ServiceName=my-app"
+```
+
+Then open `http://localhost:18888` to see your build traces and logs.
+
+### OTLP — localhost defaults
+
+When using OTLP with no endpoint specified, Botello uses the OTel SDK defaults:
+- gRPC → `http://localhost:4317`
+- HTTP/protobuf → `http://localhost:4318`
+
+```bash
+# If your collector is on default ports, just set the exporter
+dotnet build -logger:"Botello.dll;Exporter=Otlp"
+```
+
+### OTLP — via environment variables
+
+```bash
+export BOTELLO__EXPORTER=Otlp
+export BOTELLO__OTLPENDPOINT=http://localhost:4317
+export BOTELLO__SERVICENAME=my-app
+
+dotnet build MyApp.sln -logger:Botello.dll
```
### Use with msbuild.exe
```bash
-msbuild MyApp.sln /logger:"C:\tools\Botello\Botello.dll;ConnectionString=...;ServiceName=MyApp"
+msbuild MyApp.sln /logger:"C:\tools\Botello\Botello.dll;Exporter=Otlp;OtlpEndpoint=http://localhost:4317"
+```
+
+### Override a single option while keeping appsettings.json
+
+```bash
+# Enable target-level spans (off by default) without touching appsettings.json
+dotnet build -logger:"Botello.dll;IncludeTargetEvents=true"
```
### Suppress noisy telemetry on large solutions
@@ -120,18 +189,40 @@ Configuration is merged from three sources in ascending priority (last wins):
| Priority | Source | Example |
|---|---|---|
-| 1 (lowest) | `appsettings.json` next to the DLL | `"Botello": { "ServiceName": "my-app" }` |
-| 2 | Environment variables (`BOTELLO__*`) | `BOTELLO__SERVICENAME=my-app` |
-| 3 (highest) | MSBuild `Logger.Parameters` | `-logger:"Botello.dll;ServiceName=my-app"` |
+| 1 (lowest) | `appsettings.json` next to the DLL | `"Botello": { "Exporter": "Otlp" }` |
+| 2 | Environment variables (`BOTELLO__*`) | `BOTELLO__EXPORTER=Otlp` |
+| 3 (highest) | MSBuild `Logger.Parameters` | `-logger:"Botello.dll;Exporter=Otlp"` |
### All options
+#### Exporter selection
+
| Key | Type | Default | Env var | Description |
|---|---|---|---|---|
-| `ConnectionString` | `string` | *(none)* | `BOTELLO__CONNECTIONSTRING` | **Required.** Azure Application Insights connection string. Also accepts `APPLICATIONINSIGHTS_CONNECTION_STRING` as a fallback. |
-| `ServiceName` | `string` | `Botello` | `BOTELLO__SERVICENAME` | Service name shown in Application Insights. Reported as the `service.name` OpenTelemetry resource attribute. |
-| `MinimumLevel` | `LogLevel` | `Information` | `BOTELLO__MINIMUMLEVEL` | Minimum log level to emit. Accepted values: `Trace` `Debug` `Information` `Warning` `Error` `Critical`. |
-| `IncludeMessages` | `bool` | `true` | `BOTELLO__INCLUDEMESSAGES` | Forward `MessageRaised` events. High-importance → `Information`, Normal → `Debug`, Low → `Trace`. |
+| `Exporter` | `ExporterType` | `AzureMonitor` | `BOTELLO__EXPORTER` | Selects the telemetry back-end. Values: `AzureMonitor`, `Otlp`. |
+
+#### Azure Monitor options (used when Exporter = AzureMonitor)
+
+| Key | Type | Default | Env var | Description |
+|---|---|---|---|---|
+| `ConnectionString` | `string` | *(none)* | `BOTELLO__CONNECTIONSTRING` | **Required.** App Insights connection string. Also accepts `APPLICATIONINSIGHTS_CONNECTION_STRING` as a fallback. |
+
+#### OTLP options (used when Exporter = Otlp)
+
+| Key | Type | Default | Env var | Description |
+|---|---|---|---|---|
+| `OtlpEndpoint` | `string` | *(SDK default)* | `BOTELLO__OTLPENDPOINT` | Collector endpoint URL. Defaults to `http://localhost:4317` (gRPC) or `http://localhost:4318` (HTTP/protobuf). |
+| `OtlpProtocol` | `OtlpProtocolType` | `Grpc` | `BOTELLO__OTLPPROTOCOL` | Transport protocol. Values: `Grpc`, `HttpProtobuf`. |
+| `OtlpHeaders` | `string` | *(none)* | `BOTELLO__OTLPHEADERS` | Comma-separated `key=value` headers sent with every export request. |
+| `OtlpTimeout` | `int` | `10000` | `BOTELLO__OTLPTIMEOUT` | Export request timeout in milliseconds. |
+
+#### Common options
+
+| Key | Type | Default | Env var | Description |
+|---|---|---|---|---|
+| `ServiceName` | `string` | `Botello` | `BOTELLO__SERVICENAME` | Service name reported as the `service.name` OTel resource attribute. |
+| `MinimumLevel` | `LogLevel` | `Information` | `BOTELLO__MINIMUMLEVEL` | Minimum log level to emit. Values: `Trace` `Debug` `Information` `Warning` `Error` `Critical`. |
+| `IncludeMessages` | `bool` | `true` | `BOTELLO__INCLUDEMESSAGES` | Forward `MessageRaised` events. High → `Information`, Normal → `Debug`, Low → `Trace`. |
| `IncludeWarnings` | `bool` | `true` | `BOTELLO__INCLUDEWARNINGS` | Forward `WarningRaised` events at `Warning` level. |
| `IncludeErrors` | `bool` | `true` | `BOTELLO__INCLUDEERRORS` | Forward `ErrorRaised` events at `Error` level and mark the active span as failed. |
| `IncludeProjectEvents` | `bool` | `true` | `BOTELLO__INCLUDEPROJECTEVENTS` | Emit spans and `Debug` log entries for `ProjectStarted` / `ProjectFinished`. |
@@ -141,11 +232,35 @@ Boolean options in `Logger.Parameters` accept `true`/`false`, `1`/`0`, `yes`/`no
### appsettings.json reference
+#### Azure Monitor
+
```json
{
"Botello": {
- "ConnectionString": "",
- "ServiceName": "Botello",
+ "Exporter": "AzureMonitor",
+ "ConnectionString": "InstrumentationKey=...",
+ "ServiceName": "my-app",
+ "MinimumLevel": "Information",
+ "IncludeMessages": true,
+ "IncludeWarnings": true,
+ "IncludeErrors": true,
+ "IncludeProjectEvents": true,
+ "IncludeTargetEvents": false
+ }
+}
+```
+
+#### OTLP
+
+```json
+{
+ "Botello": {
+ "Exporter": "Otlp",
+ "OtlpEndpoint": "http://localhost:4317",
+ "OtlpProtocol": "Grpc",
+ "OtlpHeaders": "",
+ "OtlpTimeout": 10000,
+ "ServiceName": "my-app",
"MinimumLevel": "Information",
"IncludeMessages": true,
"IncludeWarnings": true,
@@ -179,7 +294,7 @@ Spans are marked `Ok` on success and `Error` (with a description) on failure. Wh
### Log categories
-Logs appear in Application Insights under the following category names (visible as the logger name / `customDimensions`):
+Logs appear under the following category names (visible as the logger name / `customDimensions`):
| Category | Events |
|---|---|
@@ -212,7 +327,7 @@ Every telemetry item carries these OpenTelemetry resource attributes:
## CI/CD Integration
-### GitHub Actions
+### GitHub Actions — Azure Monitor
```yaml
- name: Build
@@ -223,6 +338,20 @@ Every telemetry item carries these OpenTelemetry resource attributes:
-logger:"${{ github.workspace }}/tools/Botello/Botello.dll;ServiceName=my-app;MinimumLevel=Warning"
```
+### GitHub Actions — OTLP
+
+```yaml
+- name: Build
+ env:
+ BOTELLO__EXPORTER: Otlp
+ BOTELLO__OTLPENDPOINT: ${{ secrets.OTLP_ENDPOINT }}
+ BOTELLO__OTLPHEADERS: "Authorization=Bearer ${{ secrets.OTLP_TOKEN }}"
+ BOTELLO__SERVICENAME: my-app
+ run: |
+ dotnet build MyApp.sln \
+ -logger:"${{ github.workspace }}/tools/Botello/Botello.dll"
+```
+
### Azure Pipelines
```yaml
@@ -243,7 +372,7 @@ Every telemetry item carries these OpenTelemetry resource attributes:
1. MSBuild loads `Botello.dll` and calls `AppInsightsLogger.Initialize()`.
2. `ConfigurationLoader` merges settings from `appsettings.json`, `BOTELLO__*` environment variables, and the `Logger.Parameters` string.
-3. `OtelPipelineManager` initialises a `TracerProvider` and an `ILoggerFactory`, both pointed at Azure Monitor.
+3. `OtelPipelineManager` initialises a `TracerProvider` and an `ILoggerFactory`, routing to either Azure Monitor or an OTLP collector based on the `Exporter` setting.
4. `AppInsightsLogger` subscribes to the requested MSBuild event sources.
5. During the build, each event creates an OTel span and/or log entry.
6. When MSBuild calls `Shutdown()`, `OtelPipelineManager.Dispose()` flushes all in-flight batches — traces first, then logs — before returning, so no telemetry is dropped.
@@ -257,6 +386,7 @@ Every telemetry item carries these OpenTelemetry resource attributes:
| `Microsoft.Build.Framework` | 18.3.3 |
| `Microsoft.Build.Utilities.Core` | 18.3.3 |
| `Azure.Monitor.OpenTelemetry.Exporter` | 1.6.0 |
+| `OpenTelemetry.Exporter.OpenTelemetryProtocol` | 1.15.0 |
| `OpenTelemetry.Extensions.Hosting` | 1.15.0 |
| `Microsoft.Extensions.Configuration.Json` | 10.0.3 |
| `Microsoft.Extensions.Configuration.EnvironmentVariables` | 10.0.3 |
diff --git a/Telemetry/OtelPipelineManager.cs b/Telemetry/OtelPipelineManager.cs
index f7a79ee..1f99039 100644
--- a/Telemetry/OtelPipelineManager.cs
+++ b/Telemetry/OtelPipelineManager.cs
@@ -3,6 +3,7 @@
using Microsoft.Extensions.Logging;
using Botello.Config;
using OpenTelemetry;
+using OpenTelemetry.Exporter;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
@@ -11,7 +12,8 @@ namespace Botello.Telemetry;
///
/// Owns the OTel pipeline lifetime: a for spans
-/// and an for log records, both exporting to Azure Monitor.
+/// and an for log records, exporting to either
+/// Azure Monitor or any OTLP-compatible collector.
/// Call once before use and to flush.
///
internal sealed class OtelPipelineManager : IDisposable
@@ -52,11 +54,13 @@ public void Dispose()
if (_disposed) return;
_disposed = true;
- // Traces first, then logs — preserves causality in App Insights.
+ // Traces first, then logs — preserves causality in the back-end.
_tracerProvider?.Dispose();
_loggerFactory?.Dispose();
}
+ // ── Resource ────────────────────────────────────────────────────────
+
private static ResourceBuilder BuildResourceBuilder(LoggerConfig config) =>
ResourceBuilder
.CreateDefault()
@@ -67,12 +71,23 @@ private static ResourceBuilder BuildResourceBuilder(LoggerConfig config) =>
["telemetry.sdk.name"] = "Botello",
});
- private static TracerProvider BuildTracerProvider(LoggerConfig config, ResourceBuilder resourceBuilder) =>
- Sdk.CreateTracerProviderBuilder()
+ // ── Traces ──────────────────────────────────────────────────────────
+
+ private static TracerProvider BuildTracerProvider(LoggerConfig config, ResourceBuilder resourceBuilder)
+ {
+ var builder = Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(resourceBuilder)
- .AddSource(ActivitySourceName)
- .AddAzureMonitorTraceExporter(o => o.ConnectionString = config.ConnectionString)
- .Build()!;
+ .AddSource(ActivitySourceName);
+
+ if (config.Exporter == ExporterType.Otlp)
+ builder.AddOtlpExporter(o => ApplyOtlpOptions(config, o));
+ else
+ builder.AddAzureMonitorTraceExporter(o => o.ConnectionString = config.ConnectionString);
+
+ return builder.Build()!;
+ }
+
+ // ── Logs ────────────────────────────────────────────────────────────
private static ILoggerFactory BuildLoggerFactory(LoggerConfig config, ResourceBuilder resourceBuilder) =>
LoggerFactory.Create(builder =>
@@ -81,9 +96,32 @@ private static ILoggerFactory BuildLoggerFactory(LoggerConfig config, ResourceBu
builder.AddOpenTelemetry(otel =>
{
otel.SetResourceBuilder(resourceBuilder);
- otel.IncludeFormattedMessage = true; // store rendered message, not just the template
- otel.IncludeScopes = true; // emit BeginScope values as customDimensions
- otel.AddAzureMonitorLogExporter(o => o.ConnectionString = config.ConnectionString);
+ otel.IncludeFormattedMessage = true;
+ otel.IncludeScopes = true;
+
+ if (config.Exporter == ExporterType.Otlp)
+ otel.AddOtlpExporter(o => ApplyOtlpOptions(config, o));
+ else
+ otel.AddAzureMonitorLogExporter(o => o.ConnectionString = config.ConnectionString);
});
});
+
+ // ── OTLP shared configuration ──────────────────────────────────────
+
+ private static void ApplyOtlpOptions(LoggerConfig config, OtlpExporterOptions options)
+ {
+ options.Protocol = config.OtlpProtocol switch
+ {
+ OtlpProtocolType.HttpProtobuf => OtlpExportProtocol.HttpProtobuf,
+ _ => OtlpExportProtocol.Grpc,
+ };
+
+ if (!string.IsNullOrWhiteSpace(config.OtlpEndpoint))
+ options.Endpoint = new Uri(config.OtlpEndpoint);
+
+ if (!string.IsNullOrWhiteSpace(config.OtlpHeaders))
+ options.Headers = config.OtlpHeaders;
+
+ options.TimeoutMilliseconds = config.OtlpTimeout;
+ }
}
diff --git a/appsettings.json b/appsettings.json
index 774c0a2..4c0b11c 100644
--- a/appsettings.json
+++ b/appsettings.json
@@ -1,12 +1,40 @@
{
"Botello": {
+ // Exporter back-end: "AzureMonitor" (default) or "Otlp".
+ // Override via env var: BOTELLO__EXPORTER
+ "Exporter": "AzureMonitor",
+
+ // ── Azure Monitor settings (used when Exporter is "AzureMonitor") ──
+
// Azure Application Insights connection string.
// Override via env var: BOTELLO__CONNECTIONSTRING
// or the standard env var: APPLICATIONINSIGHTS_CONNECTION_STRING
// or via Logger.Parameters on the CLI: -logger:Botello.dll;ConnectionString=...
"ConnectionString": "",
- // Logical service name shown in Application Insights.
+ // ── OTLP settings (used when Exporter is "Otlp") ───────────────────
+
+ // OTLP collector endpoint.
+ // Defaults to http://localhost:4317 (gRPC) or http://localhost:4318 (HTTP/protobuf).
+ // Override via env var: BOTELLO__OTLPENDPOINT
+ "OtlpEndpoint": "",
+
+ // OTLP transport protocol: "Grpc" (default) or "HttpProtobuf".
+ // Override via env var: BOTELLO__OTLPPROTOCOL
+ "OtlpProtocol": "Grpc",
+
+ // Optional headers sent with every OTLP export request.
+ // Comma-separated key=value pairs, e.g. "Authorization=Bearer token,X-Tenant=abc".
+ // Override via env var: BOTELLO__OTLPHEADERS
+ "OtlpHeaders": "",
+
+ // Timeout in milliseconds for OTLP export requests. Default: 10000 (10 s).
+ // Override via env var: BOTELLO__OTLPTIMEOUT
+ "OtlpTimeout": 10000,
+
+ // ── Common settings ─────────────────────────────────────────────────
+
+ // Logical service name shown in telemetry.
// Override via env var: BOTELLO__SERVICENAME
"ServiceName": "Botello",