我如何跟踪 cx_Oracle 是如何尝试连接到数据库的?

How can I trace how cx_Oracle is attempting to connect to the database?

我有三个 Python Anaconda 环境,其中安装了 cx_Oracle 和 Oracle 的 Instantclient。我正在使用 conda 提供虚拟环境,这似乎工作正常,并且我正在使用 Oracle Wallet 向系统提供实际凭据。请注意,这些环境不会也永远不会安装 SQL*Plus,因为它们只是为了通过更高的抽象(例如 Python 或 Java 与数据库通信).

我遇到的问题来自三种环境中的两种。执行以下 Python 脚本时,连接仅适用于三种环境之一。请注意,标识符在不同环境中有所不同,但大致相同。

import cx_Oracle
import os
os.environ['TNS_ADMIN'] = '/data/config/wallets/app'
conn = cx_Oracle.connect('/@ENV_CONNECTION')

当我在一个环境中执行此操作时,我得到了一个我期望的游标实例,并且一切正常。在另外两个上,我看到了这个:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
cx_Oracle.DatabaseError: ORA-12162: TNS:net service name is incorrectly specified

我检查了五重服务名称和钱包本身。有一个 Java 应用程序也在使用这个钱包,如果钱包被认为是坏的,它将无法连接,所以我原则上排除了这种可能性。

唯一未知的是钱包本身是如何被引用的。

我如何反省 cx_Oracle 是如何引用我的 TNS_ADMIN 位置并拉入正确目录的?我觉得如果我能解决这个问题,我就可以解决剩下的问题。

我的环境:

  • 检查 cx_Oracle 传递给较低层(例如 Oracle Net)的内容的最简单方法可能是扩展连接 class 并添加您自己的日志记录。基数 class 为 cx_Oracle.Connection,见示例 https://github.com/oracle/python-cx_Oracle/blob/master/samples/Subclassing.py

  • 您可以运行 strace 或类似工具查看正在打开哪些Oracle 配置文件,例如tnsnames.ora。

  • 可以启用 Oracle 网络跟踪,请参阅 ADR Diagnostic Parameters in sqlnet.ora。对于简单的跟踪,我通常会创建一个文件“~/.sqlnet.ora”,其内容如下: TRACE_LEVEL_CLIENT = USER ADR_BASE=/tmp 然后 运行 应用程序,并通过创建的 /tmp/oradiag_* 目录向下挖掘到 *.trc 文件。

Christopher Jones 的回答让我得救了,但事实是问题远比这更微妙。

我最终在两台机器的主目录中创建了 sqlnet.ora 文件 - 一台可以工作,另一台没有。我注意到日志文件中存在显着差异。

"good" 框中有此条目:

2018-07-06 09:59:36.726 : nnftrne:Using tnsnames.ora address (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = my.oracle.db.host)(PORT = 1521)) (CONNECT_DATA = (SID = my_sid))) for name ENV_CONNECTION

"bad" 框中有此条目:

2018-07-06 10:02:12.879 : nnftrne:Using tnsnames.ora address (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = my.oracle.db.host)(PORT = 1521 for name ENV_CONNECTION

这最终转化为我遇到过的最愚蠢、最令人惊讶的空白限制之一。

请记住,正在使用的钱包(及其相应的 tnsnames.ora 文件)也在 Java 中使用。我的假设是 Python 和 Java 在从这个共享资源中读取时会表现相似,因为它们会在必要时忽略换行符和空格。

我显然错了。

"good" 框中的条目如下所示:

ENV_CONNECTION =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = my.oracle.db.host)(PORT = 1521))
    (CONNECT_DATA =
      (SID = my_sid)
    )
  )

"bad" 框中的条目如下所示:

ENV_CONNECTION =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = my.oracle.db.host)(PORT = 1521
  ))
    (CONNECT_DATA =
      (SID = my_sid)
    )
  )

差异非常细微,但对 Python 来说很明显:如果 tnsnames.ora 文件格式不正确,cx_Oracle 将 失败 解析它,而 Java 是 fine with this.

这本身就是一个错误,但修复格式后可以 Python 再次连接。