Skip to content

Commit

Permalink
Enable additional default instrumentation (#188)
Browse files Browse the repository at this point in the history
Closes #123

Enables a default set of instrumentations when using the NuGet-based
install method. For auto-instrumentation, we currently only enable the
instrumentations shipped in the OTel auto instrumentation zip (i.e., Not
`OpenTelemetry.Instrumentation.ElasticsearchClient`). We will review
those once they are stable and potentially manually pull them into our
customised zip files.

This also extended the retries for E2E, so hopefully, they will be less
brittle in CI. It also takes additional screenshots, which help verify
the Apm UI during E2E tests.
  • Loading branch information
stevejgordon authored Dec 3, 2024
1 parent 4ad44d0 commit 8d4105a
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,5 @@ jobs:
if: always()
with:
name: playwright-traces
path: .artifacts/playwright-traces/*-screenshot.jpeg
path: .artifacts/playwright-traces/*
retention-days: 1
2 changes: 1 addition & 1 deletion build/scripts/Packaging.fs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ let downloadArtifacts (_:ParseResults<Build>) =
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
Expand Down
11 changes: 4 additions & 7 deletions examples/Example.AutoInstrumentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,22 @@

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
```

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
```
2 changes: 1 addition & 1 deletion src/Elastic.OpenTelemetry/AutoInstrumentationPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) =>
Expand Down
5 changes: 4 additions & 1 deletion src/Elastic.OpenTelemetry/Elastic.OpenTelemetry.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@
<PackageReference Include="OpenTelemetry" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.ElasticsearchClient" Version="1.0.0-beta.5" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.9.0-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" Version="1.0.0-beta.12" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="0.5.0-beta.6" />
<PackageReference Include="Polyfill" Version="4.4.0" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
<PackageReference Include="OpenTelemetry.Instrumentation.SqlClient" Version="1.9.0-beta.1" />
<PackageReference Include="Polyfill" Version="7.4.0" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
<PackageReference Include="NetEscapades.EnumGenerators" Version="1.0.0-beta09" PrivateAssets="all" ExcludeAssets="runtime" />
<PackageReference Include="System.Text.Json" Version="9.0.0" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('netstandard2')) OR $(TargetFramework.StartsWith('net4'))">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ public class ApmUIBrowserContext : IAsyncLifetime
{
private readonly IConfigurationRoot _configuration;
private readonly string _serviceName;
private readonly string _playwrightScreenshotsDir;
private readonly List<string> _output;

public ApmUIBrowserContext(IConfigurationRoot configuration, string serviceName)
public ApmUIBrowserContext(IConfigurationRoot configuration, string serviceName, string playwrightScreenshotsDir, List<string> 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;
Expand Down Expand Up @@ -84,25 +89,35 @@ public async Task<IPage> 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;

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" });
await queryBar.WaitForAsync(new() { State = WaitForSelectorState.Visible, Timeout = timeout });
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
{
Expand All @@ -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
{
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<string> _output = [];

public int? MaxConcurrency => null;

private ApmUIBrowserContext? _apmUI;

public ApmUIBrowserContext ApmUI
{
get => _apmUI ??
Expand All @@ -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()
Expand All @@ -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();
Expand All @@ -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");

Expand Down
3 changes: 1 addition & 2 deletions tests/Elastic.OpenTelemetry.EndToEndTests/ServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -45,6 +44,6 @@ public async Task DisposeAsync()
if (success)
return;

DotNetRunApplication.IterateOverLog(Output.WriteLine);
fixture.WriteFailureTestOutput(Output);
}
}

0 comments on commit 8d4105a

Please sign in to comment.