Python , modwsgi et bottle: trucs et astuces

Gestion de log

Il est toujours possible de créer un logger pour écrire dans un fichier externe les logs applicatives.

import os, platform
import logging
if platform.platform().startswith('Windows'):
    logging_file = os.path.join(os.getenv('HOMEDRIVE'), os.getenv('HOMEPATH'), 'test.log')
else:
    logging_file = os.path.join(os.getenv('HOME'), 'test.log')
print("Logging to", logging_file)

logging.basicConfig(level=logging.DEBUG,
    format='%(asctime)s : %(levelname)s - %(name)s - %(module)s \ %(funcName)s : %(message)s',
    filename = logging_file,
    filemode = 'w',
)
logging.debug("Start of the program")
logging.info("Doing something")
logging.warning("Dying now")

Mais il est aussi possible d’écrire dans les logs apache du site web. Cependant cela ce limite à:

  • des messages d’erreurs
  • uniquement dans le contexte request
@route('/')
def index():
        print >> request.environ['wsgi.errors'], 'hello'
    print >> request.environ['wsgi.errors'], '\n'.join(('%s:%s' % (i,request.environ[i]) for i in request.environ.keys()))
    return static_file('index.html', root='C:/www/test1')

ce qui donne

[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] hello
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] wsgi.multiprocess:False
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SERVER_PROTOCOL:HTTP/1.1
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SERVER_SOFTWARE:Apache/2.2.22 (Win32) mod_wsgi/3.3 Python/2.7.2
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SCRIPT_NAME:/myapp
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] mod_wsgi.handler_script:
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SERVER_SIGNATURE:
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] REQUEST_METHOD:GET
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] PATH_INFO:
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] PATHEXT:.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] QUERY_STRING:
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] bottle.app:<bottle.Bottle object at 0x0289DF50>
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] HTTP_USER_AGENT:Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20100101 Firefox/21.0
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] HTTP_CONNECTION:keep-alive
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SERVER_NAME:127.0.0.1
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] REMOTE_ADDR:127.0.0.1
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] mod_wsgi.request_handler:wsgi-script
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] wsgi.url_scheme:http
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] bottle.request:<LocalRequest: GET http://127.0.0.1/myapp/>
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] mod_wsgi.callable_object:application
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SERVER_PORT:80
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] mod_wsgi.version:(3, 3)
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] mod_wsgi.input_chunked:0
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SERVER_ADDR:127.0.0.1
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] DOCUMENT_ROOT:C:/www/test1
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] mod_wsgi.process_group:
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] COMSPEC:C:\\Windows\\system32\\cmd.exe
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] route.handle:<GET '/' <function index at 0x02A9DD70>>
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SCRIPT_FILENAME:C:/www/test1/wsgi-scripts/myapp.wsgi
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SERVER_ADMIN:admin@fraoustin-PC.home
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] route.url_args:{}
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] wsgi.input:<mod_wsgi.Input object at 0x02A84610>
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] HTTP_HOST:127.0.0.1
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] wsgi.multithread:True
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] SystemRoot:C:\\Windows
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] REQUEST_URI:/myapp
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] HTTP_ACCEPT:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] WINDIR:C:\\Windows
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] wsgi.version:(1, 1)
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] bottle.route:<GET '/' <function index at 0x02A9DD70>>
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] GATEWAY_INTERFACE:CGI/1.1
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] wsgi.run_once:False
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] wsgi.errors:<mod_wsgi.Log object at 0x02A84638>
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] REMOTE_PORT:56249
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] HTTP_ACCEPT_LANGUAGE:fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] mod_wsgi.application_group:test1.fr|/myapp
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] mod_wsgi.script_reloading:1
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] wsgi.file_wrapper:<built-in method file_wrapper of mod_wsgi.Adapter object at 0x0170B920>
[Sun May 26 21:31:31 2013] [error] [client 127.0.0.1] HTTP_ACCEPT_ENCODING:gzip, deflate

Gestion d’un pool de connexion

Il est souvent utile d’utiliser des pools de connexion pour le lien avec un SGBD.

On peut utiliser pour cela le module Queue de python

# Change working directory so relative paths (and template lookup) work again
import os, sys
os.chdir(os.path.dirname(__file__))
sys.path.append(os.path.dirname(__file__)) #permet l'utilisation de pypyodbc

import bottle
from bottle import route, run, template, static_file, request, response

import time
from xml.dom.minidom import getDOMImplementation

import Queue

import pypyodbc as odbc

CNX_STR='DSN=TESTPG;UID=postgres;PWD=postgres'
SQL='select count(*) from test'
SIZE_POOL=10

class PoolConnect(Queue.Queue):

    def put(self, item, block=True, timeout=None):
        item.commit()
        Queue.Queue.put(self, item, block, timeout)

POOL = PoolConnect(maxsize=SIZE_POOL)
for i in range(0,SIZE_POOL):
    POOL.put(odbc.connect(CNX_STR))


def generate_response(value):
    impl = getDOMImplementation()
    newdoc = impl.createDocument(None,"sample",None)
    top_element = newdoc.documentElement
    att = newdoc.createElement('msg')
    txt = newdoc.createTextNode(str(value))
    att.appendChild(txt)
    top_element.appendChild(att)
    return newdoc.toxml('iso-8859-15')

@route('/')
def index():
    return static_file('index.html', root='C:/www/test1')

@route('/bypool')
def bypool():
    a = POOL.get()
    cursor = a.cursor()
    cursor.execute(SQL)
    row = cursor.fetchone()
    POOL.put(a)
    response.headers['Content-Type'] = 'text/xml'
    return generate_response(row[0])

application = bottle.default_app()