def log_error(request, view, action, errors, exc_info=None):
    # We only log the first error, send the rest as data; it's simpler this way
    error_msg = "Error %s: %s" % (action, format_error(errors[0]))

    log_kwargs = {}

    if not exc_info:
        try:
            exc_info = full_exc_info()
        except:
            exc_info = None
    if exc_info and not isinstance(exc_info, tuple) or not len(exc_info) or not exc_info[0]:
        exc_info = None

    if exc_info:
        log_kwargs["exc_info"] = exc_info

    extra_data = {
        'errors': errors,
        'process_id': os.getpid()
    }

    try:
        import psutil, math, time, thread
    except __HOLE__:
        pass
    else:
        p = psutil.Process(os.getpid())
        proc_timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(p.create_time))
        try:
            create_usec = six.text_type(p.create_time - math.floor(p.create_time))[1:5]
        except:
            create_usec = ''
        proc_timestamp += create_usec
        extra_data['process_create_date'] = proc_timestamp
        extra_data['thread_id'] = thread.get_ident()

    if isinstance(errors[0], CropDusterUrlException):
        urlconf = get_urlconf()
        resolver = get_resolver(urlconf)
        extra_data['resolver_data'] = {
            "regex": resolver.regex,
            "urlconf_name": resolver.urlconf_name,
            "default_kwargs": resolver.default_kwargs,
            "namespace": resolver.namespace,
            "urlconf_module": resolver.urlconf_module
        }

        resolver_reverse_dict = dict(
            [(force_unicode(k), resolver.reverse_dict[k]) for k in resolver.reverse_dict])
        resolver_namespace_dict = dict(
            [(force_unicode(k), resolver.namespace_dict[k]) for k in resolver.namespace_dict])

        extra_data.update({
            'resolver_data': {
                "regex": resolver.regex,
                "urlconf_name": resolver.urlconf_name,
                "default_kwargs": resolver.default_kwargs,
                "namespace": resolver.namespace,
                "urlconf_module": resolver.urlconf_module
            },
            'resolver_reverse_dict': resolver_reverse_dict,
            'resolver_namespace_dict': resolver_namespace_dict,
            'resolver_app_dict': resolver.app_dict,
            'resolver_url_patterns': resolver.url_patterns,
            'urlconf': urlconf,
            'view': 'cropduster.views.%s' % view,
        })

    raven_kwargs = {'request': request, 'extra': extra_data, 'data': {'message': error_msg}}

    if raven_client:
        if exc_info:
            return raven_client.get_ident(
                raven_client.captureException(exc_info=exc_info, **raven_kwargs))
        else:
            return raven_client.get_ident(
                raven_client.captureMessage(error_msg, **raven_kwargs))
    else:
        extra_data.update({
            'request': request,
            'url': request.path_info,
        })
        logger.error(error_msg, extra=extra_data, **log_kwargs)
        return None
dataset/ETHPy150Open theatlantic/django-cropduster/cropduster/exceptions.py/log_error