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

feat: EF 6 Query as Span. #1107

Merged
merged 33 commits into from Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5582828
Clear usings.
lucas-zimerman Jul 5, 2021
63ac7f1
clear program from ef
lucas-zimerman Jul 5, 2021
6829afd
fast hack for logging
lucas-zimerman Jul 5, 2021
295802c
WIP
lucas-zimerman Jul 5, 2021
dd2bb76
wip
lucas-zimerman Jul 6, 2021
5411fe2
Merge branch 'main' into feat/ef-query
lucas-zimerman Sep 6, 2021
ce849aa
Format code
getsentry-bot Sep 6, 2021
3e5b553
Update Entity Framework Database integration.
lucas-zimerman Sep 6, 2021
7e5619e
apply stage changes.
lucas-zimerman Sep 6, 2021
fa82b9a
Format code
getsentry-bot Sep 6, 2021
a6ed4b8
remove diag source ref, updated EntityFramework version and updated s…
lucas-zimerman Sep 6, 2021
72bfffc
Merge branch 'feat/ef-query' of https://github.com/getsentry/sentry-d…
lucas-zimerman Sep 6, 2021
24e4534
EF Sample cleanup. Small refactors to PerformanceListener. Added tests.
lucas-zimerman Sep 7, 2021
1cd16da
Format code
getsentry-bot Sep 7, 2021
1bcf037
text change
lucas-zimerman Sep 7, 2021
d475102
refactor.
lucas-zimerman Sep 7, 2021
7427900
cleanup and removed empty breadcrumbs.
lucas-zimerman Sep 8, 2021
e03e130
missing .
lucas-zimerman Sep 8, 2021
7f6a475
Merge branch 'main' into feat/ef-query
lucas-zimerman Sep 8, 2021
58b6047
Merge branch 'main' into feat/ef-query
lucas-zimerman Sep 9, 2021
43cdddf
Merge branch 'main' into feat/ef-query
lucas-zimerman Sep 9, 2021
422b4c0
Added return to null or empty check.
lucas-zimerman Sep 9, 2021
34486a8
Merge branch 'main' into feat/ef-query
lucas-zimerman Sep 13, 2021
d13bfe7
Merge branch 'main' into feat/ef-query
lucas-zimerman Sep 20, 2021
a2a58fc
fix requested changes, Added unregister extension, removed Dispose fr…
lucas-zimerman Sep 20, 2021
002e8a7
fix xml .
lucas-zimerman Sep 20, 2021
5e6bd70
Merge branch 'main' into feat/ef-query
lucas-zimerman Sep 21, 2021
bfa707d
Merge branch 'main' into feat/ef-query
bruno-garcia Nov 1, 2021
9607f5e
Fix Changelog location.
lucas-zimerman Nov 2, 2021
537354c
Name convention fixes.
lucas-zimerman Nov 2, 2021
1789ec7
Merge branch 'main' into feat/ef-query
lucas-zimerman Nov 2, 2021
fecbf12
update changelog.
lucas-zimerman Nov 2, 2021
b47c7ee
Merge branch 'main' into feat/ef-query
bruno-garcia Nov 8, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions samples/Sentry.Samples.EntityFramework/IQueryableExtensions.cs
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Sentry.Samples.EntityFramework
{
public static class IQueryableExtensions
{
public static List<T> ToList2<T>(IQueryable<T> query)
{
return query.ToList();
}

}
}
76 changes: 69 additions & 7 deletions samples/Sentry.Samples.EntityFramework/Program.cs
@@ -1,34 +1,96 @@
using System.ComponentModel.DataAnnotations;
using System.Configuration;
using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Common;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using Sentry;
using Sentry.EntityFramework;

var _ = SentrySdk.Init(o =>
{
o.Debug = true; // To see SDK logs on the console
o.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537";
o.TracesSampleRate = 1;
// Add the EntityFramework integration to the SentryOptions of your app startup code:
o.AddEntityFramework();
});

var dbConnection = Effort.DbConnectionFactory.CreateTransient();
dbConnection.SetConnectionTimeout(60);
using var db = new SampleDbContext(dbConnection, true);

var user = new SampleUser();
db.Users.Add(user);
// ========================= Insert Requests ==================
//
// ============================================================
var transaction = SentrySdk.StartTransaction("Some Http Post request", "Create");
Console.WriteLine("Some Http Post request");
SentrySdk.ConfigureScope(scope => scope.Transaction = transaction);
ISpan manualSpan;


//Populate the database
for (int j=0; j < 10; j++)
{
manualSpan = SentrySdk.GetSpan().StartChild("manual - create item");
_ = db.Users.Add(new SampleUser() { Id = j, RequiredColumn = "123" });
manualSpan.Finish();
}
manualSpan = SentrySdk.GetSpan().StartChild("manual - create item");
db.Users.Add(new SampleUser() { Id = 52, RequiredColumn = "Bill" });
manualSpan.Finish();


// This will throw a DbEntityValidationException and crash the app
// But Sentry will capture the error.
manualSpan = SentrySdk.GetSpan().StartChild("manual - save changes");
db.SaveChanges();
manualSpan.Finish();
transaction.Finish();

// ========================= Search Request ===================
//
// ============================================================
transaction = SentrySdk.StartTransaction("Some Http Search", "Create");
Console.WriteLine("Some Http Search");
SentrySdk.ConfigureScope(scope => scope.Transaction = transaction);

public class SampleUser
manualSpan = SentrySdk.GetSpan().StartChild("manual - search");
var query = db.Users
.Where(s => s.RequiredColumn == "Bill")
.ToList();
manualSpan.Finish();
transaction.Finish();
Console.WriteLine($"Found {query.Count}");

Console.WriteLine("Text SQL Search");
transaction = SentrySdk.StartTransaction("Text SQL Search", "Create");
SentrySdk.ConfigureScope(scope => scope.Transaction = transaction);
var query2 = db.Users.SqlQuery("SELECT * FROM Sample User where Id > 5").ToList();
transaction.Finish();
Console.WriteLine($"Found {query2.Count}");


public class SampleUser : IDisposable
lucas-zimerman marked this conversation as resolved.
Show resolved Hide resolved
{
private int _id { get; set; }

[Key]
public int Id { get; set; }
public int Id
{
get
{
Task.Delay(150).Wait();
return _id;
}
set
{
_id = value;
}
}
[Required]
public string RequiredColumn { get; set; }

public void Dispose() { }
}

public class SampleDbContext : DbContext
Expand Down
4 changes: 4 additions & 0 deletions src/Sentry.EntityFramework/Sentry.EntityFramework.csproj
Expand Up @@ -9,6 +9,10 @@
<PackageTags>$(PackageTags);EntityFramework;EF;EF6</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="5.0.1" />
lucas-zimerman marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Sentry\Sentry.csproj" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Sentry.EntityFramework/SentryCommandInterceptor.cs
Expand Up @@ -58,7 +58,7 @@ public virtual void Log<T>(DbCommand command, DbCommandInterceptionContext<T> in
{
_queryLogger.Log(command.CommandText, BreadcrumbLevel.Error);
}
else
else if (command.CommandText != null)
{
_queryLogger.Log(command.CommandText);
}
Expand Down
6 changes: 4 additions & 2 deletions src/Sentry.EntityFramework/SentryOptionsExtensions.cs
@@ -1,5 +1,7 @@
using System;
using System.ComponentModel;
using System.Data.Entity.Infrastructure.Interception;
using System.Diagnostics;
using Sentry.EntityFramework;
using Sentry.EntityFramework.ErrorProcessors;
using Sentry.Extensibility;
Expand All @@ -23,15 +25,15 @@ public static SentryOptions AddEntityFramework(this SentryOptions sentryOptions)
try
{
#pragma warning disable 618 // TODO: We can make the method internal on a new major release.
SentryDatabaseLogging.UseBreadcrumbs(diagnosticLogger: sentryOptions.DiagnosticLogger);
_ = SentryDatabaseLogging.UseBreadcrumbs(diagnosticLogger: sentryOptions.DiagnosticLogger);
#pragma warning restore 618
}
catch (Exception e)
{
sentryOptions.DiagnosticLogger?
.LogError("Failed to configure EF breadcrumbs. Make sure to init Sentry before EF.", e);
}

DbInterception.Add(new SentryQueryPerformanceListener());
sentryOptions.AddExceptionProcessor(new DbEntityValidationExceptionProcessor());
// DbConcurrencyExceptionProcessor is untested due to problems with testing it, so it might not be production ready
//sentryOptions.AddExceptionProcessor(new DbConcurrencyExceptionProcessor());
Expand Down
64 changes: 64 additions & 0 deletions src/Sentry.EntityFramework/SentryQueryPerformanceListener.cs
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity.Infrastructure.Interception;

namespace Sentry.EntityFramework
{
internal class SentryQueryPerformanceListener : IDbCommandInterceptor
{
//AsyncLocal
private Dictionary<string, ISpan?> _mySpans = new Dictionary<string, ISpan?>();
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
Console.WriteLine("ReaderExecuting");
CreateOrUpdateSpan("Reader", command.CommandText);
}

public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
Console.WriteLine("ReaderExecuted", command.CommandText);
Finish("Reader");
}

public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
Console.WriteLine("NonQueryExecuting");
CreateOrUpdateSpan("NonQuery", command.CommandText);
}

public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
Console.WriteLine("NonQueryExecuted");
Finish("NonQuery");
}

public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
Console.WriteLine("ScalarExecuting");
CreateOrUpdateSpan("Scalar", command.CommandText);
}

public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
Console.WriteLine("ScalarExecuted");
Finish("Scalar");
}

private void CreateOrUpdateSpan(string key, string? command)
{
var span = SentrySdk.GetSpan();
if (_mySpans.ContainsKey(key) && _mySpans[key] is ISpan oldSpan) {
oldSpan.Finish();
}
_mySpans[key] = span?.StartChild("db", command ?? key);
}

private void Finish(string key)
{
var span = _mySpans[key];
_mySpans[key] = null;
span?.Finish();
}
}
}