view hgext/logtoprocess.py @ 51599:b0aaffcb6fcf

tags-cache: directly perform a monimal walk for hgtagsfnodescache warming We do something narrower than the path retrieving data. So lets use dedicated code instead. This provides further useful speedup: ### data-env-vars.name = mozilla-try-2023-03-22-zstd-sparse-revlog # benchmark.name = hg.debug.debug-update-cache # bin-env-vars.hg.flavor = default # bin-env-vars.hg.py-re2-module = default # benchmark.variants.pre-state = warm before-this-series: 19.947581 skip-fnode-filter: 18.916804 (-5.17%, -1.03) use-rev-num: 17.493725 (-12.30%, -2.45) this-changesets: 15.919466 (-20.19%, -4.03)
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Wed, 13 Mar 2024 12:02:06 +0100
parents 642e31cb55f0
children f4733654f144
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.

"""


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:
    """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))