在PHP中获取唯一的工作进程、线程、进程或请求ID

16

在多线程环境(像大多数网页平台)中,我经常在我的应用程序的日志中包含某种类型的线程ID。这使我能够准确地知道哪个日志条目来自哪个请求/线程,当有多个同时写入同一日志的请求时。

在.NET/C#中,可以通过log4net的格式化程序完成此操作,默认情况下包括当前线程的ManagedThreadId(一个数字)或Name(给定名称)。这些属性唯一标识一个线程(例如,请参见:如何使用log4net记录线程池线程的正确上下文?

在PHP中,我没有找到类似的东西(我在Google、PHP文档和SO上问过)。它存在吗?


3
这可能会有用:http://php.net/manual/zh/function.zend-thread-id.php - Daan
5个回答

23

最近我一直使用apache_getenv("UNIQUE_ID"),并且结合crc32或其他哈希函数,它可以完美地运行。

现在为了消除对Apache及其模块的依赖,我只是使用以下代码:

$uniqueid = sprintf("%08x", abs(crc32($_SERVER['REMOTE_ADDR'] . $_SERVER['REQUEST_TIME'] . $_SERVER['REMOTE_PORT'])));

它足够唯一,以理解哪些日志属于哪些请求。如果需要更高精度,您可以使用其他哈希函数。

希望这能有所帮助。


1
提醒一下,如果同时来了两个请求,这个方法会产生两个相同的ID。 - Rangi Lin
3
在这种情况下,REMOTE_ADDR和REQUEST_TIME将是相似的,但REMOTE_PORT将不同。在保持连接请求的情况下,REMOTE_PORT可能与先前的请求相似,但是此时REQUEST_TIME不会相同。 - gilm
2
REQUEST_TIME_FLOAT 自 PHP 5.4 起可用,精确到微秒。 - Ian Dunn
3
我认为sprintf( "%08x", abs( crc32( $values ) ) )hash( 'crc32b', $values )更易读,并且实现的结果相同。 - Ian Dunn
对于问题的目的来说肯定是足够的——区分来自不同线程的日志条目。然而,我在 PHP 日志中收到了通知——从 Windows 任务计划程序调用 PHP CLI 时出现“未定义索引:REMOTE_ADDR”和“REMOTE_PORT”的相同情况。 - youcantryreachingme
@youcantryreachingme 这是真的。如果您没有通过像Apache这样的Web服务器传递,那么REMOTE_*就不会被定义。在这种情况下,我只需取进程ID和当前时间并将它们哈希在一起。 - gilm

7

zend_thread_id():

int zend_thread_id ( void ) 

This function returns a unique identifier for the current thread.

尽管如此:

该函数仅在PHP使用ZTS (Zend线程安全)支持和调试模式 (--enable-debug)构建时可用。


您还可以尝试调用 mysql_thread_id(),当您使用该API进行数据库访问时(或者使用mysqli时的mysqli::$thread_id)。


非常感谢您的回复,但是很遗憾我无法使用 zend_thread_id() 函数。虽然我在 Linux 上运行,但是 exec('gettid')mysql_thread_id() 都没有返回任何结果。也许这是因为我在共享环境中运行的原因。请问还有其他选择吗? - Jonas Sourlier
gettid不是只能从C源代码中调用,而不能从终端调用吗? - Lukasz Czerwinski

1
PHP似乎没有此功能可用的函数,但您的Web服务器可能能够通过环境变量传递标识符。例如,有一个名为“mod_unique_id”的Apache模块[1],它为每个请求生成唯一标识符并将其存储为环境变量。如果该变量存在,则应通过$ _SERVER ['unique_id'] [2]可见。
“纯PHP”解决方案是编写一个脚本来生成合适的随机标识符,通过define(“unique_id”,val)存储它,然后使用php.ini中的auto_prepend_file [3]选项在执行每个脚本时包含它。这样,当请求开始处理时,将创建唯一ID,并且在请求处理期间将其可用。

1

我见过getmypid()被用于这个目的,但它在不同的系统上表现不同。在某些情况下,ID对于每个请求是唯一的,但在其他情况下是共享的。

因此,为了确保可移植性,您最好选择其他答案中的一个。


2
getmypid返回正在运行的PHP实例的进程ID。由于在某些系统(如CGI)中,PHP保持运行并处理请求,它始终返回相同的ID。 - AaA

0

为了识别来自请求的日志数据,分配一个ID可能就像创建一个UUID版本4(随机)并将其写入日志的每一行一样简单。

甚至有软件可以帮助实现这一点:ramsey/uuid, php-middleware/request-id

使用log4php时,通过将UUID放置到LoggerMDC数据中并使用适当的LogFormatter,在每个日志记录的行中添加它非常容易。对于PSR-3记录器,可能会更加复杂,具体情况因人而异。

随机生成的UUID将适用于标识单个请求,并通过在子请求和响应的HTTP头中使用该UUID,甚至可以跨多个系统和平台在服务器群中跟踪一个请求。但是,将其作为标题放置不是我提到的任何包的任务。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接