2323except ImportError :
2424 from ordereddict import OrderedDict # python 2.6
2525
26- from inspect import getmembers
2726from logging import _levelNames , getLevelName
27+ from inspect import getmembers
2828from os import path
2929from sys import argv , exit , stdin , stdout
3030from urlparse import urlsplit
3131from xml .etree import ElementTree
3232
3333# Relative imports
3434
35- from . import csv , logging
35+ from . import logging , splunk_csv
3636from .decorators import Option
3737from .validators import Boolean , Fieldname
38- from .search_command_internals import InputHeader , MessagesHeader , \
39- SearchCommandParser
38+ from .search_command_internals import InputHeader , MessagesHeader , SearchCommandParser
4039
4140
4241class SearchCommand (object ):
@@ -48,8 +47,7 @@ def __init__(self):
4847
4948 # Variables that may be used, but not altered by derived classes
5049
51- self .logger , self ._logging_configuration = logging .configure (
52- type (self ).__name__ )
50+ self .logger , self ._logging_configuration = logging .configure (type (self ).__name__ )
5351 self .input_header = InputHeader ()
5452 self .messages = MessagesHeader ()
5553
@@ -59,6 +57,7 @@ def __init__(self):
5957 self ._configuration = None
6058 self ._fieldnames = None
6159 self ._option_view = None
60+ self ._output_file = None
6261 self ._search_results_info = None
6362 self ._service = None
6463
@@ -219,7 +218,7 @@ def convert_value(field, value):
219218 fields = [convert_field (x ) for x in reader .next ()]
220219 values = [convert_value (f , v ) for f , v in zip (fields , reader .next ())]
221220
222- search_results_info_type = namedtuple (" SearchResultsInfo" , fields )
221+ search_results_info_type = namedtuple (' SearchResultsInfo' , fields )
223222 self ._search_results_info = search_results_info_type ._make (values )
224223
225224 return self ._search_results_info
@@ -256,22 +255,22 @@ def service(self):
256255 if info is None :
257256 return None
258257
259- _ , netloc , _ , _ , _ = urlsplit (
260- info .splunkd_uri , info .splunkd_protocol , allow_fragments = False )
261-
262- splunkd_host , _ = netloc .split (':' )
258+ splunkd = urlsplit (info .splunkd_uri , info .splunkd_protocol , allow_fragments = False )
263259
264260 self ._service = Service (
265- scheme = info .splunkd_protocol , host = splunkd_host ,
266- port = info .splunkd_port , token = info .auth_token ,
267- app = info .ppc_app )
261+ scheme = splunkd .scheme , host = splunkd .hostname , port = splunkd .port , token = info .auth_token , app = info .ppc_app )
268262
269263 return self ._service
270264
271265 #endregion
272266
273267 #region Methods
274268
269+ def error_exit (self , error ):
270+ self .logger .error ('Abnormal exit: ' + error )
271+ self .write_error (error )
272+ exit (1 )
273+
275274 def process (self , args = argv , input_file = stdin , output_file = stdout ):
276275 """ Processes search results as specified by command arguments.
277276
@@ -280,64 +279,70 @@ def process(self, args=argv, input_file=stdin, output_file=stdout):
280279 :param output_file: Pipeline output file
281280
282281 """
283- self .logger .debug ('%s arguments: %s' % ( type (self ).__name__ , args ) )
282+ self .logger .debug (u '%s arguments: %s', type (self ).__name__ , args )
284283 self ._configuration = None
284+ self ._output_file = output_file
285285
286286 try :
287287 if len (args ) >= 2 and args [1 ] == '__GETINFO__' :
288288
289- ConfigurationSettings , operation , args , reader = self ._prepare (
290- args , input_file = None )
291-
289+ ConfigurationSettings , operation , args , reader = self ._prepare (args , input_file = None )
292290 self .parser .parse (args , self )
293-
294291 self ._configuration = ConfigurationSettings (self )
295-
296- writer = csv .DictWriter (
297- output_file , self , self .configuration .keys (), mv_delimiter = ',' )
292+ writer = splunk_csv .DictWriter (output_file , self , self .configuration .keys (), mv_delimiter = ',' )
298293 writer .writerow (self .configuration .items ())
299294
300295 elif len (args ) >= 2 and args [1 ] == '__EXECUTE__' :
301296
302297 self .input_header .read (input_file )
303-
304- ConfigurationSettings , operation , args , reader = self ._prepare (
305- args , input_file )
306-
298+ ConfigurationSettings , operation , args , reader = self ._prepare (args , input_file )
307299 self .parser .parse (args , self )
308-
309300 self ._configuration = ConfigurationSettings (self )
310301
311302 if self .show_configuration :
312303 self .messages .append (
313304 'info_message' , '%s command configuration settings: %s'
314305 % (self .name , self ._configuration ))
315306
316- writer = csv .DictWriter (output_file , self )
307+ writer = splunk_csv .DictWriter (output_file , self )
317308 self ._execute (operation , reader , writer )
318309
319310 else :
320311
321312 file_name = path .basename (args [0 ])
322313 message = (
323- 'Command {0} appears to be statically configured and static '
324- 'configuration is unsupported by splunklib.searchcommands. '
325- 'Please ensure that default/commands.conf contains this '
326- 'stanza: '
327- '[{0}] | '
328- 'filename = {1} | '
329- 'supports_getinfo = true | '
330- 'supports_rawargs = true | '
331- 'outputheader = true' .format (type (self ).name , file_name ))
314+ u 'Command {0} appears to be statically configured and static '
315+ u 'configuration is unsupported by splunklib.searchcommands. '
316+ u 'Please ensure that default/commands.conf contains this '
317+ u 'stanza:\n '
318+ u '[{0}]\n '
319+ u 'filename = {1}\n '
320+ u 'supports_getinfo = true\n '
321+ u 'supports_rawargs = true\n '
322+ u 'outputheader = true' .format (type (self ).name , file_name ))
332323 raise NotImplementedError (message )
333324
334- except Exception as error :
325+ except SystemExit :
326+ raise
327+
328+ except :
329+
330+ import traceback
331+ import sys
332+
333+ error_type , error_message , error_traceback = sys .exc_info ()
334+ self .logger .error (traceback .format_exc (error_traceback ))
335335
336- from traceback import format_exc
336+ origin = error_traceback
337+
338+ while origin .tb_next is not None :
339+ origin = origin .tb_next
340+
341+ filename = origin .tb_frame .f_code .co_filename
342+ lineno = origin .tb_lineno
343+
344+ self .write_error ('%s at "%s", line %d : %s' , error_type .__name__ , filename , lineno , error_message )
337345
338- writer = csv .DictWriter (output_file , self , fieldnames = ['ERROR' ])
339- writer .writerow ({'ERROR' : error })
340- self .logger .error (format_exc ())
341346 exit (1 )
342347
343348 return
@@ -348,11 +353,35 @@ def records(reader):
348353 yield record
349354 return
350355
351- def _prepare (self , argv , input_file ):
352- raise NotImplementedError ('SearchCommand._configure(self, argv)' )
356+ # TODO: Is it possible to support anything other than write_error? It does not seem so.
357+
358+ def write_debug (self , message , * args ):
359+ self ._write_message (u'DEBUG' , message , * args )
360+ return
361+
362+ def write_error (self , message , * args ):
363+ self ._write_message (u'ERROR' , message , * args )
364+ return
365+
366+ def write_info (self , message , * args ):
367+ self ._write_message (u'INFO' , message , * args )
368+ return
369+
370+ def write_warning (self , message , * args ):
371+ self ._write_message (u'WARN' , message , * args )
353372
354373 def _execute (self , operation , reader , writer ):
355- raise NotImplementedError ('SearchCommand._configure(self, argv)' )
374+ raise NotImplementedError (u'SearchCommand._configure(self, argv)' )
375+
376+ def _prepare (self , argv , input_file ):
377+ raise NotImplementedError (u'SearchCommand._configure(self, argv)' )
378+
379+ def _write_message (self , message_type , message_text , * args ):
380+ import csv
381+ if len (args ) > 0 :
382+ message_text = message_text % args
383+ writer = csv .writer (self ._output_file )
384+ writer .writerows ([[], [message_type ], [message_text ]])
356385
357386 #endregion
358387
@@ -465,7 +494,7 @@ def needs_empty_results(self):
465494 @property
466495 def outputheader (self ):
467496 """ Signals that the output of this command is a messages header
468- followed by a blank line and csv search results.
497+ followed by a blank line and splunk_csv search results.
469498
470499 Fixed: :const:`True`
471500
0 commit comments