Remove StackExchangeRedis Instrumenation (#3346)
Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
parent
8df82a2d7f
commit
c90ab4a2f2
|
@ -14,14 +14,6 @@ on:
|
|||
- '**.md'
|
||||
|
||||
jobs:
|
||||
redis-test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: [netcoreapp3.1,net6.0]
|
||||
steps:
|
||||
- run: 'echo "No build required"'
|
||||
|
||||
sql-test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
|
|
|
@ -11,18 +11,6 @@ on:
|
|||
- '**.md'
|
||||
|
||||
jobs:
|
||||
redis-test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: [netcoreapp3.1,net6.0]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Run redis docker-compose.integration
|
||||
run: docker-compose --file=test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/docker-compose.yml --file=build/docker-compose.${{ matrix.version }}.yml --project-directory=. up --exit-code-from=tests --build
|
||||
|
||||
sql-test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
|
|
|
@ -52,10 +52,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testdata", "testdata", "{77
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp.AspNetCore.3.1", "test\TestApp.AspNetCore.3.1\TestApp.AspNetCore.3.1.csproj", "{F2F81E76-6A0E-466B-B673-EBBF1A9ED075}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.StackExchangeRedis", "src\OpenTelemetry.Instrumentation.StackExchangeRedis\OpenTelemetry.Instrumentation.StackExchangeRedis.csproj", "{6B681D72-D68A-44CC-8C75-53B9A322E6EC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.StackExchangeRedis.Tests", "test\OpenTelemetry.Instrumentation.StackExchangeRedis.Tests\OpenTelemetry.Instrumentation.StackExchangeRedis.Tests.csproj", "{CA98AF29-0852-4ADD-A66B-7E96266EE7B7}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{E359BB2B-9AEC-497D-B321-7DF2450C3B8E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Jaeger", "src\OpenTelemetry.Exporter.Jaeger\OpenTelemetry.Exporter.Jaeger.csproj", "{8D47E3CF-9AE3-42FE-9084-FEB72D9AD769}"
|
||||
|
@ -274,14 +270,6 @@ Global
|
|||
{F2F81E76-6A0E-466B-B673-EBBF1A9ED075}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F2F81E76-6A0E-466B-B673-EBBF1A9ED075}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2F81E76-6A0E-466B-B673-EBBF1A9ED075}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6B681D72-D68A-44CC-8C75-53B9A322E6EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6B681D72-D68A-44CC-8C75-53B9A322E6EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6B681D72-D68A-44CC-8C75-53B9A322E6EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6B681D72-D68A-44CC-8C75-53B9A322E6EC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CA98AF29-0852-4ADD-A66B-7E96266EE7B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CA98AF29-0852-4ADD-A66B-7E96266EE7B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CA98AF29-0852-4ADD-A66B-7E96266EE7B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CA98AF29-0852-4ADD-A66B-7E96266EE7B7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8D47E3CF-9AE3-42FE-9084-FEB72D9AD769}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8D47E3CF-9AE3-42FE-9084-FEB72D9AD769}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8D47E3CF-9AE3-42FE-9084-FEB72D9AD769}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|
|
@ -57,7 +57,6 @@ libraries](https://github.com/open-telemetry/opentelemetry-specification/blob/ma
|
|||
* [ASP.NET Core](./src/OpenTelemetry.Instrumentation.AspNetCore/README.md)
|
||||
* [Grpc.Net.Client](./src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md)
|
||||
* [HTTP clients](./src/OpenTelemetry.Instrumentation.Http/README.md)
|
||||
* [Redis client](./src/OpenTelemetry.Instrumentation.StackExchangeRedis/README.md)
|
||||
* [SQL client](./src/OpenTelemetry.Instrumentation.SqlClient/README.md)
|
||||
|
||||
Here are the [exporter
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
<NewtonsoftJsonPkgVer>[12.0.2,13.0)</NewtonsoftJsonPkgVer>
|
||||
<MoqPkgVer>[4.14.5,5.0)</MoqPkgVer>
|
||||
<RabbitMQClientPkgVer>[6.1.0,7.0)</RabbitMQClientPkgVer>
|
||||
<StackExchangeRedisPkgVer>[2.1.58,3.0)</StackExchangeRedisPkgVer>
|
||||
<SwashbuckleAspNetCorePkgVer>[6.2.3]</SwashbuckleAspNetCorePkgVer>
|
||||
<XUnitRunnerVisualStudioPkgVer>[2.4.3,3.0)</XUnitRunnerVisualStudioPkgVer>
|
||||
<XUnitPkgVer>[2.4.1,3.0)</XUnitPkgVer>
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
<MicrosoftSourceLinkGitHubPkgVer>[1.0.0,2.0)</MicrosoftSourceLinkGitHubPkgVer>
|
||||
<OpenTracingPkgVer>[0.12.1,0.13)</OpenTracingPkgVer>
|
||||
<OTelPreviousStableVer>1.3.0</OTelPreviousStableVer>
|
||||
<StackExchangeRedisPkgVer>[2.1.58,3.0)</StackExchangeRedisPkgVer>
|
||||
<StyleCopAnalyzersPkgVer>[1.2.0-beta.354,2.0)</StyleCopAnalyzersPkgVer>
|
||||
<SystemCollectionsImmutablePkgVer>1.4.0</SystemCollectionsImmutablePkgVer>
|
||||
<SystemDiagnosticSourcePkgVer>6.0.0</SystemDiagnosticSourcePkgVer>
|
||||
|
|
|
@ -83,8 +83,8 @@ may be used as a reference.
|
|||
|
||||
The [inspiration of the OpenTelemetry
|
||||
project](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#instrumentation-libraries)
|
||||
is to make every library observable out of the box by having
|
||||
them call OpenTelemetry API directly. However, many libraries will not have such
|
||||
is to make every library observable out of the box by having them call
|
||||
OpenTelemetry API directly. However, many libraries will not have such
|
||||
integration, and as such there is a need for a separate library which would
|
||||
inject such calls, using mechanisms such as wrapping interfaces, subscribing to
|
||||
library-specific callbacks, or translating existing telemetry into OpenTelemetry
|
||||
|
@ -103,11 +103,11 @@ the following instrumentation libraries. The individual docs for them describes
|
|||
the library they instrument, and steps for enabling them.
|
||||
|
||||
* [ASP.NET](../../../src/OpenTelemetry.Instrumentation.AspNet/README.md)
|
||||
* [ASP.NET Core](../../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md)
|
||||
* [gRPC client](../../../src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md)
|
||||
* [ASP.NET
|
||||
Core](../../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md)
|
||||
* [gRPC
|
||||
client](../../../src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md)
|
||||
* [HTTP clients](../../../src/OpenTelemetry.Instrumentation.Http/README.md)
|
||||
* [Redis
|
||||
client](../../../src/OpenTelemetry.Instrumentation.StackExchangeRedis/README.md)
|
||||
* [SQL client](../../../src/OpenTelemetry.Instrumentation.SqlClient/README.md)
|
||||
|
||||
More community contributed instrumentations are available in [OpenTelemetry .NET
|
||||
|
@ -135,13 +135,9 @@ modify to emit activities directly.*
|
|||
Writing an instrumentation library typically involves 3 steps.
|
||||
|
||||
1. First step involves "hijacking" into the target library. The exact mechanism
|
||||
of this depends on the target library itself. For example, StackExchangeRedis
|
||||
library allows hooks into the library, and the [StackExchangeRedis
|
||||
instrumentation
|
||||
library](../../../src/OpenTelemetry.Instrumentation.StackExchangeRedis/README.md)
|
||||
in this case, leverages them. Another example is System.Data.SqlClient for
|
||||
.NET Framework, which publishes events using `EventSource`. The [SqlClient
|
||||
instrumentation
|
||||
of this depends on the target library itself. For example,
|
||||
System.Data.SqlClient for .NET Framework, which publishes events using
|
||||
`EventSource`. The [SqlClient instrumentation
|
||||
library](../../../src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs),
|
||||
in this case subscribes to the `EventSource` callbacks.
|
||||
|
||||
|
@ -151,20 +147,19 @@ Writing an instrumentation library typically involves 3 steps.
|
|||
target instrumented library. Irrespective of the actual mechanism used in
|
||||
first step, this should be uniform across all instrumentation libraries. The
|
||||
`ActivitySource` must be created using the name and version of the
|
||||
instrumentation library (eg:
|
||||
"OpenTelemetry.Instrumentation.StackExchangeRedis") and *not* the
|
||||
instrumented library (eg: "StackExchange.Redis")
|
||||
1. [Context Propagation](../../../src/OpenTelemetry.Api/README.md#context-propagation):
|
||||
If your library initiates out of process requests or
|
||||
accepts them, the library needs to
|
||||
[inject the `PropagationContext`](../../../examples/MicroserviceExample/Utils/Messaging/MessageSender.cs)
|
||||
to outgoing requests and
|
||||
[extract the context](../../../examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs)
|
||||
and hydrate the Activity/Baggage upon receiving incoming requests.
|
||||
This is only required if you're using your own protocol to
|
||||
communicate over the wire.
|
||||
(i.e. If you're using an already instrumented HttpClient or GrpcClient,
|
||||
this is already provided to you and **do not require**
|
||||
instrumentation library (eg: "OpenTelemetry.Instrumentation.Http") and *not*
|
||||
the instrumented library (eg: "System.Net.Http")
|
||||
1. [Context
|
||||
Propagation](../../../src/OpenTelemetry.Api/README.md#context-propagation):
|
||||
If your library initiates out of process requests or accepts them, the
|
||||
library needs to [inject the
|
||||
`PropagationContext`](../../../examples/MicroserviceExample/Utils/Messaging/MessageSender.cs)
|
||||
to outgoing requests and [extract the
|
||||
context](../../../examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs)
|
||||
and hydrate the Activity/Baggage upon receiving incoming requests. This is
|
||||
only required if you're using your own protocol to communicate over the
|
||||
wire. (i.e. If you're using an already instrumented HttpClient or
|
||||
GrpcClient, this is already provided to you and **do not require**
|
||||
injecting/extracting `PropagationContext` explicitly again.)
|
||||
|
||||
3. Third step is an optional step, and involves providing extension methods on
|
||||
|
@ -179,8 +174,8 @@ Writing an instrumentation library typically involves 3 steps.
|
|||
extension method on `TracerProviderBuilder`. Inside this extension
|
||||
method, it should call the `AddInstrumentation` method, and `AddSource`
|
||||
method to enable its ActivitySource for the provider. An example
|
||||
instrumentation using this approach is [StackExchangeRedis
|
||||
instrumentation](../../../src/OpenTelemetry.Instrumentation.StackExchangeRedis/TracerProviderBuilderExtensions.cs)
|
||||
instrumentation using this approach is [SqlClient
|
||||
instrumentation](../../../src/OpenTelemetry.Instrumentation.SqlClient/TracerProviderBuilderExtensions.cs)
|
||||
|
||||
2. If the instrumentation library does not requires any state management
|
||||
tied to that of `TracerProvider`, then providing `TracerProviderBuilder`
|
||||
|
@ -326,16 +321,16 @@ A demo sampler is shown [here](./MySampler.cs).
|
|||
|
||||
## Resource Detector
|
||||
|
||||
OpenTelemetry .NET SDK provides a resource detector for detecting
|
||||
resource information from the `OTEL_RESOURCE_ATTRIBUTES` and
|
||||
`OTEL_SERVICE_NAME` environment variables.
|
||||
OpenTelemetry .NET SDK provides a resource detector for detecting resource
|
||||
information from the `OTEL_RESOURCE_ATTRIBUTES` and `OTEL_SERVICE_NAME`
|
||||
environment variables.
|
||||
|
||||
Custom resource detectors can be implemented:
|
||||
|
||||
* ResourceDetectors should inherit from
|
||||
`OpenTelemetry.Resources.IResourceDetector`, (which belongs
|
||||
to the [OpenTelemetry](../../../src/OpenTelemetry/README.md)
|
||||
package), and implement the `Detect` method.
|
||||
`OpenTelemetry.Resources.IResourceDetector`, (which belongs to the
|
||||
[OpenTelemetry](../../../src/OpenTelemetry/README.md) package), and implement
|
||||
the `Detect` method.
|
||||
|
||||
A demo ResourceDetector is shown [here](./MyResourceDetector.cs).
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="StackExchange.Redis" Version="$(StackExchangeRedisPkgVer)" />
|
||||
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\SpanAttributeConstants.cs" Link="Includes\SpanAttributeConstants.cs" />
|
||||
</ItemGroup>
|
||||
|
@ -27,7 +26,6 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.GrpcNetClient\OpenTelemetry.Instrumentation.GrpcNetClient.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.Http\OpenTelemetry.Instrumentation.Http.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.StackExchangeRedis\OpenTelemetry.Instrumentation.StackExchangeRedis.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.OpenTelemetryProtocol\OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.ZPages\OpenTelemetry.Exporter.ZPages.csproj" />
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace Examples.Console
|
|||
/// <param name="args">Arguments from command line.</param>
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Parser.Default.ParseArguments<JaegerOptions, ZipkinOptions, PrometheusOptions, MetricsOptions, LogsOptions, GrpcNetClientOptions, HttpClientOptions, RedisOptions, ZPagesOptions, ConsoleOptions, OpenTelemetryShimOptions, OpenTracingShimOptions, OtlpOptions, InMemoryOptions>(args)
|
||||
Parser.Default.ParseArguments<JaegerOptions, ZipkinOptions, PrometheusOptions, MetricsOptions, LogsOptions, GrpcNetClientOptions, HttpClientOptions, ZPagesOptions, ConsoleOptions, OpenTelemetryShimOptions, OpenTracingShimOptions, OtlpOptions, InMemoryOptions>(args)
|
||||
.MapResult(
|
||||
(JaegerOptions options) => TestJaegerExporter.Run(options.Host, options.Port),
|
||||
(ZipkinOptions options) => TestZipkinExporter.Run(options.Uri),
|
||||
|
@ -55,7 +55,6 @@ namespace Examples.Console
|
|||
(LogsOptions options) => TestLogs.Run(options),
|
||||
(GrpcNetClientOptions options) => TestGrpcNetClient.Run(),
|
||||
(HttpClientOptions options) => TestHttpClient.Run(),
|
||||
(RedisOptions options) => TestRedis.Run(options.Uri),
|
||||
(ZPagesOptions options) => TestZPagesExporter.Run(),
|
||||
(ConsoleOptions options) => TestConsoleExporter.Run(options),
|
||||
(OpenTelemetryShimOptions options) => TestOTelShimWithConsoleExporter.Run(options),
|
||||
|
@ -127,13 +126,6 @@ namespace Examples.Console
|
|||
{
|
||||
}
|
||||
|
||||
[Verb("redis", HelpText = "Specify the options required to test Redis with Zipkin")]
|
||||
internal class RedisOptions
|
||||
{
|
||||
[Option('u', "uri", HelpText = "Please specify the uri of Zipkin backend", Required = true)]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zpages", HelpText = "Specify the options required to test ZPages")]
|
||||
internal class ZPagesOptions
|
||||
{
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
// <copyright file="TestRedis.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Trace;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace Examples.Console
|
||||
{
|
||||
internal class TestRedis
|
||||
{
|
||||
internal static object Run(string zipkinUri)
|
||||
{
|
||||
/*
|
||||
* Setup redis service inside local docker.
|
||||
* docker run --name opentelemetry-redis-test -d -p 6379:6379 redis
|
||||
*
|
||||
* If you face any issue with the first command, do the following ones:
|
||||
* docker exec -it opentelemetry-redis-test sh
|
||||
* redis-cli
|
||||
* set bind 0.0.0.0
|
||||
* save
|
||||
*/
|
||||
|
||||
// connect to the redis server. The default port 6379 will be used.
|
||||
var connection = ConnectionMultiplexer.Connect("localhost");
|
||||
|
||||
// Configure exporter to export traces to Zipkin
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddZipkinExporter(o =>
|
||||
{
|
||||
o.Endpoint = new Uri(zipkinUri);
|
||||
})
|
||||
.AddRedisInstrumentation(connection, options =>
|
||||
{
|
||||
// changing flushinterval from 10s to 5s
|
||||
options.FlushInterval = TimeSpan.FromSeconds(5);
|
||||
})
|
||||
.AddSource("redis-test")
|
||||
.Build();
|
||||
|
||||
ActivitySource activitySource = new ActivitySource("redis-test");
|
||||
|
||||
// select a database (by default, DB = 0)
|
||||
var db = connection.GetDatabase();
|
||||
|
||||
// Create a scoped activity. It will end automatically when using statement ends
|
||||
using (activitySource.StartActivity("Main"))
|
||||
{
|
||||
System.Console.WriteLine("About to do a busy work");
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
DoWork(db, activitySource);
|
||||
}
|
||||
}
|
||||
|
||||
System.Console.Write("Press ENTER to stop.");
|
||||
System.Console.ReadLine();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void DoWork(IDatabase db, ActivitySource activitySource)
|
||||
{
|
||||
// Start another activity. If another activity was already started, it'll use that activity as the parent activity.
|
||||
// In this example, the main method already started a activity, so that'll be the parent activity, and this will be
|
||||
// a child activity.
|
||||
using Activity activity = activitySource.StartActivity("DoWork");
|
||||
try
|
||||
{
|
||||
db.StringSet("key", "value " + DateTime.Now.ToLongDateString());
|
||||
|
||||
System.Console.WriteLine("Doing busy work");
|
||||
Thread.Sleep(1000);
|
||||
|
||||
// run a command, in this case a GET
|
||||
var myVal = db.StringGet("key");
|
||||
|
||||
System.Console.WriteLine(myVal);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException e)
|
||||
{
|
||||
activity.SetStatus(Status.Error.WithDescription(e.ToString()));
|
||||
}
|
||||
|
||||
// Annotate our activity to capture metadata about our operation
|
||||
var attributes = new Dictionary<string, object>
|
||||
{
|
||||
{ "use", "demo" },
|
||||
};
|
||||
ActivityTagsCollection eventTags = new ActivityTagsCollection(attributes);
|
||||
activity.AddEvent(new ActivityEvent("Invoking DoWork", default, eventTags));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.get -> System.Action<System.Diagnostics.Activity, StackExchange.Redis.Profiling.IProfiledCommand>
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.set -> void
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.FlushInterval.get -> System.TimeSpan
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.FlushInterval.set -> void
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.SetVerboseDatabaseStatements.get -> bool
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.SetVerboseDatabaseStatements.set -> void
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.StackExchangeRedisCallsInstrumentationOptions() -> void
|
||||
OpenTelemetry.Trace.TracerProviderBuilderExtensions
|
||||
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, StackExchange.Redis.IConnectionMultiplexer connection = null, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions> configure = null) -> OpenTelemetry.Trace.TracerProviderBuilder
|
|
@ -1,10 +0,0 @@
|
|||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.get -> System.Action<System.Diagnostics.Activity, StackExchange.Redis.Profiling.IProfiledCommand>
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.Enrich.set -> void
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.FlushInterval.get -> System.TimeSpan
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.FlushInterval.set -> void
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.SetVerboseDatabaseStatements.get -> bool
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.SetVerboseDatabaseStatements.set -> void
|
||||
OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions.StackExchangeRedisCallsInstrumentationOptions() -> void
|
||||
OpenTelemetry.Trace.TracerProviderBuilderExtensions
|
||||
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddRedisInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, StackExchange.Redis.IConnectionMultiplexer connection = null, System.Action<OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentationOptions> configure = null) -> OpenTelemetry.Trace.TracerProviderBuilder
|
|
@ -1,34 +0,0 @@
|
|||
// <copyright file="AssemblyInfo.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.StackExchangeRedis.Tests" + AssemblyInfo.PublicKey)]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)]
|
||||
|
||||
#if SIGNED
|
||||
internal static class AssemblyInfo
|
||||
{
|
||||
public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898";
|
||||
public const string MoqPublicKey = ", PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7";
|
||||
}
|
||||
#else
|
||||
internal static class AssemblyInfo
|
||||
{
|
||||
public const string PublicKey = "";
|
||||
public const string MoqPublicKey = "";
|
||||
}
|
||||
#endif
|
|
@ -1,113 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 1.0.0-rc9.4
|
||||
|
||||
Released 2022-Jun-03
|
||||
|
||||
## 1.0.0-rc9.3
|
||||
|
||||
Released 2022-Apr-15
|
||||
|
||||
* Removes .NET Framework 4.6.1. The minimum .NET Framework version supported is
|
||||
.NET 4.6.2.
|
||||
([#3190](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3190))
|
||||
|
||||
* Bumped minimum required version of `Microsoft.Extensions.Options` to 3.1.0.
|
||||
([#2582](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3196))
|
||||
|
||||
## 1.0.0-rc9.2
|
||||
|
||||
Released 2022-Apr-12
|
||||
|
||||
## 1.0.0-rc9.1
|
||||
|
||||
Released 2022-Mar-30
|
||||
|
||||
## 1.0.0-rc10 (broken. use 1.0.0-rc9.1 and newer)
|
||||
|
||||
Released 2022-Mar-04
|
||||
|
||||
## 1.0.0-rc9
|
||||
|
||||
Released 2022-Feb-02
|
||||
|
||||
## 1.0.0-rc8
|
||||
|
||||
Released 2021-Oct-08
|
||||
|
||||
* Adds SetVerboseDatabaseStatements option to allow setting more detailed
|
||||
database statement tag values.
|
||||
* Adds Enrich option to allow enriching activities from the source profiled
|
||||
command objects.
|
||||
* Removes upper constraint for Microsoft.Extensions.Options dependency.
|
||||
([#2179](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2179))
|
||||
|
||||
## 1.0.0-rc7
|
||||
|
||||
Released 2021-Jul-12
|
||||
|
||||
## 1.0.0-rc6
|
||||
|
||||
Released 2021-Jun-25
|
||||
|
||||
* `AddRedisInstrumentation` extension will now resolve `IConnectionMultiplexer`
|
||||
& `StackExchangeRedisCallsInstrumentationOptions` through DI when
|
||||
OpenTelemetry.Extensions.Hosting is in use.
|
||||
([#2110](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2110))
|
||||
|
||||
## 1.0.0-rc5
|
||||
|
||||
Released 2021-Jun-09
|
||||
|
||||
## 1.0.0-rc4
|
||||
|
||||
Released 2021-Apr-23
|
||||
|
||||
* Activities are now created with the `db.system` attribute set for usage during
|
||||
sampling.
|
||||
([#1984](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1984))
|
||||
|
||||
## 1.0.0-rc3
|
||||
|
||||
Released 2021-Mar-19
|
||||
|
||||
## 1.0.0-rc2
|
||||
|
||||
Released 2021-Jan-29
|
||||
|
||||
## 1.0.0-rc1.1
|
||||
|
||||
Released 2020-Nov-17
|
||||
|
||||
## 0.8.0-beta.1
|
||||
|
||||
Released 2020-Nov-5
|
||||
|
||||
## 0.7.0-beta.1
|
||||
|
||||
Released 2020-Oct-16
|
||||
|
||||
* Span Status is populated as per new spec
|
||||
([#1313](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1313))
|
||||
|
||||
## 0.6.0-beta.1
|
||||
|
||||
Released 2020-Sep-15
|
||||
|
||||
## 0.5.0-beta.2
|
||||
|
||||
Released 2020-08-28
|
||||
|
||||
## 0.4.0-beta.2
|
||||
|
||||
Released 2020-07-24
|
||||
|
||||
* First beta release
|
||||
|
||||
## 0.3.0-beta
|
||||
|
||||
Released 2020-07-23
|
||||
|
||||
* Initial release
|
|
@ -1,209 +0,0 @@
|
|||
// <copyright file="RedisProfilerEntryToActivityConverter.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using OpenTelemetry.Trace;
|
||||
using StackExchange.Redis.Profiling;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.StackExchangeRedis.Implementation
|
||||
{
|
||||
internal static class RedisProfilerEntryToActivityConverter
|
||||
{
|
||||
private static readonly Lazy<Func<object, (string, string)>> MessageDataGetter = new(() =>
|
||||
{
|
||||
var redisAssembly = typeof(IProfiledCommand).Assembly;
|
||||
Type profiledCommandType = redisAssembly.GetType("StackExchange.Redis.Profiling.ProfiledCommand");
|
||||
Type messageType = redisAssembly.GetType("StackExchange.Redis.Message");
|
||||
Type scriptMessageType = redisAssembly.GetType("StackExchange.Redis.RedisDatabase+ScriptEvalMessage");
|
||||
|
||||
var messageDelegate = CreateFieldGetter<object>(profiledCommandType, "Message", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var scriptDelegate = CreateFieldGetter<string>(scriptMessageType, "script", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var commandAndKeyFetcher = new PropertyFetcher<string>("CommandAndKey");
|
||||
|
||||
if (messageDelegate == null)
|
||||
{
|
||||
return new Func<object, (string, string)>(source => (null, null));
|
||||
}
|
||||
|
||||
return new Func<object, (string, string)>(source =>
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
var message = messageDelegate(source);
|
||||
if (message == null)
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
string script = null;
|
||||
if (message.GetType() == scriptMessageType)
|
||||
{
|
||||
script = scriptDelegate.Invoke(message);
|
||||
}
|
||||
|
||||
if (commandAndKeyFetcher.TryFetch(message, out var value))
|
||||
{
|
||||
return (value, script);
|
||||
}
|
||||
|
||||
return (null, script);
|
||||
});
|
||||
});
|
||||
|
||||
public static Activity ProfilerCommandToActivity(Activity parentActivity, IProfiledCommand command, StackExchangeRedisCallsInstrumentationOptions options)
|
||||
{
|
||||
var name = command.Command; // Example: SET;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = StackExchangeRedisCallsInstrumentation.ActivityName;
|
||||
}
|
||||
|
||||
var activity = StackExchangeRedisCallsInstrumentation.ActivitySource.StartActivity(
|
||||
name,
|
||||
ActivityKind.Client,
|
||||
parentActivity?.Context ?? default,
|
||||
StackExchangeRedisCallsInstrumentation.CreationTags,
|
||||
startTime: command.CommandCreated);
|
||||
|
||||
if (activity == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
activity.SetEndTime(command.CommandCreated + command.ElapsedTime);
|
||||
|
||||
if (activity.IsAllDataRequested == true)
|
||||
{
|
||||
// see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md
|
||||
|
||||
// Timing example:
|
||||
// command.CommandCreated; //2019-01-10 22:18:28Z
|
||||
|
||||
// command.CreationToEnqueued; // 00:00:32.4571995
|
||||
// command.EnqueuedToSending; // 00:00:00.0352838
|
||||
// command.SentToResponse; // 00:00:00.0060586
|
||||
// command.ResponseToCompletion; // 00:00:00.0002601
|
||||
|
||||
// Total:
|
||||
// command.ElapsedTime; // 00:00:32.4988020
|
||||
|
||||
activity.SetStatus(Status.Unset);
|
||||
|
||||
activity.SetTag(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName, command.Flags.ToString());
|
||||
|
||||
if (options.SetVerboseDatabaseStatements)
|
||||
{
|
||||
var (commandAndKey, script) = MessageDataGetter.Value.Invoke(command);
|
||||
|
||||
if (!string.IsNullOrEmpty(commandAndKey) && !string.IsNullOrEmpty(script))
|
||||
{
|
||||
activity.SetTag(SemanticConventions.AttributeDbStatement, commandAndKey + " " + script);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(commandAndKey))
|
||||
{
|
||||
activity.SetTag(SemanticConventions.AttributeDbStatement, commandAndKey);
|
||||
}
|
||||
else if (command.Command != null)
|
||||
{
|
||||
// Example: "db.statement": SET;
|
||||
activity.SetTag(SemanticConventions.AttributeDbStatement, command.Command);
|
||||
}
|
||||
}
|
||||
else if (command.Command != null)
|
||||
{
|
||||
// Example: "db.statement": SET;
|
||||
activity.SetTag(SemanticConventions.AttributeDbStatement, command.Command);
|
||||
}
|
||||
|
||||
if (command.EndPoint != null)
|
||||
{
|
||||
if (command.EndPoint is IPEndPoint ipEndPoint)
|
||||
{
|
||||
activity.SetTag(SemanticConventions.AttributeNetPeerIp, ipEndPoint.Address.ToString());
|
||||
activity.SetTag(SemanticConventions.AttributeNetPeerPort, ipEndPoint.Port);
|
||||
}
|
||||
else if (command.EndPoint is DnsEndPoint dnsEndPoint)
|
||||
{
|
||||
activity.SetTag(SemanticConventions.AttributeNetPeerName, dnsEndPoint.Host);
|
||||
activity.SetTag(SemanticConventions.AttributeNetPeerPort, dnsEndPoint.Port);
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.SetTag(SemanticConventions.AttributePeerService, command.EndPoint.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
activity.SetTag(StackExchangeRedisCallsInstrumentation.RedisDatabaseIndexKeyName, command.Db);
|
||||
|
||||
// TODO: deal with the re-transmission
|
||||
// command.RetransmissionOf;
|
||||
// command.RetransmissionReason;
|
||||
|
||||
var enqueued = command.CommandCreated.Add(command.CreationToEnqueued);
|
||||
var send = enqueued.Add(command.EnqueuedToSending);
|
||||
var response = send.Add(command.SentToResponse);
|
||||
|
||||
activity.AddEvent(new ActivityEvent("Enqueued", enqueued));
|
||||
activity.AddEvent(new ActivityEvent("Sent", send));
|
||||
activity.AddEvent(new ActivityEvent("ResponseReceived", response));
|
||||
|
||||
options.Enrich?.Invoke(activity, command);
|
||||
}
|
||||
|
||||
activity.Stop();
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
public static void DrainSession(Activity parentActivity, IEnumerable<IProfiledCommand> sessionCommands, StackExchangeRedisCallsInstrumentationOptions options)
|
||||
{
|
||||
foreach (var command in sessionCommands)
|
||||
{
|
||||
ProfilerCommandToActivity(parentActivity, command, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates getter for a field defined in private or internal type
|
||||
/// repesented with classType variable.
|
||||
/// </summary>
|
||||
private static Func<object, TField> CreateFieldGetter<TField>(Type classType, string fieldName, BindingFlags flags)
|
||||
{
|
||||
FieldInfo field = classType.GetField(fieldName, flags);
|
||||
if (field != null)
|
||||
{
|
||||
string methodName = classType.FullName + ".get_" + field.Name;
|
||||
DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(TField), new[] { typeof(object) }, true);
|
||||
ILGenerator generator = getterMethod.GetILGenerator();
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Castclass, classType);
|
||||
generator.Emit(OpCodes.Ldfld, field);
|
||||
generator.Emit(OpCodes.Ret);
|
||||
|
||||
return (Func<object, TField>)getterMethod.CreateDelegate(typeof(Func<object, TField>));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- OmniSharp/VS Code requires TargetFrameworks to be in descending order for IntelliSense and analysis. -->
|
||||
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
|
||||
<Description>StackExchange.Redis instrumentation for OpenTelemetry .NET</Description>
|
||||
<PackageTags>$(PackageTags);distributed-tracing;Redis;StackExchange.Redis</PackageTags>
|
||||
<IncludeDiagnosticSourceInstrumentationHelpers>true</IncludeDiagnosticSourceInstrumentationHelpers>
|
||||
<IncludeInstrumentationHelpers>true</IncludeInstrumentationHelpers>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ServiceProviderExtensions.cs" Link="Includes\ServiceProviderExtensions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Api\OpenTelemetry.Api.csproj" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="$(StackExchangeRedisPkgVer)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPkgVer)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,137 +0,0 @@
|
|||
# StackExchange.Redis Instrumentation for OpenTelemetry
|
||||
|
||||
[](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.StackExchangeRedis)
|
||||
[](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.StackExchangeRedis)
|
||||
|
||||
This is an
|
||||
[Instrumentation Library](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library),
|
||||
which instruments
|
||||
[StackExchange.Redis](https://www.nuget.org/packages/StackExchange.Redis/)
|
||||
and collects traces about outgoing calls to Redis.
|
||||
|
||||
**Note: This component is based on the OpenTelemetry semantic conventions for
|
||||
[traces](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions).
|
||||
These conventions are
|
||||
[Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/document-status.md),
|
||||
and hence, this package is a [pre-release](../../VERSIONING.md#pre-releases).
|
||||
Until a [stable
|
||||
version](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/telemetry-stability.md)
|
||||
is released, there can be breaking changes. You can track the progress from
|
||||
[milestones](https://github.com/open-telemetry/opentelemetry-dotnet/milestone/23).**
|
||||
|
||||
## Steps to enable OpenTelemetry.Instrumentation.StackExchangeRedis
|
||||
|
||||
## Step 1: Install Package
|
||||
|
||||
Add a reference to the
|
||||
[`OpenTelemetry.Instrumentation.StackExchangeRedis`](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.StackExchangeRedis)
|
||||
package. Also, add any other instrumentations & exporters you will need.
|
||||
|
||||
```shell
|
||||
dotnet add package OpenTelemetry.Instrumentation.StackExchangeRedis
|
||||
```
|
||||
|
||||
## Step 2: Enable StackExchange.Redis Instrumentation at application startup
|
||||
|
||||
StackExchange.Redis instrumentation must be enabled at application startup.
|
||||
`AddRedisInstrumentation` method on `TracerProviderBuilder` must be called to
|
||||
enable Redis instrumentation, passing the `IConnectionMultiplexer` instance used
|
||||
to make Redis calls. Only those Redis calls made using the same instance of the
|
||||
`IConnectionMultiplexer` will be instrumented.
|
||||
|
||||
The following example demonstrates adding StackExchange.Redis instrumentation to
|
||||
a console application. This example also sets up the OpenTelemetry Console
|
||||
exporter, which requires adding the package
|
||||
[`OpenTelemetry.Exporter.Console`](../OpenTelemetry.Exporter.Console/README.md)
|
||||
to the application.
|
||||
|
||||
```csharp
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Connect to the server.
|
||||
using var connection = ConnectionMultiplexer.Connect("localhost:6379");
|
||||
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddRedisInstrumentation(connection)
|
||||
.AddConsoleExporter()
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For an ASP.NET Core application, adding instrumentation is typically done in
|
||||
the `ConfigureServices` of your `Startup` class. Refer to documentation for
|
||||
[OpenTelemetry.Instrumentation.AspNetCore](../OpenTelemetry.Instrumentation.AspNetCore/README.md).
|
||||
|
||||
For an ASP.NET application, adding instrumentation is typically done in the
|
||||
`Global.asax.cs`. Refer to documentation for [OpenTelemetry.Instrumentation.AspNet](../OpenTelemetry.Instrumentation.AspNet/README.md).
|
||||
|
||||
## Advanced configuration
|
||||
|
||||
This instrumentation can be configured to change the default behavior by using
|
||||
`StackExchangeRedisCallsInstrumentationOptions`.
|
||||
|
||||
### FlushInterval
|
||||
|
||||
StackExchange.Redis has its own internal profiler. OpenTelemetry converts each
|
||||
profiled command from the internal profiler to an Activity for collection. By
|
||||
default, this conversion process flushes profiled commands on a 10 second
|
||||
interval. The `FlushInterval` option can be used to adjust this interval.
|
||||
|
||||
The following example shows how to use `FlushInterval`.
|
||||
|
||||
```csharp
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddRedisInstrumentation(
|
||||
connection,
|
||||
options => options.FlushInterval = TimeSpan.FromSeconds(5))
|
||||
.AddConsoleExporter()
|
||||
.Build();
|
||||
```
|
||||
|
||||
### SetVerboseDatabaseStatements
|
||||
|
||||
StackExchange.Redis by default does not give detailed database statements like
|
||||
what key or script was used during an operation. The `SetVerboseDatabaseStatements`
|
||||
option can be used to enable gathering this more detailed information.
|
||||
|
||||
The following example shows how to use `SetVerboseDatabaseStatements`.
|
||||
|
||||
```csharp
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddRedisInstrumentation(
|
||||
connection,
|
||||
options => options.SetVerboseDatabaseStatements = true)
|
||||
.AddConsoleExporter()
|
||||
.Build();
|
||||
```
|
||||
|
||||
## Enrich
|
||||
|
||||
This option allows one to enrich the activity with additional information from the
|
||||
raw `IProfiledCommand` object. The `Enrich` action is called only when
|
||||
`activity.IsAllDataRequested` is `true`. It contains the activity itself (which can
|
||||
be enriched), and the source profiled command object.
|
||||
|
||||
The following code snippet shows how to add additional tags using `Enrich`.
|
||||
|
||||
```csharp
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddRedisInstrumentation(opt => opt.Enrich = (activity, command) =>
|
||||
{
|
||||
if (command.ElapsedTime < TimeSpan.FromMilliseconds(100))
|
||||
{
|
||||
activity.SetTag("is_fast", true);
|
||||
}
|
||||
})
|
||||
.Build();
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
* [OpenTelemetry Project](https://opentelemetry.io/)
|
||||
* [StackExchange.Redis Profiling](https://stackexchange.github.io/StackExchange.Redis/Profiling_v1.html)
|
|
@ -1,151 +0,0 @@
|
|||
// <copyright file="StackExchangeRedisCallsInstrumentation.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using OpenTelemetry.Instrumentation.StackExchangeRedis.Implementation;
|
||||
using OpenTelemetry.Internal;
|
||||
using OpenTelemetry.Trace;
|
||||
using StackExchange.Redis;
|
||||
using StackExchange.Redis.Profiling;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.StackExchangeRedis
|
||||
{
|
||||
/// <summary>
|
||||
/// Redis calls instrumentation.
|
||||
/// </summary>
|
||||
internal class StackExchangeRedisCallsInstrumentation : IDisposable
|
||||
{
|
||||
internal const string RedisDatabaseIndexKeyName = "db.redis.database_index";
|
||||
internal const string RedisFlagsKeyName = "db.redis.flags";
|
||||
internal const string ActivitySourceName = "OpenTelemetry.StackExchange.Redis";
|
||||
internal const string ActivityName = ActivitySourceName + ".Execute";
|
||||
internal static readonly Version Version = typeof(StackExchangeRedisCallsInstrumentation).Assembly.GetName().Version;
|
||||
internal static readonly ActivitySource ActivitySource = new(ActivitySourceName, Version.ToString());
|
||||
internal static readonly IEnumerable<KeyValuePair<string, object>> CreationTags = new[]
|
||||
{
|
||||
new KeyValuePair<string, object>(SemanticConventions.AttributeDbSystem, "redis"),
|
||||
};
|
||||
|
||||
internal readonly ConcurrentDictionary<(ActivityTraceId TraceId, ActivitySpanId SpanId), (Activity Activity, ProfilingSession Session)> Cache
|
||||
= new();
|
||||
|
||||
private readonly StackExchangeRedisCallsInstrumentationOptions options;
|
||||
private readonly EventWaitHandle stopHandle = new(false, EventResetMode.ManualReset);
|
||||
private readonly Thread drainThread;
|
||||
|
||||
private readonly ProfilingSession defaultSession = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StackExchangeRedisCallsInstrumentation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="connection"><see cref="IConnectionMultiplexer"/> to instrument.</param>
|
||||
/// <param name="options">Configuration options for redis instrumentation.</param>
|
||||
public StackExchangeRedisCallsInstrumentation(IConnectionMultiplexer connection, StackExchangeRedisCallsInstrumentationOptions options)
|
||||
{
|
||||
Guard.ThrowIfNull(connection);
|
||||
|
||||
this.options = options ?? new StackExchangeRedisCallsInstrumentationOptions();
|
||||
|
||||
this.drainThread = new Thread(this.DrainEntries)
|
||||
{
|
||||
Name = "OpenTelemetry.Redis",
|
||||
};
|
||||
this.drainThread.Start();
|
||||
|
||||
connection.RegisterProfiler(this.GetProfilerSessionsFactory());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns session for the Redis calls recording.
|
||||
/// </summary>
|
||||
/// <returns>Session associated with the current span context to record Redis calls.</returns>
|
||||
public Func<ProfilingSession> GetProfilerSessionsFactory()
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
if (this.stopHandle.WaitOne(0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Activity parent = Activity.Current;
|
||||
|
||||
// If no parent use the default session.
|
||||
if (parent == null || parent.IdFormat != ActivityIdFormat.W3C)
|
||||
{
|
||||
return this.defaultSession;
|
||||
}
|
||||
|
||||
// Try to reuse a session for all activities created under the same TraceId+SpanId.
|
||||
var cacheKey = (parent.TraceId, parent.SpanId);
|
||||
if (!this.Cache.TryGetValue(cacheKey, out var session))
|
||||
{
|
||||
session = (parent, new ProfilingSession());
|
||||
this.Cache.TryAdd(cacheKey, session);
|
||||
}
|
||||
|
||||
return session.Session;
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.stopHandle.Set();
|
||||
this.drainThread.Join();
|
||||
|
||||
this.Flush();
|
||||
|
||||
this.stopHandle.Dispose();
|
||||
}
|
||||
|
||||
internal void Flush()
|
||||
{
|
||||
RedisProfilerEntryToActivityConverter.DrainSession(null, this.defaultSession.FinishProfiling(), this.options);
|
||||
|
||||
foreach (var entry in this.Cache)
|
||||
{
|
||||
var parent = entry.Value.Activity;
|
||||
if (parent.Duration == TimeSpan.Zero)
|
||||
{
|
||||
// Activity is still running, don't drain.
|
||||
continue;
|
||||
}
|
||||
|
||||
ProfilingSession session = entry.Value.Session;
|
||||
RedisProfilerEntryToActivityConverter.DrainSession(parent, session.FinishProfiling(), this.options);
|
||||
this.Cache.TryRemove((entry.Key.TraceId, entry.Key.SpanId), out _);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrainEntries(object state)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (this.stopHandle.WaitOne(this.options.FlushInterval))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
this.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
// <copyright file="StackExchangeRedisCallsInstrumentationOptions.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Trace;
|
||||
using StackExchange.Redis.Profiling;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.StackExchangeRedis
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for StackExchange.Redis instrumentation.
|
||||
/// </summary>
|
||||
public class StackExchangeRedisCallsInstrumentationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum time that should elapse between flushing the internal buffer of Redis profiling sessions and creating <see cref="Activity"/> objects. Default value: 00:00:10.
|
||||
/// </summary>
|
||||
public TimeSpan FlushInterval { get; set; } = TimeSpan.FromSeconds(10);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not the <see cref="StackExchangeRedisCallsInstrumentation"/> should use reflection to get more detailed <see cref="SemanticConventions.AttributeDbStatement"/> tag values. Default value: False.
|
||||
/// </summary>
|
||||
public bool SetVerboseDatabaseStatements { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action to enrich an Activity.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><see cref="Activity"/>: the activity being enriched.</para>
|
||||
/// <para><see cref="IProfiledCommand"/>: the profiled redis command from which additional information can be extracted to enrich the activity.</para>
|
||||
/// </remarks>
|
||||
public Action<Activity, IProfiledCommand> Enrich { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
// <copyright file="TracerProviderBuilderExtensions.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using OpenTelemetry.Instrumentation.StackExchangeRedis;
|
||||
using OpenTelemetry.Internal;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods to simplify registering of dependency instrumentation.
|
||||
/// </summary>
|
||||
public static class TracerProviderBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables automatic data collection of outgoing requests to Redis.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note: If an <see cref="IConnectionMultiplexer"/> is not supplied
|
||||
/// using the <paramref name="connection"/> parameter it will be
|
||||
/// resolved using the application <see cref="IServiceProvider"/>.
|
||||
/// </remarks>
|
||||
/// <param name="builder"><see cref="TracerProviderBuilder"/> being configured.</param>
|
||||
/// <param name="connection">Optional <see cref="IConnectionMultiplexer"/> to instrument.</param>
|
||||
/// <param name="configure">Optional callback to configure options.</param>
|
||||
/// <returns>The instance of <see cref="TracerProviderBuilder"/> to chain the calls.</returns>
|
||||
public static TracerProviderBuilder AddRedisInstrumentation(
|
||||
this TracerProviderBuilder builder,
|
||||
IConnectionMultiplexer connection = null,
|
||||
Action<StackExchangeRedisCallsInstrumentationOptions> configure = null)
|
||||
{
|
||||
Guard.ThrowIfNull(builder);
|
||||
|
||||
if (builder is not IDeferredTracerProviderBuilder deferredTracerProviderBuilder)
|
||||
{
|
||||
if (connection == null)
|
||||
{
|
||||
throw new NotSupportedException($"StackExchange.Redis {nameof(IConnectionMultiplexer)} must be supplied when dependency injection is unavailable - to enable dependency injection use the OpenTelemetry.Extensions.Hosting package");
|
||||
}
|
||||
|
||||
return AddRedisInstrumentation(builder, connection, new StackExchangeRedisCallsInstrumentationOptions(), configure);
|
||||
}
|
||||
|
||||
return deferredTracerProviderBuilder.Configure((sp, builder) =>
|
||||
{
|
||||
if (connection == null)
|
||||
{
|
||||
connection = (IConnectionMultiplexer)sp.GetService(typeof(IConnectionMultiplexer));
|
||||
if (connection == null)
|
||||
{
|
||||
throw new InvalidOperationException($"StackExchange.Redis {nameof(IConnectionMultiplexer)} could not be resolved through application {nameof(IServiceProvider)}");
|
||||
}
|
||||
}
|
||||
|
||||
AddRedisInstrumentation(
|
||||
builder,
|
||||
connection,
|
||||
sp.GetOptions<StackExchangeRedisCallsInstrumentationOptions>(),
|
||||
configure);
|
||||
});
|
||||
}
|
||||
|
||||
private static TracerProviderBuilder AddRedisInstrumentation(
|
||||
TracerProviderBuilder builder,
|
||||
IConnectionMultiplexer connection,
|
||||
StackExchangeRedisCallsInstrumentationOptions options,
|
||||
Action<StackExchangeRedisCallsInstrumentationOptions> configure = null)
|
||||
{
|
||||
configure?.Invoke(options);
|
||||
|
||||
return builder
|
||||
.AddInstrumentation(() => new StackExchangeRedisCallsInstrumentation(connection, options))
|
||||
.AddSource(StackExchangeRedisCallsInstrumentation.ActivitySourceName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
# Create a container for running the OpenTelemetry Redis integration tests.
|
||||
# This should be run from the root of the repo:
|
||||
# docker build --file test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/Dockerfile .
|
||||
|
||||
ARG SDK_VERSION=6.0
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0-focal AS build
|
||||
ARG PUBLISH_CONFIGURATION=Release
|
||||
ARG PUBLISH_FRAMEWORK=net6.0
|
||||
WORKDIR /repo
|
||||
COPY . ./
|
||||
WORKDIR "/repo/test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests"
|
||||
RUN dotnet publish "OpenTelemetry.Instrumentation.StackExchangeRedis.Tests.csproj" -c "${PUBLISH_CONFIGURATION}" -f "${PUBLISH_FRAMEWORK}" -o /drop -p:IntegrationBuild=true -p:TARGET_FRAMEWORK=${PUBLISH_FRAMEWORK}
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:${SDK_VERSION} AS final
|
||||
WORKDIR /test
|
||||
COPY --from=build /drop .
|
||||
ENTRYPOINT ["dotnet", "vstest", "OpenTelemetry.Instrumentation.StackExchangeRedis.Tests.dll"]
|
|
@ -1,181 +0,0 @@
|
|||
// <copyright file="RedisProfilerEntryToActivityConverterTests.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Moq;
|
||||
using OpenTelemetry.Trace;
|
||||
using StackExchange.Redis;
|
||||
using StackExchange.Redis.Profiling;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.StackExchangeRedis.Implementation
|
||||
{
|
||||
[Collection("Redis")]
|
||||
public class RedisProfilerEntryToActivityConverterTests : IDisposable
|
||||
{
|
||||
private readonly ConnectionMultiplexer connection;
|
||||
private readonly IDisposable tracerProvider;
|
||||
|
||||
public RedisProfilerEntryToActivityConverterTests()
|
||||
{
|
||||
var connectionOptions = new ConfigurationOptions
|
||||
{
|
||||
AbortOnConnectFail = false,
|
||||
};
|
||||
connectionOptions.EndPoints.Add("localhost:6379");
|
||||
|
||||
this.connection = ConnectionMultiplexer.Connect(connectionOptions);
|
||||
|
||||
this.tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddRedisInstrumentation(this.connection)
|
||||
.Build();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.tracerProvider.Dispose();
|
||||
this.connection.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfilerCommandToActivity_UsesCommandAsName()
|
||||
{
|
||||
var activity = new Activity("redis-profiler");
|
||||
var profiledCommand = new Mock<IProfiledCommand>();
|
||||
profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow);
|
||||
profiledCommand.Setup(m => m.Command).Returns("SET");
|
||||
|
||||
var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
|
||||
Assert.Equal("SET", result.DisplayName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfilerCommandToActivity_UsesTimestampAsStartTime()
|
||||
{
|
||||
var now = DateTimeOffset.Now;
|
||||
var activity = new Activity("redis-profiler");
|
||||
var profiledCommand = new Mock<IProfiledCommand>();
|
||||
profiledCommand.Setup(m => m.CommandCreated).Returns(now.DateTime);
|
||||
|
||||
var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
|
||||
Assert.Equal(now, result.StartTimeUtc);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfilerCommandToActivity_SetsDbTypeAttributeAsRedis()
|
||||
{
|
||||
var activity = new Activity("redis-profiler");
|
||||
var profiledCommand = new Mock<IProfiledCommand>();
|
||||
profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow);
|
||||
|
||||
var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
|
||||
Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeDbSystem));
|
||||
Assert.Equal("redis", result.GetTagValue(SemanticConventions.AttributeDbSystem));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfilerCommandToActivity_UsesCommandAsDbStatementAttribute()
|
||||
{
|
||||
var activity = new Activity("redis-profiler");
|
||||
var profiledCommand = new Mock<IProfiledCommand>();
|
||||
profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow);
|
||||
profiledCommand.Setup(m => m.Command).Returns("SET");
|
||||
|
||||
var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
|
||||
Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeDbStatement));
|
||||
Assert.Equal("SET", result.GetTagValue(SemanticConventions.AttributeDbStatement));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfilerCommandToActivity_UsesFlagsForFlagsAttribute()
|
||||
{
|
||||
var activity = new Activity("redis-profiler");
|
||||
var profiledCommand = new Mock<IProfiledCommand>();
|
||||
profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow);
|
||||
var expectedFlags = CommandFlags.FireAndForget |
|
||||
CommandFlags.NoRedirect;
|
||||
profiledCommand.Setup(m => m.Flags).Returns(expectedFlags);
|
||||
|
||||
var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
|
||||
Assert.NotNull(result.GetTagValue(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName));
|
||||
Assert.Equal("PreferMaster, FireAndForget, NoRedirect", result.GetTagValue(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfilerCommandToActivity_UsesIpEndPointAsEndPoint()
|
||||
{
|
||||
long address = 1;
|
||||
int port = 2;
|
||||
|
||||
var activity = new Activity("redis-profiler");
|
||||
IPEndPoint ipLocalEndPoint = new IPEndPoint(address, port);
|
||||
var profiledCommand = new Mock<IProfiledCommand>();
|
||||
profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow);
|
||||
profiledCommand.Setup(m => m.EndPoint).Returns(ipLocalEndPoint);
|
||||
|
||||
var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
|
||||
Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeNetPeerIp));
|
||||
Assert.Equal($"{address}.0.0.0", result.GetTagValue(SemanticConventions.AttributeNetPeerIp));
|
||||
Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeNetPeerPort));
|
||||
Assert.Equal(port, result.GetTagValue(SemanticConventions.AttributeNetPeerPort));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfilerCommandToActivity_UsesDnsEndPointAsEndPoint()
|
||||
{
|
||||
var dnsEndPoint = new DnsEndPoint("https://opentelemetry.io/", 443);
|
||||
|
||||
var activity = new Activity("redis-profiler");
|
||||
var profiledCommand = new Mock<IProfiledCommand>();
|
||||
profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow);
|
||||
profiledCommand.Setup(m => m.EndPoint).Returns(dnsEndPoint);
|
||||
|
||||
var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
|
||||
Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeNetPeerName));
|
||||
Assert.Equal(dnsEndPoint.Host, result.GetTagValue(SemanticConventions.AttributeNetPeerName));
|
||||
Assert.NotNull(result.GetTagValue(SemanticConventions.AttributeNetPeerPort));
|
||||
Assert.Equal(dnsEndPoint.Port, result.GetTagValue(SemanticConventions.AttributeNetPeerPort));
|
||||
}
|
||||
|
||||
#if !NETFRAMEWORK
|
||||
[Fact]
|
||||
public void ProfilerCommandToActivity_UsesOtherEndPointAsEndPoint()
|
||||
{
|
||||
var unixEndPoint = new UnixDomainSocketEndPoint("https://opentelemetry.io/");
|
||||
var activity = new Activity("redis-profiler");
|
||||
var profiledCommand = new Mock<IProfiledCommand>();
|
||||
profiledCommand.Setup(m => m.CommandCreated).Returns(DateTime.UtcNow);
|
||||
profiledCommand.Setup(m => m.EndPoint).Returns(unixEndPoint);
|
||||
|
||||
var result = RedisProfilerEntryToActivityConverter.ProfilerCommandToActivity(activity, profiledCommand.Object, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
|
||||
Assert.NotNull(result.GetTagValue(SemanticConventions.AttributePeerService));
|
||||
Assert.Equal(unixEndPoint.ToString(), result.GetTagValue(SemanticConventions.AttributePeerService));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>Unit test project for OpenTelemetry StackExchangeRedis instrumentation</Description>
|
||||
<!-- OmniSharp/VS Code requires TargetFrameworks to be in descending order for IntelliSense and analysis. -->
|
||||
<TargetFrameworks Condition="$(TARGET_FRAMEWORK) == ''">net6.0;netcoreapp3.1</TargetFrameworks>
|
||||
<TargetFrameworks Condition="$(TARGET_FRAMEWORK) == '' and $(OS) == 'Windows_NT'">$(TargetFrameworks);net462</TargetFrameworks>
|
||||
<TargetFrameworks Condition="$(TARGET_FRAMEWORK) != ''">$(TARGET_FRAMEWORK)</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\SkipUnlessEnvVarFoundTheoryAttribute.cs" Link="Implementation\SkipUnlessEnvVarFoundTheoryAttribute.cs" />
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestSampler.cs" Link="TestSampler.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.StackExchangeRedis\OpenTelemetry.Instrumentation.StackExchangeRedis.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Extensions.Hosting\OpenTelemetry.Extensions.Hosting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPkgVer)" />
|
||||
<PackageReference Include="Moq" Version="$(MoqPkgVer)" />
|
||||
<PackageReference Include="xunit" Version="$(XUnitPkgVer)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XUnitRunnerVisualStudioPkgVer)">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPkgVer)" />
|
||||
<DotNetCliToolReference Include="dotnet-xunit" Version="$(DotNetXUnitCliVer)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,417 +0,0 @@
|
|||
// <copyright file="StackExchangeRedisCallsInstrumentationTests.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using OpenTelemetry.Tests;
|
||||
using OpenTelemetry.Trace;
|
||||
using StackExchange.Redis;
|
||||
using StackExchange.Redis.Profiling;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.StackExchangeRedis.Tests
|
||||
{
|
||||
[Collection("Redis")]
|
||||
public class StackExchangeRedisCallsInstrumentationTests
|
||||
{
|
||||
/*
|
||||
To run the integration tests, set the OTEL_REDISENDPOINT machine-level environment variable to a valid Redis endpoint.
|
||||
|
||||
To use Docker...
|
||||
1) Run: docker run -d --name redis -p 6379:6379 redis
|
||||
2) Set OTEL_REDISENDPOINT as: localhost:6379
|
||||
*/
|
||||
|
||||
private const string RedisEndPointEnvVarName = "OTEL_REDISENDPOINT";
|
||||
private static readonly string RedisEndPoint = SkipUnlessEnvVarFoundTheoryAttribute.GetEnvironmentVariable(RedisEndPointEnvVarName);
|
||||
|
||||
[Trait("CategoryName", "RedisIntegrationTests")]
|
||||
[SkipUnlessEnvVarFoundTheory(RedisEndPointEnvVarName)]
|
||||
[InlineData("value1")]
|
||||
public void SuccessfulCommandTestWithKey(string value)
|
||||
{
|
||||
var connectionOptions = new ConfigurationOptions
|
||||
{
|
||||
AbortOnConnectFail = true,
|
||||
};
|
||||
connectionOptions.EndPoints.Add(RedisEndPoint);
|
||||
|
||||
using var connection = ConnectionMultiplexer.Connect(connectionOptions);
|
||||
var db = connection.GetDatabase();
|
||||
db.KeyDelete("key1");
|
||||
|
||||
var activityProcessor = new Mock<BaseProcessor<Activity>>();
|
||||
var sampler = new TestSampler();
|
||||
using (Sdk.CreateTracerProviderBuilder()
|
||||
.AddProcessor(activityProcessor.Object)
|
||||
.SetSampler(sampler)
|
||||
.AddRedisInstrumentation(connection, c => c.SetVerboseDatabaseStatements = true)
|
||||
.Build())
|
||||
{
|
||||
var prepared = LuaScript.Prepare("redis.call('set', @key, @value)");
|
||||
db.ScriptEvaluate(prepared, new { key = (RedisKey)"mykey", value = 123 });
|
||||
|
||||
var redisValue = db.StringGet("key1");
|
||||
|
||||
Assert.False(redisValue.HasValue);
|
||||
|
||||
bool set = db.StringSet("key1", value, TimeSpan.FromSeconds(60));
|
||||
|
||||
Assert.True(set);
|
||||
|
||||
redisValue = db.StringGet("key1");
|
||||
|
||||
Assert.True(redisValue.HasValue);
|
||||
Assert.Equal(value, redisValue.ToString());
|
||||
}
|
||||
|
||||
// Disposing SDK should flush the Redis profiling session immediately.
|
||||
|
||||
Assert.Equal(11, activityProcessor.Invocations.Count);
|
||||
|
||||
var scriptActivity = (Activity)activityProcessor.Invocations[1].Arguments[0];
|
||||
Assert.Equal("EVAL", scriptActivity.DisplayName);
|
||||
Assert.Equal("EVAL redis.call('set', ARGV[1], ARGV[2])", scriptActivity.GetTagValue(SemanticConventions.AttributeDbStatement));
|
||||
|
||||
VerifyActivityData((Activity)activityProcessor.Invocations[3].Arguments[0], false, connection.GetEndPoints()[0], true);
|
||||
VerifyActivityData((Activity)activityProcessor.Invocations[5].Arguments[0], true, connection.GetEndPoints()[0], true);
|
||||
VerifyActivityData((Activity)activityProcessor.Invocations[7].Arguments[0], false, connection.GetEndPoints()[0], true);
|
||||
VerifySamplingParameters(sampler.LatestSamplingParameters);
|
||||
}
|
||||
|
||||
[Trait("CategoryName", "RedisIntegrationTests")]
|
||||
[SkipUnlessEnvVarFoundTheory(RedisEndPointEnvVarName)]
|
||||
[InlineData("value1")]
|
||||
public void SuccessfulCommandTest(string value)
|
||||
{
|
||||
var connectionOptions = new ConfigurationOptions
|
||||
{
|
||||
AbortOnConnectFail = true,
|
||||
};
|
||||
connectionOptions.EndPoints.Add(RedisEndPoint);
|
||||
|
||||
using var connection = ConnectionMultiplexer.Connect(connectionOptions);
|
||||
|
||||
var activityProcessor = new Mock<BaseProcessor<Activity>>();
|
||||
var sampler = new TestSampler();
|
||||
using (Sdk.CreateTracerProviderBuilder()
|
||||
.AddProcessor(activityProcessor.Object)
|
||||
.SetSampler(sampler)
|
||||
.AddRedisInstrumentation(connection, c => c.SetVerboseDatabaseStatements = false)
|
||||
.Build())
|
||||
{
|
||||
var db = connection.GetDatabase();
|
||||
|
||||
bool set = db.StringSet("key1", value, TimeSpan.FromSeconds(60));
|
||||
|
||||
Assert.True(set);
|
||||
|
||||
var redisValue = db.StringGet("key1");
|
||||
|
||||
Assert.True(redisValue.HasValue);
|
||||
Assert.Equal(value, redisValue.ToString());
|
||||
}
|
||||
|
||||
// Disposing SDK should flush the Redis profiling session immediately.
|
||||
|
||||
Assert.Equal(7, activityProcessor.Invocations.Count);
|
||||
|
||||
VerifyActivityData((Activity)activityProcessor.Invocations[1].Arguments[0], true, connection.GetEndPoints()[0], false);
|
||||
VerifyActivityData((Activity)activityProcessor.Invocations[3].Arguments[0], false, connection.GetEndPoints()[0], false);
|
||||
VerifySamplingParameters(sampler.LatestSamplingParameters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void ProfilerSessionUsesTheSameDefault()
|
||||
{
|
||||
var connectionOptions = new ConfigurationOptions
|
||||
{
|
||||
AbortOnConnectFail = false,
|
||||
};
|
||||
connectionOptions.EndPoints.Add("localhost:6379");
|
||||
|
||||
var connection = ConnectionMultiplexer.Connect(connectionOptions);
|
||||
|
||||
using var instrumentation = new StackExchangeRedisCallsInstrumentation(connection, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
var profilerFactory = instrumentation.GetProfilerSessionsFactory();
|
||||
var first = profilerFactory();
|
||||
var second = profilerFactory();
|
||||
ProfilingSession third = null;
|
||||
await Task.Delay(1).ContinueWith((t) => { third = profilerFactory(); });
|
||||
Assert.Equal(first, second);
|
||||
Assert.Equal(second, third);
|
||||
}
|
||||
|
||||
[Trait("CategoryName", "RedisIntegrationTests")]
|
||||
[SkipUnlessEnvVarFoundTheory(RedisEndPointEnvVarName)]
|
||||
[InlineData("value1")]
|
||||
public void CanEnrichActivityFromCommand(string value)
|
||||
{
|
||||
var connectionOptions = new ConfigurationOptions
|
||||
{
|
||||
AbortOnConnectFail = true,
|
||||
};
|
||||
connectionOptions.EndPoints.Add(RedisEndPoint);
|
||||
|
||||
using var connection = ConnectionMultiplexer.Connect(connectionOptions);
|
||||
|
||||
var activityProcessor = new Mock<BaseProcessor<Activity>>();
|
||||
var sampler = new TestSampler();
|
||||
using (Sdk.CreateTracerProviderBuilder()
|
||||
.AddProcessor(activityProcessor.Object)
|
||||
.SetSampler(sampler)
|
||||
.AddRedisInstrumentation(connection, c => c.Enrich = (activity, command) =>
|
||||
{
|
||||
if (command.ElapsedTime < TimeSpan.FromMilliseconds(100))
|
||||
{
|
||||
activity.AddTag("is_fast", true);
|
||||
}
|
||||
})
|
||||
.Build())
|
||||
{
|
||||
var db = connection.GetDatabase();
|
||||
|
||||
bool set = db.StringSet("key1", value, TimeSpan.FromSeconds(60));
|
||||
|
||||
Assert.True(set);
|
||||
|
||||
var redisValue = db.StringGet("key1");
|
||||
|
||||
Assert.True(redisValue.HasValue);
|
||||
Assert.Equal(value, redisValue.ToString());
|
||||
}
|
||||
|
||||
// Disposing SDK should flush the Redis profiling session immediately.
|
||||
|
||||
Assert.Equal(7, activityProcessor.Invocations.Count);
|
||||
|
||||
var setActivity = (Activity)activityProcessor.Invocations[1].Arguments[0];
|
||||
Assert.Equal(true, setActivity.GetTagValue("is_fast"));
|
||||
var getActivity = (Activity)activityProcessor.Invocations[3].Arguments[0];
|
||||
Assert.Equal(true, getActivity.GetTagValue("is_fast"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckCacheIsFlushedProperly()
|
||||
{
|
||||
var connectionOptions = new ConfigurationOptions
|
||||
{
|
||||
AbortOnConnectFail = false,
|
||||
};
|
||||
connectionOptions.EndPoints.Add("localhost:6379");
|
||||
|
||||
var connection = ConnectionMultiplexer.Connect(connectionOptions);
|
||||
|
||||
using var instrumentation = new StackExchangeRedisCallsInstrumentation(connection, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
var profilerFactory = instrumentation.GetProfilerSessionsFactory();
|
||||
|
||||
// start a root level activity
|
||||
using Activity rootActivity = new Activity("Parent")
|
||||
.SetParentId(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded)
|
||||
.Start();
|
||||
|
||||
Assert.NotNull(rootActivity.Id);
|
||||
|
||||
// get an initial profiler from root activity
|
||||
Activity.Current = rootActivity;
|
||||
ProfilingSession profiler0 = profilerFactory();
|
||||
|
||||
// expect different result from synchronous child activity
|
||||
ProfilingSession profiler1;
|
||||
using (Activity.Current = new Activity("Child-Span-1").SetParentId(rootActivity.Id).Start())
|
||||
{
|
||||
profiler1 = profilerFactory();
|
||||
Assert.NotSame(profiler0, profiler1);
|
||||
}
|
||||
|
||||
rootActivity.Stop();
|
||||
rootActivity.Dispose();
|
||||
|
||||
instrumentation.Flush();
|
||||
Assert.Empty(instrumentation.Cache);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProfilerSessionsHandleMultipleSpans()
|
||||
{
|
||||
var connectionOptions = new ConfigurationOptions
|
||||
{
|
||||
AbortOnConnectFail = false,
|
||||
};
|
||||
connectionOptions.EndPoints.Add("localhost:6379");
|
||||
|
||||
var connection = ConnectionMultiplexer.Connect(connectionOptions);
|
||||
|
||||
using var instrumentation = new StackExchangeRedisCallsInstrumentation(connection, new StackExchangeRedisCallsInstrumentationOptions());
|
||||
var profilerFactory = instrumentation.GetProfilerSessionsFactory();
|
||||
|
||||
// start a root level activity
|
||||
using Activity rootActivity = new Activity("Parent")
|
||||
.SetParentId(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded)
|
||||
.Start();
|
||||
|
||||
Assert.NotNull(rootActivity.Id);
|
||||
|
||||
// get an initial profiler from root activity
|
||||
Activity.Current = rootActivity;
|
||||
ProfilingSession profiler0 = profilerFactory();
|
||||
|
||||
// expect different result from synchronous child activity
|
||||
ProfilingSession profiler1;
|
||||
using (Activity.Current = new Activity("Child-Span-1").SetParentId(rootActivity.Id).Start())
|
||||
{
|
||||
profiler1 = profilerFactory();
|
||||
Assert.NotSame(profiler0, profiler1);
|
||||
}
|
||||
|
||||
Activity.Current = rootActivity;
|
||||
|
||||
// expect different result from asynchronous child activity
|
||||
using (Activity.Current = new Activity("Child-Span-2").SetParentId(rootActivity.Id).Start())
|
||||
{
|
||||
// lose async context on purpose
|
||||
await Task.Delay(100).ConfigureAwait(false);
|
||||
|
||||
ProfilingSession profiler2 = profilerFactory();
|
||||
Assert.NotSame(profiler0, profiler2);
|
||||
Assert.NotSame(profiler1, profiler2);
|
||||
}
|
||||
|
||||
Activity.Current = rootActivity;
|
||||
|
||||
// ensure same result back in root activity
|
||||
ProfilingSession profiles3 = profilerFactory();
|
||||
Assert.Same(profiler0, profiles3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackExchangeRedis_BadArgs()
|
||||
{
|
||||
TracerProviderBuilder builder = null;
|
||||
Assert.Throws<ArgumentNullException>(() => builder.AddRedisInstrumentation(null));
|
||||
|
||||
var activityProcessor = new Mock<BaseProcessor<Activity>>();
|
||||
Assert.Throws<NotSupportedException>(() =>
|
||||
Sdk.CreateTracerProviderBuilder()
|
||||
.AddProcessor(activityProcessor.Object)
|
||||
.AddRedisInstrumentation(null)
|
||||
.Build());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackExchangeRedis_DependencyInjection_Success()
|
||||
{
|
||||
bool connectionMultiplexerPickedFromDI = false;
|
||||
bool optionsPickedFromDI = false;
|
||||
|
||||
var connectionOptions = new ConfigurationOptions
|
||||
{
|
||||
AbortOnConnectFail = false,
|
||||
};
|
||||
connectionOptions.EndPoints.Add("localhost");
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IConnectionMultiplexer>((sp) =>
|
||||
{
|
||||
connectionMultiplexerPickedFromDI = true;
|
||||
return ConnectionMultiplexer.Connect(connectionOptions);
|
||||
});
|
||||
services.Configure<StackExchangeRedisCallsInstrumentationOptions>(options =>
|
||||
{
|
||||
optionsPickedFromDI = true;
|
||||
});
|
||||
services.AddOpenTelemetryTracing(builder => builder.AddRedisInstrumentation());
|
||||
|
||||
using var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var tracerProvider = serviceProvider.GetRequiredService<TracerProvider>();
|
||||
|
||||
Assert.True(connectionMultiplexerPickedFromDI);
|
||||
Assert.True(optionsPickedFromDI);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackExchangeRedis_DependencyInjection_Failure()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddOpenTelemetryTracing(builder => builder.AddRedisInstrumentation());
|
||||
|
||||
using var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => serviceProvider.GetRequiredService<TracerProvider>());
|
||||
}
|
||||
|
||||
private static void VerifyActivityData(Activity activity, bool isSet, EndPoint endPoint, bool setCommandKey = false)
|
||||
{
|
||||
if (isSet)
|
||||
{
|
||||
Assert.Equal("SETEX", activity.DisplayName);
|
||||
if (setCommandKey)
|
||||
{
|
||||
Assert.Equal("SETEX key1", activity.GetTagValue(SemanticConventions.AttributeDbStatement));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal("SETEX", activity.GetTagValue(SemanticConventions.AttributeDbStatement));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal("GET", activity.DisplayName);
|
||||
if (setCommandKey)
|
||||
{
|
||||
Assert.Equal("GET key1", activity.GetTagValue(SemanticConventions.AttributeDbStatement));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal("GET", activity.GetTagValue(SemanticConventions.AttributeDbStatement));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal(Status.Unset, activity.GetStatus());
|
||||
Assert.Equal("redis", activity.GetTagValue(SemanticConventions.AttributeDbSystem));
|
||||
Assert.Equal(0, activity.GetTagValue(StackExchangeRedisCallsInstrumentation.RedisDatabaseIndexKeyName));
|
||||
|
||||
if (endPoint is IPEndPoint ipEndPoint)
|
||||
{
|
||||
Assert.Equal(ipEndPoint.Address.ToString(), activity.GetTagValue(SemanticConventions.AttributeNetPeerIp));
|
||||
Assert.Equal(ipEndPoint.Port, activity.GetTagValue(SemanticConventions.AttributeNetPeerPort));
|
||||
}
|
||||
else if (endPoint is DnsEndPoint dnsEndPoint)
|
||||
{
|
||||
Assert.Equal(dnsEndPoint.Host, activity.GetTagValue(SemanticConventions.AttributeNetPeerName));
|
||||
Assert.Equal(dnsEndPoint.Port, activity.GetTagValue(SemanticConventions.AttributeNetPeerPort));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(endPoint.ToString(), activity.GetTagValue(SemanticConventions.AttributePeerService));
|
||||
}
|
||||
}
|
||||
|
||||
private static void VerifySamplingParameters(SamplingParameters samplingParameters)
|
||||
{
|
||||
Assert.NotNull(samplingParameters.Tags);
|
||||
Assert.Contains(
|
||||
samplingParameters.Tags,
|
||||
kvp => kvp.Key == SemanticConventions.AttributeDbSystem
|
||||
&& (string)kvp.Value == "redis");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
# Start a redis container and then run OpenTelemetry redis integration tests.
|
||||
# This should be run from the root of the repo:
|
||||
# opentelemetry>docker-compose --file=test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/docker-compose.yml --project-directory=. up --exit-code-from=tests --build
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
tests:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./test/OpenTelemetry.Instrumentation.StackExchangeRedis.Tests/Dockerfile
|
||||
command: --TestCaseFilter:CategoryName=RedisIntegrationTests
|
||||
environment:
|
||||
- OTEL_REDISENDPOINT=redis:6379
|
||||
depends_on:
|
||||
- redis
|
Loading…
Reference in New Issue