Skip to content

Commit b50ef62

Browse files
Extend Composite Logger log functions to have built in buffer (#213)
* Composite logger log functions can be extended to have built in buffers * Moving logic for buffer message in TelemetryWriter * Add unit tests for write_event_with_buffer * Simplifed the case of Flush. the case event_level != self.last_telemetry_event_level is already handled and hence should not come in flush case. * Add test case for code coverage
1 parent 37d1596 commit b50ef62

File tree

4 files changed

+116
-5
lines changed

4 files changed

+116
-5
lines changed

src/core/src/bootstrap/Constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,3 +344,8 @@ class UbuntuProClientSettings(EnumBackport):
344344
MAX_OS_MAJOR_VERSION_SUPPORTED = 18
345345
MINIMUM_CLIENT_VERSION = "27.14.4"
346346

347+
class BufferMessage(EnumBackport):
348+
TRUE = 0
349+
FALSE = 1
350+
FLUSH = 2
351+

src/core/src/local_loggers/CompositeLogger.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ def __init__(self, env_layer=None, file_logger=None, current_env=None, telemetry
3535
self.current_env = current_env
3636
self.NEWLINE_REPLACE_CHAR = " "
3737

38-
def log(self, message, message_type=Constants.TelemetryEventLevel.Informational):
38+
def log(self, message, message_type=Constants.TelemetryEventLevel.Informational, buffer_msg=Constants.BufferMessage.FALSE):
3939
"""log output"""
4040
message = self.__remove_substring_from_message(message, Constants.ERROR_ADDED_TO_STATUS)
4141
message = message.strip()
4242
if self.telemetry_writer is not None and self.telemetry_writer.events_folder_path is not None and self.current_env != Constants.DEV: # turned off for dev environment as it severely slows down execution
43-
self.telemetry_writer.write_event(message, message_type)
43+
self.telemetry_writer.write_event_with_buffer(message, message_type, buffer_msg)
4444
if self.current_env in (Constants.DEV, Constants.TEST):
4545
for line in message.splitlines(): # allows the extended file logger to strip unnecessary white space
4646
print(line)
@@ -60,12 +60,12 @@ def log_warning(self, message):
6060
message = self.WARNING + (self.NEWLINE_REPLACE_CHAR.join(message.split(os.linesep))).strip()
6161
self.log(message, message_type=Constants.TelemetryEventLevel.Warning)
6262

63-
def log_debug(self, message):
63+
def log_debug(self, message, buffer_msg=Constants.BufferMessage.FALSE):
6464
"""log debug"""
6565
message = self.__remove_substring_from_message(message, Constants.ERROR_ADDED_TO_STATUS)
6666
message = message.strip()
6767
if self.telemetry_writer is not None and self.telemetry_writer.events_folder_path is not None and self.current_env not in (Constants.DEV, Constants.TEST):
68-
self.telemetry_writer.write_event(message, Constants.TelemetryEventLevel.Verbose)
68+
self.telemetry_writer.write_event_with_buffer(message, Constants.TelemetryEventLevel.Verbose, buffer_msg)
6969
if self.current_env in (Constants.DEV, Constants.TEST):
7070
self.log(self.current_env + ": " + str(self.env_layer.datetime.datetime_utcnow()) + ": " + message, Constants.TelemetryEventLevel.Verbose) # send to standard output if dev or test env
7171
elif self.file_logger is not None:
@@ -102,4 +102,3 @@ def __remove_substring_from_message(message, substring=Constants.ERROR_ADDED_TO_
102102
if substring in message:
103103
message = message.replace("[{0}]".format(Constants.ERROR_ADDED_TO_STATUS), "")
104104
return message
105-

src/core/src/service_interfaces/TelemetryWriter.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
class TelemetryWriter(object):
2929
"""Class for writing telemetry data to data transports"""
3030

31+
TELEMETRY_BUFFER_DELIMETER= "\n|\t"
3132

3233
def __init__(self, env_layer, composite_logger, events_folder_path, telemetry_supported):
3334
self.env_layer = env_layer
@@ -48,6 +49,9 @@ def __init__(self, env_layer, composite_logger, events_folder_path, telemetry_su
4849
self.write_event('Started Linux patch core operation.', Constants.TelemetryEventLevel.Informational)
4950
self.machine_info = None
5051
self.set_and_write_machine_config_info()
52+
self.telemetry_buffer_store = ""
53+
self.last_telemetry_event_level = None
54+
5155

5256
def write_config_info(self, config_info, config_type='unknown'):
5357
# Configuration info
@@ -156,6 +160,33 @@ def __ensure_message_restriction_compliance(self, full_message):
156160
self.composite_logger.log_telemetry_module_error("Error occurred while formatting message for a telemetry event. [Error={0}]".format(repr(e)))
157161
raise
158162

163+
def write_event_with_buffer(self, message, event_level, buffer_msg):
164+
if buffer_msg == Constants.BufferMessage.TRUE and (event_level == self.last_telemetry_event_level or self.last_telemetry_event_level is None):
165+
if self.telemetry_buffer_store != "":
166+
self.telemetry_buffer_store = self.telemetry_buffer_store + self.TELEMETRY_BUFFER_DELIMETER + message
167+
else:
168+
self.telemetry_buffer_store = message
169+
170+
self.last_telemetry_event_level = event_level
171+
172+
elif buffer_msg == Constants.BufferMessage.FALSE or event_level != self.last_telemetry_event_level:
173+
if self.telemetry_buffer_store != "":
174+
self.write_event(self.telemetry_buffer_store, self.last_telemetry_event_level)
175+
self.write_event(message, event_level)
176+
177+
self.last_telemetry_event_level = None
178+
self.telemetry_buffer_store = ""
179+
180+
elif buffer_msg == Constants.BufferMessage.FLUSH:
181+
if self.telemetry_buffer_store != "":
182+
self.telemetry_buffer_store = self.telemetry_buffer_store + self.TELEMETRY_BUFFER_DELIMETER + message
183+
self.write_event(self.telemetry_buffer_store, self.last_telemetry_event_level)
184+
else:
185+
self.write_event(message, event_level)
186+
187+
self.last_telemetry_event_level = None
188+
self.telemetry_buffer_store = ""
189+
159190
def write_event(self, message, event_level=Constants.TelemetryEventLevel.Informational, task_name=Constants.TelemetryTaskName.UNKNOWN, is_event_file_throttling_needed=True):
160191
""" Creates and writes event to event file after validating none of the telemetry size restrictions are breached
161192
NOTE: is_event_file_throttling_needed is used to determine if event file throttling is required and as such should always be True.

src/core/tests/Test_TelemetryWriter.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,82 @@ def test_events_deleted_outside_of_extension_while_extension_is_running(self):
215215
self.runtime.telemetry_writer.write_event("testing telemetry write to file", Constants.TelemetryEventLevel.Error, "Test Task")
216216
os.listdir = backup_os_listdir
217217

218+
def test_write_event_with_buffer_true_and_then_flush(self):
219+
self.runtime.telemetry_writer.write_event_with_buffer("Message 1", Constants.TelemetryEventLevel.Verbose,
220+
Constants.BufferMessage.TRUE)
221+
self.runtime.telemetry_writer.write_event_with_buffer("Message 2", Constants.TelemetryEventLevel.Verbose,
222+
Constants.BufferMessage.TRUE)
223+
self.runtime.telemetry_writer.write_event_with_buffer("Message 3", Constants.TelemetryEventLevel.Verbose,
224+
Constants.BufferMessage.FLUSH)
225+
226+
latest_event_file = [pos_json for pos_json in os.listdir(self.runtime.telemetry_writer.events_folder_path) if
227+
re.search('^[0-9]+.json$', pos_json)][-1]
228+
with open(os.path.join(self.runtime.telemetry_writer.events_folder_path, latest_event_file), 'r+') as f:
229+
events = json.load(f)
230+
self.assertTrue(events is not None)
231+
text_found = re.search('TC=([0-9]+)', events[-1]['Message'])
232+
f.close()
233+
self.assertTrue(text_found.string.startswith("Message 1 | Message 2 | Message 3"))
234+
235+
def test_write_event_with_buffer_only_flush(self):
236+
self.runtime.telemetry_writer.write_event_with_buffer("Message 1", Constants.TelemetryEventLevel.Verbose,
237+
Constants.BufferMessage.FLUSH)
238+
239+
latest_event_file = [pos_json for pos_json in os.listdir(self.runtime.telemetry_writer.events_folder_path) if
240+
re.search('^[0-9]+.json$', pos_json)][-1]
241+
with open(os.path.join(self.runtime.telemetry_writer.events_folder_path, latest_event_file), 'r+') as f:
242+
events = json.load(f)
243+
self.assertTrue(events is not None)
244+
text_found = re.search('TC=([0-9]+)', events[-1]['Message'])
245+
f.close()
246+
self.assertTrue(text_found.string.startswith("Message 1"))
247+
248+
def test_write_event_with_buffer_false(self):
249+
self.runtime.telemetry_writer.write_event_with_buffer("Message 1", Constants.TelemetryEventLevel.Verbose,
250+
Constants.BufferMessage.FALSE)
251+
252+
latest_event_file = [pos_json for pos_json in os.listdir(self.runtime.telemetry_writer.events_folder_path) if
253+
re.search('^[0-9]+.json$', pos_json)][-1]
254+
with open(os.path.join(self.runtime.telemetry_writer.events_folder_path, latest_event_file), 'r+') as f:
255+
events = json.load(f)
256+
self.assertTrue(events is not None)
257+
text_found = re.search('TC=([0-9]+)', events[-1]['Message'])
258+
f.close()
259+
self.assertTrue(text_found.string.startswith("Message 1"))
260+
261+
def test_write_event_with_buffer_true_and_then_flush_but_different_telemetry_event_level(self):
262+
self.runtime.telemetry_writer.write_event_with_buffer("Message 1", Constants.TelemetryEventLevel.Verbose,
263+
Constants.BufferMessage.TRUE)
264+
265+
self.runtime.telemetry_writer.write_event_with_buffer("Message 2", Constants.TelemetryEventLevel.Informational,
266+
Constants.BufferMessage.FLUSH)
267+
268+
# As the messages are with different TelemetryEventLevel, they will be written separately
269+
# even though flush is used.
270+
latest_event_file = [pos_json for pos_json in os.listdir(self.runtime.telemetry_writer.events_folder_path) if
271+
re.search('^[0-9]+.json$', pos_json)][-1]
272+
with open(os.path.join(self.runtime.telemetry_writer.events_folder_path, latest_event_file), 'r+') as f:
273+
events = json.load(f)
274+
self.assertTrue(events is not None)
275+
text_found = re.search('TC=([0-9]+)', events[-1]['Message'])
276+
f.close()
277+
self.assertTrue(text_found.string.startswith("Message 2"))
278+
279+
def test_write_event_with_buffer_true_and_empty_string_and_then_flush_with_non_empty_string(self):
280+
self.runtime.telemetry_writer.write_event_with_buffer("", Constants.TelemetryEventLevel.Verbose,
281+
Constants.BufferMessage.TRUE)
282+
283+
self.runtime.telemetry_writer.write_event_with_buffer("Message 1", Constants.TelemetryEventLevel.Verbose,
284+
Constants.BufferMessage.FLUSH)
285+
286+
latest_event_file = [pos_json for pos_json in os.listdir(self.runtime.telemetry_writer.events_folder_path) if
287+
re.search('^[0-9]+.json$', pos_json)][-1]
288+
with open(os.path.join(self.runtime.telemetry_writer.events_folder_path, latest_event_file), 'r+') as f:
289+
events = json.load(f)
290+
self.assertTrue(events is not None)
291+
text_found = re.search('TC=([0-9]+)', events[-1]['Message'])
292+
f.close()
293+
self.assertTrue(text_found.string.startswith("Message 1"))
218294

219295
if __name__ == '__main__':
220296
unittest.main()

0 commit comments

Comments
 (0)