1+ import json
2+ import traceback
3+ import socket
4+ from logging .handlers import DatagramHandler
5+ from datetime import datetime
6+
7+
8+ class LogstashHandler (DatagramHandler ):
9+ """Python logging handler for Logstash
10+ :param host: The host of the logstash server.
11+ :param port: The port of the logstash server (default 5959).
12+ :param message_type: The type of the message (default logstash).
13+ :param fqdn; Indicates whether to show fully qualified domain name or not (default False).
14+ """
15+
16+ def __init__ (self , host , port = 5959 , message_type = 'logstash' , fqdn = False ):
17+ self .message_type = message_type
18+ self .fqdn = fqdn
19+ DatagramHandler .__init__ (self , host , port )
20+
21+ def makePickle (self , record ):
22+ message_dict = self .build_message (record )
23+ return json .dumps (message_dict )
24+
25+ def build_message (self , record ):
26+ add_debug_info = False
27+
28+ if self .fqdn :
29+ host = socket .getfqdn ()
30+ else :
31+ host = socket .gethostname ()
32+
33+ message_dict = {
34+ '@fields' : {
35+ 'levelname' : record .levelname ,
36+ 'logger' : record .name ,
37+ },
38+ '@message' : record .getMessage (),
39+ '@source' : host ,
40+ '@tags' : [],
41+ '@timestamp' : self .format_timestamp (record .created ),
42+ '@type' : self .message_type ,
43+ }
44+
45+ if record .exc_info :
46+ add_debug_info = True
47+ self .add_message_field (message_dict , 'exc_info' , self .format_exception (record .exc_info ))
48+
49+ if add_debug_info :
50+ self .add_message_field (message_dict , 'pathname' , record .pathname )
51+ self .add_message_field (message_dict , 'lineno' , record .lineno )
52+ self .add_message_field (message_dict , 'process' , record .process )
53+ self .add_message_field (message_dict , 'threadName' , record .threadName )
54+ self .add_message_field (message_dict , 'lineno' , record .lineno )
55+ # funName was added in 2.5
56+ if not getattr (record , 'funcName' , None ):
57+ self .add_message_field (message_dict , 'funcName' , record .funcName )
58+ # processName was added in 2.6
59+ if not getattr (record , 'processName' , None ):
60+ self .add_message_field (message_dict , 'processName' , record .processName )
61+
62+ message_dict = self .add_extra_fields (message_dict , record )
63+
64+ return message_dict
65+
66+ def add_message_field (self , message_dict , key , value ):
67+ message_dict ['@fields' ][key ] = repr (value )
68+
69+ def add_extra_fields (self , message_dict , record ):
70+ # The list contains all the attributes listed in
71+ # http://docs.python.org/library/logging.html#logrecord-attributes
72+ skip_list = (
73+ 'args' , 'asctime' , 'created' , 'exc_info' , 'exc_text' , 'filename' ,
74+ 'funcName' , 'id' , 'levelname' , 'levelno' , 'lineno' , 'module' ,
75+ 'msecs' , 'msecs' , 'message' , 'msg' , 'name' , 'pathname' , 'process' ,
76+ 'processName' , 'relativeCreated' , 'thread' , 'threadName' )
77+
78+ for key , value in record .__dict__ .items ():
79+ if key not in skip_list :
80+ self .add_message_field (message_dict , key , value )
81+
82+ return message_dict
83+
84+ def format_exception (self , exc_info ):
85+ return '\n ' .join (traceback .format_exception (* exc_info )) if exc_info else ''
86+
87+ def format_timestamp (self , time ):
88+ return datetime .utcfromtimestamp (time ).isoformat ()
0 commit comments