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.builders.appender;
18  
19  import static org.apache.log4j.builders.BuilderManager.CATEGORY;
20  import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
21  import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
22  import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
23  import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
24  import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
25  import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
26  import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
27  
28  import java.io.Serializable;
29  import java.util.Properties;
30  import java.util.concurrent.atomic.AtomicInteger;
31  import java.util.concurrent.atomic.AtomicReference;
32  
33  import org.apache.log4j.Appender;
34  import org.apache.log4j.Layout;
35  import org.apache.log4j.bridge.AppenderWrapper;
36  import org.apache.log4j.bridge.LayoutAdapter;
37  import org.apache.log4j.bridge.LayoutWrapper;
38  import org.apache.log4j.builders.AbstractBuilder;
39  import org.apache.log4j.config.Log4j1Configuration;
40  import org.apache.log4j.config.PropertiesConfiguration;
41  import org.apache.log4j.spi.Filter;
42  import org.apache.log4j.xml.XmlConfiguration;
43  import org.apache.logging.log4j.Logger;
44  import org.apache.logging.log4j.core.appender.SocketAppender;
45  import org.apache.logging.log4j.core.config.plugins.Plugin;
46  import org.apache.logging.log4j.core.layout.SyslogLayout;
47  import org.apache.logging.log4j.core.net.Facility;
48  import org.apache.logging.log4j.core.net.Protocol;
49  import org.apache.logging.log4j.status.StatusLogger;
50  import org.w3c.dom.Element;
51  
52  /**
53   * Build a File Appender
54   */
55  @Plugin(name = "org.apache.log4j.net.SyslogAppender", category = CATEGORY)
56  public class SyslogAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
57  
58      private static final String DEFAULT_HOST = "localhost";
59      private static int DEFAULT_PORT = 514;
60      private static final String DEFAULT_FACILITY = "LOCAL0";
61  
62      private static final Logger LOGGER = StatusLogger.getLogger();
63      private static final String FACILITY_PARAM = "Facility";
64      private static final String SYSLOG_HOST_PARAM = "SyslogHost";
65      private static final String PROTOCOL_PARAM = "protocol";
66  
67  
68      public SyslogAppenderBuilder() {
69      }
70  
71      public SyslogAppenderBuilder(String prefix, Properties props) {
72          super(prefix, props);
73      }
74  
75      @Override
76      public Appender parseAppender(Element appenderElement, XmlConfiguration config) {
77          String name = appenderElement.getAttribute(NAME_ATTR);
78          AtomicReference<Layout> layout = new AtomicReference<>();
79          AtomicReference<Filter> filter = new AtomicReference<>();
80          AtomicReference<String> facility = new AtomicReference<>();
81          AtomicReference<String> level = new AtomicReference<>();
82          AtomicReference<String> host = new AtomicReference<>();
83          AtomicReference<Protocol> protocol = new AtomicReference<>();
84          forEachElement(appenderElement.getChildNodes(), currentElement -> {
85              switch (currentElement.getTagName()) {
86                  case LAYOUT_TAG:
87                      layout.set(config.parseLayout(currentElement));
88                      break;
89                  case FILTER_TAG:
90                      filter.set(config.parseFilters(currentElement));
91                      break;
92                  case PARAM_TAG: {
93                      switch (currentElement.getAttribute(NAME_ATTR)) {
94                          case SYSLOG_HOST_PARAM: {
95                              host.set(currentElement.getAttribute(VALUE_ATTR));
96                              break;
97                          }
98                          case FACILITY_PARAM:
99                              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 }