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