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
102 changes: 5 additions & 97 deletions Example/ExampleService.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
using System.Collections.Generic;

namespace Example;

/// <summary>
/// Service that demonstrates various Serilog logging features and syntax highlighting capabilities.
/// </summary>
/// <remarks>
/// This service showcases all the different ways to use Serilog logging with properties,
/// destructuring, formatting, and expressions. It serves as a comprehensive example
/// for testing the SerilogSyntax Visual Studio extension.
/// </remarks>
namespace Example;

public class ExampleService(ILogger<ExampleService> logger)
{
private static readonly string[] consoleLoggerScopes = ["Main", "ConsoleLoggerEmulationExample()"];
private static readonly string[] consoleLoggerScopes = [nameof(RunExamplesAsync), nameof(ConsoleLoggerEmulationExample)];

/// <summary>
/// Runs all example logging scenarios to demonstrate the full range of Serilog features.
/// </summary>
/// <returns>A task that completes when all examples have been executed.</returns>
public async Task RunExamplesAsync()
{
SelfLog.Enable(Console.Error);
Expand All @@ -36,14 +22,6 @@ public async Task RunExamplesAsync()
await ConsoleLoggerEmulationExample();
}

/// <summary>
/// Demonstrates all syntax highlighting features in a single comprehensive example.
/// </summary>
/// <remarks>
/// This method showcases standard properties, destructuring, stringification, formatting,
/// alignment, verbatim strings, raw string literals, and Serilog.Expressions syntax.
/// </remarks>
/// <returns>A task that completes when the showcase example has been logged.</returns>
private async Task ShowcaseExample()
{
logger.LogInformation("=== Serilog Syntax Showcase ===");
Expand Down Expand Up @@ -109,14 +87,6 @@ With properties like {UserId} and {@Order}
await Task.Delay(100);
}

/// <summary>
/// Demonstrates basic Serilog logging patterns with simple property substitution.
/// </summary>
/// <remarks>
/// Shows how to log with single and multiple properties at different log levels
/// including Debug, Information, Warning, and Error.
/// </remarks>
/// <returns>A task that completes when the basic examples have been logged.</returns>
private async Task BasicLoggingExamples()
{
logger.LogInformation("=== Basic Logging Examples ===");
Expand All @@ -140,14 +110,6 @@ private async Task BasicLoggingExamples()
await Task.Delay(100); // Simulate some work
}

/// <summary>
/// Demonstrates object destructuring and stringification in Serilog templates.
/// </summary>
/// <remarks>
/// Shows the difference between destructuring with @ (captures object structure)
/// and stringification with $ (captures ToString() representation).
/// </remarks>
/// <returns>A task that completes when the destructuring examples have been logged.</returns>
private async Task DestructuringExamples()
{
logger.LogInformation("=== Destructuring Examples ===");
Expand All @@ -171,7 +133,7 @@ private async Task DestructuringExamples()
{
new { Product = "Laptop", Price = 999.99m, Quantity = 1 },
new { Product = "Mouse", Price = 29.99m, Quantity = 2 }
},
},
Total = 1059.97m
};
logger.LogInformation("Order created {@Order}", order);
Expand All @@ -183,14 +145,6 @@ private async Task DestructuringExamples()
await Task.Delay(100);
}

/// <summary>
/// Demonstrates format specifiers and alignment options in Serilog templates.
/// </summary>
/// <remarks>
/// Shows various formatting options for dates, numbers, currency, percentages,
/// and how to use alignment to create tabular output.
/// </remarks>
/// <returns>A task that completes when the formatting examples have been logged.</returns>
private async Task FormattingExamples()
{
logger.LogInformation("=== Formatting Examples ===");
Expand All @@ -213,7 +167,7 @@ private async Task FormattingExamples()
new { Name = "Laptop", Price = 999.99m, Stock = 15 },
new { Name = "Mouse", Price = 29.99m, Stock = 147 },
new { Name = "Keyboard", Price = 79.50m, Stock = 23 }
};
};

