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.logging.log4j.core.config.builder.impl; 018 019import java.io.IOException; 020import java.io.OutputStream; 021import java.io.StringWriter; 022import java.lang.reflect.Constructor; 023import java.util.List; 024import java.util.Map; 025import java.util.concurrent.TimeUnit; 026import javax.xml.stream.XMLOutputFactory; 027import javax.xml.stream.XMLStreamException; 028import javax.xml.stream.XMLStreamWriter; 029 030import org.apache.logging.log4j.Level; 031import org.apache.logging.log4j.core.Filter; 032import org.apache.logging.log4j.core.LoggerContext; 033import org.apache.logging.log4j.core.config.Configuration; 034import org.apache.logging.log4j.core.config.ConfigurationException; 035import org.apache.logging.log4j.core.config.ConfigurationSource; 036import org.apache.logging.log4j.core.config.LoggerConfig; 037import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; 038import org.apache.logging.log4j.core.config.builder.api.AppenderRefComponentBuilder; 039import org.apache.logging.log4j.core.config.builder.api.Component; 040import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder; 041import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; 042import org.apache.logging.log4j.core.config.builder.api.CustomLevelComponentBuilder; 043import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder; 044import org.apache.logging.log4j.core.config.builder.api.KeyValuePairComponentBuilder; 045import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; 046import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; 047import org.apache.logging.log4j.core.config.builder.api.PropertyComponentBuilder; 048import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; 049import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder; 050import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder; 051import org.apache.logging.log4j.core.util.Throwables; 052 053/** 054 * @param <T> The BuiltConfiguration type. 055 * @since 2.4 056 */ 057public class DefaultConfigurationBuilder<T extends BuiltConfiguration> implements ConfigurationBuilder<T> { 058 059 private static final String INDENT = " "; 060 private static final String EOL = System.lineSeparator(); 061 062 private final Component root = new Component(); 063 private Component loggers; 064 private Component appenders; 065 private Component filters; 066 private Component properties; 067 private Component customLevels; 068 private Component scripts; 069 private final Class<T> clazz; 070 private ConfigurationSource source; 071 private int monitorInterval; 072 private Level level; 073 private String verbosity; 074 private String destination; 075 private String packages; 076 private String shutdownFlag; 077 private long shutdownTimeoutMillis; 078 private String advertiser; 079 private LoggerContext loggerContext; 080 private String name; 081 082 @SuppressWarnings("unchecked") 083 public DefaultConfigurationBuilder() { 084 this((Class<T>) BuiltConfiguration.class); 085 root.addAttribute("name", "Built"); 086 } 087 088 public DefaultConfigurationBuilder(final Class<T> clazz) { 089 if (clazz == null) { 090 throw new IllegalArgumentException("A Configuration class must be provided"); 091 } 092 this.clazz = clazz; 093 final List<Component> components = root.getComponents(); 094 properties = new Component("Properties"); 095 components.add(properties); 096 scripts = new Component("Scripts"); 097 components.add(scripts); 098 customLevels = new Component("CustomLevels"); 099 components.add(customLevels); 100 filters = new Component("Filters"); 101 components.add(filters); 102 appenders = new Component("Appenders"); 103 components.add(appenders); 104 loggers = new Component("Loggers"); 105 components.add(loggers); 106 } 107 108 protected ConfigurationBuilder<T> add(final Component parent, final ComponentBuilder<?> builder) { 109 parent.getComponents().add(builder.build()); 110 return this; 111 } 112 113 @Override 114 public ConfigurationBuilder<T> add(final AppenderComponentBuilder builder) { 115 return add(appenders, builder); 116 } 117 118 @Override 119 public ConfigurationBuilder<T> add(final CustomLevelComponentBuilder builder) { 120 return add(customLevels, builder); 121 } 122 123 @Override 124 public ConfigurationBuilder<T> add(final FilterComponentBuilder builder) { 125 return add(filters, builder); 126 } 127 128 @Override 129 public ConfigurationBuilder<T> add(final ScriptComponentBuilder builder) { 130 return add(scripts, builder); 131 } 132 133 @Override 134 public ConfigurationBuilder<T> add(final ScriptFileComponentBuilder builder) { 135 return add(scripts, builder); 136 } 137 138 @Override 139 public ConfigurationBuilder<T> add(final LoggerComponentBuilder builder) { 140 return add(loggers, builder); 141 } 142 143 @Override 144 public ConfigurationBuilder<T> add(final RootLoggerComponentBuilder builder) { 145 for (final Component c : loggers.getComponents()) { 146 if (c.getPluginType().equals(LoggerConfig.ROOT)) { 147 throw new ConfigurationException("Root Logger was previously defined"); 148 } 149 } 150 return add(loggers, builder); 151 } 152 153 @Override 154 public ConfigurationBuilder<T> addProperty(final String key, final String value) { 155 properties.addComponent(newComponent(key, "Property", value).build()); 156 return this; 157 } 158 159 @Override 160 public T build() { 161 return build(true); 162 } 163 164 @Override 165 public T build(final boolean initialize) { 166 T configuration; 167 try { 168 if (source == null) { 169 source = ConfigurationSource.NULL_SOURCE; 170 } 171 final Constructor<T> constructor = clazz.getConstructor(LoggerContext.class, ConfigurationSource.class, Component.class); 172 configuration = constructor.newInstance(loggerContext, source, root); 173 configuration.setMonitorInterval(monitorInterval); 174 configuration.getRootNode().getAttributes().putAll(root.getAttributes()); 175 if (name != null) { 176 configuration.setName(name); 177 } 178 if (level != null) { 179 configuration.getStatusConfiguration().withStatus(level); 180 } 181 if (verbosity != null) { 182 configuration.getStatusConfiguration().withVerbosity(verbosity); 183 } 184 if (destination != null) { 185 configuration.getStatusConfiguration().withDestination(destination); 186 } 187 if (packages != null) { 188 configuration.setPluginPackages(packages); 189 } 190 if (shutdownFlag != null) { 191 configuration.setShutdownHook(shutdownFlag); 192 } 193 if (shutdownTimeoutMillis > 0) { 194 configuration.setShutdownTimeoutMillis(shutdownTimeoutMillis); 195 } 196 if (advertiser != null) { 197 configuration.createAdvertiser(advertiser, source); 198 } 199 } catch (final Exception ex) { 200 throw new IllegalArgumentException("Invalid Configuration class specified", ex); 201 } 202 configuration.getStatusConfiguration().initialize(); 203 if (initialize) { 204 configuration.initialize(); 205 } 206 return configuration; 207 } 208 209 @Override 210 public void writeXmlConfiguration(final OutputStream output) throws IOException { 211 try { 212 final XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(output); 213 writeXmlConfiguration(xmlWriter); 214 xmlWriter.close(); 215 } catch (final XMLStreamException e) { 216 if (e.getNestedException() instanceof IOException) { 217 throw (IOException)e.getNestedException(); 218 } 219 Throwables.rethrow(e); 220 } 221 } 222 223 @Override 224 public String toXmlConfiguration() { 225 final StringWriter sw = new StringWriter(); 226 try { 227 final XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(sw); 228 writeXmlConfiguration(xmlWriter); 229 xmlWriter.close(); 230 } catch (final XMLStreamException e) { 231 Throwables.rethrow(e); 232 } 233 return sw.toString(); 234 } 235 236 private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLStreamException { 237 xmlWriter.writeStartDocument(); 238 xmlWriter.writeCharacters(EOL); 239 240 xmlWriter.writeStartElement("Configuration"); 241 if (name != null) { 242 xmlWriter.writeAttribute("name", name); 243 } 244 if (level != null) { 245 xmlWriter.writeAttribute("status", level.name()); 246 } 247 if (verbosity != null) { 248 xmlWriter.writeAttribute("verbose", verbosity); 249 } 250 if (destination != null) { 251 xmlWriter.writeAttribute("dest", destination); 252 } 253 if (packages != null) { 254 xmlWriter.writeAttribute("packages", packages); 255 } 256 if (shutdownFlag != null) { 257 xmlWriter.writeAttribute("shutdownHook", shutdownFlag); 258 } 259 if (shutdownTimeoutMillis > 0) { 260 xmlWriter.writeAttribute("shutdownTimeout", String.valueOf(shutdownTimeoutMillis)); 261 } 262 if (advertiser != null) { 263 xmlWriter.writeAttribute("advertiser", advertiser); 264 } 265 if (monitorInterval > 0) { 266 xmlWriter.writeAttribute("monitorInterval", String.valueOf(monitorInterval)); 267 } 268 269 xmlWriter.writeCharacters(EOL); 270 271 writeXmlSection(xmlWriter, properties); 272 writeXmlSection(xmlWriter, scripts); 273 writeXmlSection(xmlWriter, customLevels); 274 if (filters.getComponents().size() == 1) { 275 writeXmlComponent(xmlWriter, filters.getComponents().get(0), 1); 276 } else if (filters.getComponents().size() > 1) { 277 writeXmlSection(xmlWriter, filters); 278 } 279 writeXmlSection(xmlWriter, appenders); 280 writeXmlSection(xmlWriter, loggers); 281 282 xmlWriter.writeEndElement(); // "Configuration" 283 xmlWriter.writeCharacters(EOL); 284 285 xmlWriter.writeEndDocument(); 286 } 287 288 private void writeXmlSection(final XMLStreamWriter xmlWriter, final Component component) throws XMLStreamException { 289 if (!component.getAttributes().isEmpty() || !component.getComponents().isEmpty() || component.getValue() != null) { 290 writeXmlComponent(xmlWriter, component, 1); 291 } 292 } 293 294 private void writeXmlComponent(final XMLStreamWriter xmlWriter, final Component component, final int nesting) throws XMLStreamException { 295 if (!component.getComponents().isEmpty() || component.getValue() != null) { 296 writeXmlIndent(xmlWriter, nesting); 297 xmlWriter.writeStartElement(component.getPluginType()); 298 writeXmlAttributes(xmlWriter, component); 299 if (!component.getComponents().isEmpty()) { 300 xmlWriter.writeCharacters(EOL); 301 } 302 for (final Component subComponent : component.getComponents()) { 303 writeXmlComponent(xmlWriter, subComponent, nesting + 1); 304 } 305 if (component.getValue() != null) { 306 xmlWriter.writeCharacters(component.getValue()); 307 } 308 if (!component.getComponents().isEmpty()) { 309 writeXmlIndent(xmlWriter, nesting); 310 } 311 xmlWriter.writeEndElement(); 312 } else { 313 writeXmlIndent(xmlWriter, nesting); 314 xmlWriter.writeEmptyElement(component.getPluginType()); 315 writeXmlAttributes(xmlWriter, component); 316 } 317 xmlWriter.writeCharacters(EOL); 318 } 319 320 private void writeXmlIndent(final XMLStreamWriter xmlWriter, final int nesting) throws XMLStreamException { 321 for (int i = 0; i < nesting; i++) { 322 xmlWriter.writeCharacters(INDENT); 323 } 324 } 325 326 private void writeXmlAttributes(final XMLStreamWriter xmlWriter, final Component component) throws XMLStreamException { 327 for (final Map.Entry<String, String> attribute : component.getAttributes().entrySet()) { 328 xmlWriter.writeAttribute(attribute.getKey(), attribute.getValue()); 329 } 330 } 331 332 @Override 333 public ScriptComponentBuilder newScript(final String name, final String language, final String text) { 334 return new DefaultScriptComponentBuilder(this, name, language, text); 335 } 336 337 338 @Override 339 public ScriptFileComponentBuilder newScriptFile(final String path) { 340 return new DefaultScriptFileComponentBuilder(this, path, path); 341 } 342 343 @Override 344 public ScriptFileComponentBuilder newScriptFile(final String name, final String path) { 345 return new DefaultScriptFileComponentBuilder(this, name, path); 346 } 347 348 @Override 349 public AppenderComponentBuilder newAppender(final String name, final String type) { 350 return new DefaultAppenderComponentBuilder(this, name, type); 351 } 352 353 @Override 354 public AppenderRefComponentBuilder newAppenderRef(final String ref) { 355 return new DefaultAppenderRefComponentBuilder(this, ref); 356 } 357 358 @Override 359 public LoggerComponentBuilder newAsyncLogger(final String name, final Level level) { 360 return new DefaultLoggerComponentBuilder(this, name, level.toString(), "AsyncLogger"); 361 } 362 363 @Override 364 public LoggerComponentBuilder newAsyncLogger(final String name, final Level level, final boolean includeLocation) { 365 return new DefaultLoggerComponentBuilder(this, name, level.toString(), "AsyncLogger", includeLocation); 366 } 367 368 @Override 369 public LoggerComponentBuilder newAsyncLogger(final String name, final String level) { 370 return new DefaultLoggerComponentBuilder(this, name, level, "AsyncLogger"); 371 } 372 373 @Override 374 public LoggerComponentBuilder newAsyncLogger(final String name, final String level, final boolean includeLocation) { 375 return new DefaultLoggerComponentBuilder(this, name, level, "AsyncLogger"); 376 } 377 378 @Override 379 public RootLoggerComponentBuilder newAsyncRootLogger(final Level level) { 380 return new DefaultRootLoggerComponentBuilder(this, level.toString(), "AsyncRoot"); 381 } 382 383 @Override 384 public RootLoggerComponentBuilder newAsyncRootLogger(final Level level, final boolean includeLocation) { 385 return new DefaultRootLoggerComponentBuilder(this, level.toString(), "AsyncRoot", includeLocation); 386 } 387 388 @Override 389 public RootLoggerComponentBuilder newAsyncRootLogger(final String level) { 390 return new DefaultRootLoggerComponentBuilder(this, level, "AsyncRoot"); 391 } 392 393 @Override 394 public RootLoggerComponentBuilder newAsyncRootLogger(final String level, final boolean includeLocation) { 395 return new DefaultRootLoggerComponentBuilder(this, level, "AsyncRoot", includeLocation); 396 } 397 398 399 @Override 400 public <B extends ComponentBuilder<B>> ComponentBuilder<B> newComponent(final String type) { 401 return new DefaultComponentBuilder<>(this, type); 402 } 403 404 @Override 405 public <B extends ComponentBuilder<B>> ComponentBuilder<B> newComponent(final String name, final String type) { 406 return new DefaultComponentBuilder<>(this, name, type); 407 } 408 409 @Override 410 public <B extends ComponentBuilder<B>> ComponentBuilder<B> newComponent(final String name, final String type, 411 final String value) { 412 return new DefaultComponentBuilder<>(this, name, type, value); 413 } 414 415 @Override 416 public PropertyComponentBuilder newProperty(final String name, final String value) { 417 return new DefaultPropertyComponentBuilder(this, name, value); 418 } 419 420 @Override 421 public KeyValuePairComponentBuilder newKeyValuePair(final String key, final String value) { 422 return new DefaultKeyValuePairComponentBuilder(this, key, value); 423 } 424 425 @Override 426 public CustomLevelComponentBuilder newCustomLevel(final String name, final int level) { 427 return new DefaultCustomLevelComponentBuilder(this, name, level); 428 } 429 430 @Override 431 public FilterComponentBuilder newFilter(final String type, final Filter.Result onMatch, 432 final Filter.Result onMisMatch) { 433 return new DefaultFilterComponentBuilder(this, type, onMatch.name(), onMisMatch.name()); 434 } 435 436 @Override 437 public FilterComponentBuilder newFilter(final String type, final String onMatch, final String onMisMatch) { 438 return new DefaultFilterComponentBuilder(this, type, onMatch, onMisMatch); 439 } 440 441 @Override 442 public LayoutComponentBuilder newLayout(final String type) { 443 return new DefaultLayoutComponentBuilder(this, type); 444 } 445 446 447 @Override 448 public LoggerComponentBuilder newLogger(final String name, final Level level) { 449 return new DefaultLoggerComponentBuilder(this, name, level.toString()); 450 } 451 452 @Override 453 public LoggerComponentBuilder newLogger(final String name, final Level level, final boolean includeLocation) { 454 return new DefaultLoggerComponentBuilder(this, name, level.toString(), includeLocation); 455 } 456 457 @Override 458 public LoggerComponentBuilder newLogger(final String name, final String level) { 459 return new DefaultLoggerComponentBuilder(this, name, level); 460 } 461 462 @Override 463 public LoggerComponentBuilder newLogger(final String name, final String level, final boolean includeLocation) { 464 return new DefaultLoggerComponentBuilder(this, name, level, includeLocation); 465 } 466 467 @Override 468 public RootLoggerComponentBuilder newRootLogger(final Level level) { 469 return new DefaultRootLoggerComponentBuilder(this, level.toString()); 470 } 471 472 @Override 473 public RootLoggerComponentBuilder newRootLogger(final Level level, final boolean includeLocation) { 474 return new DefaultRootLoggerComponentBuilder(this, level.toString(), includeLocation); 475 } 476 477 @Override 478 public RootLoggerComponentBuilder newRootLogger(final String level) { 479 return new DefaultRootLoggerComponentBuilder(this, level); 480 } 481 482 @Override 483 public RootLoggerComponentBuilder newRootLogger(final String level, final boolean includeLocation) { 484 return new DefaultRootLoggerComponentBuilder(this, level, includeLocation); 485 } 486 487 @Override 488 public ConfigurationBuilder<T> setAdvertiser(final String advertiser) { 489 this.advertiser = advertiser; 490 return this; 491 } 492 493 /** 494 * Set the name of the configuration. 495 * 496 * @param name the name of the {@link Configuration}. By default is {@code "Assembled"}. 497 * @return this builder instance 498 */ 499 @Override 500 public ConfigurationBuilder<T> setConfigurationName(final String name) { 501 this.name = name; 502 return this; 503 } 504 505 /** 506 * Set the ConfigurationSource. 507 * 508 * @param configurationSource the {@link ConfigurationSource} 509 * @return this builder instance 510 */ 511 @Override 512 public ConfigurationBuilder<T> setConfigurationSource(final ConfigurationSource configurationSource) { 513 source = configurationSource; 514 return this; 515 } 516 517 @Override 518 public ConfigurationBuilder<T> setMonitorInterval(final String intervalSeconds) { 519 monitorInterval = Integer.parseInt(intervalSeconds); 520 return this; 521 } 522 523 @Override 524 public ConfigurationBuilder<T> setPackages(final String packages) { 525 this.packages = packages; 526 return this; 527 } 528 529 @Override 530 public ConfigurationBuilder<T> setShutdownHook(final String flag) { 531 this.shutdownFlag = flag; 532 return this; 533 } 534 535 @Override 536 public ConfigurationBuilder<T> setShutdownTimeout(final long timeout, final TimeUnit timeUnit) { 537 this.shutdownTimeoutMillis = timeUnit.toMillis(timeout); 538 return this; 539 } 540 541 @Override 542 public ConfigurationBuilder<T> setStatusLevel(final Level level) { 543 this.level = level; 544 return this; 545 } 546 547 @Override 548 public ConfigurationBuilder<T> setVerbosity(final String verbosity) { 549 this.verbosity = verbosity; 550 return this; 551 } 552 553 @Override 554 public ConfigurationBuilder<T> setDestination(final String destination) { 555 this.destination = destination; 556 return this; 557 } 558 559 @Override 560 public void setLoggerContext(final LoggerContext loggerContext) { 561 this.loggerContext = loggerContext; 562 } 563 564 @Override 565 public ConfigurationBuilder<T> addRootProperty(final String key, final String value) { 566 root.getAttributes().put(key, value); 567 return this; 568 } 569 570}