forked from hibernate/hibernate-search
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HSEARCH-798 Add a logging categories collector and put collected cate…
…gories in the appendix
- Loading branch information
1 parent
e31be5e
commit 6249c01
Showing
16 changed files
with
527 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
build/config/src/main/java/org/hibernate/search/build/report/loggers/Configuration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright Red Hat Inc. and Hibernate Authors | ||
*/ | ||
package org.hibernate.search.build.report.loggers; | ||
|
||
public final class Configuration { | ||
|
||
private Configuration() { | ||
} | ||
|
||
public static final String MODULE_NAME = "org.hibernate.search.build.loggers.module_name"; | ||
} |
197 changes: 197 additions & 0 deletions
197
...ig/src/main/java/org/hibernate/search/build/report/loggers/LoggerCategoriesProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright Red Hat Inc. and Hibernate Authors | ||
*/ | ||
package org.hibernate.search.build.report.loggers; | ||
|
||
import java.io.IOException; | ||
import java.io.OutputStreamWriter; | ||
import java.io.Writer; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.TreeMap; | ||
import java.util.TreeSet; | ||
|
||
import javax.annotation.processing.AbstractProcessor; | ||
import javax.annotation.processing.Messager; | ||
import javax.annotation.processing.ProcessingEnvironment; | ||
import javax.annotation.processing.RoundEnvironment; | ||
import javax.annotation.processing.SupportedAnnotationTypes; | ||
import javax.annotation.processing.SupportedOptions; | ||
import javax.annotation.processing.SupportedSourceVersion; | ||
import javax.lang.model.SourceVersion; | ||
import javax.lang.model.element.AnnotationMirror; | ||
import javax.lang.model.element.AnnotationValue; | ||
import javax.lang.model.element.Element; | ||
import javax.lang.model.element.ElementKind; | ||
import javax.lang.model.element.ExecutableElement; | ||
import javax.lang.model.element.TypeElement; | ||
import javax.lang.model.type.TypeKind; | ||
import javax.lang.model.type.TypeMirror; | ||
import javax.tools.Diagnostic; | ||
import javax.tools.FileObject; | ||
import javax.tools.StandardLocation; | ||
|
||
import org.yaml.snakeyaml.Yaml; | ||
|
||
@SupportedAnnotationTypes("org.hibernate.search.util.common.logging.CategorizedLogger") | ||
@SupportedOptions({ Configuration.MODULE_NAME }) | ||
@SupportedSourceVersion(SourceVersion.RELEASE_17) | ||
public class LoggerCategoriesProcessor extends AbstractProcessor { | ||
|
||
private Messager messager; | ||
private final Map<String, Set<String>> categories = new TreeMap<>(); | ||
private String moduleName; | ||
|
||
@Override | ||
public synchronized void init(ProcessingEnvironment processingEnv) { | ||
super.init( processingEnv ); | ||
messager = processingEnv.getMessager(); | ||
|
||
moduleName = processingEnv.getOptions().get( Configuration.MODULE_NAME ); | ||
if ( moduleName == null || moduleName.isBlank() ) { | ||
throw new IllegalArgumentException( | ||
"Module name cannot be null or blank. Specify the %s annotation processor argument to define the module name" | ||
.formatted( Configuration.MODULE_NAME ) ); | ||
} | ||
} | ||
|
||
@Override | ||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { | ||
messager.printMessage( Diagnostic.Kind.NOTE, "Look for logging categories" ); | ||
|
||
for ( TypeElement annotation : annotations ) { | ||
Set<? extends Element> loggers = roundEnv.getElementsAnnotatedWith( annotation ); | ||
for ( Element el : loggers ) { | ||
if ( el.getKind().isInterface() || el.getKind().isClass() ) { | ||
TypeElement logger = (TypeElement) el; | ||
if ( extendsBasicLogger( logger ) || hasLoggingMethods( logger ) ) { | ||
messager.printMessage( Diagnostic.Kind.NOTE, logger.toString() ); | ||
for ( AnnotationMirror mirror : logger.getAnnotationMirrors() ) { | ||
if ( processingEnv.getTypeUtils().isSameType( annotation.asType(), mirror.getAnnotationType() ) ) { | ||
String category = getAnnotationValueAsString( mirror, "category" ); | ||
String description = getAnnotationValueAsString( mirror, "description" ); | ||
|
||
Set<String> descriptions = categories.computeIfAbsent( category, k -> new TreeSet<>() ); | ||
if ( description != null && !description.isBlank() ) { | ||
descriptions.add( description ); | ||
} | ||
else { | ||
messager.printMessage( Diagnostic.Kind.WARNING, | ||
"Logger %s either has some log-message methods or extends a BasicLogger, but does not provide any description on what it is used for." | ||
.formatted( category ) ); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
if ( roundEnv.processingOver() ) { | ||
try { | ||
FileObject report = processingEnv.getFiler().createResource( | ||
StandardLocation.CLASS_OUTPUT, "", "META-INF/hibernate-search/logging-categories.yaml" ); | ||
try ( Writer writer = new OutputStreamWriter( report.openOutputStream(), StandardCharsets.UTF_8 ) ) { | ||
writer.write( "# List of logging categories that this module utilizes.\n" ); | ||
if ( categories.isEmpty() ) { | ||
writer.write( "# This Hibernate Search module does not use any logging categories." ); | ||
} | ||
else { | ||
Yaml yaml = new Yaml(); | ||
yaml.dump( | ||
Map.of( | ||
ReportConstants.ROOT, | ||
Map.of( | ||
ReportConstants.MODULE_NAME, moduleName, | ||
ReportConstants.CATEGORIES, toYamlCategories( categories ) | ||
) | ||
), | ||
writer | ||
); | ||
} | ||
} | ||
catch (IOException e) { | ||
throw new RuntimeException( e ); | ||
} | ||
} | ||
catch (IOException e) { | ||
throw new RuntimeException( e ); | ||
} | ||
} | ||
|
||
|
||
return false; | ||
} | ||
|
||
private List<Map<String, Object>> toYamlCategories(Map<String, Set<String>> categories) { | ||
List<Map<String, Object>> values = new ArrayList<>(); | ||
for ( var entry : categories.entrySet() ) { | ||
Map<String, Object> value = new HashMap<>(); | ||
value.put( ReportConstants.CATEGORY_NAME, entry.getKey() ); | ||
value.put( ReportConstants.CATEGORY_DESCRIPTION, new ArrayList<>( entry.getValue() ) ); | ||
|
||
values.add( value ); | ||
} | ||
return values; | ||
} | ||
|
||
private boolean hasLoggingMethods(TypeElement logger) { | ||
for ( Element element : processingEnv.getElementUtils().getAllMembers( logger ) ) { | ||
if ( element.getKind() == ElementKind.METHOD ) { | ||
ExecutableElement executable = (ExecutableElement) element; | ||
if ( isVoid( executable ) && hasLoggingAnnotation( executable ) ) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
private boolean hasLoggingAnnotation(ExecutableElement executable) { | ||
for ( AnnotationMirror am : executable.getAnnotationMirrors() ) { | ||
if ( ( (TypeElement) am.getAnnotationType().asElement() ).getQualifiedName() | ||
.contentEquals( "org.jboss.logging.annotations.LogMessage" ) ) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
private boolean isVoid(ExecutableElement executable) { | ||
return executable.getReturnType().getKind() == TypeKind.VOID; | ||
} | ||
|
||
private boolean extendsBasicLogger(TypeElement logger) { | ||
List<? extends TypeMirror> interfaces = logger.getInterfaces(); | ||
for ( TypeMirror anInterface : interfaces ) { | ||
Element el = processingEnv.getTypeUtils().asElement( anInterface ); | ||
if ( ( (TypeElement) el ).getQualifiedName().contentEquals( "org.jboss.logging.BasicLogger" ) ) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
private String getAnnotationValueAsString(AnnotationMirror annotationMirror, String name) { | ||
AnnotationValue annotationValue = getAnnotationValue( annotationMirror, name ); | ||
if ( annotationValue == null ) { | ||
return ""; | ||
} | ||
return annotationValue.getValue().toString(); | ||
} | ||
|
||
private AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String name) { | ||
var elementValues = annotationMirror.getElementValues(); | ||
for ( var entry : elementValues.entrySet() ) { | ||
if ( entry.getKey().getSimpleName().contentEquals( name ) ) { | ||
return entry.getValue(); | ||
} | ||
} | ||
return null; | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
...fig/src/main/java/org/hibernate/search/build/report/loggers/LoggerCategoriesReporter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright Red Hat Inc. and Hibernate Authors | ||
*/ | ||
package org.hibernate.search.build.report.loggers; | ||
|
||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.io.OutputStreamWriter; | ||
import java.io.Writer; | ||
import java.net.URL; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.Enumeration; | ||
import java.util.List; | ||
import java.util.Locale; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.TreeMap; | ||
import java.util.TreeSet; | ||
|
||
import org.yaml.snakeyaml.Yaml; | ||
|
||
public class LoggerCategoriesReporter { | ||
|
||
@SuppressWarnings("unchecked") | ||
public static void main(String[] args) throws IOException { | ||
if ( args.length != 1 ) { | ||
throw new IllegalArgumentException( "Has to pass exactly one parameter specifying the report output directory!" ); | ||
} | ||
Path doc = Path.of( args[0] ).resolve( "_hibernate-logging-categories.adoc" ); | ||
Files.createDirectories( doc.getParent() ); | ||
|
||
System.out.printf( Locale.ROOT, "Generating the Logging Categories report into %s\n", doc ); | ||
|
||
Enumeration<URL> reports = | ||
LoggerCategoriesReporter.class.getClassLoader() | ||
.getResources( "META-INF/hibernate-search/logging-categories.yaml" ); | ||
|
||
if ( reports.hasMoreElements() ) { | ||
System.out.printf( Locale.ROOT, "Found some logging categories yaml reports.\n" ); | ||
} | ||
else { | ||
System.out.printf( Locale.ROOT, "Found *NO* logging categories yaml reports.\n" ); | ||
} | ||
|
||
Map<String, Category> report = new TreeMap<>(); | ||
|
||
while ( reports.hasMoreElements() ) { | ||
URL reportUrl = reports.nextElement(); | ||
System.out.printf( "Processing report file: %s\n", reportUrl ); | ||
try ( InputStreamReader reader = | ||
new InputStreamReader( reportUrl.openStream(), StandardCharsets.UTF_8 ) ) { | ||
Yaml yaml = new Yaml(); | ||
Map<String, Object> load = yaml.load( reader ); | ||
|
||
if ( load == null ) { | ||
System.err.printf( "Warning: report %s is empty or invalid\n", reportUrl ); | ||
continue; | ||
} | ||
|
||
Map<String, Object> root = (Map<String, Object>) load.get( ReportConstants.ROOT ); | ||
String moduleName = (String) root.get( ReportConstants.MODULE_NAME ); | ||
List<Map<String, Object>> categories = | ||
(List<Map<String, Object>>) root.get( ReportConstants.CATEGORIES ); | ||
|
||
for ( var category : categories ) { | ||
String name = (String) category.get( ReportConstants.CATEGORY_NAME ); | ||
Category c = report.computeIfAbsent( name, Category::new ); | ||
c.modules.add( moduleName ); | ||
List<String> descr = (List<String>) category.get( ReportConstants.CATEGORY_DESCRIPTION ); | ||
if ( descr != null ) { | ||
c.descriptions.addAll( descr ); | ||
} | ||
} | ||
} | ||
} | ||
|
||
try ( FileOutputStream fos = new FileOutputStream( doc.toFile() ); | ||
Writer writer = new OutputStreamWriter( fos, StandardCharsets.UTF_8 ) ) { | ||
for ( Category category : report.values() ) { | ||
writer.write( "[[logging-category-%s]]`%s`::\n".formatted( category.name.replace( '.', '-' ), category.name ) ); | ||
if ( !category.descriptions.isEmpty() ) { | ||
writer.write( "Description:::\n" ); | ||
for ( String description : category.descriptions ) { | ||
writer.write( "* %s\n".formatted( description ) ); | ||
} | ||
} | ||
writer.write( "Used in modules:::\n" ); | ||
for ( String module : category.modules ) { | ||
writer.write( "* `%s`\n".formatted( module ) ); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private static class Category { | ||
String name; | ||
Set<String> descriptions = new TreeSet<>(); | ||
Set<String> modules = new TreeSet<>(); | ||
|
||
public Category(String name) { | ||
this.name = name; | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
build/config/src/main/java/org/hibernate/search/build/report/loggers/ReportConstants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* Copyright Red Hat Inc. and Hibernate Authors | ||
*/ | ||
package org.hibernate.search.build.report.loggers; | ||
|
||
class ReportConstants { | ||
|
||
static final String ROOT = "org.hibernate.search.report"; | ||
static final String MODULE_NAME = "module"; | ||
static final String CATEGORIES = "categories"; | ||
static final String CATEGORY_NAME = "name"; | ||
static final String CATEGORY_DESCRIPTION = "description"; | ||
} |
1 change: 1 addition & 0 deletions
1
build/config/src/main/resources/META-INF/services/gradle/incremental.annotation.processors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
org.hibernate.search.build.report.loggers.LoggerCategoriesProcessor,isolating |
1 change: 1 addition & 0 deletions
1
build/config/src/main/resources/META-INF/services/javax.annotation.processing.Processor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
org.hibernate.search.build.report.loggers.LoggerCategoriesProcessor |
Oops, something went wrong.