event_log.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. #
  2. # Copyright (C) 2017 The Android Open Source Project
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from __future__ import print_function
  16. import json
  17. import multiprocessing
  18. TASK_COMMAND = 'command'
  19. TASK_SYNC_NETWORK = 'sync-network'
  20. TASK_SYNC_LOCAL = 'sync-local'
  21. class EventLog(object):
  22. """Event log that records events that occurred during a repo invocation.
  23. Events are written to the log as a consecutive JSON entries, one per line.
  24. Each entry contains the following keys:
  25. - id: A ('RepoOp', ID) tuple, suitable for storing in a datastore.
  26. The ID is only unique for the invocation of the repo command.
  27. - name: Name of the object being operated upon.
  28. - task_name: The task that was performed.
  29. - start: Timestamp of when the operation started.
  30. - finish: Timestamp of when the operation finished.
  31. - success: Boolean indicating if the operation was successful.
  32. - try_count: A counter indicating the try count of this task.
  33. Optionally:
  34. - parent: A ('RepoOp', ID) tuple indicating the parent event for nested
  35. events.
  36. Valid task_names include:
  37. - command: The invocation of a subcommand.
  38. - sync-network: The network component of a sync command.
  39. - sync-local: The local component of a sync command.
  40. Specific tasks may include additional informational properties.
  41. """
  42. def __init__(self):
  43. """Initializes the event log."""
  44. self._log = []
  45. self._next_id = _EventIdGenerator()
  46. self._parent = None
  47. def Add(self, name, task_name, start, finish=None, success=None,
  48. try_count=1, kind='RepoOp'):
  49. """Add an event to the log.
  50. Args:
  51. name: Name of the object being operated upon.
  52. task_name: A sub-task that was performed for name.
  53. start: Timestamp of when the operation started.
  54. finish: Timestamp of when the operation finished.
  55. success: Boolean indicating if the operation was successful.
  56. try_count: A counter indicating the try count of this task.
  57. kind: The kind of the object for the unique identifier.
  58. Returns:
  59. A dictionary of the event added to the log.
  60. """
  61. event = {
  62. 'id': (kind, self._next_id.next()),
  63. 'name': name,
  64. 'task_name': task_name,
  65. 'start_time': start,
  66. 'try': try_count,
  67. }
  68. if self._parent:
  69. event['parent'] = self._parent['id']
  70. if success is not None or finish is not None:
  71. self.FinishEvent(event, finish, success)
  72. self._log.append(event)
  73. return event
  74. def AddSync(self, project, task_name, start, finish, success):
  75. """Add a event to the log for a sync command.
  76. Args:
  77. project: Project being synced.
  78. task_name: A sub-task that was performed for name.
  79. One of (TASK_SYNC_NETWORK, TASK_SYNC_LOCAL)
  80. start: Timestamp of when the operation started.
  81. finish: Timestamp of when the operation finished.
  82. success: Boolean indicating if the operation was successful.
  83. Returns:
  84. A dictionary of the event added to the log.
  85. """
  86. event = self.Add(project.relpath, success, start, finish, task_name)
  87. if event is not None:
  88. event['project'] = project.name
  89. if project.revisionExpr:
  90. event['revision'] = project.revisionExpr
  91. if project.remote.url:
  92. event['project_url'] = project.remote.url
  93. if project.remote.fetchUrl:
  94. event['remote_url'] = project.remote.fetchUrl
  95. try:
  96. event['git_hash'] = project.GetCommitRevisionId()
  97. except Exception:
  98. pass
  99. return event
  100. def GetStatusString(self, success):
  101. """Converst a boolean success to a status string.
  102. Args:
  103. success: Boolean indicating if the operation was successful.
  104. Returns:
  105. status string.
  106. """
  107. return 'pass' if success else 'fail'
  108. def FinishEvent(self, event, finish, success):
  109. """Finishes an incomplete event.
  110. Args:
  111. event: An event that has been added to the log.
  112. finish: Timestamp of when the operation finished.
  113. success: Boolean indicating if the operation was successful.
  114. Returns:
  115. A dictionary of the event added to the log.
  116. """
  117. event['status'] = self.GetStatusString(success)
  118. event['finish_time'] = finish
  119. return event
  120. def SetParent(self, event):
  121. """Set a parent event for all new entities.
  122. Args:
  123. event: The event to use as a parent.
  124. """
  125. self._parent = event
  126. def Write(self, filename):
  127. """Writes the log out to a file.
  128. Args:
  129. filename: The file to write the log to.
  130. """
  131. with open(filename, 'w+') as f:
  132. for e in self._log:
  133. json.dump(e, f, sort_keys=True)
  134. f.write('\n')
  135. def _EventIdGenerator():
  136. """Returns multi-process safe iterator that generates locally unique id.
  137. Yields:
  138. A unique, to this invocation of the program, integer id.
  139. """
  140. eid = multiprocessing.Value('i', 1)
  141. while True:
  142. with eid.get_lock():
  143. val = eid.value
  144. eid.value += 1
  145. yield val