Skip to content
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
16 changes: 15 additions & 1 deletion Sentry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1 - Solution Items", "1 - S
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{77454495-55EE-4B40-A089-71B9E8F82E89}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Basic", "samples\Sentry.Samples.Console.Basic\Sentry.Samples.Console.Basic.csproj", "{65F5A969-B386-48F5-8B7E-6C90D6C720BC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Console.Basic", "samples\Sentry.Samples.Console.Basic\Sentry.Samples.Console.Basic.csproj", "{65F5A969-B386-48F5-8B7E-6C90D6C720BC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.ME.Logging", "samples\Sentry.Samples.ME.Logging\Sentry.Samples.ME.Logging.csproj", "{DE536383-4A18-4057-A7F0-639BDEF6E1BB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Extensions.Logging", "src\Sentry.Extensions.Logging\Sentry.Extensions.Logging.csproj", "{2718BBA4-F080-4924-BA50-B63F756FB097}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -50,6 +54,14 @@ Global
{65F5A969-B386-48F5-8B7E-6C90D6C720BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65F5A969-B386-48F5-8B7E-6C90D6C720BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65F5A969-B386-48F5-8B7E-6C90D6C720BC}.Release|Any CPU.Build.0 = Release|Any CPU
{DE536383-4A18-4057-A7F0-639BDEF6E1BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE536383-4A18-4057-A7F0-639BDEF6E1BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE536383-4A18-4057-A7F0-639BDEF6E1BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE536383-4A18-4057-A7F0-639BDEF6E1BB}.Release|Any CPU.Build.0 = Release|Any CPU
{2718BBA4-F080-4924-BA50-B63F756FB097}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2718BBA4-F080-4924-BA50-B63F756FB097}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2718BBA4-F080-4924-BA50-B63F756FB097}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2718BBA4-F080-4924-BA50-B63F756FB097}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -58,6 +70,8 @@ Global
{F2486CC8-FAB7-4775-976F-C5A4CF97867F} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB}
{1E0F969B-67F9-4FCC-BCBF-596DB6460C7C} = {83263231-1A2A-4733-B759-EEFF14E8C5D5}
{65F5A969-B386-48F5-8B7E-6C90D6C720BC} = {77454495-55EE-4B40-A089-71B9E8F82E89}
{DE536383-4A18-4057-A7F0-639BDEF6E1BB} = {77454495-55EE-4B40-A089-71B9E8F82E89}
{2718BBA4-F080-4924-BA50-B63F756FB097} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6}
Expand Down
5 changes: 3 additions & 2 deletions samples/Sentry.Samples.Console.Basic/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ static class Program
{
static void Main()
{
var sentry = new HttpSentryClient();
// This exception is captured and sent to Sentry
SentryCore.Init();

// assuming it can find the DSN, the following exception is captured and sent to Sentry
throw null;
}
}
Expand Down
49 changes: 49 additions & 0 deletions samples/Sentry.Samples.ME.Logging/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using Microsoft.Extensions.Logging;

namespace Sentry.Samples.ME.Logging
{
class Program
{
static void Main()
{
SentryCore.Init(); // Initialize SDK

try
{
App();
}
catch (Exception e)
{
SentryCore.CaptureException(e);
}
finally
{
SentryCore.CloseAndFlush();
}
}

static void App()
{
using (var loggerFactory = new LoggerFactory()
.AddSentry(o =>
{
// The default values are:
o.MinimumBreadcrumbLevel = LogLevel.Information;
o.MinimumEventLevel = LogLevel.Error;
o.MaxLogBreadcrumbs = 100;
})
.AddConsole())
{
var logger = loggerFactory.CreateLogger<Program>();

logger.LogTrace("By default this is no-op");
logger.LogInformation("By default this should only store a Breadcrumb");
logger.LogError("This generates an event captured by sentry");

// Disposing the logger won't affect Sentry.
// The lifetime is managed externally (call CloseAndFlush)
}
}
}
}
17 changes: 17 additions & 0 deletions samples/Sentry.Samples.ME.Logging/Sentry.Samples.ME.Logging.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>7.2</LangVersion>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../../src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.2" />
</ItemGroup>
</Project>
15 changes: 15 additions & 0 deletions src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../../src/Sentry/Sentry.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.2" />
</ItemGroup>

</Project>
73 changes: 73 additions & 0 deletions src/Sentry.Extensions.Logging/SentryLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using Sentry.Infrastructure;
using Sentry.Protocol;

namespace Sentry.Extensions.Logging
{
internal sealed class SentryLogger : ILogger
{
private readonly string _categoryName;
private readonly ISystemClock _clock;
private readonly SentryLoggingOptions _options;

public SentryLogger(
string categoryName,
SentryLoggingOptions options,
ISystemClock clock = null)
{
Debug.Assert(categoryName != null);
Debug.Assert(options != null);
_categoryName = categoryName;
_options = options;
_clock = clock ?? SystemClock.Clock;
}

public IDisposable BeginScope<TState>(TState state) => SentryCore.PushScope();

public bool IsEnabled(LogLevel logLevel) => SentryCore.IsEnabled && logLevel >= _options.MinimumBreadcrumbLevel;

public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}

// TODO: If it's enabled, at least Breadcrumb has to be stored
if (logLevel < _options.MinimumEventLevel)
{
SentryCore.ConfigureScope(s => s.AddBreadcrumb(FromLogEvent()));
}
else
{
var @event = new SentryEvent(exception)
{
Logger = _categoryName,
Message = formatter?.Invoke(state, exception)
};

if (eventId.Id != 0 || eventId.Name != null)
{
@event.AddTag(nameof(eventId), eventId.ToString());
}

SentryCore.CaptureEvent(@event);
}

Breadcrumb FromLogEvent()
{
return new Breadcrumb(
timestamp: _clock.GetUtcNow(),
// TODO: finish breadcrumbs
message: "todo");
}
}
}
}
30 changes: 30 additions & 0 deletions src/Sentry.Extensions.Logging/SentryLoggerFactoryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.ComponentModel;
using Sentry.Extensions.Logging;

// ReSharper disable once CheckNamespace
// Ensures 'AddSentry' can be found without: 'using Sentry;'
namespace Microsoft.Extensions.Logging
{
///
[EditorBrowsable(EditorBrowsableState.Never)]
public static class SentryLoggerFactoryExtensions
{
/// <summary>
/// Adds the Sentry logging integration.
/// </summary>
/// <param name="factory">The factory.</param>
/// <param name="optionsConfiguration">The options configuration.</param>
/// <returns></returns>
public static ILoggerFactory AddSentry(
this ILoggerFactory factory,
Action<SentryLoggingOptions> optionsConfiguration = null)
{
var options = new SentryLoggingOptions();
optionsConfiguration?.Invoke(options);

factory.AddProvider(new SentryLoggerProvider(options));
return factory;
}
}
}
21 changes: 21 additions & 0 deletions src/Sentry.Extensions.Logging/SentryLoggerProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Sentry.Extensions.Logging;

namespace Microsoft.Extensions.Logging
{
internal class SentryLoggerProvider : ILoggerProvider
{
private readonly SentryLoggingOptions _options;

public SentryLoggerProvider(SentryLoggingOptions options) => _options = options;

public ILogger CreateLogger(string categoryName)
{
return new SentryLogger(categoryName, _options);
}

public void Dispose()
{
// no op, integration doesn't manage the lifeitme of the client
}
}
}
45 changes: 45 additions & 0 deletions src/Sentry.Extensions.Logging/SentryLoggingOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Microsoft.Extensions.Logging;
using Sentry.Protocol;

namespace Sentry.Extensions.Logging
{
/// <summary>
/// Sentry logging integration options
/// </summary>
public class SentryLoggingOptions
{
/// <summary>
/// Gets or sets the maximum log breadcrumbs.
/// </summary>
/// <remarks>
/// Events with level higher than <see cref="MinimumBreadcrumbLevel"/>
/// will be logged as <see cref="Breadcrumb"/>
/// When the number of events reach this configuration value,
/// older breadcrumbs start dropping to make room for new ones.
/// </remarks>
/// <value>
/// The maximum log breadcrumbs.
/// </value>
public int MaxLogBreadcrumbs { get; set; } = 100;

/// <summary>
/// Gets or sets the minimum breadcrumb level.
/// </summary>
/// <remarks>Events with this level or higher will be stored as <see cref="Breadcrumb"/></remarks>
/// <value>
/// The minimum breadcrumb level.
/// </value>
public LogLevel MinimumBreadcrumbLevel { get; set; } = LogLevel.Information;

/// <summary>
/// Gets or sets the minimum event level.
/// </summary>
/// <remarks>
/// Events with this level or higher will be sent to Sentry
/// </remarks>
/// <value>
/// The minimum event level.
/// </value>
public LogLevel MinimumEventLevel { get; set; } = LogLevel.Error;
}
}
36 changes: 36 additions & 0 deletions src/Sentry/DisabledSdk.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Threading.Tasks;
using Sentry.Protocol;

namespace Sentry
{
internal sealed class DisabledSdk : ISdk
{
private static SentryResponse DisabledResponse { get; } = new SentryResponse(false, errorMessage: "SDK Disabled");
private static readonly Task<SentryResponse> DisabledResponseTask = Task.FromResult(DisabledResponse);

public static DisabledSdk Disabled = new DisabledSdk();

private DisabledSdk() { }

public void ConfigureScope(Action<Scope> configureScope) { }

public IDisposable PushScope() => this;

public SentryResponse CaptureEvent(SentryEvent evt) => DisabledResponse;

public SentryResponse CaptureException(Exception exception) => DisabledResponse;

public Task<SentryResponse> CaptureExceptionAsync(Exception exception) => DisabledResponseTask;

public SentryResponse WithClientAndScope(Func<ISentryClient, Scope, SentryResponse> handler) => DisabledResponse;

public Task<SentryResponse> WithClientAndScopeAsync(Func<ISentryClient, Scope, Task<SentryResponse>> handler) => DisabledResponseTask;

public SentryResponse CaptureEvent(Func<SentryEvent> eventFactory) => DisabledResponse;

public Task<SentryResponse> CaptureEventAsync(Func<Task<SentryEvent>> eventFactory) => DisabledResponseTask;

public void Dispose() { }
}
}
15 changes: 4 additions & 11 deletions src/Sentry/HttpSentryClient.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Sentry.Protocol;

namespace Sentry
{
///
public class HttpSentryClient : ISentryClient, IDisposable
{
///
public HttpSentryClient()
public HttpSentryClient(SentryOptions options = null)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}

private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// TODO: A proper implementation
CaptureEventAsync(new SentryEvent(e.ExceptionObject as Exception));
}

///
public Task<SentryResponse> CaptureEventAsync(SentryEvent @event, CancellationToken cancellationToken = default)
public Task<SentryResponse> CaptureEventAsync(SentryEvent @event, Scope scope, CancellationToken cancellationToken = default)
=> Task.FromResult(new SentryResponse(false));

///
public SentryResponse CaptureEvent(SentryEvent @event) => new SentryResponse(false);
public SentryResponse CaptureEvent(SentryEvent @event, Scope scope) => new SentryResponse(false);

///
public void Dispose()
{
AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
}
}
}
Loading