-
Notifications
You must be signed in to change notification settings - Fork 0
/
display.py
289 lines (241 loc) · 11.7 KB
/
display.py
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
import curses
import threading
import time
class DisplayManager():
"""This class serves to display
data on screen it uses curses
"""
def __init__(self, parser, updatingDataLock):
""" Initialise the curse application
"""
self.stdscr = curses.initscr()
# We keep a reference to the result generated (and updated) by the parser
self.data = parser.data
# We also keep a reference to the thresholds as it serve to know which way we crossed it
self.THRESHOLDS = parser.THRESHOLDS
# We keep a reference to the lock
self.updatingDataLock = updatingDataLock
def displayManager():
""" A function to call in loop
display and update it
"""
while True:
self.display()
time.sleep(10)
self.initialiseCurse()
# We define a thread to call to update the display
self.displayManager = threading.Thread(target=displayManager, daemon = True)
return
def initialiseCurse(self):
""" This function will generate the different boxes needed
for the application starting with stdcr
"""
# We define option to add color
if curses.has_colors():
curses.start_color()
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_CYAN, curses.COLOR_BLACK)
curses.noecho()
curses.cbreak()
curses.curs_set(0)
# Basic window setup, informative text at the bottom
# and a big box
self.stdscr.keypad(True)
maxY, maxX = self.stdscr.getmaxyx()
self.stdscr.addstr(maxY-1,1,"Http log parsing program - type anykey to exit")
# This is the main window of the program, only defined to add a border
windowBorder = self.stdscr.subwin(maxY-1, maxX, 0, 0)
windowBorder.border()
# We create a window where all the stats will be printed
# That's the inside on the previous one
generalDisplay = windowBorder.derwin(maxY-2, maxX-1, 0, 0)
# If the of the display is big enough we add another window on the left
# else we do nothing to have a nicer display
# I assumed we need 95 block to display the first part in an optimal way and 30 for the second (total = 125)
if (maxX-2) > 125:
firstDisplayX = 95
secondDisplayX = (maxX-2) - 95
else:
firstDisplayX = maxX-2
secondDisplayX = None
# Height of the first window (displaying stat from the last 10s)
shortTermStatY = 9
# This is the window where we will display data on the latest 10s
# We prefill it with the imutable text
shortTermStatWindow = generalDisplay.derwin(shortTermStatY, firstDisplayX, 1, 1)
shortTermStatWindow.border()
# serves as a line counter for text printing, so I can add new line easily
y = 0
# We first print the base text
shortTermStatWindow.addstr(y,3,"Data from the latest 10s: ")
y += 2
# The section and query data structure are identical so we can use the same code to display both
for text in ["section", "query"]:
# print the section name followed by the number of view
shortTermStatWindow.addstr(y,3,"The most requested "+ text +" :")
# update line count
y += 1
y += 1
shortTermStatWindow.addstr(y, 3,"Total data served: ")
y += 1
shortTermStatWindow.addstr(y, 3,"Failed request: ")
# We need to position the three next columm, knowing that
# - the section/query needs at most 15 letter
# - the two other require 4 character plus 5 and 7 for two string ("Hit :" and "Total :")
# - we must account for the last line
# the end result must look like
# 30 character | space | 15 | space |Hit :....| space |Total :....|1 = last line
# space = int((shortStatWidth -(30 + 15 + ( 4 + 5 ) + (7 + 4))/3)
shortStatHeigh, shortStatWidth = shortTermStatWindow.getmaxyx()
space = int((shortStatWidth -(66))/3)
# We can now place the base text
for y in range(2,4):
shortTermStatWindow.addstr(y,2*space + 45,"Hit: ")
shortTermStatWindow.addstr(y,3*space + 54,"Total: ")
# We can also define the column that won't be immutable,
# they are contained in the previous and are where stats from the log will go
self.firstColumn = shortTermStatWindow.derwin(shortStatHeigh-2, 15, 1, space+30)
self.secondColumn = shortTermStatWindow.derwin(shortStatHeigh-2, 4, 1, 2*space + 50)
self.thirdColumn = shortTermStatWindow.derwin(shortStatHeigh-2, 4, 1, 3*space + 61)
# Now we generate a new window for the data on the last 2 minutes
longTermStatY, longTermStatX = (5, firstDisplayX)
# We prefill it with the imutable text
longTermStatWindow = generalDisplay.derwin(longTermStatY, longTermStatX, shortTermStatY + 1, 1)
longTermStatWindow.border()
# serves as a line counter for text printing, so I can add new line easily
y = 0
# We first print the base text
longTermStatWindow.addstr(y,3,"Data from the latest 2min: ")
y += 2
longTermStatWindow.addstr(y,3,"Total number of view: ")
# We just need one column for this block
self.longTermColumn = longTermStatWindow.derwin(longTermStatY-2, longTermStatX-space-31, 1, space+30)
# Now we generate a new window for the alerts
# the Y dimension is all that's left to display the maximun amount of alert possible
alertY, alertX = (maxY- longTermStatY - shortTermStatY -3, firstDisplayX)
# We prefill it with the imutable text
alertWindow = generalDisplay.derwin(alertY, alertX, longTermStatY + shortTermStatY + 1, 1)
alertWindow.border()
# serves as a line counter for text printing, so I can add new line easily
y = 0
# We first print the base text
alertWindow.addstr(y,3,"Alert status: ")
# We just need one column for this block
# Be careful this Column overlapse the top row
# So we must not update the first line
self.alertColumn = alertWindow.derwin(alertY-1, alertX-11, 0, 1)
self.alertDateColumn = alertWindow.derwin(alertY-2, 9, 1, alertX-10)
# If there is enough place for it we define the second column
if secondDisplayX:
sectionWindow = generalDisplay.derwin(maxY-3, secondDisplayX, 1, firstDisplayX + 1)
sectionWindow.border()
sectionWindow.addstr(y,3,"Statistics on section: ")
self.sectionColumn = sectionWindow.derwin(maxY-4, secondDisplayX-6, 1, 1)
self.sectionHitColumn = sectionWindow.derwin(maxY-4, 4, 1, secondDisplayX-5)
else:
self.sectionColumn = None
self.sectionHitColumn = None
def clearCurse(self):
""" Clean the curse application
"""
curses.nocbreak()
self.stdscr.keypad(False)
curses.echo()
curses.endwin()
return
def readableByte(self, byte):
""" Return an human readable string
of the byte given in entry (approximate)
"""
suffixes = ["bytes","KB","MB","GB"]
byte = float(byte)
for suffixe in suffixes:
if (byte < 1024):
return str(round(byte, 2)) + " " + suffixe
else:
byte = byte/1024.0
def display(self):
""" Updates the display with new data
"""
# We try to acquire the lock to make the data are not being updated
self.updatingDataLock.acquire()
self.firstColumn.clear()
self.secondColumn.clear()
self.thirdColumn.clear()
self.longTermColumn.clear()
self.alertDateColumn.clear()
if self.sectionColumn:
self.sectionColumn.clear()
self.sectionHitColumn.clear()
shortTerm = self.data["shortTerm"]
# We first fill the data from the shortTermStatWindow (first,second and third columm)
# serves as a line counter for text printing
y = 1
# The section and query data structure are identical so we can use the same code to display both
for text in ["section", "query"]:
# To reduce code cruft
result = shortTerm[text + "Result"]
if (len(result) != 0):
section = max(result, key=result.get)
# Add text to the emplacement
self.firstColumn.addstr(y, 0, section, curses.color_pair(3))
self.secondColumn.addstr(y, 0, str(result.get(section)), curses.color_pair(2))
self.thirdColumn.addstr(y, 0, str(sum(result.values())), curses.color_pair(2))
# update line count
y += 1
y += 1
self.firstColumn.addstr(y, 0,self.readableByte(shortTerm["contentServed"]), curses.color_pair(3))
y += 1
if (shortTerm["failedRequest"] > 0):
self.firstColumn.addstr(y, 0, str(shortTerm["failedRequest"]), curses.color_pair(1))
else:
self.firstColumn.addstr(y, 0,"0", curses.color_pair(2))
# Stats in the long term window
self.longTermColumn.addstr(1, 0, str(sum(self.data["longTerm"])), curses.color_pair(3))
alert = self.data["alert"]
# Stats in the alarm
if ((len(alert) == 0) or (alert[len(alert)-1][0] < self.THRESHOLDS)):
self.alertColumn.addstr(0 , 15, " OK ", curses.color_pair(2))
else:
self.alertColumn.addstr(0 , 15, " NOK ",curses.color_pair(1))
alertY, alertX = self.alertColumn.getmaxyx()
# We want to print the log from y = 1 to y = alertY finishing with the latest
y = min(len(alert), alertY-1)
if y < len(alert):
alert = alert[len(alert)-y:]
for numberOfHits, alertTime in alert:
if(y>0):
# We move the cusor to be able to call clrtoeol
self.alertColumn.move(y,0)
self.alertColumn.clrtoeol()
if (numberOfHits > self.THRESHOLDS):
self.alertColumn.addstr(y,0, "High traffic generated an alert - ")
self.alertColumn.addstr("hits = " + str(numberOfHits),curses.color_pair(1))
self.alertColumn.addstr(", triggered at :")
self.alertDateColumn.addstr(y-1, 0,alertTime.strftime("%H:%M:%S"), curses.color_pair(3))
else:
self.alertColumn.addstr(y,0, "Traffic back to normal at :")
self.alertDateColumn.addstr(y-1, 0,alertTime.strftime("%H:%M:%S"), curses.color_pair(3))
y -= 1
# We display data in the left column if it is defined
if self.sectionColumn:
y = 0
result = shortTerm["sectionResult"]
for section in sorted(result, key=result.get, reverse = True):
# Add text to the emplacement
self.sectionColumn.addstr(y, 0, section, curses.color_pair(3))
self.sectionHitColumn.addstr(y, 0, str(result.get(section)), curses.color_pair(2))
# update line count
y += 1
self.firstColumn.refresh()
self.secondColumn.refresh()
self.thirdColumn.refresh()
self.longTermColumn.refresh()
self.alertColumn.refresh()
self.alertDateColumn.refresh()
if self.sectionColumn:
self.sectionColumn.refresh()
self.sectionHitColumn.refresh()
self.updatingDataLock.release()
return