The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
rotor provides a cross platform R reimagination of logrotate. It is a companion package to the logging package lgr. In contrast to logrotate, rotor relies solely on information encoded in a suffixes of file names for conditionally creating backups (i.e. a timestamp or index). It therefore also works with backups created by other tools, as long as the filename has a format that rotor can understand.
rotate()
, rotate_date()
, and
rotate_time()
move a file and insert a suffix (either an
integer or a timestamp) into the filename. In addition, they create an
empty file in place of the original one. This is useful for log
rotation. backup()
, backup_date()
and
backup_time()
do the same but keep the original file.
rotor also includes utility functions for finding and examining the
backups of a file: list_backups()
,
backup_info()
, n_backups
,
newest_backup()
, oldest_backup()
. See the function
reference for details.
You can install the released version of rotor from CRAN with:
install.packages("rotor")
And the development version from GitHub with:
# install.packages("remotes")
::install_github("s-fleck/rotor") remotes
First we create a temporary directory for the files created by the code examples
library(rotor)
# create a directory
<- file.path(tempdir(), "rotor")
td dir.create(td, recursive = TRUE)
# create an example logfile
<- file.path(td, "mylogfile.log")
tf writeLines("An important message", tf)
backup()
makes a copy of a file and inserts an index
between the filename and the file extension. The file with the index
1
is always the most recently made backup.
backup(tf)
# backup and rotate also support compression
backup(tf, compression = TRUE)
# display backups of a file
list_backups(tf)
#> [1] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.1.log.zip"
#> [2] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.2.log"
rotate()
also backs up a file, but replaces the original
file with an empty one.
rotate(tf)
list_backups(tf)
#> [1] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.1.log"
#> [2] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.2.log.zip"
#> [3] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.3.log"
# the original file is now empty
readLines(tf)
#> character(0)
# its content was moved to the first backup
readLines(list_backups(tf)[[1]])
#> [1] "An important message"
# we can now safely write to the original file
writeLines("another important message", tf)
The max_backups
parameter limits the maximum number of
backups rotor will keep of a file. Notice how the zipped backup we
created above moves to index 4 as we create two new backups.
backup(tf, max_backups = 4)
backup(tf, max_backups = 4)
list_backups(tf)
#> [1] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.1.log"
#> [2] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.2.log"
#> [3] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.3.log"
#> [4] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.4.log.zip"
We can also use prune_backups()
to delete old backups.
Other than ensuring that no new backups is created, it works identically
to using backup()
with the max_backups
parameter. By setting it to 0
, we delete all backups.
prune_backups(tf, max_backups = 0)
rotor can also create timestamped backups.
backup_date()
creates uses a Date (yyyy-mm-dd
)
timestamp, backup_time()
uses a full datetime-stamp by
default (yyyy-mm-dd--hh-mm-ss
). The format of the timestamp
can be modified with a subset of the formatting tokens understood by
strftime()
(within certain restrictions). Backups created
with both functions are compatible with each other (but not with those
created with backup_index()
).
# be default backup_date() only makes a backup if the last backups is younger
# than 1 day, so we set `age` to -1 for this example
backup_date(tf, age = -1)
backup_date(tf, format = "%Y-%m", age = -1)
backup_time(tf)
backup_time(tf, format = "%Y-%m-%d_%H-%M-%S") # Python logging
backup_time(tf, format = "%Y%m%dT%H%M%S") # ISO 8601 compatible
backup_info(tf)
#> path
#> 1 C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.2022-09-02--13-25-45.log
#> 3 C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.2022-09-02_13-25-45.log
#> 5 C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.20220902T132545.log
#> 2 C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.2022-09-02.log
#> 4 C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/rotor/mylogfile.2022-09.log
#> name sfx ext size isdir mode mtime
#> 1 mylogfile 2022-09-02--13-25-45 log 27 FALSE 666 2022-09-02 13:25:45
#> 3 mylogfile 2022-09-02_13-25-45 log 27 FALSE 666 2022-09-02 13:25:45
#> 5 mylogfile 20220902T132545 log 27 FALSE 666 2022-09-02 13:25:45
#> 2 mylogfile 2022-09-02 log 27 FALSE 666 2022-09-02 13:25:45
#> 4 mylogfile 2022-09 log 27 FALSE 666 2022-09-02 13:25:45
#> ctime atime exe timestamp
#> 1 2022-09-02 13:25:45 2022-09-02 13:25:45 no 2022-09-02 13:25:45
#> 3 2022-09-02 13:25:45 2022-09-02 13:25:45 no 2022-09-02 13:25:45
#> 5 2022-09-02 13:25:45 2022-09-02 13:25:45 no 2022-09-02 13:25:45
#> 2 2022-09-02 13:25:45 2022-09-02 13:25:45 no 2022-09-02 00:00:00
#> 4 2022-09-02 13:25:45 2022-09-02 13:25:45 no 2022-09-01 00:00:00
If we examine the “timestamp” column in the example above, we see
that missing date information is always interpreted as the start of the
period; i.e. so "2019-01"
is equivalent to
"2019-01-01--00--00--00"
for all intents and purposes.
prune_backups(tf, max_backups = 0) # cleanup
list_backups(tf)
#> character(0)
Besides passing a total number of backups to keep,
max_backups
can also be a period or a date / datetime for
timestamped backups.
# keep all backups younger than one year
prune_backups(tf, "1 year")
# keep all backups from April 4th, 2018 and onwards
prune_backups(tf, "2018-04-01")
rotor also provides a simple on-disk key-value store that can be pruned by size, age or number of files.
<- Cache$new(file.path(tempdir(), "cache-test"), hashfun = digest::digest)
cache #> creating directory 'C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/cache-test'
<- cache$push(iris)
key1 <- cache$push(cars)
key2 <- cache$push(mtcars)
key3
$files$path
cache#> [1] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/cache-test/d3c5d071001b61a9f6131d3004fd0988"
#> [2] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/cache-test/f98a59010652c8e1ee062ed4c43f648e"
#> [3] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/cache-test/a63c70e73b58d0823ab3bcbd3b543d6f"
head(cache$read(key1))
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
$prune(max_files = 1)
cache$files$path
cache#> [1] "C:/Users/STEFAN~1.FLE/AppData/Local/Temp/RtmpYZSExE/cache-test/a63c70e73b58d0823ab3bcbd3b543d6f"
$purge() # deletes all cached files
cache$destroy() # deletes the cache directory cache
rotor’s dependencies are intentionally kept slim. It only comes with two non-base dependencies:
rotate_date()
and rotate_time()
to deal with calendar periods (such as weeks or months).Both packages have no transitive dependencies (i.e they do not depend on anything outside of base R)
Optional dependencies:
Cache
. Storage keys for cache
files can also be set manually, in which case no external dependencies
are required.These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.