{"id":15337254,"url":"https://github.com/vy/rotating-fos","last_synced_at":"2025-04-12T13:52:57.045Z","repository":{"id":28159180,"uuid":"116516517","full_name":"vy/rotating-fos","owner":"vy","description":"Java library providing rotating/rolling FileOutputStream.","archived":false,"fork":false,"pushed_at":"2024-05-02T04:51:08.000Z","size":664,"stargazers_count":34,"open_issues_count":4,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-05-02T06:09:24.151Z","etag":null,"topics":["fileoutputstream","java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"COPYING.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"vy"}},"created_at":"2018-01-06T21:22:58.000Z","updated_at":"2024-05-06T12:31:42.852Z","dependencies_parsed_at":"2023-12-04T05:27:17.453Z","dependency_job_id":"be915e43-cd2d-4f4b-a4ce-8fd6e0d884ae","html_url":"https://github.com/vy/rotating-fos","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vy%2Frotating-fos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vy%2Frotating-fos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vy%2Frotating-fos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vy%2Frotating-fos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vy","download_url":"https://codeload.github.com/vy/rotating-fos/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248576403,"owners_count":21127389,"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":["fileoutputstream","java"],"created_at":"2024-10-01T10:20:18.308Z","updated_at":"2025-04-12T13:52:57.011Z","avatar_url":"https://github.com/vy.png","language":"Java","funding_links":["https://github.com/sponsors/vy"],"categories":[],"sub_categories":[],"readme":"\u003c!---\n Copyright 2018-2024 Volkan Yazıcı \u003cvolkan@yazi.ci\u003e\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permits and\n limitations under the License.\n--\u003e\n\n[![Actions Status](https://github.com/vy/rotating-fos/workflows/build/badge.svg)](https://github.com/vy/rotating-fos/actions)\n[![Maven Central](https://img.shields.io/maven-central/v/com.vlkan.rfos/rotating-fos.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.vlkan.rfos%22)\n[![License](https://img.shields.io/github/license/vy/rotating-fos.svg)](https://www.apache.org/licenses/LICENSE-2.0.txt)\n\n`rotating-fos` is a Java 8 library providing `RotatingFileOutputStream` which\ninternally rotates a delegate `FileOutputStream` using provided rotation\npolicies similar to [logrotate](https://github.com/logrotate/logrotate),\n[Log4j](https://logging.apache.org/log4j/) and [Logback](https://logback.qos.ch/).\n\n# Usage\n\nYou first need to include `rotating-fos` in your Maven/Gradle dependencies:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.vlkan.rfos\u003c/groupId\u003e\n    \u003cartifactId\u003erotating-fos\u003c/artifactId\u003e\n    \u003cversion\u003e${rotating-fos.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n(Note that the Java 9 module name is `com.vlkan.rfos`.)\n\n`RotatingFileOutputStream` does not extend `java.io.FileOutputStream` (as a\ndeliberate design decision, see [How (not) to extend standard collection\nclasses](https://javachannel.org/posts/how-not-to-extend-standard-collection-classes/)\nand [`FileInputStream`/`FileOutputStream` considered\nharmful](https://www.cloudbees.com/blog/fileinputstream-fileoutputstream-considered-harmful)),\nbut `java.io.OutputStream`. Its basic usage is pretty straightforward:\n\n```java\nRotationConfig config = RotationConfig\n        .builder()\n        .file(\"/tmp/app.log\")\n        .filePattern(\"/tmp/app-%d{yyyyMMdd-HHmmss.SSS}.log\")\n        .policy(new SizeBasedRotationPolicy(1024 * 1024 * 100 /* 100MiB */))\n        .compress(true)\n        .policy(DailyRotationPolicy.getInstance())\n        .build();\n\ntry (RotatingFileOutputStream stream = new RotatingFileOutputStream(config)) {\n    stream.write(\"Hello, world!\".getBytes(StandardCharsets.UTF_8));\n}\n```\n\nUsing `maxBackupCount`, one can also introduce a rolling scheme where rotated\nfiles will be named as `file.0`, `file.1`, `file.2`, ..., `file.N` in the order\nfrom the newest to the oldest, `N` denoting the `maxBackupCount`:\n\n```java\nRotationConfig config = RotationConfig\n        .builder()\n        .file(\"/tmp/app.log\")\n        .maxBackupCount(10)         // Set `filePattern` to `file.%i` and keep\n                                    // the most recent 10 files.\n        .policy(new SizeBasedRotationPolicy(1024 * 1024 * 100 /* 100MiB */))\n        .build();\n\ntry (RotatingFileOutputStream stream = new RotatingFileOutputStream(config)) {\n    stream.write(\"Hello, world!\".getBytes(StandardCharsets.UTF_8));\n}\n```\n\n`RotationConfig.Builder` supports the following methods:\n\n| Method(s) | Description |\n| --------- | ----------- |\n| `file(File)`\u003cbr/\u003e`file(String)` | file accessed (e.g., `/tmp/app.log`) |\n| `filePattern(RotatingFilePattern)`\u003cbr/\u003e`filePattern(String)`| The pattern used to generate files for moving after rotation, e.g., `/tmp/app-%d{yyyyMMdd-HHmmss-SSS}.log`. This option cannot be combined with `maxBackupCount`. |\n| `policy(RotationPolicy)`\u003cbr/\u003e`policies(Set\u003cRotationPolicy\u003e policies)` | rotation policies |\n| `maxBackupCount(int)` | If greater than zero, rotated files will be named as `file.0`, `file.1`, `file.2`, ..., `file.N` in the order from the newest to the oldest, where `N` denoting the `maxBackupCount`. `maxBackupCount` defaults to `-1`, that is, no rolling. This option cannot be combined with `filePattern` or `compress`. |\n| `executorService(ScheduledExecutorService)` | scheduler for time-based policies and compression tasks |\n| `append(boolean)` | append while opening the `file` (defaults to `true`) |\n| `compress(boolean)` | Toggles GZIP compression after rotation and defaults to `false`. This option cannot be combined with `maxBackupCount`. |\n| `clock(Clock)` | clock for retrieving date and time (defaults to `SystemClock`) |\n| `callback(RotationCallback)`\u003cbr/\u003e`callbacks(Set\u003cRotationCallback\u003e)` | rotation callbacks (defaults to `LoggingRotationCallback`) |\n\nThe default `ScheduledExecutorService` can be retrieved via\n`RotationConfig#getDefaultExecutorService()`, which is a\n`ScheduledThreadPoolExecutor` of size `Runtime.getRuntime().availableProcessors()`.\nNote that unless explicitly specified in `RotationConfig.Builder`, all instances\nof `RotationConfig` (and hence of `RotatingFileOutputStream`) will share the\nsame `ScheduledExecutorService`. You can change the default pool size via\n`RotationJanitorCount` system property.\n\nPackaged rotation policies are listed below. (You can also create your own\nrotation policies by implementing `RotationPolicy` interface.)\n\n- Time-sensitive:\n  - `DailyRotationPolicy`\n  - `WeeklyRotationPolicy`\n- Byte-sensitive:\n  - `ByteMatchingRotationPolicy` (can be used to, e.g., rotate after every 1000 `\\n` (newline) occurrences, etc.)\n  - `SizeBasedRotationPolicy`\n\nOnce you have a handle on `RotatingFileOutputStream`, in addition to standard\n`java.io.OutputStream` methods (e.g., `write()`, `close()`, etc.), it provides\nthe following methods:\n\n| Method | Description |\n| ------ | ------------|\n| `getConfig()` | employed `RotationConfig` |\n| `rotate(RotationPolicy, Instant)` | trigger a rotation |\n\n`RotatingFilePattern.Builder` supports the following methods:\n\n| Method | Description |\n| ------ | ----------- |\n| `pattern(String)` | rotating file pattern (e.g., `/tmp/app-%d{yyyyMMdd-HHmmss-SSS}.log`) |\n| `locale(Locale)` | `Locale` used in the `DateTimeFormatter` (defaults to `Locale.getDefault()`) |\n| `timeZoneId(ZoneId)` | `ZoneId` denoting the time zone used in the `DateTimeFormatter` (defaults to `TimeZone.getDefault().toZoneId()`) |\n\nRotation-triggered custom behaviours can be introduced via `RotationCallback`\npassed to `RotationConfig.Builder`. `RotationCallback` provides the following\nmethods.\n\n| Method | Description |\n| ------ | ----------- |\n| `onTrigger(RotationPolicy, Instant)` | invoked at the beginning of every rotation attempt |\n| `onOpen(RotationPolicy, Instant, OutputStream)` | invoked at start and during rotation |\n| `onClose(RotationPolicy, Instant, OutputStream)` | invoked on stream close and during rotation |\n| `onSuccess(RotationPolicy, Instant, File)` | invoked after a successful rotation |\n| `onFailure(RotationPolicy, Instant, File, Exception)` | invoked after a failed rotation attempt |\n\n# Caveats\n\n- **`append` is enabled for `RotatingFileOutputStream` by default**, whereas\n  it is disabled (and hence truncates the file at start) for standard\n  `FileOutputStream` by default.\n\n- **Rotated file conflicts are not resolved by `rotating-fos`.** Once a\n  rotation policy gets triggered, `rotating-fos` applies the given\n  `filePattern` to determine the rotated file name. In order to avoid\n  previously generated files to be overridden, prefer a sufficiently\n  fine-grained date-time pattern.\n\n  For instance, given `filePattern` is `/tmp/app-%d{yyyyMMdd}.log`, if\n  `SizeBasedRotationPolicy` gets triggered multiple times within a day, the last\n  one will override the earlier generations in the same day. In order to avoid\n  this, you should use a date-time pattern with a higher resolution, such as\n  `/tmp/app-%d{yyyyMMdd-HHmmss-SSS}.log`.\n\n- **Make sure `RotationCallback` methods are not blocking.** Callbacks are\n  invoked using the `ScheduledExecutorService` passed via `RotationConfig`.\n  Hence blocking callback methods have a direct impact on time-sensitive\n  policies and compression tasks.\n\n- **When `append` is enabled, be cautious while using `onOpen` and `onClose`\n  callbacks.** These callbacks might be employed to introduce headers and/or\n  footers to certain type of files, e.g., [CSV](https://en.wikipedia.org/wiki/Comma-separated_values).\n  Though one needs to avoid injecting the same header and/or footer multiple\n  times when a file is re-opened for append. Note that this is not a problem\n  for files opened/closed via rotation.\n\n- **Byte-sensitive rotation policies can exceed given thresholds.**\n  They intercept `write(int[])`, `write(byte[])` calls of the output stream\n  and trigger when a certain condition holds. If you, say, use\n  `SizeBasedRotationPolicy` with `maxByteCount` configured to `3`,\n  and invoke `write(new byte[10])`, the rotation will be triggered once,\n  not more! Likewise, if you use `ByteMatchingRotationPolicy('.', 2)` and\n  invoke `write(\"1.2.3.4.5\".getBytes())`, rotation will be triggered once.\n\n# Security policy\n\nIf you have encountered an unlisted security vulnerability or other unexpected behaviour that has security impact, please report them privately to the [volkan@yazi.ci](mailto:volkan@yazi.ci) email address.\n\n# Contributors\n\n- [Alen (alturkovic) Turkovic](https://github.com/alturkovic) (reviewing `ByteMatchingRotationPolicy` #222)\n- [Christoph (pitschr) Pitschmann](https://github.com/pitschr) (Windows-specific\n  fixes, `RotationCallback#onOpen()` method, Java 9 module name, scheduler\n  shutdown at exit)\n- [broafka-ottokar](https://github.com/broafka-ottokar) (repeated rotation due to insufficient time resolution #207)\n- [David (kc7bfi) Robison](https://github.com/kc7bfi) (NPE due to write after close in #26)\n- [Jonas (yawkat) Konrad](https://yawk.at/) (`RotatingFileOutputStream`\n  thread-safety improvements)\n- [Lukas Bradley](https://github.com/lukasbradley/)\n- [Liran Mendelovich](https://github.com/liran2000/) (rolling via `maxBackupCount`)\n- [Nicolas Clemeur](https://github.com/nclemeur) (avoiding `FileInputStream`)\n\n# License\n\nCopyright \u0026copy; 2018-2024 [Volkan Yazıcı](https://volkan.yazi.ci)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvy%2Frotating-fos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvy%2Frotating-fos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvy%2Frotating-fos/lists"}