package com.splunk.logging; /** * @copyright * * Copyright 2013-2015 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. */ /** * @brief A handler for java.util.logging that works with Splunk http event collector. * * @details * This is a Splunk custom java.util.logging handler that intercepts logging * information and forwards it to a Splunk server through http event collector. * @todo - link to http event collector documentation * java.util.logging is configure by specifying java.util.logging.config.file * properties file. For example: * -Djava.util.logging.config.file=splunk-http-input.properties * Properties file has include logging handler and its properties. * * # Splunk http event collector handler * handlers = com.splunk.logging.HttpEventCollectorHandler * * # Http event collector application token * com.splunk.logging.HttpEventCollectorLoggingHandler.token=<token guid> * * # Splunk logging input url. * com.splunk.logging.HttpEventCollectorHandler.url * * # Logging events metadata. * com.splunk.logging.HttpEventCollectorLoggingHandler.index * com.splunk.logging.HttpEventCollectorLoggingHandler.source * com.splunk.logging.HttpEventCollectorLoggingHandler.sourcetype * * # Events batching parameters: * # Delay in millisecond between sending events, by default this value is 0, i.e., and events * # are sending immediately * com.splunk.logging.HttpEventCollectorLoggingHandler.batch_interval * * # Max number of events in a batch. By default - 0, i.e., no batching * com.splunk.logging.HttpEventCollectorLoggingHandler.batch_size_count * * # Max size of events in a batch. By default - 0, i.e., no batching * com.splunk.logging.HttpEventCollectorLoggingHandler.batch_size_bytes * * An example of logging properties file: * handlers = com.splunk.logging.HttpEventCollectorLoggingHandler * com.splunk.logging.HttpEventCollectorLoggingHandler.token=81029a58-63db-4bef-9c6f-f6b7e500f098 * * # Splunk server * com.splunk.logging.HttpEventCollectorLoggingHandler.url=https://localhost:8089 * * # Metadata * com.splunk.logging.HttpEventCollectorLoggingHandler.index=default * com.splunk.logging.HttpEventCollectorLoggingHandler.source=localhost * com.splunk.logging.HttpEventCollectorLoggingHandler.sourcetype=syslog * * # Batching * com.splunk.logging.HttpEventCollectorLoggingHandler.batch_interval = 500 * com.splunk.logging.HttpEventCollectorLoggingHandler.batch_size_count = 1000 * com.splunk.logging.HttpEventCollectorLoggingHandler.batch_size_count = 65536 * * # To improve system performance tracing events are sent asynchronously and * events with the same timestamp (that has 1 millisecond resolution) may * be indexed out of order by Splunk. send_mode parameter triggers * "sequential mode" that guarantees preserving events order. In * "sequential mode" performance of sending events to the server is lower. * com.splunk.logging.HttpEventCollectorLoggingHandler.send_mode=sequential */ import java.util.Dictionary; import java.util.Hashtable; import java.util.Locale; import java.util.logging.Handler; import java.util.logging.LogManager; import java.util.logging.LogRecord; /** * An input handler for Splunk http event collector. This handler can be used by * by specifying handlers = com.splunk.logging.HttpEventCollectorLoggingHandler in java.util.logging * properties file. */ public final class HttpEventCollectorLoggingHandler extends Handler { private HttpEventCollectorSender sender = null; private final String BatchDelayConfTag = "batch_interval"; private final String BatchCountConfTag = "batch_size_count"; private final String BatchSizeConfTag = "batch_size_bytes"; private final String RetriesOnErrorTag = "retries_on_error"; private final String UrlConfTag = "url"; private final String SendModeTag = "send_mode"; private final String MiddlewareTag = "middleware"; /** HttpEventCollectorLoggingHandler c-or */ public HttpEventCollectorLoggingHandler() { // read configuration settings Dictionary<String, String> metadata = new Hashtable<String, String>(); metadata.put(HttpEventCollectorSender.MetadataHostTag, getConfigurationProperty(HttpEventCollectorSender.MetadataHostTag, "")); metadata.put(HttpEventCollectorSender.MetadataIndexTag, getConfigurationProperty(HttpEventCollectorSender.MetadataIndexTag, "")); metadata.put(HttpEventCollectorSender.MetadataSourceTag, getConfigurationProperty(HttpEventCollectorSender.MetadataSourceTag, "")); metadata.put(HttpEventCollectorSender.MetadataSourceTypeTag, getConfigurationProperty(HttpEventCollectorSender.MetadataSourceTypeTag, "")); // http event collector endpoint properties String url = getConfigurationProperty(UrlConfTag, null); // app token String token = getConfigurationProperty("token", null); // batching properties long delay = getConfigurationNumericProperty(BatchDelayConfTag, HttpEventCollectorSender.DefaultBatchInterval); long batchCount = getConfigurationNumericProperty(BatchCountConfTag, HttpEventCollectorSender.DefaultBatchCount); long batchSize = getConfigurationNumericProperty(BatchSizeConfTag, HttpEventCollectorSender.DefaultBatchSize); long retriesOnError = getConfigurationNumericProperty(RetriesOnErrorTag, 0); String sendMode = getConfigurationProperty(SendModeTag, "sequential"); String middleware = getConfigurationProperty(MiddlewareTag, ""); // delegate all configuration params to event sender this.sender = new HttpEventCollectorSender( url, token, delay, batchCount, batchSize, sendMode, metadata); // plug a user middleware if (middleware != null && !middleware.isEmpty()) { try { this.sender.addMiddleware((HttpEventCollectorMiddleware.HttpSenderMiddleware)(Class.forName(middleware).newInstance())); } catch (Exception e) {} } // plug retries middleware if (retriesOnError > 0) { this.sender.addMiddleware(new HttpEventCollectorResendMiddleware(retriesOnError)); } if (getConfigurationProperty("disableCertificateValidation", "false").equalsIgnoreCase("true")) { this.sender.disableCertificateValidation(); } } /** * java.util.logging data handler callback * @param record is a logging record */ @Override public void publish(LogRecord record) { this.sender.send( record.getLevel().toString(), record.getMessage(), record.getLoggerName(), String.format(Locale.US, "%d", record.getThreadID()), null, // no property map available record.getThrown() == null ? null : record.getThrown().getMessage(), null // no marker available ); } /** * java.util.logging data handler callback */ @Override public void flush() { this.sender.flush(); } /** * java.util.logging data handler close callback * @throws SecurityException */ @Override public void close() throws SecurityException { this.sender.close(); } // get configuration property from java.util.logging properties file, // defaultValue == null means that property is mandatory private String getConfigurationProperty( final String property, final String defaultValue) { String value = LogManager.getLogManager().getProperty( getClass().getName() + '.' + property ); if (value == null) { value = defaultValue; } if (value == null) { throw new IllegalArgumentException(String.format( "Configuration property %s is missing", property)); } return value; } private long getConfigurationNumericProperty( final String property, long defaultValue) { return Integer.parseInt( getConfigurationProperty(property, String.format("%d", defaultValue))); } }