Python logging em Django

Postado por Rafael Sierra em 3/11/2009

Que o módulo logging e o framework Django são excelentes ferramentas pra qualquer trabalho todos sabem, mas como fazer para integrar os dois?

Eu sempre achei um saco o fato do Django não ter um módulo builtin/contrib que permitisse que ao fazer um simples logging.error('PAN!') a mensagem, a data, o nível, the universe and everything, fossem gravados no banco de dados. Pra resolver esse simples problema, nada como uma simples solução.

Solução
A Solução

Para começar crie um aplicativo em sua aplicação chamado "log", nunca crie a aplicação com nome de "logging", a não ser que você queira problemas com imports não funcionando:

python manage.py startapp log

Em seguida crie o seguinte model:

# -*- encoding: utf-8 -*-
from django.db import models
from django.contrib import admin
import logging
 
class Log(models.Model):
    level = models.IntegerField(db_index=True)
    file = models.CharField(max_length=512, db_index=True)
    lineno = models.IntegerField()
    date = models.DateTimeField(db_index=True)
    message = models.TextField()
 
    def _get_level_name(self):
        return logging.getLevelName(self.level)
    levelName = property(fget=_get_level_name)
 
    def _get_message_summary(self):
        if len(self.message) > 100:
            return '%s...'%(self.message[:100])
        else:
            return self.message
    messageSummary = property(fget=_get_message_summary)
 
    def _get_file_summary(self):
        if len(self.file) > 30:
            return '...%s'%(self.file[-30:])
        return self.file
    fileSummary = property(fget=_get_file_summary)
 
class LogAdmin(admin.ModelAdmin):
   date_hierarchy = 'date'
   list_display = ('level', 'levelName', 'date','messageSummary', 'fileSummary', 'lineno')
   list_filter = ('level',)
   search_fields = ('message', 'file')
 
admin.site.register(Log, LogAdmin)
 

Em seguida crie o arquivo "handler.py" dentro do aplicativo "log" com o seguinte conteúdo:

 
# -*- encoding: utf-8 -*-
import logging
import datetime
from SEUPROJETO.log.models import Log
 
class DjangoHandler(logging.Handler):
    '''Executa a manipulação do log e insere no banco de dados'''
    def emit(self, record):
        log = Log()
        log.level = record.levelno
        log.file = record.pathname
        log.lineno = record.lineno
        log.message = record.msg
        # TODO: Utiizar o record.created
        log.date = datetime.datetime.now()
        log.save()
 

Com isso você já tem tudo que precisa pra logar direto pro banco de dados, só falta uma coisa: Configurar o projeto para o logging usar nosso handler.

Pra fazer isso, edite o arquivo __init__.py (e você achando que esse arquivo era inutil :) e adicione o seguinte conteúdo ao arquivo:

 
import logging
from SEUPROJETO.log.handler import DjangoHandler
django_handler = DjangoHandler()
logging.root.addHandler(django_handler)
 

E pronto, é só correr pro abraço, todo e qualquer log que você executar vai ser armazenado no banco de dados.

Yes! Tudo que faltava pra terminar meu projeto!
Yes! Tudo que faltava pra terminar meu projeto!

... e antes que você venha me reclamar que a p0rr@ do log não ta aparecendo em lugar nenhum, verifique se você está usando o logging.debug com a configuração padrão do módulo logging (que só exibe mensagens de nível mais alto).

Em tempo: Eu sei que é extremamente simples criar um módulo/pacote com tudo isso, mas não seria tão didatico simplesmente falar "baixe aqui, copie ali e import lá".

Se você tiver interesse, sugira a implantação deste módulo ao django.contrib ;)

Acessando `block` tags parentes em templates Django

Postado por Rafael Sierra em 16/12/2008

Se tem algo que eu admiro muito no sistema de templates do Django, é a tag block, eu acho ela realmente f#!$ pra ca#@!&.

Só que recemente eu descobri algo nela que se eu estivesse em um ato sexual teria resultado em um fork(), que é a possibilidade de acessar o conteúdo antigo da tag antes dela ser sobrescrevida por você.

É extremamente simples, vamos pegar como exemplo o seguinte template `base.html`:

 
<html>
    <head><title>{% block title%}Meu site dahora{% endblock %}</html>
    <body>{% block content %}{% endblock %}
</html>
 

A parte legal fica por conta do bloco title. Até então, sempre que eu quizesse colocar "Meu site dahora | Home" no titulo, eu teria que fazer assim em um template filho:

 
{% extends "base.html" %}
{% block title %}Meu site dahora | Home{% endblock %}
 

O problema óbvio nesse caso, é que se um dia eu resolver trocar o titulo pra "O Jardineiro é Jesus", então eu vo ter que editar todo template que extende o template `base.html` pra arrumar isso.

Eis então que surge a solução

 
{% extends "base.html" %}
{% block title %}{{block.super}} | Home{% endblock %}
 

:D Simples e funcional. VIDA LONGA AO DJANGO!

Av. Conselheiro Nébias, 368A, Cj. 413
Vila Mathias - Santos - SP
Telefone: (13) 3345-4580

© 2008 - 2009 Stiod. Todos os direitos reservados.