logger.LogInformation("Inventory Report:");
foreach (var item in items)
Expand All @@ -229,14 +183,6 @@ private async Task FormattingExamples()
await Task.Delay(100);
}

/// <summary>
/// Demonstrates Serilog properties within C# verbatim string literals (@"...").
/// </summary>
/// <remarks>
/// Tests various edge cases including multi-line verbatim strings, escaped quotes,
/// positional parameters, and complex property combinations within verbatim strings.
/// </remarks>
/// <returns>A task that completes when the verbatim string examples have been logged.</returns>
private async Task VerbatimStringExamples()
{
logger.LogInformation("=== Additional Verbatim String Tests ===");
Expand Down Expand Up @@ -285,14 +231,6 @@ private async Task VerbatimStringExamples()
await Task.Delay(100);
}

/// <summary>
/// Demonstrates Serilog properties within C# 11 raw string literals ("""...""").
/// </summary>
/// <remarks>
/// Shows how the extension handles properties in single-line and multi-line raw strings,
/// including custom delimiter counts and embedded quotes without escaping.
/// </remarks>
/// <returns>A task that completes when the raw string literal examples have been logged.</returns>
private async Task RawStringLiteralExamples()
{
logger.LogInformation("=== Raw String Literal Tests (C# 11+) ===");
Expand Down Expand Up @@ -362,14 +300,6 @@ This allows literal triple quotes in the string
await Task.Delay(100);
}

/// <summary>
/// Demonstrates Serilog.Expressions syntax including filters, conditionals, and expression templates.
/// </summary>
/// <remarks>
/// Shows filter expressions with ByExcluding/ByIncludingOnly, conditional enrichment,
/// computed properties, conditional writes, and expression template control flow directives.
/// </remarks>
/// <returns>A task that completes when the expression examples have been logged.</returns>
private async Task SerilogExpressionsExamples()
{
logger.LogInformation("=== Serilog.Expressions Syntax Examples ===");
Expand Down Expand Up @@ -426,14 +356,6 @@ private async Task SerilogExpressionsExamples()
await Task.Delay(100);
}

/// <summary>
/// Demonstrates logging exceptions and error scenarios with structured data.
/// </summary>
/// <remarks>
/// Shows how to log exceptions with LogError, including exception properties
/// and legacy positional parameter formats.
/// </remarks>
/// <returns>A task that completes when the error handling examples have been logged.</returns>
private async Task ErrorHandlingExamples()
{
logger.LogInformation("=== Error Handling Examples ===");
Expand All @@ -460,14 +382,6 @@ private async Task ErrorHandlingExamples()
await Task.Delay(100);
}

/// <summary>
/// Demonstrates performance metrics logging with timing and throughput data.
/// </summary>
/// <remarks>
/// Shows how to log structured performance data, use logging scopes for context,
/// and track operation durations with detailed metrics.
/// </remarks>
/// <returns>A task that completes when the performance examples have been logged.</returns>
private async Task PerformanceLoggingExamples()
{
logger.LogInformation("=== Performance Logging Examples ===");
Expand Down Expand Up @@ -587,12 +501,6 @@ private async Task ConsoleLoggerEmulationExample()
await Task.Delay(100);
}

