blob: ef01394a05fcfe0caeba6b2ffa1ce6a744952465 [file] [log] [blame]
David Rileye0684ad2017-04-05 00:02:59 -07001# 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
David Rileye0684ad2017-04-05 00:02:59 -070015import json
16import multiprocessing
17
Mike Frysinger64477332023-08-21 21:20:32 -040018
Gavin Makea2e3302023-03-11 06:46:20 +000019TASK_COMMAND = "command"
20TASK_SYNC_NETWORK = "sync-network"
21TASK_SYNC_LOCAL = "sync-local"
David Rileye0684ad2017-04-05 00:02:59 -070022
David Pursehouse819827a2020-02-12 15:20:19 +090023
Mike Frysingerd4aee652023-10-19 05:13:32 -040024class EventLog:
Gavin Makea2e3302023-03-11 06:46:20 +000025 """Event log that records events that occurred during a repo invocation.
David Rileye0684ad2017-04-05 00:02:59 -070026
Gavin Makea2e3302023-03-11 06:46:20 +000027 Events are written to the log as a consecutive JSON entries, one per line.
28 Each entry contains the following keys:
29 - id: A ('RepoOp', ID) tuple, suitable for storing in a datastore.
30 The ID is only unique for the invocation of the repo command.
31 - name: Name of the object being operated upon.
32 - task_name: The task that was performed.
33 - start: Timestamp of when the operation started.
34 - finish: Timestamp of when the operation finished.
35 - success: Boolean indicating if the operation was successful.
36 - try_count: A counter indicating the try count of this task.
David Rileye0684ad2017-04-05 00:02:59 -070037
Gavin Makea2e3302023-03-11 06:46:20 +000038 Optionally:
39 - parent: A ('RepoOp', ID) tuple indicating the parent event for nested
40 events.
David Rileye0684ad2017-04-05 00:02:59 -070041
Gavin Makea2e3302023-03-11 06:46:20 +000042 Valid task_names include:
43 - command: The invocation of a subcommand.
44 - sync-network: The network component of a sync command.
45 - sync-local: The local component of a sync command.
David Rileye0684ad2017-04-05 00:02:59 -070046
Gavin Makea2e3302023-03-11 06:46:20 +000047 Specific tasks may include additional informational properties.
David Rileye0684ad2017-04-05 00:02:59 -070048 """
David Rileye0684ad2017-04-05 00:02:59 -070049
Gavin Makea2e3302023-03-11 06:46:20 +000050 def __init__(self):
51 """Initializes the event log."""
52 self._log = []
53 self._parent = None
David Rileye0684ad2017-04-05 00:02:59 -070054
Gavin Makea2e3302023-03-11 06:46:20 +000055 def Add(
56 self,
57 name,
58 task_name,
59 start,
60 finish=None,
61 success=None,
62 try_count=1,
63 kind="RepoOp",
64 ):
65 """Add an event to the log.
David Rileye0684ad2017-04-05 00:02:59 -070066
Gavin Makea2e3302023-03-11 06:46:20 +000067 Args:
68 name: Name of the object being operated upon.
69 task_name: A sub-task that was performed for name.
70 start: Timestamp of when the operation started.
71 finish: Timestamp of when the operation finished.
72 success: Boolean indicating if the operation was successful.
73 try_count: A counter indicating the try count of this task.
74 kind: The kind of the object for the unique identifier.
David Rileye0684ad2017-04-05 00:02:59 -070075
Gavin Makea2e3302023-03-11 06:46:20 +000076 Returns:
77 A dictionary of the event added to the log.
78 """
79 event = {
80 "id": (kind, _NextEventId()),
81 "name": name,
82 "task_name": task_name,
83 "start_time": start,
84 "try": try_count,
85 }
David Rileye0684ad2017-04-05 00:02:59 -070086
Gavin Makea2e3302023-03-11 06:46:20 +000087 if self._parent:
88 event["parent"] = self._parent["id"]
David Rileye0684ad2017-04-05 00:02:59 -070089
Gavin Makea2e3302023-03-11 06:46:20 +000090 if success is not None or finish is not None:
91 self.FinishEvent(event, finish, success)
David Rileye0684ad2017-04-05 00:02:59 -070092
Gavin Makea2e3302023-03-11 06:46:20 +000093 self._log.append(event)
94 return event
David Rileye0684ad2017-04-05 00:02:59 -070095
Gavin Makea2e3302023-03-11 06:46:20 +000096 def AddSync(self, project, task_name, start, finish, success):
97 """Add a event to the log for a sync command.
David Rileye0684ad2017-04-05 00:02:59 -070098
Gavin Makea2e3302023-03-11 06:46:20 +000099 Args:
100 project: Project being synced.
101 task_name: A sub-task that was performed for name.
102 One of (TASK_SYNC_NETWORK, TASK_SYNC_LOCAL)
103 start: Timestamp of when the operation started.
104 finish: Timestamp of when the operation finished.
105 success: Boolean indicating if the operation was successful.
David Rileye0684ad2017-04-05 00:02:59 -0700106
Gavin Makea2e3302023-03-11 06:46:20 +0000107 Returns:
108 A dictionary of the event added to the log.
109 """
110 event = self.Add(project.relpath, task_name, start, finish, success)
111 if event is not None:
112 event["project"] = project.name
113 if project.revisionExpr:
114 event["revision"] = project.revisionExpr
115 if project.remote.url:
116 event["project_url"] = project.remote.url
117 if project.remote.fetchUrl:
118 event["remote_url"] = project.remote.fetchUrl
119 try:
120 event["git_hash"] = project.GetCommitRevisionId()
121 except Exception:
122 pass
123 return event
David Rileye0684ad2017-04-05 00:02:59 -0700124
Gavin Makea2e3302023-03-11 06:46:20 +0000125 def GetStatusString(self, success):
126 """Converst a boolean success to a status string.
David Rileye0684ad2017-04-05 00:02:59 -0700127
Gavin Makea2e3302023-03-11 06:46:20 +0000128 Args:
129 success: Boolean indicating if the operation was successful.
David Rileye0684ad2017-04-05 00:02:59 -0700130
Gavin Makea2e3302023-03-11 06:46:20 +0000131 Returns:
132 status string.
133 """
134 return "pass" if success else "fail"
David Rileye0684ad2017-04-05 00:02:59 -0700135
Gavin Makea2e3302023-03-11 06:46:20 +0000136 def FinishEvent(self, event, finish, success):
137 """Finishes an incomplete event.
David Rileye0684ad2017-04-05 00:02:59 -0700138
Gavin Makea2e3302023-03-11 06:46:20 +0000139 Args:
140 event: An event that has been added to the log.
141 finish: Timestamp of when the operation finished.
142 success: Boolean indicating if the operation was successful.
David Rileye0684ad2017-04-05 00:02:59 -0700143
Gavin Makea2e3302023-03-11 06:46:20 +0000144 Returns:
145 A dictionary of the event added to the log.
146 """
147 event["status"] = self.GetStatusString(success)
148 event["finish_time"] = finish
149 return event
150
151 def SetParent(self, event):
152 """Set a parent event for all new entities.
153
154 Args:
155 event: The event to use as a parent.
156 """
157 self._parent = event
158
159 def Write(self, filename):
160 """Writes the log out to a file.
161
162 Args:
163 filename: The file to write the log to.
164 """
165 with open(filename, "w+") as f:
166 for e in self._log:
167 json.dump(e, f, sort_keys=True)
168 f.write("\n")
David Rileye0684ad2017-04-05 00:02:59 -0700169
170
Mike Frysinger13f323b2019-01-14 16:02:55 -0500171# An integer id that is unique across this invocation of the program.
Gavin Makea2e3302023-03-11 06:46:20 +0000172_EVENT_ID = multiprocessing.Value("i", 1)
David Rileye0684ad2017-04-05 00:02:59 -0700173
David Pursehouse819827a2020-02-12 15:20:19 +0900174
Mike Frysinger13f323b2019-01-14 16:02:55 -0500175def _NextEventId():
Gavin Makea2e3302023-03-11 06:46:20 +0000176 """Helper function for grabbing the next unique id.
Mike Frysinger13f323b2019-01-14 16:02:55 -0500177
Gavin Makea2e3302023-03-11 06:46:20 +0000178 Returns:
179 A unique, to this invocation of the program, integer id.
180 """
181 with _EVENT_ID.get_lock():
182 val = _EVENT_ID.value
183 _EVENT_ID.value += 1
184 return val