diff --git a/build.gradle b/build.gradle index fd3ad0a..ad1eb5f 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,10 @@ dependencies { implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.18.1' + implementation platform('org.apache.logging.log4j:log4j-bom:2.24.1') + implementation 'org.apache.logging.log4j:log4j-api' + implementation 'org.apache.logging.log4j:log4j-core' + testImplementation platform('org.junit:junit-bom:5.10.0') testImplementation 'org.junit.jupiter:junit-jupiter' } diff --git a/src/main/java/ch/geowerkstatt/lk2dxf/EhiLogAdapter.java b/src/main/java/ch/geowerkstatt/lk2dxf/EhiLogAdapter.java new file mode 100644 index 0000000..81d85d8 --- /dev/null +++ b/src/main/java/ch/geowerkstatt/lk2dxf/EhiLogAdapter.java @@ -0,0 +1,23 @@ +package ch.geowerkstatt.lk2dxf; + +import ch.ehi.basics.logging.LogEvent; +import ch.ehi.basics.logging.LogListener; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * An adapter that forwards log events from an EhiLogger to a log4j Logger. + */ +public final class EhiLogAdapter implements LogListener { + private static final Logger LOGGER = LogManager.getLogger(); + + @Override + public void logEvent(LogEvent logEvent) { + switch (logEvent.getEventKind()) { + case LogEvent.ERROR -> LOGGER.error("{}", logEvent.getEventMsg()); + case LogEvent.ADAPTION -> LOGGER.warn("{}", logEvent.getEventMsg()); + case LogEvent.DEBUG_TRACE, LogEvent.STATE_TRACE -> LOGGER.trace("{}", logEvent.getEventMsg()); + default -> LOGGER.info("{}", logEvent.getEventMsg()); + } + } +} diff --git a/src/main/java/ch/geowerkstatt/lk2dxf/Main.java b/src/main/java/ch/geowerkstatt/lk2dxf/Main.java index 550cf53..ff48026 100644 --- a/src/main/java/ch/geowerkstatt/lk2dxf/Main.java +++ b/src/main/java/ch/geowerkstatt/lk2dxf/Main.java @@ -1,6 +1,10 @@ package ch.geowerkstatt.lk2dxf; +import ch.ehi.basics.logging.EhiLogger; +import ch.ehi.basics.logging.StdListener; import ch.geowerkstatt.lk2dxf.mapping.ObjectMapper; +import ch.interlis.ili2c.metamodel.TransferDescription; +import ch.interlis.iox_j.utility.IoxUtility; import com.vividsolutions.jts.geom.Geometry; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; @@ -8,6 +12,12 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.appender.FileAppender; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.layout.PatternLayout; import java.io.File; import java.util.List; @@ -23,6 +33,8 @@ public final class Main { private static final String VERSION; + private static final Logger LOGGER = LogManager.getLogger(); + static { String packageVersion = Main.class.getPackage().getImplementationVersion(); VERSION = packageVersion != null ? packageVersion : "unknown"; @@ -37,7 +49,9 @@ public static void main(String[] args) { Options cliOptions = createCliOptions(); CommandLine commandLine = parseCommandLine(cliOptions, args); - if (commandLine.hasOption(OPTION_HELP)) { + if (commandLine == null) { + System.exit(1); + } else if (commandLine.hasOption(OPTION_HELP)) { printUsage(cliOptions); } else if (commandLine.hasOption(OPTION_VERSION)) { System.out.println(VERSION); @@ -47,12 +61,20 @@ public static void main(String[] args) { printUsage(cliOptions); System.exit(1); } else { - processFiles(options.get()); + configureLogging(options.get()); + if (!processFiles(options.get())) { + System.exit(1); + } } } } - private static void processFiles(LK2DxfOptions options) { + /** + * Processes the input files and writes the generated DXF to the output file. + * + * @return {@code true} if the operation was successful, {@code false} otherwise. + */ + private static boolean processFiles(LK2DxfOptions options) { Optional perimeter = options.parsePerimeter(); ObjectMapper objectMapper; @@ -73,16 +95,44 @@ private static void processFiles(LK2DxfOptions options) { objects.forEach(o -> o.writeToDxf(dxfWriter)); } catch (Exception e) { - System.err.println("Failed to process file: " + xtfFile); - e.printStackTrace(); - return; + LOGGER.error("Failed to process file: {}", xtfFile, e); + return false; } } } catch (Exception e) { - System.err.println("Failed to write DXF file: " + options.dxfFile()); - e.printStackTrace(); - return; + LOGGER.error("Failed to write DXF file: {}", options.dxfFile(), e); + return false; + } + + return true; + } + + private static void configureLogging(LK2DxfOptions lk2DxfOptions) { + Level logLevel = lk2DxfOptions.trace() ? Level.TRACE : Level.INFO; + Configurator.setRootLevel(logLevel); + + if (lk2DxfOptions.logfile().isPresent()) { + var layout = PatternLayout.newBuilder() + .withPattern("%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n") + .build(); + var fileAppender = FileAppender.newBuilder() + .setName("Logfile") + .setLayout(layout) + .withFileName(lk2DxfOptions.logfile().get()) + .withAppend(false) + .build(); + var rootLogger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger(); + rootLogger.get().addAppender(fileAppender, logLevel, null); + fileAppender.start(); } + + EhiLogger.getInstance().addListener(new EhiLogAdapter()); + EhiLogger.getInstance().removeListener(StdListener.getInstance()); + + LOGGER.info("lk2dxf version {}", VERSION); + LOGGER.info("ili2c version {}", TransferDescription.getVersion()); + LOGGER.info("iox-ili version {}", IoxUtility.getVersion()); + LOGGER.info("Transfer files: {}", lk2DxfOptions.xtfFiles()); } private static CommandLine parseCommandLine(Options options, String[] args) { @@ -90,9 +140,8 @@ private static CommandLine parseCommandLine(Options options, String[] args) { DefaultParser parser = new DefaultParser(); return parser.parse(options, args); } catch (ParseException e) { - System.err.println("Error parsing command line arguments: " + e.getMessage()); + LOGGER.error("Error parsing command line arguments: {}", e.getMessage()); printUsage(options); - System.exit(1); return null; } } diff --git a/src/main/java/ch/geowerkstatt/lk2dxf/MappedObject.java b/src/main/java/ch/geowerkstatt/lk2dxf/MappedObject.java index 9231a06..9c34e97 100644 --- a/src/main/java/ch/geowerkstatt/lk2dxf/MappedObject.java +++ b/src/main/java/ch/geowerkstatt/lk2dxf/MappedObject.java @@ -7,6 +7,8 @@ import ch.interlis.iox_j.jts.Iox2jtsext; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public record MappedObject( String oid, @@ -18,6 +20,7 @@ public record MappedObject( String text, LayerMapping layerMapping) { private static final GeometryFactory GEOMETRY_FACTORY = new JtsextGeometryFactory(); + private static final Logger LOGGER = LogManager.getLogger(); /** * Creates a new {@link MappedObject} that contains all information to process the object further. @@ -59,8 +62,7 @@ public void writeToDxf(DxfWriter dxfWriter) { default -> throw new AssertionError("Unknown output type: " + layerMapping().output()); } } catch (Exception e) { - System.err.println("Failed to write object: " + oid() + " to dxf."); - e.printStackTrace(); + LOGGER.error("Failed to write object: {} to dxf.", oid(), e); } } } diff --git a/src/main/java/ch/geowerkstatt/lk2dxf/XtfStreamReader.java b/src/main/java/ch/geowerkstatt/lk2dxf/XtfStreamReader.java index 1a4e5cd..b09df99 100644 --- a/src/main/java/ch/geowerkstatt/lk2dxf/XtfStreamReader.java +++ b/src/main/java/ch/geowerkstatt/lk2dxf/XtfStreamReader.java @@ -12,6 +12,8 @@ import ch.interlis.iox.StartTransferEvent; import ch.interlis.iox_j.logging.LogEventFactory; import ch.interlis.iox_j.utility.ReaderFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.io.File; import java.util.Spliterator; @@ -24,8 +26,10 @@ */ public final class XtfStreamReader implements AutoCloseable { private static final ReaderFactory READER_FACTORY = new ReaderFactory(); + private static final Logger LOGGER = LogManager.getLogger(); private final IoxReader reader; + private final String filename; private LKMapXtfReaderState state = null; /** @@ -37,6 +41,7 @@ public XtfStreamReader(File xtfFile) throws IoxException { LogEventFactory logEventFactory = new LogEventFactory(); Settings settings = new Settings(); this.reader = READER_FACTORY.createReader(xtfFile, logEventFactory, settings); + this.filename = xtfFile.getName(); } /** @@ -82,14 +87,14 @@ public boolean tryAdvance(Consumer action) { throw new IllegalStateException("Unexpected start transfer event in state: " + state); } state = LKMapXtfReaderState.TRANSFER; - System.out.println("Start transfer"); + LOGGER.info("Start transfer of \"{}\"", filename); } case StartBasketEvent startBasketEvent -> { if (state != LKMapXtfReaderState.TRANSFER) { throw new IllegalStateException("Unexpected start basket event in state: " + state); } state = LKMapXtfReaderState.BASKET; - System.out.println("Start basket \"" + startBasketEvent.getBid() + "\""); + LOGGER.info("Start basket \"{}\"", startBasketEvent.getBid()); } case ObjectEvent objectEvent -> { if (state != LKMapXtfReaderState.BASKET) { @@ -103,14 +108,14 @@ public boolean tryAdvance(Consumer action) { throw new IllegalStateException("Unexpected end basket event in state: " + state); } state = LKMapXtfReaderState.TRANSFER; - System.out.println("End basket"); + LOGGER.info("End basket"); } case EndTransferEvent ignored -> { if (state != LKMapXtfReaderState.TRANSFER) { throw new IllegalStateException("Unexpected end transfer event in state: " + state); } state = LKMapXtfReaderState.COMPLETED; - System.out.println("End transfer"); + LOGGER.info("End transfer of \"{}\"", filename); return false; } default -> throw new IllegalStateException("Unexpected iox event: " + event); diff --git a/src/main/java/ch/geowerkstatt/lk2dxf/mapping/ObjectMapper.java b/src/main/java/ch/geowerkstatt/lk2dxf/mapping/ObjectMapper.java index 5e63b42..19ed773 100644 --- a/src/main/java/ch/geowerkstatt/lk2dxf/mapping/ObjectMapper.java +++ b/src/main/java/ch/geowerkstatt/lk2dxf/mapping/ObjectMapper.java @@ -18,6 +18,8 @@ import ch.interlis.iom.IomObject; import ch.interlis.iom_j.Iom_jObject; import ch.interlis.iox_j.validator.Value; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.io.File; import java.io.IOException; @@ -33,6 +35,8 @@ public final class ObjectMapper { private static final String MODELS_RESOURCE = "/models"; + private static final Logger LOGGER = LogManager.getLogger(); + private final List layerMappings; private final TransferDescription transferDescription; @@ -332,7 +336,7 @@ private static TransferDescription getTransferDescription(List lay } try { - System.out.println("iliModelsPath: " + iliModelsPath); + LOGGER.debug("iliModelsPath: {}", iliModelsPath); var modelManager = new IliManager(); modelManager.setRepositories(new String[]{iliModelsPath}); var ili2cConfig = modelManager.getConfig(requiredModels, 0.0); @@ -369,11 +373,11 @@ public Stream mapObjects(Stream iomObjects) { private Optional mapObject(IomObject iomObject, Map objectCache, Set objectsWithUnresolvedRef, boolean unresolvedReferencesAllowed) { var element = transferDescription.getElement(iomObject.getobjecttag()); if (element == null) { - System.out.println("No element found for object with id \"" + iomObject.getobjectoid() + "\"."); + LOGGER.error("No element \"{}\" found for object with id \"{}\".", iomObject.getobjecttag(), iomObject.getobjectoid()); return Optional.empty(); } if (!(element instanceof AbstractClassDef classDef)) { - System.out.println("Element is not an AbstractClassDef for object with id \"" + iomObject.getobjectoid() + "\"."); + LOGGER.error("Element \"{}\" is not an AbstractClassDef for object with id \"{}\".", iomObject.getobjecttag(), iomObject.getobjectoid()); return Optional.empty(); } @@ -413,6 +417,8 @@ private Optional mapObject(IomObject iomObject, Map { } // MATCH, continue with next filter } } + + LOGGER.trace("Mapped object of type \"{}\" with id \"{}\" to layer \"{}\".", iomObject.getobjecttag(), iomObject.getobjectoid(), mapper.mapping().layer()); return Optional.of(new MappedObject( iomObject.getobjectoid(), Optional.ofNullable(resolve(iomObject, mapper.geometry(), objectCache).getComplexObjects()).map(Collection::iterator).map(Iterator::next).orElse(null), @@ -424,7 +430,7 @@ private Optional mapObject(IomObject iomObject, Map