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

io.sentry.DefaultSentryClientFactory Maven / Gradle / Ivy

There is a newer version: 8.5.0
Show newest version
package io.sentry;

import io.sentry.buffer.Buffer;
import io.sentry.buffer.DiskBuffer;
import io.sentry.config.Lookup;
import io.sentry.connection.*;
import io.sentry.context.ContextManager;
import io.sentry.context.ThreadLocalContextManager;
import io.sentry.dsn.Dsn;
import io.sentry.event.helper.ContextBuilderHelper;
import io.sentry.event.helper.HttpEventBuilderHelper;
import io.sentry.event.interfaces.*;
import io.sentry.jvmti.FrameCache;
import io.sentry.marshaller.Marshaller;
import io.sentry.marshaller.json.*;
import io.sentry.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.net.InetSocketAddress;
import java.net.Authenticator;
import java.net.Proxy;
import java.net.URL;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Default implementation of {@link SentryClientFactory}.
 * 

* In most cases this is the implementation to use or extend for additional features. */ public class DefaultSentryClientFactory extends SentryClientFactory { /** * Protocol setting to disable security checks over an SSL connection. */ public static final String NAIVE_PROTOCOL = "naive"; /** * Option for whether to compress requests sent to the Sentry Server. */ public static final String COMPRESSION_OPTION = "compression"; /** * Option to set the maximum length of the message body in the requests to the * Sentry Server. */ public static final String MAX_MESSAGE_LENGTH_OPTION = "maxmessagelength"; /** * Option to set a timeout for requests to the Sentry server, in milliseconds. */ public static final String TIMEOUT_OPTION = "timeout"; /** * Default timeout of an HTTP connection to Sentry. */ public static final int TIMEOUT_DEFAULT = (int) TimeUnit.SECONDS.toMillis(1); /** * Option to enable or disable Event buffering. A buffering directory is also required. * This setting is mostly useful on Android where a buffering directory is set by default. */ public static final String BUFFER_ENABLED_OPTION = "buffer.enabled"; /** * Default value for whether buffering is enabled (if a directory is also provided). */ public static final boolean BUFFER_ENABLED_DEFAULT = true; /** * Option to buffer events to disk when network is down. */ public static final String BUFFER_DIR_OPTION = "buffer.dir"; /** * Option for maximum number of events to cache offline when network is down. */ public static final String BUFFER_SIZE_OPTION = "buffer.size"; /** * Default number of events to cache offline when network is down or project is throttled. */ public static final int BUFFER_SIZE_DEFAULT = 10; /** * Option for how long to wait between attempts to flush the disk buffer, in milliseconds. */ public static final String BUFFER_FLUSHTIME_OPTION = "buffer.flushtime"; /** * Default number of milliseconds between attempts to flush buffered events. */ public static final long BUFFER_FLUSHTIME_DEFAULT = 60000; /** * Option to disable the graceful shutdown of the buffer flusher. */ public static final String BUFFER_GRACEFUL_SHUTDOWN_OPTION = "buffer.gracefulshutdown"; /** * Option for the graceful shutdown timeout of the buffer flushing executor, in milliseconds. */ public static final String BUFFER_SHUTDOWN_TIMEOUT_OPTION = "buffer.shutdowntimeout"; /** * Default timeout of the {@link BufferedConnection} shutdown, in milliseconds. */ public static final long BUFFER_SHUTDOWN_TIMEOUT_DEFAULT = TimeUnit.SECONDS.toMillis(1); /** * Option for whether to send events asynchronously. */ public static final String ASYNC_OPTION = "async"; /** * Option to disable the graceful shutdown of the async connection. */ public static final String ASYNC_GRACEFUL_SHUTDOWN_OPTION = "async.gracefulshutdown"; /** * Option for the number of threads used for the async connection. */ public static final String ASYNC_THREADS_OPTION = "async.threads"; /** * Option for the priority of threads used for the async connection. */ public static final String ASYNC_PRIORITY_OPTION = "async.priority"; /** * Option for the maximum size of the async send queue. */ public static final String ASYNC_QUEUE_SIZE_OPTION = "async.queuesize"; /** * Option for what to do when the async executor queue is full. */ public static final String ASYNC_QUEUE_OVERFLOW_OPTION = "async.queue.overflow"; /** * Async executor overflow behavior that will discard old events in the queue. */ public static final String ASYNC_QUEUE_DISCARDOLD = "discardold"; /** * Async executor overflow behavior that will discard the new event that was attempting * to be sent. */ public static final String ASYNC_QUEUE_DISCARDNEW = "discardnew"; /** * Async executor overflow behavior that will cause a synchronous send to occur on the * current thread. */ public static final String ASYNC_QUEUE_SYNC = "sync"; /** * Default behavior to use when the async executor queue is full. */ public static final String ASYNC_QUEUE_OVERFLOW_DEFAULT = ASYNC_QUEUE_DISCARDOLD; /** * Option for the graceful shutdown timeout of the async executor, in milliseconds. */ public static final String ASYNC_SHUTDOWN_TIMEOUT_OPTION = "async.shutdowntimeout"; /** * Default timeout of the {@link AsyncConnection} executor, in milliseconds. */ public static final long ASYNC_SHUTDOWN_TIMEOUT_DEFAULT = TimeUnit.SECONDS.toMillis(1); /** * Option for which package prefixes are part of the user's application code, as a single * comma separated string. */ public static final String IN_APP_FRAMES_OPTION = "stacktrace.app.packages"; /** * Option for whether to hide common stackframes with enclosing exceptions. */ public static final String HIDE_COMMON_FRAMES_OPTION = "stacktrace.hidecommon"; /** * Option for whether to sample events, allowing from 0.0 to 1.0 (0 to 100%) to be sent to the server. */ public static final String SAMPLE_RATE_OPTION = "sample.rate"; /** * Option to set an HTTP proxy hostname for Sentry connections. */ public static final String HTTP_PROXY_HOST_OPTION = "http.proxy.host"; /** * Option to set an HTTP proxy port for Sentry connections. */ public static final String HTTP_PROXY_PORT_OPTION = "http.proxy.port"; /** * Option to set an HTTP proxy username for Sentry connections. */ public static final String HTTP_PROXY_USER_OPTION = "http.proxy.user"; /** * Option to set an HTTP proxy password for Sentry connections. */ public static final String HTTP_PROXY_PASS_OPTION = "http.proxy.password"; /** * The default async queue size if none is provided. */ public static final int QUEUE_SIZE_DEFAULT = 50; /** * The default HTTP proxy port to use if an HTTP Proxy hostname is set but port is not. */ public static final int HTTP_PROXY_PORT_DEFAULT = 80; /** * Option to set the version of the application. */ public static final String RELEASE_OPTION = "release"; /** * Option to set the distribution of the application. */ public static final String DIST_OPTION = "dist"; /** * Option to set the environment of the application. */ public static final String ENVIRONMENT_OPTION = "environment"; /** * Option to set the server name. */ public static final String SERVERNAME_OPTION = "servername"; /** * Option to set additional tags to be sent to Sentry. */ public static final String TAGS_OPTION = "tags"; /** * Option to set tags that are extracted from the MDC system, where applicable. * @deprecated prefer {@link DefaultSentryClientFactory#MDCTAGS_OPTION} */ @Deprecated public static final String EXTRATAGS_OPTION = "extratags"; /** * Option to set tags that are extracted from the MDC system, where applicable. */ public static final String MDCTAGS_OPTION = "mdctags"; /** * Option to set extra data to be sent to Sentry. */ public static final String EXTRA_OPTION = "extra"; /** * Option for whether to enable an uncaught exception handler, defaults to 'true'. */ public static final String UNCAUGHT_HANDLER_ENABLED_OPTION = "uncaught.handler.enabled"; private static final Logger logger = LoggerFactory.getLogger(DefaultSentryClientFactory.class); private static final String FALSE = Boolean.FALSE.toString(); private static final Map REJECT_EXECUTION_HANDLERS = new HashMap<>(); static { REJECT_EXECUTION_HANDLERS.put(ASYNC_QUEUE_SYNC, new ThreadPoolExecutor.CallerRunsPolicy()); REJECT_EXECUTION_HANDLERS.put(ASYNC_QUEUE_DISCARDNEW, new ThreadPoolExecutor.DiscardPolicy()); REJECT_EXECUTION_HANDLERS.put(ASYNC_QUEUE_DISCARDOLD, new ThreadPoolExecutor.DiscardOldestPolicy()); } @Override public SentryClient createSentryClient(Dsn dsn) { try { SentryClient sentryClient = new SentryClient(createConnection(dsn), getContextManager(dsn)); try { // `ServletRequestListener` was added in the Servlet 2.4 API, and // is used as part of the `HttpEventBuilderHelper`, see: // https://tomcat.apache.org/tomcat-5.5-doc/servletapi/ Class.forName("javax.servlet.ServletRequestListener", false, this.getClass().getClassLoader()); sentryClient.addBuilderHelper(new HttpEventBuilderHelper()); } catch (ClassNotFoundException e) { logger.debug("The current environment doesn't provide access to servlets," + " or provides an unsupported version."); } sentryClient.addBuilderHelper(new ContextBuilderHelper(sentryClient)); return configureSentryClient(sentryClient, dsn); } catch (Exception e) { logger.error("Failed to initialize sentry, falling back to no-op client", e); return new SentryClient(new NoopConnection(), new ThreadLocalContextManager()); } } /** * Configures a {@link SentryClient} instance after it has been constructed. * * @param sentryClient The {@link SentryClient} to configure. * @param dsn Data Source Name of the Sentry server to use. * @return The same {@link SentryClient} instance, after configuration. */ protected SentryClient configureSentryClient(SentryClient sentryClient, Dsn dsn) { String release = getRelease(dsn); if (release != null) { sentryClient.setRelease(release); } String dist = getDist(dsn); if (dist != null) { sentryClient.setDist(dist); } String environment = getEnvironment(dsn); if (environment != null) { sentryClient.setEnvironment(environment); } String serverName = getServerName(dsn); if (serverName != null) { sentryClient.setServerName(serverName); } Map tags = getTags(dsn); if (!tags.isEmpty()) { for (Map.Entry tagEntry : tags.entrySet()) { sentryClient.addTag(tagEntry.getKey(), tagEntry.getValue()); } } Set mdcTags = getMdcTags(dsn); if (!mdcTags.isEmpty()) { for (String mdcTag : mdcTags) { sentryClient.addMdcTag(mdcTag); } } Map extra = getExtra(dsn); if (!extra.isEmpty()) { for (Map.Entry extraEntry : extra.entrySet()) { sentryClient.addExtra(extraEntry.getKey(), extraEntry.getValue()); } } if (getUncaughtHandlerEnabled(dsn)) { sentryClient.setupUncaughtExceptionHandler(); } for (String inAppPackage : getInAppFrames(dsn)) { FrameCache.addAppPackage(inAppPackage); } return sentryClient; } /** * Creates a connection to the given DSN by determining the protocol. * * @param dsn Data Source Name of the Sentry server to use. * @return a connection to the server. */ protected Connection createConnection(Dsn dsn) { String protocol = dsn.getProtocol(); Connection connection; if (protocol.equalsIgnoreCase("http") || protocol.equalsIgnoreCase("https")) { logger.debug("Using an {} connection to Sentry.", protocol.toUpperCase()); connection = createHttpConnection(dsn); } else if (protocol.equalsIgnoreCase("out")) { logger.debug("Using StdOut to send events."); connection = createStdOutConnection(dsn); } else if (protocol.equalsIgnoreCase("noop")) { logger.debug("Using noop to send events."); connection = new NoopConnection(); } else { throw new IllegalStateException("Couldn't create a connection for the protocol '" + protocol + "'"); } BufferedConnection bufferedConnection = null; if (getBufferEnabled(dsn)) { Buffer eventBuffer = getBuffer(dsn); if (eventBuffer != null) { long flushtime = getBufferFlushtime(dsn); boolean gracefulShutdown = getBufferedConnectionGracefulShutdownEnabled(dsn); Long shutdownTimeout = getBufferedConnectionShutdownTimeout(dsn); bufferedConnection = new BufferedConnection(connection, eventBuffer, flushtime, gracefulShutdown, shutdownTimeout); connection = bufferedConnection; } } // Enable async unless its value is 'false'. if (getAsyncEnabled(dsn)) { connection = createAsyncConnection(dsn, connection); } // If buffering is enabled, wrap connection with synchronous disk buffering "connection" if (bufferedConnection != null) { connection = bufferedConnection.wrapConnectionWithBufferWriter(connection); } return connection; } /** * Encapsulates an already existing connection in an {@link AsyncConnection} and get the async options from the * Sentry DSN. * * @param dsn Data Source Name of the Sentry server. * @param connection Connection to encapsulate in an {@link AsyncConnection}. * @return the asynchronous connection. */ protected Connection createAsyncConnection(Dsn dsn, Connection connection) { int maxThreads = getAsyncThreads(dsn); int priority = getAsyncPriority(dsn); BlockingDeque queue; int queueSize = getAsyncQueueSize(dsn); if (queueSize == -1) { queue = new LinkedBlockingDeque<>(); } else { queue = new LinkedBlockingDeque<>(queueSize); } ExecutorService executorService = new ThreadPoolExecutor( maxThreads, maxThreads, 0L, TimeUnit.MILLISECONDS, queue, new DaemonThreadFactory(priority), getRejectedExecutionHandler(dsn)); boolean gracefulShutdown = getAsyncGracefulShutdownEnabled(dsn); long shutdownTimeout = getAsyncShutdownTimeout(dsn); return new AsyncConnection(connection, executorService, gracefulShutdown, shutdownTimeout); } /** * Creates an HTTP connection to the Sentry server. * * @param dsn Data Source Name of the Sentry server. * @return an {@link HttpConnection} to the server. */ protected Connection createHttpConnection(Dsn dsn) { URL sentryApiUrl = HttpConnection.getSentryApiUrl(dsn.getUri(), dsn.getProjectId()); String proxyHost = getProxyHost(dsn); String proxyUser = getProxyUser(dsn); String proxyPass = getProxyPass(dsn); int proxyPort = getProxyPort(dsn); Proxy proxy = null; if (proxyHost != null) { InetSocketAddress proxyAddr = new InetSocketAddress(proxyHost, proxyPort); proxy = new Proxy(Proxy.Type.HTTP, proxyAddr); if (proxyUser != null && proxyPass != null) { Authenticator.setDefault(new ProxyAuthenticator(proxyUser, proxyPass)); } } Double sampleRate = getSampleRate(dsn); EventSampler eventSampler = null; if (sampleRate != null) { eventSampler = new RandomEventSampler(sampleRate); } HttpConnection httpConnection = new HttpConnection(sentryApiUrl, dsn.getPublicKey(), dsn.getSecretKey(), proxy, eventSampler); Marshaller marshaller = createMarshaller(dsn); httpConnection.setMarshaller(marshaller); int timeout = getTimeout(dsn); httpConnection.setConnectionTimeout(timeout); boolean bypassSecurityEnabled = getBypassSecurityEnabled(dsn); httpConnection.setBypassSecurity(bypassSecurityEnabled); return httpConnection; } /** * Uses stdout to send the logs. * * @param dsn Data Source Name of the Sentry server. * @return an {@link OutputStreamConnection} using {@code System.out}. */ protected Connection createStdOutConnection(Dsn dsn) { //CHECKSTYLE.OFF: RegexpSinglelineJava OutputStreamConnection stdOutConnection = new OutputStreamConnection(System.out); //CHECKSTYLE.ON: RegexpSinglelineJava stdOutConnection.setMarshaller(createMarshaller(dsn)); return stdOutConnection; } /** * Creates a JSON marshaller that will convert every {@link io.sentry.event.Event} in a format * handled by the Sentry server. * * @param dsn Data Source Name of the Sentry server. * @return a {@link JsonMarshaller} to process the events. */ protected Marshaller createMarshaller(Dsn dsn) { int maxMessageLength = getMaxMessageLength(dsn); JsonMarshaller marshaller = createJsonMarshaller(maxMessageLength); // Set JSON marshaller bindings StackTraceInterfaceBinding stackTraceBinding = new StackTraceInterfaceBinding(); // Enable common frames hiding unless its value is 'false'. stackTraceBinding.setRemoveCommonFramesWithEnclosing(getHideCommonFramesEnabled(dsn)); stackTraceBinding.setInAppFrames(getInAppFrames(dsn)); marshaller.addInterfaceBinding(StackTraceInterface.class, stackTraceBinding); marshaller.addInterfaceBinding(ExceptionInterface.class, new ExceptionInterfaceBinding(stackTraceBinding)); marshaller.addInterfaceBinding(MessageInterface.class, new MessageInterfaceBinding(maxMessageLength)); marshaller.addInterfaceBinding(UserInterface.class, new UserInterfaceBinding()); marshaller.addInterfaceBinding(DebugMetaInterface.class, new DebugMetaInterfaceBinding()); HttpInterfaceBinding httpBinding = new HttpInterfaceBinding(); //TODO: Add a way to clean the HttpRequest //httpBinding. marshaller.addInterfaceBinding(HttpInterface.class, httpBinding); // Enable compression unless the option is set to false marshaller.setCompression(getCompressionEnabled(dsn)); return marshaller; } /** * Create a {@link JsonMarshaller}. This method makes it easier to provide a custom implementation. * * @param maxMessageLength of the whole json output * @return new marshaller */ @SuppressWarnings("WeakerAccess") protected JsonMarshaller createJsonMarshaller(int maxMessageLength) { return new JsonMarshaller(maxMessageLength); } /** * Returns the {@link ContextManager} to use for locating and storing data that is context specific, * such as {@link io.sentry.event.Breadcrumb}s. *

* Defaults to {@link ThreadLocalContextManager}. * * @param dsn Sentry server DSN which may contain options. * @return the {@link ContextManager} to use. */ protected ContextManager getContextManager(Dsn dsn) { return new ThreadLocalContextManager(); } /** * Returns the list of package names to consider "in-app". *

* Those packages will be used with the {@link StackTraceInterface} to show frames that are a part of * the main application in the Sentry UI by default. * * @param dsn Sentry server DSN which may contain options. * @return the list of package names to consider "in-app". */ protected Collection getInAppFrames(Dsn dsn) { String inAppFramesOption = Lookup.lookup(IN_APP_FRAMES_OPTION, dsn); if (Util.isNullOrEmpty(inAppFramesOption)) { // Only warn if the user didn't set it at all if (inAppFramesOption == null) { logger.warn("No '" + IN_APP_FRAMES_OPTION + "' was configured, this option is highly recommended " + "as it affects stacktrace grouping and display on Sentry. See documentation: " + "https://docs.sentry.io/clients/java/config/#in-application-stack-frames"); } return Collections.emptyList(); } List inAppPackages = new ArrayList<>(); for (String inAppPackage : inAppFramesOption.split(",")) { if (!inAppPackage.trim().equals("")) { inAppPackages.add(inAppPackage); } } return inAppPackages; } /** * Whether or not to wrap the underlying connection in an {@link AsyncConnection}. * * @param dsn Sentry server DSN which may contain options. * @return Whether or not to wrap the underlying connection in an {@link AsyncConnection}. */ protected boolean getAsyncEnabled(Dsn dsn) { return !FALSE.equalsIgnoreCase(Lookup.lookup(ASYNC_OPTION, dsn)); } /** * Handler for tasks that cannot be immediately queued by a {@link ThreadPoolExecutor}. * * @param dsn Sentry server DSN which may contain options. * @return Handler for tasks that cannot be immediately queued by a {@link ThreadPoolExecutor}. */ protected RejectedExecutionHandler getRejectedExecutionHandler(Dsn dsn) { String overflowName = ASYNC_QUEUE_OVERFLOW_DEFAULT; String asyncQueueOverflowOption = Lookup.lookup(ASYNC_QUEUE_OVERFLOW_OPTION, dsn); if (!Util.isNullOrEmpty(asyncQueueOverflowOption)) { overflowName = asyncQueueOverflowOption.toLowerCase(); } RejectedExecutionHandler handler = REJECT_EXECUTION_HANDLERS.get(overflowName); if (handler == null) { String options = Arrays.toString(REJECT_EXECUTION_HANDLERS.keySet().toArray()); throw new RuntimeException("RejectedExecutionHandler not found: '" + overflowName + "', valid choices are: " + options); } return handler; } /** * Maximum time to wait for {@link BufferedConnection} shutdown when closed, in milliseconds. * * @param dsn Sentry server DSN which may contain options. * @return Maximum time to wait for {@link BufferedConnection} shutdown when closed, in milliseconds. */ protected long getBufferedConnectionShutdownTimeout(Dsn dsn) { return Util.parseLong(Lookup.lookup(BUFFER_SHUTDOWN_TIMEOUT_OPTION, dsn), BUFFER_SHUTDOWN_TIMEOUT_DEFAULT); } /** * Whether or not to attempt a graceful shutdown of the {@link BufferedConnection} upon close. * * @param dsn Sentry server DSN which may contain options. * @return Whether or not to attempt a graceful shutdown of the {@link BufferedConnection} upon close. */ protected boolean getBufferedConnectionGracefulShutdownEnabled(Dsn dsn) { return !FALSE.equalsIgnoreCase(Lookup.lookup(BUFFER_GRACEFUL_SHUTDOWN_OPTION, dsn)); } /** * How long to wait between attempts to flush the disk buffer, in milliseconds. * * @param dsn Sentry server DSN which may contain options. * @return ow long to wait between attempts to flush the disk buffer, in milliseconds. */ protected long getBufferFlushtime(Dsn dsn) { return Util.parseLong(Lookup.lookup(BUFFER_FLUSHTIME_OPTION, dsn), BUFFER_FLUSHTIME_DEFAULT); } /** * The graceful shutdown timeout of the async executor, in milliseconds. * * @param dsn Sentry server DSN which may contain options. * @return The graceful shutdown timeout of the async executor, in milliseconds. */ protected long getAsyncShutdownTimeout(Dsn dsn) { return Util.parseLong(Lookup.lookup(ASYNC_SHUTDOWN_TIMEOUT_OPTION, dsn), ASYNC_SHUTDOWN_TIMEOUT_DEFAULT); } /** * Whether or not to attempt the graceful shutdown of the {@link AsyncConnection} upon close. * * @param dsn Sentry server DSN which may contain options. * @return Whether or not to attempt the graceful shutdown of the {@link AsyncConnection} upon close. */ protected boolean getAsyncGracefulShutdownEnabled(Dsn dsn) { return !FALSE.equalsIgnoreCase(Lookup.lookup(ASYNC_GRACEFUL_SHUTDOWN_OPTION, dsn)); } /** * Maximum size of the async send queue. * * @param dsn Sentry server DSN which may contain options. * @return Maximum size of the async send queue. */ protected int getAsyncQueueSize(Dsn dsn) { return Util.parseInteger(Lookup.lookup(ASYNC_QUEUE_SIZE_OPTION, dsn), QUEUE_SIZE_DEFAULT); } /** * Priority of threads used for the async connection. * * @param dsn Sentry server DSN which may contain options. * @return Priority of threads used for the async connection. */ protected int getAsyncPriority(Dsn dsn) { return Util.parseInteger(Lookup.lookup(ASYNC_PRIORITY_OPTION, dsn), Thread.MIN_PRIORITY); } /** * The number of threads used for the async connection. * * @param dsn Sentry server DSN which may contain options. * @return The number of threads used for the async connection. */ protected int getAsyncThreads(Dsn dsn) { return Util.parseInteger(Lookup.lookup(ASYNC_THREADS_OPTION, dsn), Runtime.getRuntime().availableProcessors()); } /** * Whether to disable security checks over an SSL connection. * * @param dsn Sentry server DSN which may contain options. * @return Whether to disable security checks over an SSL connection. */ protected boolean getBypassSecurityEnabled(Dsn dsn) { return dsn.getProtocolSettings().contains(NAIVE_PROTOCOL); } /** * Whether to sample events, and if so how much to allow through to the server (from 0.0 to 1.0). * * @param dsn Sentry server DSN which may contain options. * @return The ratio of events to allow through to server, or null if sampling is disabled. */ protected Double getSampleRate(Dsn dsn) { return Util.parseDouble(Lookup.lookup(SAMPLE_RATE_OPTION, dsn), null); } /** * HTTP proxy port for Sentry connections. * * @param dsn Sentry server DSN which may contain options. * @return HTTP proxy port for Sentry connections. */ protected int getProxyPort(Dsn dsn) { return Util.parseInteger(Lookup.lookup(HTTP_PROXY_PORT_OPTION, dsn), HTTP_PROXY_PORT_DEFAULT); } /** * HTTP proxy hostname for Sentry connections. * * @param dsn Sentry server DSN which may contain options. * @return HTTP proxy hostname for Sentry connections. */ protected String getProxyHost(Dsn dsn) { return Lookup.lookup(HTTP_PROXY_HOST_OPTION, dsn); } /** * HTTP proxy username for Sentry connections. * * @param dsn Sentry server DSN which may contain options. * @return HTTP proxy username for Sentry connections. */ protected String getProxyUser(Dsn dsn) { return Lookup.lookup(HTTP_PROXY_USER_OPTION, dsn); } /** * HTTP proxy password for Sentry connections. * * @param dsn Sentry server DSN which may contain options. * @return HTTP proxy password for Sentry connections. */ protected String getProxyPass(Dsn dsn) { return Lookup.lookup(HTTP_PROXY_PASS_OPTION, dsn); } /** * Application version to send with {@link io.sentry.event.Event}s that don't already * have a value for the field set. * * @param dsn Sentry server DSN which may contain options. * @return Application version to send with {@link io.sentry.event.Event}s. */ protected String getRelease(Dsn dsn) { return Lookup.lookup(RELEASE_OPTION, dsn); } /** * Application distribution to send with {@link io.sentry.event.Event}s that don't already * have a value for the field set. * * @param dsn Sentry server DSN which may contain options. * @return Application version to send with {@link io.sentry.event.Event}s. */ protected String getDist(Dsn dsn) { return Lookup.lookup(DIST_OPTION, dsn); } /** * Application environmentribution to send with {@link io.sentry.event.Event}s that don't already * have a value for the field set. * * @param dsn Sentry server DSN which may contain options. * @return Application version to send with {@link io.sentry.event.Event}s. */ protected String getEnvironment(Dsn dsn) { return Lookup.lookup(ENVIRONMENT_OPTION, dsn); } /** * Server name to send with {@link io.sentry.event.Event}s that don't already * have a value for the field set. * * @param dsn Sentry server DSN which may contain options. * @return Server name to send with {@link io.sentry.event.Event}s. */ protected String getServerName(Dsn dsn) { return Lookup.lookup(SERVERNAME_OPTION, dsn); } /** * Additional tags to send with {@link io.sentry.event.Event}s. * * @param dsn Sentry server DSN which may contain options. * @return Additional tags to send with {@link io.sentry.event.Event}s. */ protected Map getTags(Dsn dsn) { return Util.parseTags(Lookup.lookup(TAGS_OPTION, dsn)); } /** * Tags to extract from the MDC system and set on {@link io.sentry.event.Event}s, where applicable. * * @param dsn Sentry server DSN which may contain options. * @return Tags to extract from the MDC system and set on {@link io.sentry.event.Event}s, where applicable. * @deprecated prefer {@link DefaultSentryClientFactory#getMdcTags(Dsn)} */ @Deprecated protected Set getExtraTags(Dsn dsn) { return getMdcTags(dsn); } /** * Tags to extract from the MDC system and set on {@link io.sentry.event.Event}s, where applicable. * * @param dsn Sentry server DSN which may contain options. * @return Tags to extract from the MDC system and set on {@link io.sentry.event.Event}s, where applicable. */ protected Set getMdcTags(Dsn dsn) { String val = Lookup.lookup(MDCTAGS_OPTION, dsn); if (Util.isNullOrEmpty(val)) { val = Lookup.lookup(EXTRATAGS_OPTION, dsn); if (!Util.isNullOrEmpty(val)) { logger.warn("The '" + EXTRATAGS_OPTION + "' option is deprecated, please use" + " the '" + MDCTAGS_OPTION + "' option instead."); } } return Util.parseMdcTags(val); } /** * Extra data to send with {@link io.sentry.event.Event}s. * * @param dsn Sentry server DSN which may contain options. * @return Extra data to send with {@link io.sentry.event.Event}s. */ protected Map getExtra(Dsn dsn) { return Util.parseExtra(Lookup.lookup(EXTRA_OPTION, dsn)); } /** * Whether to compress requests sent to the Sentry Server. * * @param dsn Sentry server DSN which may contain options. * @return Whether to compress requests sent to the Sentry Server. */ protected boolean getCompressionEnabled(Dsn dsn) { return !FALSE.equalsIgnoreCase(Lookup.lookup(COMPRESSION_OPTION, dsn)); } /** * Whether to hide common stackframes with enclosing exceptions. * * @param dsn Sentry server DSN which may contain options. * @return Whether to hide common stackframes with enclosing exceptions. */ protected boolean getHideCommonFramesEnabled(Dsn dsn) { return !FALSE.equalsIgnoreCase(Lookup.lookup(HIDE_COMMON_FRAMES_OPTION, dsn)); } /** * The maximum length of the message body in the requests to the Sentry Server. * * @param dsn Sentry server DSN which may contain options. * @return The maximum length of the message body in the requests to the Sentry Server. */ protected int getMaxMessageLength(Dsn dsn) { return Util.parseInteger( Lookup.lookup(MAX_MESSAGE_LENGTH_OPTION, dsn), JsonMarshaller.DEFAULT_MAX_MESSAGE_LENGTH); } /** * Timeout for requests to the Sentry server, in milliseconds. * * @param dsn Sentry server DSN which may contain options. * @return Timeout for requests to the Sentry server, in milliseconds. */ protected int getTimeout(Dsn dsn) { return Util.parseInteger(Lookup.lookup(TIMEOUT_OPTION, dsn), TIMEOUT_DEFAULT); } /** * Whether or not buffering is enabled. * * @param dsn Sentry server DSN which may contain options. * @return Whether or not buffering is enabled. */ protected boolean getBufferEnabled(Dsn dsn) { String bufferEnabled = Lookup.lookup(BUFFER_ENABLED_OPTION, dsn); if (bufferEnabled != null) { return Boolean.parseBoolean(bufferEnabled); } return BUFFER_ENABLED_DEFAULT; } /** * Get the {@link Buffer} where events are stored when network is down. * * @param dsn Dsn passed in by the user. * @return the {@link Buffer} where events are stored when network is down. */ protected Buffer getBuffer(Dsn dsn) { String bufferDir = Lookup.lookup(BUFFER_DIR_OPTION, dsn); if (bufferDir != null) { return new DiskBuffer(new File(bufferDir), getBufferSize(dsn)); } return null; } /** * Get the maximum number of events to cache offline when network is down. * * @param dsn Dsn passed in by the user. * @return the maximum number of events to cache offline when network is down. */ protected int getBufferSize(Dsn dsn) { return Util.parseInteger(Lookup.lookup(BUFFER_SIZE_OPTION, dsn), BUFFER_SIZE_DEFAULT); } /** * Whether or not to enable a {@link SentryUncaughtExceptionHandler}. * * @param dsn Sentry server DSN which may contain options. * @return Whether or not to enable a {@link SentryUncaughtExceptionHandler}. */ protected boolean getUncaughtHandlerEnabled(Dsn dsn) { return !FALSE.equalsIgnoreCase(Lookup.lookup(UNCAUGHT_HANDLER_ENABLED_OPTION, dsn)); } /** * Thread factory generating daemon threads with a custom priority. *

* Those (usually) low priority threads will allow to send event details to sentry concurrently without slowing * down the main application. */ @SuppressWarnings("PMD.AvoidThreadGroup") protected static final class DaemonThreadFactory implements ThreadFactory { private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; private final int priority; private DaemonThreadFactory(int priority) { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "sentry-pool-" + POOL_NUMBER.getAndIncrement() + "-thread-"; this.priority = priority; } @Override public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (!t.isDaemon()) { t.setDaemon(true); } if (t.getPriority() != priority) { t.setPriority(priority); } return t; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy