LogScope.NET Usage Guide
A comprehensive guide to using LogScope.NET for tracing, profiling, and logging in .NET applications.
Table of Contents
Installation & Setup
Requirements
Package Installation
Choose the package that fits your needs:
# Core library
dotnet add package DevInstance.LogScope
# With dependency injection support
dotnet add package DevInstance.LogScope.NET
# Microsoft.Extensions.Logging integration
dotnet add package DevInstance.LogScope.Extensions.MicrosoftLogger
Quick Start
With Dependency Injection:
using DevInstance.LogScope;
var builder = WebApplication.CreateBuilder(args);
// Add scope logging with console output
builder.Services.AddConsoleScopeLogging(LogLevel.DEBUG);
var app = builder.Build();
Console Application (Non-DI):
using DevInstance.LogScope;
var manager = DefaultScopeLogFactory.CreateConsoleLogger(LogLevel.DEBUG);
var log = manager.CreateLogger(this);
Core Concepts
What is a Scope?
A scope represents a method or code section that you want to trace. LogScope uses the IDisposable pattern where:
- Creating a scope marks the entry point
Dispose() (end of using block) marks the exit point
- Execution time is automatically measured between entry and exit
Why Use Scopes?
LogScope provides more streamlined coding compared to conventional logging APIs:
Traditional Logging:
public void ProcessOrder(Order order)
{
_logger.LogDebug("ProcessOrder started");
var stopwatch = Stopwatch.StartNew();
try
{
// Process order...
_logger.LogDebug("Processing order {OrderId}", order.Id);
}
finally
{
stopwatch.Stop();
_logger.LogDebug("ProcessOrder completed in {Elapsed}ms", stopwatch.ElapsedMilliseconds);
}
}
With LogScope:
public void ProcessOrder(Order order)
{
using var scope = log.DebugScope();
// Process order...
scope.D($"Processing order {order.Id}");
}
Basic Usage
Creating a Logger
Inject IScopeManager and create a logger in your class:
using DevInstance.LogScope;
public class OrderService
{
private readonly IScopeLog log;
public OrderService(IScopeManager logManager)
{
log = logManager.CreateLogger(this);
}
}
Using Scopes
Wrap code sections in scopes using the using statement:
public void ProcessOrder(Order order)
{
using var scope = log.DebugScope();
// Your code here...
scope.D("Order processed successfully");
}
Nested Scopes
Scopes can be nested to show hierarchical execution:
public void ProcessOrder(Order order)
{
using var scope = log.DebugScope();
ValidateOrder(order);
CalculateTotal(order);
SaveOrder(order);
}
private void ValidateOrder(Order order)
{
using var scope = log.DebugScope();
scope.D($"Validating order {order.Id}");
// Validation logic...
}
private void CalculateTotal(Order order)
{
using var scope = log.DebugScope();
scope.D($"Calculating total for {order.Items.Count} items");
// Calculation logic...
}
private void SaveOrder(Order order)
{
using var scope = log.DebugScope();
scope.D("Saving to database");
// Save logic...
}
Log Levels
LogScope supports five log levels, from least to most verbose:
| Level |
Description |
Use Case |
NOLOG |
Logging disabled |
Production with no logging |
ERROR |
Error messages only |
Production environments |
WARNING |
Warnings and errors |
Production with diagnostics |
INFO |
Informational messages |
General monitoring |
DEBUG |
All messages |
Development and debugging |
Setting Log Level
// At startup
services.AddConsoleScopeLogging(LogLevel.DEBUG);
// Or for console apps
var manager = DefaultScopeLogFactory.CreateConsoleLogger(LogLevel.INFO);
Scope Methods
Creating Scopes
| Method |
Level |
Description |
DebugScope() |
DEBUG |
Creates a debug-level scope |
InfoScope() |
INFO |
Creates an info-level scope |
WarningScope() |
WARNING |
Creates a warning-level scope |
ErrorScope() |
ERROR |
Creates an error-level scope |
Logging Within Scopes
| Method |
Level |
Description |
scope.D(message) |
DEBUG |
Log debug message |
scope.I(message) |
INFO |
Log info message |
scope.W(message) |
WARNING |
Log warning message |
scope.E(message) |
ERROR |
Log error message |
scope.E(exception) |
ERROR |
Log exception |
Example
public async Task<Order> GetOrderAsync(string id)
{
using var scope = log.DebugScope();
scope.D($"Fetching order {id}");
var order = await _repository.FindAsync(id);
if (order == null)
{
scope.W($"Order {id} not found");
return null;
}
scope.I($"Found order with {order.Items.Count} items");
return order;
}
Configuration Options
Configure the output format when using Microsoft.Extensions.Logging integration:
services.AddMicrosoftScopeLogging(new DefaultFormattersOptions
{
ShowTimestamp = true, // Show timestamp in output
ShowThreadNumber = true, // Show thread ID
ShowClassName = true, // Show class name
ShowMethodName = true, // Show method name
IndentSize = 2 // Indentation for nested scopes
});
You can create custom formatters by implementing IScopeFormatter:
public class CustomFormatter : IScopeFormatter
{
public string FormatEntry(ScopeContext context)
{
return $"[START] {context.ClassName}.{context.MethodName}";
}
public string FormatExit(ScopeContext context, TimeSpan elapsed)
{
return $"[END] {context.ClassName}.{context.MethodName} ({elapsed.TotalMilliseconds}ms)";
}
public string FormatMessage(ScopeContext context, string message, LogLevel level)
{
return $"[{level}] {message}";
}
}
Integration Examples
ASP.NET Core Web API
// Program.cs
using DevInstance.LogScope;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddConsoleScopeLogging(LogLevel.DEBUG);
var app = builder.Build();
app.MapControllers();
app.Run();
// OrdersController.cs
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IScopeLog log;
private readonly IOrderService _orderService;
public OrdersController(IScopeManager logManager, IOrderService orderService)
{
log = logManager.CreateLogger(this);
_orderService = orderService;
}
[HttpGet("{id}")]
public async Task<ActionResult<Order>> GetOrder(string id)
{
using var scope = log.DebugScope();
scope.D($"API request for order {id}");
var order = await _orderService.GetByIdAsync(id);
if (order == null)
{
scope.W("Order not found");
return NotFound();
}
return Ok(order);
}
}
Microsoft.Extensions.Logging Integration
using DevInstance.LogScope.Extensions.MicrosoftLogger;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMicrosoftScopeLogging(new DefaultFormattersOptions
{
ShowTimestamp = true,
ShowThreadNumber = true
});
// Your existing ILogger instances will work alongside LogScope
builder.Logging.AddConsole();
Console Application
using DevInstance.LogScope;
class Program
{
static void Main(string[] args)
{
var manager = DefaultScopeLogFactory.CreateConsoleLogger(LogLevel.DEBUG);
var processor = new DataProcessor(manager);
processor.ProcessData();
}
}
class DataProcessor
{
private readonly IScopeLog log;
public DataProcessor(IScopeManager manager)
{
log = manager.CreateLogger(this);
}
public void ProcessData()
{
using var scope = log.DebugScope();
scope.I("Starting data processing");
for (int i = 0; i < 10; i++)
{
ProcessItem(i);
}
scope.I("Data processing complete");
}
private void ProcessItem(int index)
{
using var scope = log.DebugScope();
scope.D($"Processing item {index}");
Thread.Sleep(100); // Simulate work
}
}
LogScope produces hierarchical output that clearly shows execution flow:
[12:34:56.789] --> OrdersController.GetOrder
[12:34:56.790] API request for order ORD-123
[12:34:56.791] --> OrderService.GetByIdAsync
[12:34:56.792] Fetching order from database
[12:34:56.850] --> OrderRepository.FindAsync
[12:34:56.920] <-- OrderRepository.FindAsync (70ms)
[12:34:56.921] Order found with 3 items
[12:34:56.922] <-- OrderService.GetByIdAsync (131ms)
[12:34:56.923] <-- OrdersController.GetOrder (134ms)
Output Elements
| Symbol |
Meaning |
--> |
Scope entry (method start) |
<-- |
Scope exit (method end) with elapsed time |
| Indentation |
Nesting level of scopes |
(XXms) |
Execution time for the scope |
Complete Examples
Full Service Implementation
using DevInstance.LogScope;
public interface IOrderService
{
Task<Order> CreateOrderAsync(CreateOrderRequest request);
Task<Order> GetByIdAsync(string id);
Task<ModelList<Order>> GetOrdersAsync(OrderQuery query);
}
[WebService]
public class OrderService : IOrderService
{
private readonly IScopeLog log;
private readonly IOrderRepository _repository;
private readonly IInventoryService _inventory;
public OrderService(
IScopeManager logManager,
IOrderRepository repository,
IInventoryService inventory)
{
log = logManager.CreateLogger(this);
_repository = repository;
_inventory = inventory;
}
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
using var scope = log.DebugScope();
scope.I($"Creating order for customer {request.CustomerId}");
// Validate request
ValidateRequest(request);
// Check inventory
await CheckInventoryAsync(request.Items);
// Create order
var order = new Order
{
Id = Guid.NewGuid().ToString(),
CustomerId = request.CustomerId,
Items = request.Items,
CreatedAt = DateTime.UtcNow
};
// Calculate totals
CalculateTotals(order);
// Save to database
await _repository.CreateAsync(order);
scope.I($"Order {order.Id} created successfully");
return order;
}
public async Task<Order> GetByIdAsync(string id)
{
using var scope = log.DebugScope();
scope.D($"Fetching order {id}");
var order = await _repository.FindAsync(id);
if (order == null)
{
scope.W($"Order {id} not found");
}
return order;
}
public async Task<ModelList<Order>> GetOrdersAsync(OrderQuery query)
{
using var scope = log.DebugScope();
scope.D($"Fetching orders: Page={query.Page}, PageSize={query.PageSize}");
return await _repository.GetPagedAsync(query);
}
private void ValidateRequest(CreateOrderRequest request)
{
using var scope = log.DebugScope();
if (string.IsNullOrEmpty(request.CustomerId))
{
scope.E("Customer ID is required");
throw new BadRequestException("Customer ID is required");
}
if (request.Items == null || request.Items.Count == 0)
{
scope.E("Order must contain at least one item");
throw new BadRequestException("Order must contain at least one item");
}
scope.D("Request validation passed");
}
private async Task CheckInventoryAsync(List<OrderItem> items)
{
using var scope = log.DebugScope();
foreach (var item in items)
{
scope.D($"Checking inventory for product {item.ProductId}");
var available = await _inventory.CheckAvailabilityAsync(
item.ProductId,
item.Quantity);
if (!available)
{
scope.E($"Insufficient inventory for product {item.ProductId}");
throw new BadRequestException(
$"Insufficient inventory for product {item.ProductId}");
}
}
scope.I("All items available in inventory");
}
private void CalculateTotals(Order order)
{
using var scope = log.DebugScope();
order.Subtotal = order.Items.Sum(i => i.Price * i.Quantity);
order.Tax = order.Subtotal * 0.1m;
order.Total = order.Subtotal + order.Tax;
scope.D($"Order total: {order.Total:C}");
}
}
API Reference
Core Interfaces
| Interface |
Description |
IScopeManager |
Factory for creating loggers |
IScopeLog |
Logger instance for a class |
ILogScope |
Active scope with logging methods |
IScopeFormatter |
Custom output formatting |
Factory Methods
| Method |
Description |
DefaultScopeLogFactory.CreateConsoleLogger(level) |
Create console logger |
services.AddConsoleScopeLogging(level) |
Add DI console logging |
services.AddMicrosoftScopeLogging(options) |
Add Microsoft.Extensions.Logging integration |
IScopeManager Methods
| Method |
Description |
CreateLogger(object instance) |
Create logger for class instance |
CreateLogger<T>() |
Create logger for type T |
CreateLogger(string name) |
Create logger with custom name |
IScopeLog Methods
| Method |
Description |
DebugScope() |
Create DEBUG level scope |
InfoScope() |
Create INFO level scope |
WarningScope() |
Create WARNING level scope |
ErrorScope() |
Create ERROR level scope |
ILogScope Methods
| Method |
Description |
D(string message) |
Log DEBUG message |
I(string message) |
Log INFO message |
W(string message) |
Log WARNING message |
E(string message) |
Log ERROR message |
E(Exception ex) |
Log exception |