-
Notifications
You must be signed in to change notification settings - Fork 2
/
README.Rmd
215 lines (165 loc) · 6.61 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
---
output: github_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
```
# rotor
<!-- badges: start -->
[![Lifecycle: maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://lifecycle.r-lib.org/articles/stages.html#maturing)
[![CRAN status](https://www.r-pkg.org/badges/version/rotor)](https://cran.r-project.org/package=rotor)
<!-- badges: end -->
**rotor** provides a cross platform R reimagination of
[logrotate](https://linux.die.net/man/8/logrotate). It is a companion package to
the logging package [lgr](https://github.com/s-fleck/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](https://s-fleck.github.io/rotor/reference/index.html) for
details.
## Installation
You can install the released version of rotor from [CRAN](https://CRAN.R-project.org) with:
``` r
install.packages("rotor")
```
And the development version from [GitHub](https://github.com/) with:
``` r
# install.packages("remotes")
remotes::install_github("s-fleck/rotor")
```
## Example
First we create a temporary directory for the files created by the code examples
```{r setup, results = "hide"}
library(rotor)
# create a directory
td <- file.path(tempdir(), "rotor")
dir.create(td, recursive = TRUE)
# create an example logfile
tf <- file.path(td, "mylogfile.log")
writeLines("An important message", tf)
```
### Indexed backups
`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.
```{r backup index, collapse=TRUE}
backup(tf)
# backup and rotate also support compression
backup(tf, compression = TRUE)
# display backups of a file
list_backups(tf)
```
`rotate()` also backs up a file, but replaces the original file with an empty
one.
```{r rotate index, collapse=TRUE}
rotate(tf)
list_backups(tf)
# the original file is now empty
readLines(tf)
# its content was moved to the first backup
readLines(list_backups(tf)[[1]])
# 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.
```{r}
backup(tf, max_backups = 4)
backup(tf, max_backups = 4)
list_backups(tf)
```
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.
```{r}
prune_backups(tf, max_backups = 0)
```
## Timestamped backups
**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()`).
```{r timestamp}
# 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)
```
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.
```{r}
prune_backups(tf, max_backups = 0) # cleanup
list_backups(tf)
```
Besides passing a total number of backups to keep, `max_backups` can also be
a period or a date / datetime for timestamped backups.
```{r eval = FALSE}
# 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")
```
```{r cleanup, include = FALSE}
unlink(td, recursive = TRUE)
```
## Cache
rotor also provides a simple on-disk key-value store that can be pruned by size,
age or number of files.
```{r}
cache <- Cache$new(file.path(tempdir(), "cache-test"), hashfun = digest::digest)
key1 <- cache$push(iris)
key2 <- cache$push(cars)
key3 <- cache$push(mtcars)
cache$files$path
head(cache$read(key1))
cache$prune(max_files = 1)
cache$files$path
cache$purge() # deletes all cached files
cache$destroy() # deletes the cache directory
```
# Dependencies
**rotor**'s dependencies are intentionally kept slim. It only comes with two
non-base dependencies:
* [R6](https://github.com/r-lib/R6): A light weight system for
encapsulated object-oriented programming.
* [dint](https://github.com/s-fleck/dint): A toolkit for working year-quarter
and year-month dates that I am also the author of. It is used by
`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:
* [digest](https://github.com/eddelbuettel/digest),
[ulid](https://cran.r-project.org/package=ulid), or
[uuid](https://CRAN.R-project.org/package=uuid ) for generating
hashes or UIDs when using `Cache`. Storage keys for cache files can also be set
manually, in which case no external dependencies are required.
* [zip](https://CRAN.R-project.org/package=zip) is supported as an alternative
to the integrated zip function in R. Might work better on some systems and
worse on others.
* [crayon](https://cran.r-project.org/package=crayon) for
terminal colors