[uast-inspections] IDEA-354390 Support structured logging for SLF4J

- don't highlight if last arguments are structured arguments

GitOrigin-RevId: fb0bf20ff547df4e41c5c6a50e89afe29d149f48
This commit is contained in:
Mikhail Pyltsin
2024-06-04 19:12:08 +02:00
committed by intellij-monorepo-bot
parent b5ebbd1b62
commit 71cab9b683
3 changed files with 93 additions and 2 deletions

View File

@@ -8,12 +8,15 @@ import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.options.OptPane
import com.intellij.codeInspection.util.InspectionMessage
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.util.InheritanceUtil
import com.intellij.uast.UastHintedVisitorAdapter
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
private const val STRUCTURED_ARGUMENT_CLASS = "net.logstash.logback.argument.StructuredArgument"
class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLocalInspectionTool() {
@JvmField
var slf4jToLog4J2Type = Slf4jToLog4J2Type.AUTO
@@ -73,12 +76,22 @@ class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLo
if (placeholderCountHolder.count <= finalArgumentCount) ResultType.SUCCESS else ResultType.PARTIAL_PLACE_HOLDER_MISMATCH
}
else {
if (placeholderCountHolder.count == finalArgumentCount) ResultType.SUCCESS else ResultType.PLACE_HOLDER_MISMATCH
if (placeholderCountHolder.count == finalArgumentCount) ResultType.SUCCESS else calculateSlf4jStructureLogging(placeholderCountHolder, context, finalArgumentCount)
}
}
PlaceholderLoggerType.SLF4J_EQUAL_PLACEHOLDERS, PlaceholderLoggerType.LOG4J_EQUAL_PLACEHOLDERS, PlaceholderLoggerType.AKKA_PLACEHOLDERS -> {
if (placeholderCountHolder.status == PlaceholdersStatus.PARTIAL) {
if (placeholderCountHolder.count <= finalArgumentCount) ResultType.SUCCESS else ResultType.PARTIAL_PLACE_HOLDER_MISMATCH
if (placeholderCountHolder.count <= finalArgumentCount) {
ResultType.SUCCESS
}
else {
if (context.loggerType == PlaceholderLoggerType.SLF4J_EQUAL_PLACEHOLDERS) {
calculateSlf4jStructureLogging(placeholderCountHolder, context, finalArgumentCount)
}
else {
ResultType.PARTIAL_PLACE_HOLDER_MISMATCH
}
}
}
else {
if (placeholderCountHolder.count == finalArgumentCount) ResultType.SUCCESS else ResultType.PLACE_HOLDER_MISMATCH
@@ -112,6 +125,31 @@ class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLo
return true
}
/**
* Calculate results for SLF4J if previous calculation failed and check if arguments can be `StructuredArgument`.
* In this case, IDEA shouldn't highlight it because these arguments can be used in json events only
*/
private fun calculateSlf4jStructureLogging(placeholderCountHolder: PlaceholderCountResult, context: PlaceholderContext, finalArgumentCount: Int): ResultType {
if (placeholderCountHolder.count >= finalArgumentCount) {
//by default
return ResultType.PLACE_HOLDER_MISMATCH
}
if (placeholderCountHolder.count < 0 || context.placeholderParameters.size < finalArgumentCount) {
return ResultType.PLACE_HOLDER_MISMATCH
}
for (argumentIndex in (placeholderCountHolder.count)..<finalArgumentCount) {
val argument = context.placeholderParameters.getOrNull(argumentIndex) ?: continue
val argumentType = argument.getExpressionType() ?: continue
if (argumentType.equalsToText(STRUCTURED_ARGUMENT_CLASS) ||
InheritanceUtil.isInheritor(argumentType, STRUCTURED_ARGUMENT_CLASS)) {
continue
}
return ResultType.PLACE_HOLDER_MISMATCH
}
return ResultType.SUCCESS
}
private fun registerProblem(holder: ProblemsHolder, logStringArgument: UExpression, result: Result) {
val errorString = buildErrorString(result)
val anchor = logStringArgument.sourcePsi ?: return

View File

@@ -169,5 +169,16 @@ public interface Logger {
LoggingEventBuilder atTrace();
}
""".trimIndent())
fixture.addClass("""
package net.logstash.logback.argument;
public final class StructuredArguments {
public static StructuredArgument kv(Object... object){
return new StructuredArgument();}
}
""".trimIndent())
fixture.addClass("""
package net.logstash.logback.argument;
public class StructuredArgument{}
""".trimIndent())
}
}

View File

@@ -613,5 +613,47 @@ class JavaLoggingPlaceholderCountMatchesArgumentCountInspectionTest : LoggingPla
}
""".trimIndent())
}
fun `test slf4j structured logging`() {
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static net.logstash.logback.argument.StructuredArguments.kv;
class Demo {
public static final Logger log = LoggerFactory.getLogger(Demo.class);
public void demo() {
log.info(<warning descr="More arguments provided (4) than placeholders specified (2)">"Message1 {} {}"</warning>, 1, //should
kv("k1", "v1"), 2,
kv("k2", "v2")
);
log.info("Message2 {} {}", 1, 2,
kv("k1", "v1"),
kv("k2", "v2")
);
log.info("Message3 {} {}", 1, 2,
kv("k1", "v1"),
kv("k2", "v2"), new RuntimeException());
log.info(<warning descr="More arguments provided (5) than placeholders specified (2)">"Message4 {} {}"</warning>, 1, 2, //should
kv("k1", "v1"),
kv("k2", "v2"), 3, new RuntimeException());
log.info("Message5 {} {}",
kv("k1", "v1"),
kv("k2", "v2")
);
log.atInfo().log("Message6 {} {}", 1,
kv("k1", "v1"),
kv("k2", "v2"));
log.atInfo().log(<warning descr="More arguments provided (4) than placeholders specified (2)">"Message7 {} {}"</warning>, 1, //should
kv("k1", "v1"),
kv("k2", "v2"), 2);
log.atInfo().log(<warning descr="More arguments provided (5) than placeholders specified (2)">"Message8 {} {}"</warning>, 1, 2, 3, //should
kv("k1", "v1"),
kv("k2", "v2"));
}
}
""".trimIndent())
}
}