2017-04-05 07:02:59 +00:00
|
|
|
# Copyright (C) 2017 The Android Open Source Project
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
import json
|
|
|
|
import multiprocessing
|
|
|
|
|
2023-08-22 01:20:32 +00:00
|
|
|
|
2023-03-11 06:46:20 +00:00
|
|
|
TASK_COMMAND = "command"
|
|
|
|
TASK_SYNC_NETWORK = "sync-network"
|
|
|
|
TASK_SYNC_LOCAL = "sync-local"
|
2017-04-05 07:02:59 +00:00
|
|
|
|
2020-02-12 06:20:19 +00:00
|
|
|
|
2023-10-19 09:13:32 +00:00
|
|
|
class EventLog:
|
2023-03-11 06:46:20 +00:00
|
|
|
"""Event log that records events that occurred during a repo invocation.
|
|
|
|
|
|
|
|
Events are written to the log as a consecutive JSON entries, one per line.
|
|
|
|
Each entry contains the following keys:
|
|
|
|
- id: A ('RepoOp', ID) tuple, suitable for storing in a datastore.
|
|
|
|
The ID is only unique for the invocation of the repo command.
|
|
|
|
- name: Name of the object being operated upon.
|
|
|
|
- task_name: The task that was performed.
|
|
|
|
- start: Timestamp of when the operation started.
|
|
|
|
- finish: Timestamp of when the operation finished.
|
|
|
|
- success: Boolean indicating if the operation was successful.
|
|
|
|
- try_count: A counter indicating the try count of this task.
|
|
|
|
|
|
|
|
Optionally:
|
|
|
|
- parent: A ('RepoOp', ID) tuple indicating the parent event for nested
|
|
|
|
events.
|
|
|
|
|
|
|
|
Valid task_names include:
|
|
|
|
- command: The invocation of a subcommand.
|
|
|
|
- sync-network: The network component of a sync command.
|
|
|
|
- sync-local: The local component of a sync command.
|
|
|
|
|
|
|
|
Specific tasks may include additional informational properties.
|
2017-04-05 07:02:59 +00:00
|
|
|
"""
|
|
|
|
|
2023-03-11 06:46:20 +00:00
|
|
|
def __init__(self):
|
|
|
|
"""Initializes the event log."""
|
|
|
|
self._log = []
|
|
|
|
self._parent = None
|
|
|
|
|
|
|
|
def Add(
|
|
|
|
self,
|
|
|
|
name,
|
|
|
|
task_name,
|
|
|
|
start,
|
|
|
|
finish=None,
|
|
|
|
success=None,
|
|
|
|
try_count=1,
|
|
|
|
kind="RepoOp",
|
|
|
|
):
|
|
|
|
"""Add an event to the log.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
name: Name of the object being operated upon.
|
|
|
|
task_name: A sub-task that was performed for name.
|
|
|
|
start: Timestamp of when the operation started.
|
|
|
|
finish: Timestamp of when the operation finished.
|
|
|
|
success: Boolean indicating if the operation was successful.
|
|
|
|
try_count: A counter indicating the try count of this task.
|
|
|
|
kind: The kind of the object for the unique identifier.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A dictionary of the event added to the log.
|
|
|
|
"""
|
|
|
|
event = {
|
|
|
|
"id": (kind, _NextEventId()),
|
|
|
|
"name": name,
|
|
|
|
"task_name": task_name,
|
|
|
|
"start_time": start,
|
|
|
|
"try": try_count,
|
|
|
|
}
|
|
|
|
|
|
|
|
if self._parent:
|
|
|
|
event["parent"] = self._parent["id"]
|
|
|
|
|
|
|
|
if success is not None or finish is not None:
|
|
|
|
self.FinishEvent(event, finish, success)
|
|
|
|
|
|
|
|
self._log.append(event)
|
|
|
|
return event
|
|
|
|
|
|
|
|
def AddSync(self, project, task_name, start, finish, success):
|
|
|
|
"""Add a event to the log for a sync command.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
project: Project being synced.
|
|
|
|
task_name: A sub-task that was performed for name.
|
|
|
|
One of (TASK_SYNC_NETWORK, TASK_SYNC_LOCAL)
|
|
|
|
start: Timestamp of when the operation started.
|
|
|
|
finish: Timestamp of when the operation finished.
|
|
|
|
success: Boolean indicating if the operation was successful.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A dictionary of the event added to the log.
|
|
|
|
"""
|
|
|
|
event = self.Add(project.relpath, task_name, start, finish, success)
|
|
|
|
if event is not None:
|
|
|
|
event["project"] = project.name
|
|
|
|
if project.revisionExpr:
|
|
|
|
event["revision"] = project.revisionExpr
|
|
|
|
if project.remote.url:
|
|
|
|
event["project_url"] = project.remote.url
|
|
|
|
if project.remote.fetchUrl:
|
|
|
|
event["remote_url"] = project.remote.fetchUrl
|
|
|
|
try:
|
|
|
|
event["git_hash"] = project.GetCommitRevisionId()
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
return event
|
|
|
|
|
|
|
|
def GetStatusString(self, success):
|
|
|
|
"""Converst a boolean success to a status string.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
success: Boolean indicating if the operation was successful.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
status string.
|
|
|
|
"""
|
|
|
|
return "pass" if success else "fail"
|
|
|
|
|
|
|
|
def FinishEvent(self, event, finish, success):
|
|
|
|
"""Finishes an incomplete event.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
event: An event that has been added to the log.
|
|
|
|
finish: Timestamp of when the operation finished.
|
|
|
|
success: Boolean indicating if the operation was successful.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A dictionary of the event added to the log.
|
|
|
|
"""
|
|
|
|
event["status"] = self.GetStatusString(success)
|
|
|
|
event["finish_time"] = finish
|
|
|
|
return event
|
|
|
|
|
|
|
|
def SetParent(self, event):
|
|
|
|
"""Set a parent event for all new entities.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
event: The event to use as a parent.
|
|
|
|
"""
|
|
|
|
self._parent = event
|
|
|
|
|
|
|
|
def Write(self, filename):
|
|
|
|
"""Writes the log out to a file.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
filename: The file to write the log to.
|
|
|
|
"""
|
|
|
|
with open(filename, "w+") as f:
|
|
|
|
for e in self._log:
|
|
|
|
json.dump(e, f, sort_keys=True)
|
|
|
|
f.write("\n")
|
2017-04-05 07:02:59 +00:00
|
|
|
|
|
|
|
|
2024-10-23 23:15:12 +00:00
|
|
|
# An integer id that is unique across this invocation of the program, to be set
|
|
|
|
# by the first Add event. We can't set it here since it results in leaked
|
|
|
|
# resources (see: https://issues.gerritcodereview.com/353656374).
|
|
|
|
_EVENT_ID = None
|
2017-04-05 07:02:59 +00:00
|
|
|
|
2020-02-12 06:20:19 +00:00
|
|
|
|
event_log: turn id generation from a generator to a func call
Running lots of sync processes in parallel can hit the failure:
Fetching projects: 23% (124/523)Exception in thread Thread-201:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
self.run()
File "/usr/lib/python2.7/threading.py", line 754, in run
self.__target(*self.__args, **self.__kwargs)
File "/usr/local/src/repo/subcmds/sync.py", line 278, in _FetchProjectList
success = self._FetchHelper(opt, project, *args, **kwargs)
File "/usr/local/src/repo/subcmds/sync.py", line 357, in _FetchHelper
start, finish, success)
File "/usr/local/src/repo/event_log.py", line 104, in AddSync
event = self.Add(project.relpath, task_name, start, finish, success)
File "/usr/local/src/repo/event_log.py", line 74, in Add
'id': (kind, next(self._next_id)),
ValueError: generator already executing
It looks like, while we lock the multiprocessing value correctly, the
generator that wraps the value isn't parallel safe. Since we don't
have a way of doing that (as it's part of the language), turn it into
a plain function call instead.
Bug: https://crbug.com/gerrit/10293
Change-Id: I0db03601986ca0370a1699bab32adb03e7b2910a
2019-01-14 21:02:55 +00:00
|
|
|
def _NextEventId():
|
2023-03-11 06:46:20 +00:00
|
|
|
"""Helper function for grabbing the next unique id.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A unique, to this invocation of the program, integer id.
|
|
|
|
"""
|
2024-10-23 23:15:12 +00:00
|
|
|
global _EVENT_ID
|
|
|
|
if _EVENT_ID is None:
|
|
|
|
# There is a small chance of race condition - two parallel processes
|
|
|
|
# setting up _EVENT_ID. However, we expect TASK_COMMAND to happen before
|
|
|
|
# mp kicks in.
|
|
|
|
_EVENT_ID = multiprocessing.Value("i", 1)
|
2023-03-11 06:46:20 +00:00
|
|
|
with _EVENT_ID.get_lock():
|
|
|
|
val = _EVENT_ID.value
|
|
|
|
_EVENT_ID.value += 1
|
|
|
|
return val
|