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 */
017
018package org.apache.logging.log4j.core.appender;
019
020import java.io.Serializable;
021import java.net.URL;
022import java.util.Objects;
023import java.util.concurrent.TimeUnit;
024
025import org.apache.logging.log4j.core.Appender;
026import org.apache.logging.log4j.core.Filter;
027import org.apache.logging.log4j.core.Layout;
028import org.apache.logging.log4j.core.LogEvent;
029import org.apache.logging.log4j.core.config.Node;
030import org.apache.logging.log4j.core.config.Property;
031import org.apache.logging.log4j.core.config.plugins.Plugin;
032import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
033import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
034import org.apache.logging.log4j.core.config.plugins.PluginElement;
035import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
036import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
037
038/**
039 * Sends log events over HTTP.
040 */
041@Plugin(name = "Http", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
042public final class HttpAppender extends AbstractAppender {
043
044    /**
045     * Builds HttpAppender instances.
046     * @param <B> The type to build
047     */
048    public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
049            implements org.apache.logging.log4j.core.util.Builder<HttpAppender> {
050
051        @PluginBuilderAttribute
052        @Required(message = "No URL provided for HttpAppender")
053        private URL url;
054
055        @PluginBuilderAttribute
056        private String method = "POST";
057
058        @PluginBuilderAttribute
059        private int connectTimeoutMillis = 0;
060
061        @PluginBuilderAttribute
062        private int readTimeoutMillis = 0;
063
064        @PluginElement("Headers")
065        private Property[] headers;
066
067        @PluginElement("SslConfiguration")
068        private SslConfiguration sslConfiguration;
069
070        @PluginBuilderAttribute
071        private boolean verifyHostname = true;
072
073        @Override
074        public HttpAppender build() {
075            final HttpManager httpManager = new HttpURLConnectionManager(getConfiguration(),
076                    getConfiguration().getLoggerContext(), getName(), url, method, connectTimeoutMillis,
077                    readTimeoutMillis, headers, sslConfiguration, verifyHostname);
078            return new HttpAppender(getName(), getLayout(), getFilter(), isIgnoreExceptions(), httpManager,
079                    getPropertyArray());
080        }
081
082        public URL getUrl() {
083            return url;
084        }
085
086        public String getMethod() {
087            return method;
088        }
089
090        public int getConnectTimeoutMillis() {
091            return connectTimeoutMillis;
092        }
093
094        public int getReadTimeoutMillis() {
095            return readTimeoutMillis;
096        }
097
098        public Property[] getHeaders() {
099            return headers;
100        }
101
102        public SslConfiguration getSslConfiguration() {
103            return sslConfiguration;
104        }
105
106        public boolean isVerifyHostname() {
107            return verifyHostname;
108        }
109
110        public B setUrl(final URL url) {
111            this.url = url;
112            return asBuilder();
113        }
114
115        public B setMethod(final String method) {
116            this.method = method;
117            return asBuilder();
118        }
119
120        public B setConnectTimeoutMillis(final int connectTimeoutMillis) {
121            this.connectTimeoutMillis = connectTimeoutMillis;
122            return asBuilder();
123        }
124
125        public B setReadTimeoutMillis(final int readTimeoutMillis) {
126            this.readTimeoutMillis = readTimeoutMillis;
127            return asBuilder();
128        }
129
130        public B setHeaders(final Property[] headers) {
131            this.headers = headers;
132            return asBuilder();
133        }
134
135        public B setSslConfiguration(final SslConfiguration sslConfiguration) {
136            this.sslConfiguration = sslConfiguration;
137            return asBuilder();
138        }
139
140        public B setVerifyHostname(final boolean verifyHostname) {
141            this.verifyHostname = verifyHostname;
142            return asBuilder();
143        }
144    }
145
146    /**
147     * @return a builder for a HttpAppender.
148     */
149    @PluginBuilderFactory
150    public static <B extends Builder<B>> B newBuilder() {
151        return new Builder<B>().asBuilder();
152    }
153
154    private final HttpManager manager;
155
156    private HttpAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
157            final boolean ignoreExceptions, final HttpManager manager, final Property[] properties) {
158        super(name, filter, layout, ignoreExceptions, properties);
159        Objects.requireNonNull(layout, "layout");
160        this.manager = Objects.requireNonNull(manager, "manager");
161    }
162
163    @Override
164    public void start() {
165        super.start();
166        manager.startup();
167    }
168
169    @Override
170    public void append(final LogEvent event) {
171        try {
172            manager.send(getLayout(), event);
173        } catch (final Exception e) {
174            error("Unable to send HTTP in appender [" + getName() + "]", event, e);
175        }
176    }
177
178    @Override
179    public boolean stop(final long timeout, final TimeUnit timeUnit) {
180        setStopping();
181        boolean stopped = super.stop(timeout, timeUnit, false);
182        stopped &= manager.stop(timeout, timeUnit);
183        setStopped();
184        return stopped;
185    }
186
187    @Override
188    public String toString() {
189        return "HttpAppender{" +
190            "name=" + getName() +
191            ", state=" + getState() +
192            '}';
193    }
194}