Package com.splunk.logging

Source Code of com.splunk.logging.TcpAppender

package com.splunk.logging;
/*
* Copyright 2013-2014 Splunk, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"): you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import ch.qos.logback.core.Layout;
import ch.qos.logback.core.net.DefaultSocketConnector;
import ch.qos.logback.core.net.SocketConnector;
import ch.qos.logback.core.util.CloseUtil;

import javax.net.SocketFactory;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.*;
import java.util.concurrent.*;

/**
* Logback Appender which writes its events to a TCP port.
*
* This class is based on the logic of Logback's SocketAppender, but does not try to serialize Java
* objects for deserialization and logging elsewhere.
*/
public class TcpAppender extends AppenderBase<ILoggingEvent> implements Runnable, SocketConnector.ExceptionHandler {
    private static final int DEFAULT_RECONNECTION_DELAY = 30000; // in ms
    private static final int DEFAULT_QUEUE_SIZE = 0;
    private static final int DEFAULT_ACCEPT_CONNECTION_DELAY = 5000;

    private String host;
    private int port;
    private InetAddress address;

    private Layout<ILoggingEvent> layout;
    private Future<?> task;
    private Future<Socket> connectorTask;

    private int reconnectionDelay = DEFAULT_RECONNECTION_DELAY;
    private int queueSize = DEFAULT_QUEUE_SIZE;
    private int acceptConnectionTimeout = DEFAULT_ACCEPT_CONNECTION_DELAY;

    private BlockingQueue<ILoggingEvent> queue;

    // The socket will be modified by the another thread (in SocketConnector) which
    // handles reconnection of dropped connections.
    private volatile Socket socket;

    // The appender is created by Logback calling a superclass constructor with no arguments.
    // Then it calls setters (and the setters defined by the class define what arguments are
    // understood. Once all the fields have been set, Logback calls start(), When shutting down,
    // Logback calls stop().
    //
    // start() queues the appender as a Runnable, so run() eventually gets invoked to do the
    // actual work. run() opens a port using Logback utilities that reconnect when a connection
    // is lost, and then block on a queue of events, writing them to TCP as soon as they
    // become available.
    //
    // The append method, which Logback logging calls invoke, pushes events to that queue and nothing else.

    @Override
    public void connectionFailed(SocketConnector socketConnector, Exception e) {
        if (e instanceof InterruptedException) {
            addInfo("connector interrupted");
        } else if (e instanceof ConnectException) {
            addInfo(host + ":" + port + " connection refused");
        } else {
            addInfo(host + ":" + port + " " + e);
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                SocketConnector connector = new DefaultSocketConnector(address, port, 0, reconnectionDelay);
                connector.setExceptionHandler(this);
                connector.setSocketFactory(SocketFactory.getDefault());

                try {
                    connectorTask = getContext().getExecutorService().submit(connector);
                } catch (RejectedExecutionException e) {
                    connectorTask = null;
                    break;
                }

                try {
                    socket = connectorTask.get();
                    connectorTask = null;
                } catch (ExecutionException e) {
                    socket = null;
                    break;
                }

                try {
                    socket.setSoTimeout(acceptConnectionTimeout);
                    OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
                    socket.setSoTimeout(0);

                    addInfo(host + ":" + port + " connection established");

                    while (true) {
                        ILoggingEvent event = queue.take();
                        String formatted = layout.doLayout(event);
                        writer.write(formatted);
                        writer.flush();
                    }
                } catch (SocketException e) {
                    addInfo(host + ":" + port + " connection failed: " + e);
                } catch (IOException e) {
                    CloseUtil.closeQuietly(socket);
                    socket = null;
                    addInfo(host + ":" + port + " connection closed");
                }
            }
        } catch (InterruptedException ex) {
            // Exiting.
        }
        addInfo("shutting down");
    }

    @Override
    public void start() {
        if (started) {
            return;
        }

        boolean errorPresent = false;

        // Handle options
        if (port <= 0) {
            errorPresent = true;
            addError("No port was configured for appender"
                    + name
                    + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_port");
        }

        if (host == null) {
            errorPresent = true;
            addError("No remote host was configured for appender"
                    + name
                    + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_host");
        }

        if (queueSize < 0) {
            errorPresent = true;
            addError("Queue size must be non-negative");
        }

        if (this.layout == null) {
            addError("No layout set for the appender named [" + name + "].");
            errorPresent = true;
        }

        if (!errorPresent) {
            try {
                address = InetAddress.getByName(host);
            } catch (UnknownHostException ex) {
                addError("unknown host: " + host);
                errorPresent = true;
            }
        }


        try {
            address = InetAddress.getByName(host);
        } catch (UnknownHostException e) {
            addError("Unknown host: " + host);
            errorPresent = true;
        }

        // Dispatch this instance of the appender.
        if (!errorPresent) {
            queue = queueSize <= 0 ? new SynchronousQueue<ILoggingEvent>() : new ArrayBlockingQueue<ILoggingEvent>(queueSize);
            task = getContext().getExecutorService().submit(this);
        }

        super.start();
    }

    @Override
    public void stop() {
        if (!started)
            return;

        CloseUtil.closeQuietly(socket);
        task.cancel(true);
        if(connectorTask != null) {
            connectorTask.cancel(true);
        }
        super.stop();
    }

    @Override
    protected void append(ILoggingEvent event) {
        // Get runtime information now, rather than when
        // the event is actually logged, so that it has
        // the right thread and environment information.
        event.prepareForDeferredProcessing();
        event.getCallerData();

        // Append to the queue to be logged.
        if (event != null && started)
            queue.offer(event);
    }

    // The setters are peculiar here. They are used by Logback (via reflection) to set
    // the parameters of the appender, but they should never be called except by
    // Logback before start() is called.
    public void setRemoteHost(String host) { this.host = host; }
    public String getRemoteHost() { return this.host; }

    public void setPort(int port) { this.port = port; }
    public int getPort() { return this.port; }

    public void setReconnectionDelay(int reconnectionDelay) { this.reconnectionDelay = reconnectionDelay; }
    public int getReconnectionDelay() { return this.reconnectionDelay; }

    public void setQueueSize(int queueSize) { this.queueSize = queueSize; }
    public int getQueueSize() { return this.queueSize; }

    public void setLayout(Layout<ILoggingEvent> layout) { this.layout = layout; }
    public Layout<ILoggingEvent> getLayout() { return this.layout; }
}
TOP

Related Classes of com.splunk.logging.TcpAppender

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.