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.log4j.builders.appender; 018 019import static org.apache.log4j.builders.BuilderManager.CATEGORY; 020import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM; 021import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG; 022import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG; 023import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR; 024import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG; 025import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR; 026import static org.apache.log4j.xml.XmlConfiguration.forEachElement; 027 028import java.io.Serializable; 029import java.util.Properties; 030import java.util.concurrent.atomic.AtomicInteger; 031import java.util.concurrent.atomic.AtomicReference; 032 033import org.apache.log4j.Appender; 034import org.apache.log4j.Layout; 035import org.apache.log4j.bridge.AppenderWrapper; 036import org.apache.log4j.bridge.LayoutAdapter; 037import org.apache.log4j.bridge.LayoutWrapper; 038import org.apache.log4j.builders.AbstractBuilder; 039import org.apache.log4j.config.Log4j1Configuration; 040import org.apache.log4j.config.PropertiesConfiguration; 041import org.apache.log4j.spi.Filter; 042import org.apache.log4j.xml.XmlConfiguration; 043import org.apache.logging.log4j.Logger; 044import org.apache.logging.log4j.core.appender.SocketAppender; 045import org.apache.logging.log4j.core.config.plugins.Plugin; 046import org.apache.logging.log4j.core.layout.SyslogLayout; 047import org.apache.logging.log4j.core.net.Facility; 048import org.apache.logging.log4j.core.net.Protocol; 049import org.apache.logging.log4j.status.StatusLogger; 050import org.w3c.dom.Element; 051 052/** 053 * Build a File Appender 054 */ 055@Plugin(name = "org.apache.log4j.net.SyslogAppender", category = CATEGORY) 056public class SyslogAppenderBuilder extends AbstractBuilder implements AppenderBuilder { 057 058 private static final String DEFAULT_HOST = "localhost"; 059 private static int DEFAULT_PORT = 514; 060 private static final String DEFAULT_FACILITY = "LOCAL0"; 061 062 private static final Logger LOGGER = StatusLogger.getLogger(); 063 private static final String FACILITY_PARAM = "Facility"; 064 private static final String SYSLOG_HOST_PARAM = "SyslogHost"; 065 private static final String PROTOCOL_PARAM = "protocol"; 066 067 068 public SyslogAppenderBuilder() { 069 } 070 071 public SyslogAppenderBuilder(String prefix, Properties props) { 072 super(prefix, props); 073 } 074 075 @Override 076 public Appender parseAppender(Element appenderElement, XmlConfiguration config) { 077 String name = appenderElement.getAttribute(NAME_ATTR); 078 AtomicReference<Layout> layout = new AtomicReference<>(); 079 AtomicReference<Filter> filter = new AtomicReference<>(); 080 AtomicReference<String> facility = new AtomicReference<>(); 081 AtomicReference<String> level = new AtomicReference<>(); 082 AtomicReference<String> host = new AtomicReference<>(); 083 AtomicReference<Protocol> protocol = new AtomicReference<>(); 084 forEachElement(appenderElement.getChildNodes(), currentElement -> { 085 switch (currentElement.getTagName()) { 086 case LAYOUT_TAG: 087 layout.set(config.parseLayout(currentElement)); 088 break; 089 case FILTER_TAG: 090 filter.set(config.parseFilters(currentElement)); 091 break; 092 case PARAM_TAG: { 093 switch (currentElement.getAttribute(NAME_ATTR)) { 094 case SYSLOG_HOST_PARAM: { 095 host.set(currentElement.getAttribute(VALUE_ATTR)); 096 break; 097 } 098 case FACILITY_PARAM: 099 facility.set(currentElement.getAttribute(VALUE_ATTR)); 100 break; 101 case THRESHOLD_PARAM: { 102 String value = currentElement.getAttribute(VALUE_ATTR); 103 if (value == null) { 104 LOGGER.warn("No value supplied for Threshold parameter, ignoring."); 105 } else { 106 level.set(value); 107 } 108 break; 109 } 110 case PROTOCOL_PARAM: 111 protocol.set(Protocol.valueOf(currentElement.getAttribute(VALUE_ATTR))); 112 break; 113 } 114 break; 115 } 116 } 117 }); 118 119 return createAppender(name, config, layout.get(), facility.get(), filter.get(), host.get(), level.get(), protocol.get()); 120 } 121 122 123 @Override 124 public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix, 125 final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) { 126 Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name); 127 Layout layout = configuration.parseLayout(layoutPrefix, name, props); 128 String level = getProperty(THRESHOLD_PARAM); 129 String facility = getProperty(FACILITY_PARAM, DEFAULT_FACILITY); 130 String syslogHost = getProperty(SYSLOG_HOST_PARAM, DEFAULT_HOST + ":" + DEFAULT_PORT); 131 String protocol = getProperty(PROTOCOL_PARAM, Protocol.TCP.name()); 132 133 return createAppender(name, configuration, layout, facility, filter, syslogHost, level, Protocol.valueOf(protocol)); 134 } 135 136 private Appender createAppender(final String name, final Log4j1Configuration configuration, Layout layout, 137 String facility, final Filter filter, final String syslogHost, final String level, final Protocol protocol) { 138 AtomicReference<String> host = new AtomicReference<>(); 139 AtomicInteger port = new AtomicInteger(); 140 resolveSyslogHost(syslogHost, host, port); 141 org.apache.logging.log4j.core.Layout<? extends Serializable> appenderLayout; 142 if (layout instanceof LayoutWrapper) { 143 appenderLayout = ((LayoutWrapper) layout).getLayout(); 144 } else if (layout != null) { 145 appenderLayout = new LayoutAdapter(layout); 146 } else { 147 appenderLayout = SyslogLayout.newBuilder() 148 .setFacility(Facility.toFacility(facility)) 149 .setConfiguration(configuration) 150 .build(); 151 } 152 153 org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter); 154 return new AppenderWrapper(SocketAppender.newBuilder() 155 .setName(name) 156 .setConfiguration(configuration) 157 .setLayout(appenderLayout) 158 .setFilter(fileFilter) 159 .withPort(port.get()) 160 .withProtocol(protocol) 161 .withHost(host.get()) 162 .build()); 163 } 164 165 private void resolveSyslogHost(String syslogHost, AtomicReference<String> host, AtomicInteger port) { 166 // 167 // If not an unbracketed IPv6 address then 168 // parse as a URL 169 // 170 String[] parts = syslogHost.split(":"); 171 if (parts.length == 1) { 172 host.set(parts[0]); 173 port.set(DEFAULT_PORT); 174 } else if (parts.length == 2) { 175 host.set(parts[0]); 176 port.set(Integer.parseInt(parts[1])); 177 } else { 178 LOGGER.warn("Invalid {} setting: {}. Using default.", SYSLOG_HOST_PARAM, syslogHost); 179 host.set(DEFAULT_HOST); 180 port.set(DEFAULT_PORT); 181 } 182 } 183}