open-telemetry / opentelemetry-dotnet

The OpenTelemetry .NET Client
https://opentelemetry.io
Apache License 2.0
3.27k stars 769 forks source link

Tracing not working in Blazor WebAssembly application #2816

Open mkuennek opened 2 years ago

mkuennek commented 2 years ago

Bug Report

OpenTelemetry.Exporter.Console 1.2.0-rc1

Runtime version (e.g. net461, net48, netcoreapp3.1, net5.0 etc. You can find this information from the *.csproj file):

net6.0 Blazor app running as WebAssembly

Symptom

A clear and concise description of what the bug is.

Adding tracing to a Blazor wasm app results in System.PlatformNotSupportedException during runtime as Process.GetCurrentProcess() is not supported in Blazor wasm. The stacktrace is:

System.AggregateException: One or more errors occurred. (The type initializer for 'OpenTelemetry.Resources.ResourceBuilder' threw an exception.) ---> System.TypeInitializationException: The type initializer for 'OpenTelemetry.Resources.ResourceBuilder' threw an exception. ---> System.PlatformNotSupportedException: System.Diagnostics.Process is not supported on this platform. at System.Diagnostics.Process.GetCurrentProcess() at OpenTelemetry.Resources.ResourceBuilder..cctor() --- End of inner exception stack trace --- at OpenTelemetry.Trace.TracerProviderBuilderBase..ctor() at OpenTelemetry.Trace.TracerProviderBuilderSdk..ctor() at OpenTelemetry.Sdk.CreateTracerProviderBuilder() at Program. $(String[] args) in C:\Sources\Privat\OtelBlazorTracingBug\Program.cs:line 12 --- End of inner exception stack trace ---

The reasons seems to be the in the DefaultResource property of the ResourceBuilder class, see https://github.com/open-telemetry/opentelemetry-dotnet/blob/d56f925a734aacfb84cca14a8992655e774867f1/src/OpenTelemetry/Resources/ResourceBuilder.cs#L33

What did you expect to see?

Tracing to work in a Blazor wasm app.

What did you see instead?

Runtime exception

Reproduce

See the following repo for a simple reproduction: https://github.com/mkuennek/OtelBlazorTracingBug

Additional Context

Also discovered elsewhere, see https://wolfgang-ziegler.com/blog/otel-dotnet-blazor

JoeShook commented 2 years ago

Blazor WebAssembly Experimenting

I was also interested in applying OpenTelemetry to a BlazorWebAssembly. So, I took the evidence from this bug report and forked the code. I created a hosted Blazor WebAssembly app. This means my WebAssembly is served up from a Blazor Server, hosted in Kestrel in my case. It is the default template having a counter page that is only client side. Also, a weather page where the Blazor client calls the Blazor server. Now we have a simple template to experiment with. I attempted to commit my changes in steps so one could maybe follow my discovery path.

Long story short

Blazor WebAssembly is single threaded. The friction here is OpenTelemetry DotNet is dependent on EventListener and ActivityListener and some kind of EventSource strategy. This part is a little murky in my head so I will surely say something inaccurate. But the behavior results in a background service that is responsible for supplying activities that start traceIds that we see in our Spans.

I feel like this is not a bug. OpenTelemetry Dotnet is not compatible with a single threaded architecture. It should either be a feature request or should be written as an exporter built for a single threaded environment in the opentelemetry-dotnet-contrib. I am not a maintainer but It does seem like that might make sense. Not sure of what the name would be. I have a bit of an appetite for taking on the work after spending a multiple days digging around on this. On the other hand, there is an issue, Real multithreading (on supported browsers) that would solve this. While it is in the .NET 7 Planning, it is still not assigned. I see @danroth27 commented on it as late as March 3rd, so I am hoping it might get some traction because initiating telemetry data from Blazor WebAssembly using System.Diagnositics would be consistent.

This is the long story

First off, I was successful in generating trace and log telemetry from Blazor WebAssembly to Blazor Server (asp.net). I included a DockerComposeSetup folder so you can experiment the same way I did.

