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