/// <summary>
/// Simulates a file operation that throws an exception for error logging demonstration.
/// </summary>
/// <param name="fileName">The name of the file to simulate processing.</param>
/// <returns>A task that fails with a FileNotFoundException.</returns>
/// <exception cref="FileNotFoundException">Always thrown to demonstrate error logging.</exception>
private async Task SimulateOperationAsync(string fileName)
{
logger.LogDebug("Attempting to process file {FileName}", fileName);
Expand Down
1 change: 1 addition & 0 deletions Example/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
global using Serilog.Templates;
global using Serilog.Templates.Themes;
global using System;
global using System.Collections.Generic;
global using System.Diagnostics;
global using System.IO;
global using System.Threading.Tasks;
57 changes: 40 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,27 @@ A Visual Studio 2022 extension that provides syntax highlighting, brace matching
### 🎨 Syntax Highlighting

#### Message Templates
- **Property names** highlighted in blue: `{UserId}`, `{UserName}`
- **Destructuring operator** `@` highlighted in dark goldenrod: `{@User}`
- **Stringification operator** `$` highlighted in dark goldenrod: `{$Settings}`
- **Format specifiers** highlighted in teal: `{Timestamp:yyyy-MM-dd}`
- **Property names** highlighted in theme-appropriate blue: `{UserId}`, `{UserName}`
- **Destructuring operator** `@` highlighted in warm orange/red: `{@User}`
- **Stringification operator** `$` highlighted in warm orange/red: `{$Settings}`
- **Format specifiers** highlighted in green: `{Timestamp:yyyy-MM-dd}`
- **Alignment** highlighted in red: `{Name,10}`, `{Price,-8}`
- **Positional parameters** highlighted in dark violet: `{0}`, `{1}`
- **Property braces** highlighted in purple for structure
- **Positional parameters** highlighted in purple: `{0}`, `{1}`
- **Property braces** highlighted for structure
- **Multi-line verbatim strings** fully supported with proper highlighting across lines
- **C# 11 raw string literals** supported with `"""` delimiters for complex templates
- **Automatic theme adaptation** - All colors automatically adjust for Light/Dark themes

#### Serilog.Expressions
- **Filter expressions** in `Filter.ByExcluding()` and `Filter.ByIncludingOnly()`
- **Expression templates** with control flow directives
- **Operators** highlighted distinctly: `and`, `or`, `not`, `like`, `in`, `is null`
- **Functions** highlighted: `StartsWith()`, `Contains()`, `Length()`, etc.
- **Literals** properly colored: strings, numbers, booleans, null
- **Directives** highlighted: `{#if}`, `{#each}`, `{#else}`, `{#end}`
- **Built-in properties**: `@t`, `@m`, `@l`, `@x`, `@i`, `@p`
- **Operators** highlighted in red: `and`, `or`, `not`, `like`, `in`, `is null`
- **Functions** highlighted in purple: `StartsWith()`, `Contains()`, `Length()`, etc.
- **Keywords** highlighted in blue: conditional and control flow keywords
- **Literals** highlighted in cyan/teal: strings, numbers, booleans, null
- **Directives** highlighted in magenta: `{#if}`, `{#each}`, `{#else}`, `{#end}`
- **Built-in properties** highlighted in teal: `@t`, `@m`, `@l`, `@x`, `@i`, `@p`
- **Theme-aware colors** - All expression elements adapt to Light/Dark themes

### 🔗 Smart Detection
- Works with any logger variable name (not just `_logger` or `log`)
Expand Down Expand Up @@ -69,8 +72,19 @@ A Visual Studio 2022 extension that provides syntax highlighting, brace matching

## Customization

### Theme-Aware Colors & Accessibility
The extension automatically adapts to your Visual Studio theme with **WCAG AA compliant colors**:

- **Automatic theme detection** - Colors change instantly when switching between Light and Dark themes
- **WCAG AA accessibility** - All colors maintain 4.5:1+ contrast ratios for excellent readability
- **Semantic color grouping** - Related elements use harmonious color families:
- Properties: Blue family (`{UserId}`, `{Name}`)
- Operators: Warm colors (`@`, `$`)
- Format specifiers: Green family (`:yyyy-MM-dd`)
- Expression functions: Purple family (`StartsWith()`, `Length()`)

### Color Customization
The extension's colors can be customized to match your preferences:
You can still customize colors to match your preferences:

1. Go to **Tools** > **Options** > **Environment** > **Fonts and Colors**
2. In the **Display items** list, look for:
Expand All @@ -81,10 +95,17 @@ The extension's colors can be customized to match your preferences:
- Serilog Alignment
- Serilog Positional Index
- Serilog Property Brace
- Serilog Expression Property
- Serilog Expression Function
- Serilog Expression Keyword
- Serilog Expression Literal
- Serilog Expression Operator
- Serilog Expression Directive
- Serilog Expression Built-in
3. Select any item and modify its **Item foreground** color
4. Click **OK** to apply changes

The extension uses colors that work well in both light and dark themes by default, meeting WCAG AA accessibility standards.
**Note**: Custom colors override the automatic theme-aware colors.

## Getting Started

Expand All @@ -109,10 +130,11 @@ Log.Information("User {UserId} logged in with {@Details} at {Timestamp:HH:mm:ss}
```

You should see:
- `UserId` in blue
- `@` in dark goldenrod, `Details` in blue
- `Timestamp` in blue, `:HH:mm:ss` in teal
- Matching braces highlighted in purple when cursor is on them
- `UserId` in blue (adapts to your theme)
- `@` in warm orange/red, `Details` in blue
- `Timestamp` in blue, `:HH:mm:ss` in green
- Matching braces highlighted when cursor is on them
- Colors automatically match your Light/Dark theme preference

## Supported Serilog Syntax

Expand Down Expand Up @@ -225,6 +247,7 @@ Key components:
- `SerilogBraceMatcher` - Provides brace matching
- `SerilogNavigationProvider` - Enables property-to-argument navigation
- `SerilogCallDetector` - Optimized Serilog call detection with pre-check optimization
- `SerilogThemeColors` - Theme-aware color management with WCAG AA compliance
- `TemplateParser` - Parses Serilog message templates
- `LruCache` - Thread-safe LRU cache providing 268x-510x performance improvement

Expand Down
13 changes: 13 additions & 0 deletions SerilogSyntax/Classification/ISerilogClassificationDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace SerilogSyntax.Classification;

/// <summary>
/// Interface for Serilog classification format definitions that support theme-aware color updates.
/// </summary>
public interface ISerilogClassificationDefinition
{
/// <summary>
/// Reinitializes the classification format colors based on the current Visual Studio theme.
/// This method is called automatically when the VS theme changes.
/// </summary>
void Reinitialize();
}
48 changes: 48 additions & 0 deletions SerilogSyntax/Classification/SerilogClassificationFormatBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Microsoft.VisualStudio.Text.Classification;

namespace SerilogSyntax.Classification;

/// <summary>
/// Base class for theme-aware Serilog classification format definitions.
/// Automatically updates colors when Visual Studio theme changes.
/// </summary>
public abstract class SerilogClassificationFormatBase : ClassificationFormatDefinition, ISerilogClassificationDefinition
{
private readonly SerilogThemeColors _themeColors;
private readonly string _classificationTypeName;

/// <summary>
/// Initializes a new instance of the theme-aware classification format.
/// </summary>
/// <param name="themeColors">The theme colors service.</param>
/// <param name="classificationTypeName">The classification type name.</param>
/// <param name="displayName">The display name for the format.</param>
protected SerilogClassificationFormatBase(
SerilogThemeColors themeColors,
string classificationTypeName,
string displayName)
{
_themeColors = themeColors;
_classificationTypeName = classificationTypeName;
DisplayName = displayName;

_themeColors.RegisterClassificationDefinition(this);
Reinitialize();
}

/// <summary>
/// Reinitializes the classification format colors based on the current Visual Studio theme.
/// Called automatically when the VS theme changes.
/// </summary>
public virtual void Reinitialize()
{
var colors = _themeColors.GetColorsForCurrentTheme();
if (colors.TryGetValue(_classificationTypeName, out var textProperties))
{
ForegroundColor = textProperties.Foreground;
BackgroundColor = textProperties.Background;
IsBold = textProperties.IsBold;
IsItalic = textProperties.IsItalic;
}
}
}
Loading
Loading