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(), getConfiguration().getLoggerContext(),
076                getName(), url, method, connectTimeoutMillis, readTimeoutMillis, headers, sslConfiguration, verifyHostname);
077            return new HttpAppender(getName(), getLayout(), getFilter(), isIgnoreExceptions(), httpManager);
078        }
079
080        public URL getUrl() {
081            return url;
082        }
083
084        public String getMethod() {
085            return method;
086        }
087
088        public int getConnectTimeoutMillis() {
089            return connectTimeoutMillis;
090        }
091
092        public int getReadTimeoutMillis() {
093            return readTimeoutMillis;
094        }
095
096        public Property[] getHeaders() {
097            return headers;
098        }
099
100        public SslConfiguration getSslConfiguration() {
101            return sslConfiguration;
102        }
103
104        public boolean isVerifyHostname() {
105            return verifyHostname;
106        }
107
108        public B setUrl(final URL url) {
109            this.url = url;
110            return asBuilder();
111        }
112
113        public B setMethod(final String method) {
114            this.method = method;
115            return asBuilder();
116        }
117
118        public B setConnectTimeoutMillis(final int connectTimeoutMillis) {
119            this.connectTimeoutMillis = connectTimeoutMillis;
120            return asBuilder();
121        }
122
123        public B setReadTimeoutMillis(final int readTimeoutMillis) {
124            this.readTimeoutMillis = readTimeoutMillis;
125            return asBuilder();
126        }
127
128        public B setHeaders(final Property[] headers) {
129            this.headers = headers;
130            return asBuilder();
131        }
132
133        public B setSslConfiguration(final SslConfiguration sslConfiguration) {
134            this.sslConfiguration = sslConfiguration;
135            return asBuilder();
136        }
137
138        public B setVerifyHostname(final boolean verifyHostname) {
139            this.verifyHostname = verifyHostname;
140            return asBuilder();
141        }
142    }
143
144    /**
145     * @return a builder for a HttpAppender.
146     */
147    @PluginBuilderFactory
148    public static <B extends Builder<B>> B newBuilder() {
149        return new Builder<B>().asBuilder();
150    }
151
152    private final HttpManager manager;
153
154    private HttpAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
155                         final boolean ignoreExceptions, final HttpManager manager) {
156        super(name, filter, layout, ignoreExceptions);
157        Objects.requireNonNull(layout, "layout");
158        this.manager = Objects.requireNonNull(manager, "manager");
159    }
160
161    @Override
162    public void start() {
163        super.start();
164        manager.startup();
165    }
166
167    @Override
168    public void append(final LogEvent event) {
169        try {
170            manager.send(getLayout(), event);
171        } catch (final Exception e) {
172            error("Unable to send HTTP in appender [" + getName() + "]", event, e);
173        }
174    }
175
176    @Override
177    public boolean stop(final long timeout, final TimeUnit timeUnit) {
178        setStopping();
179        boolean stopped = super.stop(timeout, timeUnit, false);
180        stopped &= manager.stop(timeout, timeUnit);
181        setStopped();
182        return stopped;
183    }
184
185    @Override
186    public String toString() {
187        return "HttpAppender{" +
188            "name=" + getName() +
189            ", state=" + getState() +
190            '}';
191    }
192}