Skip to content

Django performance integration suggestions #956

Closed
@Christophe31

Description

@Christophe31

Hello,

To have better view in performance with django I added myself a few extra tools in my project.

I feel like they answer common needs, here I propose them as is.

Not sure any of these tools would be of interest to you add to this lib.

#!/usr/bin/env python

import sentry_sdk

################### A template tag to see the cost of a block before thinking about caching.
from django.template import Node
from django.template.base import token_kwargs
from django.template.loader_tags import register

class SentrySpanNode(Node):
    def __init__(self, var, name, nodelist,  description, data, op):
        self.nodelist = nodelist
        self.description = description
        self.op = op
        self.data = data

    def __repr__(self):
        return '<%s>' % self.__class__.__name__

    def render(self, context):
        with sentry_sdk.start_span(
                op=self.op,
                description=self.description
        ) as span:
            span.set_data("data", self.data)
            return self.nodelist.render(context)


@register.tag('sentryspan')
def do_start_span(parser, token):
    bits = token.split_contents()
    remaining_bits = bits[1:]
    kwargs = token_kwargs(remaining_bits, parser)
    nodelist = parser.parse(('endsentryspan',))
    description = str(kwargs.get("description", "unnamed"))
    data = kwargs.get("data", {})
    op = str(kwargs.get("op", "render_node"))
    parser.delete_first_token()
    return SentrySpanNode(None, None, nodelist, description=description, data=data, op=op)
############################################################################
############## A simple decorator
from functools import wraps


def sentry_timeit(op="function", description=None):
    def decorator(method):
        @wraps(method)
        def timed(*args, **kw):
            with sentry_sdk.start_span(
                op=op,
                description=description or ".".join(
                    method.__code__.co_filename[:-3].split("/")[-1:] +
                    [method.__name__])
            ) as span:
                kwargs = kw.copy()
                for i, arg in enumerate(args):
                    kwargs[i] = arg
                span.set_tag("func_name", method.__name__)
                span.set_data("args", kwargs)
                return method(*args, **kw)
        return timed
    return decorator

###################### Monkeypatch django template processing most common entries
###### edit: This feature as been implemented more expensively in PR #957 ######
# note: monkeypatching the Template.render method seems overkill as the call often cascade.
import django.shortcuts
from django.template.response import SimpleTemplateResponse
django.shortcuts.render = sentry_timeit("render")(django.shortcuts.render)
_original = SimpleTemplateResponse.rendered_content


@property
@sentry_timeit("render")
def rendered_content(self):
    return _original.fget(self)


SimpleTemplateResponse.rendered_content = rendered_content
########################################

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions