ExecutionMonitor 0.1.30

dotnet add package ExecutionMonitor --version 0.1.30
                    
NuGet\Install-Package ExecutionMonitor -Version 0.1.30
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ExecutionMonitor" Version="0.1.30" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ExecutionMonitor" Version="0.1.30" />
                    
Directory.Packages.props
<PackageReference Include="ExecutionMonitor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ExecutionMonitor --version 0.1.30
                    
#r "nuget: ExecutionMonitor, 0.1.30"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ExecutionMonitor@0.1.30
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ExecutionMonitor&version=0.1.30
                    
Install as a Cake Addin
#tool nuget:?package=ExecutionMonitor&version=0.1.30
                    
Install as a Cake Tool

ExecutionMonitor

ExecutionMonitor adds attribute-based execution monitoring to ASP.NET Core and .NET apps. It captures slow and failed executions, stores them in memory, files, or SQL Server, can generate HTML, JSON, and text reports on demand, and can optionally produce AI-assisted recommendations with a built-in rule-based fallback when no AI provider is configured. It is shipped as a single NuGet package, so consumers install ExecutionMonitor once and do not add the internal ExecutionMonitor.* projects separately.

Install

dotnet add package ExecutionMonitor

If you are using ExecutionMonitor from NuGet, install only this package. The package already includes the internal runtime assemblies it needs.

If you are using a local package source, add the feed first:

dotnet nuget add source C:\path\to\ExecutionMonitor\Deploy\packages -n ExecutionMonitorLocal
dotnet add package ExecutionMonitor --source ExecutionMonitorLocal

Create A Fresh Sample App

Use this flow when you want to test the package outside this repository.

  1. Create a new ASP.NET Core Web API project.
  2. Add the package from the local feed or from your published NuGet source.
  3. Copy the configuration keys into appsettings.json.
  4. Register ExecutionMonitor in Program.cs.
  5. Add one or more monitored controllers.
  6. Start the app and hit the demo endpoints.
  7. Open the report endpoints and confirm records are being captured.
  8. Switch the store to SQL Server when you want durable shared history.

Example commands:

dotnet new webapi -n Demo.ExecutionMonitor.Sample
cd Demo.ExecutionMonitor.Sample
dotnet nuget add source C:\Users\User\Documents\Inovation\packages\ExecutionMonitor\Deploy\packages -n ExecutionMonitorLocal
dotnet add package ExecutionMonitor --source ExecutionMonitorLocal

Example Program.cs:

using ExecutionMonitor;
using ExecutionMonitor.Abstractions;
using ExecutionMonitor.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddExecutionMonitor(options =>
{
    options.ApplicationName = "Demo.ExecutionMonitor.Sample";
    options.EnvironmentName = builder.Environment.EnvironmentName;
    options.DefaultSlowAfterMs = 250;
    options.RecordSuccessfulExecutions = true;
    options.EnableAiAssistance = true;
    options.AiStoreRecommendations = true;
    options.AiTriggerMode = ExecutionMonitorAiTriggerMode.OnDemand;
})
.UseSqlServer(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString("ExecutionMonitor");
    options.SchemaName = "monitor";
    options.FallbackSchemaName = "dbo";
    options.AutoCreateObjects = true;
    options.AutoMigrate = true;
});

var app = builder.Build();

app.UseExecutionMonitor();
app.MapControllers();
app.MapExecutionMonitorEndpoints("/execution-monitor");

app.Run();

When AutoCreateObjects = true, the package creates or reuses the SQL objects it needs at startup. In a fresh database, that means it will create monitor.ExecutionLogs and monitor.ExecutionRecommendations automatically if they do not already exist.

Example appsettings.json:

{
  "ConnectionStrings": {
    "ExecutionMonitor": "Server=localhost;Database=ExecutionMonitor;Trusted_Connection=True;TrustServerCertificate=True"
  },
  "ExecutionMonitor": {
    "Ai": {
      "Endpoint": "https://api.openai.com/v1/chat/completions",
      "ApiKey": "replace-with-your-openai-key",
      "Model": "gpt-4o-mini"
    }
  }
}

Quick Start

using ExecutionMonitor;
using ExecutionMonitor.Abstractions;
using ExecutionMonitor.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddExecutionMonitor(options =>
{
    options.ApplicationName = "MyApp";
    options.EnvironmentName = builder.Environment.EnvironmentName;
    options.DefaultSlowAfterMs = 1000;
    options.RecordSuccessfulExecutions = true;
    options.EnableAiAssistance = true;
    options.AiStoreRecommendations = true;
    options.AiTriggerMode = ExecutionMonitorAiTriggerMode.OnDemand;
});

var app = builder.Build();

app.UseExecutionMonitor();
app.MapControllers();
app.MapExecutionMonitorEndpoints("/execution-monitor");

app.Run();

Use In Non-Web Apps

ExecutionMonitor is not only for APIs. You can use the shared monitoring core in:

  • console apps
  • worker services
  • Windows services
  • background jobs
  • desktop apps that run managed code

For non-web apps, you typically use these pieces:

  • IExecutionMonitorService to wrap code you want to measure
  • IExecutionMonitorStore to persist records
  • IExecutionReportService to build reports in code
  • IExecutionRecommendationProvider to generate AI or rule-based recommendations

Non-web app example

If you use AddExecutionMonitor(...) in a console app or worker service, add the ASP.NET Core shared framework reference so the hosting abstractions are available:

<ItemGroup>
  <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

Important note for plain console apps:

If you use ExecutionMonitor in a plain console app, add the same framework reference above. That matches the package's hosted-service and web-aware dependencies and avoids the missing ASP.NET Core abstractions error.

using ExecutionMonitor;
using ExecutionMonitor.Abstractions;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddExecutionMonitor(options =>
{
    options.ApplicationName = "ExecutionMonitor.Worker";
    options.EnvironmentName = "Production";
    options.DefaultSlowAfterMs = 500;
    options.RecordSuccessfulExecutions = true;
    options.RecordSlowExecutions = true;
    options.RecordFailedExecutions = true;
    options.EnableAiAssistance = true;
    options.AiMode = ExecutionMonitorAiMode.RuleBasedOnly;
    options.AiTriggerMode = ExecutionMonitorAiTriggerMode.OnDemand;
    options.AiStoreRecommendations = true;
    options.AiCacheRecommendations = true;
})
.UseInMemory();

using var provider = services.BuildServiceProvider();

var monitor = provider.GetRequiredService<IExecutionMonitorService>();
var reports = provider.GetRequiredService<IExecutionReportService>();

await monitor.ExecuteAsync(async ct =>
{
    await Task.Delay(250, ct);
});

var report = await reports.GenerateAsync(new ExecutionReportOptions
{
    Format = "Json",
    IncludeRecommendations = true,
    MaxRows = 50
});

Console.WriteLine(report);

Log A Named Method In A Console App

If you want the record to show the method name in a non-web app, wrap a named method and pass a template record into ExecuteAsync(...). When that method is slow or fails, the store records it using the names you supplied.

using ExecutionMonitor;
using ExecutionMonitor.Abstractions;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddExecutionMonitor(options =>
{
    options.ApplicationName = "ExecutionMonitor.Worker";
    options.EnvironmentName = "Production";
    options.DefaultSlowAfterMs = 500;
    options.RecordSuccessfulExecutions = true;
    options.RecordSlowExecutions = true;
    options.RecordFailedExecutions = true;
})
.UseSqlServer(options =>
{
    options.ConnectionString = "Server=localhost;Database=ExecutionMonitor;Trusted_Connection=True;TrustServerCertificate=True";
    options.SchemaName = "monitor";
    options.FallbackSchemaName = "dbo";
    options.AutoCreateObjects = true;
    options.AutoMigrate = true;
});

using var provider = services.BuildServiceProvider();
var monitor = provider.GetRequiredService<IExecutionMonitorService>();

await monitor.ExecuteAsync(DoWorkAsync, new ExecutionRecord
{
    ClassName = nameof(Program),
    MethodName = nameof(DoWorkAsync),
    ApplicationName = "ExecutionMonitor.Worker",
    EnvironmentName = "Production"
});

static async Task DoWorkAsync(CancellationToken cancellationToken)
{
    await Task.Delay(750, cancellationToken);
}

In this example:

  • DoWorkAsync is the method that actually runs
  • the ExecutionRecord template supplies ClassName and MethodName
  • if the call is slow, it is captured because RecordSlowExecutions = true
  • if the call fails, it is captured because RecordFailedExecutions = true

Non-web app with SQL Server

If you want durable history and report generation across restarts, use SQL Server:

services.AddExecutionMonitor(options =>
{
    options.ApplicationName = "ExecutionMonitor.Worker";
    options.EnvironmentName = "Production";
    options.DefaultSlowAfterMs = 500;
    options.EnableAiAssistance = true;
    options.AiMode = ExecutionMonitorAiMode.OpenAICompatible;
    options.AiTriggerMode = ExecutionMonitorAiTriggerMode.OnDemand;
    options.AiStoreRecommendations = true;
})
.UseSqlServer(options =>
{
    options.ConnectionString = configuration.GetConnectionString("ExecutionMonitor");
    options.SchemaName = "monitor";
    options.FallbackSchemaName = "dbo";
    options.AutoCreateObjects = true;
    options.AutoMigrate = true;
});

Non-web config keys

Keep the values in appsettings.json, user secrets, or environment variables:

{
  "ConnectionStrings": {
    "ExecutionMonitor": "Server=localhost;Database=ExecutionMonitor;Trusted_Connection=True;TrustServerCertificate=True"
  },
  "ExecutionMonitor": {
    "Ai": {
      "Endpoint": "https://api.openai.com/v1/chat/completions",
      "ApiKey": "replace-with-your-openai-key",
      "Model": "gpt-4o-mini"
    },
    "License": {
      "AppId": "ExecutionMonitor.Worker",
      "LicensePath": "C:\\ProgramData\\ExecutionMonitor\\ExecutionMonitor.license.json",
      "PublicKeyPath": "C:\\ProgramData\\ExecutionMonitor\\ExecutionMonitor.public.pem"
    }
  }
}

If ExecutionMonitor:Ai:ApiKey is missing, the package falls back to the built-in rule-based provider.

Configuration

You can keep everything in config. The sample app reads these values:

{
  "ConnectionStrings": {
    "ExecutionMonitor": "Server=localhost;Database=ExecutionMonitor;Trusted_Connection=True;TrustServerCertificate=True"
  },
  "ExecutionMonitor": {
    "Ai": {
      "Endpoint": "https://api.openai.com/v1/chat/completions",
      "ApiKey": "replace-with-your-openai-key",
      "Model": "gpt-4o-mini"
    }
  }
}

Connection string key:

ConnectionStrings:ExecutionMonitor

AI keys:

ExecutionMonitor:Ai:Endpoint
ExecutionMonitor:Ai:ApiKey
ExecutionMonitor:Ai:Model

Demo Code

Example monitored controller:

using ExecutionMonitor.Abstractions;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/demo")]
public sealed class DemoController : ControllerBase
{
    [HttpGet("fast")]
    [MonitorExecution(SlowAfterMs = 150, Category = "Demo")]
    public IActionResult Fast() => Ok(new { Message = "Fast response" });

    [HttpGet("slow")]
    [MonitorExecution(SlowAfterMs = 150, Category = "Demo")]
    public async Task<IActionResult> Slow()
    {
        await Task.Delay(250);
        return Ok(new { Message = "Slow response" });
    }

    [HttpGet("fail")]
    [MonitorExecution(SlowAfterMs = 150, Category = "Demo")]
    public IActionResult Fail() => throw new InvalidOperationException("Simulated failure");
}

Attribute Options

[MonitorExecution] controls how a single controller action or endpoint is captured.

Option Type Default Description
SlowAfterMs int 1000 Marks the call as slow when its duration is greater than or equal to this threshold in milliseconds.
Category string? null Groups executions into a named bucket such as Orders, Reporting, or Demo.
CaptureParameters bool false Stores the input parameter values in the execution record.
CaptureReturnValue bool false Stores the returned value when the action completes successfully.
MaskSensitiveData bool true Redacts sensitive values before they are stored or shown in reports.

Related attributes:

  • [IgnoreExecutionMonitor] skips an action or controller entirely.
  • [MaskParameter] masks a specific parameter by name.
  • [ExecutionCategory] applies a category at the class or method level.
  • [TrackSla] marks a maximum allowed duration for SLA tracking.

TrackSla vs SlowAfterMs

Both values help the package decide when a call is "slow", but they serve different layers:

  • SlowAfterMs on [MonitorExecution] is the execution-monitoring threshold for that action.
  • [TrackSla(maxMs)] is an SLA-specific threshold that can be applied at the controller or action level.
  • If both are present, [MonitorExecution(SlowAfterMs = ...)] takes precedence for that monitored action.
  • If [MonitorExecution] does not set SlowAfterMs, the package falls back to [TrackSla].
  • If neither is set, the global default ExecutionMonitorOptions.DefaultSlowAfterMs is used.

Example:

[ApiController]
[Route("api/orders")]
[TrackSla(250)]
public sealed class OrdersController : ControllerBase
{
    [HttpGet("{id}")]
    [MonitorExecution(SlowAfterMs = 100)]
    public IActionResult GetOrder(string id) => Ok(new { Id = id });
}

In that example:

  • the order endpoint uses 100 ms because the method-level SlowAfterMs wins
  • other actions on the controller would use 250 ms from TrackSla unless they override it again

Where To Use Each Attribute

Attribute Use On What It Affects
[MonitorExecution] Controller class, controller action, or endpoint metadata Main per-action monitoring settings such as slow threshold, category, parameter capture, and return-value capture.
[IgnoreExecutionMonitor] Controller class or controller action Completely excludes the decorated controller or action from monitoring.
[ExecutionCategory] Controller class or controller action Adds a category label that can be used for filtering and reports.
[TrackSla] Controller class or controller action Sets a performance SLA threshold used when SlowAfterMs is not explicitly set.
[MaskParameter] Action parameter Marks a specific parameter as sensitive so it is masked in stored records and reports.

Rules of precedence:

  • Method-level attributes override controller-level attributes.
  • MonitorExecution.SlowAfterMs overrides TrackSla.MaxMs.
  • MonitorExecution.Category overrides ExecutionCategory.
  • MaskParameter applies only to the parameter it decorates.
  • IgnoreExecutionMonitor stops monitoring entirely for the decorated scope.

Minimal APIs:

  • MonitorExecution can also be attached through endpoint metadata.
  • If you are using minimal APIs, add the metadata when mapping the route instead of decorating a controller class or action method.

Storage

  • InMemory for local development and tests
  • File for JSON file snapshots
  • SQL Server for real database persistence

Use SQL Server when you want shared reporting and durable history:

builder.Services.AddExecutionMonitor(options =>
{
    options.EnableAiAssistance = true;
})
.UseSqlServer(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString("ExecutionMonitor");
    options.SchemaName = "monitor";
    options.FallbackSchemaName = "dbo";
    options.AutoCreateObjects = true;
});

App Type Guide

ExecutionMonitor has two layers:

  • a shared monitoring/reporting core that works in any .NET app
  • an ASP.NET Core integration layer that adds attributes, middleware, and HTTP report endpoints

Use in non-web apps

Use the shared core when you want to monitor background work, scheduled jobs, console commands, Windows services, or desktop workflows.

You can:

  • wrap code with IExecutionMonitorService
  • query or render reports with IExecutionReportService
  • store executions with IExecutionMonitorStore
  • generate AI recommendations with IExecutionRecommendationProvider

Example:

using ExecutionMonitor;
using ExecutionMonitor.Abstractions;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddExecutionMonitor(options =>
{
    options.ApplicationName = "WorkerJob";
    options.EnvironmentName = "Production";
    options.RecordSuccessfulExecutions = true;
})
.UseInMemory();

using var provider = services.BuildServiceProvider();

var monitor = provider.GetRequiredService<IExecutionMonitorService>();
var reports = provider.GetRequiredService<IExecutionReportService>();

await monitor.ExecuteAsync(async ct =>
{
    await Task.Delay(250, ct);
});

var html = await reports.GenerateAsync(new ExecutionReportOptions
{
    Format = "Html",
    IncludeRecommendations = true,
    MaxRows = 50
});

Use in web apps

Use the ASP.NET Core layer when you want to monitor controller actions and expose dashboard or report endpoints.

You can:

  • decorate actions with [MonitorExecution]
  • apply [TrackSla] and [ExecutionCategory]
  • exclude endpoints with [IgnoreExecutionMonitor]
  • mask sensitive inputs with [MaskParameter]
  • expose /execution-monitor/... reporting routes

Example:

builder.Services.AddExecutionMonitor(options =>
{
    options.ApplicationName = "MyApi";
    options.EnvironmentName = builder.Environment.EnvironmentName;
}).UseSqlServer(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString("ExecutionMonitor");
    options.AutoCreateObjects = true;
});

app.UseExecutionMonitor();
app.MapControllers();
app.MapExecutionMonitorEndpoints("/execution-monitor");

Works in both

These pieces are reusable in both app types:

  • ExecutionMonitorOptions
  • ExecutionReportOptions
  • ExecutionRecordQuery
  • ExecutionRecord
  • ExecutionSummary
  • ExecutionReport
  • IExecutionMonitorService
  • IExecutionReportService
  • IExecutionMonitorStore
  • IExecutionRecommendationProvider

Web only

These are only useful when the app hosts HTTP endpoints:

  • MonitorExecutionAttribute
  • IgnoreExecutionMonitorAttribute
  • ExecutionCategoryAttribute
  • TrackSlaAttribute
  • MaskParameterAttribute
  • ExecutionMonitorMiddleware
  • ExecutionMonitorActionFilter
  • UseExecutionMonitor()
  • MapExecutionMonitorEndpoints()

Where To Set Configuration

You can set ExecutionMonitor behavior in either code or config.

Common options

These live in AddExecutionMonitor(...) and work in both app types:

  • ApplicationName
  • EnvironmentName
  • DefaultSlowAfterMs
  • CaptureParameters
  • CaptureReturnValue
  • MaskSensitiveData
  • RecordSuccessfulExecutions
  • RecordSlowExecutions
  • RecordFailedExecutions
  • SuccessSamplingRate
  • EnableAiAssistance
  • AiMode
  • AiTriggerMode
  • GenerateRecommendationOnSlowCall
  • GenerateRecommendationOnFailure
  • GenerateReportAutomatically
  • FailOnAiConfigurationError
  • AiStoreRecommendations
  • AiCacheRecommendations

Non-web app example

Use this in a console app, worker service, background job, or Windows app:

using ExecutionMonitor;
using ExecutionMonitor.Abstractions;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddExecutionMonitor(options =>
{
    options.ApplicationName = "BillingWorker";
    options.EnvironmentName = "Production";
    options.DefaultSlowAfterMs = 500;
    options.EnableAiAssistance = true;
    options.AiMode = ExecutionMonitorAiMode.OpenAICompatible;
    options.AiTriggerMode = ExecutionMonitorAiTriggerMode.OnDemand;
    options.AiStoreRecommendations = true;
})
.UseSqlServer(options =>
{
    options.ConnectionString = "Server=localhost;Database=ExecutionMonitor;Trusted_Connection=True;TrustServerCertificate=True";
    options.SchemaName = "monitor";
    options.AutoCreateObjects = true;
});

If you want to keep secrets out of code, read them from config:

var connectionString = configuration.GetConnectionString("ExecutionMonitor");
var aiSection = configuration.GetSection("ExecutionMonitor:Ai");

Web app example

Use this in an ASP.NET Core API or MVC app:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddExecutionMonitor(options =>
{
    options.ApplicationName = "OrdersApi";
    options.EnvironmentName = builder.Environment.EnvironmentName;
    options.DefaultSlowAfterMs = 1000;
    options.EnableAiAssistance = true;
    options.AiStoreRecommendations = true;
})
.UseSqlServer(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString("ExecutionMonitor");
    options.SchemaName = "monitor";
    options.AutoCreateObjects = true;
})
.UseOpenAI(options =>
{
    builder.Configuration.GetSection("ExecutionMonitor:Ai").Bind(options);
});

var app = builder.Build();

app.UseExecutionMonitor();
app.MapControllers();
app.MapExecutionMonitorEndpoints("/execution-monitor");

Web App Flow

The diagram below shows the full request path for ASP.NET Core apps, including when AI is used and when the package stays rule-based.

flowchart TD
    U[User / Client Request] --> A[ASP.NET Core app]
    A --> B[ExecutionMonitor middleware / action filter]
    B --> C{MonitorExecution enabled?}
    C -- No --> D[Request continues without monitoring]
    C -- Yes --> E[Capture request metadata]
    E --> F[Execute controller action / endpoint]
    F --> G{Completed successfully?}
    G -- Yes --> H{Record successful executions or sampled?}
    G -- No --> I[Mark failure and capture exception]
    H -- No --> J[Skip persistence]
    H -- Yes --> K[Build ExecutionRecord]
    I --> K
    K --> L[Store execution in memory / file / SQL Server]
    L --> M{AI enabled?}
    M -- No --> N[Return response and persist only execution data]
    M -- Yes --> O{AI trigger mode}
    O -- OnDemand --> P[AI is called only from report or generate URLs]
    O -- InlineForSlowAndFailed --> Q[AI runs during the monitored request for slow or failed calls]
    O -- QueuedForSlowAndFailed --> R[AI runs in background after slow or failed calls]
    P --> S{Remote AI configured and reachable?}
    Q --> S
    R --> S
    S -- No --> T[Fallback to rule-based recommendation]
    S -- Yes --> U2[Call OpenAI-compatible or Azure OpenAI endpoint]
    U2 --> V[Parse recommendation JSON / text]
    T --> W[Add recommendation to record]
    V --> W
    W --> X{AiStoreRecommendations enabled?}
    X -- Yes --> Y[Persist recommendation to store]
    X -- No --> Z[Keep recommendation only in memory for this request]
    Y --> AA[Report endpoints can read the saved recommendation later]
    Z --> AA

Web App Request Paths

  • GET /execution-monitor/report/html
  • GET /execution-monitor/report/json
  • GET /execution-monitor/report/text
  • GET /execution-monitor/summary
  • GET /execution-monitor/slow-methods
  • GET /execution-monitor/failures
  • GET /execution-monitor/recommendations
  • POST /execution-monitor/recommendations/generate
  • POST /execution-monitor/recommendations/generate/{executionLogId}

AI is used only when one of these conditions is true:

  • AiTriggerMode = OnDemand and you call a report or generate endpoint with AI enabled.
  • AiTriggerMode = InlineForSlowAndFailed and the monitored call is slow or failed.
  • AiTriggerMode = QueuedForSlowAndFailed and the monitored call is slow or failed, with generation moved off the request path.

If the remote AI provider is missing, misconfigured, or rate-limited, the package falls back to the built-in rule-based provider.

Config keys to set

Use these config paths for the most common values:

ConnectionStrings:ExecutionMonitor
ExecutionMonitor:Ai:Endpoint
ExecutionMonitor:Ai:ApiKey
ExecutionMonitor:Ai:Model
ExecutionMonitor:Ai:DeploymentName
ExecutionMonitor:Ai:ApiVersion
ExecutionMonitor:Ai:Organization

Full Configuration Reference

Monitoring options

Set these in AddExecutionMonitor(...):

Option Default Purpose
ApplicationName empty Names the app in stored records and reports.
EnvironmentName empty Stores the deployment environment such as Development or Production.
DefaultSlowAfterMs 1000 Global slow-call threshold when an attribute does not override it.
CaptureParameters false Captures input arguments for monitored executions.
CaptureReturnValue false Captures return values for successful executions.
MaskSensitiveData true Redacts sensitive values before storage and reporting.
RecordSuccessfulExecutions false Stores successful calls when enabled.
RecordSlowExecutions true Stores slow executions.
RecordFailedExecutions true Stores failed executions.
SuccessSamplingRate 0.0 Keeps successful-call volume lower when you only want sampled telemetry.
EnableAiAssistance false Turns on AI recommendation support.
AiMode RuleBasedOnly Selects rule-based, OpenAI-compatible, or Azure OpenAI behavior.
AiTriggerMode OnDemand Chooses when AI runs: on demand, queued, or inline.
GenerateRecommendationOnSlowCall true Lets slow calls trigger recommendations.
GenerateRecommendationOnFailure true Lets failures trigger recommendations.
GenerateReportAutomatically false Enables automatic report generation behavior when supported by the host integration.
FailOnAiConfigurationError false Controls whether bad AI config should fail fast or fall back.
AiStoreRecommendations false Persists generated recommendations back to the store.
AiCacheRecommendations true Reuses a cached recommendation for the same execution record.
SQL Server storage options

Set these in .UseSqlServer(...):

Option Purpose
ConnectionString SQL Server connection string used by the store.
SchemaName Preferred schema for ExecutionLogs and ExecutionRecommendations.
FallbackSchemaName Existing schema to reuse when the preferred schema is not available.
AutoCreateObjects Creates the tables when they do not exist.
AutoMigrate Allows the store to reuse compatible objects instead of requiring a blank schema.
AI configuration keys

Set these in config for OpenAI-compatible or Azure OpenAI usage:

Config key Purpose
ExecutionMonitor:Ai:Endpoint Chat-completions endpoint.
ExecutionMonitor:Ai:ApiKey Remote AI API key.
ExecutionMonitor:Ai:Model OpenAI-compatible model name.
ExecutionMonitor:Ai:DeploymentName Azure OpenAI deployment name.
ExecutionMonitor:Ai:ApiVersion Azure OpenAI API version.
ExecutionMonitor:Ai:Organization Optional OpenAI organization header.
ExecutionMonitor:Ai:TimeoutSeconds Request timeout for AI calls.
ExecutionMonitor:Ai:MaxOutputTokens Maximum AI response size.

Example appsettings.json:

{
  "ConnectionStrings": {
    "ExecutionMonitor": "Server=localhost;Database=ExecutionMonitor;Trusted_Connection=True;TrustServerCertificate=True"
  },
  "ExecutionMonitor": {
    "Ai": {
      "Endpoint": "https://api.openai.com/v1/chat/completions",
      "ApiKey": "replace-with-your-openai-key",
      "Model": "gpt-4o-mini",
      "TimeoutSeconds": 20,
      "MaxOutputTokens": 800
    }
  }
}

Licensing Workflow

The app should compute the identity values, create a protected local trial state on first run, and present the identity values to the user. The user then sends those values to owner (rajanvikramsingh@gmail.com) when a paid license is generated.

What to collect from the user side

  • MachineId
  • AppId

For the free trial, the app also creates and manages a hidden local state file automatically. The user should not edit that file.

License config keys:

  • ExecutionMonitor:License:AppId
  • ExecutionMonitor:License:LicensePath
  • ExecutionMonitor:License:PublicKeyPath
  • ExecutionMonitor:License:RevalidationEndpoint
  • ExecutionMonitor:License:RevalidationInterval

Suggested Pricing

These are example starting prices per machine per month. Adjust them to match your own licensing strategy.

Edition Price per machine / month Notes
Free $0 Protected 36-hour trial, one machine, unlimited apps on that machine.
Pro $19 Production use for a single machine, with licensed revalidation support.
Enterprise Custom Volume pricing, deployment support, and contract-based terms.

User flow

  1. Install the app.
  2. The app auto-creates a protected free-trial state file on first run.
  3. The app shows MachineId and AppId.
  4. The user copies those values for activation if they want Pro or Enterprise.
  5. Owner (rajanvikramsingh@gmail.com) generates a signed license and sends it back.
  6. The app installs the license file and continues to validate it automatically.
  7. If no license file is present, the app keeps running on the protected free-trial state until the trial expires.
  8. If a revalidation endpoint is configured, the app rechecks the paid license periodically.
  9. If validation fails, the app fails closed instead of continuing in an unlicensed paid state.

Web apps

Expose a small endpoint that returns the identity values:

[ApiController]
[Route("api/license")]
public sealed class LicenseController : ControllerBase
{
    private readonly LicenseIdentity _identity;
    private readonly ILicenseStateStore _licenseStateStore;

    public LicenseController(LicenseIdentity identity, ILicenseStateStore licenseStateStore)
    {
        _identity = identity;
        _licenseStateStore = licenseStateStore;
    }

    [HttpGet("identity")]
    public async Task<IActionResult> GetIdentity(CancellationToken cancellationToken)
    {
        var state = await _licenseStateStore.EnsureFreeTrialAsync(_identity, TimeSpan.FromHours(36), cancellationToken).ConfigureAwait(false);
        return Ok(new
        {
            _identity.AppId,
            _identity.MachineId,
            Trial = new
            {
                state.LicenseId,
                state.Edition,
                state.FirstActivatedUtc,
                state.LastSeenUtc,
                state.ExpiresUtc,
            }
        });
    }
}

The user can then copy the result or call:

curl http://localhost:5055/api/license/identity

Non-web apps

Print the values at startup, or show them in a simple admin screen. The same protected state store can be used in the background:

var identity = LicenseIdentityProvider.Create(
    configuration["ExecutionMonitor:License:AppId"]);

var stateStore = new ProtectedLicenseStateStore();
await stateStore.EnsureFreeTrialAsync(identity, TimeSpan.FromHours(36));

Console.WriteLine($"AppId: {identity.AppId}");
Console.WriteLine($"MachineId: {identity.MachineId}");

For Pro and Enterprise, ask the user to email owner (rajanvikramsingh@gmail.com) the MachineId and AppId, then generate a signed license from those values.

AI behavior setup

These settings live on AddExecutionMonitor(...) and can be used in both app types:

  • EnableAiAssistance
  • AiMode
  • AiTriggerMode
  • GenerateRecommendationOnSlowCall
  • GenerateRecommendationOnFailure
  • GenerateReportAutomatically
  • FailOnAiConfigurationError
  • AiStoreRecommendations
  • AiCacheRecommendations

If ExecutionMonitor:Ai:ApiKey is not provided, the package falls back to the rule-based provider instead of calling a remote AI service.

Non-web app example

Use this in a console app, worker service, background job, or Windows app:

services.AddExecutionMonitor(options =>
{
    options.EnableAiAssistance = true;
    options.AiMode = ExecutionMonitorAiMode.RuleBasedOnly;
    options.AiTriggerMode = ExecutionMonitorAiTriggerMode.OnDemand;
    options.GenerateRecommendationOnSlowCall = true;
    options.GenerateRecommendationOnFailure = true;
    options.AiStoreRecommendations = true;
    options.AiCacheRecommendations = true;
});

If you want to call a remote model from a non-web app, add the provider after the shared options:

services.AddExecutionMonitor(options =>
{
    options.EnableAiAssistance = true;
    options.AiMode = ExecutionMonitorAiMode.OpenAICompatible;
    options.AiTriggerMode = ExecutionMonitorAiTriggerMode.OnDemand;
})
.UseOpenAI(ai =>
{
    ai.Endpoint = configuration["ExecutionMonitor:Ai:Endpoint"];
    ai.ApiKey = configuration["ExecutionMonitor:Ai:ApiKey"];
    ai.Model = configuration["ExecutionMonitor:Ai:Model"];
});
Web app example

Use this in an ASP.NET Core API or MVC app:

builder.Services.AddExecutionMonitor(options =>
{
    options.EnableAiAssistance = true;
    options.AiMode = ExecutionMonitorAiMode.AzureOpenAI;
    options.AiTriggerMode = ExecutionMonitorAiTriggerMode.InlineForSlowAndFailed;
    options.GenerateRecommendationOnSlowCall = true;
    options.GenerateRecommendationOnFailure = true;
    options.AiStoreRecommendations = true;
    options.AiCacheRecommendations = true;
})
.UseAzureOpenAI(ai =>
{
    builder.Configuration.GetSection("ExecutionMonitor:Ai").Bind(ai);
});

What each of these means:

  • RuleBasedOnly: use the built-in fallback rules, no remote AI call
  • OpenAICompatible: use OpenAI-style chat completions
  • AzureOpenAI: use Azure OpenAI chat completions
  • OnDemand: generate recommendations only when reports or generate endpoints are called
  • QueuedForSlowAndFailed: prepare recommendations for slow and failed executions outside the request path
  • InlineForSlowAndFailed: generate recommendations during the monitored call itself
  • AiStoreRecommendations = true: persist the generated recommendation with the execution record
  • AiCacheRecommendations = true: reuse a matching prior recommendation instead of generating again
  • GenerateRecommendationOnSlowCall = true: allow slow calls to trigger recommendation generation
  • GenerateRecommendationOnFailure = true: allow failed calls to trigger recommendation generation

Report Generation

Report generation works in both app types, but the delivery method is different.

In non-web apps

Generate the report directly in code:

var report = await reports.BuildReportAsync(new ExecutionReportOptions
{
    IncludeRecommendations = true,
    MaxRows = 100
});

var json = await reports.GenerateAsync(new ExecutionReportOptions
{
    Format = "Json",
    IncludeRecommendations = true,
    MaxRows = 100
});

Use this when you want to:

  • save reports to disk
  • print summaries to the console
  • send report output by email
  • build operational tooling without hosting a website

In web apps

Expose the built-in report endpoints:

app.MapExecutionMonitorEndpoints("/execution-monitor");

Then call:

  • GET /execution-monitor/dashboard
  • GET /execution-monitor/report/html
  • GET /execution-monitor/report/json
  • GET /execution-monitor/report/text
  • GET /execution-monitor/summary
  • GET /execution-monitor/slow-methods
  • GET /execution-monitor/failures
  • GET /execution-monitor/recommendations
  • POST /execution-monitor/recommendations/generate

Generate Reports

The reporting API is hosted by the application that references this package. There is no separate reporting service to deploy. The consuming app must:

  1. Register ExecutionMonitor in Program.cs.
  2. Add the middleware with app.UseExecutionMonitor().
  3. Map the report endpoints with app.MapExecutionMonitorEndpoints("/execution-monitor").
  4. Deploy with a real store, usually SQL Server, so the report endpoints can read persisted history.
  5. Configure AI credentials if you want recommendations generated automatically or on demand.

Map the report endpoints:

app.MapExecutionMonitorEndpoints("/execution-monitor");

Then open:

  • GET /execution-monitor/dashboard
  • GET /execution-monitor/report/html
  • GET /execution-monitor/report/json
  • GET /execution-monitor/report/text
  • GET /execution-monitor/summary
  • GET /execution-monitor/slow-methods
  • GET /execution-monitor/failures
  • GET /execution-monitor/recommendations
  • POST /execution-monitor/recommendations/generate

Example report request:

curl http://localhost:5055/execution-monitor/report/json

Example AI recommendation generation:

curl -X POST http://localhost:5055/execution-monitor/recommendations/generate -H "Content-Type: application/json" -d "{\"onlyFailed\":true,\"maxRows\":20}"

If the consuming app is deployed behind IIS, Docker, Azure App Service, or a reverse proxy, the reporting URLs stay the same because they are part of the API surface of the consuming app. The only requirement is that the deployed API can reach the same SQL Server database configured under ConnectionStrings:ExecutionMonitor.

AI Behavior

  • ExecutionMonitorAiMode.RuleBasedOnly uses the built-in fallback provider and does not call a remote AI service.
  • ExecutionMonitorAiMode.OpenAICompatible uses OpenAI-style chat completions.
  • ExecutionMonitorAiMode.AzureOpenAI uses Azure OpenAI chat completions.
  • ExecutionMonitorAiTriggerMode.OnDemand generates recommendations only when you call a report or generate endpoint.
  • ExecutionMonitorAiTriggerMode.QueuedForSlowAndFailed prepares recommendations for slow and failed executions outside the request path.
  • ExecutionMonitorAiTriggerMode.InlineForSlowAndFailed generates recommendations during the monitored call itself.
  • AiStoreRecommendations = true saves generated recommendations back to the store.
  • AiCacheRecommendations = true reuses prior recommendations when the same execution shape is seen again.
  • GenerateRecommendationOnSlowCall = true allows slow calls to trigger AI recommendations.
  • GenerateRecommendationOnFailure = true allows failed calls to trigger AI recommendations.

Recommendation Reuse And Database Storage

  • When a record already contains recommendations, report generation does not ask AI to generate another one for that same loaded record.
  • AiCacheRecommendations = true avoids repeating the remote AI call in the same process for the same execution record.
  • AiStoreRecommendations = true saves generated recommendations back to the store.
  • The SQL Server store persists recommendations in the ExecutionRecommendations table.
  • The SQL Server read path reloads stored recommendations with the execution record, so future report requests can reuse them instead of treating the record as empty.
  • If you call POST /execution-monitor/recommendations/generate/{executionLogId}, the generated recommendation is stored and then returned for that execution record.

Notes

  • Slow and failed executions are stored by default.
  • Successful executions are only stored when sampling or explicit recording is enabled.
  • AI recommendations are only captured when AI is enabled, the provider is configured, and the trigger mode allows it.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.30 95 7/6/2026
0.1.29 78 7/5/2026
0.1.26 70 7/5/2026