Source code for spylunking.tcp_splunk_publisher

"""
This class was built for writing JSON log messages to Splunk over a TCP Port.

Available environment variables:

::

    export SPLUNK_TCP_ADDRESS="<splunk port: 1514>"
    export SPLUNK_INDEX="<splunk index>"
    export SPLUNK_SOURCE="<splunk source>"
    export SPLUNK_SOURCETYPE="<splunk sourcetype>"
    export SPLUNK_DEBUG="<1 enable debug|0 off>"
    export SPLUNK_LOG_TOKEN="<splunk log token>"

"""

import os
import socket
import datetime
from logging.handlers import SocketHandler
from spylunking.rnow import rnow
from spylunking.consts import IS_PY2
from spylunking.consts import SPLUNK_TCP_ADDRESS
from spylunking.consts import SPLUNK_INDEX
from spylunking.consts import SPLUNK_SOURCE
from spylunking.consts import SPLUNK_SOURCETYPE
from spylunking.consts import SPLUNK_DEBUG
from spylunking.consts import SPLUNK_LOG_NAME
from spylunking.consts import SPLUNK_DEPLOY_CONFIG
from spylunking.consts import SPLUNK_ENV_NAME


[docs]class TCPSplunkPublisher(SocketHandler, object): """ A logging handler to send logs to a Splunk Enterprise instance with a Splunk TCP input set up for json with: The TCP Splunk Publisher requires having a JSON-ready entry in the ``/opt/splunk/etc/system/default/props.conf`` config file. Tokens are not required for this to work, but can be included for authenticated TCP logging. If you want to send include a token in on the tcp log body, then you can export: :: export SPLUNK_LOG_TOKEN=<Optional Log Token> Here are the additional lines added to the splunk props.conf for validation: :: [usejson] SHOULD_LINEMERGE = false KV_MODE = json TIME_FORMAT = %Y-%m-%dT%H:%M:%S.%6N%:z """ def __init__( self, address=None, index=None, hostname=None, source=None, sourcetype='json', name=None, dc=None, env=None, custom_dict=None, debug=False, **kwargs): """__init__ Initialize the TCPSplunkPublisher :param address: Splunk fqdn:8088 - overrides host and port :param index: Splunk index :param hostname: Splunk address <host:port> :param source: source for log records :param sourcetype: json :param name: logger name :param dc: deployment config :param env: environment name :param custom_dict: custom json dictionary to merge on self.formatter.format() :param debug: enable debug mode """ self.host = None self.port = None # self.address is used in the SocketHandler base class self.tcp_address = address if SPLUNK_TCP_ADDRESS: self.tcp_address = SPLUNK_TCP_ADDRESS self.host = self.tcp_address.split(':')[0] self.port = int(self.tcp_address.split(':')[1]) super(TCPSplunkPublisher, self).__init__( self.host, self.port) self.token = None if not self.token: self.token = os.getenv( 'SPLUNK_LOG_TOKEN', None) self.index = index if self.index is None: self.index = SPLUNK_INDEX self.source = source if self.source is None: self.source = SPLUNK_SOURCE self.sourcetype = sourcetype if self.sourcetype is None: self.sourcetype = SPLUNK_SOURCETYPE self.custom = custom_dict self.debug = SPLUNK_DEBUG or debug if hostname is None: self.hostname = socket.gethostname() else: self.hostname = hostname self.included_vals = {} self.included_vals['index'] = self.index if self.source: self.included_vals['source'] = self.source self.included_vals['sourcetype'] = self.sourcetype self.included_vals['hostname'] = self.hostname if name: self.included_vals['name'] = name else: self.included_vals['name'] = SPLUNK_LOG_NAME if dc: self.included_vals['dc'] = dc else: self.included_vals['dc'] = SPLUNK_DEPLOY_CONFIG if env: self.included_vals['env'] = env else: self.included_vals['env'] = SPLUNK_ENV_NAME self.debug_log( 'ready {}:{}'.format( self.host, self.port)) # end of __init__
[docs] def write_log( self, log_message): """write_log Write logs to stdout :param log_message: message to log """ print(('{} tcpsp {}'.format( rnow(), log_message)))
# end of write_log
[docs] def debug_log( self, log_message): """debug_log Write logs that only show up in debug mode. To turn on debugging with environment variables please set this environment variable: :: export SPLUNK_DEBUG="1" :param log_message: message to log """ if self.debug: print(('{} tcpsp DEBUG {}'.format( rnow(), log_message)))
# end of debug_log
[docs] def set_fields( self, custom_dict): """set_fields Set the formatter's fields as needed even after the logger has been created. :param custom_dict: new fields to patch in """ self.custom = custom_dict
# end of set_fields
[docs] def build_into_splunk_tcp_message( self, body): """build_into_splunk_tcp_message Format a message for a Splunk JSON sourcetype :param body: splunk JSON dictionary msg body """ # is for an authenticated Splunk TCP Port log_msg = None if self.token: if IS_PY2: log_msg = ('token={}, body={}').format( self.token, body) else: log_msg = ('token={}, body={}').format( self.token, body).encode() else: if IS_PY2: log_msg = ('{}').format( body) else: log_msg = body.encode() # end of support for building different # Splunk socket-ready messages self.debug_log('build={}'.format( log_msg)) return log_msg
# end of build_into_splunk_tcp_message
[docs] def makePickle( self, record): """makePickle Convert a log record into a JSON dictionary packaged into a socket-ready message for a Splunk TCP Port configured that is set up with a JSON sourcetype :param record: log record to format as a socket-ready message """ use_vals = self.custom if not use_vals: use_vals = {} for i in self.included_vals: if i not in use_vals: use_vals[i] = self.included_vals[i] # for now leave as None use_vals['asctime'] = None use_vals['systime'] = datetime.datetime.now().strftime( '%Y-%m-%d %H:%M:%S.%f') self.formatter.set_fields( new_fields=use_vals) self.debug_log('format={}'.format( record)) log_json_str = self.formatter.format(record) self.debug_log('build={}'.format( log_json_str)) splunk_msg = self.build_into_splunk_tcp_message( body=log_json_str) + b'\n' self.debug_log('send={}'.format( splunk_msg)) return splunk_msg
# end of makePickle # end of TCPSplunkPublisher