Skip to content

Commit 1e7c5fa

Browse files
tomekl007olim7t
authored andcommitted
JAVA-2279: Add logs to the generated mapper code
1 parent 49f0183 commit 1e7c5fa

9 files changed

Lines changed: 273 additions & 8 deletions

File tree

manual/mapper/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,22 @@ From the mapper, you can then obtain a DAO instance and execute queries:
152152
ProductDao dao = inventoryMapper.productDao(CqlIdentifier.fromCql("inventory"));
153153
dao.save(new Product(UUID.randomUUID(), "Mechanical keyboard"));
154154
```
155+
156+
### Logging
157+
158+
The code generated by the mapper includes logs. They are issued with SLF4J, and can be configured
159+
the same way as the [core driver logs](../core/logging/).
160+
161+
They can help you figure out which queries the mapper is generating under the hood, for example:
162+
163+
```
164+
DEBUG ProductDaoImpl__MapperGenerated - [s0] Initializing new instance for keyspace = ks_0 and table = null
165+
DEBUG ProductHelper__MapperGenerated - [s0] Entity Product will be mapped to ks_0.product
166+
DEBUG ProductDaoImpl__MapperGenerated - [s0] Preparing query
167+
`SELECT id,description,dimensions FROM ks_0.product WHERE id=:id`
168+
for method findById(java.util.UUID)
169+
```
170+
171+
You can decide which logs to enable using the standard SLF4J mechanisms (categories and levels). In
172+
addition, if you want no logs at all, it's possible to entirely remove them from the generated code
173+
with the Java compiler option `-Acom.datastax.oss.driver.mapper.logs.enabled=false`.

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/DefaultProcessorContext.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.datastax.oss.driver.internal.core.context.DefaultDriverContext;
1919
import com.datastax.oss.driver.internal.core.util.concurrent.CycleDetector;
2020
import com.datastax.oss.driver.internal.core.util.concurrent.LazyReference;
21+
import com.datastax.oss.driver.internal.mapper.processor.dao.LoggingGenerator;
2122
import com.datastax.oss.driver.internal.mapper.processor.entity.DefaultEntityFactory;
2223
import com.datastax.oss.driver.internal.mapper.processor.entity.EntityFactory;
2324
import com.datastax.oss.driver.internal.mapper.processor.util.Classes;
@@ -40,20 +41,24 @@ public class DefaultProcessorContext implements ProcessorContext {
4041
private final DecoratedMessager messager;
4142
private final Types typeUtils;
4243
private final Elements elementUtils;
44+
private boolean logsEnabled;
4345
private final Classes classUtils;
4446
private final JavaPoetFiler filer;
47+
private final LoggingGenerator loggingGenerator;
4548

4649
public DefaultProcessorContext(
4750
DecoratedMessager messager,
4851
Types typeUtils,
4952
Elements elementUtils,
5053
Filer filer,
51-
String indent) {
54+
String indent,
55+
boolean logsEnabled) {
5256
this.messager = messager;
5357
this.typeUtils = typeUtils;
5458
this.elementUtils = elementUtils;
5559
this.classUtils = new Classes(typeUtils, elementUtils);
5660
this.filer = new JavaPoetFiler(filer, indent);
61+
this.loggingGenerator = new LoggingGenerator(logsEnabled);
5762
}
5863

5964
protected CodeGeneratorFactory buildCodeGeneratorFactory() {
@@ -98,4 +103,9 @@ public CodeGeneratorFactory getCodeGeneratorFactory() {
98103
public EntityFactory getEntityFactory() {
99104
return entityFactoryRef.get();
100105
}
106+
107+
@Override
108+
public LoggingGenerator getLoggingGenerator() {
109+
return loggingGenerator;
110+
}
101111
}

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/MapperProcessor.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,20 @@
4040

4141
@AutoService(Processor.class)
4242
public class MapperProcessor extends AbstractProcessor {
43+
private static final boolean DEFAULT_MAPPER_LOGS_ENABLED = true;
4344

4445
private static final String INDENT_AMOUNT_OPTION = "com.datastax.oss.driver.mapper.indent";
4546
private static final String INDENT_WITH_TABS_OPTION =
4647
"com.datastax.oss.driver.mapper.indentWithTabs";
48+
private static final String MAPPER_LOGS_ENABLED_OPTION =
49+
"com.datastax.oss.driver.mapper.logs.enabled";
4750

4851
private DecoratedMessager messager;
4952
private Types typeUtils;
5053
private Elements elementUtils;
5154
private Filer filer;
5255
private String indent;
56+
private boolean logsEnabled;
5357

5458
@Override
5559
public synchronized void init(ProcessingEnvironment processingEnvironment) {
@@ -59,12 +63,22 @@ public synchronized void init(ProcessingEnvironment processingEnvironment) {
5963
elementUtils = processingEnvironment.getElementUtils();
6064
filer = processingEnvironment.getFiler();
6165
indent = computeIndent(processingEnvironment.getOptions());
66+
logsEnabled = isLogsEnabled(processingEnvironment.getOptions());
67+
}
68+
69+
private boolean isLogsEnabled(Map<String, String> options) {
70+
String mapperLogsEnabled = options.get(MAPPER_LOGS_ENABLED_OPTION);
71+
if (mapperLogsEnabled != null) {
72+
return Boolean.parseBoolean(mapperLogsEnabled);
73+
}
74+
return DEFAULT_MAPPER_LOGS_ENABLED;
6275
}
6376

6477
@Override
6578
public boolean process(
6679
Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
67-
ProcessorContext context = buildContext(messager, typeUtils, elementUtils, filer, indent);
80+
ProcessorContext context =
81+
buildContext(messager, typeUtils, elementUtils, filer, indent, logsEnabled);
6882

6983
CodeGeneratorFactory generatorFactory = context.getCodeGeneratorFactory();
7084
processAnnotatedTypes(
@@ -81,8 +95,10 @@ protected ProcessorContext buildContext(
8195
Types typeUtils,
8296
Elements elementUtils,
8397
Filer filer,
84-
String indent) {
85-
return new DefaultProcessorContext(messager, typeUtils, elementUtils, filer, indent);
98+
String indent,
99+
boolean logsEnabled) {
100+
return new DefaultProcessorContext(
101+
messager, typeUtils, elementUtils, filer, indent, logsEnabled);
86102
}
87103

88104
protected void processAnnotatedTypes(
@@ -119,7 +135,8 @@ public Set<String> getSupportedAnnotationTypes() {
119135

120136
@Override
121137
public Set<String> getSupportedOptions() {
122-
return ImmutableSet.of(INDENT_AMOUNT_OPTION, INDENT_WITH_TABS_OPTION);
138+
return ImmutableSet.of(
139+
INDENT_AMOUNT_OPTION, INDENT_WITH_TABS_OPTION, MAPPER_LOGS_ENABLED_OPTION);
123140
}
124141

125142
@Override

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/ProcessorContext.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.datastax.oss.driver.internal.mapper.processor;
1717

18+
import com.datastax.oss.driver.internal.mapper.processor.dao.LoggingGenerator;
1819
import com.datastax.oss.driver.internal.mapper.processor.entity.EntityFactory;
1920
import com.datastax.oss.driver.internal.mapper.processor.util.Classes;
2021
import javax.lang.model.util.Elements;
@@ -36,4 +37,6 @@ public interface ProcessorContext {
3637
CodeGeneratorFactory getCodeGeneratorFactory();
3738

3839
EntityFactory getEntityFactory();
40+
41+
LoggingGenerator getLoggingGenerator();
3942
}

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/dao/DaoImplementationGenerator.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ protected JavaFile.Builder getContents() {
340340
.addParameter(MapperContext.class, "context")
341341
.addStatement("super(context)");
342342

343+
context.getLoggingGenerator().addLoggerField(classBuilder, implementationName);
344+
343345
// For each entity helper that was requested by a method generator, create a field for it and
344346
// add a constructor parameter for it (the instance gets created in initAsync).
345347
for (Map.Entry<ClassName, String> entry : entityHelperFields.entrySet()) {
@@ -401,6 +403,14 @@ private MethodSpec.Builder getInitAsyncContents() {
401403
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
402404
.addParameter(MapperContext.class, "context");
403405

406+
LoggingGenerator loggingGenerator = context.getLoggingGenerator();
407+
loggingGenerator.debug(
408+
initAsyncBuilder,
409+
"[{}] Initializing new instance for keyspace = {} and table = {}",
410+
CodeBlock.of("context.getSession().getName()"),
411+
CodeBlock.of("context.getKeyspaceId()"),
412+
CodeBlock.of("context.getTableId()"));
413+
404414
generateProtocolVersionCheck(initAsyncBuilder);
405415

406416
initAsyncBuilder.beginControlFlow("try");
@@ -431,6 +441,13 @@ private MethodSpec.Builder getInitAsyncContents() {
431441
String simpleStatementName = preparedStatement.fieldName + "_simple";
432442
preparedStatement.simpleStatementGenerator.accept(initAsyncBuilder, simpleStatementName);
433443
// - prepare it asynchronously, store all CompletionStages in a list
444+
loggingGenerator.debug(
445+
initAsyncBuilder,
446+
String.format(
447+
"[{}] Preparing query `{}` for method %s",
448+
preparedStatement.methodElement.toString()),
449+
CodeBlock.of("context.getSession().getName()"),
450+
CodeBlock.of("$L.getQuery()", simpleStatementName));
434451
initAsyncBuilder
435452
.addStatement(
436453
"$T $L = prepare($L, context)",
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright DataStax, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (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+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
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 com.datastax.oss.driver.internal.mapper.processor.dao;
17+
18+
import com.squareup.javapoet.ClassName;
19+
import com.squareup.javapoet.CodeBlock;
20+
import com.squareup.javapoet.FieldSpec;
21+
import com.squareup.javapoet.MethodSpec;
22+
import com.squareup.javapoet.TypeSpec;
23+
import javax.lang.model.element.Modifier;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
public class LoggingGenerator {
28+
private final boolean logsEnabled;
29+
30+
public LoggingGenerator(boolean logsEnabled) {
31+
this.logsEnabled = logsEnabled;
32+
}
33+
34+
/**
35+
* Generates a logger in a constant, such as:
36+
*
37+
* <pre>
38+
* private static final Logger LOG = LoggerFactory.getLogger(Foobar.class);
39+
* </pre>
40+
*
41+
* @param classBuilder where to generate.
42+
* @param className the name of the class ({@code Foobar}).
43+
*/
44+
public void addLoggerField(TypeSpec.Builder classBuilder, ClassName className) {
45+
if (logsEnabled) {
46+
classBuilder.addField(
47+
FieldSpec.builder(
48+
ClassName.get(Logger.class),
49+
"LOG",
50+
Modifier.PRIVATE,
51+
Modifier.FINAL,
52+
Modifier.STATIC)
53+
.initializer("$T.getLogger($T.class)", LoggerFactory.class, className)
54+
.build());
55+
}
56+
}
57+
58+
/**
59+
* Generates a debug log statement, such as:
60+
*
61+
* <pre>
62+
* LOG.debug("setting {} = {}", key, value);
63+
* </pre>
64+
*
65+
* <p>This assumes that {@link #addLoggerField(TypeSpec.Builder, ClassName)} has already been
66+
* called for the class where this is generated.
67+
*
68+
* @param builder where to generate.
69+
* @param template the message ({@code "setting {} = {}"}).
70+
* @param arguments the arguments ({@code key} and {@code value}).
71+
*/
72+
public void debug(MethodSpec.Builder builder, String template, CodeBlock... arguments) {
73+
if (logsEnabled) {
74+
builder.addCode("$[LOG.debug($S", template);
75+
for (CodeBlock argument : arguments) {
76+
builder.addCode(",\n$L", argument);
77+
}
78+
builder.addCode(");$]\n");
79+
}
80+
}
81+
}

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/entity/EntityHelperGenerator.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.datastax.oss.driver.internal.mapper.processor.util.generation.GenericTypeConstantGenerator;
2727
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
2828
import com.squareup.javapoet.ClassName;
29+
import com.squareup.javapoet.CodeBlock;
2930
import com.squareup.javapoet.FieldSpec;
3031
import com.squareup.javapoet.JavaFile;
3132
import com.squareup.javapoet.MethodSpec;
@@ -90,6 +91,8 @@ protected JavaFile.Builder getContents() {
9091
ParameterizedTypeName.get(
9192
ClassName.get(EntityHelperBase.class), ClassName.get(classElement)));
9293

94+
context.getLoggingGenerator().addLoggerField(classContents, helperName);
95+
9396
classContents.addMethod(
9497
MethodSpec.methodBuilder("getEntityClass")
9598
.addModifiers(Modifier.PUBLIC)
@@ -126,6 +129,16 @@ protected JavaFile.Builder getContents() {
126129
entityDefinition.getDefaultKeyspace(),
127130
entityDefinition.getCqlName());
128131
}
132+
context
133+
.getLoggingGenerator()
134+
.debug(
135+
constructorContents,
136+
String.format(
137+
"[{}] Entity %s will be mapped to {}{}",
138+
entityDefinition.getClassName().simpleName()),
139+
CodeBlock.of("context.getSession().getName()"),
140+
CodeBlock.of("getKeyspaceId() == null ? \"\" : getKeyspaceId() + \".\""),
141+
CodeBlock.of("getTableId()"));
129142

130143
genericTypeConstantGenerator.generate(classContents);
131144

mapper-processor/src/test/java/com/datastax/oss/driver/internal/mapper/processor/MapperProcessorTest.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.squareup.javapoet.JavaFile;
2323
import com.squareup.javapoet.TypeSpec;
2424
import java.util.ArrayList;
25+
import java.util.Collections;
2526
import java.util.List;
2627
import javax.tools.JavaFileObject;
2728

@@ -32,14 +33,30 @@ public abstract class MapperProcessorTest {
3233
*
3334
* @param packageName the package of the types to process. Note that it is currently not possible
3435
* to process multiple packages (and it's unlikely to be needed in unit tests).
36+
* @param options the compiler options (use to pass -A options to the processor).
3537
* @param typeSpecs the contents of the classes or interfaces to process.
3638
*/
37-
protected Compilation compileWithMapperProcessor(String packageName, TypeSpec... typeSpecs) {
39+
protected Compilation compileWithMapperProcessor(
40+
String packageName, Iterable<?> options, TypeSpec... typeSpecs) {
3841
List<JavaFileObject> files = new ArrayList<>();
3942
for (TypeSpec typeSpec : typeSpecs) {
4043
files.add(JavaFile.builder(packageName, typeSpec).build().toJavaFileObject());
4144
}
42-
return Compiler.javac().withProcessors(new MapperProcessor()).compile(files);
45+
return Compiler.javac()
46+
.withProcessors(new MapperProcessor())
47+
.withOptions(options)
48+
.compile(files);
49+
}
50+
51+
/**
52+
* Launches an in-process execution of javac with {@link MapperProcessor} enabled.
53+
*
54+
* @param packageName the package of the types to process. Note that it is currently not possible
55+
* to process multiple packages (and it's unlikely to be needed in unit tests).
56+
* @param typeSpecs the contents of the classes or interfaces to process.
57+
*/
58+
protected Compilation compileWithMapperProcessor(String packageName, TypeSpec... typeSpecs) {
59+
return compileWithMapperProcessor(packageName, Collections.emptyList(), typeSpecs);
4360
}
4461

4562
protected void should_fail_with_expected_error(
@@ -55,7 +72,8 @@ protected void should_succeed_with_expected_warning(
5572
}
5673

5774
protected void should_succeed_without_warnings(String packageName, TypeSpec... typeSpecs) {
58-
Compilation compilation = compileWithMapperProcessor(packageName, typeSpecs);
75+
Compilation compilation =
76+
compileWithMapperProcessor(packageName, Collections.emptyList(), typeSpecs);
5977
assertThat(compilation).succeededWithoutWarnings();
6078
}
6179
}

0 commit comments

Comments
 (0)