Skip to content

Latest commit

 

History

222 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Structured Logging logo

ReSharper Structured Logging

Build Quality Gate Status

An extension for ReSharper and Rider IDE that highlights structured logging templates and contains some useful analyzers

At the moment it supports Serilog, NLog, Microsoft.Extensions.Logging and ZLogger, including templates declared with Microsoft.Extensions.Logging.LoggerMessageAttribute, ZLogger.ZLoggerMessageAttribute and Microsoft.Extensions.Logging.LoggerMessage.Define/DefineScope.

Analyzers

Analyzer Quick fix Adopted by R#/Rider
Message template highlighting 2021.2
Anonymous object is not destructured
Complex object is not destructured
Complex object is not destructured in context
Destructuring or stringification operator has no effect on a scalar value
Contextual logger mismatch
Exception passed as a template argument
Exception logged as text
Duplicate properties in a template 2025.2, Serilog-style calls only
Template should be a compile-time constant 2025.1, as a hint
Prefer named properties instead of positional ones
Inconsistent log property naming
Inconsistent log property naming in context
Log event messages should be fragments, not sentences

The last column names the ReSharper/Rider release that adopted the feature. Where a version is listed the extension either no longer provides the feature at all, or still reports it because the built-in inspection does not fully replace it:

  • Duplicate properties are reported by the IDE for calls such as Log.Information("{Id} {Id}", ...), but not for templates declared with LoggerMessageAttribute.
  • A template that is not a compile-time constant is reported by the IDE as a hint tied to CA2254, while the extension reports it as a warning. The quick fix comes from ReSharper.

Code Completion

A property gets its name the moment the hole is typed, so that is where the name is offered. With the caret inside { of a message template, code completion lists names built from the arguments that fill the holes:

_logger.LogInformation("Shipped {", order.Id, customer.Email);
//                              ^ OrderId, Id, CustomerEmail, Email

The names for the argument this very hole will be bound to come first, and each argument offers both its qualified name and its leaf one, so order.Customer.Name suggests CustomerName and then Name. They are already in the case configured under Settings -> Environment -> Structured Logging, the same one the naming analyzer holds the template to. A name another hole of the template already uses is left out, and accepting one closes the hole when the brace is missing, leaving the caret past it. The list appears after a destructuring or stringification operator as well, at {@ and {$, and when completing the name of a hole that is already closed.

Nothing is offered where no name can be derived: for a positional hole such as {0}, which renaming it is the answer to, for a template whose hole values are passed as one array instead of being expanded, for a concatenated template, and once every argument is already bound to a hole.

Templates declared with LoggerMessageAttribute are completed by ReSharper and Rider themselves, from the parameters of the method the attribute decorates, which is the right source there because no argument fills those holes. LoggerMessage.Define binds its holes to generic type arguments and ZLogger 2.x binds them to the interpolations of the template, so neither has an expression to name a hole after.

Custom Logging Wrappers

A project that logs through its own wrapper rather than calling the logger directly is analyzed as well, once the wrapper says which parameter carries the template. Annotate the method with MessageTemplateFormatMethodAttribute, naming the parameter:

[MessageTemplateFormatMethod("messageTemplate")]
public void LogError(Exception exception, string messageTemplate, params object[] propertyValues)

or annotate the parameter itself with JetBrains.Annotations' StructuredMessageTemplateAttribute, which the built-in ReSharper and Rider template highlighting understands too:

public void LogError(Exception exception, [StructuredMessageTemplate] string messageTemplate, params object[] propertyValues)

Either way the arguments that follow the template parameter are taken as the hole values, so a wrapper is held to the same rules as a direct call. Rules that move an argument, such as Exception passed as a template argument, additionally need the wrapper to declare an overload that takes the exception before the template.

ZLogger

ZLogger 1.x ZLog* calls take a plain string format and behave like any other template.

ZLogger 2.x replaced those overloads with an interpolated string handler, so the template and its holes live inside the interpolated string:

logger.ZLogError($"Could not open socket {host:@Host} on {port}");

The holes are the interpolations, named after :@name where one is given and after the source text of the expression otherwise, and the parameters that follow the template are ZLogger's own context together with the caller-info arguments the compiler fills in. That changes what several rules mean, and each rule documents how it applies. Destructuring is Serilog syntax and has no counterpart here: ZLogger serializes with the :json format instead, and the @ of a hole introduces a name.

Dimming Logging Statements

Logging statements can be greyed out, the way unreachable code is rendered, so that they stand out less than the surrounding code. The option is off by default; enable Dim logging statements in Settings -> Environment -> Structured Logging.

Only a statement that consists of nothing but a logging call is dimmed, so a logging call feeding a larger expression keeps its usual colors. Analysis squiggles stay visible on dimmed statements.

Highlighting

Adopted by ReSharper and Rider in 2021.2, so message templates are highlighted out of the box and the extension no longer provides it.

Highlighting

Installation ReSharper

Look for Structured Logging in ReSharper -> Extension manager. JetBrains Plugins Repository

Installation Rider

Look for Structured Logging in Settings -> Plugins -> Browse repositories. JetBrains Plugins Repository

Turning Off Analyzers

Individual analyzers can be disabled as needed either through code comments or by adding a line to a project's .editorconfig file.

Turning Off Via Comments

The analyzer name can be used as-is in a ReSharper comment to disable an analyzer on a per-file or per-line basis. For example:

// ReSharper disable once TemplateIsNotCompileTimeConstantProblem

Turning Off Via .editorconfig

To disable an analyzer for an entire directory, you can add a line to a .editorconfig file (learn more). In this case, the analyzer name needs to be converted to snake_case, prefixed with resharper_ and suffixed with _highlighting. For example:

resharper_template_is_not_compile_time_constant_problem_highlighting = none

Credits

Inspired by SerilogAnalyzer

About

An extension for ReSharper and Rider that highlights structured logging templates and contains some useful analyzers

Topics

Resources

Stars

162 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages