避免OpenERP审计轨迹错误

4 投票
3 回答
1531 浏览
提问于 2025-04-16 03:31

我想通过安装审计跟踪模块来管理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 个回答

1

你能在像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()
2

我想我找到了答案。
来看一个例子:

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。

5

要理解发生了什么,查看源代码是很重要的。不过从你发布的内容来看,之前的光标似乎没有被明确关闭。

cr = sqldb.db_connect(dbname).cursor()
.........
cr.close()
cr = None

我建议你去修改一下audittrail.py,找找你在哪里创建光标,在哪里关闭光标。一个常见的问题是对异常处理不当,导致代码跳过正常的关闭步骤。

试着在可疑的光标操作周围加上try、except和finally语句。这应该能帮助你解决问题。

撰写回答