Skip to content

Commit c59c405

Browse files
pdelagravejevanlingentimtebeekclaude
authored
LoggerLevelArgumentToMethod for JBoss Logging (#243)
* LoggerLevelArgumentToMethod for JBoss Logging * Fixes from review * Polish * Add package-info * Polish * Add to best practices already * Create a separate test for documentation purposes * Add recipe examples documentation Added example documentation for the following recipes: - ArgumentArrayToVarargs - FormattedArgumentsToVMethodRecipes (JBoss) - JBossLoggingBestPractices - LoggerLevelArgumentToMethod (JBoss) These examples demonstrate the transformations performed by each recipe. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Don't change if we can not determine level * Only transfer the whitespace from the first argument --------- Co-authored-by: lingenj <[email protected]> Co-authored-by: Tim te Beek <[email protected]> Co-authored-by: Claude <[email protected]>
1 parent 36a5200 commit c59c405

File tree

5 files changed

+405
-0
lines changed

5 files changed

+405
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.logging.jboss;
17+
18+
import org.jspecify.annotations.Nullable;
19+
import org.openrewrite.ExecutionContext;
20+
import org.openrewrite.Preconditions;
21+
import org.openrewrite.Recipe;
22+
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.internal.ListUtils;
24+
import org.openrewrite.java.JavaIsoVisitor;
25+
import org.openrewrite.java.MethodMatcher;
26+
import org.openrewrite.java.search.UsesMethod;
27+
import org.openrewrite.java.search.UsesType;
28+
import org.openrewrite.java.tree.Expression;
29+
import org.openrewrite.java.tree.J;
30+
import org.openrewrite.java.tree.JavaType;
31+
import org.openrewrite.java.tree.TypeUtils;
32+
33+
import java.util.List;
34+
35+
import static java.util.Objects.requireNonNull;
36+
37+
public class LoggerLevelArgumentToMethod extends Recipe {
38+
private static final MethodMatcher LOG_MATCHER = new MethodMatcher("org.jboss.logging.Logger log(*,*,..)", true);
39+
private static final MethodMatcher LOGF_MATCHER = new MethodMatcher("org.jboss.logging.Logger logf(*,*,..)", true);
40+
private static final MethodMatcher LOGV_MATCHER = new MethodMatcher("org.jboss.logging.Logger logv(*,*,..)", true);
41+
42+
@Override
43+
public String getDisplayName() {
44+
return "Replace JBoss Logging Level arguments with the corresponding eponymous level method calls";
45+
}
46+
47+
@Override
48+
public String getDescription() {
49+
return "Replace calls to `Logger.log(Level, ...)` with the corresponding eponymous level method calls. For example `Logger.log(Level.INFO, ...)` to `Logger.info(...)`.";
50+
}
51+
52+
@Override
53+
public TreeVisitor<?, ExecutionContext> getVisitor() {
54+
TreeVisitor<?, ExecutionContext> preconditions = Preconditions.and(
55+
new UsesType<>("org.jboss.logging.Logger", true),
56+
new UsesType<>("org.jboss.logging.Logger.Level", true),
57+
Preconditions.or(
58+
new UsesMethod<>(LOG_MATCHER),
59+
new UsesMethod<>(LOGF_MATCHER),
60+
new UsesMethod<>(LOGV_MATCHER)
61+
)
62+
);
63+
return Preconditions.check(preconditions, new JavaIsoVisitor<ExecutionContext>() {
64+
@Override
65+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation mi, ExecutionContext ctx) {
66+
J.MethodInvocation m = super.visitMethodInvocation(mi, ctx);
67+
if (!(LOG_MATCHER.matches(m) || LOGF_MATCHER.matches(m) || LOGV_MATCHER.matches(m))) {
68+
return m;
69+
}
70+
List<Expression> args = m.getArguments();
71+
Expression firstArgument = args.get(0);
72+
Expression secondArgument = args.get(1);
73+
74+
String logLevelName;
75+
List<Expression> updatedArguments;
76+
if (TypeUtils.isAssignableTo("org.jboss.logging.Logger.Level", firstArgument.getType())) {
77+
String suffix = "";
78+
if (LOGF_MATCHER.matches(m)) {
79+
suffix = "f";
80+
} else if (LOGV_MATCHER.matches(m)) {
81+
suffix = "v";
82+
}
83+
logLevelName = extractLogLevelName(firstArgument);
84+
if (logLevelName != null) {
85+
logLevelName += suffix;
86+
}
87+
updatedArguments = ListUtils.concat(
88+
(Expression) secondArgument.withPrefix(secondArgument.getPrefix()
89+
.withWhitespace(firstArgument.getPrefix().getWhitespace())),
90+
args.subList(2, args.size()));
91+
} else if (TypeUtils.isAssignableTo("java.lang.String", firstArgument.getType()) &&
92+
TypeUtils.isAssignableTo("org.jboss.logging.Logger.Level", secondArgument.getType()) &&
93+
LOG_MATCHER.matches(m)) { // `logf(String, ..)` and `logv(String, ..)` don't have a logger.level() equivalent
94+
logLevelName = extractLogLevelName(secondArgument);
95+
updatedArguments = ListUtils.filter(args, it -> it != secondArgument);
96+
} else {
97+
return m;
98+
}
99+
100+
// If we can't extract a log level name, we don't change the method call
101+
if (logLevelName == null) {
102+
return m;
103+
}
104+
105+
JavaType.Method updatedMethodType = requireNonNull(m.getMethodType())
106+
.withParameterTypes(ListUtils.filter(m.getMethodType().getParameterTypes(), it -> !TypeUtils.isAssignableTo("org.jboss.logging.Logger.Level", it)))
107+
.withParameterNames(ListUtils.filter(m.getMethodType().getParameterNames(), it -> !"level".equals(it)))
108+
.withName(logLevelName.toLowerCase());
109+
110+
return m
111+
.withArguments(updatedArguments)
112+
.withMethodType(updatedMethodType)
113+
.withName(m.getName().withSimpleName(logLevelName.toLowerCase()));
114+
}
115+
116+
@Nullable
117+
String extractLogLevelName(Expression expression) {
118+
if (expression instanceof J.Identifier) {
119+
J.Identifier identifier = (J.Identifier) expression;
120+
if (identifier.getFieldType() != null &&
121+
identifier.getFieldType().getOwner() instanceof JavaType.Class) {
122+
return identifier.getSimpleName();
123+
}
124+
} else if (expression instanceof J.FieldAccess) {
125+
return ((J.FieldAccess) expression).getSimpleName();
126+
}
127+
return null;
128+
}
129+
}
130+
);
131+
}
132+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
@NullMarked
17+
@NonNullFields
18+
package org.openrewrite.java.logging.jboss;
19+
20+
import org.jspecify.annotations.NullMarked;
21+
import org.openrewrite.internal.lang.NonNullFields;

src/main/resources/META-INF/rewrite/examples.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,29 @@ examples:
359359
language: java
360360
---
361361
type: specs.openrewrite.org/v1beta/example
362+
recipeName: org.openrewrite.java.logging.jboss.LoggerLevelArgumentToMethod
363+
examples:
364+
- description: ''
365+
sources:
366+
- before: |
367+
import org.jboss.logging.Logger;
368+
369+
class Test {
370+
void test(Logger logger, String msg) {
371+
logger.log(Logger.Level.INFO, msg);
372+
}
373+
}
374+
after: |
375+
import org.jboss.logging.Logger;
376+
377+
class Test {
378+
void test(Logger logger, String msg) {
379+
logger.info(msg);
380+
}
381+
}
382+
language: java
383+
---
384+
type: specs.openrewrite.org/v1beta/example
362385
recipeName: org.openrewrite.java.logging.jul.LoggerLevelArgumentToMethodRecipes
363386
examples:
364387
- description: ''

src/main/resources/META-INF/rewrite/jboss.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ tags:
2424
- logging
2525
- jboss
2626
recipeList:
27+
- org.openrewrite.java.logging.jboss.LoggerLevelArgumentToMethod
2728
- org.openrewrite.java.logging.jboss.FormattedArgumentsToVMethodRecipes
2829
- org.openrewrite.java.logging.ArgumentArrayToVarargs

0 commit comments

Comments
 (0)