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