-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy patheventdata.py
More file actions
176 lines (147 loc) · 6.19 KB
/
eventdata.py
File metadata and controls
176 lines (147 loc) · 6.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import datetime
from enum import Enum
# Event Data
class EventData:
_fields = list()
@classmethod
def setFields(cls, fields):
cls._fields = fields
def __init__(self):
for field in self._fields:
setattr(self, field, None)
# Event Data Parser
_DATEPART = slice(0, 10)
_TIMEPART = slice(11, 24)
_HHMMSS = slice(0, 8)
_MSEC = slice(-4, None)
_LINEBREAK = '\n'
_STATEMENTSTART = '-------------------------------------------------------------------------------'
_STATEMENTEND = '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
_TRANSACTIONPREFIX = '(TRA_'
_TCPV4PREFIX = 'TCPv4:'
_ATTPREFIX = '(ATT_'
_SQL_CLIENT_SIGNATURES = ('__SUPSQL__',)
class ParseState(Enum):
SQLTEXT = 1
OTHER = 2
class EventParser:
def __init__(self):
self._tmp = EventData()
self._parsedEvent = None
self._state = ParseState.OTHER
def parse(self, line):
line = line.rstrip('\n ')
(new_date_time, new_event_name) = self._findEventInfo(line)
if new_date_time:
self._state = ParseState.OTHER
if self._tmp.DATE_TIME:
self._pushEvent()
self._tmp = EventData()
self._tmp.DATE_TIME = new_date_time
self._tmp.EVENT_NAME = new_event_name
self._tmp.RAW_OUTPUT = line
return
elif not self._tmp.RAW_OUTPUT:
return
else:
self._tmp.RAW_OUTPUT = _LINEBREAK.join([self._tmp.RAW_OUTPUT, line])
if self._isStatementStart(line):
self._state = ParseState.SQLTEXT
return
if self._isStatementEnd(line):
self._state = ParseState.OTHER
return
if self._state == ParseState.SQLTEXT:
self._tmp.SQL_TEXT = line if not self._tmp.SQL_TEXT else _LINEBREAK.join([self._tmp.SQL_TEXT, line])
return
(transactionid, isolation_mode, rec_version, lock_mode, read_mode) = self._findTransactionInfo(line)
if transactionid:
self._tmp.TRANSACTIONID = transactionid
self._tmp.ISOLATION_MODE = isolation_mode
self._tmp.REC_VERSION = rec_version
self._tmp.LOCK_MODE = lock_mode
self._tmp.READ_MODE = read_mode
return
(attachmentid, user_name, remote_address) = self._findConnectionInfo(line)
if attachmentid:
self._tmp.ATTACHMENTID = attachmentid
self._tmp.USER_NAME = user_name
self._tmp.REMOTE_ADDRESS = remote_address
return
(module_name, module_line) = self._findModuleInfo(line)
if module_name:
self._tmp.MODULE_NAME = module_name
self._tmp.MODULE_LINE = module_line
return
# etc...
def popEvent(self):
event = None
if self._parsedEvent:
event = self._parsedEvent
self._parsedEvent = None
return event
def _pushEvent(self):
self._parsedEvent = self._tmp
def _findEventInfo(self, line):
'''1234-12-12T12:12:12.1234 <...> EVENT_TYPE'''
event_name = date_time = None
if len(line) > 10 and line[4] == line[7] == '-' and line[10] == 'T':
date = self._dateFromStr(line[_DATEPART])
time = self._timeFromStr(line[_TIMEPART])
if date and time:
event_name = line.rstrip('\n').rpartition(' ')[2]
date_time = datetime.datetime.combine(date, time)
return (date_time, event_name)
def _findModuleInfo(self, line):
'''/*<SQL_CLIENT_SIGNATURE>/<MODULE_NAME>/<MODULE_LINE>*/'''
module_name = module_line = None
for signature in _SQL_CLIENT_SIGNATURES:
idx = line.find(signature)
if idx != -1:
stripped = slice(idx, line.find('*/'))
(_, module_name, module_line) = line[stripped].split('/')
module_name = module_name.split('.')[0]
return (module_name, module_line)
def _findTransactionInfo(self, line):
''' (TRA_12345, PAPAM1 | PARAM2 | PARAM3 |...)'''
transactionid = isolation_mode = rec_version = lock_mode = read_mode = None
line = line.strip()
if line.startswith(_TRANSACTIONPREFIX):
(transactionid, _, transactionParams) = line.lstrip(_TRANSACTIONPREFIX).rstrip(')').partition(',')
transactionParams = tuple(a.strip() for a in transactionParams.split('|'))
if (len(transactionParams) == 4):
(isolation_mode, rec_version, lock_mode, read_mode) = transactionParams
elif (len(transactionParams) == 3):
(isolation_mode, lock_mode, read_mode) = transactionParams
return (transactionid, isolation_mode, rec_version, lock_mode, read_mode)
def _findConnectionInfo(self, line):
''' /path/to/database.fdb (ATT_123, LOGIN:NONE, ENCODING, TCPv4:123.123.123.123)'''
attachmentid = user_name = remote_address = None
ipIndex = line.find(_TCPV4PREFIX)
if ipIndex != -1:
remote_address = line[ipIndex+len(_TCPV4PREFIX):].rstrip(')')
attIndex = line.find(_ATTPREFIX)
if attIndex != -1:
attSize = line[attIndex:].find(',')
attachmentid = line[attIndex+len(_ATTPREFIX):attIndex+attSize]
userIndex = attIndex+attSize+1
userSize = line[userIndex:].find(':')
user_name = line[userIndex:userIndex+userSize].strip()
return (attachmentid, user_name, remote_address)
def _isStatementStart(self, line):
return _STATEMENTSTART in line
def _isStatementEnd(self, line):
return _STATEMENTEND in line
def _dateFromStr(self, dateStr):
try:
lst = dateStr.split(sep='-', maxsplit=2)
return datetime.date(year=int(lst[0]), month=int(lst[1]), day=int(lst[2]))
except:
return None
def _timeFromStr(self, timeStr):
try:
msec = timeStr[_MSEC]
lst = timeStr[_HHMMSS].split(sep=':', maxsplit=2)
return datetime.time(hour=int(lst[0]), minute=int(lst[1]), second=int(lst[2]), microsecond=int(msec)*100)
except:
return None