Skip to content

Commit

Permalink
readme & renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
migajek committed Jan 5, 2024
1 parent 9b3e315 commit 7626808
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 6 deletions.
107 changes: 107 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# PicoProfiler

PicoProfiler is a tiny abstraction layer over built-in `Stopwatch`, leveraging `IDisposable + using` pattern

## Getting started

### Microsoft.Extensions.Logging integration

1. Install package: `dotnet add package PicoProfiler.Logging`

2. Sample usage:
```
static async Task Main(string[] args)
{
using var loggerFactory = CreateLoggerFactory();
await RunLoggingSample(loggerFactory.CreateLogger<Program>());
}
private static async Task RunLoggingSample(ILogger logger)
{
using var _ = logger.StartProfiler();
await MyTimeConsumingWork();
}
private static ILoggerFactory CreateLoggerFactory() => LoggerFactory
.Create(lb => lb.AddConsole(cfg => { }));
private static async Task MyTimeConsumingWork() => await Task.Delay(TimeSpan.FromMilliseconds(374));
```

3. In the console output, you'll see :
```
info: PicoSampleApp.Program[0]
RunLoggingSample finished in 381.37 ms
```

#### Notes on usage

Action name can be provided. When not provided, `CallerMemberName` is used - hence the *RunLoggingSample* name is outputted

Default log level and default message template can be adjusted with `LoggerOutputConfiguration.Instance`.

It is also possible to override the log level and message template for each call (see parameters of the methods and overrides).


### Console output

1. Install package: `dotnet add package PicoProfiler`
2. Sample usage code:
```
static async Task Main(string[] args)
{
await RunConsoleSample();
}
private static async Task RunConsoleSample()
{
using var _ = PicoProfilerConsoleOutput.Start();
await MyTimeConsumingWork();
}
private static async Task MyTimeConsumingWork() => await Task.Delay(TimeSpan.FromMilliseconds(374));
```

3. You'll end up with the following console output: `RunConsoleSample finished in 389,55 ms`


#### Notes on usage

Action name can be provided. When not provided, `CallerMemberName` is used - hence the *RunConsoleSample* name is outputted.

Default message template can be adjusted in `ConsoleOutputConfiguration.Instance`

It is also possible to provide a per-call template (in fact, a message factory method)


### Plain Profiler with no output

The core profiler does not output anywhere - it just calls the provided lambda when finishing (when disposed, it calls `Finish`).

The Console output and Logging output is just a bunch of factory methods that provide an action to format & output message.

The following code will create a plain profiler and call the provided action at the end of the method, outputting to Debug Output
```
private static async Task PlainSample()
{
Profiler.Start(elapsed => Debug.WriteLine($"Elapsed time is: {elapsed.TotalMilliseconds:.##}"));
await MyTimeConsumingWork();
}
```

### Profiler API and advanced usages

`Profiler` is a static factory class.
* `Create` - creates a new Pico Profiler and returns `IPicoProfiler`. The profiler is not started until you start it explicitly!
* `Start` - creates *and starts* new Pico Profiler, returns `IPicoProfiler`
* both factory methods can take an optional action to be called when profiler finishes (either manually by calling `Finish` on the instance, or when it's being disposed)

* `IPicoProfiler` is an interface to interact with profiler instance. It also implements `IDisposable` so the `using (Profiler.Start(..))` pattern can be leveraged to measure the performance of code block.
- when profiler instance is being disposed, it calls `Finish` on itself
- `Start()` starts or resumes the current profiler stopwatch
- `Pause()` pauses the current profiler stopwatch
- `Finish()` stops the current profiler stopwatch and runs the action provided (if any)

## Chanelog

* 0.2.0 first public release
5 changes: 5 additions & 0 deletions samples/PicoSampleApp/PicoSampleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\PicoProfiler.Logging\PicoProfiler.Logging.csproj" />
<ProjectReference Include="..\..\src\PicoProfiler\PicoProfiler.csproj" />
</ItemGroup>

Expand Down
26 changes: 25 additions & 1 deletion samples/PicoSampleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
using PicoProfiler.ConsoleOutput;
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using PicoProfiler;
using PicoProfiler.ConsoleOutput;
using PicoProfiler.Logging;

namespace PicoSampleApp;

internal class Program
{
static async Task Main(string[] args)
{
await PlainSample();

await RunConsoleSample();

using var loggerFactory = CreateLoggerFactory();
await RunLoggingSample(loggerFactory.CreateLogger<Program>());
}

private static async Task PlainSample()
{
Profiler.Start(elapsed => Debug.WriteLine($"Elapsed time is: {elapsed.TotalMilliseconds:.##}"));
await MyTimeConsumingWork();
}

private static async Task RunConsoleSample()
Expand All @@ -15,5 +30,14 @@ private static async Task RunConsoleSample()
await MyTimeConsumingWork();
}

private static async Task RunLoggingSample(ILogger logger)
{
using var _ = logger.StartProfiler();
await MyTimeConsumingWork();
}

private static ILoggerFactory CreateLoggerFactory() => LoggerFactory
.Create(lb => lb.AddConsole(cfg => { }));

private static async Task MyTimeConsumingWork() => await Task.Delay(TimeSpan.FromMilliseconds(374));
}
6 changes: 3 additions & 3 deletions src/PicoProfiler.Logging/PicoProfilerLoggingConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

namespace PicoProfiler.Logging;

public class PicoProfilerLoggingConfiguration
public class LoggerOutputConfiguration
{
public static PicoProfilerLoggingConfiguration Instance = new ();
public static LoggerOutputConfiguration Instance = new ();

public LogLevel DefaultLogLevel { get; set; }= LogLevel.Information;

public LoggerMessageFactoryWithActionName DefaultActionMessageFactory =
(actionName, time) => ("{ActionName} finished in {ElapsedMilliseconds:.##} ms",
new object[] { actionName, time.TotalMilliseconds });

private PicoProfilerLoggingConfiguration()
private LoggerOutputConfiguration()
{
}
}
4 changes: 2 additions & 2 deletions src/PicoProfiler.Logging/PicoProfilerLoggingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static IPicoProfiler CreateProfiler(this ILogger logger, LoggerMessageFac
LogLevel? logLevel = null)
{
// evaluate it now as config might change later.
var level = logLevel ?? PicoProfilerLoggingConfiguration.Instance.DefaultLogLevel;
var level = logLevel ?? LoggerOutputConfiguration.Instance.DefaultLogLevel;

return Profiler.Create(elapsed =>
{
Expand Down Expand Up @@ -56,7 +56,7 @@ public static IPicoProfiler CreateProfiler(this ILogger logger,
LogLevel? logLevel = null)
{

var action = messageFactory ?? PicoProfilerLoggingConfiguration.Instance.DefaultActionMessageFactory;
var action = messageFactory ?? LoggerOutputConfiguration.Instance.DefaultActionMessageFactory;
return CreateProfiler(logger,
ts => action(actionName, ts),

Check warning on line 61 in src/PicoProfiler.Logging/PicoProfilerLoggingExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'actionName' in '(string messageTemplate, object[] parameters) LoggerMessageFactoryWithActionName.Invoke(string actionName, TimeSpan elapsedTime)'.

Check warning on line 61 in src/PicoProfiler.Logging/PicoProfilerLoggingExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'actionName' in '(string messageTemplate, object[] parameters) LoggerMessageFactoryWithActionName.Invoke(string actionName, TimeSpan elapsedTime)'.
logLevel);
Expand Down

0 comments on commit 7626808

Please sign in to comment.