diff --git a/Directory.Packages.props b/Directory.Packages.props index 8d52d3668a..96a7326009 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -64,6 +64,7 @@ + diff --git a/examples/actor.open-telemetry/ActorOpenTelemetry.csproj b/examples/actor.open-telemetry/ActorOpenTelemetry.csproj new file mode 100644 index 0000000000..e9896ca5aa --- /dev/null +++ b/examples/actor.open-telemetry/ActorOpenTelemetry.csproj @@ -0,0 +1,16 @@ + + + Exe + net8.0 + 10 + + + + + + + + + + + diff --git a/examples/actor.open-telemetry/Program.cs b/examples/actor.open-telemetry/Program.cs new file mode 100644 index 0000000000..a5813bed26 --- /dev/null +++ b/examples/actor.open-telemetry/Program.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using Proto; +using Proto.OpenTelemetry; + +// Configure logging so Proto.Actor writes to the console +var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); +Log.SetLoggerFactory(loggerFactory); + +// Export spans and metrics to the console and an OTLP collector (e.g., TraceLens) +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("ActorOpenTelemetrySample")) + .AddProtoActorInstrumentation() + .AddConsoleExporter() + .AddOtlpExporter() + .Build(); + +using var meterProvider = Sdk.CreateMeterProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("ActorOpenTelemetrySample")) + .AddProtoActorInstrumentation() + .AddConsoleExporter() + .AddOtlpExporter() + .Build(); + +var system = new ActorSystem(ActorSystemConfig.Setup().WithMetrics()); + +// Simple actor that prints its trace id +var props = Props.FromFunc(ctx => +{ + if (ctx.Message is string msg) + { + Console.WriteLine($"[actor] received '{msg}' traceId={Activity.Current?.TraceId}"); + } + + return Task.CompletedTask; +}).WithTracing(); + +var pid = system.Root.Spawn(props); +var tracedRoot = system.Root.WithTracing(); + +using var rootActivity = new ActivitySource(Proto.ProtoTags.ActivitySourceName).StartActivity("root"); +tracedRoot.Send(pid, "hello"); + +// Flush telemetry before exiting +tracerProvider.ForceFlush(); +meterProvider.ForceFlush(); + +Console.WriteLine("Press enter to exit"); +Console.ReadLine(); diff --git a/examples/actor.open-telemetry/README.md b/examples/actor.open-telemetry/README.md new file mode 100644 index 0000000000..2c24b23b58 --- /dev/null +++ b/examples/actor.open-telemetry/README.md @@ -0,0 +1,22 @@ +# Actor OpenTelemetry + +Demonstrates Proto.Actor tracing and metrics using OpenTelemetry with console exporters. The sample runs a single actor system and prints spans and counters directly to the console, so no external collector is required. It also sends telemetry to an OTLP collector on `localhost:4317`, which can be visualized with [TraceLens](https://tracelens.io). + +## Run + +```bash +DOTNET_ReadyToRun=0 dotnet run --project examples/actor.open-telemetry +``` + +The program sends a message to an instrumented actor. The console output shows the trace identifier for the message and periodic dumps of Proto.Actor metrics. + +## Visualize with TraceLens + +Start TraceLens via Docker Compose to explore the emitted spans and counters: + +```bash +curl -L https://raw.githubusercontent.com/asynkron/TraceLens/main/docker-compose.yml -o docker-compose.yml +docker compose up +``` + +With TraceLens running, execute the example again. Open http://localhost:5001 to inspect traces and metrics.