多个处理器记录到同一个旋转文件
multiple processors logging to same rotate file
我的 nginx+uwsgi+django 站点出现问题。
我知道django+uwsgi没什么特别的,应该是日志模块本身的东西。
在我的站点中,我使用 RotatingFileHandler 来记录特殊条目,但是,当 uwsgi 运行 多个工作处理器时,今天我发现,
多个日志文件同时更改。例如,这里是文件片段:
[root@speed logs]# ls -lth
total 18M
-rw-rw-rw- 1 root root 2.1M Sep 14 19:44 backend.log.7
-rw-rw-rw- 1 root root 1.3M Sep 14 19:43 backend.log.6
-rw-rw-rw- 1 root root 738K Sep 14 19:43 backend.log.3
-rw-rw-rw- 1 root root 554K Sep 14 19:43 backend.log.1
-rw-rw-rw- 1 root root 1013K Sep 14 19:42 backend.log.4
-rw-rw-rw- 1 root root 837K Sep 14 19:41 backend.log.5
-rw-rw-rw- 1 root root 650K Sep 14 19:40 backend.log.2
-rw-rw-rw- 1 root root 656K Sep 14 19:40 backend.log
-rw-r--r-- 1 root root 10M Sep 13 10:11 backend.log.8
-rw-r--r-- 1 root root 0 Aug 21 15:53 general.log
[root@speed-app logs]#
实际上,我将旋转文件设置为每个文件 10M,最多 10 个文件。
我google了很多,之前很多人都打过这个,好像logging模块本身不支持这个。
我发现有人提到了 ConcurrentLogHandler(https://pypi.python.org/pypi/ConcurrentLogHandler/0.9.1)。
以前有人用过这个人吗?看到是基于文件锁的,不知道这哥们的表现好不好
或者谁有更好的主意将多个 uwsig 实例记录到同一个旋转文件?
谢谢。
卫斯理
只是为了解决这个问题,这里是一个完整的解决方案示例,它使用 python StreamHandler、uWSGI "daemonized file logging" 和 logrotate
守护进程以轮换方式记录到文件。
正如您将看到的,uWSGI 日志记录从您的应用程序捕获 stdout/stderr 并将其重定向到 stdout/stderr(默认情况下)或定义的其他 logger/handlers。
设置 Django/uWSGI
你的 Django settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
'format': '%(asctime)s - %(process)s - %(levelname)s - %(name)s : %(message)s',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'DEBUG',
},
}
代码中的某处
log = logging.getLogger(__name__)
log.info("test log!")
运行 带有一些日志参数的 uWSGI
$ uwsgi --http :9090 --chdir=`pwd -P` --wsgi-file=wsgi.py \
--daemonize=test.log \ # daemonize AND set log file
--log-maxsize=10000 \ # a 10k file rotate
--workers=4 # start 4 workers
输出
摘录test.log
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 79755)
spawned uWSGI worker 1 (pid: 79813, cores: 1)
spawned uWSGI worker 2 (pid: 79814, cores: 1)
spawned uWSGI worker 3 (pid: 79815, cores: 1)
spawned uWSGI worker 4 (pid: 79816, cores: 1)
spawned uWSGI http 1 (pid: 79817)
2015-10-12 07:55:48,458 - 79816 - INFO - testapp.views : test log!
2015-10-12 07:55:51,440 - 79813 - INFO - testapp.views : test log!
2015-10-12 07:55:51,965 - 79814 - INFO - testapp.views : test log!
2015-10-12 07:55:52,810 - 79815 - INFO - testapp.views : test log!
在同一个目录,过了一会儿:
-rw-r----- 1 big staff 1.0K Oct 12 09:56 test.log
-rw-r----- 1 big staff 11K Oct 12 09:55 test.log.1444636554
对数旋转
或者,要自己处理文件旋转,省略 --log-maxsize
参数并使用 logrotate
配置文件 (/etc/logrotate.d/uwsgi-test-app
):
/home/demo/test_django/*log {
rotate 10
size 10k
daily
compress
delaycompress
}
请注意,以上值仅供参考,您可能不希望旋转大小为 10k。有关 logrotate 格式的更多信息,请参阅 an example blog post。
如果您使用python的logrotation(当多个gunicorn进程指向同一个日志文件时)那么您应该确保主日志文件在轮换期间仅被编辑而未被重命名、移动等。为此,您复制主日志文件,然后将其清除!
翻转方法的片段(在logging.handlers.RotatingFileHandler的代码中编辑)
def doRollover(self):
self.stream.close()
if self.backupCount > 0:
for i in range(self.backupCount - 1, 0, -1):
sfn = "%s.%d" % (self.baseFilename, i)
dfn = "%s.%d" % (self.baseFilename, i + 1)
if os.path.exists(sfn):
if os.path.exists(dfn):
os.remove(dfn)
os.rename(sfn, dfn)
dfn = self.baseFilename + ".1"
if os.path.exists(dfn):
os.remove(dfn)
# os.rename(self.baseFilename, dfn) # Intead of this
# Do this
shutil.copyfile(self.baseFilename, dfn)
open(self.baseFilename, 'w').close()
if self.encoding:
self.stream = codecs.open(self.baseFilename, "w", self.encoding)
else:
self.stream = open(self.baseFilename, "w")
然后你可以像这样创建你的记录器:
logger = logging.getLogger(logfile_name)
logfile = '{}/{}.log'.format(logfile_folder, logfile_name)
handler = RotatingFileHandler(
logfile, maxBytes=maxBytes, backupCount=10
)
formatter = logging.Formatter(format, "%Y-%m-%d_%H:%M:%S")
formatter.converter = time.gmtime
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.isEnabledFor = lambda level: True
logger.propagate = 0
logger.warning("This is a log")
我的 nginx+uwsgi+django 站点出现问题。 我知道django+uwsgi没什么特别的,应该是日志模块本身的东西。
在我的站点中,我使用 RotatingFileHandler 来记录特殊条目,但是,当 uwsgi 运行 多个工作处理器时,今天我发现, 多个日志文件同时更改。例如,这里是文件片段:
[root@speed logs]# ls -lth
total 18M
-rw-rw-rw- 1 root root 2.1M Sep 14 19:44 backend.log.7
-rw-rw-rw- 1 root root 1.3M Sep 14 19:43 backend.log.6
-rw-rw-rw- 1 root root 738K Sep 14 19:43 backend.log.3
-rw-rw-rw- 1 root root 554K Sep 14 19:43 backend.log.1
-rw-rw-rw- 1 root root 1013K Sep 14 19:42 backend.log.4
-rw-rw-rw- 1 root root 837K Sep 14 19:41 backend.log.5
-rw-rw-rw- 1 root root 650K Sep 14 19:40 backend.log.2
-rw-rw-rw- 1 root root 656K Sep 14 19:40 backend.log
-rw-r--r-- 1 root root 10M Sep 13 10:11 backend.log.8
-rw-r--r-- 1 root root 0 Aug 21 15:53 general.log
[root@speed-app logs]#
实际上,我将旋转文件设置为每个文件 10M,最多 10 个文件。
我google了很多,之前很多人都打过这个,好像logging模块本身不支持这个。
我发现有人提到了 ConcurrentLogHandler(https://pypi.python.org/pypi/ConcurrentLogHandler/0.9.1)。 以前有人用过这个人吗?看到是基于文件锁的,不知道这哥们的表现好不好
或者谁有更好的主意将多个 uwsig 实例记录到同一个旋转文件?
谢谢。 卫斯理
只是为了解决这个问题,这里是一个完整的解决方案示例,它使用 python StreamHandler、uWSGI "daemonized file logging" 和 logrotate
守护进程以轮换方式记录到文件。
正如您将看到的,uWSGI 日志记录从您的应用程序捕获 stdout/stderr 并将其重定向到 stdout/stderr(默认情况下)或定义的其他 logger/handlers。
设置 Django/uWSGI
你的 Django settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
'format': '%(asctime)s - %(process)s - %(levelname)s - %(name)s : %(message)s',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'DEBUG',
},
}
代码中的某处
log = logging.getLogger(__name__)
log.info("test log!")
运行 带有一些日志参数的 uWSGI
$ uwsgi --http :9090 --chdir=`pwd -P` --wsgi-file=wsgi.py \
--daemonize=test.log \ # daemonize AND set log file
--log-maxsize=10000 \ # a 10k file rotate
--workers=4 # start 4 workers
输出
摘录test.log
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 79755)
spawned uWSGI worker 1 (pid: 79813, cores: 1)
spawned uWSGI worker 2 (pid: 79814, cores: 1)
spawned uWSGI worker 3 (pid: 79815, cores: 1)
spawned uWSGI worker 4 (pid: 79816, cores: 1)
spawned uWSGI http 1 (pid: 79817)
2015-10-12 07:55:48,458 - 79816 - INFO - testapp.views : test log!
2015-10-12 07:55:51,440 - 79813 - INFO - testapp.views : test log!
2015-10-12 07:55:51,965 - 79814 - INFO - testapp.views : test log!
2015-10-12 07:55:52,810 - 79815 - INFO - testapp.views : test log!
在同一个目录,过了一会儿:
-rw-r----- 1 big staff 1.0K Oct 12 09:56 test.log
-rw-r----- 1 big staff 11K Oct 12 09:55 test.log.1444636554
对数旋转
或者,要自己处理文件旋转,省略 --log-maxsize
参数并使用 logrotate
配置文件 (/etc/logrotate.d/uwsgi-test-app
):
/home/demo/test_django/*log {
rotate 10
size 10k
daily
compress
delaycompress
}
请注意,以上值仅供参考,您可能不希望旋转大小为 10k。有关 logrotate 格式的更多信息,请参阅 an example blog post。
如果您使用python的logrotation(当多个gunicorn进程指向同一个日志文件时)那么您应该确保主日志文件在轮换期间仅被编辑而未被重命名、移动等。为此,您复制主日志文件,然后将其清除!
翻转方法的片段(在logging.handlers.RotatingFileHandler的代码中编辑)
def doRollover(self):
self.stream.close()
if self.backupCount > 0:
for i in range(self.backupCount - 1, 0, -1):
sfn = "%s.%d" % (self.baseFilename, i)
dfn = "%s.%d" % (self.baseFilename, i + 1)
if os.path.exists(sfn):
if os.path.exists(dfn):
os.remove(dfn)
os.rename(sfn, dfn)
dfn = self.baseFilename + ".1"
if os.path.exists(dfn):
os.remove(dfn)
# os.rename(self.baseFilename, dfn) # Intead of this
# Do this
shutil.copyfile(self.baseFilename, dfn)
open(self.baseFilename, 'w').close()
if self.encoding:
self.stream = codecs.open(self.baseFilename, "w", self.encoding)
else:
self.stream = open(self.baseFilename, "w")
然后你可以像这样创建你的记录器:
logger = logging.getLogger(logfile_name)
logfile = '{}/{}.log'.format(logfile_folder, logfile_name)
handler = RotatingFileHandler(
logfile, maxBytes=maxBytes, backupCount=10
)
formatter = logging.Formatter(format, "%Y-%m-%d_%H:%M:%S")
formatter.converter = time.gmtime
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.isEnabledFor = lambda level: True
logger.propagate = 0
logger.warning("This is a log")