避免OpenERP审计轨迹错误
我想通过安装审计跟踪模块来管理OpenERP用户的活动。
在创建了一些规则后(定义哪个用户、哪个对象以及哪个活动(比如创建、更新等)会被监控),我更新了一个产品来看看效果。
但是,当我尝试更新产品时,系统出现了错误。查看日志后,我发现了以下信息:
[2010-08-31 12:53:35,042] 游标没有被明确关闭
[2010-08-31 12:53:35,043] 游标是在 /home/pilgrim/working/sources/addons/audittrail/audittrail.py:204 创建的
这里是导致错误的那一行代码:
cr = pooler.get_db(db).cursor()
查看sql_db.py时,我看到了这个注释:
def __del__(self):
if not self.__closed:
# Oops. 'self' has not been closed explicitly.
# The cursor will be deleted by the garbage collector,
# but the database connection is not put back into the connection
# pool, preventing some operation on the database like dropping it.
# This can also lead to a server overload.
msg = "Cursor not closed explicitly\n" \
"Cursor was created at %s:%s" % self.__caller
log(msg, netsvc.LOG_WARNING)
self.close()
因为我对Python还很陌生,所以不知道该如何解决这个问题?
有没有什么提示可以帮助我解决这个问题?
谢谢!
3 个回答
你能在像PyDev插件这样的调试工具中运行OpenERP吗?我发现这是追踪问题最有效的方法。我没有使用审计跟踪模块,但我快速查看了一下源代码,看起来光标是在log_fct()
的开头附近打开的。(我本以为它会报告第207行,你用的是什么版本呢?)我认为相关的代码是:
def log_fct(self, db, uid, passwd, object, method, fct_src, *args):
logged_uids = []
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor() # line 207 in version 5.0.12
# ...
if method in ('create'):
# ...
cr.close()
return res_id
# ...
cr.close()
看起来这个方法里有几个返回语句,但每个返回语句似乎都先调用了cr.close()
,所以我没有看到明显的问题。试着在这个方法里设置一个断点,用调试工具运行它。如果不行,你可以尝试用下面这样的方式写入日志:
logger = netsvc.Logger()
logger.notifyChannel('audittrail', netsvc.LOG_INFO, 'something happened')
更新:
你提到在高负载下会发生这种情况。也许是抛出了一个异常,导致光标没有被关闭。你可以使用try
... finally
语句来确保光标始终被关闭。下面是上面的示例转换后的样子:
def log_fct(self, db, uid, passwd, object, method, fct_src, *args):
logged_uids = []
pool = pooler.get_pool(db)
cr = pooler.get_db(db).cursor() # line 207 in version 5.0.12
try:
# ...
if method in ('create'):
# ...
return res_id
# ...
finally:
cr.close()
我想我找到了答案。
来看一个例子:
def a():
try:
print 'before return '
return 1
finally:
print 'in finally'
调用 a()
before return
in finally
1
这很正常。好的。
再试一个例子(代码摘自 audittrail.py):
def do_something_with_db(db):
// open cusror again
cr = db.cursor()
// do somethign
// close cursor internally
cr.close()
def execute(db)
// 1, open connection and open cursor
cr = db.cursor
try:
//2, do something with db, seeing that this method will open cursor again
return do_something_with_db(db)
finally:
cr.close()
看到 do_something_with_db 的实现试图打开一个游标(可以叫做连接),但当前的游标并没有明确关闭。
所以解决办法很简单:把当前的游标传递过去。
Before
**do_something_with_db(db)**
after
**do_something_with_db(cr)**
现在错误消失了。
@Don Kirkby:是的,我们应该尝试使用 try...finally。
要理解发生了什么,查看源代码是很重要的。不过从你发布的内容来看,之前的光标似乎没有被明确关闭。
cr = sqldb.db_connect(dbname).cursor()
.........
cr.close()
cr = None
我建议你去修改一下audittrail.py,找找你在哪里创建光标,在哪里关闭光标。一个常见的问题是对异常处理不当,导致代码跳过正常的关闭步骤。
试着在可疑的光标操作周围加上try、except和finally语句。这应该能帮助你解决问题。