Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
C
cpython
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
Batuhan Osman TASKAYA
cpython
Commits
23b94d0b
Kaydet (Commit)
23b94d0b
authored
Ock 04, 2012
tarafından
Vinay Sajip
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Refactored logging rotating handlers for improved flexibility.
üst
239a0429
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
215 additions
and
17 deletions
+215
-17
logging-cookbook.rst
Doc/howto/logging-cookbook.rst
+28
-0
logging.handlers.rst
Doc/library/logging.handlers.rst
+81
-0
handlers.py
Lib/logging/handlers.py
+56
-14
test_logging.py
Lib/test/test_logging.py
+50
-3
No files found.
Doc/howto/logging-cookbook.rst
Dosyayı görüntüle @
23b94d0b
...
@@ -1102,3 +1102,31 @@ This dictionary is passed to :func:`~logging.config.dictConfig` to put the confi
...
@@ -1102,3 +1102,31 @@ This dictionary is passed to :func:`~logging.config.dictConfig` to put the confi
For more information about this configuration, you can see the `relevant
For more information about this configuration, you can see the `relevant
section <https://docs.djangoproject.com/en/1.3/topics/logging/#configuring-logging>`_
section <https://docs.djangoproject.com/en/1.3/topics/logging/#configuring-logging>`_
of the Django documentation.
of the Django documentation.
.. _cookbook-rotator-namer:
Using a rotator and namer to customise log rotation processing
--------------------------------------------------------------
An example of how you can define a namer and rotator is given in the following
snippet, which shows zlib-based compression of the log file::
def namer(name):
return name + ".gz"
def rotator(source, dest):
with open(source, "rb") as sf:
data = sf.read()
compressed = zlib.compress(data, 9)
with open(dest, "wb") as df:
df.write(compressed)
os.remove(source)
rh = logging.handlers.RotatingFileHandler(...)
rh.rotator = rotator
rh.namer = namer
These are not “true” .gz files, as they are bare compressed data, with no
“container” such as you’d find in an actual gzip file. This snippet is just
for illustration purposes.
Doc/library/logging.handlers.rst
Dosyayı görüntüle @
23b94d0b
...
@@ -164,6 +164,87 @@ this value.
...
@@ -164,6 +164,87 @@ this value.
changed. If it has, the existing stream is flushed and closed and the
changed. If it has, the existing stream is flushed and closed and the
file opened again, before outputting the record to the file.
file opened again, before outputting the record to the file.
.. _base-rotating-handler:
BaseRotatingHandler
^^^^^^^^^^^^^^^^^^^
The :class:`BaseRotatingHandler` class, located in the :mod:`logging.handlers`
module, is the base class for the rotating file handlers,
:class:`RotatingFileHandler` and :class:`TimedRotatingFileHandler`. You should
not need to instantiate this class, but it has attributes and methods you may
need to override.
.. class:: BaseRotatingHandler(filename, mode, encoding=None, delay=False)
The parameters are as for :class:`FileHandler`. The attributes are:
.. attribute:: namer
If this attribute is set to a callable, the :meth:`rotation_filename`
method delegates to this callable. The parameters passed to the callable
are those passed to :meth:`rotation_filename`.
.. note:: The namer function is called quite a few times during rollover,
so it should be as simple and as fast as possible. It should also
return the same output every time for a given input, otherwise the
rollover behaviour may not work as expected.
.. versionadded:: 3.3
.. attribute:: BaseRotatingHandler.rotator
If this attribute is set to a callable, the :meth:`rotate` method
delegates to this callable. The parameters passed to the callable are
those passed to :meth:`rotate`.
.. versionadded:: 3.3
.. method:: BaseRotatingHandler.rotation_filename(default_name)
Modify the filename of a log file when rotating.
This is provided so that a custom filename can be provided.
The default implementation calls the 'namer' attribute of the handler,
if it's callable, passing the default name to it. If the attribute isn't
callable (the default is `None`), the name is returned unchanged.
:param default_name: The default name for the log file.
.. versionadded:: 3.3
.. method:: BaseRotatingHandler.rotate(source, dest)
When rotating, rotate the current log.
The default implementation calls the 'rotator' attribute of the handler,
if it's callable, passing the source and dest arguments to it. If the
attribute isn't callable (the default is `None`), the source is simply
renamed to the destination.
:param source: The source filename. This is normally the base
filename, e.g. 'test.log'
:param dest: The destination filename. This is normally
what the source is rotated to, e.g. 'test.log.1'.
.. versionadded:: 3.3
The reason the attributes exist is to save you having to subclass - you can use
the same callables for instances of :class:`RotatingFileHandler` and
:class:`TimedRotatingFileHandler`. If either the namer or rotator callable
raises an exception, this will be handled in the same way as any other
exception during an :meth:`emit` call, i.e. via the :meth:`handleError` method
of the handler.
If you need to make more significant changes to rotation processing, you can
override the methods.
For an example, see :ref:`cookbook-rotator-namer`.
.. _rotating-file-handler:
.. _rotating-file-handler:
RotatingFileHandler
RotatingFileHandler
...
...
Lib/logging/handlers.py
Dosyayı görüntüle @
23b94d0b
...
@@ -52,13 +52,15 @@ class BaseRotatingHandler(logging.FileHandler):
...
@@ -52,13 +52,15 @@ class BaseRotatingHandler(logging.FileHandler):
Not meant to be instantiated directly. Instead, use RotatingFileHandler
Not meant to be instantiated directly. Instead, use RotatingFileHandler
or TimedRotatingFileHandler.
or TimedRotatingFileHandler.
"""
"""
def
__init__
(
self
,
filename
,
mode
,
encoding
=
None
,
delay
=
0
):
def
__init__
(
self
,
filename
,
mode
,
encoding
=
None
,
delay
=
False
):
"""
"""
Use the specified filename for streamed logging
Use the specified filename for streamed logging
"""
"""
logging
.
FileHandler
.
__init__
(
self
,
filename
,
mode
,
encoding
,
delay
)
logging
.
FileHandler
.
__init__
(
self
,
filename
,
mode
,
encoding
,
delay
)
self
.
mode
=
mode
self
.
mode
=
mode
self
.
encoding
=
encoding
self
.
encoding
=
encoding
self
.
namer
=
None
self
.
rotator
=
None
def
emit
(
self
,
record
):
def
emit
(
self
,
record
):
"""
"""
...
@@ -76,12 +78,50 @@ class BaseRotatingHandler(logging.FileHandler):
...
@@ -76,12 +78,50 @@ class BaseRotatingHandler(logging.FileHandler):
except
:
except
:
self
.
handleError
(
record
)
self
.
handleError
(
record
)
def
rotation_filename
(
self
,
default_name
):
"""
Modify the filename of a log file when rotating.
This is provided so that a custom filename can be provided.
The default implementation calls the 'namer' attribute of the
handler, if it's callable, passing the default name to
it. If the attribute isn't callable (the default is None), the name
is returned unchanged.
:param default_name: The default name for the log file.
"""
if
not
callable
(
self
.
namer
):
result
=
default_name
else
:
result
=
self
.
namer
(
default_name
)
return
result
def
rotate
(
self
,
source
,
dest
):
"""
When rotating, rotate the current log.
The default implementation calls the 'rotator' attribute of the
handler, if it's callable, passing the source and dest arguments to
it. If the attribute isn't callable (the default is None), the source
is simply renamed to the destination.
:param source: The source filename. This is normally the base
filename, e.g. 'test.log'
:param dest: The destination filename. This is normally
what the source is rotated to, e.g. 'test.log.1'.
"""
if
not
callable
(
self
.
rotator
):
os
.
rename
(
source
,
dest
)
else
:
self
.
rotator
(
source
,
dest
)
class
RotatingFileHandler
(
BaseRotatingHandler
):
class
RotatingFileHandler
(
BaseRotatingHandler
):
"""
"""
Handler for logging to a set of files, which switches from one file
Handler for logging to a set of files, which switches from one file
to the next when the current file reaches a certain size.
to the next when the current file reaches a certain size.
"""
"""
def
__init__
(
self
,
filename
,
mode
=
'a'
,
maxBytes
=
0
,
backupCount
=
0
,
encoding
=
None
,
delay
=
0
):
def
__init__
(
self
,
filename
,
mode
=
'a'
,
maxBytes
=
0
,
backupCount
=
0
,
encoding
=
None
,
delay
=
False
):
"""
"""
Open the specified file and use it as the stream for logging.
Open the specified file and use it as the stream for logging.
...
@@ -122,16 +162,17 @@ class RotatingFileHandler(BaseRotatingHandler):
...
@@ -122,16 +162,17 @@ class RotatingFileHandler(BaseRotatingHandler):
self
.
stream
=
None
self
.
stream
=
None
if
self
.
backupCount
>
0
:
if
self
.
backupCount
>
0
:
for
i
in
range
(
self
.
backupCount
-
1
,
0
,
-
1
):
for
i
in
range
(
self
.
backupCount
-
1
,
0
,
-
1
):
sfn
=
"
%
s.
%
d"
%
(
self
.
baseFilename
,
i
)
sfn
=
self
.
rotation_filename
(
"
%
s.
%
d"
%
(
self
.
baseFilename
,
i
))
dfn
=
"
%
s.
%
d"
%
(
self
.
baseFilename
,
i
+
1
)
dfn
=
self
.
rotation_filename
(
"
%
s.
%
d"
%
(
self
.
baseFilename
,
i
+
1
))
if
os
.
path
.
exists
(
sfn
):
if
os
.
path
.
exists
(
sfn
):
if
os
.
path
.
exists
(
dfn
):
if
os
.
path
.
exists
(
dfn
):
os
.
remove
(
dfn
)
os
.
remove
(
dfn
)
os
.
rename
(
sfn
,
dfn
)
os
.
rename
(
sfn
,
dfn
)
dfn
=
self
.
baseFilename
+
".1"
dfn
=
self
.
rotation_filename
(
self
.
baseFilename
+
".1"
)
if
os
.
path
.
exists
(
dfn
):
if
os
.
path
.
exists
(
dfn
):
os
.
remove
(
dfn
)
os
.
remove
(
dfn
)
os
.
renam
e
(
self
.
baseFilename
,
dfn
)
self
.
rotat
e
(
self
.
baseFilename
,
dfn
)
self
.
mode
=
'w'
self
.
mode
=
'w'
self
.
stream
=
self
.
_open
()
self
.
stream
=
self
.
_open
()
...
@@ -179,19 +220,19 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
...
@@ -179,19 +220,19 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
if
self
.
when
==
'S'
:
if
self
.
when
==
'S'
:
self
.
interval
=
1
# one second
self
.
interval
=
1
# one second
self
.
suffix
=
"
%
Y-
%
m-
%
d_
%
H-
%
M-
%
S"
self
.
suffix
=
"
%
Y-
%
m-
%
d_
%
H-
%
M-
%
S"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}
(\.\w+)?
$"
elif
self
.
when
==
'M'
:
elif
self
.
when
==
'M'
:
self
.
interval
=
60
# one minute
self
.
interval
=
60
# one minute
self
.
suffix
=
"
%
Y-
%
m-
%
d_
%
H-
%
M"
self
.
suffix
=
"
%
Y-
%
m-
%
d_
%
H-
%
M"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}$"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}
(\.\w+)?
$"
elif
self
.
when
==
'H'
:
elif
self
.
when
==
'H'
:
self
.
interval
=
60
*
60
# one hour
self
.
interval
=
60
*
60
# one hour
self
.
suffix
=
"
%
Y-
%
m-
%
d_
%
H"
self
.
suffix
=
"
%
Y-
%
m-
%
d_
%
H"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}_\d{2}$"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}_\d{2}
(\.\w+)?
$"
elif
self
.
when
==
'D'
or
self
.
when
==
'MIDNIGHT'
:
elif
self
.
when
==
'D'
or
self
.
when
==
'MIDNIGHT'
:
self
.
interval
=
60
*
60
*
24
# one day
self
.
interval
=
60
*
60
*
24
# one day
self
.
suffix
=
"
%
Y-
%
m-
%
d"
self
.
suffix
=
"
%
Y-
%
m-
%
d"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}$"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}
(\.\w+)?
$"
elif
self
.
when
.
startswith
(
'W'
):
elif
self
.
when
.
startswith
(
'W'
):
self
.
interval
=
60
*
60
*
24
*
7
# one week
self
.
interval
=
60
*
60
*
24
*
7
# one week
if
len
(
self
.
when
)
!=
2
:
if
len
(
self
.
when
)
!=
2
:
...
@@ -200,7 +241,7 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
...
@@ -200,7 +241,7 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
raise
ValueError
(
"Invalid day specified for weekly rollover:
%
s"
%
self
.
when
)
raise
ValueError
(
"Invalid day specified for weekly rollover:
%
s"
%
self
.
when
)
self
.
dayOfWeek
=
int
(
self
.
when
[
1
])
self
.
dayOfWeek
=
int
(
self
.
when
[
1
])
self
.
suffix
=
"
%
Y-
%
m-
%
d"
self
.
suffix
=
"
%
Y-
%
m-
%
d"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}$"
self
.
extMatch
=
r"^\d{4}-\d{2}-\d{2}
(\.\w+)?
$"
else
:
else
:
raise
ValueError
(
"Invalid rollover interval specified:
%
s"
%
self
.
when
)
raise
ValueError
(
"Invalid rollover interval specified:
%
s"
%
self
.
when
)
...
@@ -323,10 +364,11 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
...
@@ -323,10 +364,11 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
timeTuple
=
time
.
gmtime
(
t
)
timeTuple
=
time
.
gmtime
(
t
)
else
:
else
:
timeTuple
=
time
.
localtime
(
t
)
timeTuple
=
time
.
localtime
(
t
)
dfn
=
self
.
baseFilename
+
"."
+
time
.
strftime
(
self
.
suffix
,
timeTuple
)
dfn
=
self
.
rotation_filename
(
self
.
baseFilename
+
"."
+
time
.
strftime
(
self
.
suffix
,
timeTuple
))
if
os
.
path
.
exists
(
dfn
):
if
os
.
path
.
exists
(
dfn
):
os
.
remove
(
dfn
)
os
.
remove
(
dfn
)
os
.
renam
e
(
self
.
baseFilename
,
dfn
)
self
.
rotat
e
(
self
.
baseFilename
,
dfn
)
if
self
.
backupCount
>
0
:
if
self
.
backupCount
>
0
:
for
s
in
self
.
getFilesToDelete
():
for
s
in
self
.
getFilesToDelete
():
os
.
remove
(
s
)
os
.
remove
(
s
)
...
@@ -367,7 +409,7 @@ class WatchedFileHandler(logging.FileHandler):
...
@@ -367,7 +409,7 @@ class WatchedFileHandler(logging.FileHandler):
This handler is based on a suggestion and patch by Chad J.
This handler is based on a suggestion and patch by Chad J.
Schroeder.
Schroeder.
"""
"""
def
__init__
(
self
,
filename
,
mode
=
'a'
,
encoding
=
None
,
delay
=
0
):
def
__init__
(
self
,
filename
,
mode
=
'a'
,
encoding
=
None
,
delay
=
False
):
logging
.
FileHandler
.
__init__
(
self
,
filename
,
mode
,
encoding
,
delay
)
logging
.
FileHandler
.
__init__
(
self
,
filename
,
mode
,
encoding
,
delay
)
if
not
os
.
path
.
exists
(
self
.
baseFilename
):
if
not
os
.
path
.
exists
(
self
.
baseFilename
):
self
.
dev
,
self
.
ino
=
-
1
,
-
1
self
.
dev
,
self
.
ino
=
-
1
,
-
1
...
...
Lib/test/test_logging.py
Dosyayı görüntüle @
23b94d0b
...
@@ -46,6 +46,7 @@ import time
...
@@ -46,6 +46,7 @@ import time
import
unittest
import
unittest
import
warnings
import
warnings
import
weakref
import
weakref
import
zlib
try
:
try
:
import
threading
import
threading
# The following imports are needed only for tests which
# The following imports are needed only for tests which
...
@@ -3587,15 +3588,61 @@ class RotatingFileHandlerTest(BaseFileTest):
...
@@ -3587,15 +3588,61 @@ class RotatingFileHandlerTest(BaseFileTest):
rh
.
close
()
rh
.
close
()
def
test_rollover_filenames
(
self
):
def
test_rollover_filenames
(
self
):
def
namer
(
name
):
return
name
+
".test"
rh
=
logging
.
handlers
.
RotatingFileHandler
(
rh
=
logging
.
handlers
.
RotatingFileHandler
(
self
.
fn
,
backupCount
=
2
,
maxBytes
=
1
)
self
.
fn
,
backupCount
=
2
,
maxBytes
=
1
)
rh
.
namer
=
namer
rh
.
emit
(
self
.
next_rec
())
rh
.
emit
(
self
.
next_rec
())
self
.
assertLogFile
(
self
.
fn
)
self
.
assertLogFile
(
self
.
fn
)
rh
.
emit
(
self
.
next_rec
())
rh
.
emit
(
self
.
next_rec
())
self
.
assertLogFile
(
self
.
fn
+
".1"
)
self
.
assertLogFile
(
namer
(
self
.
fn
+
".1"
)
)
rh
.
emit
(
self
.
next_rec
())
rh
.
emit
(
self
.
next_rec
())
self
.
assertLogFile
(
self
.
fn
+
".2"
)
self
.
assertLogFile
(
namer
(
self
.
fn
+
".2"
))
self
.
assertFalse
(
os
.
path
.
exists
(
self
.
fn
+
".3"
))
self
.
assertFalse
(
os
.
path
.
exists
(
namer
(
self
.
fn
+
".3"
)))
rh
.
close
()
def
test_rotator
(
self
):
def
namer
(
name
):
return
name
+
".gz"
def
rotator
(
source
,
dest
):
with
open
(
source
,
"rb"
)
as
sf
:
data
=
sf
.
read
()
compressed
=
zlib
.
compress
(
data
,
9
)
with
open
(
dest
,
"wb"
)
as
df
:
df
.
write
(
compressed
)
os
.
remove
(
source
)
rh
=
logging
.
handlers
.
RotatingFileHandler
(
self
.
fn
,
backupCount
=
2
,
maxBytes
=
1
)
rh
.
rotator
=
rotator
rh
.
namer
=
namer
m1
=
self
.
next_rec
()
rh
.
emit
(
m1
)
self
.
assertLogFile
(
self
.
fn
)
m2
=
self
.
next_rec
()
rh
.
emit
(
m2
)
fn
=
namer
(
self
.
fn
+
".1"
)
self
.
assertLogFile
(
fn
)
with
open
(
fn
,
"rb"
)
as
f
:
compressed
=
f
.
read
()
data
=
zlib
.
decompress
(
compressed
)
self
.
assertEqual
(
data
.
decode
(
"ascii"
),
m1
.
msg
+
"
\n
"
)
rh
.
emit
(
self
.
next_rec
())
fn
=
namer
(
self
.
fn
+
".2"
)
self
.
assertLogFile
(
fn
)
with
open
(
fn
,
"rb"
)
as
f
:
compressed
=
f
.
read
()
data
=
zlib
.
decompress
(
compressed
)
self
.
assertEqual
(
data
.
decode
(
"ascii"
),
m1
.
msg
+
"
\n
"
)
rh
.
emit
(
self
.
next_rec
())
fn
=
namer
(
self
.
fn
+
".2"
)
with
open
(
fn
,
"rb"
)
as
f
:
compressed
=
f
.
read
()
data
=
zlib
.
decompress
(
compressed
)
self
.
assertEqual
(
data
.
decode
(
"ascii"
),
m2
.
msg
+
"
\n
"
)
self
.
assertFalse
(
os
.
path
.
exists
(
namer
(
self
.
fn
+
".3"
)))
rh
.
close
()
rh
.
close
()
class
TimedRotatingFileHandlerTest
(
BaseFileTest
):
class
TimedRotatingFileHandlerTest
(
BaseFileTest
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment