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.getRootNode().getAttributes().putAll(root.getAttributes()); 174 if (name != null) { 175 configuration.setName(name); 176 } 177 if (level != null) { 178 configuration.getStatusConfiguration().withStatus(level); 179 } 180 if (verbosity != null) { 181 configuration.getStatusConfiguration().withVerbosity(verbosity); 182 } 183 if (destination != null) { 184 configuration.getStatusConfiguration().withDestination(destination); 185 } 186 if (packages != null) { 187 configuration.setPluginPackages(packages); 188 } 189 if (shutdownFlag != null) { 190 configuration.setShutdownHook(shutdownFlag); 191 } 192 if (shutdownTimeoutMillis > 0) { 193 configuration.setShutdownTimeoutMillis(shutdownTimeoutMillis); 194 } 195 if (advertiser != null) { 196 configuration.createAdvertiser(advertiser, source); 197 } 198 configuration.setMonitorInterval(monitorInterval); 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) { 360 return new DefaultLoggerComponentBuilder(this, name, null, "AsyncLogger"); 361 } 362 363 @Override 364 public LoggerComponentBuilder newAsyncLogger(final String name, final boolean includeLocation) { 365 return new DefaultLoggerComponentBuilder(this, name, null, "AsyncLogger", includeLocation); 366 } 367 368 @Override 369 public LoggerComponentBuilder newAsyncLogger(final String name, final Level level) { 370 return new DefaultLoggerComponentBuilder(this, name, level.toString(), "AsyncLogger"); 371 } 372 373 @Override 374 public LoggerComponentBuilder newAsyncLogger(final String name, final Level level, final boolean includeLocation) { 375 return new DefaultLoggerComponentBuilder(this, name, level.toString(), "AsyncLogger", includeLocation); 376 } 377 378 @Override 379 public LoggerComponentBuilder newAsyncLogger(final String name, final String level) { 380 return new DefaultLoggerComponentBuilder(this, name, level, "AsyncLogger"); 381 } 382 383 @Override 384 public LoggerComponentBuilder newAsyncLogger(final String name, final String level, final boolean includeLocation) { 385 return new DefaultLoggerComponentBuilder(this, name, level, "AsyncLogger"); 386 } 387 388 @Override 389 public RootLoggerComponentBuilder newAsyncRootLogger() { 390 return new DefaultRootLoggerComponentBuilder(this, "AsyncRoot"); 391 } 392 393 @Override 394 public RootLoggerComponentBuilder newAsyncRootLogger(final boolean includeLocation) { 395 return new DefaultRootLoggerComponentBuilder(this, null, "AsyncRoot", includeLocation); 396 } 397 398 @Override 399 public RootLoggerComponentBuilder newAsyncRootLogger(final Level level) { 400 return new DefaultRootLoggerComponentBuilder(this, level.toString(), "AsyncRoot"); 401 } 402 403 @Override 404 public RootLoggerComponentBuilder newAsyncRootLogger(final Level level, final boolean includeLocation) { 405 return new DefaultRootLoggerComponentBuilder(this, level.toString(), "AsyncRoot", includeLocation); 406 } 407 408 @Override 409 public RootLoggerComponentBuilder newAsyncRootLogger(final String level) { 410 return new DefaultRootLoggerComponentBuilder(this, level, "AsyncRoot"); 411 } 412 413 @Override 414 public RootLoggerComponentBuilder newAsyncRootLogger(final String level, final boolean includeLocation) { 415 return new DefaultRootLoggerComponentBuilder(this, level, "AsyncRoot", includeLocation); 416 } 417 418 419 @Override 420 public <B extends ComponentBuilder<B>> ComponentBuilder<B> newComponent(final String type) { 421 return new DefaultComponentBuilder<>(this, type); 422 } 423 424 @Override 425 public <B extends ComponentBuilder<B>> ComponentBuilder<B> newComponent(final String name, final String type) { 426 return new DefaultComponentBuilder<>(this, name, type); 427 } 428 429 @Override 430 public <B extends ComponentBuilder<B>> ComponentBuilder<B> newComponent(final String name, final String type, 431 final String value) { 432 return new DefaultComponentBuilder<>(this, name, type, value); 433 } 434 435 @Override 436 public PropertyComponentBuilder newProperty(final String name, final String value) { 437 return new DefaultPropertyComponentBuilder(this, name, value); 438 } 439 440 @Override 441 public KeyValuePairComponentBuilder newKeyValuePair(final String key, final String value) { 442 return new DefaultKeyValuePairComponentBuilder(this, key, value); 443 } 444 445 @Override 446 public CustomLevelComponentBuilder newCustomLevel(final String name, final int level) { 447 return new DefaultCustomLevelComponentBuilder(this, name, level); 448 } 449 450 @Override 451 public FilterComponentBuilder newFilter(final String type, final Filter.Result onMatch, 452 final Filter.Result onMismatch) { 453 return new DefaultFilterComponentBuilder(this, type, onMatch.name(), onMismatch.name()); 454 } 455 456 @Override 457 public FilterComponentBuilder newFilter(final String type, final String onMatch, final String onMismatch) { 458 return new DefaultFilterComponentBuilder(this, type, onMatch, onMismatch); 459 } 460 461 @Override 462 public LayoutComponentBuilder newLayout(final String type) { 463 return new DefaultLayoutComponentBuilder(this, type); 464 } 465 466 @Override 467 public LoggerComponentBuilder newLogger(final String name) { 468 return new DefaultLoggerComponentBuilder(this, name, null); 469 } 470 471 @Override 472 public LoggerComponentBuilder newLogger(final String name, final boolean includeLocation) { 473 return new DefaultLoggerComponentBuilder(this, name, null, includeLocation); 474 } 475 476 @Override 477 public LoggerComponentBuilder newLogger(final String name, final Level level) { 478 return new DefaultLoggerComponentBuilder(this, name, level.toString()); 479 } 480 481 @Override 482 public LoggerComponentBuilder newLogger(final String name, final Level level, final boolean includeLocation) { 483 return new DefaultLoggerComponentBuilder(this, name, level.toString(), includeLocation); 484 } 485 486 @Override 487 public LoggerComponentBuilder newLogger(final String name, final String level) { 488 return new DefaultLoggerComponentBuilder(this, name, level); 489 } 490 491 @Override 492 public LoggerComponentBuilder newLogger(final String name, final String level, final boolean includeLocation) { 493 return new DefaultLoggerComponentBuilder(this, name, level, includeLocation); 494 } 495 496 @Override 497 public RootLoggerComponentBuilder newRootLogger() { 498 return new DefaultRootLoggerComponentBuilder(this, null); 499 } 500 501 @Override 502 public RootLoggerComponentBuilder newRootLogger(final boolean includeLocation) { 503 return new DefaultRootLoggerComponentBuilder(this, null, includeLocation); 504 } 505 506 @Override 507 public RootLoggerComponentBuilder newRootLogger(final Level level) { 508 return new DefaultRootLoggerComponentBuilder(this, level.toString()); 509 } 510 511 @Override 512 public RootLoggerComponentBuilder newRootLogger(final Level level, final boolean includeLocation) { 513 return new DefaultRootLoggerComponentBuilder(this, level.toString(), includeLocation); 514 } 515 516 @Override 517 public RootLoggerComponentBuilder newRootLogger(final String level) { 518 return new DefaultRootLoggerComponentBuilder(this, level); 519 } 520 521 @Override 522 public RootLoggerComponentBuilder newRootLogger(final String level, final boolean includeLocation) { 523 return new DefaultRootLoggerComponentBuilder(this, level, includeLocation); 524 } 525 526 @Override 527 public ConfigurationBuilder<T> setAdvertiser(final String advertiser) { 528 this.advertiser = advertiser; 529 return this; 530 } 531 532 /** 533 * Set the name of the configuration. 534 * 535 * @param name the name of the {@link Configuration}. By default is {@code "Assembled"}. 536 * @return this builder instance 537 */ 538 @Override 539 public ConfigurationBuilder<T> setConfigurationName(final String name) { 540 this.name = name; 541 return this; 542 } 543 544 /** 545 * Set the ConfigurationSource. 546 * 547 * @param configurationSource the {@link ConfigurationSource} 548 * @return this builder instance 549 */ 550 @Override 551 public ConfigurationBuilder<T> setConfigurationSource(final ConfigurationSource configurationSource) { 552 source = configurationSource; 553 return this; 554 } 555 556 @Override 557 public ConfigurationBuilder<T> setMonitorInterval(final String intervalSeconds) { 558 monitorInterval = Integer.parseInt(intervalSeconds); 559 return this; 560 } 561 562 @Override 563 public ConfigurationBuilder<T> setPackages(final String packages) { 564 this.packages = packages; 565 return this; 566 } 567 568 @Override 569 public ConfigurationBuilder<T> setShutdownHook(final String flag) { 570 this.shutdownFlag = flag; 571 return this; 572 } 573 574 @Override 575 public ConfigurationBuilder<T> setShutdownTimeout(final long timeout, final TimeUnit timeUnit) { 576 this.shutdownTimeoutMillis = timeUnit.toMillis(timeout); 577 return this; 578 } 579 580 @Override 581 public ConfigurationBuilder<T> setStatusLevel(final Level level) { 582 this.level = level; 583 return this; 584 } 585 586 @Override 587 public ConfigurationBuilder<T> setVerbosity(final String verbosity) { 588 this.verbosity = verbosity; 589 return this; 590 } 591 592 @Override 593 public ConfigurationBuilder<T> setDestination(final String destination) { 594 this.destination = destination; 595 return this; 596 } 597 598 @Override 599 public void setLoggerContext(final LoggerContext loggerContext) { 600 this.loggerContext = loggerContext; 601 } 602 603 @Override 604 public ConfigurationBuilder<T> addRootProperty(final String key, final String value) { 605 root.getAttributes().put(key, value); 606 return this; 607 } 608 609}