Skip to content

Repository files navigation

Ling.Interceptors

English | 简体中文

Build NuGet

Ling.Interceptors is a Roslyn analyzer and incremental source generator for compile-time C# method-call interception and monitoring. It requires .NET SDK 9.0.200 or later.

Package

Package Purpose
Ling.Interceptors Public interception and monitoring API, bundled analyzer, generator, and build integration.
Ling.Interceptors.Logging Microsoft.Extensions.Logging sink.
Ling.Interceptors.Console JSON Lines sink for standard error.
Ling.Interceptors.OpenTelemetry ActivitySource and Meter sink.

Installation

dotnet add package Ling.Interceptors

The package automatically adds Ling.Interceptors.Generated to InterceptorsNamespaces; consumer projects do not need to edit that property.

Monitoring

Monitoring runtime contracts, the no-op default sink, and the default value formatter are included in Ling.Interceptors; the main package has no System.Text.Json dependency.

Mark the target method instead of writing a replacement. The generator finds calls in the current compilation and emits the wrapper automatically:

[Monitor(CaptureParameters = true, CaptureReturnValue = true)]
[return: SensitiveData]
public async Task<Order> PlaceOrder(
    int customerId,
    [SensitiveData] string product)
{
    // implementation
}

SensitiveData is independent from Monitor. Sensitive strings are partially masked; other sensitive values are redacted before a sink receives them. Parameters and return values are opt-in, while exceptions and timing are enabled by default.

Configure an output sink explicitly. The default is a no-op:

MonitorRuntime.Sink = new LoggerMonitorSink(loggerFactory);
// or: new ConsoleMonitorSink()
// or: new OpenTelemetryMonitorSink()

The default formatter preserves scalar values, masks sensitive values, and represents other objects as a declared-type summary without calling ToString() or serializing them. Set MonitorRuntime.Formatter to a custom formatter when structured values are required; MonitorValueContext.IsSensitive identifies values that must not be exposed.

Calls in a referenced assembly are not rewritten. Both the declaring project and a project that calls a monitored method need Ling.Interceptors.

Usage

Declare a static replacement method. The second constructor argument must use a qualified nameof(T.Method) expression, and the scope is required.

using Ling.Interceptors;

internal sealed class Service
{
    internal void Send(string value) => Console.WriteLine($"original:{value}");
}

internal static class Replacements
{
    [Intercept("trace", nameof(Service.Send),
        InterceptionScope.Explicit | InterceptionScope.Compilation)]
    internal static void Send(Service service, string value)
    {
        Console.WriteLine($"trace:{value}");
        service.Send(value); // Calls the original implementation.
    }
}

Use an explicit marker for one call site, or let Compilation match ordinary calls throughout the current project:

service./* intercept:trace */Send("one"); // Explicit replacement
service.Send("two");                      // Compilation replacement

Scopes

Scope Meaning
Explicit Allows a call site to select the rule with /* intercept:id */.
Compilation Replaces matching ordinary method calls in the current compilation.
GeneratedCode Extends a Compilation rule to generated files and enables the two-phase build when other generators emit source.
None Disables the rule.

GeneratedCode must be combined with Compilation. Explicit rules have priority over compilation-wide rules. Calls inside a replacement method (including its lambdas and local functions) are excluded, so a replacement can safely call the original implementation.

Limits

  • The replacement method must be internal or public, static, and signature-compatible with the target method.
  • Targets may be in the current project or a referenced assembly, but already-compiled DLL internals cannot be changed.
  • Constructors, properties, operators, delegate invocations, and method-group conversions are not supported.
  • Rules and replacement methods are defined in the current compilation in v1.
  • Monitoring supports ordinary methods, Task, Task<T>, ValueTask, and ValueTask<T>; ref-return methods are not monitored.

Generated code and two-phase builds

For Compilation | GeneratedCode, the package recognizes conventional generated files (<auto-generated>, .g.cs, .generated.cs, .designer.cs) and GeneratedCodeAttribute. During command-line Build, Pack, and Publish, it materializes output from other source generators and runs a final compilation so same-round generated calls can be intercepted. Design-time builds remain a preview and do not initiate nested compilation.

Samples

Each sample is a small executable application. They demonstrate integration choices rather than serving as test projects.

  • Basic interception and monitoring — Starts with explicit and compilation-wide [Intercept] rules, then shows synchronous, Task, and ValueTask [Monitor] methods through ConsoleMonitorSink. Run: dotnet run --project samples/Ling.Interceptors.Sample
  • Package and two-phase build — Consumes the locally packed Ling.Interceptors NuGet package and demonstrates interception of a call emitted by another source generator. Run: dotnet run --project samples/Ling.Interceptors.PackSample
  • ILogger integration — Configures LoggerMonitorSink with a console logging provider and shows structured, masked method parameters. Run: dotnet run --project samples/Ling.Interceptors.LoggingSample
  • OpenTelemetry integration — Configures OpenTelemetryMonitorSink and uses native ActivityListener and MeterListener to display emitted trace and duration data without choosing an SDK exporter. Run: dotnet run --project samples/Ling.Interceptors.OpenTelemetrySample
  • Cross-assembly monitoring — Keeps the [Monitor] API in a referenced target assembly and installs the generator only in the caller, demonstrating the normal consumer topology. Run: dotnet run --project samples/Ling.Interceptors.CrossAssembly.Caller

Development

  • Build: dotnet build Ling.Interceptors.slnx
  • Test .NET 9 and .NET 10: dotnet test Ling.Interceptors.slnx
  • Pack: dotnet pack src/Ling.Interceptors/Ling.Interceptors.csproj -c Release

Contributing

Contributions are welcome. Please include xUnit coverage for behavior changes.

License

MIT

About

Compile-time C# method-call interception with Roslyn interceptors.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages