View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.log4j.config;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.nio.file.FileVisitResult;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.SimpleFileVisitor;
28  import java.nio.file.attribute.BasicFileAttributes;
29  import java.util.concurrent.atomic.AtomicInteger;
30  
31  import javax.xml.transform.TransformerException;
32  import javax.xml.transform.stream.StreamResult;
33  import javax.xml.transform.stream.StreamSource;
34  
35  import org.apache.logging.log4j.core.config.ConfigurationException;
36  import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
37  import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
38  import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder;
39  import org.apache.logging.log4j.core.tools.BasicCommandLineArguments;
40  import org.apache.logging.log4j.core.tools.picocli.CommandLine;
41  import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command;
42  import org.apache.logging.log4j.core.tools.picocli.CommandLine.Option;
43  
44  /**
45   * Tool for converting a Log4j 1.x properties configuration file to Log4j 2.x XML configuration file.
46   *
47   * <p>
48   * Run with "--help" on the command line.
49   * </p>
50   *
51   * <p>
52   * Example:
53   * </p>
54   *
55   * <pre>
56   * java org.apache.log4j.config.Log4j1ConfigurationConverter --recurse
57   * E:\vcs\git\apache\logging\logging-log4j2\log4j-1.2-api\src\test\resources\config-1.2\hadoop --in log4j.properties --verbose
58   * </pre>
59   */
60  public final class Log4j1ConfigurationConverter {
61  
62      @Command(name = "Log4j1ConfigurationConverter")
63      public static class CommandLineArguments extends BasicCommandLineArguments implements Runnable {
64  
65          @Option(names = { "--failfast", "-f" }, description = "Fails on the first failure in recurse mode.")
66          private boolean failFast;
67  
68          @Option(names = { "--in", "-i" }, description = "Specifies the input file.")
69          private Path pathIn;
70  
71          @Option(names = { "--out", "-o" }, description = "Specifies the output file.")
72          private Path pathOut;
73  
74          @Option(names = { "--recurse", "-r" }, description = "Recurses into this folder looking for the input file")
75          private Path recurseIntoPath;
76  
77          @Option(names = { "--verbose", "-v" }, description = "Be verbose.")
78          private boolean verbose;
79  
80          public Path getPathIn() {
81              return pathIn;
82          }
83  
84          public Path getPathOut() {
85              return pathOut;
86          }
87  
88          public Path getRecurseIntoPath() {
89              return recurseIntoPath;
90          }
91  
92          public boolean isFailFast() {
93              return failFast;
94          }
95  
96          public boolean isVerbose() {
97              return verbose;
98          }
99  
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 }