1# -*- coding: utf-8 -*- 2from datetime import datetime 3from datetime import timedelta 4from haproxy import DELTA_REGEX 5from haproxy import START_REGEX 6 7 8def filter_ip(ip): 9 """Filter :class:`.Line` objects by IP. 10 11 :param ip: IP that you want to filter to. 12 :type ip: string 13 :returns: a function that filters by the provided IP. 14 :rtype: function 15 """ 16 def filter_func(log_line): 17 return log_line.get_ip() == ip 18 19 return filter_func 20 21 22def filter_ip_range(ip_range): 23 """Filter :class:`.Line` objects by IP range. 24 25 Both *192.168.1.203* and *192.168.1.10* are valid if the provided ip 26 range is ``192.168.1`` whereas *192.168.2.103* is not valid (note the 27 *.2.*). 28 29 :param ip_range: IP range that you want to filter to. 30 :type ip_range: string 31 :returns: a function that filters by the provided IP range. 32 :rtype: function 33 """ 34 def filter_func(log_line): 35 ip = log_line.get_ip() 36 if ip: 37 return ip.startswith(ip_range) 38 39 return filter_func 40 41 42def filter_path(path): 43 """Filter :class:`.Line` objects by their request path. 44 45 :param path: part of a path that needs to be on the request path. 46 :type path: string 47 :returns: a function that filters by the provided path. 48 :rtype: function 49 """ 50 def filter_func(log_line): 51 return path in log_line.http_request_path 52 53 return filter_func 54 55 56def filter_ssl(ignore=True): 57 """Filter :class:`.Line` objects that from SSL connections. 58 59 :param ignore: parameter to be ignored just to conform to the rule that all 60 filters need a parameter 61 :type ignore: bool 62 :returns: a function that filters SSL log lines. 63 :rtype: function 64 """ 65 def filter_func(log_line): 66 return log_line.is_https() 67 68 return filter_func 69 70 71def filter_slow_requests(slowness): 72 """Filter :class:`.Line` objects by their response time. 73 74 :param slowness: minimum time, in milliseconds, a server needs to answer 75 a request. If the server takes more time than that the log line is 76 accepted. 77 :type slowness: string 78 :returns: a function that filters by the server response time. 79 :rtype: function 80 """ 81 def filter_func(log_line): 82 slowness_int = int(slowness) 83 return slowness_int <= log_line.time_wait_response 84 85 return filter_func 86 87 88def filter_wait_on_queues(max_waiting): 89 """Filter :class:`.Line` objects by their queueing time in 90 HAProxy. 91 92 :param max_waiting: maximum time, in milliseconds, a request is waiting on 93 HAProxy prior to be delivered to a backend server. If HAProxy takes less 94 than that time the log line is counted. 95 :type max_waiting: string 96 :returns: a function that filters by HAProxy queueing time. 97 :rtype: function 98 """ 99 def filter_func(log_line): 100 waiting = int(max_waiting) 101 return waiting >= log_line.time_wait_queues 102 103 return filter_func 104 105 106def filter_time_frame(start, delta): 107 """Filter :class:`.Line` objects by their connection time. 108 109 :param start: a time expression (see -s argument on --help for its format) 110 to filter log lines that are before this time. 111 :type start: string 112 :param delta: a relative time expression (see -s argument on --help for 113 its format) to limit the amount of time log lines will be considered. 114 :type delta: string 115 :returns: a function that filters by the time a request is made. 116 :rtype: function 117 """ 118 start_value = start 119 delta_value = delta 120 end_value = None 121 122 if start_value is not '': 123 start_value = _date_str_to_datetime(start_value) 124 125 if delta_value is not '': 126 delta_value = _delta_str_to_timedelta(delta_value) 127 128 if start_value is not '' and delta_value is not '': 129 end_value = start_value + delta_value 130 131 def filter_func(log_line): 132 if start_value is '': 133 return True 134 elif start_value > log_line.accept_date: 135 return False 136 137 if end_value is None: 138 return True 139 elif end_value < log_line.accept_date: 140 return False 141 142 return True 143 144 return filter_func 145 146 147def filter_status_code(http_status): 148 """Filter :class:`.Line` objects by their HTTP status code. 149 150 :param http_status: HTTP status code (200, 404, 502...) to filter lines 151 with. 152 :type http_status: string 153 :returns: a function that filters by HTTP status code. 154 :rtype: function 155 """ 156 def filter_func(log_line): 157 return log_line.status_code == http_status 158 159 return filter_func 160 161 162def filter_status_code_family(family_number): 163 """Filter :class:`.Line` objects by their family of HTTP status 164 code, i.e. 2xx, 3xx, 4xx 165 166 :param family_number: First digit of the HTTP status code family, i.e. 2 167 to get all the 2xx status codes, 4 for the client errors and so on. 168 :type family_number: string 169 :returns: a function that filters by HTTP status code family. 170 :rtype: function 171 """ 172 def filter_func(log_line): 173 return log_line.status_code.startswith(family_number) 174 175 return filter_func 176 177 178def filter_http_method(http_method): 179 """Filter :class:`.Line` objects by their HTTP method used (i.e. 180 GET, POST...). 181 182 :param http_method: HTTP method (POST, GET...). 183 :type http_method: string 184 :returns: a function that filters by the given HTTP method. 185 :rtype: function 186 """ 187 def filter_func(log_line): 188 return log_line.http_request_method == http_method 189 190 return filter_func 191 192 193def filter_backend(backend_name): 194 """Filter :class:`.Line` objects by the HAProxy backend name 195 they were processed with. 196 197 :param backend_name: Name of the HAProxy backend section to investigate. 198 :type backend_name: string 199 :returns: a function that filters by the given backend name. 200 :rtype: function 201 """ 202 def filter_func(log_line): 203 return log_line.backend_name == backend_name 204 205 return filter_func 206 207 208def filter_frontend(frontend_name): 209 """Filter :class:`.Line` objects by the HAProxy frontend name 210 the connection arrived from. 211 212 :param frontend_name: Name of the HAProxy frontend section to investigate. 213 :type frontend_name: string 214 :returns: a function that filters by the given frontend name. 215 :rtype: function 216 """ 217 def filter_func(log_line): 218 return log_line.frontend_name == frontend_name 219 220 return filter_func 221 222 223def filter_server(server_name): 224 """Filter :class:`.Line` objects by the downstream server that 225 handled the connection. 226 227 :param server_name: Name of the server HAProxy send the connection to. 228 :type server_name: string 229 :returns: a function that filters by the given server name. 230 :rtype: function 231 """ 232 def filter_func(log_line): 233 return log_line.server_name == server_name 234 235 return filter_func 236 237 238def filter_response_size(size): 239 """Filter :class:`.Line` objects by the response size (in bytes). 240 241 Specially useful when looking for big file downloads. 242 243 :param size: Minimum amount of bytes a response body weighted. 244 :type size: string 245 :returns: a function that filters by the response size. 246 :rtype: function 247 """ 248 if size.startswith('+'): 249 size_value = int(size[1:]) 250 else: 251 size_value = int(size) 252 253 def filter_func(log_line): 254 bytes_read = log_line.bytes_read 255 if bytes_read.startswith('+'): 256 bytes_read = int(bytes_read[1:]) 257 else: 258 bytes_read = int(bytes_read) 259 260 return bytes_read >= size_value 261 262 return filter_func 263 264 265def _date_str_to_datetime(date): 266 matches = START_REGEX.match(date) 267 268 raw_date_input = '{0}/{1}/{2}'.format( 269 matches.group('day'), 270 matches.group('month'), 271 matches.group('year') 272 ) 273 date_format = '%d/%b/%Y' 274 if matches.group('hour'): 275 date_format += ':%H' 276 raw_date_input += ':{0}'.format(matches.group('hour')) 277 if matches.group('minute'): 278 date_format += ':%M' 279 raw_date_input += ':{0}'.format(matches.group('minute')) 280 if matches.group('second'): 281 date_format += ':%S' 282 raw_date_input += ':{0}'.format(matches.group('second')) 283 284 return datetime.strptime(raw_date_input, date_format) 285 286 287def _delta_str_to_timedelta(delta): 288 matches = DELTA_REGEX.match(delta) 289 290 value = int(matches.group('value')) 291 time_unit = matches.group('time_unit') 292 293 if time_unit == 's': 294 return timedelta(seconds=value) 295 elif time_unit == 'm': 296 return timedelta(minutes=value) 297 elif time_unit == 'h': 298 return timedelta(hours=value) 299 if time_unit == 'd': 300 return timedelta(days=value) 301