diff options
author | David Riley <davidriley@google.com> | 2017-04-05 00:02:59 -0700 |
---|---|---|
committer | David Pursehouse <dpursehouse@collab.net> | 2017-05-29 13:39:54 +0900 |
commit | e0684addeeb920ddf83168d5fbbbe50cb9287abd (patch) | |
tree | 03a3dd940950463c0c88062d1fce2aae78708777 /event_log.py | |
parent | fef9f21b28d3b59804fdd5db2bbb86e797051dab (diff) | |
download | git-repo-e0684addeeb920ddf83168d5fbbbe50cb9287abd.tar.gz |
sync: Add support to dump a JSON event log of all sync events.
Change-Id: Id4852968ac1b2bf0093007cf2e5ca951ddab8b3b
Diffstat (limited to 'event_log.py')
-rw-r--r-- | event_log.py | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/event_log.py b/event_log.py new file mode 100644 index 00000000..d73511da --- /dev/null +++ b/event_log.py | |||
@@ -0,0 +1,177 @@ | |||
1 | # | ||
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 | |||
16 | from __future__ import print_function | ||
17 | |||
18 | import json | ||
19 | import multiprocessing | ||
20 | |||
21 | TASK_COMMAND = 'command' | ||
22 | TASK_SYNC_NETWORK = 'sync-network' | ||
23 | TASK_SYNC_LOCAL = 'sync-local' | ||
24 | |||
25 | class 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 = [] | ||
54 | self._next_id = _EventIdGenerator() | ||
55 | 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 = { | ||
74 | 'id': (kind, self._next_id.next()), | ||
75 | '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 | """ | ||
104 | event = self.Add(project.relpath, success, start, finish, task_name) | ||
105 | 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 | |||
165 | def _EventIdGenerator(): | ||
166 | """Returns multi-process safe iterator that generates locally unique id. | ||
167 | |||
168 | Yields: | ||
169 | A unique, to this invocation of the program, integer id. | ||
170 | """ | ||
171 | eid = multiprocessing.Value('i', 1) | ||
172 | |||
173 | while True: | ||
174 | with eid.get_lock(): | ||
175 | val = eid.value | ||
176 | eid.value += 1 | ||
177 | yield val | ||