001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.log4j.config; 018 019import java.io.ByteArrayInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.nio.file.FileVisitResult; 025import java.nio.file.Files; 026import java.nio.file.Path; 027import java.nio.file.SimpleFileVisitor; 028import java.nio.file.attribute.BasicFileAttributes; 029import java.util.concurrent.atomic.AtomicInteger; 030 031import javax.xml.transform.TransformerException; 032import javax.xml.transform.stream.StreamResult; 033import javax.xml.transform.stream.StreamSource; 034 035import org.apache.logging.log4j.core.config.ConfigurationException; 036import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; 037import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; 038import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder; 039import org.apache.logging.log4j.core.tools.BasicCommandLineArguments; 040import org.apache.logging.log4j.core.tools.picocli.CommandLine; 041import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command; 042import org.apache.logging.log4j.core.tools.picocli.CommandLine.Option; 043 044/** 045 * Tool for converting a Log4j 1.x properties configuration file to Log4j 2.x XML configuration file. 046 * 047 * <p> 048 * Run with "--help" on the command line. 049 * </p> 050 * 051 * <p> 052 * Example: 053 * </p> 054 * 055 * <pre> 056 * java org.apache.log4j.config.Log4j1ConfigurationConverter --recurse 057 * E:\vcs\git\apache\logging\logging-log4j2\log4j-1.2-api\src\test\resources\config-1.2\hadoop --in log4j.properties --verbose 058 * </pre> 059 */ 060public final class Log4j1ConfigurationConverter { 061 062 @Command(name = "Log4j1ConfigurationConverter") 063 public static class CommandLineArguments extends BasicCommandLineArguments implements Runnable { 064 065 @Option(names = { "--failfast", "-f" }, description = "Fails on the first failure in recurse mode.") 066 private boolean failFast; 067 068 @Option(names = { "--in", "-i" }, description = "Specifies the input file.") 069 private Path pathIn; 070 071 @Option(names = { "--out", "-o" }, description = "Specifies the output file.") 072 private Path pathOut; 073 074 @Option(names = { "--recurse", "-r" }, description = "Recurses into this folder looking for the input file") 075 private Path recurseIntoPath; 076 077 @Option(names = { "--verbose", "-v" }, description = "Be verbose.") 078 private boolean verbose; 079 080 public Path getPathIn() { 081 return pathIn; 082 } 083 084 public Path getPathOut() { 085 return pathOut; 086 } 087 088 public Path getRecurseIntoPath() { 089 return recurseIntoPath; 090 } 091 092 public boolean isFailFast() { 093 return failFast; 094 } 095 096 public boolean isVerbose() { 097 return verbose; 098 } 099 100 public void setFailFast(final boolean failFast) { 101 this.failFast = failFast; 102 } 103 104 public void setPathIn(final Path pathIn) { 105 this.pathIn = pathIn; 106 } 107 108 public void setPathOut(final Path pathOut) { 109 this.pathOut = pathOut; 110 } 111 112 public void setRecurseIntoPath(final Path recurseIntoPath) { 113 this.recurseIntoPath = recurseIntoPath; 114 } 115 116 public void setVerbose(final boolean verbose) { 117 this.verbose = verbose; 118 } 119 120 @Override 121 public void run() { 122 if (isHelp()) { 123 CommandLine.usage(this, System.err); 124 return; 125 } 126 new Log4j1ConfigurationConverter(this).run(); 127 } 128 129 @Override 130 public String toString() { 131 return "CommandLineArguments [recurseIntoPath=" + recurseIntoPath + ", verbose=" + verbose + ", pathIn=" 132 + pathIn + ", pathOut=" + pathOut + "]"; 133 } 134 } 135 136 private static final String FILE_EXT_XML = ".xml"; 137 138 public static void main(final String[] args) { 139 CommandLine.run(new CommandLineArguments(), System.err, args); 140 } 141 142 public static Log4j1ConfigurationConverter run(final CommandLineArguments cla) { 143 final Log4j1ConfigurationConverter log4j1ConfigurationConverter = new Log4j1ConfigurationConverter(cla); 144 log4j1ConfigurationConverter.run(); 145 return log4j1ConfigurationConverter; 146 } 147 148 private final CommandLineArguments cla; 149 150 private Log4j1ConfigurationConverter(final CommandLineArguments cla) { 151 this.cla = cla; 152 } 153 154 protected void convert(final InputStream input, final OutputStream output) throws IOException { 155 final ConfigurationBuilder<BuiltConfiguration> builder = new Log4j1ConfigurationParser() 156 .buildConfigurationBuilder(input); 157 builder.writeXmlConfiguration(output); 158 } 159 160 InputStream getInputStream() throws IOException { 161 final Path pathIn = cla.getPathIn(); 162 return pathIn == null ? System.in : new InputStreamWrapper(Files.newInputStream(pathIn), pathIn.toString()); 163 } 164 165 OutputStream getOutputStream() throws IOException { 166 final Path pathOut = cla.getPathOut(); 167 return pathOut == null ? System.out : Files.newOutputStream(pathOut); 168 } 169 170 private void run() { 171 if (cla.getRecurseIntoPath() != null) { 172 final AtomicInteger countOKs = new AtomicInteger(); 173 final AtomicInteger countFails = new AtomicInteger(); 174 try { 175 Files.walkFileTree(cla.getRecurseIntoPath(), new SimpleFileVisitor<Path>() { 176 @Override 177 public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) 178 throws IOException { 179 if (cla.getPathIn() == null || file.getFileName().equals(cla.getPathIn())) { 180 verbose("Reading %s", file); 181 String newFile = file.getFileName().toString(); 182 final int lastIndex = newFile.lastIndexOf("."); 183 newFile = lastIndex < 0 ? newFile + FILE_EXT_XML 184 : newFile.substring(0, lastIndex) + FILE_EXT_XML; 185 final Path resolvedPath = file.resolveSibling(newFile); 186 try (final InputStream input = new InputStreamWrapper(Files.newInputStream(file), file.toString()); 187 final OutputStream output = Files.newOutputStream(resolvedPath)) { 188 try { 189 final ByteArrayOutputStream tmpOutput = new ByteArrayOutputStream(); 190 convert(input, tmpOutput); 191 tmpOutput.close(); 192 DefaultConfigurationBuilder.formatXml( 193 new StreamSource(new ByteArrayInputStream(tmpOutput.toByteArray())), 194 new StreamResult(output)); 195 countOKs.incrementAndGet(); 196 } catch (ConfigurationException | IOException e) { 197 countFails.incrementAndGet(); 198 if (cla.isFailFast()) { 199 throw e; 200 } 201 e.printStackTrace(); 202 } catch (TransformerException e) { 203 countFails.incrementAndGet(); 204 if (cla.isFailFast()) { 205 throw new IOException(e); 206 } 207 e.printStackTrace(); 208 } 209 verbose("Wrote %s", resolvedPath); 210 } 211 } 212 return FileVisitResult.CONTINUE; 213 } 214 }); 215 } catch (final IOException e) { 216 throw new ConfigurationException(e); 217 } finally { 218 verbose("OK = %,d, Failures = %,d, Total = %,d", countOKs.get(), countFails.get(), 219 countOKs.get() + countFails.get()); 220 } 221 } else { 222 verbose("Reading %s", cla.getPathIn()); 223 try (final InputStream input = getInputStream(); final OutputStream output = getOutputStream()) { 224 convert(input, output); 225 } catch (final IOException e) { 226 throw new ConfigurationException(e); 227 } 228 verbose("Wrote %s", cla.getPathOut()); 229 } 230 } 231 232 private void verbose(final String template, final Object... args) { 233 if (cla.isVerbose()) { 234 System.err.println(String.format(template, args)); 235 } 236 } 237 238}