|
| 1 | +package dev.archunitjava.diagnostics; |
| 2 | + |
| 3 | +import dev.archunitjava.graph.DependencyGraph; |
| 4 | +import dev.archunitjava.importer.ImportResolutionResult; |
| 5 | +import dev.archunitjava.metrics.MetricSample; |
| 6 | +import dev.archunitjava.result.RuleResult; |
| 7 | +import java.util.Collection; |
| 8 | +import java.util.Map; |
| 9 | + |
| 10 | +/** Read-only inspection of existing immutable data; never parses, loads or reevaluates target code. */ |
| 11 | +public final class AnalysisInspection { |
| 12 | + private AnalysisInspection() {} |
| 13 | + |
| 14 | + public static void imports(ImportResolutionResult imports, AnalysisLogging log) { |
| 15 | + if (log.level() == LogLevel.OFF) return; |
| 16 | + log.emit(LogLevel.INFO, "EXTRACT", "Import complete", () -> Map.of( |
| 17 | + "types", "" + imports.model().types().size(), "externalTypes", "" + imports.externalTypes().size(), |
| 18 | + "resources", "" + imports.assembly().selections().size())); |
| 19 | + if (log.enabled(LogLevel.DEBUG)) { |
| 20 | + imports.assembly().selections().forEach(selection -> log.emit(LogLevel.DEBUG, "EXTRACT", |
| 21 | + "Selected resource", () -> Map.of("resource", selection.winner().name(), |
| 22 | + "precedence", "" + selection.winner().precedence()))); |
| 23 | + imports.importedTypes().forEach(type -> log.emit(LogLevel.DEBUG, "EXTRACT", "Imported type", |
| 24 | + () -> Map.of("type", type.winner().binaryName(), "scope", type.lookupScope(), |
| 25 | + "shadowedDefinitions", "" + type.shadowedDefinitions().size()))); |
| 26 | + imports.externalTypes().forEach(type -> log.emit(LogLevel.DEBUG, "EXTRACT", "External type", |
| 27 | + () -> Map.of("type", type.name().binaryName()))); |
| 28 | + } |
| 29 | + imports.assembly().diagnostics().forEach(diagnostic -> log.emit(switch (diagnostic.code()) { |
| 30 | + case DUPLICATE_INPUT, DUPLICATE_RESOURCE, MULTI_RELEASE_ENTRY_IGNORED, |
| 31 | + RESOURCE_EXCLUDED, SYMLINK_SKIPPED -> LogLevel.DEBUG; |
| 32 | + default -> LogLevel.WARN; |
| 33 | + }, "EXTRACT", |
| 34 | + "Input selection diagnostic", () -> Map.of("code", diagnostic.code().name(), |
| 35 | + "input", diagnostic.input(), "context", diagnostic.context().toString()))); |
| 36 | + imports.diagnostics().forEach(diagnostic -> log.emit(LogLevel.WARN, "EXTRACT", |
| 37 | + "Import resolution diagnostic", () -> Map.of("diagnostic", diagnostic.toString()))); |
| 38 | + imports.model().classFileDiagnostics().forEach(diagnostic -> log.emit(LogLevel.ERROR, "EXTRACT", |
| 39 | + "Class file diagnostic", () -> Map.of("diagnostic", diagnostic.toString()))); |
| 40 | + imports.model().diagnostics().forEach(diagnostic -> log.emit(LogLevel.WARN, "EXTRACT", |
| 41 | + "Model diagnostic", () -> Map.of("diagnostic", diagnostic.toString()))); |
| 42 | + } |
| 43 | + |
| 44 | + public static void graph(DependencyGraph graph, AnalysisLogging log) { |
| 45 | + log.emit(LogLevel.INFO, "PROJECT", "Graph ready", () -> Map.of( |
| 46 | + "nodes", "" + graph.nodes().size(), "edges", "" + graph.edges().size())); |
| 47 | + if (!log.enabled(LogLevel.DEBUG)) return; |
| 48 | + graph.nodes().forEach(node -> log.emit(LogLevel.DEBUG, "PROJECT", "Graph node", |
| 49 | + () -> Map.of("node", node.id().stableKey()))); |
| 50 | + graph.edges().forEach(edge -> { |
| 51 | + log.emit(LogLevel.DEBUG, "PROJECT", "Dependency edge", () -> Map.of( |
| 52 | + "origin", edge.origin().stableKey(), "target", edge.target().stableKey(), |
| 53 | + "kind", edge.kind().name(), "evidence", "" + edge.evidence().size())); |
| 54 | + edge.evidence().forEach(evidence -> log.emit(LogLevel.DEBUG, "PROJECT", "Dependency evidence", |
| 55 | + () -> Map.of("origin", edge.origin().stableKey(), "target", edge.target().stableKey(), |
| 56 | + "evidence", evidence.toString()))); |
| 57 | + }); |
| 58 | + } |
| 59 | + |
| 60 | + public static void metrics(Collection<MetricSample> samples, AnalysisLogging log) { |
| 61 | + if (!log.enabled(LogLevel.DEBUG)) return; |
| 62 | + samples.stream().sorted().forEach(sample -> log.emit(LogLevel.DEBUG, "ASSERT", "Metric sample", |
| 63 | + () -> Map.of("subject", sample.subject().stableKey(), "metric", sample.metric().name(), |
| 64 | + "value", sample.amount().stableValue(), "unit", sample.amount().unit().name()))); |
| 65 | + } |
| 66 | + |
| 67 | + public static void result(RuleResult result, AnalysisLogging log) { |
| 68 | + if (log.level() == LogLevel.OFF) return; |
| 69 | + LogLevel level = switch (result.status()) { |
| 70 | + case PASSED -> LogLevel.INFO; |
| 71 | + case FAILED, INCOMPLETE -> LogLevel.ERROR; |
| 72 | + case SKIPPED -> LogLevel.WARN; |
| 73 | + }; |
| 74 | + log.emit(level, "REPORT", "Rule complete", () -> Map.of("rule", result.ruleId(), |
| 75 | + "status", result.status().name(), "violations", "" + result.violations().size())); |
| 76 | + result.diagnostics().forEach(diagnostic -> log.emit( |
| 77 | + switch (diagnostic.severity()) { |
| 78 | + case ERROR -> LogLevel.ERROR; |
| 79 | + case WARNING -> LogLevel.WARN; |
| 80 | + case INFO -> LogLevel.INFO; |
| 81 | + }, |
| 82 | + "REPORT", "Rule diagnostic", () -> Map.of("rule", result.ruleId(), |
| 83 | + "code", diagnostic.code(), "context", diagnostic.context().toString()))); |
| 84 | + if (!log.enabled(LogLevel.DEBUG)) return; |
| 85 | + result.violations().forEach(violation -> { |
| 86 | + log.emit(LogLevel.DEBUG, "REPORT", "Violation", () -> Map.of("rule", result.ruleId(), |
| 87 | + "id", violation.id().value(), "code", violation.code(), |
| 88 | + "subjects", violation.subjects().toString(), "attributes", violation.attributes().toString())); |
| 89 | + violation.evidence().forEach(evidence -> log.emit(LogLevel.DEBUG, "REPORT", "Violation evidence", |
| 90 | + () -> Map.of("id", violation.id().value(), "evidence", evidence.toString()))); |
| 91 | + }); |
| 92 | + } |
| 93 | +} |
0 commit comments