├── .editorconfig
├── .gitattributes
├── .github
└── dependabot.yml
├── .gitignore
├── .nuke
├── build.schema.json
└── parameters.json
├── Directory.Build.props
├── LICENSE
├── README.md
├── appveyor.yml
├── build.cmd
├── build.gradle
├── build.ps1
├── build.sh
├── build
├── .editorconfig
├── Build.cs
├── Directory.Build.props
├── Directory.Build.targets
├── ReSharper.Structured.Logging.nuspec
├── _build.csproj
└── _build.csproj.DotSettings
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
└── highlighting.png
├── rules
├── AnonymousObjectDestructuringProblem.md
├── ComplexObjectDestructuringProblem.md
├── ComplexObjectInContextDestructuringProblem.md
├── ContextualLoggerProblem.md
├── ExceptionPassedAsTemplateArgumentProblem.md
├── InconsistentContextLogPropertyNaming.md
├── InconsistentLogPropertyNaming.md
├── LogMessageIsSentenceProblem.md
├── PositionalPropertyUsedProblem.md
├── TemplateDuplicatePropertyProblem.md
└── TemplateIsNotCompileTimeConstantProblem.md
├── settings.gradle
├── src
├── .idea
│ └── .idea.ReSharper.Structured.Logging
│ │ └── .idea
│ │ ├── .gitignore
│ │ ├── .name
│ │ ├── encodings.xml
│ │ ├── indexLayout.xml
│ │ └── vcs.xml
├── .run
│ ├── Pack ReSharper.run.xml
│ ├── Pack Rider.run.xml
│ ├── Test ReSharper.run.xml
│ └── Test Rider.run.xml
├── ReSharper.Structured.Logging.sln
├── ReSharper.Structured.Logging
│ ├── Analyzer
│ │ ├── AnonymousTypeDestructureAnalyzer.cs
│ │ ├── CompileTimeConstantTemplateAnalyzer.cs
│ │ ├── ComplexObjectDestructureAnalyzer.cs
│ │ ├── ContextualLoggerConstructorAnalyzer.cs
│ │ ├── ContextualLoggerSerilogFactoryAnalyzer.cs
│ │ ├── CorrectExceptionPassingAnalyzer.cs
│ │ ├── DuplicatePropertiesTemplateAnalyzer.cs
│ │ ├── LogMessageIsSentenceAnalyzer.cs
│ │ ├── PositionalPropertiesUsageAnalyzer.cs
│ │ └── PropertiesNamingAnalyzer.cs
│ ├── Caching
│ │ └── TemplateParameterNameAttributeProvider.cs
│ ├── Extensions
│ │ └── PsiExtensions.cs
│ ├── Highlighting
│ │ ├── AnonymousObjectWithoutDestructuringWarning.cs
│ │ ├── ComplexObjectDestructuringInContextWarning.cs
│ │ ├── ComplexObjectDestructuringWarning.cs
│ │ ├── ComplexObjectDestructuringWarningBase.cs
│ │ ├── ContextualLoggerWarning.cs
│ │ ├── DuplicateTemplatePropertyWarning.cs
│ │ ├── ExceptionPassedAsTemplateArgumentWarning.cs
│ │ ├── InconsistentContextLogPropertyNamingWarning.cs
│ │ ├── InconsistentLogPropertyNamingWarning.cs
│ │ ├── InconsistentLogPropertyNamingWarningBase.cs
│ │ ├── LogMessageIsSentenceWarning.cs
│ │ ├── PositionalPropertyUsedWarning.cs
│ │ ├── TemplateFormatStringNonExistingArgumentWarning.cs
│ │ └── TemplateIsNotCompileTimeConstantWarning.cs
│ ├── Models
│ │ └── MessageTemplateTokenInformation.cs
│ ├── QuickFixes
│ │ ├── AddDestructuringToMessageTemplatePropertyFix.cs
│ │ ├── RemoveTrailingPeriodFix.cs
│ │ ├── RenameContextLogPropertyFix.cs
│ │ ├── RenameLogPropertyFix.cs
│ │ └── TemplateIsNotCompileTimeConstantFix.cs
│ ├── ReSharper.Structured.Logging.Rider.csproj
│ ├── ReSharper.Structured.Logging.csproj
│ ├── Serilog
│ │ ├── Core
│ │ │ └── IMessageTemplateParser.cs
│ │ ├── Events
│ │ │ └── MessageTemplate.cs
│ │ └── Parsing
│ │ │ ├── Alignment.cs
│ │ │ ├── AlignmentDirection.cs
│ │ │ ├── Destructuring.cs
│ │ │ ├── MessageTemplateParser.cs
│ │ │ ├── MessageTemplateToken.cs
│ │ │ ├── PropertyToken.cs
│ │ │ └── TextToken.cs
│ ├── Settings
│ │ ├── PropertyNamingType.cs
│ │ ├── StructuredLoggingGroup.cs
│ │ ├── StructuredLoggingOptionsPage.cs
│ │ ├── StructuredLoggingSettings.cs
│ │ └── StructuredLoggingSettingsAccessor.cs
│ ├── Utils
│ │ └── PropertyNameProvider.cs
│ ├── Wiki
│ │ └── StructuredLoggingWikiDataProvider.cs
│ ├── ZoneMarker.cs
│ └── app.config
└── rider
│ └── main
│ ├── kotlin
│ └── com
│ │ └── jetbrains
│ │ └── rider
│ │ └── settings
│ │ ├── StructuredLoggingBundle.kt
│ │ └── StructuredLoggingPluginOptionsPage.kt
│ └── resources
│ ├── META-INF
│ └── plugin.xml
│ └── messages
│ └── StructuredLoggingBundle.properties
└── test
├── data
├── Analyzers
│ ├── AnonymousTypeDestructure
│ │ ├── SerilogWithComplexPropertyWithoutDestructure.cs
│ │ ├── SerilogWithComplexPropertyWithoutDestructure.cs.gold
│ │ ├── SerilogWithoutDestructure.cs
│ │ └── SerilogWithoutDestructure.cs.gold
│ ├── ComplexTypeDestructure
│ │ ├── SerilogContextExplicitDestructure.cs
│ │ ├── SerilogContextExplicitDestructure.cs.gold
│ │ ├── SerilogContextNumericWithoutDestructure.cs
│ │ ├── SerilogContextNumericWithoutDestructure.cs.gold
│ │ ├── SerilogContextWithoutDestructure.cs
│ │ ├── SerilogContextWithoutDestructure.cs.gold
│ │ ├── SerilogCustomExceptionWithoutDestructure.cs
│ │ ├── SerilogCustomExceptionWithoutDestructure.cs.gold
│ │ ├── SerilogDictionaryWithoutDestructure.cs
│ │ ├── SerilogDictionaryWithoutDestructure.cs.gold
│ │ ├── SerilogEnumerableWithoutDestructure.cs
│ │ ├── SerilogEnumerableWithoutDestructure.cs.gold
│ │ ├── SerilogForceStringWithoutDestructure.cs
│ │ ├── SerilogForceStringWithoutDestructure.cs.gold
│ │ ├── SerilogNullableWithoutDestructure.cs
│ │ ├── SerilogNullableWithoutDestructure.cs.gold
│ │ ├── SerilogNumericWithoutDestructure.cs
│ │ ├── SerilogNumericWithoutDestructure.cs.gold
│ │ ├── SerilogParentWithOverriddenToString.cs
│ │ ├── SerilogParentWithOverriddenToString.cs.gold
│ │ ├── SerilogWithoutDestructure.cs
│ │ └── SerilogWithoutDestructure.cs.gold
│ ├── ContextualLoggerConstructor
│ │ ├── MicrosoftCorrectContextType.cs
│ │ ├── MicrosoftCorrectContextType.cs.gold
│ │ ├── MicrosoftWrongContextType.cs
│ │ ├── MicrosoftWrongContextType.cs.gold
│ │ ├── MicrosoftWrongContextTypeMultipleNamespaces.cs
│ │ ├── MicrosoftWrongContextTypeMultipleNamespaces.cs.gold
│ │ ├── MicrosoftWrongContextTypeMultipleParameters.cs
│ │ └── MicrosoftWrongContextTypeMultipleParameters.cs.gold
│ ├── ContextualLoggerSerilogFactory
│ │ ├── SerilogCorrectContextType.cs
│ │ ├── SerilogCorrectContextType.cs.gold
│ │ ├── SerilogWrongContextType.cs
│ │ └── SerilogWrongContextType.cs.gold
│ ├── CorrectExceptionPassing
│ │ ├── SerilogCorrectExceptionPassing.cs
│ │ ├── SerilogCorrectExceptionPassing.cs.gold
│ │ ├── SerilogIncorrectExceptionPassing.cs
│ │ ├── SerilogIncorrectExceptionPassing.cs.gold
│ │ ├── SerilogIncorrectExceptionPassingDynamicTemplate.cs
│ │ ├── SerilogIncorrectExceptionPassingDynamicTemplate.cs.gold
│ │ ├── SerilogMultipleExceptionPassing.cs
│ │ └── SerilogMultipleExceptionPassing.cs.gold
│ ├── DuplicatePropertiesTemplate
│ │ ├── SerilogDuplicateNamedProperty.cs
│ │ └── SerilogDuplicateNamedProperty.cs.gold
│ ├── LogMessageIsSentence
│ │ ├── SerilogNotSentenceMessage.cs
│ │ ├── SerilogNotSentenceMessage.cs.gold
│ │ ├── SerilogSentenceMessage.cs
│ │ └── SerilogSentenceMessage.cs.gold
│ ├── PositionalPropertiesUsage
│ │ ├── SerilogPositionProperty.cs
│ │ └── SerilogPositionProperty.cs.gold
│ ├── PropertiesNamingAnalyzer
│ │ ├── SerilogContextInterpolatedStringProperty.cs
│ │ ├── SerilogContextInterpolatedStringProperty.cs.gold
│ │ ├── SerilogContextInvalidNamedProperty.cs
│ │ ├── SerilogContextInvalidNamedProperty.cs.gold
│ │ ├── SerilogIgnoredInvalidNamedProperty.cs
│ │ ├── SerilogIgnoredInvalidNamedProperty.cs.gold
│ │ ├── SerilogInvalidElasticNamedProperty.cs
│ │ ├── SerilogInvalidElasticNamedProperty.cs.gold
│ │ ├── SerilogInvalidNamedProperty.cs
│ │ ├── SerilogInvalidNamedProperty.cs.gold
│ │ ├── SerilogInvalidNamedPropertyWithDot.cs
│ │ ├── SerilogInvalidNamedPropertyWithDot.cs.gold
│ │ ├── SerilogInvalidNamedPropertyWithSpace.cs
│ │ ├── SerilogInvalidNamedPropertyWithSpace.cs.gold
│ │ ├── SerilogInvalidSyntax.cs
│ │ ├── SerilogInvalidSyntax.cs.gold
│ │ ├── SerilogValidDestructuredNamedProperty.cs
│ │ ├── SerilogValidDestructuredNamedProperty.cs.gold
│ │ ├── SerilogValidNamedProperty.cs
│ │ └── SerilogValidNamedProperty.cs.gold
│ └── PropertiesNamingAnalyzerDotNet6
│ │ ├── ZLoggerInvalidNamedProperty.cs
│ │ └── ZLoggerInvalidNamedProperty.cs.gold
├── QuickFixes
│ ├── AddDestructuringFix
│ │ ├── SerilogEscapedString.cs
│ │ ├── SerilogEscapedString.cs.gold
│ │ ├── SerilogNewAnonymousObject.cs
│ │ ├── SerilogNewAnonymousObject.cs.gold
│ │ ├── SerilogNewComplexObject.cs
│ │ └── SerilogNewComplexObject.cs.gold
│ ├── RemoveTrailingPeriodFix
│ │ ├── SerilogTrailingPeriod.cs
│ │ └── SerilogTrailingPeriod.cs.gold
│ ├── RenameContextLogPropertyFix
│ │ ├── SerilogContextProperty.cs
│ │ └── SerilogContextProperty.cs.gold
│ └── RenameLogPropertyFix
│ │ ├── SerilogDestructuredProperty.cs
│ │ ├── SerilogDestructuredProperty.cs.gold
│ │ ├── SerilogProperty.cs
│ │ ├── SerilogProperty.cs.gold
│ │ ├── SerilogPropertyConcatenated.cs
│ │ └── SerilogPropertyConcatenated.cs.gold
└── nuget.config
└── src
├── Analyzer
├── AnonymousTypeDestructureAnalyzerTests.cs
├── ComplexObjectDestructureAnalyzerTests.cs
├── ContextualLoggerConstructorAnalyzerTests.cs
├── ContextualLoggerSerilogFactoryAnalyzerTests.cs
├── CorrectExceptionPassingAnalyzerTests.cs
├── DuplicatePropertiesTemplateAnalyzerTests.cs
├── LogMessageIsSentenceAnalyzerTests.cs
├── MessageTemplateTests.cs
├── PositionalPropertiesUsageAnalyzerTests.cs
├── PropertiesElasticNamingAnalyzerTests.cs
├── PropertiesIgnoredRegexNamingAnalyzerTests.cs
├── PropertiesNamingAnalyzerDotNet6Tests.cs
└── PropertiesNamingAnalyzerTests.cs
├── Constants
└── NugetPackages.cs
├── QuickFixes
├── AddDestructuringToMessageTemplatePropertyFixTests.cs
├── QuickFixTestBase.cs
├── RemoveTrailingPeriodFixTests.cs
├── RenameContextLogPropertyFixTests.cs
└── RenameLogPropertyFixTests.cs
├── ReSharper.Structured.Logging.Rider.Tests.csproj
├── ReSharper.Structured.Logging.Tests.csproj
├── TestEnvironment.cs
└── app.config
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 | dotnet_separate_import_directive_groups = true
9 | dotnet_sort_system_directives_first = true
10 | indent_size = 4
11 |
12 | [{*.csproj,*.json,*.yml,*.xml,*.props,*.nuspec,*.config}]
13 | indent_size = 2
14 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "gradle"
4 | directory: "/"
5 | schedule:
6 | interval: "monthly"
7 |
8 | - package-ecosystem: "nuget"
9 | directory: "/build/"
10 | schedule:
11 | interval: "monthly"
12 |
--------------------------------------------------------------------------------
/.nuke/build.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-04/schema#",
3 | "definitions": {
4 | "Host": {
5 | "type": "string",
6 | "enum": [
7 | "AppVeyor",
8 | "AzurePipelines",
9 | "Bamboo",
10 | "Bitbucket",
11 | "Bitrise",
12 | "GitHubActions",
13 | "GitLab",
14 | "Jenkins",
15 | "Rider",
16 | "SpaceAutomation",
17 | "TeamCity",
18 | "Terminal",
19 | "TravisCI",
20 | "VisualStudio",
21 | "VSCode"
22 | ]
23 | },
24 | "ExecutableTarget": {
25 | "type": "string",
26 | "enum": [
27 | "Clean",
28 | "Compile",
29 | "Pack",
30 | "PackRiderPlugin",
31 | "Sonar",
32 | "SonarBegin",
33 | "Test",
34 | "UpdateBuildVersion",
35 | "UploadReSharperArtifact",
36 | "UploadRiderArtifact"
37 | ]
38 | },
39 | "Verbosity": {
40 | "type": "string",
41 | "description": "",
42 | "enum": [
43 | "Verbose",
44 | "Normal",
45 | "Minimal",
46 | "Quiet"
47 | ]
48 | },
49 | "NukeBuild": {
50 | "properties": {
51 | "Continue": {
52 | "type": "boolean",
53 | "description": "Indicates to continue a previously failed build attempt"
54 | },
55 | "Help": {
56 | "type": "boolean",
57 | "description": "Shows the help text for this build assembly"
58 | },
59 | "Host": {
60 | "description": "Host for execution. Default is 'automatic'",
61 | "$ref": "#/definitions/Host"
62 | },
63 | "NoLogo": {
64 | "type": "boolean",
65 | "description": "Disables displaying the NUKE logo"
66 | },
67 | "Partition": {
68 | "type": "string",
69 | "description": "Partition to use on CI"
70 | },
71 | "Plan": {
72 | "type": "boolean",
73 | "description": "Shows the execution plan (HTML)"
74 | },
75 | "Profile": {
76 | "type": "array",
77 | "description": "Defines the profiles to load",
78 | "items": {
79 | "type": "string"
80 | }
81 | },
82 | "Root": {
83 | "type": "string",
84 | "description": "Root directory during build execution"
85 | },
86 | "Skip": {
87 | "type": "array",
88 | "description": "List of targets to be skipped. Empty list skips all dependencies",
89 | "items": {
90 | "$ref": "#/definitions/ExecutableTarget"
91 | }
92 | },
93 | "Target": {
94 | "type": "array",
95 | "description": "List of targets to be invoked. Default is '{default_target}'",
96 | "items": {
97 | "$ref": "#/definitions/ExecutableTarget"
98 | }
99 | },
100 | "Verbosity": {
101 | "description": "Logging verbosity during build execution. Default is 'Normal'",
102 | "$ref": "#/definitions/Verbosity"
103 | }
104 | }
105 | }
106 | },
107 | "allOf": [
108 | {
109 | "properties": {
110 | "Configuration": {
111 | "type": "string"
112 | },
113 | "IsRiderHost": {
114 | "type": "boolean"
115 | },
116 | "Solution": {
117 | "type": "string",
118 | "description": "Path to a solution file that is automatically loaded"
119 | }
120 | }
121 | },
122 | {
123 | "$ref": "#/definitions/NukeBuild"
124 | }
125 | ]
126 | }
127 |
--------------------------------------------------------------------------------
/.nuke/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./build.schema.json",
3 | "Solution": "src/ReSharper.Structured.Logging.sln"
4 | }
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 2025.1.0
6 |
7 |
8 |
9 | $(MSBuildWarningsAsMessages);MSB3277;MSB3270
10 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Oleg Shevchenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ReSharper Structured Logging
2 | [](https://ci.appveyor.com/project/olsh/resharper-structured-logging)
3 | [](https://sonarcloud.io/dashboard?id=resharper-structured-logging)
4 |
5 | An extension for ReSharper and Rider IDE that highlights structured logging templates and contains some useful analyzers
6 |
7 | > [The highlighting is a built-in feature starting from R#/Rider 2021.2](https://github.com/olsh/resharper-structured-logging/issues/35#issuecomment-900883583),
8 | > but the extension still contains some useful analyzers that are not (yet) implemented by JetBrains team
9 |
10 | At the moment it supports Serilog, NLog, Microsoft.Extensions.Logging and ZLogger
11 |
12 | ## Installation ReSharper
13 |
14 | Look for `Structured Logging` in ReSharper -> Extension manager.
15 | [JetBrains Plugins Repository](https://plugins.jetbrains.com/plugin/12083-structured-logging)
16 |
17 | ## Installation Rider
18 |
19 | Look for `Structured Logging` in Settings -> Plugins -> Browse repositories.
20 | [JetBrains Plugins Repository](https://plugins.jetbrains.com/plugin/12832-structured-logging)
21 |
22 | ## Highlighting
23 |
24 | 
25 |
26 | ## Analyzers
27 |
28 | * [Anonymous object is not destructured](rules/AnonymousObjectDestructuringProblem.md)
29 | * [Complex object is not destructured](rules/ComplexObjectDestructuringProblem.md)
30 | * [Complex object is not destructured in context](rules/ComplexObjectInContextDestructuringProblem.md)
31 | * [Contextual logger mismatch](rules/ContextualLoggerProblem.md)
32 | * [Exception passed as a template argument](rules/ExceptionPassedAsTemplateArgumentProblem.md)
33 | * [Duplicate properties in a template](rules/TemplateDuplicatePropertyProblem.md)
34 | * [Template should be a compile-time constant](rules/TemplateIsNotCompileTimeConstantProblem.md)
35 | * [Prefer named properties instead of positional ones](rules/PositionalPropertyUsedProblem.md)
36 | * [Inconsistent log property naming](rules/InconsistentLogPropertyNaming.md)
37 | * [Inconsistent log property naming in context](rules/InconsistentContextLogPropertyNaming.md)
38 | * [Log event messages should be fragments, not sentences](rules/LogMessageIsSentenceProblem.md)
39 |
40 | ## Turning Off Analyzers
41 |
42 | Individual analyzers can be disabled as needed either through code comments or by adding a line to a project's
43 | `.editorconfig` file.
44 |
45 | ### Turning Off Via Comments
46 |
47 | The analyzer name can be used as-is in a ReSharper comment to disable an analyzer on a per-file or per-line basis.
48 | For example:
49 |
50 | ```csharp
51 | // ReSharper disable once TemplateIsNotCompileTimeConstantProblem
52 | ```
53 |
54 | ### Turning Off Via `.editorconfig`
55 |
56 | To disable an analyzer for an entire directory, you can add a line to a `.editorconfig` file
57 | ([learn more](https://editorconfig.org)). In this case, the analyzer name needs to be converted to `snake_case`, prefixed
58 | with `resharper_` and suffixed with `_highlighting`. For example:
59 |
60 | ```editorconfig
61 | resharper_template_is_not_compile_time_constant_problem_highlighting = none
62 | ```
63 |
64 | ## Credits
65 |
66 | Inspired by [SerilogAnalyzer](https://github.com/Suchiman/SerilogAnalyzer)
67 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2022
2 |
3 | skip_branch_with_pr: true
4 | skip_tags: true
5 |
6 | install:
7 | - SET JAVA_HOME=C:\Program Files\Java\jdk17
8 | - SET PATH=%JAVA_HOME%\bin;%PATH%
9 | build_script:
10 | - cmd: >-
11 | build.cmd update-build-version
12 |
13 | build.cmd upload-resharper-artifact --configuration Release
14 |
15 | build.cmd upload-rider-artifact --configuration Release --is-rider-host
16 |
17 | build.cmd sonar --configuration Release
18 | test: false
19 |
20 | cache:
21 | - '%USERPROFILE%\.sonar\cache'
22 | - '.gradle -> build.gradle'
23 |
--------------------------------------------------------------------------------
/build.cmd:
--------------------------------------------------------------------------------
1 | :; set -eo pipefail
2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
3 | :; ${SCRIPT_DIR}/build.sh "$@"
4 | :; exit $?
5 |
6 | @ECHO OFF
7 | powershell -ExecutionPolicy ByPass -NoProfile "%~dp0build.ps1" %*
8 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.jetbrains.kotlin.jvm' version '2.1.21'
3 | id 'org.jetbrains.intellij.platform' version '2.6.0'
4 | }
5 |
6 | buildDir = 'gradle-build'
7 | version = ext.PluginVersion
8 |
9 | compileKotlin {
10 | kotlinOptions {
11 | jvmTarget = "17"
12 | }
13 | }
14 |
15 | sourceSets {
16 | main {
17 | java.srcDir 'src/rider/main/kotlin'
18 | resources.srcDir 'src/rider/main/resources'
19 | }
20 | }
21 |
22 | repositories {
23 | mavenCentral()
24 | intellijPlatform {
25 | defaultRepositories()
26 | jetbrainsRuntime()
27 | }
28 | }
29 |
30 | dependencies {
31 | intellijPlatform {
32 | rider(ProductVersion, false)
33 | jetbrainsRuntime()
34 | }
35 | }
36 |
37 | intellijPlatform {
38 | pluginConfiguration {
39 | name = rootProject.name
40 | }
41 | }
42 |
43 | prepareSandbox {
44 | def dotNetFiles = [
45 | "${DotNetOutputDirectory}/${DotNetProjectName}.dll",
46 | "${DotNetOutputDirectory}/${DotNetProjectName}.pdb",
47 | ]
48 |
49 | dotNetFiles.forEach { f ->
50 | def file = file(f)
51 | from(file) {
52 | into "${rootProject.name}/dotnet"
53 | }
54 | }
55 |
56 | doLast {
57 | dotNetFiles.forEach { f ->
58 | if (!file(f).exists()) {
59 | throw new RuntimeException("File $f does not exist")
60 | }
61 | }
62 | }
63 | }
64 |
65 | wrapper {
66 | gradleVersion = '8.13'
67 | distributionType = Wrapper.DistributionType.ALL
68 | distributionUrl = "https://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-${gradleVersion}-all.zip"
69 | }
70 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | Param(
3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
4 | [string[]]$BuildArguments
5 | )
6 |
7 | Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)"
8 |
9 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 }
10 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
11 |
12 | ###########################################################################
13 | # CONFIGURATION
14 | ###########################################################################
15 |
16 | $BuildProjectFile = "$PSScriptRoot\build\_build.csproj"
17 | $TempDirectory = "$PSScriptRoot\\.nuke\temp"
18 |
19 | $DotNetGlobalFile = "$PSScriptRoot\\global.json"
20 | $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1"
21 | $DotNetChannel = "Current"
22 |
23 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1
24 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
25 | $env:DOTNET_MULTILEVEL_LOOKUP = 0
26 |
27 | ###########################################################################
28 | # EXECUTION
29 | ###########################################################################
30 |
31 | function ExecSafe([scriptblock] $cmd) {
32 | & $cmd
33 | if ($LASTEXITCODE) { exit $LASTEXITCODE }
34 | }
35 |
36 | # If dotnet CLI is installed globally and it matches requested version, use for execution
37 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and `
38 | $(dotnet --version) -and $LASTEXITCODE -eq 0) {
39 | $env:DOTNET_EXE = (Get-Command "dotnet").Path
40 | }
41 | else {
42 | # Download install script
43 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
44 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null
45 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
46 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
47 |
48 | # If global.json exists, load expected version
49 | if (Test-Path $DotNetGlobalFile) {
50 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
51 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
52 | $DotNetVersion = $DotNetGlobal.sdk.version
53 | }
54 | }
55 |
56 | # Install by channel or version
57 | $DotNetDirectory = "$TempDirectory\dotnet-win"
58 | if (!(Test-Path variable:DotNetVersion)) {
59 | ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
60 | } else {
61 | ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
62 | }
63 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
64 | }
65 |
66 | Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)"
67 |
68 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet }
69 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }
70 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | bash --version 2>&1 | head -n 1
4 |
5 | set -eo pipefail
6 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
7 |
8 | ###########################################################################
9 | # CONFIGURATION
10 | ###########################################################################
11 |
12 | BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj"
13 | TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp"
14 |
15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
16 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh"
17 | DOTNET_CHANNEL="Current"
18 |
19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1
20 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
21 | export DOTNET_MULTILEVEL_LOOKUP=0
22 |
23 | ###########################################################################
24 | # EXECUTION
25 | ###########################################################################
26 |
27 | function FirstJsonValue {
28 | perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}"
29 | }
30 |
31 | # If dotnet CLI is installed globally and it matches requested version, use for execution
32 | if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then
33 | export DOTNET_EXE="$(command -v dotnet)"
34 | else
35 | # Download install script
36 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh"
37 | mkdir -p "$TEMP_DIRECTORY"
38 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL"
39 | chmod +x "$DOTNET_INSTALL_FILE"
40 |
41 | # If global.json exists, load expected version
42 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then
43 | DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")")
44 | if [[ "$DOTNET_VERSION" == "" ]]; then
45 | unset DOTNET_VERSION
46 | fi
47 | fi
48 |
49 | # Install by channel or version
50 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix"
51 | if [[ -z ${DOTNET_VERSION+x} ]]; then
52 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path
53 | else
54 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path
55 | fi
56 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
57 | fi
58 |
59 | echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)"
60 |
61 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet
62 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"
63 |
--------------------------------------------------------------------------------
/build/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | dotnet_style_qualification_for_field = false:warning
3 | dotnet_style_qualification_for_property = false:warning
4 | dotnet_style_qualification_for_method = false:warning
5 | dotnet_style_qualification_for_event = false:warning
6 | dotnet_style_require_accessibility_modifiers = never:warning
7 |
8 | csharp_style_expression_bodied_methods = true:silent
9 | csharp_style_expression_bodied_properties = true:warning
10 | csharp_style_expression_bodied_indexers = true:warning
11 | csharp_style_expression_bodied_accessors = true:warning
12 |
--------------------------------------------------------------------------------
/build/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/build/Directory.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/build/ReSharper.Structured.Logging.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $project$
6 | 1.0.0
7 | Structured Logging
8 | Oleg Shevchenko
9 | false
10 | MIT
11 | https://github.com/olsh/resharper-structured-logging
12 | Contains some useful analyzers for structured logging. Supports Serilog, NLog, and Microsoft.Extensions.Logging.
13 | https://github.com/olsh/resharper-structured-logging/releases
14 | resharper serilog nlog templates logging structuredlogging
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/build/_build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 |
7 | CS0649;CS0169
8 | ..
9 | ..
10 | 1
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/build/_build.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | DO_NOT_SHOW
3 | DO_NOT_SHOW
4 | DO_NOT_SHOW
5 | DO_NOT_SHOW
6 | Implicit
7 | Implicit
8 | ExpressionBody
9 | 0
10 | NEXT_LINE
11 | True
12 | False
13 | 120
14 | IF_OWNER_IS_SINGLE_LINE
15 | WRAP_IF_LONG
16 | False
17 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
18 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
19 | <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
20 | <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
21 | True
22 | True
23 | True
24 | True
25 | True
26 | True
27 | True
28 | True
29 | True
30 | True
31 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Any property can be overwritten from command-line via
2 | # -P=
3 |
4 | PluginVersion=_PLACEHOLDER_
5 | ProductVersion=_PLACEHOLDER_
6 | DotNetOutputDirectory=./src/ReSharper.Structured.Logging/bin/ReSharper.Structured.Logging.Rider/Debug
7 | DotNetProjectName=ReSharper.Structured.Logging.Rider
8 |
9 | # We need to disable Gradle daemon after build otherwise the build hangs on build server
10 | # https://github.com/appveyor/ci/issues/1745
11 | org.gradle.daemon=false
12 |
13 | # We need it to avoid bundle Kotlin jars into plugin
14 | kotlin.stdlib.default.dependency=false
15 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/olsh/resharper-structured-logging/db8bd99bbd9089a7b842a1c008afdf319936dfdf/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-8.13-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/images/highlighting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/olsh/resharper-structured-logging/db8bd99bbd9089a7b842a1c008afdf319936dfdf/images/highlighting.png
--------------------------------------------------------------------------------
/rules/AnonymousObjectDestructuringProblem.md:
--------------------------------------------------------------------------------
1 | #### Anonymous objects must be destructured
2 |
3 | Noncompliant Code Examples:
4 | ```csharp
5 | Log.Error("Processed {Position}", new { x = 4, y = 2});
6 | ```
7 |
8 | Compliant Solution:
9 | ```csharp
10 | Log.Error("Processed {@Position}", new { x = 4, y = 2});
11 | ```
12 |
--------------------------------------------------------------------------------
/rules/ComplexObjectDestructuringProblem.md:
--------------------------------------------------------------------------------
1 | #### Complex objects with default `ToString()` implementation probably need to be destructured
2 |
3 | Noncompliant Code Example:
4 | ```csharp
5 | class User
6 | {
7 | public int Age { get; set; }
8 | }
9 |
10 | ...
11 |
12 | Log.Information("The user is {MyUser}", new User());
13 | ```
14 |
15 | Compliant Solution:
16 | ```csharp
17 | class User
18 | {
19 | public int Age { get; set; }
20 | }
21 |
22 | ...
23 |
24 | Log.Information("The user is {@MyUser}", new User());
25 |
26 | // or
27 |
28 | Log.Information("The user is {$MyUser}", new User());
29 | ```
30 |
--------------------------------------------------------------------------------
/rules/ComplexObjectInContextDestructuringProblem.md:
--------------------------------------------------------------------------------
1 | #### Complex objects with default `ToString()` implementation probably need to be destructured
2 |
3 | Noncompliant Code Example:
4 | ```csharp
5 | class User
6 | {
7 | public int Age { get; set; }
8 | }
9 |
10 | ...
11 |
12 | LogContext.PushProperty("User", new User());
13 | ```
14 |
15 | Compliant Solution:
16 | ```csharp
17 | class User
18 | {
19 | public int Age { get; set; }
20 | }
21 |
22 | ...
23 |
24 | LogContext.PushProperty("User", new User(), true);
25 |
26 | // or
27 |
28 | LogContext.PushProperty("User", new User(), false);
29 | ```
30 |
--------------------------------------------------------------------------------
/rules/ContextualLoggerProblem.md:
--------------------------------------------------------------------------------
1 | #### Incorrect type is used for contextual logger
2 |
3 | Noncompliant Code Examples:
4 | ```csharp
5 | class A
6 | {
7 | private static readonly ILogger Logger = Logger.ForContext();
8 | }
9 |
10 | class B {}
11 | ```
12 |
13 | ```csharp
14 | class A
15 | {
16 | ILogger _log;
17 |
18 | public A(ILogger log)
19 | {
20 | _log = log;
21 | }
22 | }
23 |
24 | class B { }
25 | ```
26 |
27 | Compliant Solution:
28 | ```csharp
29 | class A
30 | {
31 | private static readonly ILogger Logger = Logger.ForContext();
32 | }
33 |
34 | class B {}
35 | ```
36 |
37 | ```csharp
38 | class A
39 | {
40 | ILogger _log;
41 |
42 | public A(ILogger log)
43 | {
44 | _log = log;
45 | }
46 | }
47 |
48 | class B {}
49 | ```
50 |
--------------------------------------------------------------------------------
/rules/ExceptionPassedAsTemplateArgumentProblem.md:
--------------------------------------------------------------------------------
1 | #### Exception passed as a template argument
2 |
3 | Noncompliant Code Example:
4 | ```csharp
5 | catch (Exception exception)
6 | {
7 | Log.Error(ex, "Disk quota {Quota} MB exceeded {Exception}", quota, exception);
8 | }
9 | ```
10 |
11 | Compliant Solution:
12 | ```csharp
13 | catch (Exception exception)
14 | {
15 | Log.Error(exception, "Disk quota {Quota} MB exceeded", quota);
16 | }
17 | ```
18 |
--------------------------------------------------------------------------------
/rules/InconsistentContextLogPropertyNaming.md:
--------------------------------------------------------------------------------
1 | #### Inconsistent log property naming in context (can be configured in the extension settings)
2 |
3 | Noncompliant Code Examples:
4 | ```csharp
5 | LogContext.PushProperty("property_name", 1);
6 | ```
7 |
8 | Compliant Solution:
9 | ```csharp
10 | LogContext.PushProperty("PropertyName", 1);
11 | ```
12 |
--------------------------------------------------------------------------------
/rules/InconsistentLogPropertyNaming.md:
--------------------------------------------------------------------------------
1 | #### Inconsistent log property naming (can be configured in the extension settings)
2 |
3 | Noncompliant Code Examples:
4 | ```csharp
5 | Log.Error("Processed {property_name}", 1);
6 | ```
7 |
8 | Compliant Solution:
9 | ```csharp
10 | Log.Error("Processed {PropertyName}", 1);
11 | ```
12 |
--------------------------------------------------------------------------------
/rules/LogMessageIsSentenceProblem.md:
--------------------------------------------------------------------------------
1 | #### Log event messages should be fragments, not sentences
2 |
3 | [https://benfoster.io/blog/serilog-best-practices/#message-template-recommendations](https://benfoster.io/blog/serilog-best-practices/#message-template-recommendations)
4 |
5 | Noncompliant Code Examples:
6 | ```csharp
7 | Log.Error("Disk quota {Quota} MB exceeded by {User}.", quota, user);
8 | ```
9 |
10 |
11 | Compliant Solution:
12 | ```csharp
13 | Log.Error("Disk quota {Quota} MB exceeded by {User}", quota, user);
14 | ```
15 |
--------------------------------------------------------------------------------
/rules/PositionalPropertyUsedProblem.md:
--------------------------------------------------------------------------------
1 | #### Prefer named properties instead of positional ones
2 |
3 | Noncompliant Code Examples:
4 | ```csharp
5 | Log.Error("Disk quota {0} MB exceeded by {1}", quota, user);
6 | ```
7 |
8 |
9 | Compliant Solution:
10 | ```csharp
11 | Log.Error("Disk quota {Quota} MB exceeded by {User}", quota, user);
12 | ```
13 |
--------------------------------------------------------------------------------
/rules/TemplateDuplicatePropertyProblem.md:
--------------------------------------------------------------------------------
1 | #### Duplicate template property
2 |
3 | Noncompliant Code Example:
4 | ```csharp
5 | Log.Error("Disk quota {Quota} MB exceeded by {Quota}", quota, user);
6 | ```
7 |
8 | Compliant Solution:
9 | ```csharp
10 | Log.Error("Disk quota {Quota} MB exceeded by {User}", quota, user);
11 | ```
12 |
--------------------------------------------------------------------------------
/rules/TemplateIsNotCompileTimeConstantProblem.md:
--------------------------------------------------------------------------------
1 | #### Message template is not a compile time constant
2 |
3 | Noncompliant Code Examples:
4 | ```csharp
5 | Log.Error($"Disk quota {quota} MB exceeded by {user}");
6 | ```
7 |
8 | ```csharp
9 | Log.Error(string.Format("Disk quota {0} MB exceeded by {1}", quota, user));
10 | ```
11 |
12 |
13 | Compliant Solution:
14 | ```csharp
15 | Log.Error("Disk quota {Quota} MB exceeded by {User}", quota, user);
16 | ```
17 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'rider-structured-logging'
2 |
--------------------------------------------------------------------------------
/src/.idea/.idea.ReSharper.Structured.Logging/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Rider ignored files
5 | /modules.xml
6 | /contentModel.xml
7 | /projectSettingsUpdater.xml
8 | /.idea.ReSharper.Structured.Logging.iml
9 | # Editor-based HTTP Client requests
10 | /httpRequests/
11 | # Datasource local storage ignored files
12 | /dataSources/
13 | /dataSources.local.xml
14 | # GitHub Copilot persisted chat sessions
15 | /copilot/chatSessions
16 |
--------------------------------------------------------------------------------
/src/.idea/.idea.ReSharper.Structured.Logging/.idea/.name:
--------------------------------------------------------------------------------
1 | ReSharper.Structured.Logging
--------------------------------------------------------------------------------
/src/.idea/.idea.ReSharper.Structured.Logging/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/.idea/.idea.ReSharper.Structured.Logging/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/.idea/.idea.ReSharper.Structured.Logging/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/.run/Pack ReSharper.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/.run/Pack Rider.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/.run/Test ReSharper.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/.run/Test Rider.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32126.317
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReSharper.Structured.Logging", "ReSharper.Structured.Logging\ReSharper.Structured.Logging.csproj", "{0B29307F-4E38-4611-BA0D-408A4B4F4E15}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReSharper.Structured.Logging.Tests", "..\test\src\ReSharper.Structured.Logging.Tests.csproj", "{D07C40A7-39BE-4725-889F-FF2F2B781442}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8F7509A6-86AF-48A9-BD71-464ED879D8FA}"
11 | ProjectSection(SolutionItems) = preProject
12 | ..\.editorconfig = ..\.editorconfig
13 | ..\appveyor.yml = ..\appveyor.yml
14 | ..\build.gradle = ..\build.gradle
15 | ..\Directory.Build.props = ..\Directory.Build.props
16 | rider\main\resources\META-INF\plugin.xml = rider\main\resources\META-INF\plugin.xml
17 | ..\README.md = ..\README.md
18 | ..\.gitignore = ..\.gitignore
19 | ..\.github\dependabot.yml = ..\.github\dependabot.yml
20 | EndProjectSection
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReSharper.Structured.Logging.Rider", "ReSharper.Structured.Logging\ReSharper.Structured.Logging.Rider.csproj", "{81C42342-6FD5-48D0-B45F-FD364B170FC1}"
23 | EndProject
24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReSharper.Structured.Logging.Rider.Tests", "..\test\src\ReSharper.Structured.Logging.Rider.Tests.csproj", "{6BC99E0B-6362-4A62-84F1-103B93896294}"
25 | EndProject
26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rules", "Rules", "{D93C6901-5685-495B-A790-6C1467978205}"
27 | ProjectSection(SolutionItems) = preProject
28 | ..\rules\AnonymousObjectDestructuringProblem.md = ..\rules\AnonymousObjectDestructuringProblem.md
29 | ..\rules\ComplexObjectDestructuringProblem.md = ..\rules\ComplexObjectDestructuringProblem.md
30 | ..\rules\ComplexObjectInContextDestructuringProblem.md = ..\rules\ComplexObjectInContextDestructuringProblem.md
31 | ..\rules\ContextualLoggerProblem.md = ..\rules\ContextualLoggerProblem.md
32 | ..\rules\ExceptionPassedAsTemplateArgumentProblem.md = ..\rules\ExceptionPassedAsTemplateArgumentProblem.md
33 | ..\rules\InconsistentContextLogPropertyNaming.md = ..\rules\InconsistentContextLogPropertyNaming.md
34 | ..\rules\InconsistentLogPropertyNaming.md = ..\rules\InconsistentLogPropertyNaming.md
35 | ..\rules\LogMessageIsSentenceProblem.md = ..\rules\LogMessageIsSentenceProblem.md
36 | ..\rules\PositionalPropertyUsedProblem.md = ..\rules\PositionalPropertyUsedProblem.md
37 | ..\rules\TemplateDuplicatePropertyProblem.md = ..\rules\TemplateDuplicatePropertyProblem.md
38 | ..\rules\TemplateIsNotCompileTimeConstantProblem.md = ..\rules\TemplateIsNotCompileTimeConstantProblem.md
39 | EndProjectSection
40 | EndProject
41 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "..\build\_build.csproj", "{A7717BDB-133B-433E-97CF-624B746FDDC8}"
42 | EndProject
43 | Global
44 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
45 | Debug|Any CPU = Debug|Any CPU
46 | Release|Any CPU = Release|Any CPU
47 | EndGlobalSection
48 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
49 | {0B29307F-4E38-4611-BA0D-408A4B4F4E15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {0B29307F-4E38-4611-BA0D-408A4B4F4E15}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {0B29307F-4E38-4611-BA0D-408A4B4F4E15}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 | {0B29307F-4E38-4611-BA0D-408A4B4F4E15}.Release|Any CPU.Build.0 = Release|Any CPU
53 | {D07C40A7-39BE-4725-889F-FF2F2B781442}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54 | {D07C40A7-39BE-4725-889F-FF2F2B781442}.Debug|Any CPU.Build.0 = Debug|Any CPU
55 | {D07C40A7-39BE-4725-889F-FF2F2B781442}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 | {D07C40A7-39BE-4725-889F-FF2F2B781442}.Release|Any CPU.Build.0 = Release|Any CPU
57 | {81C42342-6FD5-48D0-B45F-FD364B170FC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58 | {81C42342-6FD5-48D0-B45F-FD364B170FC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
59 | {81C42342-6FD5-48D0-B45F-FD364B170FC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
60 | {81C42342-6FD5-48D0-B45F-FD364B170FC1}.Release|Any CPU.Build.0 = Release|Any CPU
61 | {6BC99E0B-6362-4A62-84F1-103B93896294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 | {6BC99E0B-6362-4A62-84F1-103B93896294}.Debug|Any CPU.Build.0 = Debug|Any CPU
63 | {6BC99E0B-6362-4A62-84F1-103B93896294}.Release|Any CPU.ActiveCfg = Release|Any CPU
64 | {6BC99E0B-6362-4A62-84F1-103B93896294}.Release|Any CPU.Build.0 = Release|Any CPU
65 | {A7717BDB-133B-433E-97CF-624B746FDDC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
66 | {A7717BDB-133B-433E-97CF-624B746FDDC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
67 | EndGlobalSection
68 | GlobalSection(SolutionProperties) = preSolution
69 | HideSolutionNode = FALSE
70 | EndGlobalSection
71 | GlobalSection(ExtensibilityGlobals) = postSolution
72 | SolutionGuid = {2861CBD1-55F1-4AF9-8F6D-F2C345E8BB03}
73 | EndGlobalSection
74 | EndGlobal
75 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Analyzer/AnonymousTypeDestructureAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | using JetBrains.ReSharper.Feature.Services.Daemon;
5 | using JetBrains.ReSharper.Psi;
6 | using JetBrains.ReSharper.Psi.CodeAnnotations;
7 | using JetBrains.ReSharper.Psi.CSharp.Tree;
8 |
9 | using ReSharper.Structured.Logging.Caching;
10 | using ReSharper.Structured.Logging.Extensions;
11 | using ReSharper.Structured.Logging.Highlighting;
12 | using ReSharper.Structured.Logging.Serilog.Parsing;
13 |
14 | namespace ReSharper.Structured.Logging.Analyzer
15 | {
16 | [ElementProblemAnalyzer(typeof(IInvocationExpression))]
17 | public class AnonymousTypeDestructureAnalyzer : ElementProblemAnalyzer
18 | {
19 | private readonly MessageTemplateParser _messageTemplateParser;
20 |
21 | private readonly Lazy _templateParameterNameAttributeProvider;
22 |
23 | public AnonymousTypeDestructureAnalyzer(MessageTemplateParser messageTemplateParser, CodeAnnotationsCache codeAnnotationsCache)
24 | {
25 | _messageTemplateParser = messageTemplateParser;
26 | _templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
27 | }
28 |
29 | protected override void Run(
30 | IInvocationExpression element,
31 | ElementProblemAnalyzerData data,
32 | IHighlightingConsumer consumer)
33 | {
34 | var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
35 | if (templateArgument == null)
36 | {
37 | return;
38 | }
39 |
40 | var anonymousObjectsArguments = element.ArgumentList.Arguments
41 | .Where(a => a.Value is IAnonymousObjectCreationExpression)
42 | .ToArray();
43 | if (anonymousObjectsArguments.Length == 0)
44 | {
45 | return;
46 | }
47 |
48 | var templateText = templateArgument.TryGetTemplateText();
49 | if (templateText == null)
50 | {
51 | return;
52 | }
53 |
54 | var messageTemplate = _messageTemplateParser.Parse(templateText);
55 | if (messageTemplate.NamedProperties == null)
56 | {
57 | return;
58 | }
59 |
60 | var templateArgumentIndex = templateArgument.IndexOf();
61 | foreach (var argument in anonymousObjectsArguments)
62 | {
63 | var index = argument.IndexOf() - templateArgumentIndex - 1;
64 | if (index < messageTemplate.NamedProperties.Length)
65 | {
66 | var namedProperty = messageTemplate.NamedProperties[index];
67 | if (namedProperty.Destructuring != Destructuring.Default)
68 | {
69 | continue;
70 | }
71 |
72 | var tokenInformation = templateArgument.GetTokenInformation(namedProperty);
73 | consumer.AddHighlighting(new AnonymousObjectDestructuringWarning(tokenInformation));
74 | }
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Analyzer/CompileTimeConstantTemplateAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using JetBrains.ReSharper.Feature.Services.Daemon;
4 | using JetBrains.ReSharper.Psi.CodeAnnotations;
5 | using JetBrains.ReSharper.Psi.CSharp.Tree;
6 |
7 | using ReSharper.Structured.Logging.Caching;
8 | using ReSharper.Structured.Logging.Extensions;
9 | using ReSharper.Structured.Logging.Highlighting;
10 |
11 | namespace ReSharper.Structured.Logging.Analyzer
12 | {
13 | [ElementProblemAnalyzer(typeof(IInvocationExpression))]
14 | public class CompileTimeConstantTemplateAnalyzer : ElementProblemAnalyzer
15 | {
16 | private readonly Lazy _templateParameterNameAttributeProvider;
17 |
18 | public CompileTimeConstantTemplateAnalyzer(CodeAnnotationsCache codeAnnotationsCache)
19 | {
20 | _templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
21 | }
22 |
23 | protected override void Run(
24 | IInvocationExpression element,
25 | ElementProblemAnalyzerData data,
26 | IHighlightingConsumer consumer)
27 | {
28 | var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
29 | if (templateArgument?.Value == null)
30 | {
31 | return;
32 | }
33 |
34 | if (templateArgument.Value.IsConstantValue())
35 | {
36 | return;
37 | }
38 |
39 | consumer.AddHighlighting(new TemplateIsNotCompileTimeConstantWarning(element, templateArgument));
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Analyzer/ContextualLoggerConstructorAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.Feature.Services.Daemon;
2 | using JetBrains.ReSharper.Psi;
3 | using JetBrains.ReSharper.Psi.CSharp.Tree;
4 | using JetBrains.ReSharper.Psi.Tree;
5 | using JetBrains.ReSharper.Psi.Util;
6 |
7 | using ReSharper.Structured.Logging.Extensions;
8 | using ReSharper.Structured.Logging.Highlighting;
9 |
10 | namespace ReSharper.Structured.Logging.Analyzer
11 | {
12 | [ElementProblemAnalyzer(typeof(IConstructorDeclaration))]
13 | public class ContextualLoggerConstructorAnalyzer : ElementProblemAnalyzer
14 | {
15 | // ReSharper disable once CognitiveComplexity
16 | protected override void Run(IConstructorDeclaration element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer)
17 | {
18 | if (element.Params?.ParameterDeclarations == null)
19 | {
20 | return;
21 | }
22 |
23 | foreach (var declaration in element.Params.ParameterDeclarations)
24 | {
25 | if (!(declaration.Type is IDeclaredType declaredType))
26 | {
27 | continue;
28 | }
29 |
30 | if (!declaredType.IsGenericMicrosoftExtensionsLogger())
31 | {
32 | continue;
33 | }
34 |
35 | var argumentType = declaredType.GetFirstGenericArgumentType();
36 | if (argumentType == null)
37 | {
38 | continue;
39 | }
40 |
41 | var containingType = element.DeclaredElement?.GetContainingType();
42 | var className = containingType?.GetClrName().FullName;
43 | if (className == null)
44 | {
45 | continue;
46 | }
47 |
48 | if (className.Equals(argumentType.GetClassType()?.GetClrName().FullName))
49 | {
50 | continue;
51 | }
52 |
53 | consumer.AddHighlighting(new ContextualLoggerWarning(declaration.TypeUsage.GetDocumentRange()));
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Analyzer/ContextualLoggerSerilogFactoryAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.Feature.Services.Daemon;
2 | using JetBrains.ReSharper.Psi.CSharp.Tree;
3 | using JetBrains.ReSharper.Psi.Tree;
4 |
5 | using ReSharper.Structured.Logging.Extensions;
6 | using ReSharper.Structured.Logging.Highlighting;
7 |
8 | namespace ReSharper.Structured.Logging.Analyzer
9 | {
10 | [ElementProblemAnalyzer(typeof(IInvocationExpression))]
11 | public class ContextualLoggerSerilogFactoryAnalyzer : ElementProblemAnalyzer
12 | {
13 | protected override void Run(IInvocationExpression element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer)
14 | {
15 | if (!element.IsSerilogContextFactoryLogger())
16 | {
17 | return;
18 | }
19 |
20 | var containingNode = element.GetContainingNode();
21 | if (containingNode == null)
22 | {
23 | return;
24 | }
25 |
26 | var invocationTypeArgument = element.TypeArguments[0]?.GetScalarType()?.GetClrName();
27 | if (invocationTypeArgument?.FullName == containingNode.CLRName)
28 | {
29 | return;
30 | }
31 |
32 | consumer.AddHighlighting(new ContextualLoggerWarning(element.GetDocumentRange()));
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Analyzer/CorrectExceptionPassingAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using JetBrains.Metadata.Reader.API;
4 | using JetBrains.ReSharper.Feature.Services.Daemon;
5 | using JetBrains.ReSharper.Psi;
6 | using JetBrains.ReSharper.Psi.CodeAnnotations;
7 | using JetBrains.ReSharper.Psi.CSharp.Tree;
8 | using JetBrains.ReSharper.Psi.Util;
9 |
10 | using ReSharper.Structured.Logging.Caching;
11 | using ReSharper.Structured.Logging.Extensions;
12 | using ReSharper.Structured.Logging.Highlighting;
13 |
14 | namespace ReSharper.Structured.Logging.Analyzer
15 | {
16 | [ElementProblemAnalyzer(typeof(IInvocationExpression))]
17 | public class CorrectExceptionPassingAnalyzer : ElementProblemAnalyzer
18 | {
19 | private readonly Lazy _templateParameterNameAttributeProvider;
20 |
21 | public CorrectExceptionPassingAnalyzer(CodeAnnotationsCache codeAnnotationsCache)
22 | {
23 | _templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
24 | }
25 |
26 | // ReSharper disable once CognitiveComplexity
27 | protected override void Run(
28 | IInvocationExpression element,
29 | ElementProblemAnalyzerData data,
30 | IHighlightingConsumer consumer)
31 | {
32 | var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
33 | if (templateArgument == null)
34 | {
35 | return;
36 | }
37 |
38 | var exceptionType = element.PsiModule.GetPredefinedType().TryGetType(PredefinedType.EXCEPTION_FQN, NullableAnnotation.Unknown);
39 | if (exceptionType == null)
40 | {
41 | return;
42 | }
43 |
44 | ICSharpArgument invalidExceptionArgument = FindInvalidExceptionArgument(element, templateArgument, exceptionType);
45 | if (invalidExceptionArgument == null)
46 | {
47 | return;
48 | }
49 |
50 | var overloadAvailable = false;
51 | var candidates = element.InvocationExpressionReference.GetCandidates().ToArray();
52 | var invalidArgumentIndex = invalidExceptionArgument.IndexOf();
53 | foreach (var candidate in candidates)
54 | {
55 | if (!(candidate.GetDeclaredElement() is IMethod declaredElement))
56 | {
57 | continue;
58 | }
59 |
60 | foreach (var parameter in declaredElement.Parameters)
61 | {
62 | if (invalidArgumentIndex <= parameter.IndexOf())
63 | {
64 | break;
65 | }
66 |
67 | if (parameter.Type.IsSubtypeOf(exceptionType))
68 | {
69 | overloadAvailable = true;
70 | break;
71 | }
72 | }
73 |
74 | if (overloadAvailable)
75 | {
76 | break;
77 | }
78 | }
79 |
80 | if (!overloadAvailable)
81 | {
82 | return;
83 | }
84 |
85 | consumer.AddHighlighting(new ExceptionPassedAsTemplateArgumentWarning(invalidExceptionArgument.GetDocumentRange()));
86 | }
87 |
88 | private ICSharpArgument FindInvalidExceptionArgument(IInvocationExpression invocationExpression, ICSharpArgument templateArgument, IDeclaredType exceptionType)
89 | {
90 | var templateArgumentIndex = templateArgument.IndexOf();
91 | foreach (var argument in invocationExpression.ArgumentList.Arguments)
92 | {
93 | var argumentType = argument.Value?.Type();
94 | if (!(argumentType is IDeclaredType declaredType))
95 | {
96 | continue;
97 | }
98 |
99 | if (!declaredType.IsSubtypeOf(exceptionType))
100 | {
101 | continue;
102 | }
103 |
104 | if (templateArgumentIndex > argument.IndexOf())
105 | {
106 | return null;
107 | }
108 |
109 | return argument;
110 | }
111 |
112 | return null;
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Analyzer/DuplicatePropertiesTemplateAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | using JetBrains.ReSharper.Feature.Services.Daemon;
5 | using JetBrains.ReSharper.Psi.CodeAnnotations;
6 | using JetBrains.ReSharper.Psi.CSharp.Tree;
7 |
8 | using ReSharper.Structured.Logging.Caching;
9 | using ReSharper.Structured.Logging.Extensions;
10 | using ReSharper.Structured.Logging.Highlighting;
11 | using ReSharper.Structured.Logging.Serilog.Parsing;
12 |
13 | namespace ReSharper.Structured.Logging.Analyzer
14 | {
15 | [ElementProblemAnalyzer(typeof(IInvocationExpression))]
16 | public class DuplicatePropertiesTemplateAnalyzer : ElementProblemAnalyzer
17 | {
18 | private readonly MessageTemplateParser _messageTemplateParser;
19 |
20 | private readonly Lazy _templateParameterNameAttributeProvider;
21 |
22 | public DuplicatePropertiesTemplateAnalyzer(MessageTemplateParser messageTemplateParser, CodeAnnotationsCache codeAnnotationsCache)
23 | {
24 | _messageTemplateParser = messageTemplateParser;
25 | _templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
26 | }
27 |
28 | protected override void Run(
29 | IInvocationExpression element,
30 | ElementProblemAnalyzerData data,
31 | IHighlightingConsumer consumer)
32 | {
33 | var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
34 | var templateText = templateArgument?.TryGetTemplateText();
35 | if (templateText == null)
36 | {
37 | return;
38 | }
39 |
40 | var messageTemplate = _messageTemplateParser.Parse(templateText);
41 | if (messageTemplate.NamedProperties == null)
42 | {
43 | return;
44 | }
45 |
46 | foreach (var duplicates in messageTemplate.NamedProperties
47 | .GroupBy(n => n.PropertyName)
48 | .Where(g => g.Count() > 1))
49 | {
50 | foreach (var token in duplicates)
51 | {
52 | consumer.AddHighlighting(new DuplicateTemplatePropertyWarning(templateArgument.GetTokenInformation(token)));
53 | }
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Analyzer/LogMessageIsSentenceAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 |
4 | using JetBrains.ReSharper.Feature.Services.Daemon;
5 | using JetBrains.ReSharper.Psi.CodeAnnotations;
6 | using JetBrains.ReSharper.Psi.CSharp.Tree;
7 | using JetBrains.ReSharper.Psi.Util;
8 |
9 | using ReSharper.Structured.Logging.Caching;
10 | using ReSharper.Structured.Logging.Extensions;
11 | using ReSharper.Structured.Logging.Highlighting;
12 |
13 | namespace ReSharper.Structured.Logging.Analyzer
14 | {
15 | [ElementProblemAnalyzer(typeof(IInvocationExpression), HighlightingTypes = new[] { typeof(LogMessageIsSentenceWarning) })]
16 | public class LogMessageIsSentenceAnalyzer : ElementProblemAnalyzer
17 | {
18 | private readonly Lazy _templateParameterNameAttributeProvider;
19 |
20 | private static readonly Regex DotAtTheEnd = new Regex(@"(?();
25 | }
26 |
27 | protected override void Run(IInvocationExpression element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer)
28 | {
29 | var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
30 | var lastFragmentExpression = templateArgument?.TryCreateLastTemplateFragmentExpression();
31 | if (lastFragmentExpression == null)
32 | {
33 | return;
34 | }
35 |
36 | var unquotedText = lastFragmentExpression.Expression.GetUnquotedText();
37 | if (!DotAtTheEnd.IsMatch(unquotedText))
38 | {
39 | return;
40 | }
41 |
42 | consumer.AddHighlighting(new LogMessageIsSentenceWarning(lastFragmentExpression, DotAtTheEnd));
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Analyzer/PositionalPropertiesUsageAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using JetBrains.ReSharper.Feature.Services.Daemon;
4 | using JetBrains.ReSharper.Psi.CodeAnnotations;
5 | using JetBrains.ReSharper.Psi.CSharp.Tree;
6 |
7 | using ReSharper.Structured.Logging.Caching;
8 | using ReSharper.Structured.Logging.Extensions;
9 | using ReSharper.Structured.Logging.Highlighting;
10 | using ReSharper.Structured.Logging.Serilog.Parsing;
11 |
12 | namespace ReSharper.Structured.Logging.Analyzer
13 | {
14 | [ElementProblemAnalyzer(typeof(IInvocationExpression))]
15 | public class PositionalPropertiesUsageAnalyzer : ElementProblemAnalyzer
16 | {
17 | private readonly MessageTemplateParser _messageTemplateParser;
18 |
19 | private readonly Lazy _templateParameterNameAttributeProvider;
20 |
21 | public PositionalPropertiesUsageAnalyzer(MessageTemplateParser messageTemplateParser, CodeAnnotationsCache codeAnnotationsCache)
22 | {
23 | _messageTemplateParser = messageTemplateParser;
24 | _templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
25 | }
26 |
27 | protected override void Run(
28 | IInvocationExpression element,
29 | ElementProblemAnalyzerData data,
30 | IHighlightingConsumer consumer)
31 | {
32 | var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
33 | var templateText = templateArgument?.TryGetTemplateText();
34 | if (templateText == null)
35 | {
36 | return;
37 | }
38 |
39 | var messageTemplate = _messageTemplateParser.Parse(templateText);
40 | if (messageTemplate.PositionalProperties == null)
41 | {
42 | return;
43 | }
44 |
45 | foreach (var property in messageTemplate.PositionalProperties)
46 | {
47 | consumer.AddHighlighting(new PositionalPropertyUsedWarning(templateArgument.GetTokenInformation(property)));
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Analyzer/PropertiesNamingAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 |
4 | using JetBrains.Application.Settings;
5 | using JetBrains.ProjectModel;
6 | using JetBrains.ReSharper.Feature.Services.Daemon;
7 | using JetBrains.ReSharper.Psi.CodeAnnotations;
8 | using JetBrains.ReSharper.Psi.CSharp.Tree;
9 | using JetBrains.ReSharper.Psi.Tree;
10 |
11 | using ReSharper.Structured.Logging.Caching;
12 | using ReSharper.Structured.Logging.Extensions;
13 | using ReSharper.Structured.Logging.Highlighting;
14 | using ReSharper.Structured.Logging.Serilog.Parsing;
15 | using ReSharper.Structured.Logging.Services;
16 | using ReSharper.Structured.Logging.Settings;
17 |
18 | namespace ReSharper.Structured.Logging.Analyzer;
19 |
20 | [ElementProblemAnalyzer(typeof(IInvocationExpression))]
21 | public class PropertiesNamingAnalyzer : ElementProblemAnalyzer
22 | {
23 | private readonly MessageTemplateParser _messageTemplateParser;
24 |
25 | private readonly Lazy _templateParameterNameAttributeProvider;
26 |
27 | public PropertiesNamingAnalyzer(MessageTemplateParser messageTemplateParser, CodeAnnotationsCache codeAnnotationsCache)
28 | {
29 | _messageTemplateParser = messageTemplateParser;
30 | _templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
31 | }
32 |
33 | protected override void Run(
34 | IInvocationExpression element,
35 | ElementProblemAnalyzerData data,
36 | IHighlightingConsumer consumer)
37 | {
38 | var settingsStore = element.GetProject()
39 | ?.GetSolution()
40 | .GetSettingsStore();
41 |
42 | var ignoreRegexString = settingsStore?.GetValue(StructuredLoggingSettingsAccessor.IgnoredPropertiesRegex);
43 | var ignoredPropertiesRegex = string.IsNullOrWhiteSpace(ignoreRegexString) ? null : new Regex(ignoreRegexString);
44 |
45 | CheckPropertiesInTemplate(element, consumer, settingsStore, ignoredPropertiesRegex);
46 | CheckPropertiesInContext(element, consumer, settingsStore, ignoredPropertiesRegex);
47 | }
48 |
49 | private void CheckPropertiesInTemplate(
50 | IInvocationExpression element,
51 | IHighlightingConsumer consumer,
52 | IContextBoundSettingsStore settingsStore,
53 | Regex ignoredPropertiesRegex)
54 | {
55 | var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
56 | var templateText = templateArgument?.TryGetTemplateText();
57 | if (templateText == null)
58 | {
59 | return;
60 | }
61 |
62 | var messageTemplate = _messageTemplateParser.Parse(templateText);
63 | if (messageTemplate.NamedProperties == null)
64 | {
65 | return;
66 | }
67 |
68 | foreach (var property in messageTemplate.NamedProperties)
69 | {
70 | if (string.IsNullOrEmpty(property.PropertyName))
71 | {
72 | continue;
73 | }
74 |
75 | var suggestedName = GetSuggestedName(property.PropertyName, settingsStore, ignoredPropertiesRegex);
76 | if (string.Equals(suggestedName, property.PropertyName))
77 | {
78 | continue;
79 | }
80 |
81 | consumer.AddHighlighting(
82 | new InconsistentLogPropertyNamingWarning(templateArgument.GetTokenInformation(property), property,
83 | suggestedName));
84 | }
85 | }
86 |
87 | private void CheckPropertiesInContext(
88 | IInvocationExpression element,
89 | IHighlightingConsumer consumer,
90 | IContextBoundSettingsStore settingsStore,
91 | Regex ignoredPropertiesRegex)
92 | {
93 | if (!element.IsSerilogContextPushPropertyMethod())
94 | {
95 | return;
96 | }
97 |
98 | if (element.ArgumentList.Arguments.Count < 1)
99 | {
100 | return;
101 | }
102 |
103 | var propertyArgument = element.ArgumentList.Arguments[0];
104 |
105 | var propertyName = string.Empty;
106 | propertyArgument.Value?.ConstantValue.IsString(out propertyName);
107 | if (string.IsNullOrEmpty(propertyName))
108 | {
109 | return;
110 | }
111 |
112 | var suggestedName = GetSuggestedName(propertyName, settingsStore, ignoredPropertiesRegex);
113 | if (string.Equals(propertyName, suggestedName))
114 | {
115 | return;
116 | }
117 |
118 | consumer.AddHighlighting(new InconsistentContextLogPropertyNamingWarning(propertyArgument, propertyName, suggestedName));
119 | }
120 |
121 | private string GetSuggestedName(string propertyName, IContextBoundSettingsStore settingsStore, Regex ignoredPropertiesRegex)
122 | {
123 | if (ignoredPropertiesRegex != null && ignoredPropertiesRegex.IsMatch(propertyName))
124 | {
125 | return propertyName;
126 | }
127 |
128 | return PropertyNameProvider.GetSuggestedName(propertyName, settingsStore);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Caching/TemplateParameterNameAttributeProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | using JetBrains.Application.Parts;
6 | using JetBrains.ReSharper.Psi;
7 | using JetBrains.ReSharper.Psi.CodeAnnotations;
8 |
9 | namespace ReSharper.Structured.Logging.Caching;
10 |
11 | [CodeAnnotationProvider(Instantiation.DemandAnyThreadUnsafe)]
12 | public class TemplateParameterNameAttributeProvider(
13 | AttributeInstancesProvider attributeInstancesProvider,
14 | CodeAnnotationsConfiguration codeAnnotationsConfiguration)
15 | : CodeAnnotationInfoProvider(attributeInstancesProvider, codeAnnotationsConfiguration, true)
16 | {
17 | private const string MessageTemplateFormatMethodAttribute = "MessageTemplateFormatMethodAttribute";
18 |
19 | protected override string CalculateInfo(ITypeMember attributesOwner, IEnumerable attributeInstances)
20 | {
21 | var templateFormatAttribute = attributeInstances
22 | .FirstOrDefault(a => string.Equals(a.GetAttributeShortName(), MessageTemplateFormatMethodAttribute, StringComparison.Ordinal));
23 |
24 | if (templateFormatAttribute != null)
25 | {
26 | return templateFormatAttribute.PositionParameters()
27 | .FirstOrDefault()
28 | ?.ConstantValue.StringValue;
29 | }
30 |
31 | var className = attributesOwner.ContainingType?.GetClrName().FullName;
32 | if (className == "Microsoft.Extensions.Logging.LoggerExtensions")
33 | {
34 | return attributesOwner.ShortName == "BeginScope" ? "messageFormat" : "message";
35 | }
36 |
37 | if (className == "ZLogger.ZLoggerExtensions")
38 | {
39 | return "format";
40 | }
41 |
42 | return null;
43 | }
44 |
45 | protected override string GetDefaultInfo(ITypeMember attributesOwner)
46 | {
47 | return null;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/AnonymousObjectWithoutDestructuringWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 |
5 | using ReSharper.Structured.Logging.Models;
6 | using ReSharper.Structured.Logging.Settings;
7 |
8 | namespace ReSharper.Structured.Logging.Highlighting
9 | {
10 | [RegisterConfigurableSeverity(
11 | SeverityId,
12 | null,
13 | StructuredLoggingGroup.Id,
14 | Message,
15 | Message,
16 | Severity.WARNING)]
17 | [ConfigurableSeverityHighlighting(
18 | SeverityId,
19 | CSharpLanguage.Name,
20 | OverlapResolve = OverlapResolveKind.WARNING,
21 | ToolTipFormatString = Message)]
22 | public class AnonymousObjectDestructuringWarning : IHighlighting
23 | {
24 | private const string Message = "Anonymous objects must be destructured";
25 |
26 | public const string SeverityId = "AnonymousObjectDestructuringProblem";
27 |
28 | public AnonymousObjectDestructuringWarning(MessageTemplateTokenInformation tokenInformation)
29 | {
30 | TokenInformation = tokenInformation;
31 | }
32 |
33 | public string ErrorStripeToolTip => ToolTip;
34 |
35 | public MessageTemplateTokenInformation TokenInformation { get; }
36 |
37 | public string ToolTip => Message;
38 |
39 | public DocumentRange CalculateRange()
40 | {
41 | return TokenInformation.DocumentRange;
42 | }
43 |
44 | public bool IsValid()
45 | {
46 | return TokenInformation.DocumentRange.IsValid();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/ComplexObjectDestructuringInContextWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 | using JetBrains.ReSharper.Psi.CSharp.Tree;
5 | using JetBrains.ReSharper.Psi.Tree;
6 |
7 | using ReSharper.Structured.Logging.Settings;
8 |
9 | namespace ReSharper.Structured.Logging.Highlighting
10 | {
11 | [RegisterConfigurableSeverity(
12 | SeverityId,
13 | null,
14 | StructuredLoggingGroup.Id,
15 | Message,
16 | Message,
17 | Severity.WARNING)]
18 | [ConfigurableSeverityHighlighting(
19 | SeverityId,
20 | CSharpLanguage.Name,
21 | OverlapResolve = OverlapResolveKind.WARNING,
22 | ToolTipFormatString = Message)]
23 | public class ComplexObjectDestructuringInContextWarning : ComplexObjectDestructuringWarningBase, IHighlighting
24 | {
25 | public const string SeverityId = "ComplexObjectInContextDestructuringProblem";
26 |
27 | private readonly IInvocationExpression _invocationExpression;
28 |
29 | public ComplexObjectDestructuringInContextWarning(IInvocationExpression invocationExpression)
30 | {
31 | _invocationExpression = invocationExpression;
32 | }
33 |
34 | public string ErrorStripeToolTip => ToolTip;
35 |
36 | public string ToolTip => Message;
37 |
38 | public DocumentRange CalculateRange()
39 | {
40 | return _invocationExpression.GetDocumentRange();
41 | }
42 |
43 | public bool IsValid()
44 | {
45 | return _invocationExpression.GetDocumentRange().IsValid();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/ComplexObjectDestructuringWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 |
5 | using ReSharper.Structured.Logging.Models;
6 | using ReSharper.Structured.Logging.Settings;
7 |
8 | namespace ReSharper.Structured.Logging.Highlighting
9 | {
10 | [RegisterConfigurableSeverity(
11 | SeverityId,
12 | null,
13 | StructuredLoggingGroup.Id,
14 | Message,
15 | Message,
16 | Severity.WARNING)]
17 | [ConfigurableSeverityHighlighting(
18 | SeverityId,
19 | CSharpLanguage.Name,
20 | OverlapResolve = OverlapResolveKind.WARNING,
21 | ToolTipFormatString = Message)]
22 | public class ComplexObjectDestructuringWarning : ComplexObjectDestructuringWarningBase, IHighlighting
23 | {
24 | public const string SeverityId = "ComplexObjectDestructuringProblem";
25 |
26 | public ComplexObjectDestructuringWarning(MessageTemplateTokenInformation tokenInformation)
27 | {
28 | TokenInformation = tokenInformation;
29 | }
30 |
31 | public string ErrorStripeToolTip => ToolTip;
32 |
33 | public MessageTemplateTokenInformation TokenInformation { get; }
34 |
35 | public string ToolTip => Message;
36 |
37 | public DocumentRange CalculateRange()
38 | {
39 | return TokenInformation.DocumentRange;
40 | }
41 |
42 | public bool IsValid()
43 | {
44 | return TokenInformation.DocumentRange.IsValid();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/ComplexObjectDestructuringWarningBase.cs:
--------------------------------------------------------------------------------
1 | namespace ReSharper.Structured.Logging.Highlighting
2 | {
3 | public abstract class ComplexObjectDestructuringWarningBase
4 | {
5 | protected const string Message = "Complex objects with default ToString() implementation probably need to be destructured";
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/ContextualLoggerWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 |
5 | using ReSharper.Structured.Logging.Settings;
6 |
7 | namespace ReSharper.Structured.Logging.Highlighting
8 | {
9 | [RegisterConfigurableSeverity(
10 | SeverityId,
11 | null,
12 | StructuredLoggingGroup.Id,
13 | Message,
14 | Message,
15 | Severity.WARNING)]
16 | [ConfigurableSeverityHighlighting(
17 | SeverityId,
18 | CSharpLanguage.Name,
19 | OverlapResolve = OverlapResolveKind.WARNING,
20 | ToolTipFormatString = Message)]
21 | public class ContextualLoggerWarning : IHighlighting
22 | {
23 | private const string Message = "Incorrect type is used for contextual logger";
24 |
25 | public const string SeverityId = "ContextualLoggerProblem";
26 |
27 | private readonly DocumentRange _range;
28 |
29 | public ContextualLoggerWarning(DocumentRange documentRange)
30 | {
31 | _range = documentRange;
32 | }
33 |
34 | public string ErrorStripeToolTip => ToolTip;
35 |
36 | public string ToolTip => Message;
37 |
38 | public DocumentRange CalculateRange()
39 | {
40 | return _range;
41 | }
42 |
43 | public bool IsValid()
44 | {
45 | return _range.IsValid();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/DuplicateTemplatePropertyWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 |
5 | using ReSharper.Structured.Logging.Models;
6 | using ReSharper.Structured.Logging.Settings;
7 |
8 | namespace ReSharper.Structured.Logging.Highlighting
9 | {
10 | [RegisterConfigurableSeverity(
11 | SeverityId,
12 | null,
13 | StructuredLoggingGroup.Id,
14 | Message,
15 | Message,
16 | Severity.WARNING)]
17 | [ConfigurableSeverityHighlighting(
18 | SeverityId,
19 | CSharpLanguage.Name,
20 | OverlapResolve = OverlapResolveKind.WARNING,
21 | ToolTipFormatString = Message)]
22 | public class DuplicateTemplatePropertyWarning : IHighlighting
23 | {
24 | private const string Message = "Duplicate properties in message template";
25 |
26 | public const string SeverityId = "TemplateDuplicatePropertyProblem";
27 |
28 | private readonly DocumentRange _documentRange;
29 |
30 | public DuplicateTemplatePropertyWarning(MessageTemplateTokenInformation tokenInformation)
31 | {
32 | _documentRange = tokenInformation.DocumentRange;
33 | }
34 |
35 | public string ErrorStripeToolTip => ToolTip;
36 |
37 | public string ToolTip => Message;
38 |
39 | public DocumentRange CalculateRange()
40 | {
41 | return _documentRange;
42 | }
43 |
44 | public bool IsValid()
45 | {
46 | return _documentRange.IsValid();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/ExceptionPassedAsTemplateArgumentWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 |
5 | using ReSharper.Structured.Logging.Settings;
6 |
7 | namespace ReSharper.Structured.Logging.Highlighting
8 | {
9 | [RegisterConfigurableSeverity(
10 | SeverityId,
11 | null,
12 | StructuredLoggingGroup.Id,
13 | Message,
14 | Message,
15 | Severity.WARNING)]
16 | [ConfigurableSeverityHighlighting(
17 | SeverityId,
18 | CSharpLanguage.Name,
19 | OverlapResolve = OverlapResolveKind.WARNING,
20 | ToolTipFormatString = Message)]
21 | public class ExceptionPassedAsTemplateArgumentWarning : IHighlighting
22 | {
23 | public const string SeverityId = "ExceptionPassedAsTemplateArgumentProblem";
24 |
25 | private const string Message = "Exception should be passed to the exception argument";
26 |
27 | private readonly DocumentRange _documentRange;
28 |
29 | public ExceptionPassedAsTemplateArgumentWarning(DocumentRange documentRange)
30 | {
31 | _documentRange = documentRange;
32 | }
33 |
34 | public string ErrorStripeToolTip => ToolTip;
35 |
36 | public string ToolTip => Message;
37 |
38 | public DocumentRange CalculateRange()
39 | {
40 | return _documentRange;
41 | }
42 |
43 | public bool IsValid()
44 | {
45 | return _documentRange.IsValid();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/InconsistentContextLogPropertyNamingWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 | using JetBrains.ReSharper.Psi.CSharp.Tree;
5 |
6 | using ReSharper.Structured.Logging.Settings;
7 |
8 | namespace ReSharper.Structured.Logging.Highlighting
9 | {
10 | [RegisterConfigurableSeverity(
11 | SeverityId,
12 | null,
13 | StructuredLoggingGroup.Id,
14 | Message,
15 | Message,
16 | Severity.WARNING)]
17 | [ConfigurableSeverityHighlighting(
18 | SeverityId,
19 | CSharpLanguage.Name,
20 | OverlapResolve = OverlapResolveKind.WARNING,
21 | ToolTipFormatString = Message)]
22 | public class InconsistentContextLogPropertyNamingWarning : InconsistentLogPropertyNamingWarningBase, IHighlighting
23 | {
24 | private readonly string _propertyName;
25 |
26 | public const string SeverityId = "InconsistentContextLogPropertyNaming";
27 |
28 | public InconsistentContextLogPropertyNamingWarning(ICSharpArgument argument, string propertyName, string suggestedName)
29 | {
30 | _propertyName = propertyName;
31 | Argument = argument;
32 | SuggestedName = suggestedName;
33 | }
34 |
35 | public string ErrorStripeToolTip => ToolTip;
36 |
37 | public ICSharpArgument Argument { get; }
38 |
39 |
40 | public string SuggestedName { get; }
41 |
42 | public string ToolTip => GetToolTipMessage(_propertyName, SuggestedName);
43 |
44 | public DocumentRange CalculateRange()
45 | {
46 | return Argument.GetDocumentRange();
47 | }
48 |
49 | public bool IsValid()
50 | {
51 | return Argument.GetDocumentRange().IsValid();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/InconsistentLogPropertyNamingWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 |
5 | using ReSharper.Structured.Logging.Models;
6 | using ReSharper.Structured.Logging.Serilog.Parsing;
7 | using ReSharper.Structured.Logging.Settings;
8 |
9 | namespace ReSharper.Structured.Logging.Highlighting
10 | {
11 | [RegisterConfigurableSeverity(
12 | SeverityId,
13 | null,
14 | StructuredLoggingGroup.Id,
15 | Message,
16 | Message,
17 | Severity.WARNING)]
18 | [ConfigurableSeverityHighlighting(
19 | SeverityId,
20 | CSharpLanguage.Name,
21 | OverlapResolve = OverlapResolveKind.WARNING,
22 | ToolTipFormatString = Message)]
23 | public class InconsistentLogPropertyNamingWarning : InconsistentLogPropertyNamingWarningBase, IHighlighting
24 | {
25 | public const string SeverityId = "InconsistentLogPropertyNaming";
26 |
27 | public InconsistentLogPropertyNamingWarning(
28 | MessageTemplateTokenInformation tokenInformation,
29 | PropertyToken namedProperty,
30 | string suggestedName)
31 | {
32 | TokenInformation = tokenInformation;
33 | NamedProperty = namedProperty;
34 | SuggestedName = suggestedName;
35 | }
36 |
37 | public string ErrorStripeToolTip => ToolTip;
38 |
39 | public MessageTemplateTokenInformation TokenInformation { get; }
40 |
41 | public PropertyToken NamedProperty { get; }
42 |
43 | public string SuggestedName { get; }
44 |
45 | public string ToolTip => GetToolTipMessage(NamedProperty.PropertyName, SuggestedName);
46 |
47 | public DocumentRange CalculateRange()
48 | {
49 | return TokenInformation.DocumentRange;
50 | }
51 |
52 | public bool IsValid()
53 | {
54 | return TokenInformation.DocumentRange.IsValid();
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/InconsistentLogPropertyNamingWarningBase.cs:
--------------------------------------------------------------------------------
1 | namespace ReSharper.Structured.Logging.Highlighting
2 | {
3 | public class InconsistentLogPropertyNamingWarningBase
4 | {
5 | protected const string Message = "Property name '{0}' does not match naming rules. Suggested name is '{1}'.";
6 |
7 | protected string GetToolTipMessage(string propertyName, string suggestedName)
8 | {
9 | return $"Property name '{propertyName}' does not match naming rules. Suggested name is '{suggestedName}'.";
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/LogMessageIsSentenceWarning.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 |
3 | using JetBrains.DocumentModel;
4 | using JetBrains.ReSharper.Feature.Services.Daemon;
5 | using JetBrains.ReSharper.Psi.CSharp;
6 | using JetBrains.ReSharper.Psi.Tree;
7 | using JetBrains.ReSharper.Psi.Util;
8 |
9 | using ReSharper.Structured.Logging.Settings;
10 |
11 | namespace ReSharper.Structured.Logging.Highlighting
12 | {
13 | [RegisterConfigurableSeverity(
14 | SeverityId,
15 | null,
16 | StructuredLoggingGroup.Id,
17 | Message,
18 | Message,
19 | Severity.WARNING)]
20 | [ConfigurableSeverityHighlighting(
21 | SeverityId,
22 | CSharpLanguage.Name,
23 | OverlapResolve = OverlapResolveKind.WARNING,
24 | ToolTipFormatString = Message)]
25 | public class LogMessageIsSentenceWarning : IHighlighting
26 | {
27 | private const string Message = "Log event messages should be fragments, not sentences. Avoid a trailing period/full stop.";
28 |
29 | public const string SeverityId = "LogMessageIsSentenceProblem";
30 |
31 | private readonly DocumentRange _documentRange;
32 |
33 | public LogMessageIsSentenceWarning(IStringLiteralAlterer stringLiteral, Regex regex)
34 | {
35 | StringLiteral = stringLiteral;
36 | Regex = regex;
37 | _documentRange = stringLiteral.Expression.GetDocumentRange();
38 | }
39 |
40 | public string ErrorStripeToolTip => ToolTip;
41 |
42 | public string ToolTip => Message;
43 |
44 | public IStringLiteralAlterer StringLiteral { get; }
45 |
46 | public Regex Regex { get; }
47 |
48 | public DocumentRange CalculateRange()
49 | {
50 | return _documentRange;
51 | }
52 |
53 | public bool IsValid()
54 | {
55 | return _documentRange.IsValid();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/PositionalPropertyUsedWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 |
5 | using ReSharper.Structured.Logging.Models;
6 | using ReSharper.Structured.Logging.Settings;
7 |
8 | namespace ReSharper.Structured.Logging.Highlighting
9 | {
10 | [RegisterConfigurableSeverity(
11 | SeverityId,
12 | null,
13 | StructuredLoggingGroup.Id,
14 | Message,
15 | Message,
16 | Severity.WARNING)]
17 | [ConfigurableSeverityHighlighting(
18 | SeverityId,
19 | CSharpLanguage.Name,
20 | OverlapResolve = OverlapResolveKind.WARNING,
21 | ToolTipFormatString = Message)]
22 | public class PositionalPropertyUsedWarning : IHighlighting
23 | {
24 | private readonly MessageTemplateTokenInformation _tokenInformation;
25 |
26 | private const string Message = "Prefer named properties instead of positional ones";
27 |
28 | public const string SeverityId = "PositionalPropertyUsedProblem";
29 |
30 | public PositionalPropertyUsedWarning(MessageTemplateTokenInformation tokenInformation)
31 | {
32 | _tokenInformation = tokenInformation;
33 | }
34 |
35 | public string ErrorStripeToolTip => ToolTip;
36 |
37 | public string ToolTip => Message;
38 |
39 | public DocumentRange CalculateRange()
40 | {
41 | return _tokenInformation.DocumentRange;
42 | }
43 |
44 | public bool IsValid()
45 | {
46 | return _tokenInformation.DocumentRange.IsValid();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/TemplateFormatStringNonExistingArgumentWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 |
5 | using ReSharper.Structured.Logging.Settings;
6 |
7 | namespace ReSharper.Structured.Logging.Highlighting
8 | {
9 | [RegisterConfigurableSeverity(
10 | SeverityId,
11 | null,
12 | StructuredLoggingGroup.Id,
13 | Message,
14 | Message,
15 | Severity.WARNING)]
16 | [ConfigurableSeverityHighlighting(
17 | SeverityId,
18 | CSharpLanguage.Name,
19 | OverlapResolve = OverlapResolveKind.WARNING,
20 | ToolTipFormatString = Message)]
21 | public class TemplateFormatStringNonExistingArgumentWarning : IHighlighting
22 | {
23 | private const string SeverityId = "TemplateFormatStringProblem";
24 |
25 | private const string Message = "Non-existing argument in message template";
26 |
27 | private readonly DocumentRange _documentRange;
28 |
29 | public TemplateFormatStringNonExistingArgumentWarning(DocumentRange documentRange)
30 | {
31 | _documentRange = documentRange;
32 | }
33 |
34 | public string ErrorStripeToolTip => ToolTip;
35 |
36 | public string ToolTip => Message;
37 |
38 | public DocumentRange CalculateRange()
39 | {
40 | return _documentRange;
41 | }
42 |
43 | public bool IsValid()
44 | {
45 | return _documentRange.IsValid();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Highlighting/TemplateIsNotCompileTimeConstantWarning.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Feature.Services.Daemon;
3 | using JetBrains.ReSharper.Psi.CSharp;
4 | using JetBrains.ReSharper.Psi.CSharp.Tree;
5 | using JetBrains.ReSharper.Psi.Tree;
6 |
7 | using ReSharper.Structured.Logging.Settings;
8 |
9 | namespace ReSharper.Structured.Logging.Highlighting
10 | {
11 | [RegisterConfigurableSeverity(
12 | SeverityId,
13 | null,
14 | StructuredLoggingGroup.Id,
15 | Message,
16 | Message,
17 | Severity.WARNING)]
18 | [ConfigurableSeverityHighlighting(
19 | SeverityId,
20 | CSharpLanguage.Name,
21 | OverlapResolve = OverlapResolveKind.WARNING,
22 | ToolTipFormatString = Message)]
23 | public class TemplateIsNotCompileTimeConstantWarning : IHighlighting
24 | {
25 | public const string SeverityId = "TemplateIsNotCompileTimeConstantProblem";
26 |
27 | private const string Message = "Message template should be compile time constant";
28 |
29 | public TemplateIsNotCompileTimeConstantWarning(
30 | IInvocationExpression invocationExpression,
31 | ICSharpArgument messageTemplateArgument)
32 | {
33 | InvocationExpression = invocationExpression;
34 | MessageTemplateArgument = messageTemplateArgument;
35 | }
36 |
37 | public IInvocationExpression InvocationExpression { get; }
38 | public ICSharpArgument MessageTemplateArgument { get; }
39 |
40 | public string ErrorStripeToolTip => ToolTip;
41 |
42 | public string ToolTip => Message;
43 |
44 | public DocumentRange CalculateRange()
45 | {
46 | return MessageTemplateArgument.Expression.GetDocumentRange();
47 | }
48 |
49 | public bool IsValid()
50 | {
51 | return MessageTemplateArgument.IsValid();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Models/MessageTemplateTokenInformation.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DocumentModel;
2 | using JetBrains.ReSharper.Psi.Tree;
3 | using JetBrains.ReSharper.Psi.Util;
4 |
5 | namespace ReSharper.Structured.Logging.Models
6 | {
7 | public class MessageTemplateTokenInformation
8 | {
9 | public MessageTemplateTokenInformation(
10 | DocumentRange documentRange,
11 | IStringLiteralAlterer stringLiteral)
12 | {
13 | DocumentRange = documentRange;
14 | StringLiteral = stringLiteral;
15 | }
16 |
17 | public DocumentRange DocumentRange { get; }
18 |
19 | public IStringLiteralAlterer StringLiteral { get; }
20 |
21 | public int RelativeStartIndex => DocumentRange.StartOffset - StringLiteral.Expression.GetDocumentRange().StartOffset - 1;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/QuickFixes/AddDestructuringToMessageTemplatePropertyFix.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using JetBrains.Annotations;
4 | using JetBrains.Application.Progress;
5 | using JetBrains.ProjectModel;
6 | using JetBrains.ReSharper.Feature.Services.QuickFixes;
7 | using JetBrains.ReSharper.Psi.CSharp;
8 | using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
9 | using JetBrains.ReSharper.Psi.Util;
10 | using JetBrains.ReSharper.Resources.Shell;
11 | using JetBrains.TextControl;
12 | using JetBrains.Util;
13 |
14 | using ReSharper.Structured.Logging.Highlighting;
15 | using ReSharper.Structured.Logging.Models;
16 |
17 | namespace ReSharper.Structured.Logging.QuickFixes
18 | {
19 | [QuickFix]
20 | public class AddDestructuringToMessageTemplatePropertyFix : QuickFixBase
21 | {
22 | private readonly MessageTemplateTokenInformation _tokenInformation;
23 |
24 | public AddDestructuringToMessageTemplatePropertyFix([NotNull] AnonymousObjectDestructuringWarning error)
25 | {
26 | _tokenInformation = error.TokenInformation;
27 | }
28 |
29 | public AddDestructuringToMessageTemplatePropertyFix([NotNull] ComplexObjectDestructuringWarning error)
30 | {
31 | _tokenInformation = error.TokenInformation;
32 | }
33 |
34 | public override string Text => "Add destructuring to property";
35 |
36 | public override bool IsAvailable(IUserDataHolder cache)
37 | {
38 | return _tokenInformation.DocumentRange.IsValid();
39 | }
40 |
41 | protected override Action ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
42 | {
43 | using (WriteLockCookie.Create())
44 | {
45 | var factory = CSharpElementFactory.GetInstance(_tokenInformation.StringLiteral.Expression, false);
46 | var startIndex = _tokenInformation.RelativeStartIndex;
47 | var expression = factory.CreateExpression(
48 | $"\"{_tokenInformation.StringLiteral.Expression.GetUnquotedText().Insert(startIndex + 1, "@")}\"");
49 |
50 | // ReSharper disable once AssignNullToNotNullAttribute
51 | ModificationUtil.ReplaceChild(_tokenInformation.StringLiteral.Expression, expression);
52 | }
53 |
54 | return null;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/QuickFixes/RemoveTrailingPeriodFix.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 |
4 | using JetBrains.Annotations;
5 | using JetBrains.Application.Progress;
6 | using JetBrains.ProjectModel;
7 | using JetBrains.ReSharper.Feature.Services.QuickFixes;
8 | using JetBrains.ReSharper.Psi.CSharp;
9 | using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
10 | using JetBrains.ReSharper.Psi.Tree;
11 | using JetBrains.ReSharper.Psi.Util;
12 | using JetBrains.ReSharper.Resources.Shell;
13 | using JetBrains.TextControl;
14 | using JetBrains.Util;
15 |
16 | using ReSharper.Structured.Logging.Highlighting;
17 |
18 | namespace ReSharper.Structured.Logging.QuickFixes
19 | {
20 | [QuickFix]
21 | public class RemoveTrailingPeriodFix : ScopedQuickFixBase
22 | {
23 | private readonly IStringLiteralAlterer _stringLiteral;
24 |
25 | private readonly Regex _regex;
26 |
27 | public RemoveTrailingPeriodFix([NotNull] LogMessageIsSentenceWarning error)
28 | {
29 | _stringLiteral = error.StringLiteral;
30 | _regex = error.Regex;
31 | }
32 |
33 | public override string Text => "Remove period";
34 |
35 | public override bool IsAvailable(IUserDataHolder cache)
36 | {
37 | return _stringLiteral.Expression.GetDocumentRange().IsValid();
38 | }
39 |
40 | ///
41 | protected override ITreeNode TryGetContextTreeNode()
42 | {
43 | return _stringLiteral.Expression;
44 | }
45 |
46 | protected override Action ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
47 | {
48 | using (WriteLockCookie.Create())
49 | {
50 | var factory = CSharpElementFactory.GetInstance(_stringLiteral.Expression, false);
51 | var expression = factory.CreateExpression(
52 | $"\"{_regex.Replace(_stringLiteral.Expression.GetUnquotedText(), string.Empty)}\"");
53 |
54 | ModificationUtil.ReplaceChild(_stringLiteral.Expression, expression);
55 | }
56 |
57 | return null;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/QuickFixes/RenameContextLogPropertyFix.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using JetBrains.Annotations;
4 | using JetBrains.Application.Progress;
5 | using JetBrains.ProjectModel;
6 | using JetBrains.ReSharper.Feature.Services.QuickFixes;
7 | using JetBrains.ReSharper.Psi.CSharp;
8 | using JetBrains.ReSharper.Psi.CSharp.Tree;
9 | using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
10 | using JetBrains.ReSharper.Resources.Shell;
11 | using JetBrains.TextControl;
12 | using JetBrains.Util;
13 |
14 | using ReSharper.Structured.Logging.Highlighting;
15 |
16 | namespace ReSharper.Structured.Logging.QuickFixes
17 | {
18 | [QuickFix]
19 | public class RenameContextLogPropertyFix : QuickFixBase
20 | {
21 | private readonly ICSharpArgument _argument;
22 |
23 | private readonly string _suggestedName;
24 |
25 | public RenameContextLogPropertyFix([NotNull] InconsistentContextLogPropertyNamingWarning error)
26 | {
27 | _suggestedName = error.SuggestedName;
28 | _argument = error.Argument;
29 | }
30 |
31 | public override string Text => $"Rename property to '{_suggestedName}'";
32 |
33 | public override bool IsAvailable(IUserDataHolder cache)
34 | {
35 | return _argument.IsValid();
36 | }
37 |
38 | protected override Action ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
39 | {
40 | using (WriteLockCookie.Create())
41 | {
42 | // ReSharper disable once AssignNullToNotNullAttribute
43 | var factory = CSharpElementFactory.GetInstance(_argument.Expression, false);
44 |
45 | var expression = factory.CreateExpression($"\"{_suggestedName}\"");
46 | ModificationUtil.ReplaceChild(_argument.Expression, expression);
47 | }
48 |
49 | return null;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/QuickFixes/RenameLogPropertyFix.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using JetBrains.Annotations;
4 | using JetBrains.Application.Progress;
5 | using JetBrains.ProjectModel;
6 | using JetBrains.ReSharper.Feature.Services.QuickFixes;
7 | using JetBrains.ReSharper.Psi.CSharp;
8 | using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
9 | using JetBrains.ReSharper.Psi.Util;
10 | using JetBrains.ReSharper.Resources.Shell;
11 | using JetBrains.TextControl;
12 | using JetBrains.Util;
13 |
14 | using ReSharper.Structured.Logging.Highlighting;
15 | using ReSharper.Structured.Logging.Models;
16 | using ReSharper.Structured.Logging.Serilog.Parsing;
17 |
18 | namespace ReSharper.Structured.Logging.QuickFixes
19 | {
20 | [QuickFix]
21 | public class RenameLogPropertyFix : QuickFixBase
22 | {
23 | private readonly PropertyToken _namedProperty;
24 |
25 | private readonly MessageTemplateTokenInformation _tokenInformation;
26 |
27 | private readonly string _suggestedName;
28 |
29 | public RenameLogPropertyFix([NotNull] InconsistentLogPropertyNamingWarning error)
30 | {
31 | _namedProperty = error.NamedProperty;
32 | _suggestedName = error.SuggestedName;
33 | _tokenInformation = error.TokenInformation;
34 | }
35 |
36 | public override string Text => $"Rename property to '{_suggestedName}'";
37 |
38 | public override bool IsAvailable(IUserDataHolder cache)
39 | {
40 | return _tokenInformation.DocumentRange.IsValid();
41 | }
42 |
43 | protected override Action ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
44 | {
45 | using (WriteLockCookie.Create())
46 | {
47 | var factory = CSharpElementFactory.GetInstance(_tokenInformation.StringLiteral.Expression, false);
48 | var relativeStartIndex = _tokenInformation.RelativeStartIndex;
49 | var startIndex = _namedProperty.Destructuring == Destructuring.Default
50 | ? relativeStartIndex + 1
51 | : relativeStartIndex + 2;
52 | var length = _namedProperty.Destructuring == Destructuring.Default
53 | ? _namedProperty.Length - 2
54 | : _namedProperty.Length - 3;
55 |
56 | var expression = factory.CreateExpression(
57 | $"\"{_tokenInformation.StringLiteral.Expression.GetUnquotedText().Remove(startIndex, length).Insert(startIndex, _suggestedName)}\"");
58 | ModificationUtil.ReplaceChild(_tokenInformation.StringLiteral.Expression, expression);
59 | }
60 |
61 | return null;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/ReSharper.Structured.Logging.Rider.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net472
4 | false
5 | latest
6 |
7 |
8 |
9 | true
10 | false
11 | ReSharper.Structured.Logging
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/ReSharper.Structured.Logging.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net472
4 | false
5 | latest
6 |
7 |
8 | false
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | bin\$(MSBuildProjectName)\$(Configuration)\
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Serilog/Core/IMessageTemplateParser.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2015 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using ReSharper.Structured.Logging.Serilog.Events;
16 |
17 | namespace ReSharper.Structured.Logging.Serilog.Core
18 | {
19 | public interface IMessageTemplateParser
20 | {
21 | MessageTemplate Parse(string messageTemplate);
22 | }
23 | }
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Serilog/Events/MessageTemplate.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2015 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 | using System.Collections.Generic;
17 | using System.Linq;
18 |
19 | using ReSharper.Structured.Logging.Serilog.Parsing;
20 |
21 | namespace ReSharper.Structured.Logging.Serilog.Events
22 | {
23 | ///
24 | /// Represents a message template passed to a log method. The template
25 | /// can subsequently render the template in textual form given the list
26 | /// of properties.
27 | ///
28 | public class MessageTemplate
29 | {
30 | ///
31 | /// Represents the empty message template.
32 | ///
33 | public static MessageTemplate Empty { get; } = new MessageTemplate(Enumerable.Empty());
34 |
35 | readonly MessageTemplateToken[] _tokens;
36 |
37 | ///
38 | /// Construct a message template using manually-defined text and property tokens.
39 | ///
40 | /// The text and property tokens defining the template.
41 | public MessageTemplate(IEnumerable tokens)
42 | // ReSharper disable PossibleMultipleEnumeration
43 | : this(string.Join("", tokens), tokens)
44 | // ReSharper enable PossibleMultipleEnumeration
45 | {
46 | }
47 |
48 | ///
49 | /// Construct a message template using manually-defined text and property tokens.
50 | ///
51 | /// The full text of the template; used by Serilog internally to avoid unneeded
52 | /// string concatenation.
53 | /// The text and property tokens defining the template.
54 | public MessageTemplate(string text, IEnumerable tokens)
55 | {
56 | if (text == null) throw new ArgumentNullException(nameof(text));
57 | if (tokens == null) throw new ArgumentNullException(nameof(tokens));
58 |
59 | Text = text;
60 | _tokens = tokens.ToArray();
61 |
62 | var propertyTokens = GetElementsOfTypeToArray(_tokens);
63 | if (propertyTokens.Length != 0)
64 | {
65 | var allPositional = true;
66 | var anyPositional = false;
67 | foreach (var propertyToken in propertyTokens)
68 | {
69 | if (propertyToken.IsPositional)
70 | anyPositional = true;
71 | else
72 | allPositional = false;
73 | }
74 |
75 | if (allPositional)
76 | {
77 | PositionalProperties = propertyTokens;
78 | }
79 | else
80 | {
81 | if (anyPositional)
82 | IsMixedTemplate = true;
83 |
84 | NamedProperties = propertyTokens;
85 | }
86 | }
87 | }
88 |
89 | ///
90 | /// Similar to , but faster.
91 | ///
92 | static TResult[] GetElementsOfTypeToArray(MessageTemplateToken[] tokens)
93 | where TResult: class
94 | {
95 | var result = new List(tokens.Length / 2);
96 | for (var i = 0; i < tokens.Length; i++)
97 | {
98 | var token = tokens[i] as TResult;
99 | if (token != null)
100 | {
101 | result.Add(token);
102 | }
103 | }
104 | return result.ToArray();
105 | }
106 |
107 | ///
108 | /// The raw text describing the template.
109 | ///
110 | public string Text { get; }
111 |
112 | ///
113 | /// Render the template as a string.
114 | ///
115 | /// The string representation of the template.
116 | public override string ToString()
117 | {
118 | return Text;
119 | }
120 |
121 | ///
122 | /// The tokens parsed from the template.
123 | ///
124 | public IEnumerable Tokens => _tokens;
125 |
126 | internal MessageTemplateToken[] TokenArray => _tokens;
127 |
128 | internal PropertyToken[] NamedProperties { get; }
129 |
130 | internal PropertyToken[] PositionalProperties { get; }
131 |
132 | public bool IsMixedTemplate { get; }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Serilog/Parsing/Alignment.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2015 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace ReSharper.Structured.Logging.Serilog.Parsing
16 | {
17 | ///
18 | /// A structure representing the alignment settings to apply when rendering a property.
19 | ///
20 | public struct Alignment
21 | {
22 | ///
23 | /// Initializes a new instance of .
24 | ///
25 | /// The text alignment direction.
26 | /// The width of the text, in characters.
27 | public Alignment(AlignmentDirection direction, int width)
28 | {
29 | Direction = direction;
30 | Width = width;
31 | }
32 |
33 | ///
34 | /// The text alignment direction.
35 | ///
36 | public AlignmentDirection Direction { get; }
37 |
38 | ///
39 | /// The width of the text.
40 | ///
41 | public int Width { get; }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Serilog/Parsing/AlignmentDirection.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2015 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace ReSharper.Structured.Logging.Serilog.Parsing
16 | {
17 | ///
18 | /// Defines the direction of the alignment.
19 | ///
20 | public enum AlignmentDirection
21 | {
22 | ///
23 | /// Text will be left-aligned.
24 | ///
25 | Left,
26 | ///
27 | /// Text will be right-aligned.
28 | ///
29 | Right
30 | }
31 | }
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Serilog/Parsing/Destructuring.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2015 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace ReSharper.Structured.Logging.Serilog.Parsing
16 | {
17 | ///
18 | /// Instructs the logger on how to store information about provided
19 | /// parameters.
20 | ///
21 | public enum Destructuring
22 | {
23 | ///
24 | /// Convert known types and objects to scalars, arrays to sequences.
25 | ///
26 | Default,
27 |
28 | ///
29 | /// Convert all types to scalar strings. Prefix name with '$'.
30 | ///
31 | Stringify,
32 |
33 | ///
34 | /// Convert known types to scalars, destructure objects and collections
35 | /// into sequences and structures. Prefix name with '@'.
36 | ///
37 | Destructure
38 | }
39 | }
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Serilog/Parsing/MessageTemplateToken.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2015 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace ReSharper.Structured.Logging.Serilog.Parsing
16 | {
17 | ///
18 | /// An element parsed from a message template string.
19 | ///
20 | public abstract class MessageTemplateToken
21 | {
22 | ///
23 | /// Construct a .
24 | ///
25 | /// The token's start index in the template.
26 | protected MessageTemplateToken(int startIndex)
27 | {
28 | StartIndex = startIndex;
29 | }
30 |
31 | ///
32 | /// The token's start index in the template.
33 | ///
34 | // ReSharper disable once UnusedAutoPropertyAccessor.Global
35 | public int StartIndex { get; }
36 |
37 | ///
38 | /// The token's length.
39 | ///
40 | public abstract int Length { get; }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Serilog/Parsing/TextToken.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2013-2015 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 |
17 | namespace ReSharper.Structured.Logging.Serilog.Parsing
18 | {
19 | ///
20 | /// A message template token representing literal text.
21 | ///
22 | public sealed class TextToken : MessageTemplateToken
23 | {
24 | ///
25 | /// Construct a .
26 | ///
27 | /// The text of the token.
28 | /// The token's start index in the template.
29 | ///
30 | public TextToken(string text, int startIndex = -1) : base(startIndex)
31 | {
32 | Text = text ?? throw new ArgumentNullException(nameof(text));
33 | }
34 |
35 | ///
36 | /// The token's length.
37 | ///
38 | public override int Length => Text.Length;
39 |
40 | ///
41 | /// Determines whether the specified is equal to the current .
42 | ///
43 | ///
44 | /// true if the specified object is equal to the current object; otherwise, false.
45 | ///
46 | /// The object to compare with the current object. 2
47 | public override bool Equals(object obj)
48 | {
49 | var tt = obj as TextToken;
50 | return tt != null && tt.Text == Text;
51 | }
52 |
53 | ///
54 | /// Serves as a hash function for a particular type.
55 | ///
56 | ///
57 | /// A hash code for the current .
58 | ///
59 | /// 2
60 | public override int GetHashCode() => Text.GetHashCode();
61 |
62 | ///
63 | /// Returns a string that represents the current object.
64 | ///
65 | ///
66 | /// A string that represents the current object.
67 | ///
68 | /// 2
69 | public override string ToString() => Text;
70 |
71 | ///
72 | /// The text of the token.
73 | ///
74 | public string Text { get; }
75 | }
76 | }
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Settings/PropertyNamingType.cs:
--------------------------------------------------------------------------------
1 | namespace ReSharper.Structured.Logging.Settings
2 | {
3 | public enum PropertyNamingType
4 | {
5 | PascalCase,
6 |
7 | CamelCase,
8 |
9 | SnakeCase,
10 |
11 | ///
12 | /// The elastic naming convention.
13 | ///
14 | ///
15 | /// https://www.elastic.co/guide/en/beats/devguide/current/event-conventions.html
16 | ///
17 | ElasticNaming
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Settings/StructuredLoggingGroup.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.Feature.Services.Daemon;
2 |
3 | namespace ReSharper.Structured.Logging.Settings
4 | {
5 | [RegisterConfigurableHighlightingsGroup(Id, Name)]
6 | public static class StructuredLoggingGroup
7 | {
8 | public const string Id = "StructuredLogging";
9 |
10 | private const string Name = "Structured Logging Misuse";
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Settings/StructuredLoggingOptionsPage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using JetBrains.Application.Settings;
4 | using JetBrains.Application.UI.Options;
5 | using JetBrains.Application.UI.Options.OptionPages;
6 | using JetBrains.Application.UI.Options.OptionsDialog;
7 | using JetBrains.IDE.UI.Extensions;
8 | using JetBrains.IDE.UI.Options;
9 | using JetBrains.Lifetimes;
10 | using JetBrains.ReSharper.Feature.Services.Resources;
11 |
12 | namespace ReSharper.Structured.Logging.Settings
13 | {
14 | [OptionsPage(Pid, "Structured Logging", typeof(FeaturesEnvironmentOptionsThemedIcons.StringFormat), ParentId = EnvironmentPage.Pid)]
15 | public class StructuredLoggingOptionsPage : BeSimpleOptionsPage
16 | {
17 | private const string Pid = "StructuredLogging";
18 |
19 | public StructuredLoggingOptionsPage(
20 | Lifetime lifetime,
21 | OptionsPageContext optionsPageContext,
22 | OptionsSettingsSmartContext optionsSettingsSmartContext,
23 | bool wrapInScrollablePanel = false)
24 | : base(lifetime, optionsPageContext, optionsSettingsSmartContext, wrapInScrollablePanel)
25 | {
26 | AddHeader("Log properties naming style");
27 | AddComboOptionFromEnum(
28 | (StructuredLoggingSettings settings) => settings.PropertyNamingType,
29 | type =>
30 | {
31 | switch (type)
32 | {
33 | case PropertyNamingType.PascalCase:
34 | return "PascalCase";
35 | case PropertyNamingType.CamelCase:
36 | return "camelCase";
37 | case PropertyNamingType.SnakeCase:
38 | return "snake_case";
39 | case PropertyNamingType.ElasticNaming:
40 | return "elastic.naming";
41 | default:
42 | throw new ArgumentOutOfRangeException(nameof(type), type, null);
43 | }
44 | });
45 |
46 | AddHeader("Ignored properties naming");
47 | AddCommentText("You may specify a regular expression here and if a property matches this expression, then it will be skipped during naming analysis.");
48 | var ignoredRegex = OptionsSettingsSmartContext
49 | .GetValueProperty(lifetime, StructuredLoggingSettingsAccessor.IgnoredPropertiesRegex);
50 | AddControl(ignoredRegex.GetBeTextBox(lifetime));
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Settings/StructuredLoggingSettings.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Settings;
2 | using JetBrains.Application.Settings.WellKnownRootKeys;
3 |
4 | namespace ReSharper.Structured.Logging.Settings
5 | {
6 | [SettingsKey(typeof(EnvironmentSettings), "Settings for Structured Logging")]
7 | public class StructuredLoggingSettings
8 | {
9 | [SettingsEntry(PropertyNamingType.PascalCase, "Properties naming case")]
10 | public PropertyNamingType PropertyNamingType { get; set; }
11 |
12 | [SettingsEntry("", "Ignored properties RegEx")]
13 | public string IgnoredPropertiesRegex { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Settings/StructuredLoggingSettingsAccessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 |
4 | using JetBrains.Annotations;
5 |
6 | namespace ReSharper.Structured.Logging.Settings
7 | {
8 | public static class StructuredLoggingSettingsAccessor
9 | {
10 | [NotNull]
11 | public static readonly Expression> PropertyNamingType = x => x.PropertyNamingType;
12 |
13 | [NotNull]
14 | public static readonly Expression> IgnoredPropertiesRegex = x => x.IgnoredPropertiesRegex;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Utils/PropertyNameProvider.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Annotations;
2 | using JetBrains.Application.Settings;
3 | using JetBrains.Util;
4 |
5 | using ReSharper.Structured.Logging.Settings;
6 |
7 | namespace ReSharper.Structured.Logging.Services;
8 |
9 | public static class PropertyNameProvider
10 | {
11 | public static string GetSuggestedName([NotNull]string propertyName, [CanBeNull]IContextBoundSettingsStore settingsStore)
12 | {
13 | var namingType = settingsStore
14 | ?.GetValue(StructuredLoggingSettingsAccessor.PropertyNamingType)
15 | ?? PropertyNamingType.PascalCase;
16 |
17 | switch (namingType)
18 | {
19 | case PropertyNamingType.PascalCase:
20 | return StringUtil.MakeUpperCamelCaseName(propertyName);
21 | case PropertyNamingType.CamelCase:
22 | return StringUtil.MakeUpperCamelCaseName(propertyName).Decapitalize();
23 | case PropertyNamingType.SnakeCase:
24 | return StringUtil.MakeUnderscoreCaseName(propertyName);
25 | case PropertyNamingType.ElasticNaming:
26 | return StringUtil.MakeUnderscoreCaseName(propertyName).Replace('_', '.');
27 | default:
28 | return propertyName;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/Wiki/StructuredLoggingWikiDataProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | using JetBrains.Application;
4 | using JetBrains.Application.Parts;
5 | using JetBrains.ReSharper.Feature.Services.Explanatory;
6 |
7 | using ReSharper.Structured.Logging.Highlighting;
8 |
9 | namespace ReSharper.Structured.Logging.Wiki
10 | {
11 | [ShellComponent(Instantiation.DemandAnyThreadSafe)]
12 | public class StructuredLoggingWikiDataProvider : ICodeInspectionWikiDataProvider
13 | {
14 | private static readonly IDictionary AttributeUrlMap = new Dictionary
15 | {
16 | {
17 | DuplicateTemplatePropertyWarning.SeverityId,
18 | CreateSeverityUrl(DuplicateTemplatePropertyWarning.SeverityId)
19 | },
20 | {
21 | ExceptionPassedAsTemplateArgumentWarning.SeverityId,
22 | CreateSeverityUrl(ExceptionPassedAsTemplateArgumentWarning.SeverityId)
23 | },
24 | {
25 | TemplateIsNotCompileTimeConstantWarning.SeverityId,
26 | CreateSeverityUrl(TemplateIsNotCompileTimeConstantWarning.SeverityId)
27 | },
28 | {
29 | AnonymousObjectDestructuringWarning.SeverityId,
30 | CreateSeverityUrl(AnonymousObjectDestructuringWarning.SeverityId)
31 | },
32 | {
33 | ContextualLoggerWarning.SeverityId,
34 | CreateSeverityUrl(ContextualLoggerWarning.SeverityId)
35 | },
36 | {
37 | ComplexObjectDestructuringWarning.SeverityId,
38 | CreateSeverityUrl(ComplexObjectDestructuringWarning.SeverityId)
39 | },
40 | {
41 | PositionalPropertyUsedWarning.SeverityId,
42 | CreateSeverityUrl(PositionalPropertyUsedWarning.SeverityId)
43 | },
44 | {
45 | InconsistentLogPropertyNamingWarning.SeverityId,
46 | CreateSeverityUrl(InconsistentLogPropertyNamingWarning.SeverityId)
47 | },
48 | {
49 | LogMessageIsSentenceWarning.SeverityId,
50 | CreateSeverityUrl(LogMessageIsSentenceWarning.SeverityId)
51 | },
52 | {
53 | ComplexObjectDestructuringInContextWarning.SeverityId,
54 | CreateSeverityUrl(ComplexObjectDestructuringInContextWarning.SeverityId)
55 | },
56 | {
57 | InconsistentContextLogPropertyNamingWarning.SeverityId,
58 | CreateSeverityUrl(InconsistentContextLogPropertyNamingWarning.SeverityId)
59 | }
60 | };
61 |
62 | public bool TryGetValue(string attributeId, out string url)
63 | {
64 | return AttributeUrlMap.TryGetValue(attributeId, out url);
65 | }
66 |
67 | private static string CreateSeverityUrl(string severityId)
68 | {
69 | return $"https://github.com/olsh/resharper-structured-logging/blob/master/rules/{severityId}.md";
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/ZoneMarker.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.BuildScript.Application.Zones;
2 | using JetBrains.ReSharper.Psi.CSharp;
3 |
4 | namespace ReSharper.Structured.Logging
5 | {
6 | [ZoneMarker]
7 | public class ZoneMarker : IRequire
8 | {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/ReSharper.Structured.Logging/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/com/jetbrains/rider/settings/StructuredLoggingBundle.kt:
--------------------------------------------------------------------------------
1 | package com.jetbrains.rider.settings
2 |
3 | import com.intellij.DynamicBundle
4 | import org.jetbrains.annotations.Nls
5 | import org.jetbrains.annotations.NonNls
6 | import org.jetbrains.annotations.PropertyKey
7 |
8 | class StructuredLoggingBundle : DynamicBundle(BUNDLE) {
9 | companion object {
10 | @NonNls
11 | private const val BUNDLE = "messages.StructuredLoggingBundle"
12 | private val INSTANCE: StructuredLoggingBundle = StructuredLoggingBundle()
13 |
14 | @Nls
15 | fun message(
16 | @PropertyKey(resourceBundle = BUNDLE) key: String,
17 | vararg params: Any
18 | ): String {
19 | return INSTANCE.getMessage(key, *params)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/com/jetbrains/rider/settings/StructuredLoggingPluginOptionsPage.kt:
--------------------------------------------------------------------------------
1 | package com.jetbrains.rider.settings
2 |
3 | import com.jetbrains.rider.settings.simple.SimpleOptionsPage
4 | import com.jetbrains.rider.settings.StructuredLoggingBundle
5 |
6 | class StructuredLoggingPluginOptionsPage : SimpleOptionsPage(
7 | name = StructuredLoggingBundle.message("configurable.name.structuredlogging.title"),
8 | pageId = "StructuredLogging")
9 | {
10 | override fun getId(): String {
11 | return "StructuredLogging"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/rider/main/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | com.intellij.resharper.StructuredLogging
3 | Structured Logging
4 |
7 | For more information visit the project repository.
8 |
9 |
10 |
11 | Release notes
12 | ]]>
13 | https://github.com/olsh/resharper-structured-logging/releases
14 |
15 | Oleg Shevchenko
16 |
17 | messages.StructuredLoggingBundle
18 |
19 |
25 |
26 | com.intellij.modules.rider
27 |
28 |
--------------------------------------------------------------------------------
/src/rider/main/resources/messages/StructuredLoggingBundle.properties:
--------------------------------------------------------------------------------
1 | configurable.name.structuredlogging.title=Structured Logging
2 |
--------------------------------------------------------------------------------
/test/data/Analyzers/AnonymousTypeDestructure/SerilogWithComplexPropertyWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using System;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{MyProperty}", new { Test = 1, Complex = new Random() });
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/AnonymousTypeDestructure/SerilogWithComplexPropertyWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using System;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("|{MyProperty}|(0)", new { Test = 1, Complex = new Random() });
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 | (0): ReSharper Warning: Anonymous objects must be destructured
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/AnonymousTypeDestructure/SerilogWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{MyProperty}", new { Test = 1 });
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/AnonymousTypeDestructure/SerilogWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("|{MyProperty}|(0)", new { Test = 1 });
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 | (0): ReSharper Warning: Anonymous objects must be destructured
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogContextExplicitDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 | using Serilog.Context;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | LogContext.PushProperty("Test", new Random(), true);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogContextExplicitDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 | using Serilog.Context;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | LogContext.PushProperty("Test", new Random(), true);
12 | }
13 | }
14 | }
15 |
16 | ---------------------------------------------------------
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogContextNumericWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 | using Serilog.Context;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | LogContext.PushProperty("Test", 1);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogContextNumericWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 | using Serilog.Context;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | LogContext.PushProperty("Test", 1);
12 | }
13 | }
14 | }
15 |
16 | ---------------------------------------------------------
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogContextWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 | using Serilog.Context;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | LogContext.PushProperty("Test", new Random());
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogContextWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 | using Serilog.Context;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | |LogContext.PushProperty("Test", new Random())|(0);
12 | }
13 | }
14 | }
15 |
16 | ---------------------------------------------------------
17 | (0): ReSharper Warning: Complex objects with default ToString() implementation probably need to be destructured
18 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogCustomExceptionWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Error(new MyException(), "{MyProperty}", new Random());
11 | }
12 | }
13 |
14 | public class MyException : Exception
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogCustomExceptionWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Error(new MyException(), "|{MyProperty}|(0)", new Random());
11 | }
12 | }
13 |
14 | public class MyException : Exception
15 | {
16 | }
17 | }
18 |
19 | ---------------------------------------------------------
20 | (0): ReSharper Warning: Complex objects with default ToString() implementation probably need to be destructured
21 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogDictionaryWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Serilog;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | Log.Logger.Information("{$MyProperty}", new Dictionary());
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogDictionaryWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Serilog;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | Log.Logger.Information("{$MyProperty}", new Dictionary());
12 | }
13 | }
14 | }
15 |
16 | ---------------------------------------------------------
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogEnumerableWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Serilog;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | IEnumerable list = new List() { "test" };
12 | Log.Logger.Information("{MyProperty}", list);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogEnumerableWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Serilog;
4 |
5 | namespace ConsoleApp
6 | {
7 | public static class Program
8 | {
9 | public static void Main()
10 | {
11 | IEnumerable list = new List() { "test" };
12 | Log.Logger.Information("{MyProperty}", list);
13 | }
14 | }
15 | }
16 |
17 | ---------------------------------------------------------
18 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogForceStringWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{$MyProperty}", new Random());
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogForceStringWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{$MyProperty}", new Random());
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogNullableWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | int? a = 1;
11 | Log.Logger.Information("{$MyProperty}", a);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogNullableWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | int? a = 1;
11 | Log.Logger.Information("{$MyProperty}", a);
12 | }
13 | }
14 | }
15 |
16 | ---------------------------------------------------------
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogNumericWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{$MyProperty}", 3);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogNumericWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{$MyProperty}", 3);
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogParentWithOverriddenToString.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{MyProperty}", new B());
11 | }
12 | }
13 |
14 | public class A
15 | {
16 | public override string ToString() => "Custom ToString";
17 | }
18 |
19 | public class B: A { }
20 | }
21 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogParentWithOverriddenToString.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{MyProperty}", new B());
11 | }
12 | }
13 |
14 | public class A
15 | {
16 | public override string ToString() => "Custom ToString";
17 | }
18 |
19 | public class B: A { }
20 | }
21 |
22 | ---------------------------------------------------------
23 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogWithoutDestructure.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{MyProperty}", new Random());
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ComplexTypeDestructure/SerilogWithoutDestructure.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("|{MyProperty}|(0)", new Random());
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 | (0): ReSharper Warning: Complex objects with default ToString() implementation probably need to be destructured
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerConstructor/MicrosoftCorrectContextType.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | class A
4 | {
5 | ILogger _log;
6 |
7 | public A(ILogger log)
8 | {
9 | _log = log;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerConstructor/MicrosoftCorrectContextType.cs.gold:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | class A
4 | {
5 | ILogger _log;
6 |
7 | public A(ILogger log)
8 | {
9 | _log = log;
10 | }
11 | }
12 |
13 | ---------------------------------------------------------
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextType.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | class A
4 | {
5 | ILogger _log;
6 |
7 | public A(ILogger log)
8 | {
9 | _log = log;
10 | }
11 | }
12 |
13 | class B { }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextType.cs.gold:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | class A
4 | {
5 | ILogger _log;
6 |
7 | public A(|ILogger|(0) log)
8 | {
9 | _log = log;
10 | }
11 | }
12 |
13 | class B { }
14 |
15 | ---------------------------------------------------------
16 | (0): ReSharper Warning: Incorrect type is used for contextual logger
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextTypeMultipleNamespaces.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | namespace X
4 | {
5 | class A { }
6 | }
7 |
8 | namespace Y
9 | {
10 | class A
11 | {
12 | ILogger _log;
13 |
14 | public A(ILogger log)
15 | {
16 | _log = log;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextTypeMultipleNamespaces.cs.gold:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | namespace X
4 | {
5 | class A { }
6 | }
7 |
8 | namespace Y
9 | {
10 | class A
11 | {
12 | ILogger _log;
13 |
14 | public A(|ILogger|(0) log)
15 | {
16 | _log = log;
17 | }
18 | }
19 | }
20 |
21 | ---------------------------------------------------------
22 | (0): ReSharper Warning: Incorrect type is used for contextual logger
23 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextTypeMultipleParameters.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | class A
4 | {
5 | ILogger _log;
6 |
7 | public A(int a, ILogger log)
8 | {
9 | _log = log;
10 | }
11 | }
12 |
13 | class B { }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextTypeMultipleParameters.cs.gold:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | class A
4 | {
5 | ILogger _log;
6 |
7 | public A(int a, |ILogger|(0) log)
8 | {
9 | _log = log;
10 | }
11 | }
12 |
13 | class B { }
14 |
15 | ---------------------------------------------------------
16 | (0): ReSharper Warning: Incorrect type is used for contextual logger
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerSerilogFactory/SerilogCorrectContextType.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | class A
4 | {
5 | private static readonly ILogger Logger = Logger.ForContext();
6 | }
7 |
8 | class B {}
9 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerSerilogFactory/SerilogCorrectContextType.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | class A
4 | {
5 | private static readonly ILogger Logger = Logger.ForContext();
6 | }
7 |
8 | class B {}
9 |
10 | ---------------------------------------------------------
11 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerSerilogFactory/SerilogWrongContextType.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | class A
4 | {
5 | private static readonly ILogger Logger = Logger.ForContext();
6 | }
7 |
8 | class B {}
9 |
--------------------------------------------------------------------------------
/test/data/Analyzers/ContextualLoggerSerilogFactory/SerilogWrongContextType.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | class A
4 | {
5 | private static readonly ILogger Logger = |Logger.ForContext()|(0);
6 | }
7 |
8 | class B {}
9 |
10 | ---------------------------------------------------------
11 | (0): ReSharper Warning: Incorrect type is used for contextual logger
12 |
--------------------------------------------------------------------------------
/test/data/Analyzers/CorrectExceptionPassing/SerilogCorrectExceptionPassing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information(new Exception(), "{One}", 1);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/CorrectExceptionPassing/SerilogCorrectExceptionPassing.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information(new Exception(), "{One}", 1);
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/CorrectExceptionPassing/SerilogIncorrectExceptionPassing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{One} {Exc}", 1, new Exception());
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/CorrectExceptionPassing/SerilogIncorrectExceptionPassing.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{One} {Exc}", 1, |new Exception()|(0));
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 | (0): ReSharper Warning: Exception should be passed to the exception argument
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/CorrectExceptionPassing/SerilogIncorrectExceptionPassingDynamicTemplate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information($"{DateTime.Now} {{Error}}", new Exception());
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/CorrectExceptionPassing/SerilogIncorrectExceptionPassingDynamicTemplate.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information($"{DateTime.Now} |{{|(0)Error|}}|(1)", |new Exception()|(2));
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 | (0): ReSharper C# Escape Character 1:
17 | (1): ReSharper C# Escape Character 1:
18 | (2): ReSharper Warning: Exception should be passed to the exception argument
19 |
--------------------------------------------------------------------------------
/test/data/Analyzers/CorrectExceptionPassing/SerilogMultipleExceptionPassing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information(new Exception(), "{One} {OtherException}", 1, new Exception());
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/CorrectExceptionPassing/SerilogMultipleExceptionPassing.cs.gold:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information(new Exception(), "{One} {OtherException}", 1, new Exception());
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/DuplicatePropertiesTemplate/SerilogDuplicateNamedProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{Test} {Test}", 1, 2);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/DuplicatePropertiesTemplate/SerilogDuplicateNamedProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("|{Test}|(0) |{Test}|(1)", 1, 2);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 | (0): ReSharper Warning: Duplicate properties in message template
16 | (1): ReSharper Warning: Duplicate properties in message template
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/LogMessageIsSentence/SerilogNotSentenceMessage.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Loading {Name}...", "World");
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/LogMessageIsSentence/SerilogNotSentenceMessage.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Loading {Name}...", "World");
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/LogMessageIsSentence/SerilogSentenceMessage.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Hello {Name}.", "World");
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/LogMessageIsSentence/SerilogSentenceMessage.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information(|"Hello {Name}."|(0), "World");
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 | (0): ReSharper Warning: Log event messages should be fragments, not sentences. Avoid a trailing period/full stop.
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PositionalPropertiesUsage/SerilogPositionProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{0}", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PositionalPropertiesUsage/SerilogPositionProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("|{0}|(0)", 1);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 | (0): ReSharper Warning: Prefer named properties instead of positional ones
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogContextInterpolatedStringProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using Serilog.Context;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | var s = "world";
11 | LogContext.PushProperty($"Hello{s}", 1);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogContextInterpolatedStringProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using Serilog.Context;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | var s = "world";
11 | LogContext.PushProperty($"Hello{s}", 1);
12 | }
13 | }
14 | }
15 |
16 | ---------------------------------------------------------
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogContextInvalidNamedProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using Serilog.Context;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | LogContext.PushProperty("test", 1);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogContextInvalidNamedProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using Serilog.Context;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | LogContext.PushProperty(|"test"|(0), 1);
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 | (0): ReSharper Warning: Property name 'test' does not match naming rules. Suggested name is 'Test'.
17 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogIgnoredInvalidNamedProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{MY_IGNORED.Property_}", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogIgnoredInvalidNamedProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{MY_IGNORED.Property_}", 1);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidElasticNamedProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{myProperty}", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidElasticNamedProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("|{myProperty}|(0)", 1);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 | (0): ReSharper Warning: Property name 'myProperty' does not match naming rules. Suggested name is 'my.property'.
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{myProperty}", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("|{myProperty}|(0)", 1);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 | (0): ReSharper Warning: Property name 'myProperty' does not match naming rules. Suggested name is 'MyProperty'.
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedPropertyWithDot.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{My.Property}", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedPropertyWithDot.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("|{My.Property}|(0)", 1);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 | (0): ReSharper Warning: Property name 'My.Property' does not match naming rules. Suggested name is 'MyProperty'.
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedPropertyWithSpace.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{My Property}", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedPropertyWithSpace.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("|{My Property}|(0)", 1);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 | (0): ReSharper Warning: Property name 'My Property' does not match naming rules. Suggested name is 'MyProperty'.
16 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidSyntax.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information(%"{MyProperty}", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidSyntax.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information(%"{MyProperty}", 1);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogValidDestructuredNamedProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{@MyProperty}", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogValidDestructuredNamedProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{@MyProperty}", 1);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogValidNamedProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{MyProperty}", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzer/SerilogValidNamedProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{MyProperty}", 1);
10 | }
11 | }
12 | }
13 |
14 | ---------------------------------------------------------
15 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzerDotNet6/ZLoggerInvalidNamedProperty.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using ZLogger;
3 |
4 | namespace ConsoleApp
5 | {
6 | class A
7 | {
8 | public A(ILogger log)
9 | {
10 | log.ZLogInformation("{myProperty}", 1);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/Analyzers/PropertiesNamingAnalyzerDotNet6/ZLoggerInvalidNamedProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using ZLogger;
3 |
4 | namespace ConsoleApp
5 | {
6 | class A
7 | {
8 | public A(ILogger log)
9 | {
10 | log.ZLogInformation("|{myProperty}|(0)", 1);
11 | }
12 | }
13 | }
14 |
15 | ---------------------------------------------------------
16 | (0): ReSharper Warning: Property name 'myProperty' does not match naming rules. Suggested name is 'MyProperty'.
17 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/AddDestructuringFix/SerilogEscapedString.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Escaped \r\n {MyPro{caret}perty} \r\n string", new { Test = 1 });
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/AddDestructuringFix/SerilogEscapedString.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Escaped \r\n {@MyPro{caret}perty} \r\n string", new { Test = 1 });
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/AddDestructuringFix/SerilogNewAnonymousObject.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{MyPro{caret}perty}", new { Test = 1 });
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/AddDestructuringFix/SerilogNewAnonymousObject.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("{@MyPro{caret}perty}", new { Test = 1 });
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/AddDestructuringFix/SerilogNewComplexObject.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using System;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{MyPro{caret}perty}", new Random());
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/AddDestructuringFix/SerilogNewComplexObject.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using System;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | Log.Logger.Information("{@MyPro{caret}perty}", new Random());
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RemoveTrailingPeriodFix/SerilogTrailingPeriod.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Test {caret}{Property} prop.", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RemoveTrailingPeriodFix/SerilogTrailingPeriod.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Test {caret}{Property} prop", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RenameContextLogPropertyFix/SerilogContextProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using Serilog.Context;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | LogContext.PushProperty("{caret}test", 1);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RenameContextLogPropertyFix/SerilogContextProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using Serilog.Context;
3 |
4 | namespace ConsoleApp
5 | {
6 | public static class Program
7 | {
8 | public static void Main()
9 | {
10 | LogContext.PushProperty("T{caret}est", 1);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RenameLogPropertyFix/SerilogDestructuredProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Test {@my{caret}Property} prop", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RenameLogPropertyFix/SerilogDestructuredProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Test {@My{caret}Property} prop", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RenameLogPropertyFix/SerilogProperty.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Test {my{caret}Property} prop", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RenameLogPropertyFix/SerilogProperty.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Test {My{caret}Property} prop", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RenameLogPropertyFix/SerilogPropertyConcatenated.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Test" + " {my{caret}Property} prop", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/QuickFixes/RenameLogPropertyFix/SerilogPropertyConcatenated.cs.gold:
--------------------------------------------------------------------------------
1 | using Serilog;
2 |
3 | namespace ConsoleApp
4 | {
5 | public static class Program
6 | {
7 | public static void Main()
8 | {
9 | Log.Logger.Information("Test" + " {My{caret}Property} prop", 1);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/data/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/test/src/Analyzer/AnonymousTypeDestructureAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace ReSharper.Structured.Logging.Tests.Analyzer
4 | {
5 | public class AnonymousTypeDestructureAnalyzerTests : MessageTemplateAnalyzerTestBase
6 | {
7 | protected override string SubPath => "AnonymousTypeDestructure";
8 |
9 | [Test] public void TestSerilogWithoutDestructure() => DoNamedTest2();
10 |
11 | [Test] public void TestSerilogWithComplexPropertyWithoutDestructure() => DoNamedTest2();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/src/Analyzer/ComplexObjectDestructureAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace ReSharper.Structured.Logging.Tests.Analyzer
4 | {
5 | public class ComplexObjectDestructureAnalyzerTests : MessageTemplateAnalyzerTestBase
6 | {
7 | protected override string SubPath => "ComplexTypeDestructure";
8 |
9 | [Test] public void TestSerilogWithoutDestructure() => DoNamedTest2();
10 |
11 | [Test] public void TestSerilogForceStringWithoutDestructure() => DoNamedTest2();
12 |
13 | [Test] public void TestSerilogNumericWithoutDestructure() => DoNamedTest2();
14 |
15 | [Test] public void TestSerilogEnumerableWithoutDestructure() => DoNamedTest2();
16 |
17 | [Test] public void TestSerilogNullableWithoutDestructure() => DoNamedTest2();
18 |
19 | [Test] public void TestSerilogDictionaryWithoutDestructure() => DoNamedTest2();
20 |
21 | [Test] public void TestSerilogContextWithoutDestructure() => DoNamedTest2();
22 |
23 | [Test] public void TestSerilogContextNumericWithoutDestructure() => DoNamedTest2();
24 |
25 | [Test] public void TestSerilogContextExplicitDestructure() => DoNamedTest2();
26 |
27 | [Test] public void TestSerilogCustomExceptionWithoutDestructure() => DoNamedTest2();
28 |
29 | [Test] public void TestSerilogParentWithOverriddenToString() => DoNamedTest2();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/src/Analyzer/ContextualLoggerConstructorAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.TestFramework;
2 |
3 | using NUnit.Framework;
4 |
5 | namespace ReSharper.Structured.Logging.Tests.Analyzer
6 | {
7 | [TestNet60]
8 | public class ContextualLoggerConstructorAnalyzerTests : MessageTemplateAnalyzerTestBase
9 | {
10 | protected override string SubPath => "ContextualLoggerConstructor";
11 |
12 | [Test] public void TestMicrosoftCorrectContextType() => DoNamedTest2();
13 |
14 | [Test] public void TestMicrosoftWrongContextType() => DoNamedTest2();
15 |
16 | [Test] public void TestMicrosoftWrongContextTypeMultipleNamespaces() => DoNamedTest2();
17 |
18 | [Test] public void TestMicrosoftWrongContextTypeMultipleParameters() => DoNamedTest2();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/src/Analyzer/ContextualLoggerSerilogFactoryAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace ReSharper.Structured.Logging.Tests.Analyzer
4 | {
5 | public class ContextualLoggerSerilogFactoryAnalyzerTests : MessageTemplateAnalyzerTestBase
6 | {
7 | protected override string SubPath => "ContextualLoggerSerilogFactory";
8 |
9 | [Test] public void TestSerilogCorrectContextType() => DoNamedTest2();
10 |
11 | [Test] public void TestSerilogWrongContextType() => DoNamedTest2();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/src/Analyzer/CorrectExceptionPassingAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace ReSharper.Structured.Logging.Tests.Analyzer
4 | {
5 | public class CorrectExceptionPassingAnalyzerTests : MessageTemplateAnalyzerTestBase
6 | {
7 | protected override string SubPath => "CorrectExceptionPassing";
8 |
9 | [Test] public void TestSerilogCorrectExceptionPassing() => DoNamedTest2();
10 |
11 | [Test] public void TestSerilogIncorrectExceptionPassing() => DoNamedTest2();
12 |
13 | [Test] public void TestSerilogIncorrectExceptionPassingDynamicTemplate() => DoNamedTest2();
14 |
15 | [Test] public void TestSerilogMultipleExceptionPassing() => DoNamedTest2();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/src/Analyzer/DuplicatePropertiesTemplateAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace ReSharper.Structured.Logging.Tests.Analyzer
4 | {
5 | public class DuplicatePropertiesTemplateAnalyzerTests : MessageTemplateAnalyzerTestBase
6 | {
7 | protected override string SubPath => "DuplicatePropertiesTemplate";
8 |
9 | [Test] public void TestSerilogDuplicateNamedProperty() => DoNamedTest2();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/test/src/Analyzer/LogMessageIsSentenceAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace ReSharper.Structured.Logging.Tests.Analyzer
4 | {
5 | public class LogMessageIsSentenceAnalyzerTests : MessageTemplateAnalyzerTestBase
6 | {
7 | protected override string SubPath => "LogMessageIsSentence";
8 |
9 | [Test] public void TestSerilogSentenceMessage() => DoNamedTest2();
10 |
11 | [Test] public void TestSerilogNotSentenceMessage() => DoNamedTest2();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/src/Analyzer/MessageTemplateTests.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Annotations;
2 | using JetBrains.Application.Settings;
3 | using JetBrains.ReSharper.Daemon.StringAnalysis;
4 | using JetBrains.ReSharper.Feature.Services.Daemon;
5 | using JetBrains.ReSharper.FeaturesTestFramework.Daemon;
6 | using JetBrains.ReSharper.Psi;
7 | using JetBrains.ReSharper.TestFramework;
8 |
9 | using ReSharper.Structured.Logging.Highlighting;
10 | using ReSharper.Structured.Logging.Tests.Constants;
11 |
12 | namespace ReSharper.Structured.Logging.Tests.Analyzer
13 | {
14 | [TestPackages(
15 | NugetPackages.SerilogNugetPackage,
16 | NugetPackages.MicrosoftLoggingPackage,
17 | NugetPackages.NlogLoggingPackage,
18 | Inherits = true)]
19 | public abstract class MessageTemplateAnalyzerTestBase : CSharpHighlightingTestBase
20 | {
21 | protected abstract string SubPath { get; }
22 |
23 | protected override string RelativeTestDataPath => @"Analyzers\" + SubPath;
24 |
25 | protected override void DoTestSolution([NotNull] params string[] fileSet)
26 | {
27 | ExecuteWithinSettingsTransaction(
28 | settingsStore =>
29 | {
30 | RunGuarded(() => MutateSettings(settingsStore));
31 | base.DoTestSolution(fileSet);
32 | });
33 | }
34 |
35 | protected override bool HighlightingPredicate(
36 | IHighlighting highlighting,
37 | IPsiSourceFile sourceFile,
38 | IContextBoundSettingsStore settingsStore)
39 | {
40 | return highlighting is TemplateFormatStringNonExistingArgumentWarning
41 | || highlighting is StringEscapeCharacterHighlighting
42 | || highlighting is DuplicateTemplatePropertyWarning
43 | || highlighting is AnonymousObjectDestructuringWarning
44 | || highlighting is ContextualLoggerWarning
45 | || highlighting is ExceptionPassedAsTemplateArgumentWarning
46 | || highlighting is ComplexObjectDestructuringWarning
47 | || highlighting is ComplexObjectDestructuringInContextWarning
48 | || highlighting is PositionalPropertyUsedWarning
49 | || highlighting is InconsistentLogPropertyNamingWarning
50 | || highlighting is InconsistentContextLogPropertyNamingWarning
51 | || highlighting is LogMessageIsSentenceWarning;
52 | }
53 |
54 | protected virtual void MutateSettings([NotNull] IContextBoundSettingsStore settingsStore)
55 | {
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/test/src/Analyzer/PositionalPropertiesUsageAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace ReSharper.Structured.Logging.Tests.Analyzer
4 | {
5 | public class PositionalPropertiesUsageAnalyzerTests : MessageTemplateAnalyzerTestBase
6 | {
7 | protected override string SubPath => "PositionalPropertiesUsage";
8 |
9 | [Test] public void TestSerilogPositionProperty() => DoNamedTest2();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/test/src/Analyzer/PropertiesElasticNamingAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Settings;
2 |
3 | using NUnit.Framework;
4 |
5 | using ReSharper.Structured.Logging.Settings;
6 |
7 | namespace ReSharper.Structured.Logging.Tests.Analyzer
8 | {
9 | // ReSharper disable once TestFileNameWarning
10 | public class PropertiesElasticNamingAnalyzerTests : MessageTemplateAnalyzerTestBase
11 | {
12 | protected override string SubPath => "PropertiesNamingAnalyzer";
13 |
14 | [Test] public void TestSerilogInvalidElasticNamedProperty() => DoNamedTest2();
15 |
16 | protected override void MutateSettings(IContextBoundSettingsStore settingsStore)
17 | {
18 | settingsStore.SetValue(settings => settings.PropertyNamingType, PropertyNamingType.ElasticNaming);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/src/Analyzer/PropertiesIgnoredRegexNamingAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Settings;
2 |
3 | using NUnit.Framework;
4 |
5 | using ReSharper.Structured.Logging.Settings;
6 |
7 | namespace ReSharper.Structured.Logging.Tests.Analyzer
8 | {
9 | // ReSharper disable once TestFileNameWarning
10 | public class PropertiesIgnoredRegexNamingAnalyzerTests : MessageTemplateAnalyzerTestBase
11 | {
12 | protected override string SubPath => "PropertiesNamingAnalyzer";
13 |
14 | [Test] public void TestSerilogIgnoredInvalidNamedProperty() => DoNamedTest2();
15 |
16 | protected override void MutateSettings(IContextBoundSettingsStore settingsStore)
17 | {
18 | settingsStore.SetValue(settings => settings.IgnoredPropertiesRegex,"MY_.*");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/src/Analyzer/PropertiesNamingAnalyzerDotNet6Tests.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.TestFramework;
2 |
3 | using NUnit.Framework;
4 |
5 | using ReSharper.Structured.Logging.Tests.Constants;
6 |
7 | namespace ReSharper.Structured.Logging.Tests.Analyzer
8 | {
9 | [TestNet60]
10 | [TestPackages(
11 | NugetPackages.ZLoggerLoggingPackage,
12 | Inherits = true)]
13 | public class PropertiesNamingAnalyzerDotNet6Tests : MessageTemplateAnalyzerTestBase
14 | {
15 | protected override string SubPath => "PropertiesNamingAnalyzerDotNet6";
16 |
17 | [Test] public void TestZLoggerInvalidNamedProperty() => DoNamedTest2();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/src/Analyzer/PropertiesNamingAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace ReSharper.Structured.Logging.Tests.Analyzer
4 | {
5 | public class PropertiesNamingAnalyzerTests : MessageTemplateAnalyzerTestBase
6 | {
7 | protected override string SubPath => "PropertiesNamingAnalyzer";
8 |
9 | [Test] public void TestSerilogInvalidNamedProperty() => DoNamedTest2();
10 |
11 | [Test] public void TestSerilogValidNamedProperty() => DoNamedTest2();
12 |
13 | [Test] public void TestSerilogValidDestructuredNamedProperty() => DoNamedTest2();
14 |
15 | [Test] public void TestSerilogContextInvalidNamedProperty() => DoNamedTest2();
16 |
17 | [Test] public void TestSerilogContextInterpolatedStringProperty() => DoNamedTest2();
18 |
19 | [Test] public void TestSerilogInvalidNamedPropertyWithDot() => DoNamedTest2();
20 |
21 | [Test] public void TestSerilogInvalidSyntax() => DoNamedTest2();
22 |
23 | [Test] public void TestSerilogInvalidNamedPropertyWithSpace() => DoNamedTest2();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/src/Constants/NugetPackages.cs:
--------------------------------------------------------------------------------
1 | namespace ReSharper.Structured.Logging.Tests.Constants
2 | {
3 | internal static class NugetPackages
4 | {
5 | public const string SerilogNugetPackage = "Serilog/2.7.1";
6 |
7 | public const string MicrosoftLoggingPackage = "Microsoft.Extensions.Logging/6.0.0";
8 |
9 | public const string NlogLoggingPackage = "NLog/4.5.11";
10 |
11 | public const string ZLoggerLoggingPackage = "ZLogger/1.7.0";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/src/QuickFixes/AddDestructuringToMessageTemplatePropertyFixTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using ReSharper.Structured.Logging.QuickFixes;
3 |
4 | namespace ReSharper.Structured.Logging.Tests.QuickFixes
5 | {
6 | public class AddDestructuringToMessageTemplatePropertyFixTests : QuickFixTestBase
7 | {
8 | protected override string SubPath => "AddDestructuringFix";
9 |
10 | [Test] public void TestSerilogEscapedString() => DoNamedTest2();
11 |
12 | [Test] public void TestSerilogNewAnonymousObject() => DoNamedTest2();
13 |
14 | [Test] public void TestSerilogNewComplexObject() => DoNamedTest2();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/src/QuickFixes/QuickFixTestBase.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.Feature.Services.QuickFixes;
2 | using JetBrains.ReSharper.FeaturesTestFramework.Intentions;
3 | using JetBrains.ReSharper.TestFramework;
4 |
5 | using NUnit.Framework;
6 |
7 | using ReSharper.Structured.Logging.Tests.Constants;
8 |
9 | namespace ReSharper.Structured.Logging.Tests.QuickFixes
10 | {
11 | [TestFixture]
12 | [TestNetFramework46]
13 | [TestPackages(
14 | NugetPackages.SerilogNugetPackage,
15 | NugetPackages.MicrosoftLoggingPackage,
16 | NugetPackages.NlogLoggingPackage,
17 | Inherits = true)]
18 |
19 | // ReSharper disable once TestClassNameSuffixWarning
20 | public abstract class QuickFixTestBase : CSharpQuickFixTestBase
21 | where TQuickFix : IQuickFix
22 | {
23 | protected override string RelativeTestDataPath => @"QuickFixes\" + SubPath;
24 |
25 | protected abstract string SubPath { get; }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/src/QuickFixes/RemoveTrailingPeriodFixTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | using ReSharper.Structured.Logging.QuickFixes;
4 |
5 | namespace ReSharper.Structured.Logging.Tests.QuickFixes
6 | {
7 | public class RemoveTrailingPeriodFixTests : QuickFixTestBase
8 | {
9 | protected override string SubPath => "RemoveTrailingPeriodFix";
10 |
11 | [Test] public void TestSerilogTrailingPeriod() => DoNamedTest2();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/src/QuickFixes/RenameContextLogPropertyFixTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | using ReSharper.Structured.Logging.QuickFixes;
4 |
5 | namespace ReSharper.Structured.Logging.Tests.QuickFixes
6 | {
7 | public class RenameContextLogPropertyFixTests : QuickFixTestBase
8 | {
9 | protected override string SubPath => "RenameContextLogPropertyFix";
10 |
11 | [Test] public void TestSerilogContextProperty() => DoNamedTest2();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/src/QuickFixes/RenameLogPropertyFixTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | using ReSharper.Structured.Logging.QuickFixes;
4 |
5 | namespace ReSharper.Structured.Logging.Tests.QuickFixes
6 | {
7 | public class RenameLogPropertyFixTests : QuickFixTestBase
8 | {
9 | protected override string SubPath => "RenameLogPropertyFix";
10 |
11 | [Test] public void TestSerilogProperty() => DoNamedTest2();
12 |
13 | [Test] public void TestSerilogDestructuredProperty() => DoNamedTest2();
14 |
15 | [Test] public void TestSerilogPropertyConcatenated() => DoNamedTest2();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/src/ReSharper.Structured.Logging.Rider.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net472
4 | false
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | true
15 | ReSharper.Structured.Logging.Tests
16 |
17 |
18 | RIDER
19 | bin\$(MSBuildProjectName)\$(Configuration)\
20 |
21 |
22 |
--------------------------------------------------------------------------------
/test/src/ReSharper.Structured.Logging.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net472
4 | false
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | bin\$(MSBuildProjectName)\$(Configuration)\
14 |
15 |
16 |
--------------------------------------------------------------------------------
/test/src/TestEnvironment.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.BuildScript.Application.Zones;
2 | using JetBrains.ReSharper.TestFramework;
3 | using JetBrains.TestFramework;
4 | using JetBrains.TestFramework.Application.Zones;
5 |
6 | using NUnit.Framework;
7 |
8 | #if RIDER
9 | using JetBrains.Rider.Backend.Env;
10 | #endif
11 |
12 | [assembly: RequiresThread(System.Threading.ApartmentState.STA)]
13 |
14 | namespace ReSharper.Structured.Logging.Tests
15 | {
16 | [ZoneDefinition]
17 | public interface IReSharperSerilog : ITestsEnvZone, IRequire
18 | #if RIDER
19 | , IRequire
20 | #endif
21 | {
22 | }
23 |
24 | [SetUpFixture]
25 | public class TestEnvironment : ExtensionTestEnvironmentAssembly
26 | {
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test/src/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------