Mercurial > public > mercurial-scm > hg
view hgext/logtoprocess.py @ 44417:7c0b8652fd8c
logtoprocess: avoid traceback when running long commands
$ hg log -r "present($(yes | tr -d '\n' | head -c 130000))" "$(yes | tr -d '\n' | head -c 5000)" --config extensions.logtoprocess= --config logtoprocess.commandfinish=whatever
Traceback (most recent call last):
File "/usr/bin/hg", line 67, in <module>
dispatch.run()
File "/usr/lib64/python2.7/site-packages/mercurial/dispatch.py", line 111, in run
status = dispatch(req)
File "/usr/lib64/python2.7/site-packages/mercurial/dispatch.py", line 290, in dispatch
canonical_command=req.canonical_command,
File "/usr/lib64/python2.7/site-packages/mercurial/ui.py", line 1991, in log
logger.log(self, event, msg, opts)
File "/usr/lib64/python2.7/site-packages/hgext/logtoprocess.py", line 72, in log
procutil.runbgcommand(script, fullenv, shell=True)
File "/usr/lib64/python2.7/site-packages/mercurial/utils/procutil.py", line 597, in runbgcommand
b'error running %r: %s' % (cmd, os.strerror(returncode)),
OSError: [Errno 7] error running 'whatever': Argument list too long
This can happen if you pass a bunch of filenames to hg commit, for instance.
This is due to a size limit on individual env vars (on linux, but I
imagine there are similar limits in other OSes):
$ FOO=$(yes | head -c 131000) /usr/bin/true
$ FOO=$(yes | head -c 132000) /usr/bin/true
-bash: /usr/bin/true: Argument list too long
I propose to avoid this by truncating the message. I didn't make the
limit configurable as it doesn't seem particularly convenient to
customize this. I'm not sure if various OSes would want radically
different limits here?
Differential Revision: https://phab.mercurial-scm.org/D8203
author | Valentin Gatien-Baron <vgatien-baron@janestreet.com> |
---|---|
date | Mon, 02 Mar 2020 14:36:20 -0500 |
parents | 687b865b95ad |
children | 6000f5b25c9b |
line wrap: on
line source
# logtoprocess.py - send ui.log() data to a subprocess # # Copyright 2016 Facebook, Inc. # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. """send ui.log() data to a subprocess (EXPERIMENTAL) This extension lets you specify a shell command per ui.log() event, sending all remaining arguments to as environment variables to that command. Positional arguments construct a log message, which is passed in the `MSG1` environment variables. Each keyword argument is set as a `OPT_UPPERCASE_KEY` variable (so the key is uppercased, and prefixed with `OPT_`). The original event name is passed in the `EVENT` environment variable, and the process ID of mercurial is given in `HGPID`. So given a call `ui.log('foo', 'bar %s\n', 'baz', spam='eggs'), a script configured for the `foo` event can expect an environment with `MSG1=bar baz`, and `OPT_SPAM=eggs`. Scripts are configured in the `[logtoprocess]` section, each key an event name. For example:: [logtoprocess] commandexception = echo "$MSG1" > /var/log/mercurial_exceptions.log would log the warning message and traceback of any failed command dispatch. Scripts are run asynchronously as detached daemon processes; mercurial will not ensure that they exit cleanly. """ from __future__ import absolute_import import os from mercurial.utils import procutil # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should # be specifying the version(s) of Mercurial they are tested with, or # leave the attribute unspecified. testedwith = b'ships-with-hg-core' class processlogger(object): """Map log events to external commands Arguments are passed on as environment variables. """ def __init__(self, ui): self._scripts = dict(ui.configitems(b'logtoprocess')) def tracked(self, event): return bool(self._scripts.get(event)) def log(self, ui, event, msg, opts): script = self._scripts[event] maxmsg = 100000 if len(msg) > maxmsg: # Each env var has a 128KiB limit on linux. msg can be long, in # particular for command event, where it's the full command line. # Prefer truncating the message than raising "Argument list too # long" error. msg = msg[:maxmsg] + b' (truncated)' env = { b'EVENT': event, b'HGPID': os.getpid(), b'MSG1': msg, } # keyword arguments get prefixed with OPT_ and uppercased env.update( (b'OPT_%s' % key.upper(), value) for key, value in opts.items() ) fullenv = procutil.shellenviron(env) procutil.runbgcommand(script, fullenv, shell=True) def uipopulate(ui): ui.setlogger(b'logtoprocess', processlogger(ui))