Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

opentelemetry support #205

Merged
merged 2 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions FreeRedis.sln
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeRedis.DistributedCache"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console_net8_cluster_client_side_caching", "examples\console_net8_cluster_client_side_caching\console_net8_cluster_client_side_caching.csproj", "{1C4387AA-C4BD-4388-97D1-3A769439705D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeRedis.OpenTelemetry", "src\FreeRedis.OpenTelemetry\FreeRedis.OpenTelemetry.csproj", "{CA71A937-D939-4922-B1DB-0380833B7270}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetryTest", "examples\OpenTelemetryTest\OpenTelemetryTest.csproj", "{37FAC646-809D-4BD4-8B74-1A9BEE02E963}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -225,6 +229,30 @@ Global
{1C4387AA-C4BD-4388-97D1-3A769439705D}.Release|x64.Build.0 = Release|Any CPU
{1C4387AA-C4BD-4388-97D1-3A769439705D}.Release|x86.ActiveCfg = Release|Any CPU
{1C4387AA-C4BD-4388-97D1-3A769439705D}.Release|x86.Build.0 = Release|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Debug|x64.ActiveCfg = Debug|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Debug|x64.Build.0 = Debug|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Debug|x86.ActiveCfg = Debug|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Debug|x86.Build.0 = Debug|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Release|Any CPU.Build.0 = Release|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Release|x64.ActiveCfg = Release|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Release|x64.Build.0 = Release|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Release|x86.ActiveCfg = Release|Any CPU
{CA71A937-D939-4922-B1DB-0380833B7270}.Release|x86.Build.0 = Release|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Debug|x64.ActiveCfg = Debug|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Debug|x64.Build.0 = Debug|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Debug|x86.ActiveCfg = Debug|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Debug|x86.Build.0 = Debug|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Release|Any CPU.Build.0 = Release|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Release|x64.ActiveCfg = Release|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Release|x64.Build.0 = Release|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Release|x86.ActiveCfg = Release|Any CPU
{37FAC646-809D-4BD4-8B74-1A9BEE02E963}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -246,6 +274,8 @@ Global
{7AC68251-83D4-423F-849C-04BD89F4BDFB} = {DD1426BF-F56F-49A8-9D4E-2BA0E3BEFC72}
{B0B0B55E-4D41-4419-83FE-309F72699A3A} = {D8045351-59E4-45B9-81F5-D21CC0996344}
{1C4387AA-C4BD-4388-97D1-3A769439705D} = {DD1426BF-F56F-49A8-9D4E-2BA0E3BEFC72}
{CA71A937-D939-4922-B1DB-0380833B7270} = {D8045351-59E4-45B9-81F5-D21CC0996344}
{37FAC646-809D-4BD4-8B74-1A9BEE02E963} = {DD1426BF-F56F-49A8-9D4E-2BA0E3BEFC72}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B36AD060-F6AE-49C7-A310-B58B8467CE69}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using FreeRedis;
using Microsoft.AspNetCore.Mvc;

namespace OpenTelemetryTest.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly IRedisClient _redisClient;
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

private readonly ILogger<WeatherForecastController> _logger;

public WeatherForecastController(ILogger<WeatherForecastController> logger, IRedisClient redisClient)
{
_logger = logger;
_redisClient = redisClient;
}

[HttpGet(Name = "GetWeatherForecast")]
public async Task<IEnumerable<WeatherForecast>> Get()
{
var response = Enumerable.Range(1, 20).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray();

await _redisClient.SetAsync("test01", response, 60 * 5);
await _redisClient.GetAsync("test01");

var cache = response.GroupBy(x => x.Summary).ToDictionary(g => g.Key!, g => g.ToList());
await _redisClient.MSetAsync(cache);

var cache1 = await _redisClient.GetAsync<List<WeatherForecast>>(Summaries[Random.Shared.Next(Summaries.Length)]);

return response;
}
}
}
25 changes: 25 additions & 0 deletions examples/OpenTelemetryTest/OpenTelemetryTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
<PackageReference Include="OpenTelemetry" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\FreeRedis.OpenTelemetry\FreeRedis.OpenTelemetry.csproj" />
<ProjectReference Include="..\..\src\FreeRedis\FreeRedis.csproj" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions examples/OpenTelemetryTest/OpenTelemetryTest.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@OpenTelemetryTest_HostAddress = http://localhost:5252

GET {{OpenTelemetryTest_HostAddress}}/weatherforecast/
Accept: application/json

###
62 changes: 62 additions & 0 deletions examples/OpenTelemetryTest/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using FreeRedis;
using FreeRedis.OpenTelemetry;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenTelemetry.Exporter;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var services = builder.Services;
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

//redis
var redisClient = new RedisClient(builder.Configuration.GetConnectionString("Redis"));
redisClient.Serialize = obj => System.Text.Json.JsonSerializer.Serialize(obj);
redisClient.Deserialize = (json, type) => System.Text.Json.JsonSerializer.Deserialize(json, type);
//redisClient.Notice += (s, e) =>
//{
// Console.WriteLine(e.Log);
//};
services.TryAddSingleton<IRedisClient>(redisClient);

//OpenTelemetry
var otel = services.AddOpenTelemetry();
otel.ConfigureResource(resource =>
{
resource.AddTelemetrySdk();
resource.AddEnvironmentVariableDetector();
resource.AddService("FreeRedisTest");
});
var otlpUrl = builder.Configuration["OpenTelemetry:OtlpHttpUrl"];
otel.WithTracing(tracing => tracing.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddFreeRedisInstrumentation(redisClient)
.SetSampler(new AlwaysOnSampler())
//.AddConsoleExporter()
.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri($"http://{otlpUrl}/v1/traces");
otlpOptions.Protocol = OtlpExportProtocol.HttpProtobuf;
otlpOptions.Headers = "Authorization=Basic YWRtaW46dGVzdEAxMjM=";
})
);

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();
15 changes: 15 additions & 0 deletions examples/OpenTelemetryTest/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchUrl": "weatherforecast",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
13 changes: 13 additions & 0 deletions examples/OpenTelemetryTest/WeatherForecast.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace OpenTelemetryTest
{
public class WeatherForecast
{
public DateOnly Date { get; set; }

public int TemperatureC { get; set; }

public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

public string? Summary { get; set; }
}
}
14 changes: 14 additions & 0 deletions examples/OpenTelemetryTest/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"Redis": "127.0.0.1:6379,password=test@123,defaultDatabase=0"
},
"OpenTelemetry": {
"OtlpHttpUrl": "127.0.0.1:4318"
}
}
9 changes: 9 additions & 0 deletions examples/OpenTelemetryTest/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
77 changes: 77 additions & 0 deletions src/FreeRedis.OpenTelemetry/DiagnosticListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using OpenTelemetry.Trace;
using System.Diagnostics;

namespace FreeRedis.OpenTelemetry;

public class DiagnosticListener : IObserver<KeyValuePair<string, object?>>
{
public const string SourceName = "FreeRedis.OpenTelemetry";

private static readonly ActivitySource ActivitySource = new(SourceName, "1.0.0");

/// <summary>Notifies the observer that the provider has finished sending push-based notifications.</summary>
public void OnCompleted()
{
}

/// <summary>Notifies the observer that the provider has experienced an error condition.</summary>
/// <param name="error">An object that provides additional information about the error.</param>
public void OnError(Exception error)
{
}

/// <summary>Provides the observer with new data.</summary>
/// <param name="evt">The current notification information.</param>
public void OnNext(KeyValuePair<string, object?> evt)
{
//https://opentelemetry.io/docs/specs/semconv/database/redis/
switch (evt.Key)
{
case FreeRedisDiagnosticListenerNames.NoticeCallBefore:
{
var eventData = (InterceptorBeforeEventArgs)evt.Value!;
var activity = ActivitySource.StartActivity("redis command execute: " + eventData.Command);
if (activity != null)
{
activity.SetTag("db.system", "redis");
activity.SetTag("db.operation.name", eventData.Command._command);
activity.SetTag("db.query.text", eventData.Command);
//Activity.Current?.SetTag("network.peer.address", ip);
//Activity.Current?.SetTag("network.peer.port", port);

activity.AddEvent(new ActivityEvent("redis command execute start",
DateTimeOffset.FromUnixTimeMilliseconds(eventData.OperationTimestamp!.Value)));
}
}
break;
case FreeRedisDiagnosticListenerNames.NoticeCallAfter:
{
var eventData = (InterceptorAfterEventArgs)evt.Value!;
var writeTarget = eventData.Command.WriteTarget;
if (!string.IsNullOrEmpty(writeTarget))
{
var parts = writeTarget.Split([':', '/'], StringSplitOptions.RemoveEmptyEntries);
var ip = parts[0];
var port = int.Parse(parts[1]);
var dbIndex = int.Parse(parts[2]);

Activity.Current?.SetTag("server.address", ip);
Activity.Current?.SetTag("server.port", port);
Activity.Current?.SetTag("db.namespace", dbIndex);
}
var tags = new ActivityTagsCollection { new("free_redis.duration", eventData.ElapsedMilliseconds) };
if (eventData.Exception != null)
{
Activity.Current?.SetStatus(Status.Error.WithDescription(eventData.Exception.Message));
tags.Add(new("error.type", eventData.Exception.Message));
}
Activity.Current?.AddEvent(new ActivityEvent("redis command executed",
DateTimeOffset.FromUnixTimeMilliseconds(eventData.OperationTimestamp!.Value), tags)
);

Activity.Current?.Stop();
}
break;
}
}
}
Loading
Loading