{"id":14066513,"url":"https://github.com/s-fleck/rotor","last_synced_at":"2025-07-26T14:31:22.418Z","repository":{"id":56935388,"uuid":"178069508","full_name":"s-fleck/rotor","owner":"s-fleck","description":"Rotate logfiles (and other files) from R","archived":false,"fork":false,"pushed_at":"2022-10-17T08:13:57.000Z","size":917,"stargazers_count":12,"open_issues_count":6,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-01T12:47:17.790Z","etag":null,"topics":["backup","logging","logrotate","logrotation","r"],"latest_commit_sha":null,"homepage":"https://s-fleck.github.io/rotor/reference/index.html","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/s-fleck.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-27T20:25:33.000Z","updated_at":"2024-02-20T14:14:33.000Z","dependencies_parsed_at":"2022-08-21T06:50:44.010Z","dependency_job_id":null,"html_url":"https://github.com/s-fleck/rotor","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s-fleck%2Frotor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s-fleck%2Frotor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s-fleck%2Frotor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s-fleck%2Frotor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/s-fleck","download_url":"https://codeload.github.com/s-fleck/rotor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227688613,"owners_count":17804590,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backup","logging","logrotate","logrotation","r"],"created_at":"2024-08-13T07:05:08.437Z","updated_at":"2024-12-02T07:27:53.435Z","avatar_url":"https://github.com/s-fleck.png","language":"R","funding_links":[],"categories":["R"],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n```\n# rotor\n\n\u003c!-- badges: start --\u003e\n[![Lifecycle: maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://lifecycle.r-lib.org/articles/stages.html#maturing)\n[![CRAN status](https://www.r-pkg.org/badges/version/rotor)](https://cran.r-project.org/package=rotor)\n\u003c!-- badges: end --\u003e\n\n**rotor** provides a cross platform R reimagination of\n[logrotate](https://linux.die.net/man/8/logrotate). It is a companion package to\nthe logging package [lgr](https://github.com/s-fleck/lgr). In\ncontrast to logrotate, rotor relies solely on information encoded in a suffixes\nof file names for conditionally creating backups (i.e. a timestamp or index). It\ntherefore also works with backups created by other tools, as long as the\nfilename has a format that rotor can understand.\n\n`rotate()`, `rotate_date()`, and `rotate_time()` move a file and insert a suffix\n(either an integer or a timestamp) into the filename. In addition, they create\nan empty file in place of the original one. This is useful for log rotation.\n`backup()`, `backup_date()` and `backup_time()` do the same but keep the\noriginal file.\n\nrotor also includes utility functions for finding and examining the backups of a \nfile: `list_backups()`, `backup_info()`, `n_backups`, `newest_backup()`, \n`oldest_backup()`. See the \n[function reference](https://s-fleck.github.io/rotor/reference/index.html) for \ndetails.\n\n## Installation\n\nYou can install the released version of rotor from [CRAN](https://CRAN.R-project.org) with:\n\n``` r\ninstall.packages(\"rotor\")\n```\n\nAnd the development version from [GitHub](https://github.com/) with:\n\n``` r\n# install.packages(\"remotes\")\nremotes::install_github(\"s-fleck/rotor\")\n```\n## Example\n\nFirst we create a temporary directory for the files created by the code examples\n```{r setup, results = \"hide\"}\nlibrary(rotor)\n\n# create a directory\ntd \u003c- file.path(tempdir(), \"rotor\")\ndir.create(td, recursive = TRUE)\n\n# create an example logfile\ntf \u003c- file.path(td, \"mylogfile.log\")\nwriteLines(\"An important message\", tf)\n```\n\n### Indexed backups\n\n`backup()` makes a copy of a file and inserts an index between the filename\nand the file extension. The file with the index `1` is always the most recently\nmade backup.\n```{r backup index, collapse=TRUE}\nbackup(tf)\n\n# backup and rotate also support compression\nbackup(tf, compression = TRUE) \n\n# display backups of a file\nlist_backups(tf)  \n```\n\n`rotate()` also backs up a file, but replaces the original file with an empty \none.\n\n```{r rotate index, collapse=TRUE}\nrotate(tf)\nlist_backups(tf)\n\n# the original file is now empty\nreadLines(tf)\n\n# its content was moved to the first backup\nreadLines(list_backups(tf)[[1]])\n\n# we can now safely write to the original file\nwriteLines(\"another important message\", tf)\n```\n\nThe `max_backups` parameter limits the maximum number of backups rotor will\nkeep of a file. Notice how the zipped backup we created above moves to index 4 \nas we create two new backups.\n```{r}\nbackup(tf, max_backups = 4)\nbackup(tf, max_backups = 4)\n\nlist_backups(tf)\n```\n\nWe can also use `prune_backups()` to delete old backups. Other than ensuring\nthat no new backups is created, it works identically to using `backup()` with\nthe `max_backups` parameter. By setting it to `0`, we delete all backups.\n```{r}\nprune_backups(tf, max_backups = 0)\n```\n\n## Timestamped backups\n\n**rotor** can also create timestamped backups. `backup_date()` creates uses a \nDate (`yyyy-mm-dd`) timestamp, `backup_time()` uses a full datetime-stamp by \ndefault (`yyyy-mm-dd--hh-mm-ss`). The format of the timestamp can be modified\nwith a subset of the formatting tokens understood by `strftime()` (within \ncertain restrictions). Backups created with both functions are compatible with \neach other (but not with those created with `backup_index()`).\n\n```{r timestamp}\n# be default backup_date() only makes a backup if the last backups is younger\n# than 1 day, so we set `age` to -1 for this example\nbackup_date(tf, age = -1)  \nbackup_date(tf, format = \"%Y-%m\", age = -1)\nbackup_time(tf)\nbackup_time(tf, format = \"%Y-%m-%d_%H-%M-%S\")  # Python logging\nbackup_time(tf, format = \"%Y%m%dT%H%M%S\")  # ISO 8601 compatible\n\nbackup_info(tf)\n```\nIf we examine the \"timestamp\" column in the example above, we see that missing \ndate information is always interpreted as the start of the period; i.e. so \n`\"2019-01\"` is equivalent to `\"2019-01-01--00--00--00\"` for all intents and \npurposes.\n\n```{r}\nprune_backups(tf, max_backups = 0)  # cleanup\nlist_backups(tf)\n```\n\nBesides passing a total number of backups to keep, `max_backups` can also be \na period or a date / datetime for timestamped backups.\n```{r eval = FALSE}\n# keep all backups younger than one year\nprune_backups(tf, \"1 year\") \n  \n# keep all backups from April 4th, 2018 and onwards\nprune_backups(tf, \"2018-04-01\")  \n```\n\n```{r cleanup, include = FALSE}\nunlink(td, recursive = TRUE)\n```\n\n\n## Cache\n\nrotor also provides a simple on-disk key-value store that can be pruned by size,\nage or number of files.\n \n```{r}\ncache \u003c- Cache$new(file.path(tempdir(), \"cache-test\"), hashfun = digest::digest)\nkey1 \u003c- cache$push(iris)\nkey2 \u003c- cache$push(cars)\nkey3 \u003c- cache$push(mtcars)\n\ncache$files$path\n\nhead(cache$read(key1))\n\ncache$prune(max_files = 1)\ncache$files$path\ncache$purge()  # deletes all cached files\ncache$destroy()  # deletes the cache directory\n```\n\n\n\n# Dependencies\n\n**rotor**'s dependencies are intentionally kept slim. It only comes with two\nnon-base dependencies:\n\n* [R6](https://github.com/r-lib/R6): A light weight system for \n  encapsulated object-oriented programming.\n* [dint](https://github.com/s-fleck/dint): A toolkit for working year-quarter \n  and year-month dates that I am also the author of. It is used by \n  `rotate_date()` and `rotate_time()` to deal with calendar periods (such as\n  weeks or months).\n  \nBoth packages have no transitive dependencies (i.e they do not depend on \nanything outside of base R)\n\n\nOptional dependencies:\n\n* [digest](https://github.com/eddelbuettel/digest),  \n  [ulid](https://cran.r-project.org/package=ulid), or\n  [uuid](https://CRAN.R-project.org/package=uuid ) for generating\n  hashes or UIDs when using `Cache`. Storage keys for cache files can also be set \n  manually, in which case no external dependencies are required.\n* [zip](https://CRAN.R-project.org/package=zip) is supported as an alternative \n  to the integrated zip function in R. Might work better on some systems and\n  worse on others.\n* [crayon](https://cran.r-project.org/package=crayon) for \n  terminal colors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs-fleck%2Frotor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fs-fleck%2Frotor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs-fleck%2Frotor/lists"}