This repository has been archived by the owner on Jul 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Updated interfaces and readme. Incremented major version because of b…
…reaking changes.
- Loading branch information
Showing
6 changed files
with
358 additions
and
294 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,53 @@ | ||
# MiniProfilerContrib.Logging | ||
|
||
[![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/MiniProfilerContrib.Logging?color=blue)](https://www.nuget.org/packages/MiniProfilerContrib.Logging) | ||
[![Nuget](https://img.shields.io/nuget/dt/MiniProfilerContrib.Logging?color=blue)](https://www.nuget.org/packages/MiniProfilerContrib.Logging) | ||
![build](https://github.com/gowon/MiniProfilerContrib.Logging/workflows/build/badge.svg) | ||
[![codecov](https://codecov.io/gh/gowon/MiniProfilerContrib.Logging/branch/master/graph/badge.svg)](https://codecov.io/gh/gowon/MiniProfilerContrib.Logging) | ||
|
||
MiniProfiler: Integration for Microsoft.Extensions.Logging | ||
Save MiniProfiler results into a Microsoft.Extensions.Logging logger. | ||
|
||
## Installing via NuGet | ||
|
||
To get started install the *MiniProfilerContrib.Logging* package: | ||
|
||
```powershell | ||
PM> Install-Package MiniProfilerContrib.Logging | ||
``` | ||
|
||
or | ||
|
||
```bash | ||
dotnet add package MiniProfilerContrib.Logging | ||
``` | ||
|
||
## Usage | ||
|
||
The `LoggerStorage` can accept an `ILoggerFactory` or `ILogger<MiniProfiler>`. These can be retrieved through dependency injection or by bootstrapping a logger factory. | ||
|
||
```csharp | ||
var loggerFactory = LoggerFactory.Create(builder => | ||
builder | ||
.AddConsole() | ||
.AddDebug() | ||
.SetMinimumLevel(LogLevel.Trace)); | ||
|
||
var logger = loggerFactory.CreateLogger<MiniProfiler>() | ||
``` | ||
|
||
Then, setup the `MiniProfiler` using a `LoggerStorage`, passing along the logger as well as the logging level the profiler output will be (default `LogLevel.Debug`). | ||
|
||
```csharp | ||
MiniProfiler.Configure(new MiniProfilerOptions | ||
{ | ||
Storage = new LoggerStorage(logger) | ||
}); | ||
``` | ||
|
||
And you can continue to use MiniProfiler as usual. | ||
|
||
> See the [MiniProfiler documentation](https://miniprofiler.com/) and the `samples` folder for working examples. | ||
## License | ||
|
||
MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,63 @@ | ||
namespace ConsoleAppExample | ||
using System; | ||
using System.Data.Common; | ||
using System.Data.SQLite; | ||
using System.Net; | ||
using Dapper; | ||
using Microsoft.Extensions.Logging; | ||
using StackExchange.Contrib.Profiling.Storage; | ||
using StackExchange.Profiling; | ||
using StackExchange.Profiling.Data; | ||
|
||
namespace ConsoleAppExample | ||
{ | ||
using System.Data.Common; | ||
using System.Data.SQLite; | ||
using System.Net; | ||
using Dapper; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Logging.Debug; | ||
using StackExchange.Contrib.Profiling.Storage; | ||
using StackExchange.Profiling; | ||
using StackExchange.Profiling.Data; | ||
|
||
internal class Program | ||
{ | ||
private static void Main(string[] args) | ||
{ | ||
var loggerFactory = LoggerFactory.Create(builder => | ||
builder | ||
.AddConsole() | ||
.AddDebug() | ||
.SetMinimumLevel(LogLevel.Trace)); | ||
|
||
MiniProfiler.Configure(new MiniProfilerOptions | ||
{ | ||
Storage = new LoggingProvider(new DebugLoggerProvider().CreateLogger(nameof(ConsoleAppExample)), | ||
LogLevel.Debug) | ||
Storage = new LoggerStorage(loggerFactory, LogLevel.Debug) | ||
}); | ||
|
||
var mp = MiniProfiler.StartNew("Test"); | ||
var profiler = MiniProfiler.StartNew("Sample Profile"); | ||
|
||
using (mp.Step("Level 1")) | ||
using (var conn = GetConnection()) | ||
using (profiler.Step("Many operations")) | ||
using (var conn = GetConnection(profiler)) | ||
{ | ||
conn.Query<long>("select 1"); | ||
|
||
using (mp.Step("Level 2")) | ||
using (profiler.Step("Nested operation")) | ||
{ | ||
conn.Query<long>("select 1"); | ||
} | ||
|
||
using (var wc = new WebClient()) | ||
using (mp.CustomTiming("http", "GET https://google.com")) | ||
using (profiler.CustomTiming("http", "GET https://google.com")) | ||
{ | ||
wc.DownloadString("https://google.com"); | ||
} | ||
} | ||
|
||
mp.Stop(); | ||
profiler.Stop(); | ||
|
||
Console.ReadKey(); | ||
} | ||
|
||
public static DbConnection GetConnection() | ||
public static DbConnection GetConnection(MiniProfiler profiler) | ||
{ | ||
DbConnection cnn = new SQLiteConnection("Data Source=:memory:"); | ||
var connection = new SQLiteConnection("Data Source=:memory:"); | ||
|
||
// to get profiling times, we have to wrap whatever connection we're using in a ProfiledDbConnection | ||
// when MiniProfiler.Current is null, this connection will not record any database timings | ||
if (MiniProfiler.Current != null) | ||
{ | ||
cnn = new ProfiledDbConnection(cnn, MiniProfiler.Current); | ||
} | ||
|
||
cnn.Open(); | ||
return cnn; | ||
// when profiler is null, this connection will not record any database timings | ||
var profiled = new ProfiledDbConnection(connection, profiler); | ||
profiled.Open(); | ||
return profiled; | ||
} | ||
} | ||
} |
222 changes: 112 additions & 110 deletions
222
...rofilerContrib.Logging/LoggingProvider.cs → ...iProfilerContrib.Logging/LoggerStorage.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,110 +1,112 @@ | ||
namespace StackExchange.Contrib.Profiling.Storage | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
using Newtonsoft.Json; | ||
using StackExchange.Profiling; | ||
using StackExchange.Profiling.Storage; | ||
|
||
public class LoggingProvider : IAsyncStorage | ||
{ | ||
private readonly Func<MiniProfiler, object> _formatter; | ||
private readonly ILogger _logger; | ||
private readonly LogLevel _profilingLevel; | ||
|
||
public LoggingProvider(ILogger logger, LogLevel profilingLevel = LogLevel.Trace, | ||
Func<MiniProfiler, object> formatter = null) | ||
{ | ||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||
_profilingLevel = profilingLevel; | ||
_formatter = formatter ?? (profiler => | ||
{ | ||
var json = JsonConvert.SerializeObject(profiler); | ||
var obj = JsonConvert.DeserializeObject<IDictionary<string, object>>(json, | ||
new RecursiveDictionaryConverter()); | ||
return new Dictionary<string, object> {[nameof(MiniProfiler)] = obj}; | ||
}); | ||
} | ||
|
||
public void Save(MiniProfiler profiler) | ||
{ | ||
if (!_logger.IsEnabled(_profilingLevel)) | ||
{ | ||
return; | ||
} | ||
|
||
using (_logger.BeginScope(_formatter.Invoke(MiniProfiler.Current))) | ||
{ | ||
_logger.Log(_profilingLevel, MiniProfiler.Current.RenderPlainText()); | ||
} | ||
} | ||
|
||
public MiniProfiler Load(Guid id) | ||
{ | ||
return null; | ||
} | ||
|
||
public async Task SaveAsync(MiniProfiler profiler) | ||
{ | ||
Save(profiler); | ||
await Task.CompletedTask; | ||
} | ||
|
||
public IEnumerable<Guid> List( | ||
int maxResults, | ||
DateTime? start = null, | ||
DateTime? finish = null, | ||
ListResultsOrder orderBy = ListResultsOrder.Descending) | ||
{ | ||
return Enumerable.Empty<Guid>(); | ||
} | ||
|
||
public Task<IEnumerable<Guid>> ListAsync( | ||
int maxResults, | ||
DateTime? start = null, | ||
DateTime? finish = null, | ||
ListResultsOrder orderBy = ListResultsOrder.Descending) | ||
{ | ||
return Task.FromResult(Enumerable.Empty<Guid>()); | ||
} | ||
|
||
public Task<MiniProfiler> LoadAsync(Guid id) | ||
{ | ||
return Task.FromResult((MiniProfiler) null); | ||
} | ||
|
||
public void SetUnviewed(string user, Guid id) | ||
{ | ||
/* no-op */ | ||
} | ||
|
||
public Task SetUnviewedAsync(string user, Guid id) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
public void SetViewed(string user, Guid id) | ||
{ | ||
/* no-op */ | ||
} | ||
|
||
public Task SetViewedAsync(string user, Guid id) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
public List<Guid> GetUnviewedIds(string user) | ||
{ | ||
return new List<Guid>(); | ||
} | ||
|
||
public Task<List<Guid>> GetUnviewedIdsAsync(string user) | ||
{ | ||
return Task.FromResult(new List<Guid>()); | ||
} | ||
} | ||
} | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
using Newtonsoft.Json; | ||
using StackExchange.Profiling; | ||
using StackExchange.Profiling.Storage; | ||
|
||
namespace StackExchange.Contrib.Profiling.Storage | ||
{ | ||
public class LoggerStorage : IAsyncStorage | ||
{ | ||
private readonly Func<MiniProfiler, object> _formatter; | ||
private readonly ILogger<MiniProfiler> _logger; | ||
private readonly LogLevel _profilingLevel; | ||
|
||
public LoggerStorage(ILoggerFactory loggerFactory, LogLevel profilingLevel = LogLevel.Debug, | ||
Func<MiniProfiler, object> formatter = null) : this(loggerFactory.CreateLogger<MiniProfiler>(), | ||
profilingLevel, formatter) | ||
{ | ||
} | ||
|
||
public LoggerStorage(ILogger<MiniProfiler> logger, LogLevel profilingLevel = LogLevel.Debug, | ||
Func<MiniProfiler, object> formatter = null) | ||
{ | ||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||
_profilingLevel = profilingLevel; | ||
_formatter = formatter ?? (profiler => | ||
{ | ||
var json = JsonConvert.SerializeObject(profiler); | ||
var obj = JsonConvert.DeserializeObject<IDictionary<string, object>>(json, | ||
new RecursiveDictionaryConverter()); | ||
return new Dictionary<string, object> {[nameof(MiniProfiler)] = obj}; | ||
}); | ||
} | ||
|
||
public void Save(MiniProfiler profiler) | ||
{ | ||
if (!_logger.IsEnabled(_profilingLevel)) return; | ||
|
||
using (_logger.BeginScope(_formatter.Invoke(profiler))) | ||
{ | ||
_logger.Log(_profilingLevel, profiler.RenderPlainText()); | ||
} | ||
} | ||
|
||
public MiniProfiler Load(Guid id) | ||
{ | ||
return null; | ||
} | ||
|
||
public async Task SaveAsync(MiniProfiler profiler) | ||
{ | ||
await Task.Run(() => Save(profiler)); | ||
} | ||
|
||
public IEnumerable<Guid> List( | ||
int maxResults, | ||
DateTime? start = null, | ||
DateTime? finish = null, | ||
ListResultsOrder orderBy = ListResultsOrder.Descending) | ||
{ | ||
return Enumerable.Empty<Guid>(); | ||
} | ||
|
||
public Task<IEnumerable<Guid>> ListAsync( | ||
int maxResults, | ||
DateTime? start = null, | ||
DateTime? finish = null, | ||
ListResultsOrder orderBy = ListResultsOrder.Descending) | ||
{ | ||
return Task.FromResult(Enumerable.Empty<Guid>()); | ||
} | ||
|
||
public Task<MiniProfiler> LoadAsync(Guid id) | ||
{ | ||
return Task.FromResult((MiniProfiler) null); | ||
} | ||
|
||
public void SetUnviewed(string user, Guid id) | ||
{ | ||
/* no-op */ | ||
} | ||
|
||
public Task SetUnviewedAsync(string user, Guid id) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
public void SetViewed(string user, Guid id) | ||
{ | ||
/* no-op */ | ||
} | ||
|
||
public Task SetViewedAsync(string user, Guid id) | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
public List<Guid> GetUnviewedIds(string user) | ||
{ | ||
return new List<Guid>(); | ||
} | ||
|
||
public Task<List<Guid>> GetUnviewedIdsAsync(string user) | ||
{ | ||
return Task.FromResult(new List<Guid>()); | ||
} | ||
} | ||
} |
Oops, something went wrong.