Skip to content

Commit

Permalink
StackExchange.Redis integration (#130)
Browse files Browse the repository at this point in the history
* use explicit type parameters

* remove log line

* add async, span tagging

* add integration test for redis

* add redis install script
  • Loading branch information
dd-caleb authored and lucaspimentel committed Sep 17, 2018
1 parent 8b1929e commit 9b594f9
Show file tree
Hide file tree
Showing 23 changed files with 521 additions and 42 deletions.
16 changes: 16 additions & 0 deletions Datadog.Trace.sln
Expand Up @@ -107,13 +107,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Datadog.Trace.ClrProfiler.I
{BC7B3216-9C37-4570-8A4A-5D716AB98E9C} = {BC7B3216-9C37-4570-8A4A-5D716AB98E9C}
{C0C8D381-D6B9-4C76-9428-F40F2FA93A9A} = {C0C8D381-D6B9-4C76-9428-F40F2FA93A9A}
{086FF8A0-9CEE-470A-9751-78B0F1340649} = {086FF8A0-9CEE-470A-9751-78B0F1340649}
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A} = {F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.SqlServer", "samples\Samples.SqlServer\Samples.SqlServer.csproj", "{086FF8A0-9CEE-470A-9751-78B0F1340649}"
ProjectSection(ProjectDependencies) = postProject
{C0C8D381-D6B9-4C76-9428-F40F2FA93A9A} = {C0C8D381-D6B9-4C76-9428-F40F2FA93A9A}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.RedisCore", "samples\Samples.RedisCore\Samples.RedisCore.csproj", "{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -364,6 +367,18 @@ Global
{086FF8A0-9CEE-470A-9751-78B0F1340649}.Release|x64.Build.0 = Release|x64
{086FF8A0-9CEE-470A-9751-78B0F1340649}.Release|x86.ActiveCfg = Release|x86
{086FF8A0-9CEE-470A-9751-78B0F1340649}.Release|x86.Build.0 = Release|x86
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Debug|x64.ActiveCfg = Debug|x64
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Debug|x64.Build.0 = Debug|x64
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Debug|x86.ActiveCfg = Debug|x86
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Debug|x86.Build.0 = Debug|x86
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Release|Any CPU.Build.0 = Release|Any CPU
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Release|x64.ActiveCfg = Release|x64
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Release|x64.Build.0 = Release|x64
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Release|x86.ActiveCfg = Release|x86
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -390,6 +405,7 @@ Global
{BC7B3216-9C37-4570-8A4A-5D716AB98E9C} = {AA6F5582-3B71-49AC-AA39-8F7815AC46BE}
{0D546118-B70A-44D0-B675-39EDB99FCEEE} = {8CEC2042-F11C-49F5-A674-2355793B600A}
{086FF8A0-9CEE-470A-9751-78B0F1340649} = {AA6F5582-3B71-49AC-AA39-8F7815AC46BE}
{F5B27CC4-1DF6-4ECD-A4FD-8200152F9A5A} = {AA6F5582-3B71-49AC-AA39-8F7815AC46BE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F}
Expand Down
20 changes: 20 additions & 0 deletions ci/install-redis.ps1
@@ -0,0 +1,20 @@
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"

$tmp = [System.IO.Path]::GetTempPath()
$installerUrl = "https://github.com/MicrosoftArchive/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.msi"
$installerPath = Join-Path $tmp "install-redis.msi"
$installerLog = Join-Path $tmp 'install-redis.log'

if (Test-Path "C:\Program Files\Redis\redis-server.exe") {
Write-Output "redis is already installed"
Exit 0;
}

if (-not (Test-Path $installerPath)) {
Write-Output "downloading redis to $installerPath"
(New-Object Net.WebClient).DownloadFile($installerUrl, $installerPath)
}

Write-Output "installing redis"
Start-Process "msiexec.exe" -ArgumentList "/i",$installerPath,"/qn","/norestart","/L",$installerLog -Wait -NoNewWindow
Get-Content $installerLog
2 changes: 1 addition & 1 deletion devenv.bat
Expand Up @@ -50,7 +50,7 @@ SET CORECLR_PROFILER={846F5F1C-F9AE-4B07-969E-05C26BC060D8}
SET CORECLR_PROFILER_PATH=%~dp0\src\Datadog.Trace.ClrProfiler.Native\bin\%profiler_configuration%\%profiler_platform%\Datadog.Trace.ClrProfiler.Native.dll

rem Limit profiling to these processes only
SET DD_PROFILER_PROCESSES=w3wp.exe;iisexpress.exe;Samples.AspNetCoreMvc2.exe;dotnet.exe;Samples.ConsoleFramework.exe;Samples.ConsoleCore.exe;Samples.SqlServer.exe
SET DD_PROFILER_PROCESSES=w3wp.exe;iisexpress.exe;Samples.AspNetCoreMvc2.exe;dotnet.exe;Samples.ConsoleFramework.exe;Samples.ConsoleCore.exe;Samples.SqlServer.exe;Samples.RedisCore.exe

rem Set location of integration definitions
SET DD_INTEGRATIONS=%~dp0\integrations.json;%~dp0\test-integrations.json
Expand Down
39 changes: 38 additions & 1 deletion integrations.json
Expand Up @@ -97,7 +97,7 @@
{
"name": "SqlServer",
"method_replacements": [
{
{
"caller": {
"assembly": "System.Data",
"type": "System.Data.SqlClient.SqlCommand"
Expand Down Expand Up @@ -131,5 +131,42 @@
}
}
]
},
{
"name": "StackExchangeRedis",
"method_replacements": [
{
"caller": {
"assembly": "StackExchange.Redis"
},
"target": {
"assembly": "StackExchange.Redis",
"type": "StackExchange.Redis.ConnectionMultiplexer",
"method": "ExecuteSyncImpl"
},
"wrapper": {
"assembly": "Datadog.Trace.ClrProfiler.Managed, Version=0.2.2.0, Culture=neutral, PublicKeyToken=def86d061d0d2eeb",
"type": "Datadog.Trace.ClrProfiler.Integrations.StackExchangeRedis",
"method": "ExecuteSyncImpl",
"signature": "00 04 1C 1C 1C 1C 1C"
}
},
{
"caller": {
"assembly": "StackExchange.Redis"
},
"target": {
"assembly": "StackExchange.Redis",
"type": "StackExchange.Redis.ConnectionMultiplexer",
"method": "ExecuteAsyncImpl"
},
"wrapper": {
"assembly": "Datadog.Trace.ClrProfiler.Managed, Version=0.2.2.0, Culture=neutral, PublicKeyToken=def86d061d0d2eeb",
"type": "Datadog.Trace.ClrProfiler.Integrations.StackExchangeRedis",
"method": "ExecuteAsyncImpl",
"signature": "00 05 1C 1C 1C 1C 1C 1C"
}
}
]
}
]
19 changes: 19 additions & 0 deletions samples/Samples.RedisCore/Program.cs
@@ -0,0 +1,19 @@
using System;
using StackExchange.Redis;

namespace Samples.RedisCore
{
class Program
{
static void Main(string[] args)
{
var redis = ConnectionMultiplexer.Connect("localhost");
for (var i = 0; i < 100; i++)
{
redis.GetDatabase().StringSet($"KEY-{i}", $"VALUE {i}");
var value = redis.GetDatabase().StringGetAsync($"KEY-{i}").Result;
Console.WriteLine(value.ToString());
}
}
}
}
7 changes: 7 additions & 0 deletions samples/Samples.RedisCore/Properties/launchSettings.json
@@ -0,0 +1,7 @@
{
"profiles": {
"Samples.RedisCore": {
"commandName": "Project"
}
}
}
26 changes: 26 additions & 0 deletions samples/Samples.RedisCore/Samples.RedisCore.csproj
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net461;net47;netcoreapp2.0</TargetFrameworks>
<Platforms>AnyCPU;x64;x86</Platforms>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64</RuntimeIdentifiers>
</PropertyGroup>

<PropertyGroup Condition=" '$(OS)' != 'Windows_NT' ">
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' != 'netcoreapp2.0'">
<RuntimeIdentifier>win-$(Platform)</RuntimeIdentifier>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="StackExchange.Redis" Version="2.0.505" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Datadog.Trace.ClrProfiler.Managed\Datadog.Trace.ClrProfiler.Managed.csproj" />
</ItemGroup>

</Project>
46 changes: 38 additions & 8 deletions src/Datadog.Trace.ClrProfiler.Managed/DynamicMethodBuilder.cs
Expand Up @@ -17,12 +17,14 @@ public static class DynamicMethodBuilder
/// <typeparam name="TDelegate">A <see cref="Delegate"/> type with the signature of the method to call.</typeparam>
/// <param name="type">The <see cref="Type"/> that contains the method.</param>
/// <param name="methodName">The name of the method.</param>
/// <param name="isStatic"><c>true</c> if the method is static, <c>false</c> otherwise.</param>
/// <param name="methodParameterTypes">optional types for the method parameters</param>
/// <param name="methodGenericArguments">optional generic type arguments for a generic method</param>
/// <returns>A <see cref="Delegate"/> that can be used to execute the dynamic method.</returns>
public static TDelegate CreateMethodCallDelegate<TDelegate>(
Type type,
string methodName,
bool isStatic)
Type[] methodParameterTypes = null,
Type[] methodGenericArguments = null)
where TDelegate : Delegate
{
Type delegateType = typeof(TDelegate);
Expand All @@ -48,20 +50,48 @@ public static class DynamicMethodBuilder
}

// find any method that matches by name and parameter types
MethodInfo methodInfo = type.GetMethod(
methodName,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static,
null,
isStatic ? parameterTypes : parameterTypes.Skip(1).ToArray(),
null);
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
methods = methods.Where(m => m.Name == methodName).ToArray();
if (methodParameterTypes != null)
{
methods = methods.Where(m =>
{
var ps = m.GetParameters();
if (ps.Length != methodParameterTypes.Length)
{
return false;
}
for (var i = 0; i < ps.Length; i++)
{
var t1 = ps[i].ParameterType;
var t2 = methodParameterTypes[i];
// generics can be tricky to compare for type equality
// so we will just check the namespace and name
if (t1.Namespace != t2.Namespace || t1.Name != t2.Name)
{
return false;
}
}
return true;
}).ToArray();
}

MethodInfo methodInfo = methods.FirstOrDefault();
if (methodInfo == null)
{
// method not found
// TODO: logging
return null;
}

if (methodGenericArguments != null)
{
methodInfo = methodInfo.MakeGenericMethod(methodGenericArguments);
}

var dynamicMethod = new DynamicMethod(methodName, returnType, parameterTypes, type);
ILGenerator ilGenerator = dynamicMethod.GetILGenerator();

Expand Down
Expand Up @@ -86,8 +86,7 @@ public AspNetCoreMvc2Integration(object actionDescriptorObj, object httpContextO

_beforeAction = DynamicMethodBuilder.CreateMethodCallDelegate<Action<object, object, object, object>>(
type,
"BeforeAction",
isStatic: true);
"BeforeAction");
}
}
catch
Expand Down Expand Up @@ -141,8 +140,7 @@ public AspNetCoreMvc2Integration(object actionDescriptorObj, object httpContextO

_afterAction = DynamicMethodBuilder.CreateMethodCallDelegate<Action<object, object, object, object>>(
type,
"AfterAction",
isStatic: true);
"AfterAction");
}
}
catch
Expand Down
Expand Up @@ -29,7 +29,7 @@ public static object ExecuteReaderWithMethod(dynamic @this, int behavior, string
_executeReaderWithMethod = DynamicMethodBuilder.CreateMethodCallDelegate<Func<object, CommandBehavior, string, object>>(
command.GetType(),
"ExecuteReader",
isStatic: false);
new Type[] { typeof(CommandBehavior), typeof(string) });
}

using (var scope = CreateScope(command))
Expand Down Expand Up @@ -61,7 +61,7 @@ public static object ExecuteReader(dynamic @this, int behavior)
_executeReader = DynamicMethodBuilder.CreateMethodCallDelegate<Func<object, CommandBehavior, object>>(
command.GetType(),
"ExecuteReader",
isStatic: false);
new Type[] { typeof(CommandBehavior) });
}

using (var scope = CreateScope(command))
Expand Down

0 comments on commit 9b594f9

Please sign in to comment.