Custom detekt rule for logger usage check
[dcaegen2/collectors/hv-ves.git] / build / hv-collector-analysis / src / main / kotlin / org / onap / dcae / collectors / veshv / analysis / SuboptimalLoggerUsage.kt
1 /*
2  * ============LICENSE_START=======================================================
3  * dcaegen2-collectors-veshv
4  * ================================================================================
5  * Copyright (C) 2018 NOKIA
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.onap.dcae.collectors.veshv.analysis
21
22 import io.gitlab.arturbosch.detekt.api.CodeSmell
23 import io.gitlab.arturbosch.detekt.api.Config
24 import io.gitlab.arturbosch.detekt.api.Debt
25 import io.gitlab.arturbosch.detekt.api.Entity
26 import io.gitlab.arturbosch.detekt.api.Issue
27 import io.gitlab.arturbosch.detekt.api.Rule
28 import io.gitlab.arturbosch.detekt.api.Severity
29 import org.jetbrains.kotlin.com.intellij.psi.PsiElement
30 import org.jetbrains.kotlin.psi.KtCallExpression
31 import org.jetbrains.kotlin.psi.KtOperationExpression
32 import org.jetbrains.kotlin.psi.KtStringTemplateExpression
33 import org.jetbrains.kotlin.psi.KtValueArgumentList
34 import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
35
36 /**
37  * @author Piotr Jaszczyk <piotr.jaszczyk@nokia.com>
38  * @since November 2018
39  */
40 class SuboptimalLoggerUsage(config: Config) : Rule(config) {
41
42     override val issue = Issue(javaClass.simpleName,
43             Severity.Performance,
44             """
45                 Reports usage of unoptimized logger calls.
46                 In Kotlin every method call (including logger calls) is eagerly evaluated by default. That means that
47                 each argument will be evaluated even if loglevel is higher than in current call. The most common way of
48                 mitigating this issue is to use lazy loading by means of lambda expressions, so instead of
49                 log.debug("a=${'$'}a") we can write log.debug{ "a=${'$'}a" }. Logging string literals is fine - no
50                 additional computation will be performed.""".trimIndent(),
51             Debt(mins = 10))
52
53     private val loggerNames = config.valueOrDefault("loggerNames", DEFAULT_LOGGER_NAMES).split(",")
54
55     private val loggingMethods = config.valueOrDefault("loggingMethodNames", DEFAULT_LOGGING_METHOD_NAMES).split(",")
56
57     override fun visitCallExpression(expression: KtCallExpression) {
58         val targetObject = expression.parent.firstChild
59         val methodName = expression.firstChild
60
61         logExpressionArguments(targetObject, methodName)
62                 ?.let(this::checkGettingWarningMessage)
63                 ?.let { reportCodeSmell(expression, it) }
64     }
65
66     private fun logExpressionArguments(targetObject: PsiElement, methodName: PsiElement) =
67             if (isLogExpression(targetObject, methodName))
68                 methodName.nextSibling as? KtValueArgumentList
69             else null
70
71     private fun isLogExpression(targetObject: PsiElement, methodName: PsiElement) =
72             loggerNames.any(targetObject::textMatches) && loggingMethods.any(methodName::textMatches)
73
74     private fun checkGettingWarningMessage(args: KtValueArgumentList) = when {
75         args.anyDescendantOfType<KtOperationExpression> { true } ->
76             "should not use any operators in logging expression"
77         args.anyDescendantOfType<KtCallExpression> { true } ->
78             "should not call anything in logging expression"
79         args.anyDescendantOfType<KtStringTemplateExpression> { it.hasInterpolation() } ->
80             "should not use string interpolation in logging expression"
81         else -> null
82     }
83
84     private fun reportCodeSmell(expression: KtCallExpression, message: String) {
85         report(CodeSmell(issue, Entity.from(expression), message))
86     }
87
88     companion object {
89         const val DEFAULT_LOGGER_NAMES = "logger,LOGGER,log,LOG"
90         const val DEFAULT_LOGGING_METHOD_NAMES = "trace,debug,info,warn,error,severe"
91     }
92 }