Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Opentelemetry #497

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft

Conversation

TheAngryByrd
Copy link
Contributor

@TheAngryByrd TheAngryByrd commented Apr 11, 2024

I used the Aspire dashboard. You can run it via docker:

docker run --rm -it -p 18888:18888 -p 4317:18889 -d --name aspire-dashboard mcr.microsoft.com/dotnet/nightly/aspire-dashboard:8.0.0-preview.4

Then you can run the Expecto.Tests project. It should export OpenTelemetry traces to Aspire.

Example of traces for tests showing up:

image

Clicking View on one of them with a red dot (red dot indicates there were exceptions/errors):

image

Clicking on the top span, you can see details about its run.

image

Clicking on one of the child spans you can see details such as Exception data.

image

image

I didn't show it here but you can also export logs that are associated with a test trace.

@@ -9,6 +10,20 @@ open Expecto.Logging.Message
open Helpers
open Mono.Cecil

//! The other option is to use a dedicated activity source for Expecto instead of adding it to the config
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One option is to have Expecto have a dedicate ActivitySource, the other is to add it the the Expecto config and allow a user to pass it along.

// only problem is the only way to update the config is by using the CLIArguments currently
// we would have to add a new CLIArgument but that doesn't really work as it's not a reallyCLI option
// or have another way of updating the config after it's been created
activitySource : ActivitySource option
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned, we can have an activity source in the config but there's not really a function that lets people update the config.

It might be more tricky to do this option if people are using the dotnet test integration so maybe a dedicated source is appropriate.

@@ -559,8 +580,63 @@ module Impl =
}
}

let inline internal setStatus (status : ActivityStatusCode) (span : Activity) =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of helper functions. Don't have to live here, just here for the moment.

Comment on lines 634 to 636
let span =
config.activitySource
|> createActivity (config.joinWith.format test.name)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the starting point for creating an span. Later we add additional information to it based on whether we pass/fail/ignore.

Comment on lines 26 to 33
use traceProvider =
Sdk
.CreateTracerProviderBuilder()
.AddSource(serviceName)
.SetResourceBuilder(resourceBuilder )
.AddOtlpExporter()
.Build()
let tracer = traceProvider.GetTracer(serviceName)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is adding the OpenTelemetry Exporter to the test project, mostly for demo purposes. Could create a separate project if desired.

let test =
Impl.testFromThisAssembly()
|> Option.orDefault (TestList ([], Normal))
|> Test.shuffle "."
runTestsWithCLIArgs [NUnit_Summary "bin/Expecto.Tests.TestResults.xml"] args test
runTestsWithCLIArgs [NUnit_Summary "bin/Expecto.Tests.TestResults.xml"; CLIArguments.ActivitySource activitySource] args test
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here a consumer would pass in the ActivitySource. Otherwise they'd have to add the dedicated Expecto one to the .AddSource call on line 29.

@farlee2121
Copy link
Collaborator

I'm still thinking over generalization approaches. I haven't found a clear nice answer yet.

Have you considered how the current span approach would behave with stress testing?

@TheAngryByrd
Copy link
Contributor Author

Oh one of the things missing too is how to integrate with dotnet test since we can't call addOpenTelemetry_SpanPerTest, we'd probably need some attribute that would do the call on our behalf when calling into testFromAssembly

@farlee2121
Copy link
Collaborator

Good point. That's a problem for shuffle and filter too.
I'm not sure why no discovery methods (like testFromThisAssembly) are officially exposed. They are available, if unofficially, under the Expecto.Impl module.

Seems like exposing such a method would be desirable for library extension.

@farlee2121
Copy link
Collaborator

Upon review, I think I got wrap up in my own line of thought and didn't interpret your statement correctly.

I'm not sure I see the incompatibility with dotnet test. Doesn't YoloDev.Expecto.TestSdk respect whatever is configured in main?

@TheAngryByrd
Copy link
Contributor Author

TheAngryByrd commented Apr 18, 2024

As far as I know, dotnet test doesn't care whats in your main/entrypoint. Yolodev uses Expecto.Impl.testFromAssembly to find tests.

I suppose you can call addOpenTelemetry_SpanPerTest for every testlist that uses [<Tests>]. There might be some tricks required to get ActivitySource to not be null, I'll have to look later.

Specifically the tricks that @baronfel has in https://github.com/baronfel/otel-startup-hook

@farlee2121
Copy link
Collaborator

Interesting. I hadn't thought of such a limitation.

Tests names can be listed via dotnet run, but that doesn't provide fully-hydrated Test records.
I don't see a clear solution to this either, since I'm not sure anonymous functions could be effectively transferred out-of-process.

@TheAngryByrd
Copy link
Contributor Author

So creating the TraceProvider statically:

do
    let provider = traceProvider()
    AppDomain.CurrentDomain.ProcessExit.Add(fun _ -> provider.Dispose())

And addOpenTelemetry_SpanPerTest has to be added per [<Tests>]. Which isn't too bad but might get annoying for people that have lots of [<Tests>] instead of a few.

After that, this does work for dotnet test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants