diff --git a/.craft.yml b/.craft.yml
index 535f6ad22c..d098d717f6 100644
--- a/.craft.yml
+++ b/.craft.yml
@@ -19,6 +19,7 @@ targets:
nuget:Sentry.Hangfire:
nuget:Sentry.Log4Net:
nuget:Sentry.Maui:
+ nuget:Sentry.Maui.CommunityToolkit.Mvvm:
nuget:Sentry.NLog:
nuget:Sentry.OpenTelemetry:
nuget:Sentry.OpenTelemetry.Exporter:
diff --git a/src/Sentry.Extensions.Logging/SentryLogger.cs b/src/Sentry.Extensions.Logging/SentryLogger.cs
index 32394b52b7..65eb1ff4da 100644
--- a/src/Sentry.Extensions.Logging/SentryLogger.cs
+++ b/src/Sentry.Extensions.Logging/SentryLogger.cs
@@ -1,5 +1,6 @@
using Microsoft.Extensions.Logging;
using Sentry.Infrastructure;
+using Sentry.Internal;
namespace Sentry.Extensions.Logging;
@@ -182,22 +183,7 @@ private bool ShouldAddBreadcrumb(
exception));
- private bool IsFromSentry()
- {
- if (string.Equals(CategoryName, "Sentry", StringComparison.Ordinal))
- {
- return true;
- }
-
-#if DEBUG
- if (CategoryName.StartsWith("Sentry.Samples.", StringComparison.Ordinal))
- {
- return false;
- }
-#endif
-
- return CategoryName.StartsWith("Sentry.", StringComparison.Ordinal);
- }
+ private bool IsFromSentry() => SentrySdkNamespaces.IsSentrySdk(CategoryName);
internal static bool IsEfExceptionMessage(EventId eventId)
{
diff --git a/src/Sentry.Serilog/Sentry.Serilog.csproj b/src/Sentry.Serilog/Sentry.Serilog.csproj
index 8a5acc19ba..c49ce23f98 100644
--- a/src/Sentry.Serilog/Sentry.Serilog.csproj
+++ b/src/Sentry.Serilog/Sentry.Serilog.csproj
@@ -19,6 +19,7 @@
+
diff --git a/src/Sentry.Serilog/SentrySink.cs b/src/Sentry.Serilog/SentrySink.cs
index f00556794b..8076d2da6d 100644
--- a/src/Sentry.Serilog/SentrySink.cs
+++ b/src/Sentry.Serilog/SentrySink.cs
@@ -91,7 +91,7 @@ private void InnerEmit(LogEvent logEvent)
{
if (logEvent.TryGetSourceContext(out var context))
{
- if (IsSentryContext(context))
+ if (SentrySdkNamespaces.IsSentrySdk(context))
{
return;
}
@@ -175,10 +175,6 @@ private void InnerEmit(LogEvent logEvent)
}
}
- private static bool IsSentryContext(string context) =>
- context.StartsWith("Sentry.") ||
- string.Equals(context, "Sentry", StringComparison.Ordinal);
-
private string FormatLogEvent(LogEvent logEvent)
{
if (_options.TextFormatter is { } formatter)
diff --git a/src/Sentry/Internal/SentrySdkNamespaces.cs b/src/Sentry/Internal/SentrySdkNamespaces.cs
new file mode 100644
index 0000000000..b898cc8543
--- /dev/null
+++ b/src/Sentry/Internal/SentrySdkNamespaces.cs
@@ -0,0 +1,61 @@
+namespace Sentry.Internal;
+
+///
+/// Identifies logger names that belong to the Sentry SDK itself, so the logging integrations can
+/// skip them rather than feeding the SDK's own output back into Sentry.
+///
+///
+/// The PackageRoots half of this class is generated from .craft.yml - see the
+/// GenerateSentrySdkNamespaces target in Sentry.csproj.
+///
+internal static partial class SentrySdkNamespaces
+{
+ private const string Prefix = "Sentry";
+
+ ///
+ /// The core Sentry package can't be matched by prefix the way the other packages can:
+ /// all of its types live directly under Sentry., and so does application code that
+ /// happens to use that namespace (Sentry.Samples.*, Sentry.MyApp.*). Matching on
+ /// Sentry. would therefore silently discard the user's own logs.
+ ///
+ /// Core has no logging dependency of its own, so the only name it ever logs under is the one
+ /// MelDiagnosticLogger (in Sentry.Extensions.Logging) gets from
+ /// ILogger<ISentryClient>. Matching that exactly is enough.
+ ///
+ private static readonly string[] CoreNames = { Prefix, "Sentry.ISentryClient" };
+
+ ///
+ /// Whether - a Serilog SourceContext, a
+ /// Microsoft.Extensions.Logging category, or an equivalent from another framework -
+ /// belongs to the Sentry SDK.
+ ///
+ internal static bool IsSentrySdk(string? loggerName)
+ {
+ if (loggerName is null || !loggerName.StartsWith(Prefix, StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ foreach (var name in CoreNames)
+ {
+ if (string.Equals(loggerName, name, StringComparison.Ordinal))
+ {
+ return true;
+ }
+ }
+
+ foreach (var root in PackageRoots)
+ {
+ // Either the package's own namespace, or something nested inside it. Comparing the
+ // character after the root keeps a package named e.g. 'Sentry.Maui' from matching an
+ // unrelated 'Sentry.MauiSomething'.
+ if (loggerName.StartsWith(root, StringComparison.Ordinal) &&
+ (loggerName.Length == root.Length || loggerName[root.Length] == '.'))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj
index 3684ad5862..f8b5a34f4c 100644
--- a/src/Sentry/Sentry.csproj
+++ b/src/Sentry/Sentry.csproj
@@ -225,4 +225,72 @@
+
+
+ <_SentryCraftManifest>$(MSBuildThisFileDirectory)..\..\.craft.yml
+
+
+
+
+
+
+
+
+
+
+ <_SentrySdkPackage Include="$([System.Text.RegularExpressions.Regex]::Replace('%(_SentryCraftLine.Identity)', '^\s*nuget:([A-Za-z0-9\.]+):\s*$', '$1'))"
+ Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(_SentryCraftLine.Identity)', '^\s*nuget:[A-Za-z0-9\.]+:\s*$'))" />
+
+ <_SentrySdkPackage Remove="Sentry" />
+
+
+
+
+
+ <_SentrySdkNamespacesLine Include="// <auto-generated/>" />
+ <_SentrySdkNamespacesLine Include="// Generated from .craft.yml by the GenerateSentrySdkNamespaces target in Sentry.csproj." />
+ <_SentrySdkNamespacesLine Include="namespace Sentry.Internal%3B" />
+ <_SentrySdkNamespacesLine Include="internal static partial class SentrySdkNamespaces" />
+ <_SentrySdkNamespacesLine Include="{" />
+ <_SentrySdkNamespacesLine Include=" // Namespace roots owned by SDK packages other than the core 'Sentry' package." />
+ <_SentrySdkNamespacesLine Include=" internal static readonly string[] PackageRoots =" />
+ <_SentrySdkNamespacesLine Include=" {" />
+ <_SentrySdkNamespacesLine Include=" "%(_SentrySdkPackage.Identity)"," />
+ <_SentrySdkNamespacesLine Include=" }%3B" />
+ <_SentrySdkNamespacesLine Include="}" />
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggerTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggerTests.cs
index baf89cee44..02f2bf9531 100644
--- a/test/Sentry.Extensions.Logging.Tests/SentryLoggerTests.cs
+++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggerTests.cs
@@ -218,24 +218,15 @@ public void LogError_DefaultOptions_CapturesEvent()
.CaptureEvent(Arg.Any());
}
- [Fact]
- public void Log_SentryCategory_DoesNotSendEvent()
+ [Theory]
+ [InlineData("Sentry")]
+ [InlineData("Sentry.ISentryClient")]
+ [InlineData("Sentry.Extensions.Logging.SentryLogger")]
+ [InlineData("Sentry.AspNetCore.SentryMiddleware")]
+ public void Log_SentryCategory_DoesNotSendEvent(string categoryName)
{
var expectedException = new Exception("expected message");
- _fixture.CategoryName = "Sentry.Some.Class";
- var sut = _fixture.GetSut();
-
- sut.Log