简体   繁体   English

Python多处理日志记录错误-TypeError:必须为整数(获取类型为NoneType)

[英]Python Multiprocessing Logging Error - TypeError: an integer is required (got type NoneType)

I've got a python3 (3.4.5) project I've been working on that utilizes multiprocessing.Pool to run around 50+ jobs through 4 workers. 我有一个正在进行的python3(3.4.5)项目,该项目利用了multiprocessing.Pool通过4个工人可以运行大约50多个工作。 I have a separate process setup with a logging.handlers.QueueListener so I can log global stuff to a single file via a Queue used with multiprocessing.Manager() . 我有一个单独的进程设置,带有logging.handlers.QueueListener因此我可以通过与multiprocessing.Manager()一起使用的Queue将全局内容记录到单个文件中。 So basically the flow goes like this 所以基本上流程是这样的

  1. Main program Starts 主程序启动
  2. Create Queue via multiprocessing.Manager() 通过multiprocessing.Manager()创建Queue
  3. Start dedicated logging process with a QueueListener listening to the Queue I just created for the global log. 使用QueueListener开始专用的日志记录过程,以侦听我刚刚为全局日志创建的Queue (I've also tried this just using a thread off of the main program with the same results.) (我也尝试过仅使用主程序中的一个线程来获得相同的结果。)
  4. Create a multiprocessing.Pool to process the individual jobs, passing them the Queue created previously and the necessary config info to run and setup their logging (there's a global log, plus an individual log for each job with more granular info). 创建一个multiprocessing.Pool以处理各个作业,将先前创建的Queue以及运行和设置其日志的必要配置信息传递给它们(有一个全局日志,外加每个作业的单个日志以及更详细的信息)。 The jobs are started with map_async . 作业从map_async开始。
  5. Wait for all of the jobs to process, then do some final steps and clean-up. 等待所有作业处理,然后执行一些最后的步骤并进行清理。

I keep getting an intermittent error on some of the jobs though, usually there is an error on 1 of the jobs (a different one each time), occasionally there are 2 of the same errors or zero though. 我在某些作业上经常遇到间歇性错误,通常是其中1个作业有错误(每次都不同),偶尔也有2个相同的错误或为零。 As far as I can tell, it's not the code in the jobs that's causing the error, but something in either the multiprocessing or logging setup. 据我所知,导致错误的不是作业中的代码,而是multiprocessinglogging设置中的某些内容。 Here's an example of the error I'm getting: 这是我遇到的错误的示例:

--- Logging error ---
Traceback (most recent call last):
  File "/usr/lib64/python3.4/logging/handlers.py", line 1347, in emit
    self.enqueue(self.prepare(record))
  File "/usr/lib64/python3.4/logging/handlers.py", line 1313, in enqueue
    self.queue.put_nowait(record)
  File "<string>", line 2, in put_nowait
  File "/usr/lib64/python3.4/multiprocessing/managers.py", line 731, in _callmethod
    conn.send((self._id, methodname, args, kwds))
  File "/usr/lib64/python3.4/multiprocessing/connection.py", line 206, in send
    self._send_bytes(ForkingPickler.dumps(obj))
  File "/usr/lib64/python3.4/multiprocessing/connection.py", line 413, in _send_bytes
    self._send(chunk)
  File "/usr/lib64/python3.4/multiprocessing/connection.py", line 369, in _send
    n = write(self._handle, buf)
TypeError: an integer is required (got type NoneType)
Call stack:
  File "./sampling__test__py.py", line 100, in <module>
    run_pool     = multiprocessing.Pool(4)
  File "/usr/lib64/python3.4/multiprocessing/context.py", line 118, in Pool
    context=self.get_context())
  File "/usr/lib64/python3.4/multiprocessing/pool.py", line 168, in __init__
    self._repopulate_pool()
  File "/usr/lib64/python3.4/multiprocessing/pool.py", line 233, in _repopulate_pool
    w.start()
  File "/usr/lib64/python3.4/multiprocessing/process.py", line 105, in start
    self._popen = self._Popen(self)
  File "/usr/lib64/python3.4/multiprocessing/context.py", line 267, in _Popen
    return Popen(process_obj)
  File "/usr/lib64/python3.4/multiprocessing/popen_fork.py", line 21, in __init__
    self._launch(process_obj)
  File "/usr/lib64/python3.4/multiprocessing/popen_fork.py", line 77, in _launch
    code = process_obj._bootstrap()
  File "/usr/lib64/python3.4/multiprocessing/process.py", line 254, in _bootstrap
    self.run()
  File "/usr/lib64/python3.4/multiprocessing/process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/lib64/python3.4/multiprocessing/pool.py", line 119, in worker
    result = (True, func(*args, **kwds))
  File "/usr/lib64/python3.4/multiprocessing/pool.py", line 44, in mapstar
    return list(map(*args))
  File "/home/username/value_check.py", line 338, in value_check
    global_logger.info("SplitTime: {str_timeDelta} -- COMPLETED: {Check_Name} --- Total Txn Count: {var_Total_Txn_Count} --- Criteria Txn Count: {var_Criteria_Txn_Count} --- Threshold: {Threshold} --- Low_Vol Threshold: {LowVolThresh}".format(str_timeDelta = timeDelta(datetime.now() - YAML_Config['start_time']), **YAML_Config))
Message: 'SplitTime: 00:01:05,031 -- COMPLETED: ALPHA_CHECK --- Total Txn Count: 1234--- Criteria Txn Count: 0 --- Threshold: 10 --- Low_Vol Threshold: 0'
Arguments: None

The error in the code refers back to a logging object in my code, but even when I put try/except logic around the call it doesn't do anything, the error appears to be happening upstream. 代码中的错误指的是代码中的一个日志记录对象,但是即使我在调用周围放置了try/except逻辑,它也无能为力,但错误似乎发生在上游。 I've also tried changing what is being logged from a formatted string to just a simple string to no avail. 我还尝试将正在记录的内容从格式化的字符串更改为只是一个简单的字符串,但无济于事。 It seems like somewhere along the way the individual jobs are either losing their connection to the Queue or something in the Queue is failing and causing the problems. 这似乎是某处沿途的各个作业或者失去他们的连接Queue在什么Queue失败,造成的问题。

Any ideas? 有任何想法吗? I've been working to get a newer version of Python available which would be beneficial for a number of reasons (f-strings in particular), but I don't know if that'd resolve this issue and I'm running out of troubleshooting ideas. 我一直在努力获取较新版本的Python,由于多种原因(特别是f字符串),这将是有益的,但是我不知道这是否可以解决此问题,并且我已经用完了故障排除思路。

Even when I put try/except logic around the call it doesn't do anything. 即使我在调用中加入了try / except逻辑,它也无济于事。

That's likely because, if the logging package encounters an exception that has to do with logging itself, it will print the traceback, but not raise the exception itself. 这可能是因为,如果日志记录程序包遇到与日志记录本身有关的异常,它将打印回溯,但不会引发异常本身。 This is more fully explained in the docstring for logging.Handler.handleError . logging.Handler.handleError的文档字符串中对此进行了更全面的说明。

One place to start is by setting: 一个开始的地方是通过设置:

logging.raiseExceptions = True

If the module-level attribute raiseExceptions is False, exceptions get silently ignored. 如果模块级别的属性raiseExceptions为False,则异常将被忽略。

If that doesn't help, you could put an import pdb; pdb.set_trace() 如果那没有帮助,则可以import pdb; pdb.set_trace() import pdb; pdb.set_trace() call in the code for .emit() ; import pdb; pdb.set_trace()中的代码调用.emit() ; something like: 就像是:

def emit(self, record):
    try:
        msg = self.format(record)
        stream = self.stream
        stream.write(msg)
        stream.write(self.terminator)
        self.flush()
    except Exception as e:
        import pdb; pdb.set_trace()  # < ---
        self.handleError(record)

Where record will be a LogRecord instance. 其中record将是LogRecord实例。 Usually when I see a logging error pop up, it's because I've used the wrong number of args for the given format string, but inspect that record object should hopefully tell you more. 通常,当我看到弹出日志错误时,这是​​因为我为给定的格式字符串使用了错误数量的args,但是检查该record对象应该可以告诉您更多信息。

Lastly there is, from the call stack, your logging call itself: 最后,在调用堆栈中,有日志记录调用本身:

global_logger.info(
    "SplitTime: {str_timeDelta} -- "
    "COMPLETED: {Check_Name} --- "
    "Total Txn Count: {var_Total_Txn_Count} --- "
    "Criteria Txn Count: {var_Criteria_Txn_Count} --- "
    "Threshold: {Threshold} --- "
    "Low_Vol Threshold: {LowVolThresh}".format(
    str_timeDelta = timeDelta(datetime.now() - YAML_Config['start_time']), **YAML_Config))

Tough to say exactly what is ultimately raise the exception since the string does appear to be fully formatted. 很难说出最终导致异常的确切原因,因为该字符串似乎已被完全格式化。 (Though we can't see YAML_Config .) (尽管我们看不到YAML_Config 。)

Regardless of that, one recommendation: you can take advantage of using logging's "lazy" string formatting rather than str.format() as you currently have. 不管怎么说,一个建议:您可以利用日志记录的“惰性”字符串格式,而不是像当前那样使用str.format() The str.format() call will get evaluated as soon as it can, whereas if you pass kwargs to global_logger.info() , the logging package will wait to evaluate them until it must. str.format()调用将尽快得到评估,而如果将kwargs传递给global_logger.info() ,则日志记录包将等待对其进行评估,直到必须进行评估。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM