Commit 541751a1 authored by Savorboard's avatar Savorboard Committed by Lemon

Add supports for EntityFrameworkCore (#37)

* Add components project of SkyWalking.Diagnostics.EntityFrameworkCore for supports EntityFrameworkCore diagnostics.

* Add builder extensions file and EF diagnostics default impl file.

* Add EntityFrameworkDiagnostic processor.

* Add unit tests for EntityFrameworkCore diagnostics component.
parent 38e00071
...@@ -40,9 +40,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkyWalking.Diagnostics.Http ...@@ -40,9 +40,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkyWalking.Diagnostics.Http
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkyWalking.Extensions.DependencyInjection", "src\SkyWalking.Extensions.DependencyInjection\SkyWalking.Extensions.DependencyInjection.csproj", "{BF5579ED-113C-4EE6-AE03-9A9CA590C924}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkyWalking.Extensions.DependencyInjection", "src\SkyWalking.Extensions.DependencyInjection\SkyWalking.Extensions.DependencyInjection.csproj", "{BF5579ED-113C-4EE6-AE03-9A9CA590C924}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkyWalking.Diagnostics.CAP", "src\SkyWalking.Diagnostics.CAP\SkyWalking.Diagnostics.CAP.csproj", "{55621423-19C3-4928-8B45-666FA87BF6A2}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkyWalking.Diagnostics.CAP", "src\SkyWalking.Diagnostics.CAP\SkyWalking.Diagnostics.CAP.csproj", "{55621423-19C3-4928-8B45-666FA87BF6A2}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkyWalking.Diagnostics.HttpClient.Tests", "test\SkyWalking.Diagnostics.HttpClient.Tests\SkyWalking.Diagnostics.HttpClient.Tests.csproj", "{05E533B1-473F-48C0-95F7-B87061BE9834}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkyWalking.Diagnostics.HttpClient.Tests", "test\SkyWalking.Diagnostics.HttpClient.Tests\SkyWalking.Diagnostics.HttpClient.Tests.csproj", "{05E533B1-473F-48C0-95F7-B87061BE9834}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkyWalking.Diagnostics.EntityFrameworkCore", "src\SkyWalking.Diagnostics.EntityFrameworkCore\SkyWalking.Diagnostics.EntityFrameworkCore.csproj", "{CD334460-8E61-41EB-9762-62C6A342CACB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkyWalking.Diagnostics.EntityFrameworkCore.Tests", "test\SkyWalking.Diagnostics.EntityFrameworkCore.Tests\SkyWalking.Diagnostics.EntityFrameworkCore.Tests.csproj", "{4CF91A8C-25C8-4BD8-A93D-3183AF0FE2E2}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
...@@ -98,6 +102,14 @@ Global ...@@ -98,6 +102,14 @@ Global
{05E533B1-473F-48C0-95F7-B87061BE9834}.Debug|Any CPU.Build.0 = Debug|Any CPU {05E533B1-473F-48C0-95F7-B87061BE9834}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05E533B1-473F-48C0-95F7-B87061BE9834}.Release|Any CPU.ActiveCfg = Release|Any CPU {05E533B1-473F-48C0-95F7-B87061BE9834}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05E533B1-473F-48C0-95F7-B87061BE9834}.Release|Any CPU.Build.0 = Release|Any CPU {05E533B1-473F-48C0-95F7-B87061BE9834}.Release|Any CPU.Build.0 = Release|Any CPU
{CD334460-8E61-41EB-9762-62C6A342CACB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD334460-8E61-41EB-9762-62C6A342CACB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD334460-8E61-41EB-9762-62C6A342CACB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD334460-8E61-41EB-9762-62C6A342CACB}.Release|Any CPU.Build.0 = Release|Any CPU
{4CF91A8C-25C8-4BD8-A93D-3183AF0FE2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CF91A8C-25C8-4BD8-A93D-3183AF0FE2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4CF91A8C-25C8-4BD8-A93D-3183AF0FE2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4CF91A8C-25C8-4BD8-A93D-3183AF0FE2E2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
...@@ -117,6 +129,8 @@ Global ...@@ -117,6 +129,8 @@ Global
{BF5579ED-113C-4EE6-AE03-9A9CA590C924} = {79ED86A5-E9B9-49B2-9354-C911C079D03E} {BF5579ED-113C-4EE6-AE03-9A9CA590C924} = {79ED86A5-E9B9-49B2-9354-C911C079D03E}
{55621423-19C3-4928-8B45-666FA87BF6A2} = {79ED86A5-E9B9-49B2-9354-C911C079D03E} {55621423-19C3-4928-8B45-666FA87BF6A2} = {79ED86A5-E9B9-49B2-9354-C911C079D03E}
{05E533B1-473F-48C0-95F7-B87061BE9834} = {613F0980-91ED-4064-8E8C-168582EF4AD7} {05E533B1-473F-48C0-95F7-B87061BE9834} = {613F0980-91ED-4064-8E8C-168582EF4AD7}
{CD334460-8E61-41EB-9762-62C6A342CACB} = {79ED86A5-E9B9-49B2-9354-C911C079D03E}
{4CF91A8C-25C8-4BD8-A93D-3183AF0FE2E2} = {613F0980-91ED-4064-8E8C-168582EF4AD7}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {94C0DA2C-CCCB-4314-93A2-9809B5DD0583} SolutionGuid = {94C0DA2C-CCCB-4314-93A2-9809B5DD0583}
......
/*
* Licensed to the OpenSkywalking under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*
*/
using System;
using System.Data.Common;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using SkyWalking.Context;
using SkyWalking.Context.Tag;
using SkyWalking.Context.Trace;
using SkyWalking.NetworkProtocol.Trace;
namespace SkyWalking.Diagnostics.EntityFrameworkCore
{
public class EntityFrameworkCoreDiagnosticProcessor : ITracingDiagnosticProcessor
{
private Func<CommandEventData, string> _operationNameResolver;
public string ListenerName => DbLoggerCategory.Name;
/// <summary>
/// A delegate that returns the OpenTracing "operation name" for the given command.
/// </summary>
public Func<CommandEventData, string> OperationNameResolver
{
get
{
return _operationNameResolver ??
(_operationNameResolver = (data) => "DB " + data.ExecuteMethod.ToString());
}
set => _operationNameResolver = value ?? throw new ArgumentNullException(nameof(OperationNameResolver));
}
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting")]
public void CommandExecuting([Object]CommandEventData eventData)
{
var operationName = OperationNameResolver(eventData);
var span = ContextManager.CreateLocalSpan(operationName);
span.SetComponent(ComponentsDefine.EntityFrameworkCore);
span.SetLayer(SpanLayer.DB);
Tags.DbInstance.Set(span, eventData.Command.Connection.Database);
Tags.DbStatement.Set(span, eventData.Command.CommandText);
Tags.DbBindVariables.Set(span, BuildParameterVariables(eventData.Command.Parameters));
}
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted")]
public void CommandExecuted()
{
ContextManager.StopSpan();
}
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandError")]
public void CommandError(CommandErrorEventData eventData)
{
var span = ContextManager.ActiveSpan;
if (span == null)
{
return;
}
if (eventData != null)
{
span.Log(eventData.Exception);
}
span.ErrorOccurred();
ContextManager.StopSpan(span);
}
private string BuildParameterVariables(DbParameterCollection dbParameters)
{
if (dbParameters == null)
{
return string.Empty;
}
return dbParameters.FormatParameters(false);
}
}
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>SkyWalking.Diagnostics.EntityFrameworkCore</AssemblyName>
<AssemblyTitle>SkyWalking.Diagnostics.EntityFrameworkCore</AssemblyTitle>
<PackageId>SkyWalking.AspNetCore</PackageId>
<PackageTags>SkyWalking;APM;EntityFrameworkCore;EF</PackageTags>
<Description>SkyWalking.Diagnostics.EntityFrameworkCore notifies of EF requests.</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SkyWalking.Abstractions\SkyWalking.Abstractions.csproj" />
<ProjectReference Include="..\SkyWalking.Core\SkyWalking.Core.csproj" />
<ProjectReference Include="..\SkyWalking.Extensions.DependencyInjection\SkyWalking.Extensions.DependencyInjection.csproj" />
</ItemGroup>
</Project>
/*
* Licensed to the OpenSkywalking under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*
*/
using System;
using Microsoft.Extensions.DependencyInjection;
using SkyWalking.Extensions.DependencyInjection;
namespace SkyWalking.Diagnostics.EntityFrameworkCore
{
public static class SkyWalkingBuilderExtensions
{
public static SkyWalkingBuilder AddEntityFrameworkCore(this SkyWalkingBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.Services.AddSingleton<ITracingDiagnosticProcessor, EntityFrameworkCoreDiagnosticProcessor>();
return builder;
}
}
}
\ No newline at end of file
using System;
using System.Diagnostics;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using SkyWalking.Diagnostics.EntityFrameworkCore.Tests.Fakes;
using Xunit;
namespace SkyWalking.Diagnostics.EntityFrameworkCore.Tests
{
// ReSharper disable once InconsistentNaming
public class EFCoreDiagnosticProcessorTests : IDisposable
{
private readonly DbContextOptions<FakeDbContext> _options;
public EFCoreDiagnosticProcessorTests()
{
// In-memory database only exists while the connection is open
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
_options = new DbContextOptionsBuilder<FakeDbContext>()
.UseSqlite(connection)
.Options;
using (var dbContext = new FakeDbContext(_options))
{
dbContext.Database.EnsureCreated();
}
}
[Fact]
public void DbContext_Init_Success_Test()
{
using (var dbContext = new FakeDbContext(_options))
{
Assert.NotNull(dbContext);
}
}
[Fact]
public void DbContext_Works_Success_Test()
{
using (var dbContext = new FakeDbContext(_options))
{
dbContext.Users.Add(new FakeUser("Zhang", "San"));
dbContext.SaveChanges();
}
using (var dbContext = new FakeDbContext(_options))
{
Assert.Single(dbContext.Users);
}
}
[Fact]
public void EF_Diagnostics_Success_Test()
{
var processorObserver = new TracingDiagnosticProcessorObserver(new[]
{
new EntityFrameworkCoreDiagnosticProcessor()
});
DiagnosticListener.AllListeners.Subscribe(processorObserver);
using (var tracerContextListener = new FakeIgnoreTracerContextListener())
{
try
{
using (var dbContext = new FakeDbContext(_options))
{
dbContext.Users.Add(new FakeUser("Zhang", "San"));
dbContext.SaveChanges();
}
}
catch
{
// ignored
}
Assert.Equal(1, tracerContextListener.Counter);
}
}
public void Dispose()
{
using (var context = new FakeDbContext(_options))
{
context.Database.EnsureDeleted();
}
}
}
}
using Microsoft.EntityFrameworkCore;
namespace SkyWalking.Diagnostics.EntityFrameworkCore.Tests.Fakes
{
public class FakeDbContext : DbContext
{
public FakeDbContext(DbContextOptions<FakeDbContext> options)
: base(options)
{
}
public DbSet<FakeUser> Users { get; set; }
}
}
using System;
using SkyWalking.Context;
namespace SkyWalking.Diagnostics.EntityFrameworkCore.Tests.Fakes
{
public class FakeIgnoreTracerContextListener : IIgnoreTracerContextListener ,IDisposable
{
public int Counter { get; set; }
public FakeIgnoreTracerContextListener()
{
IgnoredTracerContext.ListenerManager.Add(this);
}
public void AfterFinish(ITracerContext tracerContext)
{
Counter = Counter + 1;
}
public void Dispose()
{
IgnoredTracerContext.ListenerManager.Remove(this);
}
}
}
\ No newline at end of file
namespace SkyWalking.Diagnostics.EntityFrameworkCore.Tests.Fakes
{
public class FakeUser
{
public FakeUser()
{
}
public FakeUser(string firstName, string lastName)
{
}
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return $"Output for [User] - Id:{Id}, FirstName:{FirstName}, LastName:{LastName}";
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SkyWalking.Diagnostics.EntityFrameworkCore\SkyWalking.Diagnostics.EntityFrameworkCore.csproj" />
</ItemGroup>
</Project>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment