diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 70e43ee..5ea50ad 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -78,5 +78,5 @@ jobs: if: always() with: name: playwright-traces - path: .artifacts/playwright-traces/*-screenshot.jpeg + path: .artifacts/playwright-traces/* retention-days: 1 diff --git a/build/scripts/Packaging.fs b/build/scripts/Packaging.fs index a094c01..ed1f3c0 100644 --- a/build/scripts/Packaging.fs +++ b/build/scripts/Packaging.fs @@ -80,7 +80,7 @@ let downloadArtifacts (_:ParseResults) = async { Console.WriteLine($"Retrieving {asset.Name}"); let! fileData = httpClient.GetByteArrayAsync(asset.BrowserDownloadUrl) |> Async.AwaitTask - Console.WriteLine($"Saveing %i{fileData.Length} bytes to {f.FullName}") + Console.WriteLine($"Saving %i{fileData.Length} bytes to {f.FullName}") File.WriteAllBytes(f.FullName, fileData) f.Refresh() } |> Async.RunSynchronously diff --git a/examples/Example.AutoInstrumentation/README.md b/examples/Example.AutoInstrumentation/README.md index 42c48b9..7842eee 100644 --- a/examples/Example.AutoInstrumentation/README.md +++ b/examples/Example.AutoInstrumentation/README.md @@ -2,7 +2,7 @@ This is a very minimal .NET application that we use to validate our OpenTelemetry plugin loads correctly -This happens automated through our testing setup: +This happens automatically through our testing setup: ```bash $ ./build.sh test --test-suite=integration @@ -10,17 +10,14 @@ $ ./build.sh test --test-suite=integration Which ends up running the tests in `/tests/AutoInstrumentation.IntegrationTests` - -To quickly see the `DockerFile` in action run the following from the root of this repository. - +To quickly see the `DockerFile` in action run the following from the root of this repository. ```bash $ docker build -t example.autoinstrumentation:latest -f examples/Example.AutoInstrumentation/Dockerfile --no-cache . && \ - docker run -it --rm -p 5000:8080 --name autoin example.autoinstrumentation:latest + docker run -it --rm -p 5000:8080 --name autoin example.autoinstrumentation:latest ``` - ```bash docker build -t distribution.autoinstrumentation:latest -f examples/Example.AutoInstrumentation/distribution.Dockerfile --platform linux/arm64 --no-cache . && \ - docker run -it --rm -p 5000:8080 --name distri --platform linux/arm64 distribution.autoinstrumentation:latest + docker run -it --rm -p 5000:8080 --name distri --platform linux/arm64 distribution.autoinstrumentation:latest ``` \ No newline at end of file diff --git a/src/Elastic.OpenTelemetry/AutoInstrumentationPlugin.cs b/src/Elastic.OpenTelemetry/AutoInstrumentationPlugin.cs index 4b8a1f3..6bec4b5 100644 --- a/src/Elastic.OpenTelemetry/AutoInstrumentationPlugin.cs +++ b/src/Elastic.OpenTelemetry/AutoInstrumentationPlugin.cs @@ -52,7 +52,7 @@ public void MeterProviderInitialized(MeterProvider meterProvider) /// To configure tracing SDK before Auto Instrumentation configured SDK public TracerProviderBuilder BeforeConfigureTracerProvider(TracerProviderBuilder builder) => - builder.UseElasticDefaults(_skipOtlp, _logger); + builder.UseAutoInstrumentationElasticDefaults(_skipOtlp, _logger); /// To configure tracing SDK after Auto Instrumentation configured SDK public TracerProviderBuilder AfterConfigureTracerProvider(TracerProviderBuilder builder) => diff --git a/src/Elastic.OpenTelemetry/Elastic.OpenTelemetry.csproj b/src/Elastic.OpenTelemetry/Elastic.OpenTelemetry.csproj index 9cd8688..78f1ec3 100644 --- a/src/Elastic.OpenTelemetry/Elastic.OpenTelemetry.csproj +++ b/src/Elastic.OpenTelemetry/Elastic.OpenTelemetry.csproj @@ -22,13 +22,16 @@ + - + + + diff --git a/src/Elastic.OpenTelemetry/Extensions/TracerProviderBuilderExtensions.cs b/src/Elastic.OpenTelemetry/Extensions/TracerProviderBuilderExtensions.cs index 5285c81..7743bed 100644 --- a/src/Elastic.OpenTelemetry/Extensions/TracerProviderBuilderExtensions.cs +++ b/src/Elastic.OpenTelemetry/Extensions/TracerProviderBuilderExtensions.cs @@ -48,6 +48,23 @@ public static TracerProviderBuilder UseElasticDefaults(this TracerProviderBuilde .AddHttpClientInstrumentation() .AddGrpcClientInstrumentation() .AddEntityFrameworkCoreInstrumentation() + .AddElasticsearchClientInstrumentation() + .AddSqlClientInstrumentation() + .AddSource("Elastic.Transport") + .AddElasticProcessors(logger); + + if (!skipOtlp) + builder.AddOtlpExporter(); + + logger.LogConfiguredSignalProvider(nameof(Traces), nameof(TracerProviderBuilder)); + return builder; + } + + internal static TracerProviderBuilder UseAutoInstrumentationElasticDefaults(this TracerProviderBuilder builder, bool skipOtlp = false, ILogger? logger = null) + { + logger ??= NullLogger.Instance; + + builder .AddSource("Elastic.Transport") .AddElasticProcessors(logger); diff --git a/tests/Elastic.OpenTelemetry.EndToEndTests/DistributedFixture/ApmUIBrowserContext.cs b/tests/Elastic.OpenTelemetry.EndToEndTests/DistributedFixture/ApmUIBrowserContext.cs index 7090f07..299e7ad 100644 --- a/tests/Elastic.OpenTelemetry.EndToEndTests/DistributedFixture/ApmUIBrowserContext.cs +++ b/tests/Elastic.OpenTelemetry.EndToEndTests/DistributedFixture/ApmUIBrowserContext.cs @@ -14,11 +14,16 @@ public class ApmUIBrowserContext : IAsyncLifetime { private readonly IConfigurationRoot _configuration; private readonly string _serviceName; + private readonly string _playwrightScreenshotsDir; + private readonly List _output; - public ApmUIBrowserContext(IConfigurationRoot configuration, string serviceName) + public ApmUIBrowserContext(IConfigurationRoot configuration, string serviceName, string playwrightScreenshotsDir, List output) { _configuration = configuration; _serviceName = serviceName; + _playwrightScreenshotsDir = playwrightScreenshotsDir; + _output = output; + //"https://{instance}.apm.us-east-1.aws.elastic.cloud:443" // https://{instance}.kb.us-east-1.aws.elastic.cloud/app/apm/services?comparisonEnabled=true&environment=ENVIRONMENT_ALL&rangeFrom=now-15m&rangeTo=now&offset=1d var endpoint = configuration["E2E:Endpoint"]?.Trim() ?? string.Empty; @@ -84,6 +89,12 @@ public async Task OpenApmLandingPage(string testName) return page; } + private void Log(string message) + { + Console.WriteLine(message); + _output.Add(message); + } + public async Task WaitForServiceOnOverview(IPage page) { var timeout = (float)TimeSpan.FromSeconds(30).TotalMilliseconds; @@ -91,7 +102,9 @@ public async Task WaitForServiceOnOverview(IPage page) var servicesHeader = page.GetByRole(AriaRole.Heading, new() { Name = "Services" }); await servicesHeader.WaitForAsync(new() { State = WaitForSelectorState.Visible, Timeout = timeout }); - Console.WriteLine($"Search for service name: {_serviceName}"); + await page.ScreenshotAsync(new() { Path = Path.Join(_playwrightScreenshotsDir, "services-loaded.jpeg"), FullPage = true }); + + Log($"Search for service name: {_serviceName}"); //service.name : dotnet-e2e-* var queryBar = page.GetByRole(AriaRole.Searchbox, new() { Name = "Search services by name" }); @@ -99,10 +112,12 @@ public async Task WaitForServiceOnOverview(IPage page) await queryBar.FillAsync(_serviceName); await queryBar.PressAsync("Enter"); + await page.ScreenshotAsync(new() { Path = Path.Join(_playwrightScreenshotsDir, "filter-services.jpeg"), FullPage = true }); + Exception? observed = null; var refreshTimeout = (float)TimeSpan.FromSeconds(5).TotalMilliseconds; - for (var i = 0; i < 10; i++) + for (var i = 0; i < 20; i++) { try { @@ -113,10 +128,7 @@ public async Task WaitForServiceOnOverview(IPage page) } catch (Exception e) { - await page.ScreenshotAsync(new() { Path = $"squibble{i}.jpeg", FullPage = true }); - observed ??= e; - await page.ReloadAsync(); } finally { @@ -125,31 +137,32 @@ public async Task WaitForServiceOnOverview(IPage page) } if (observed != null) throw observed; //TODO proper rethrow with stack - } private int _unnamedTests; public async Task StopTrace(IPage page, bool success, [CallerMemberName] string? testName = null) { testName ??= $"unknown_test_{_unnamedTests++}"; - //only dump trace zip of test name is provided. + + //only dump trace zip if tests failed if (success) + { await page.Context.Tracing.StopAsync(new()); + } else { var root = DotNetRunApplication.GetSolutionRoot(); var zip = Path.Combine(root.FullName, ".artifacts", "playwright-traces", $"{testName}.zip"); await page.Context.Tracing.StopAsync(new() { Path = zip }); - using var archive = ZipFile.OpenRead(zip); - var entries = archive.Entries.Where(e => e.FullName.StartsWith("resources") && e.FullName.EndsWith(".jpeg")).ToList(); - var lastScreenshot = entries.MaxBy(e => e.LastWriteTime); - lastScreenshot?.ExtractToFile(Path.Combine(root.FullName, ".artifacts", "playwright-traces", $"{testName}-screenshot.jpeg")); + //using var archive = ZipFile.OpenRead(zip); + //var entries = archive.Entries.Where(e => e.FullName.StartsWith("resources") && e.FullName.EndsWith(".jpeg")).ToList(); + //var lastScreenshot = entries.MaxBy(e => e.LastWriteTime); + //lastScreenshot?.ExtractToFile(Path.Combine(root.FullName, ".artifacts", "playwright-traces", $"{testName}-screenshot.jpeg")); } await page.CloseAsync(); } - public async Task DisposeAsync() { await Browser.DisposeAsync(); diff --git a/tests/Elastic.OpenTelemetry.EndToEndTests/DistributedFixture/DistributedApplicationFixture.cs b/tests/Elastic.OpenTelemetry.EndToEndTests/DistributedFixture/DistributedApplicationFixture.cs index f44a2fc..4cf8500 100644 --- a/tests/Elastic.OpenTelemetry.EndToEndTests/DistributedFixture/DistributedApplicationFixture.cs +++ b/tests/Elastic.OpenTelemetry.EndToEndTests/DistributedFixture/DistributedApplicationFixture.cs @@ -6,7 +6,9 @@ using System.Security.Cryptography; using System.Text; using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Nullean.Xunit.Partitions.Sdk; +using Xunit.Abstractions; namespace Elastic.OpenTelemetry.EndToEndTests.DistributedFixture; @@ -18,11 +20,14 @@ public class DistributedApplicationFixture : IPartitionLifetime public bool Started => AspNetApplication.ProcessId.HasValue; + public string PlaywrightScreenshotsDirectory { get; } = Path.Combine(DotNetRunApplication.GetSolutionRoot().FullName, ".artifacts", "playwright-traces", "screenshots"); + private readonly List _output = []; public int? MaxConcurrency => null; private ApmUIBrowserContext? _apmUI; + public ApmUIBrowserContext ApmUI { get => _apmUI ?? @@ -42,11 +47,7 @@ public AspNetCoreExampleApplication AspNetApplication private static string ShaForCurrentTicks() { var buffer = Encoding.UTF8.GetBytes(DateTime.UtcNow.Ticks.ToString(DateTimeFormatInfo.InvariantInfo)); - - return BitConverter.ToString(SHA1.Create().ComputeHash(buffer)) - .Replace("-", "") - .ToLowerInvariant() - .Substring(0, 12); + return Convert.ToHexStringLower(SHA1.HashData(buffer)).Substring(0, 12); } public string FailureTestOutput() @@ -66,6 +67,18 @@ public string FailureTestOutput() } + public void WriteFailureTestOutput(ITestOutputHelper testOutputHelper) + { + foreach (var line in _output) + testOutputHelper.WriteLine(line); + + DotNetRunApplication.IterateOverLog(s => + { + Console.WriteLine(s); + testOutputHelper.WriteLine(s); + }); + } + public async Task DisposeAsync() { _aspNetApplication?.Dispose(); @@ -91,7 +104,7 @@ public async Task InitializeAsync() Log("Started ASP.NET application"); - ApmUI = new ApmUIBrowserContext(configuration, ServiceName); + ApmUI = new ApmUIBrowserContext(configuration, ServiceName, PlaywrightScreenshotsDirectory, _output); Log("Started UI Browser context"); diff --git a/tests/Elastic.OpenTelemetry.EndToEndTests/ServiceTests.cs b/tests/Elastic.OpenTelemetry.EndToEndTests/ServiceTests.cs index 4c5dcae..c3a79e1 100644 --- a/tests/Elastic.OpenTelemetry.EndToEndTests/ServiceTests.cs +++ b/tests/Elastic.OpenTelemetry.EndToEndTests/ServiceTests.cs @@ -34,7 +34,6 @@ public async Task LatencyShowsAGraph() .ToBeVisibleAsync(new() { Timeout = timeout }); } - public async Task InitializeAsync() => _page = await fixture.ApmUI.NewProfiledPage(_testName); public async Task DisposeAsync() @@ -45,6 +44,6 @@ public async Task DisposeAsync() if (success) return; - DotNetRunApplication.IterateOverLog(Output.WriteLine); + fixture.WriteFailureTestOutput(Output); } }