blob: c77c564893b31cf146fbe5a0c4be68dc0db0dc63 [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
18TASK_COMMAND = 'command'
19TASK_SYNC_NETWORK = 'sync-network'
20TASK_SYNC_LOCAL = 'sync-local'
21
David Pursehouse819827a2020-02-12 15:20:19 +090022
David Rileye0684ad2017-04-05 00:02:59 -070023class EventLog(object):
24 """Event log that records events that occurred during a repo invocation.
25
26 Events are written to the log as a consecutive JSON entries, one per line.
27 Each entry contains the following keys:
28 - id: A ('RepoOp', ID) tuple, suitable for storing in a datastore.
29 The ID is only unique for the invocation of the repo command.
30 - name: Name of the object being operated upon.
31 - task_name: The task that was performed.
32 - start: Timestamp of when the operation started.
33 - finish: Timestamp of when the operation finished.
34 - success: Boolean indicating if the operation was successful.
35 - try_count: A counter indicating the try count of this task.
36
37 Optionally:
38 - parent: A ('RepoOp', ID) tuple indicating the parent event for nested
39 events.
40
41 Valid task_names include:
42 - command: The invocation of a subcommand.
43 - sync-network: The network component of a sync command.
44 - sync-local: The local component of a sync command.
45
46 Specific tasks may include additional informational properties.
47 """
48
49 def __init__(self):
50 """Initializes the event log."""
51 self._log = []
David Rileye0684ad2017-04-05 00:02:59 -070052 self._parent = None
53
54 def Add(self, name, task_name, start, finish=None, success=None,
55 try_count=1, kind='RepoOp'):
56 """Add an event to the log.
57
58 Args:
59 name: Name of the object being operated upon.
60 task_name: A sub-task that was performed for name.
61 start: Timestamp of when the operation started.
62 finish: Timestamp of when the operation finished.
63 success: Boolean indicating if the operation was successful.
64 try_count: A counter indicating the try count of this task.
65 kind: The kind of the object for the unique identifier.
66
67 Returns:
68 A dictionary of the event added to the log.
69 """
70 event = {
Mike Frysinger13f323b2019-01-14 16:02:55 -050071 'id': (kind, _NextEventId()),
David Rileye0684ad2017-04-05 00:02:59 -070072 'name': name,
73 'task_name': task_name,
74 'start_time': start,
75 'try': try_count,
76 }
77
78 if self._parent:
79 event['parent'] = self._parent['id']
80
81 if success is not None or finish is not None:
82 self.FinishEvent(event, finish, success)
83
84 self._log.append(event)
85 return event
86
87 def AddSync(self, project, task_name, start, finish, success):
88 """Add a event to the log for a sync command.
89
90 Args:
91 project: Project being synced.
92 task_name: A sub-task that was performed for name.
93 One of (TASK_SYNC_NETWORK, TASK_SYNC_LOCAL)
94 start: Timestamp of when the operation started.
95 finish: Timestamp of when the operation finished.
96 success: Boolean indicating if the operation was successful.
97
98 Returns:
99 A dictionary of the event added to the log.
100 """
David Pursehouse685320b2017-12-14 13:38:58 +0900101 event = self.Add(project.relpath, task_name, start, finish, success)
David Rileye0684ad2017-04-05 00:02:59 -0700102 if event is not None:
103 event['project'] = project.name
104 if project.revisionExpr:
105 event['revision'] = project.revisionExpr
106 if project.remote.url:
107 event['project_url'] = project.remote.url
108 if project.remote.fetchUrl:
109 event['remote_url'] = project.remote.fetchUrl
110 try:
111 event['git_hash'] = project.GetCommitRevisionId()
112 except Exception:
113 pass
114 return event
115
116 def GetStatusString(self, success):
117 """Converst a boolean success to a status string.
118
119 Args:
120 success: Boolean indicating if the operation was successful.
121
122 Returns:
123 status string.
124 """
125 return 'pass' if success else 'fail'
126
127 def FinishEvent(self, event, finish, success):
128 """Finishes an incomplete event.
129
130 Args:
131 event: An event that has been added to the log.
132 finish: Timestamp of when the operation finished.
133 success: Boolean indicating if the operation was successful.
134
135 Returns:
136 A dictionary of the event added to the log.
137 """
David Pursehouse54a4e602020-02-12 14:31:05 +0900138 event['status'] = self.GetStatusString(success)
David Rileye0684ad2017-04-05 00:02:59 -0700139 event['finish_time'] = finish
140 return event
141
142 def SetParent(self, event):
143 """Set a parent event for all new entities.
144
145 Args:
146 event: The event to use as a parent.
147 """
148 self._parent = event
149
150 def Write(self, filename):
151 """Writes the log out to a file.
152
153 Args:
154 filename: The file to write the log to.
155 """
156 with open(filename, 'w+') as f:
157 for e in self._log:
158 json.dump(e, f, sort_keys=True)
159 f.write('\n')
160
161
Mike Frysinger13f323b2019-01-14 16:02:55 -0500162# An integer id that is unique across this invocation of the program.
163_EVENT_ID = multiprocessing.Value('i', 1)
David Rileye0684ad2017-04-05 00:02:59 -0700164
David Pursehouse819827a2020-02-12 15:20:19 +0900165
Mike Frysinger13f323b2019-01-14 16:02:55 -0500166def _NextEventId():
167 """Helper function for grabbing the next unique id.
168
169 Returns:
David Rileye0684ad2017-04-05 00:02:59 -0700170 A unique, to this invocation of the program, integer id.
171 """
Mike Frysinger13f323b2019-01-14 16:02:55 -0500172 with _EVENT_ID.get_lock():
173 val = _EVENT_ID.value
174 _EVENT_ID.value += 1
175 return val