Example #1
0
 def _validate(self, instance, value):
     if value is None and self.required:
         raise ConfigurationError(
             "Configuration error: value for {} is required.".format(self.dict_key), self.dict_key
         )
     if self.validators and value is not None:
         for validator in self.validators:
             value = validator(value, self.dict_key)
     if self.type and value is not None:
         try:
             value = self.type(value)
         except ValueError as e:
             raise ConfigurationError("{}: {}".format(self.dict_key, compat.text_type(e)), self.dict_key)
     instance._errors.pop(self.dict_key, None)
     return value
Example #2
0
 def __call__(self, value, field_name):
     value = compat.text_type(value)
     match = re.match(self.regex, value, re.IGNORECASE)
     if not match:
         raise ConfigurationError(
             "{} does not match pattern {}".format(value,
                                                   self.verbose_pattern),
             field_name)
     val, unit = match.groups()
     try:
         val = int(val) * self.unit_multipliers[unit]
     except KeyError:
         raise ConfigurationError("{} is not a supported unit".format(unit),
                                  field_name)
     return val
Example #3
0
    def tag(self, **tags):
        """
        This method is deprecated, please use "label()" instead.

        Tag this span with one or multiple key/value tags. Both the values should be strings

            span_obj.tag(key1="value1", key2="value2")

        Note that keys will be dedotted, replacing dot (.), star (*) and double quote (") with an underscore (_)

        :param tags: key/value pairs of tags
        :return: None
        """
        for key in tags.keys():
            self.labels[LABEL_RE.sub("_", compat.text_type(key))] = encoding.keyword_field(compat.text_type(tags[key]))
Example #4
0
def test_header_encodings(elasticapm_client):
    """
    Tests that headers are encoded as bytestrings. If they aren't,
    urllib assumes it needs to encode the data as well, which is already a zlib
    encoded bytestring, and explodes.
    """
    headers = {compat.text_type("X"): compat.text_type("V")}
    transport = Transport("http://localhost:9999",
                          headers=headers,
                          client=elasticapm_client)
    transport.start_thread()
    try:
        with mock.patch("elasticapm.transport.http.urllib3.PoolManager.urlopen"
                        ) as mock_urlopen:
            mock_urlopen.return_value = mock.Mock(status=202)
            transport.send("")
        _, args, kwargs = mock_urlopen.mock_calls[0]
        if compat.PY2:
            assert isinstance(args[1], compat.binary_type)
        for k, v in kwargs["headers"].items():
            assert isinstance(k, compat.binary_type)
            assert isinstance(v, compat.binary_type)
    finally:
        transport.close()
Example #5
0
def get_lines_from_file(filename,
                        lineno,
                        context_lines,
                        loader=None,
                        module_name=None):
    """
    Returns context_lines before and after lineno from file.
    Returns (pre_context_lineno, pre_context, context_line, post_context).
    """
    lineno = lineno - 1
    lower_bound = max(0, lineno - context_lines)
    upper_bound = lineno + context_lines

    source = None
    if loader is not None and hasattr(loader, "get_source"):
        result = get_source_lines_from_loader(loader, module_name, lineno,
                                              lower_bound, upper_bound)
        if result is not None:
            return result

    if source is None:
        try:
            with open(filename, "rb") as file_obj:
                encoding = "utf8"
                # try to find encoding of source file by "coding" header
                # if none is found, utf8 is used as a fallback
                for line in itertools.islice(file_obj, 0, 2):
                    match = _coding_re.search(line.decode("utf8"))
                    if match:
                        encoding = match.group(1)
                        break
                file_obj.seek(0)
                lines = [
                    compat.text_type(line, encoding, "replace") for line in
                    itertools.islice(file_obj, lower_bound, upper_bound + 1)
                ]
                offset = lineno - lower_bound
                return (
                    [l.strip("\r\n") for l in lines[0:offset]],
                    lines[offset].strip("\r\n"),
                    [l.strip("\r\n")
                     for l in lines[offset +
                                    1:]] if len(lines) > offset else [],
                )
        except (OSError, IOError, IndexError):
            pass
    return None, None, None
