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.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.nio.file.FileVisitResult;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.nio.file.SimpleFileVisitor;
26  import java.nio.file.attribute.BasicFileAttributes;
27  import java.util.concurrent.atomic.AtomicInteger;
28  
29  import org.apache.logging.log4j.core.config.ConfigurationException;
30  import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
31  import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
32  import org.apache.logging.log4j.core.tools.BasicCommandLineArguments;
33  import org.apache.logging.log4j.core.tools.picocli.CommandLine;
34  import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command;
35  import org.apache.logging.log4j.core.tools.picocli.CommandLine.Option;
36  
37  /**
38   * Tool for converting a Log4j 1.x properties configuration file to Log4j 2.x XML configuration file.
39   *
40   * <p>
41   * Run with "--help" on the command line.
42   * </p>
43   *
44   * <p>
45   * Example:
46   * </p>
47   *
48   * <pre>
49   * java org.apache.log4j.config.Log4j1ConfigurationConverter --recurse
50   * E:\vcs\git\apache\logging\logging-log4j2\log4j-1.2-api\src\test\resources\config-1.2\hadoop --in log4j.properties --verbose
51   * </pre>
52   */
53  public final class Log4j1ConfigurationConverter {
54  
55      @Command(name = "Log4j1ConfigurationConverter")
56      public static class CommandLineArguments extends BasicCommandLineArguments implements Runnable {
57  
58          @Option(names = { "--failfast", "-f" }, description = "Fails on the first failure in recurse mode.")
59          private boolean failFast;
60  
61          @Option(names = { "--in", "-i" }, description = "Specifies the input file.")
62          private Path pathIn;
63  
64          @Option(names = { "--out", "-o" }, description = "Specifies the output file.")
65          private Path pathOut;
66  
67          @Option(names = { "--recurse", "-r" }, description = "Recurses into this folder looking for the input file")
68          private Path recurseIntoPath;
69  
70          @Option(names = { "--verbose", "-v" }, description = "Be verbose.")
71          private boolean verbose;
72  
73          public Path getPathIn() {
74              return pathIn;
75          }
76  
77          public Path getPathOut() {
78              return pathOut;
79          }
80  
81          public Path getRecurseIntoPath() {
82              return recurseIntoPath;
83          }
84  
85          public boolean isFailFast() {
86              return failFast;
87          }
88  
89          public boolean isVerbose() {
90              return verbose;
91          }
92  
93          public void setFailFast(final boolean failFast) {
94              this.failFast = failFast;
95          }
96  
97          public void setPathIn(final Path pathIn) {
98              this.pathIn = pathIn;
99          }
100 
101         public void setPathOut(final Path pathOut) {
102             this.pathOut = pathOut;
103         }
104 
105         public void setRecurseIntoPath(final Path recurseIntoPath) {
106             this.recurseIntoPath = recurseIntoPath;
107         }
108 
109         public void setVerbose(final boolean verbose) {
110             this.verbose = verbose;
111         }
112 
113         @Override
114         public void run() {
115             if (isHelp()) {
116                 CommandLine.usage(this, System.err);
117                 return;
118             }
119             new Log4j1ConfigurationConverter(this).run();
120         }
121 
122         @Override
123         public String toString() {
124             return "CommandLineArguments [recurseIntoPath=" + recurseIntoPath + ", verbose=" + verbose + ", pathIn="
125                     + pathIn + ", pathOut=" + pathOut + "]";
126         }
127     }
128 
129     private static final String FILE_EXT_XML = ".xml";
130 
131     public static void main(final String[] args) {
132         CommandLine.run(new CommandLineArguments(), System.err, args);
133     }
134 
135     public static Log4j1ConfigurationConverter run(final CommandLineArguments cla) {
136         final Log4j1ConfigurationConverter log4j1ConfigurationConverter = new Log4j1ConfigurationConverter(cla);
137         log4j1ConfigurationConverter.run();
138         return log4j1ConfigurationConverter;
139     }
140 
141     private final CommandLineArguments cla;
142 
143     private Log4j1ConfigurationConverter(final CommandLineArguments cla) {
144         this.cla = cla;
145     }
146 
147     protected void convert(final InputStream input, final OutputStream output) throws IOException {
148         final ConfigurationBuilder<BuiltConfiguration> builder = new Log4j1ConfigurationParser()
149                 .buildConfigurationBuilder(input);
150         builder.writeXmlConfiguration(output);
151     }
152 
153     InputStream getInputStream() throws IOException {
154         final Path pathIn = cla.getPathIn();
155         return pathIn == null ? System.in : new InputStreamWrapper(Files.newInputStream(pathIn), pathIn.toString());
156     }
157 
158     OutputStream getOutputStream() throws IOException {
159         final Path pathOut = cla.getPathOut();
160         return pathOut == null ? System.out : Files.newOutputStream(pathOut);
161     }
162 
163     private void run() {
164         if (cla.getRecurseIntoPath() != null) {
165             final AtomicInteger countOKs = new AtomicInteger();
166             final AtomicInteger countFails = new AtomicInteger();
167             try {
168                 Files.walkFileTree(cla.getRecurseIntoPath(), new SimpleFileVisitor<Path>() {
169                     @Override
170                     public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs)
171                             throws IOException {
172                         if (cla.getPathIn() == null || file.getFileName().equals(cla.getPathIn())) {
173                             verbose("Reading %s", file);
174                             String newFile = file.getFileName().toString();
175                             final int lastIndex = newFile.lastIndexOf(".");
176                             newFile = lastIndex < 0 ? newFile + FILE_EXT_XML
177                                     : newFile.substring(0, lastIndex) + FILE_EXT_XML;
178                             final Path resolved = file.resolveSibling(newFile);
179                             try (final InputStream input = new InputStreamWrapper(Files.newInputStream(file), file.toString());
180                                     final OutputStream output = Files.newOutputStream(resolved)) {
181                                 try {
182                                     convert(input, output);
183                                     countOKs.incrementAndGet();
184                                 } catch (ConfigurationException | IOException e) {
185                                     countFails.incrementAndGet();
186                                     if (cla.isFailFast()) {
187                                         throw e;
188                                     }
189                                     e.printStackTrace();
190                                 }
191                                 verbose("Wrote %s", resolved);
192                             }
193                         }
194                         return FileVisitResult.CONTINUE;
195                     }
196                 });
197             } catch (final IOException e) {
198                 throw new ConfigurationException(e);
199             } finally {
200                 verbose("OK = %,d, Failures = %,d, Total = %,d", countOKs.get(), countFails.get(),
201                         countOKs.get() + countFails.get());
202             }
203         } else {
204             verbose("Reading %s", cla.getPathIn());
205             try (final InputStream input = getInputStream(); final OutputStream output = getOutputStream()) {
206                 convert(input, output);
207             } catch (final IOException e) {
208                 throw new ConfigurationException(e);
209             }
210             verbose("Wrote %s", cla.getPathOut());
211         }
212     }
213 
214     private void verbose(final String template, final Object... args) {
215         if (cla.isVerbose()) {
216             System.err.println(String.format(template, args));
217         }
218     }
219 
220 }