If you try to register tracing instrumentation the traditional way through dependency injection you will discover a few things. First fixup also noted in this bug report.

private static string GetCurrentProcessName()
{
    try
    {
        return System.Diagnostics.Process.GetCurrentProcess().ProcessName;
    }
    catch (Exception)
    {
        // Expected when hosted in WebAssembly
        return null;
    }
}

The ZipkinExporter and OtlpTraceExporter both call HttpClient.Send rather than HttpClient.SendAsync. Not sure why the Async technique was not used everywhere. Maybe someone can enlighten me? If you follow the HttpClient.Send code you will eventually find your way to dotnet runtime code attributed with the following, [UnsupportedOSPlatform("browser")].

Anyway, I applied this logic.

if (RuntimeInformation.RuntimeIdentifier != "browser-wasm")
{
    return this.HttpClient.Send(request, cancellationToken);
}
else
{
    return this.HttpClient.SendAsync(request, CancellationToken.None).GetAwaiter().GetResult();
}

Note that I had to compile with DebugType = portable. Without it I would not be able to step into any WebAssembly code nor the Otel libraries. This one got me for a few days. I chased my tail thinking I had a Visual Studio 2022 issue. Also, I would launch my WebAssembly as the startup and just test the counter page at times rather than the traditional server project as the startup. My Blazor debug and hot reload is a mixed bag of frustration.

I ran into errors like Blazor WebAssembly Error : Cannot wait on monitors on this runtime.. I guess I already kind of knew this would not work but I had to see it fail for myself.

Some where along this journey I moved away from dependency injection and went straight to the counter page and FetchData page and constructed the tracing and logging code. I failed getting Otlp tracing even when targeting the http receiver vs the grpc receiver. Remember this is web hosted and http2 is not supported so you wouldn’t be successful exporting over grcp. But what I was seeing is still the Cannot wait on monitors error above. So, I tried ZipkinExporter and it was successful. Then I included logging and that was also successful. It is cool to see logs and using Loki link to the telemetry including the propagated telemetry to the backend services. That is what I went on this journey to see.
The Blazor Server is nothing more than the same thing as the Examples.AspNetCore project. Nothing challenging there.

Logging Note: I created a OtlpHttpLogsExportClient so as it was missing to satisfy my experiment.

But… Why is this even working? Let’s try to publish it and run it. After all my startup times are egregious. Something is going on with my environment I suspect. So, the published version started to show issues. The Counter page would log one time and then fail. It appears that request headers like cache = no-cache, so I would start getting provisional header errors in the Edge browser debugger. On the FetchData page, logging and tracing worked but the traceparent header was missing so propagation to backend services did not work. I mean it could be more things, but traceparent was obviously missing. It is like we are missing some code after “publish” or in the debugger the TraceProvider that we create with Sdk.CreateTracerProviderBuilder() and the “Extension” friends hooking *listeners allow us to get away with a not so exact single threaded platform.

I just know after this journey the Blazor Assembly we have today would need a single threaded friendly Exporter. The exporter classes and processors are coded in such a way that you can’t inject a ServiceName without adopting the background listener model. Look at ZipkinExporter’s, SetLocalEndpointFromResource and start waking back. There is just no way to get a ServiceName in there. OtlpExporter is a bit different but essentially the design does not allow a way to inject a Resource so I can set the ServiceName.

And therefore, I am back to thinking the way to move forward on Blazor WebAssembly is to construct a new OtlpExporter over http only, that does not rely on background services. But does it have a place in this repo or the contrib repo?

ProgrammerAL commented 2 years ago

Adding a note to this issue so others don't have to research like I just did. It's possible this issue is now fixed after #3405 was merged in. I haven't been able to test it yet because the current version of code hasn't been pushed to a new NuGet package. #3405 was merged on June 24, 2022, and the latest NuGet was published June 3rd, 2022. So once the next NuGet is published we can test again on Blazor WebAssembly.

JoeShook commented 2 years ago

While @ProgrammerAl pointed out the default service name issue may be fixed after #3405 the lack of threading support in Blazor WASM would still be a blocker. But... It appears further experimentation with my sample project could be revisited now that there is experimental support for threading in Blazor WASM. wasm-experimental: Experimental support for WebAssembly multithreading

snow-jallen commented 1 year ago

Is there a way to do this now?

vincentnl commented 1 year ago

I am working on a proof of concept to get 'poor man's' tracing up and running using the opentelemetry endpoint as a target. Got it working - and basic tracing enabled.

Feel free to comment or add:

https://github.com/vincentnl/TracesForBlazor

gunawanyuwono commented 11 months ago

I am working on a proof of concept to get 'poor man's' tracing up and running using the opentelemetry endpoint as a target. Got it working - and basic tracing enabled.

Feel free to comment or add:

https://github.com/vincentnl/TracesForBlazor

Hi vincentnl, I try the project, but not working

Note: I attach opentelemetry config and docker compose file in zip file collector-docker.zip I set otlp for http at port 4319 I change port for otlp http

TracerForBlazorOptions options = new TracerForBlazorOptions() { Url = "http://127.0.0.1:4319", SendType = OpenTelemetrySendTypes.HttpProto, SendInterval = TimeSpan.FromSeconds(3) };

Regards,

Gunawan

vincentnl commented 11 months ago

Dear Gunawan,

I think you are referring to distributed tracing? As that the trace spans different parts of the system.

That is not implemented - as stated it is purely Poor man’s standin for blazor wasm.

It will not work over server or hybrid.

On Jan 3, 2024, at 05:17, gunawanyuwono @.***> wrote:



I am working on a proof of concept to get 'poor man's' tracing up and running using the opentelemetry endpoint as a target. Got it working - and basic tracing enabled.

Feel free to comment or add:

https://github.com/vincentnl/TracesForBlazor

Hi vincentnl, can we use this library for cross-origin (other host project for api target of web assembly)? So we have 2 project 1.Project webassembly 2.Project Web API web assembly client will send request to Web API Project (I use ASP.NET Web API Core 6.0)

Regards,

Gunawan

— Reply to this email directly, view it on GitHubhttps://github.com/open-telemetry/opentelemetry-dotnet/issues/2816#issuecomment-1874829532, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AA7CUF7YYETKYLPPNT6DJ33YMTLVHAVCNFSM5M2NSECKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBXGQ4DEOJVGMZA. You are receiving this because you are subscribed to this thread.Message ID: @.***>

luisabreu commented 10 months ago

Hello guys,

any news on this issue? I mean, has there been any developments on the blazor wasm /distributed tracing incompatibilities lately?

madskonradsen commented 5 months ago

Multithreading has now been postponed to .Net10 - https://github.com/dotnet/aspnetcore/issues/17730#issuecomment-2059602250 - and it could be postponed even further(was originally planned for .Net5).

verdie-g commented 2 months ago

@cijothomas could https://github.com/open-telemetry/opentelemetry-dotnet/pull/5838 also solve this issue as we are removing custom threads from OTEL?

REscobar commented 6 days ago

I can confirm that a Blazor WASM app is able to push traces to a Zipkin endpoint using the changes proposed in this pr #5838 and the exporter changes proposed in this branch https://github.com/verdie-g/opentelemetry-dotnet/commit/02eb4503bcd5dfcb415351f1791ddc3b913eb5a7, only the batch exporter works though, since simple exporter is still sync over async on that branch, I did not test other signals or exporters.

Some screenshots: image image image

Configuration on blazor app

builder.Services.AddOpenTelemetry()
    .ConfigureResource(r =>
    {
        r.AddService("BlazorWASMFrontend");
    })
    .WithTracing(tracerProviderBuilder =>
        tracerProviderBuilder.AddHttpClientInstrumentation()
        .AddSource("BlazorWASMFrontend")
        //.AddConsoleExporter()
        .AddZipkinExporter(o =>
        {
            o.ExportProcessorType = ExportProcessorType.Batch;
            o.HttpClientFactory = () =>
            {
                return new HttpClient();
            };
            o.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
        })
    )