event_log.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. # -*- coding:utf-8 -*-
  2. #
  3. # Copyright (C) 2017 The Android Open Source Project
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. from __future__ import print_function
  17. import json
  18. import multiprocessing
  19. TASK_COMMAND = 'command'
  20. TASK_SYNC_NETWORK = 'sync-network'
  21. TASK_SYNC_LOCAL = 'sync-local'
  22. class EventLog(object):
  23. """Event log that records events that occurred during a repo invocation.
  24. Events are written to the log as a consecutive JSON entries, one per line.
  25. Each entry contains the following keys:
  26. - id: A ('RepoOp', ID) tuple, suitable for storing in a datastore.
  27. The ID is only unique for the invocation of the repo command.
  28. - name: Name of the object being operated upon.
  29. - task_name: The task that was performed.
  30. - start: Timestamp of when the operation started.
  31. - finish: Timestamp of when the operation finished.
  32. - success: Boolean indicating if the operation was successful.
  33. - try_count: A counter indicating the try count of this task.
  34. Optionally:
  35. - parent: A ('RepoOp', ID) tuple indicating the parent event for nested
  36. events.
  37. Valid task_names include:
  38. - command: The invocation of a subcommand.
  39. - sync-network: The network component of a sync command.
  40. - sync-local: The local component of a sync command.
  41. Specific tasks may include additional informational properties.
  42. """
  43. def __init__(self):
  44. """Initializes the event log."""
  45. self._log = []
  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, _NextEventId()),
  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, task_name, start, finish, success)
  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. # An integer id that is unique across this invocation of the program.
  136. _EVENT_ID = multiprocessing.Value('i', 1)
  137. def _NextEventId():
  138. """Helper function for grabbing the next unique id.
  139. Returns:
  140. A unique, to this invocation of the program, integer id.
  141. """
  142. with _EVENT_ID.get_lock():
  143. val = _EVENT_ID.value
  144. _EVENT_ID.value += 1
  145. return val