All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.getsentry.raven.connection.AsyncConnection Maven / Gradle / Ivy

There is a newer version: 8.0.3
Show newest version
package com.getsentry.raven.connection;

import com.getsentry.raven.Raven;
import com.getsentry.raven.environment.RavenEnvironment;
import com.getsentry.raven.event.Event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Asynchronous usage of a connection.
 * 

* Instead of synchronously sending each event to a connection, use a ThreadPool to establish the connection * and submit the event. */ public class AsyncConnection implements Connection { private static final Logger logger = LoggerFactory.getLogger(AsyncConnection.class); // CHECKSTYLE.OFF: ConstantName private static final Logger lockdownLogger = LoggerFactory.getLogger(Raven.class.getName() + ".lockdown"); // CHECKSTYLE.ON: ConstantName /** * Timeout of the {@link #executorService}, in milliseconds. */ private final long shutdownTimeout; /** * Connection used to actually send the events. */ private final Connection actualConnection; /** * Executor service in charge of running the connection in separate threads. */ private final ExecutorService executorService; /** * Shutdown hook used to stop the async connection properly when the JVM quits. */ private final ShutDownHook shutDownHook = new ShutDownHook(); /** * Boolean that represents if graceful shutdown is enabled. */ private boolean gracefulShutdown; /** * Boolean used to check whether the connection is still open or not. */ private volatile boolean closed; /** * Creates a connection which will rely on an executor to send events. *

* Will propagate the {@link #close()} operation. * * @param actualConnection connection used to send the events. * @param executorService executorService used to process events, if null, the executorService will automatically * be set to {@code Executors.newSingleThreadExecutor()} * @param gracefulShutdown Indicates whether or not the shutdown operation should be managed by a ShutdownHook. * @param shutdownTimeout timeout for graceful shutdown of the executor, in milliseconds. */ public AsyncConnection(Connection actualConnection, ExecutorService executorService, boolean gracefulShutdown, long shutdownTimeout) { this.actualConnection = actualConnection; if (executorService == null) { this.executorService = Executors.newSingleThreadExecutor(); } else { this.executorService = executorService; } if (gracefulShutdown) { this.gracefulShutdown = gracefulShutdown; addShutdownHook(); } this.shutdownTimeout = shutdownTimeout; } /** * Adds a hook to shutdown the {@link #executorService} gracefully when the JVM shuts down. */ private void addShutdownHook() { // JUL loggers are shutdown by an other shutdown hook, it's possible that nothing will get actually logged. Runtime.getRuntime().addShutdownHook(shutDownHook); } /** * {@inheritDoc} *

* The event will be added to a queue and will be handled by a separate {@code Thread} later on. */ @Override public void send(Event event) { if (!closed) { executorService.execute(new EventSubmitter(event)); } } @Override public void addEventSendFailureCallback(EventSendFailureCallback eventSendFailureCallback) { actualConnection.addEventSendFailureCallback(eventSendFailureCallback); } /** * {@inheritDoc}. *

* Closing the {@link AsyncConnection} will attempt a graceful shutdown of the {@link #executorService} with a * timeout of {@link #shutdownTimeout}, allowing the current events to be submitted while new events will * be rejected.
* If the shutdown times out, the {@code executorService} will be forced to shutdown. */ @Override public void close() throws IOException { if (gracefulShutdown) { shutDownHook.enabled = false; } doClose(); } /** * Close the connection whether it's from the shutdown hook or not. * * @see #close() */ @SuppressWarnings("checkstyle:magicnumber") private void doClose() throws IOException { logger.info("Gracefully shutdown sentry threads."); closed = true; executorService.shutdown(); try { if (shutdownTimeout == -1L) { // Block until the executor terminates, but log periodically. long waitBetweenLoggingMs = 5000L; while (true) { if (executorService.awaitTermination(waitBetweenLoggingMs, TimeUnit.MILLISECONDS)) { break; } logger.info("Still waiting on async executor to terminate."); } } else if (!executorService.awaitTermination(shutdownTimeout, TimeUnit.MILLISECONDS)) { logger.warn("Graceful shutdown took too much time, forcing the shutdown."); List tasks = executorService.shutdownNow(); logger.info("{} tasks failed to execute before the shutdown.", tasks.size()); } logger.info("Shutdown finished."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.error("Graceful shutdown interrupted, forcing the shutdown."); List tasks = executorService.shutdownNow(); logger.info("{} tasks failed to execute before the shutdown.", tasks.size()); } finally { actualConnection.close(); } } /** * Simple runnable using the {@link #send(com.getsentry.raven.event.Event)} method of the * {@link #actualConnection}. */ private final class EventSubmitter implements Runnable { private final Event event; private EventSubmitter(Event event) { this.event = event; } @Override public void run() { RavenEnvironment.startManagingThread(); try { // The current thread is managed by raven actualConnection.send(event); } catch (LockedDownException e) { lockdownLogger.warn("The connection to Sentry is currently locked down.", e); } catch (Exception e) { logger.error("An exception occurred while sending the event to Sentry.", e); } finally { RavenEnvironment.stopManagingThread(); } } } private final class ShutDownHook extends Thread { /** * Whether or not this ShutDownHook instance will do anything when run. */ private volatile boolean enabled = true; @Override public void run() { if (!enabled) { return; } RavenEnvironment.startManagingThread(); try { // The current thread is managed by raven logger.info("Automatic shutdown of the async connection"); AsyncConnection.this.doClose(); } catch (Exception e) { logger.error("An exception occurred while closing the connection.", e); } finally { RavenEnvironment.stopManagingThread(); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy