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.appender; 018 019import java.io.Writer; 020 021import org.apache.logging.log4j.core.Appender; 022import org.apache.logging.log4j.core.Core; 023import org.apache.logging.log4j.core.Filter; 024import org.apache.logging.log4j.core.StringLayout; 025import org.apache.logging.log4j.core.config.Property; 026import org.apache.logging.log4j.core.config.plugins.Plugin; 027import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 028import org.apache.logging.log4j.core.config.plugins.PluginFactory; 029import org.apache.logging.log4j.core.layout.PatternLayout; 030import org.apache.logging.log4j.core.util.CloseShieldWriter; 031 032/** 033 * Appends log events to a {@link Writer}. 034 */ 035@Plugin(name = "Writer", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) 036public final class WriterAppender extends AbstractWriterAppender<WriterManager> { 037 038 /** 039 * Builds WriterAppender instances. 040 */ 041 public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B> 042 implements org.apache.logging.log4j.core.util.Builder<WriterAppender> { 043 044 private boolean follow = false; 045 046 private Writer target; 047 048 @Override 049 public WriterAppender build() { 050 final StringLayout layout = (StringLayout) getLayout(); 051 final StringLayout actualLayout = layout != null ? layout : PatternLayout.createDefaultLayout(); 052 return new WriterAppender(getName(), actualLayout, getFilter(), getManager(target, follow, actualLayout), 053 isIgnoreExceptions(), getPropertyArray()); 054 } 055 056 public B setFollow(final boolean shouldFollow) { 057 this.follow = shouldFollow; 058 return asBuilder(); 059 } 060 061 public B setTarget(final Writer aTarget) { 062 this.target = aTarget; 063 return asBuilder(); 064 } 065 } 066 /** 067 * Holds data to pass to factory method. 068 */ 069 private static class FactoryData { 070 private final StringLayout layout; 071 private final String name; 072 private final Writer writer; 073 074 /** 075 * Builds instances. 076 * 077 * @param writer 078 * The OutputStream. 079 * @param type 080 * The name of the target. 081 * @param layout 082 * A String layout 083 */ 084 public FactoryData(final Writer writer, final String type, final StringLayout layout) { 085 this.writer = writer; 086 this.name = type; 087 this.layout = layout; 088 } 089 } 090 091 private static class WriterManagerFactory implements ManagerFactory<WriterManager, FactoryData> { 092 093 /** 094 * Creates a WriterManager. 095 * 096 * @param name 097 * The name of the entity to manage. 098 * @param data 099 * The data required to create the entity. 100 * @return The WriterManager 101 */ 102 @Override 103 public WriterManager createManager(final String name, final FactoryData data) { 104 return new WriterManager(data.writer, data.name, data.layout, true); 105 } 106 } 107 108 private static WriterManagerFactory factory = new WriterManagerFactory(); 109 110 /** 111 * Creates a WriterAppender. 112 * 113 * @param layout 114 * The layout to use or null to get the default layout. 115 * @param filter 116 * The Filter or null. 117 * @param target 118 * The target Writer 119 * @param follow 120 * If true will follow changes to the underlying output stream. 121 * Use false as the default. 122 * @param name 123 * The name of the Appender (required). 124 * @param ignore 125 * If {@code "true"} (default) exceptions encountered when 126 * appending events are logged; otherwise they are propagated to 127 * the caller. Use true as the default. 128 * @return The ConsoleAppender. 129 */ 130 @PluginFactory 131 public static WriterAppender createAppender(StringLayout layout, final Filter filter, final Writer target, 132 final String name, final boolean follow, final boolean ignore) { 133 if (name == null) { 134 LOGGER.error("No name provided for WriterAppender"); 135 return null; 136 } 137 if (layout == null) { 138 layout = PatternLayout.createDefaultLayout(); 139 } 140 return new WriterAppender(name, layout, filter, getManager(target, follow, layout), ignore, null); 141 } 142 143 private static WriterManager getManager(final Writer target, final boolean follow, final StringLayout layout) { 144 final Writer writer = new CloseShieldWriter(target); 145 final String managerName = target.getClass().getName() + "@" + Integer.toHexString(target.hashCode()) + '.' 146 + follow; 147 return WriterManager.getManager(managerName, new FactoryData(writer, managerName, layout), factory); 148 } 149 150 @PluginBuilderFactory 151 public static <B extends Builder<B>> B newBuilder() { 152 return new Builder<B>().asBuilder(); 153 } 154 155 private WriterAppender(final String name, final StringLayout layout, final Filter filter, 156 final WriterManager manager, final boolean ignoreExceptions, final Property[] properties) { 157 super(name, layout, filter, ignoreExceptions, true, properties, manager); 158 } 159 160}