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