Reland "git-cache: Start collecting metrics."

This is a reland of a84eaf515f9dd4f199425e0cfc46f3e1b2b0984a

Original change's description:
> git-cache: Start collecting metrics.
>
> Change-Id: I36f4b37a78ad23d615e3f49f158e8de785d5214a
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/2875855
> Reviewed-by: Gavin Mak <gavinmak@google.com>
> Commit-Queue: Edward Lesmes <ehmaldonado@chromium.org>

Change-Id: I80091d78deb0b1aaba280572a1384848990d9d93
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/2877262
Reviewed-by: Anthony Polito <apolito@google.com>
Commit-Queue: Edward Lesmes <ehmaldonado@chromium.org>
diff --git a/git_cache.py b/git_cache.py
index 37b6795..cef1e14 100755
--- a/git_cache.py
+++ b/git_cache.py
@@ -27,6 +27,7 @@
 from download_from_google_storage import Gsutil
 import gclient_utils
 import lockfile
+import metrics
 import subcommand
 
 # Analogous to gc.autopacklimit git config.
@@ -582,6 +583,7 @@
 
 
 @subcommand.usage('[url of repo to check for caching]')
+@metrics.collector.collect_metrics('git cache exists')
 def CMDexists(parser, args):
   """Check to see if there already is a cache of the given repo."""
   _, args = parser.parse_args(args)
@@ -596,6 +598,7 @@
 
 
 @subcommand.usage('[url of repo to create a bootstrap zip file]')
+@metrics.collector.collect_metrics('git cache update-bootstrap')
 def CMDupdate_bootstrap(parser, args):
   """Create and uploads a bootstrap tarball."""
   # Lets just assert we can't do this on Windows.
@@ -630,6 +633,7 @@
 
 
 @subcommand.usage('[url of repo to add to or update in cache]')
+@metrics.collector.collect_metrics('git cache populate')
 def CMDpopulate(parser, args):
   """Ensure that the cache has all up-to-date objects for the given repo."""
   parser.add_option('--depth', type='int',
@@ -682,6 +686,7 @@
 
 
 @subcommand.usage('Fetch new commits into cache and current checkout')
+@metrics.collector.collect_metrics('git cache fetch')
 def CMDfetch(parser, args):
   """Update mirror, and fetch in cwd."""
   parser.add_option('--all', action='store_true', help='Fetch all remotes')
@@ -747,6 +752,7 @@
 
 
 @subcommand.usage('do not use - it is a noop.')
+@metrics.collector.collect_metrics('git cache unlock')
 def CMDunlock(parser, args):
   """This command does nothing."""
   print('This command does nothing and will be removed in the future.')
@@ -770,7 +776,19 @@
                     help='Timeout for acquiring cache lock, in seconds')
 
   def parse_args(self, args=None, values=None):
-    options, args = optparse.OptionParser.parse_args(self, args, values)
+    # Create an optparse.Values object that will store only the actual passed
+    # options, without the defaults.
+    actual_options = optparse.Values()
+    _, args = optparse.OptionParser.parse_args(self, args, actual_options)
+    # Create an optparse.Values object with the default options.
+    options = optparse.Values(self.get_default_values().__dict__)
+    # Update it with the options passed by the user.
+    options._update_careful(actual_options.__dict__)
+    # Store the options passed by the user in an _actual_options attribute.
+    # We store only the keys, and not the values, since the values can contain
+    # arbitrary information, which might be PII.
+    metrics.collector.add('arguments', list(actual_options.__dict__.keys()))
+
     if options.quiet:
       options.verbose = 0
 
@@ -798,7 +816,8 @@
 
 if __name__ == '__main__':
   try:
-    sys.exit(main(sys.argv[1:]))
+    with metrics.collector.print_notice_and_exit():
+      sys.exit(main(sys.argv[1:]))
   except KeyboardInterrupt:
     sys.stderr.write('interrupted\n')
     sys.exit(1)