Example #6
0
    def get_config(self, current_version=None, keys=None):
        """
        Gets configuration from a remote APM Server

        :param current_version: version of the current configuration
        :param keys: a JSON-serializable dict to identify this instance, e.g.
                {
                    "service": {
                        "name": "foo",
                        "environment": "bar"
                    }
                }
        :return: a three-tuple of new version, config dictionary and validity in seconds.
                 Any element of the tuple can be None.
        """
        url = self._config_url
        data = json_encoder.dumps(keys).encode("utf-8")
        headers = self._headers.copy()
        headers.update(self.auth_headers)
        max_age = 300
        if current_version:
            headers["If-None-Match"] = current_version
        try:
            response = self.http.urlopen(
                "POST", url, body=data, headers=headers, timeout=self._timeout, preload_content=False
            )
        except (urllib3.exceptions.RequestError, urllib3.exceptions.HTTPError) as e:
            logger.debug("HTTP error while fetching remote config: %s", compat.text_type(e))
            return current_version, None, max_age
        body = response.read()
        if "Cache-Control" in response.headers:
            try:
                max_age = int(next(re.finditer(r"max-age=(\d+)", response.headers["Cache-Control"])).groups()[0])
            except StopIteration:
                logger.debug("Could not parse Cache-Control header: %s", response.headers["Cache-Control"])
        if response.status == 304:
            # config is unchanged, return
            logger.debug("Configuration unchanged")
            return current_version, None, max_age
        elif response.status >= 400:
            return None, None, max_age

        if not body:
            logger.debug("APM Server answered with empty body and status code %s", response.status)
            return current_version, None, max_age

        return response.headers.get("Etag"), json_encoder.loads(body.decode("utf-8")), max_age
Example #7
0
    def get_data_from_request(self, request, event_type):
        result = {
            "env": dict(get_environ(request.META)),
            "method": request.method,
            "socket": {
                "remote_address": request.META.get("REMOTE_ADDR")
            },
            "cookies": dict(request.COOKIES),
        }
        if self.config.capture_headers:
            request_headers = dict(get_headers(request.META))

            for key, value in request_headers.items():
                if isinstance(value, (int, float)):
                    request_headers[key] = str(value)

            result["headers"] = request_headers

        if request.method in constants.HTTP_WITH_BODY:
            capture_body = self.config.capture_body in ("all", event_type)
            if not capture_body:
                result["body"] = "[REDACTED]"
            else:
                content_type = request.META.get("CONTENT_TYPE")
                if content_type == "application/x-www-form-urlencoded":
                    data = compat.multidict_to_dict(request.POST)
                elif content_type and content_type.startswith(
                        "multipart/form-data"):
                    data = compat.multidict_to_dict(request.POST)
                    if request.FILES:
                        data["_files"] = {
                            field: file.name
                            for field, file in compat.iteritems(request.FILES)
                        }
                else:
                    try:
                        data = request.body
                    except Exception as e:
                        self.logger.debug("Can't capture request body: %s",
                                          compat.text_type(e))
                        data = "<unavailable>"
                if data is not None:
                    result["body"] = data

        url = get_raw_uri(request)
        result["url"] = get_url_dict(url)
        return result
Example #8
0
 def set_tag(self, key, value):
     if self.is_transaction:
         if key == "type":
             self.elastic_apm_ref.transaction_type = value
         elif key == "result":
             self.elastic_apm_ref.result = value
         elif key == tags.HTTP_STATUS_CODE:
             self.elastic_apm_ref.result = "HTTP {}xx".format(
                 compat.text_type(value)[0])
             traces.set_context({"status_code": value}, "response")
         elif key == "user.id":
             traces.set_user_context(user_id=value)
         elif key == "user.username":
             traces.set_user_context(username=value)
         elif key == "user.email":
             traces.set_user_context(email=value)
         elif key == tags.HTTP_URL:
             traces.set_context({"url": get_url_dict(value)}, "request")
         elif key == tags.HTTP_METHOD:
             traces.set_context({"method": value}, "request")
         elif key == tags.COMPONENT:
             traces.set_context({"framework": {"name": value}}, "service")
         else:
             self.elastic_apm_ref.tag(**{key: value})
     else:
         if key.startswith("db."):
             span_context = self.elastic_apm_ref.context or {}
             if "db" not in span_context:
                 span_context["db"] = {}
             if key == tags.DATABASE_STATEMENT:
                 span_context["db"]["statement"] = value
             elif key == tags.DATABASE_USER:
                 span_context["db"]["user"] = value
             elif key == tags.DATABASE_TYPE:
                 span_context["db"]["type"] = value
                 self.elastic_apm_ref.type = "db." + value
             else:
                 self.elastic_apm_ref.tag(**{key: value})
             self.elastic_apm_ref.context = span_context
         elif key == tags.SPAN_KIND:
             self.elastic_apm_ref.type = value
         else:
             self.elastic_apm_ref.tag(**{key: value})
     return self
Example #9
0
    def get_config(self, current_version=None, keys=None):
        url = self._config_url
        data = json_encoder.dumps(keys).encode("utf-8")
        headers = self._headers.copy()
        max_age = 300
        if current_version:
            headers["If-None-Match"] = current_version
        try:
            response = self.http.urlopen("POST",
                                         url,
                                         body=data,
                                         headers=headers,
                                         timeout=self._timeout,
                                         preload_content=False)
        except (urllib3.exceptions.RequestError,
                urllib3.exceptions.HTTPError) as e:
            logger.debug("HTTP error while fetching remote config: %s",
                         compat.text_type(e))
            return current_version, None, max_age
        body = response.read()
        if "Cache-Control" in response.headers:
            try:
                max_age = int(
                    next(
                        re.finditer(
                            r"max-age=(\d+)",
                            response.headers["Cache-Control"])).groups()[0])
            except StopIteration:
                logger.debug("Could not parse Cache-Control header: %s",
                             response["Cache-Control"])
        if response.status == 304:
            # config is unchanged, return
            logger.debug("Configuration unchanged")
            return current_version, None, max_age
        elif response.status >= 400:
            return None, None, max_age

        return response.headers.get("Etag"), json_encoder.loads(
            body.decode("utf-8")), max_age
Example #10
0
class ProxyClient(object):
    """
    A proxy which represents the current client at all times.
    """

    # introspection support:
    __members__ = property(lambda x: x.__dir__())

    # Need to pretend to be the wrapped class, for the sake of objects that care
    # about this (especially in equality tests)
    __class__ = property(lambda x: get_client().__class__)

    __dict__ = property(lambda o: get_client().__dict__)

    __repr__ = lambda: repr(get_client())
    __getattr__ = lambda x, o: getattr(get_client(), o)
    __setattr__ = lambda x, o, v: setattr(get_client(), o, v)
    __delattr__ = lambda x, o: delattr(get_client(), o)

    __lt__ = lambda x, o: get_client() < o
    __le__ = lambda x, o: get_client() <= o
    __eq__ = lambda x, o: get_client() == o
    __ne__ = lambda x, o: get_client() != o
    __gt__ = lambda x, o: get_client() > o
    __ge__ = lambda x, o: get_client() >= o
    if compat.PY2:
        __cmp__ = lambda x, o: cmp(get_client(), o)  # noqa F821
    __hash__ = lambda x: hash(get_client())
    # attributes are currently not callable
    # __call__ = lambda x, *a, **kw: get_client()(*a, **kw)
    __nonzero__ = lambda x: bool(get_client())
    __len__ = lambda x: len(get_client())
    __getitem__ = lambda x, i: get_client()[i]
    __iter__ = lambda x: iter(get_client())
    __contains__ = lambda x, i: i in get_client()
    __getslice__ = lambda x, i, j: get_client()[i:j]
    __add__ = lambda x, o: get_client() + o
    __sub__ = lambda x, o: get_client() - o
    __mul__ = lambda x, o: get_client() * o
    __floordiv__ = lambda x, o: get_client() // o
    __mod__ = lambda x, o: get_client() % o
    __divmod__ = lambda x, o: get_client().__divmod__(o)
    __pow__ = lambda x, o: get_client()**o
    __lshift__ = lambda x, o: get_client() << o
    __rshift__ = lambda x, o: get_client() >> o
    __and__ = lambda x, o: get_client() & o
    __xor__ = lambda x, o: get_client() ^ o
    __or__ = lambda x, o: get_client() | o
    __div__ = lambda x, o: get_client().__div__(o)
    __truediv__ = lambda x, o: get_client().__truediv__(o)
    __neg__ = lambda x: -(get_client())
    __pos__ = lambda x: +(get_client())
    __abs__ = lambda x: abs(get_client())
    __invert__ = lambda x: ~(get_client())
    __complex__ = lambda x: complex(get_client())
    __int__ = lambda x: int(get_client())
    if compat.PY2:
        __long__ = lambda x: long(get_client())  # noqa F821
    __float__ = lambda x: float(get_client())
    __str__ = lambda x: str(get_client())
    __unicode__ = lambda x: compat.text_type(get_client())
    __oct__ = lambda x: oct(get_client())
    __hex__ = lambda x: hex(get_client())
    __index__ = lambda x: get_client().__index__()
    __coerce__ = lambda x, o: x.__coerce__(x, o)
    __enter__ = lambda x: x.__enter__()
    __exit__ = lambda x, *a, **kw: x.__exit__(*a, **kw)
Example #11
0
    def _emit(self, record, **kwargs):
        data = {}

        for k, v in compat.iteritems(record.__dict__):
            if "." not in k and k not in ("culprit",):
                continue
            data[k] = v

        stack = getattr(record, "stack", None)
        if stack is True:
            stack = iter_stack_frames(config=self.client.config)

        if stack:
            frames = []
            started = False
            last_mod = ""
            for item in stack:
                if isinstance(item, (list, tuple)):
                    frame, lineno = item
                else:
                    frame, lineno = item, item.f_lineno

                if not started:
                    f_globals = getattr(frame, "f_globals", {})
                    module_name = f_globals.get("__name__", "")
                    if last_mod.startswith("logging") and not module_name.startswith("logging"):
                        started = True
                    else:
                        last_mod = module_name
                        continue
                frames.append((frame, lineno))
            stack = frames

        custom = getattr(record, "data", {})
        # Add in all of the data from the record that we aren't already capturing
        for k in record.__dict__.keys():
            if k in (
                "stack",
                "name",
                "args",
                "msg",
                "levelno",
                "exc_text",
                "exc_info",
                "data",
                "created",
                "levelname",
                "msecs",
                "relativeCreated",
            ):
                continue
            if k.startswith("_"):
                continue
            custom[k] = record.__dict__[k]

        # If there's no exception being processed,
        # exc_info may be a 3-tuple of None
        # http://docs.python.org/library/sys.html#sys.exc_info
        if record.exc_info and all(record.exc_info):
            handler = self.client.get_handler("elasticapm.events.Exception")
            exception = handler.capture(self.client, exc_info=record.exc_info)
        else:
            exception = None

        return self.client.capture(
            "Message",
            param_message={"message": compat.text_type(record.msg), "params": record.args},
            stack=stack,
            custom=custom,
            exception=exception,
            level=record.levelno,
            logger_name=record.name,
            **kwargs
        )
Example #12
0
 def __call__(self, value, field_name):
     value = compat.text_type(value)
     match = re.match(self.regex, value)
     if match:
         return value
     raise ConfigurationError("{} does not match pattern {}".format(value, self.verbose_pattern), field_name)
Example #13
0
 def _labels_to_key(self, labels):
     return tuple((k, compat.text_type(v)) for k, v in sorted(compat.iteritems(labels)))
Example #14
0
def no_error(request, id=None):
    resp = HttpResponse(compat.text_type(id))
    resp["My-Header"] = "foo"
    return resp
Example #15
0
    def get_data_from_request(self, request, event_type):
        result = {
            "env": dict(get_environ(request.META)),
            "method": request.method,
            "socket": {
                "remote_address": request.META.get("REMOTE_ADDR"),
                "encrypted": request.is_secure()
            },
            "cookies": dict(request.COOKIES),
        }
        if self.config.capture_headers:
            request_headers = dict(get_headers(request.META))

            for key, value in request_headers.items():
                if isinstance(value, (int, float)):
                    request_headers[key] = str(value)

            result["headers"] = request_headers

        if request.method in constants.HTTP_WITH_BODY:
            capture_body = self.config.capture_body in ("all", event_type)
            if not capture_body:
                result["body"] = "[REDACTED]"
            else:
                content_type = request.META.get("CONTENT_TYPE")
                if content_type == "application/x-www-form-urlencoded":
                    data = compat.multidict_to_dict(request.POST)
                elif content_type and content_type.startswith(
                        "multipart/form-data"):
                    data = compat.multidict_to_dict(request.POST)
                    if request.FILES:
                        data["_files"] = {
                            field: file.name
                            for field, file in compat.iteritems(request.FILES)
                        }
                else:
                    try:
                        data = request.body
                    except Exception as e:
                        self.logger.debug("Can't capture request body: %s",
                                          compat.text_type(e))
                        data = "<unavailable>"
                if data is not None:
                    result["body"] = data

        if hasattr(request, "get_raw_uri"):
            # added in Django 1.9
            url = request.get_raw_uri()
        else:
            try:
                # Requires host to be in ALLOWED_HOSTS, might throw a
                # DisallowedHost exception
                url = request.build_absolute_uri()
            except DisallowedHost:
                # We can't figure out the real URL, so we have to set it to
                # DisallowedHost
                result["url"] = {"full": "DisallowedHost"}
                url = None
        if url:
            result["url"] = get_url_dict(url)
        return result