Esempio n. 1
0
class TestMetlog(object):
    logger = 'tests'

    def setUp(self):
        self.mock_sender = Mock()
        self.client = MetlogClient(self.mock_sender, self.logger)
        # overwrite the class-wide threadlocal w/ an instance one
        # so values won't persist btn tests
        self.client.timer._local = threading.local()

        plugin = config_plugin({'net':True})
        self.client.add_method('procinfo', plugin)

    def test_add_procinfo(self):
        HOST = 'localhost'                 # Symbolic name meaning the local host
        PORT = 50017              # Arbitrary non-privileged port
        def echo_serv():
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

            s.bind((HOST, PORT))
            s.listen(1)

            conn, addr = s.accept()
            data = conn.recv(1024)
            conn.send(data)
            conn.close()
            s.close()

        t = threading.Thread(target=echo_serv)
        t.start()
        time.sleep(1)

        def client_code():
            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            client.connect((HOST, PORT))
            client.send('Hello, world')
            data = client.recv(1024)
            client.close()
            time.sleep(1)

        self.client.procinfo(net=True)
        eq_(1, len(self.client.sender.method_calls))
        fields = self.client.sender.method_calls[0][1][0]['fields']
        assert fields == {u'net': [{u'status': u'LISTEN', u'type': u'TCP', u'local': u'127.0.0.1:50017', u'remote': u'*:*'}]}

        # Start the client up just so that the server will die gracefully
        tc = threading.Thread(target=client_code)
        tc.start()
Esempio n. 2
0
class TestConfiguration(object):
    """
    Configuration for plugin based loggers should *override* what the developer
    uses.  IOTW - developers are overridden by ops.
    """
    logger = 'tests'

    def setUp(self):
        self.mock_sender = Mock()
        self.client = MetlogClient(self.mock_sender, self.logger)
        # overwrite the class-wide threadlocal w/ an instance one
        # so values won't persist btn tests
        self.client.timer._local = threading.local()

        plugin = config_plugin({'net':False})
        self.client.add_method('procinfo', plugin)

    def test_no_netlogging(self):
        self.client.procinfo(net=True)
        eq_(0, len(self.client.sender.method_calls))
Esempio n. 3
0
def client_from_dict_config(config, client=None, clear_global=False):
    """
    Configure a metlog client, fully configured w/ sender and plugins.

    :param config: Configuration dictionary.
    :param client: MetlogClient instance to configure. If None, one will be
                   created.
    :param clear_global: If True, delete any existing global config on the
                         CLIENT_HOLDER before applying new config.

    The configuration dict supports the following values:

    logger
      Metlog client default logger value.
    severity
      Metlog client default severity value.
    disabled_timers
      Sequence of string tokens identifying timers that are to be deactivated.
    filters
      Sequence of 2-tuples `(filter_provider, config)`. Each `filter_provider`
      is a dotted name referring to a function which, when called and passed
      the associated `config` dict as kwargs, will return a usable MetlogClient
      filter function.
    plugins
      Nested dictionary containing plugin configuration. Keys are the plugin
      names (i.e. the name the method will be given when attached to the
      client). Values are 2-tuples `(plugin_provider, config)`. Each
      `plugin_provider` is a dotted name referring to a function which, when
      called and passed the associated `config`, will return the usable plugin
      method.
    sender
      Nested dictionary containing sender configuration.
    global
      Dictionary to be applied to CLIENT_HOLDER's `global_config` storage.
      New config will overwrite any conflicting values, but will not delete
      other config entries. To delete, calling code should call the function
      with `clear_global` set to True.

    All of the configuration values are optional, but failure to include a
    sender may result in a non-functional Metlog client. Any unrecognized keys
    will be ignored.

    Note that any top level config values starting with `sender_` will be added
    to the `sender` config dictionary, overwriting any values that may already
    be set.

    The sender configuration supports the following values:

    class (required)
      Dotted name identifying the sender class to instantiate.
    args
      Sequence of non-keyword args to pass to sender constructor.
    <kwargs>
      All remaining key-value pairs in the sender config dict will be passed as
      keyword arguments to the sender constructor.
    """
    # Make a deep copy of the configuration so that subsequent uses of
    # the config won't blow up
    config = nest_prefixes(copy.deepcopy(config))

    sender_config = config.get('sender', {})
    logger = config.get('logger', '')
    severity = config.get('severity', 6)
    disabled_timers = config.get('disabled_timers', [])
    filter_specs = config.get('filters', [])
    plugins_data = config.pop('plugins', {})
    global_conf = config.get('global', {})

    # update global config stored in CLIENT_HOLDER
    from metlog.holder import CLIENT_HOLDER
    if clear_global:
        CLIENT_HOLDER.global_config = {}
    CLIENT_HOLDER.global_config.update(global_conf)

    resolver = DottedNameResolver()

    # instantiate sender
    sender_clsname = sender_config.pop('class')
    sender_cls = resolver.resolve(sender_clsname)
    sender_args = sender_config.pop('args', tuple())
    sender = sender_cls(*sender_args, **sender_config)

    # initialize filters
    filters = [resolver.resolve(dotted_name)(**cfg)
               for (dotted_name, cfg) in filter_specs]

    # instantiate and/or configure client
    if client is None:
        client = MetlogClient(sender, logger, severity, disabled_timers,
                              filters)
    else:
        client.setup(sender, logger, severity, disabled_timers, filters)

    # initialize plugins and attach to client
    for section_name, plugin_spec in plugins_data.items():
        # each plugin spec is a 2-tuple: (dotted_name, cfg)
        plugin_config = plugin_spec[1]
        plugin_override = plugin_config.pop('override', False)
        plugin_fn = resolver.resolve(plugin_spec[0])(plugin_config)
        client.add_method(plugin_fn, plugin_override)

    return client
Esempio n. 4
0
def client_from_dict_config(config, client=None, clear_global=False):
    """
    Configure a metlog client, fully configured w/ sender and plugins.

    :param config: Configuration dictionary.
    :param client: MetlogClient instance to configure. If None, one will be
                   created.
    :param clear_global: If True, delete any existing global config on the
                         CLIENT_HOLDER before applying new config.

    The configuration dict supports the following values:

    logger
      Metlog client default logger value.
    severity
      Metlog client default severity value.
    disabled_timers
      Sequence of string tokens identifying timers that are to be deactivated.
    filters
      Sequence of 2-tuples `(filter_provider, config)`. Each `filter_provider`
      is a dotted name referring to a function which, when called and passed
      the associated `config` dict as kwargs, will return a usable MetlogClient
      filter function.
    plugins
      Nested dictionary containing plugin configuration. Keys are the plugin
      names (i.e. the name the method will be given when attached to the
      client). Values are 2-tuples `(plugin_provider, config)`. Each
      `plugin_provider` is a dotted name referring to a function which, when
      called and passed the associated `config`, will return the usable plugin
      method.
    sender
      Nested dictionary containing sender configuration.
    global
      Dictionary to be applied to CLIENT_HOLDER's `global_config` storage.
      New config will overwrite any conflicting values, but will not delete
      other config entries. To delete, calling code should call the function
      with `clear_global` set to True.

    All of the configuration values are optional, but failure to include a
    sender may result in a non-functional Metlog client. Any unrecognized keys
    will be ignored.

    Note that any top level config values starting with `sender_` will be added
    to the `sender` config dictionary, overwriting any values that may already
    be set.

    The sender configuration supports the following values:

    class (required)
      Dotted name identifying the sender class to instantiate.
    args
      Sequence of non-keyword args to pass to sender constructor.
    <kwargs>
      All remaining key-value pairs in the sender config dict will be passed as
      keyword arguments to the sender constructor.
    """
    # Make a deep copy of the configuration so that subsequent uses of
    # the config won't blow up
    config = nest_prefixes(copy.deepcopy(config))
    config_copy = json.dumps(copy.deepcopy(config))

    sender_config = config.get('sender', {})
    logger = config.get('logger', '')
    severity = config.get('severity', 6)
    disabled_timers = config.get('disabled_timers', [])
    filter_specs = config.get('filters', [])
    plugins_data = config.pop('plugins', {})
    global_conf = config.get('global', {})

    # update global config stored in CLIENT_HOLDER
    from metlog.holder import CLIENT_HOLDER
    if clear_global:
        CLIENT_HOLDER.global_config = {}
    CLIENT_HOLDER.global_config.update(global_conf)

    resolver = DottedNameResolver()

    # instantiate sender
    sender_clsname = sender_config.pop('class')
    sender_cls = resolver.resolve(sender_clsname)
    sender_args = sender_config.pop('args', tuple())
    sender = sender_cls(*sender_args, **sender_config)

    # initialize filters
    filters = [
        resolver.resolve(dotted_name)(**cfg)
        for (dotted_name, cfg) in filter_specs
    ]

    # instantiate and/or configure client
    if client is None:
        client = MetlogClient(sender, logger, severity, disabled_timers,
                              filters)
    else:
        client.setup(sender, logger, severity, disabled_timers, filters)

    # initialize plugins and attach to client
    for section_name, plugin_spec in plugins_data.items():
        # each plugin spec is a 2-tuple: (dotted_name, cfg)
        plugin_config = plugin_spec[1]
        plugin_override = plugin_config.pop('override', False)
        plugin_fn = resolver.resolve(plugin_spec[0])(plugin_config)
        client.add_method(plugin_fn, plugin_override)

    # We bind the configuration into the client itself to ease
    # debugging
    client._config = config_copy
    return client
Esempio n. 5
0
    def __init__(self, ini_path=None, ini_dir=None, load_sections=None):
        """
        :param ini_dir: Directory path in which to start looking for the ini
        file.  Will climb the file tree from here looking for 'tests.ini' file,
        unless 'WEAVE_TESTFILE' env var is set, in which case it will climb the
        file tree from here looking for 'tests_${WEAVE_TESTFILE}.ini'.

        :param ini_path: Full path to configuration file.  Takes precedence
        over ini_dir, if both are provided.  Raises IOError if file doesn't
        exist.

        One or the other of `ini_dir` or `ini_path` arguments MUST be provided.

        :param load_sections: A sequence of strings that name the configuration
        sections that should be dynamically loaded.  Any entry in this sequence
        could alternately be a 2-tuple containing the name of the section and
        the corresponding class parameter value to use.
        """
        self.start_dir = ini_dir
        if ini_path:
            if not os.path.isfile(ini_path):
                raise IOError("invalid config file: %s" % ini_path)
            ini_dir = os.path.dirname(ini_path)
        elif ini_dir:
            if 'WEAVE_TESTFILE' in os.environ:
                test_filename = 'tests_%s.ini' % os.environ['WEAVE_TESTFILE']
            else:
                test_filename = 'tests.ini'

            while True:
                ini_path = os.path.join(ini_dir, test_filename)
                if os.path.exists(ini_path):
                    break

                if ini_path == ("/%s" % test_filename) \
                    or ini_path == test_filename:
                    raise IOError("cannot locate %s" % test_filename)

                ini_dir = os.path.split(ini_dir)[0]
            else:
                raise ValueError('No ini_path or ini_dir specified.')

        self.ini_dir = ini_dir
        self.ini_path = ini_path

        ini_cfg = RawConfigParser()
        ini_cfg.read(ini_path)

        # loading loggers
        self.config = self.convert_config(ini_cfg, ini_path)

        # Ensure that metlog is available, either from the config
        # or by setting up a default client.
        try:
            loader = load_and_configure(self.config, "metlog_loader")
            client = loader.default_client
        except KeyError:
            sender = DebugCaptureSender()
            client = MetlogClient(sender, "syncserver")
        CLIENT_HOLDER.set_client(client.logger, client)
        if not hasattr(client, "cef"):
            log_cef_fn = metlog_cef.cef_plugin.config_plugin(dict())
            client.add_method(log_cef_fn)

        if load_sections is not None:
            for section in load_sections:
                if isinstance(section, tuple):
                    self.add_class(section[0], cls_param=section[1])
                else:
                    self.add_class(section)
Esempio n. 6
0
    def __init__(self, ini_path=None, ini_dir=None, load_sections=None):
        """
        :param ini_dir: Directory path in which to start looking for the ini
        file.  Will climb the file tree from here looking for 'tests.ini' file,
        unless 'WEAVE_TESTFILE' env var is set, in which case it will climb the
        file tree from here looking for 'tests_${WEAVE_TESTFILE}.ini'.

        :param ini_path: Full path to configuration file.  Takes precedence
        over ini_dir, if both are provided.  Raises IOError if file doesn't
        exist.

        One or the other of `ini_dir` or `ini_path` arguments MUST be provided.

        :param load_sections: A sequence of strings that name the configuration
        sections that should be dynamically loaded.  Any entry in this sequence
        could alternately be a 2-tuple containing the name of the section and
        the corresponding class parameter value to use.
        """
        self.start_dir = ini_dir
        if ini_path:
            if not os.path.isfile(ini_path):
                raise IOError("invalid config file: %s" % ini_path)
            ini_dir = os.path.dirname(ini_path)
        elif ini_dir:
            if 'WEAVE_TESTFILE' in os.environ:
                test_filename = 'tests_%s.ini' % os.environ['WEAVE_TESTFILE']
            else:
                test_filename = 'tests.ini'

            while True:
                ini_path = os.path.join(ini_dir, test_filename)
                if os.path.exists(ini_path):
                    break

                if ini_path == ("/%s" % test_filename) \
                    or ini_path == test_filename:
                    raise IOError("cannot locate %s" % test_filename)

                ini_dir = os.path.split(ini_dir)[0]
            else:
                raise ValueError('No ini_path or ini_dir specified.')

        self.ini_dir = ini_dir
        self.ini_path = ini_path

        ini_cfg = RawConfigParser()
        ini_cfg.read(ini_path)

        # loading loggers
        self.config = self.convert_config(ini_cfg, ini_path)

        # Ensure that metlog is available, either from the config
        # or by setting up a default client.
        try:
            loader = load_and_configure(self.config, "metlog_loader")
            client = loader.default_client
        except KeyError:
            sender = DebugCaptureSender()
            client = MetlogClient(sender, "syncserver")
        CLIENT_HOLDER.set_client(client.logger, client)
        if not hasattr(client, "cef"):
            log_cef_fn = metlog_cef.cef_plugin.config_plugin(dict())
            client.add_method(log_cef_fn)

        if load_sections is not None:
            for section in load_sections:
                if isinstance(section, tuple):
                    self.add_class(section[0], cls_param=section[1])
                else:
                    self.add_class(section)