event_log.py 5.3 KB

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