{"id":37023507,"url":"https://github.com/elf4j/elf4j-provider","last_synced_at":"2026-01-14T02:49:35.284Z","repository":{"id":143161487,"uuid":"607348454","full_name":"elf4j/elf4j-provider","owner":"elf4j","description":"A native logging service provider implementation of ELF4J (Easy Logging Facade for Java), and an independent logging solution for any Java application","archived":false,"fork":false,"pushed_at":"2025-12-01T16:43:22.000Z","size":466,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-12-04T05:57:32.439Z","etag":null,"topics":["asynchronous-logger","asynchronous-logging","elf4j","elf4j-engine","java","java-asynchronous-logging","java-logger","java-logging","java-logging-facade","java-logging-framework","java-nonblocking-logger","java-spi","json","json-asynchronous-logger","json-logger","logging-framework","spi-provider"],"latest_commit_sha":null,"homepage":"https://elf4j.github.io/elf4j-provider/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elf4j.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"q3769","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2023-02-27T20:05:35.000Z","updated_at":"2025-12-01T16:43:25.000Z","dependencies_parsed_at":"2025-10-03T15:11:14.143Z","dependency_job_id":"959207a2-5772-425f-9cf1-5a49a86d5193","html_url":"https://github.com/elf4j/elf4j-provider","commit_stats":null,"previous_names":["elf4j/elf4j-provider"],"tags_count":74,"template":false,"template_full_name":null,"purl":"pkg:github/elf4j/elf4j-provider","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elf4j%2Felf4j-provider","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elf4j%2Felf4j-provider/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elf4j%2Felf4j-provider/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elf4j%2Felf4j-provider/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elf4j","download_url":"https://codeload.github.com/elf4j/elf4j-provider/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elf4j%2Felf4j-provider/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408773,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["asynchronous-logger","asynchronous-logging","elf4j","elf4j-engine","java","java-asynchronous-logging","java-logger","java-logging","java-logging-facade","java-logging-framework","java-nonblocking-logger","java-spi","json","json-asynchronous-logger","json-logger","logging-framework","spi-provider"],"created_at":"2026-01-14T02:49:34.626Z","updated_at":"2026-01-14T02:49:35.272Z","avatar_url":"https://github.com/elf4j.png","language":"Java","funding_links":["https://github.com/sponsors/q3769"],"categories":[],"sub_categories":[],"readme":"[![Maven Central](https://img.shields.io/maven-central/v/io.github.elf4j/elf4j-provider.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.elf4j%22%20AND%20a:%22elf4j-provider%22)\n\n# elf4j-provider\n\nThe native logging _service provider_ implementation of [elf4j](https://github.com/elf4j/elf4j) (Easy Logging Facade for Java), and an independent drop-in logging solution for any Java application\n\n## User story\n\nAs an application developer using the elf4j logging facade, I want to have the option of using a runtime log _service provider_ that natively implements the [API and SPI](https://github.com/elf4j/elf4j#log-service-interface-and-access-api) of elf4j.\n\n## Prerequisite\n\n* Java 8+ required for versions earlier than 14.0.0 (exclusive)\n* Java 21+ required for versions later than 14.0.0 (inclusive)\n\n## Implementation notes\n\n* Guiding principle: Reasonable default and Pareto's 80/20 rule\n\n* This is simply a packaging unit of the `elf4j-engine`, using the  Java [Service Provider Framework](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) mechanism.  For more implementation detail, see the [elf4j-engine](https://github.com/elf4j/elf4j-engine) code repo.\n\n## Installation\n\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.elf4j/elf4j-provider.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.elf4j%22%20AND%20a:%22elf4j-provider%22)\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.elf4j\u003c/groupId\u003e\n    \u003cartifactId\u003eelf4j-provider\u003c/artifactId\u003e\n    \u003cversion\u003e${latest.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Features\n\n### Async logging only\n\nelf4j-provider output is always asynchronous. This is for throughout performance and, moreover, the 80/20 rule: When was\nthe last time a use\ncase truly required that logging had to be synchronous, and always blocking the application's normal work flow?\n\n### Standard streams output only\n\nOnly standard streams (stdout/stderr) channels are supported as log output destinations. It has been common, yet arguably redundant, that an application logging framework supports other output channels - given how specially and comprehensively the application's hosting system is already equipped to redirect/forward standard-stream data to other destinations. The `elf4j-provider`/`elf4j-engine` does not consider such data collecting process as an application-level concern - be it as simple as a Linux shell redirect to (rotating) log files, or as sophisticated as running a collector agent of a comprehensive observability service (e.g. Splunk, ELK, Newrelic, Datadog, etc...).\n\n### Log patterns including JSON\n\nJSON is a supported output pattern, in hopes of helping external log analysis tools. This is in addition to the usual\nline-based patterns - timestamp, level, thread, logger, class, method, file name, line number, and log message.\n\n### Service administration\n\n* Supports configuration refresh during runtime via, with option of passing in replacement properties instead of reloading the configuration file. The most frequent use case would be to change the minimum log output threshold level, without restarting the application.\n* To avoid loss of logs when the application shuts down, it is the user's responsibility to\n  call `LogServiceManager.stop` before the application exits. Upon that call, the log service will\n    1. stop accepting new log events\n    2. block and wait for all the accepted log events to finish processing\n\n  Alternatively, the user can register a JVM shutdown hook provided by `LogServiceManager.getShutdownHookThread`.\n\n## Usage\n\n* As with any other [elf4j](https://github.com/elf4j/elf4j) logging provider, client application should code\n  against [service API](https://github.com/elf4j/elf4j#service-interface-and-access-api) of the elf4j facade, and drop\n  in this provider implementation as a runtime dependency shown in the \"Installation\" section.\n\n* See elf4j for [API sample usage](https://github.com/elf4j/elf4j#use-it---for-log-service-api-clients).\n\n* In case of multiple elf4j service providers in classpath, pick this one like so:\n  ```\n  java -Delf4j.service.provider.fqcn=\"elf4j.engine.NativeLoggerFactory\" MyApplication\n  ```  \n  More details [here](https://github.com/elf4j/elf4j/blob/main/README.md#only-one-in-effect-logging-provider).\n\n## Configuration\n\n### Properties file\n\nThe default configuration file location is at the root of the application class path, with file\nname `elf4j-test.properties`, or if that is missing, `elf4j.properties`. Alternatively, to override the default\nlocation, use Java system property to provide an absolute path:\n\n```\njava -Delf4j.properties.location=/absoloute/path/to/myConfigurationFile -jar MyApplicaiton.jar\n``` \n\nAbsence of a configuration file results in no logging (no-op) at runtime. When present, though, the configuration file\nrequires zero/no configuration thus can be empty: the default configuration is a stdout writer with a minimum level\nof `TRACE` and a basic line-based logging pattern. To customize the default logging configuration, see the\nconfiguration sample file below.\n\n### Level\n\nA log output is rendered only when the `Logger` instance severity level is on or above the minimum output levels for\nboth the log caller class and the log writer.\n\n* The default severity level of a `Logger` instance from `Logger.instance()` is `INFO`, which is not configurable: The\n  elf4j [API](https://github.com/elf4j/elf4j#logging-service-interface-and-access-api) should be used to\n  programmatically obtain `Logger` instances of desired severity levels.\n* The default minimum output level for both log caller classes and the writer and is `TRACE`, which is configurable: For\n  caller classes, the minimum output level can be configured on global, package, or individual class levels.\n\n### Writer\n\nBy default, the elf4j-engine supports one single writer instance of the standard-stream type. The codebase is\nextension-ready for multiple writer types; and for each custom type, multiple writer instances. However, the need for\nsuch extensions (e.g. a flat-file writer type with multiple writer instances for different target files) is rare,\nconsidering the abundant host/OS and vendor options to ship standard-stream data to various destinations other than the\ndefault system console.\n\n### Output format pattern\n\nAll individual patterns, including the JSON pattern, can either be the only output of the log entry, or mixed together\nwith any other patterns. They each take the form of `{pattern:displayOptions}`, where multiple display options are\nseparated by commas. Patterns inside curly brace pairs are predefined and will be interpreted before output, while\npatterns outside curly brace pairs are written out verbatim.\n\nThe predefined patterns are:\n\n* `timestamp`: Date time format configurable via Java\n  `DateTimeFormatter` [pattern syntax](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns),\n  default to ISO datetime format with time zone offset of the application running host\n* `logger`: Option of `simple`, `full`, or `compressed` (only the first letter for each package segment) for class names,\n  default to `simple`\n* `level`: Length configurable, default to full length\n* `thread`: Option of `name` or `id`, default to name\n* `class` (performance-sensitive): Option of `simple`, `full`, or `compressed` (only the first letter for each package segment) for class names,\n  default to `simple`\n* `method` (performance-sensitive): No configuration options, simple method name\n* `filename` (performance-sensitive): No configuration options, simple file name\n* `linenumber` (performance-sensitive): No configuration options, the line number where the log is issued in the file\n* `sysprop`: Option `name` for the JRE System Property\n* `sysenv`: Option `name` for the system environment variable\n* `message`: No configuration options, always prints user message, and exception stack trace if any\n* `json`: Multiple options allowed - `caller-thread` to include caller thread (name and id), `caller-detail` to include\n  caller stack detail (class, method, filename, linenumber), and option `pretty` to indent the JSON text to more\n  readable format. Default is no thread/caller detail and the minified single-line format\n* `context`: Mandatory display option `key` whose value will be retrieved from\n  the [`MDC`](https://www.slf4j.org/api/org/slf4j/MDC.html) context and displayed in the\n  log message; the `key` cannot be empty, otherwise, an error will be raised\n\n_Note_:\n\n* Pattern names are case-insensitive.\n* A system-dependent line feed character (hidden pattern) is appended automatically at the end of each log entry; this is\nnot configurable.\n* Performance-sensitive patterns should only be used when necessary or if the application is performance tolerant.\n\n#### Pattern output samples\n\nLine-based Default\n\n* Pattern: none, which is the same as\n\n  ```\n  {timestamp} {level} {logger} - {message}\n  ```\n\n* Output:\n\n  ```\n  2023-03-22T21:11:33.040-05:00 INFO IntegrationTest$defaultLogger - Hello, world!\n  ```\n\nLine-based Customized\n\n* Pattern:\n  ```\n  {timestamp:yyyy-MM-dd'T'HH:mm:ss.SSSXXX} {level:5} [{thread:name}] {class:compressed}#{method}(L{linenumber}@{filename}) -- {message}\n  ```\n* Output:\n  ```\n  2023-03-30T17:14:54.735-05:00 INFO  [main] e.e.IntegrationTest$defaultLogger#hey(L50@IntegrationTest.java) -- Hello, world!\n  ```\n\nJSON Default\n\n* Pattern:\n  ```\n  {json}\n  ```\n* Output:\n  ```\n  {\"timestamp\":\"2023-10-07T22:52:47.962872-05:00\",\"message\":\"Hello, world!\",\"level\":\"INFO\",\"callerClass\":\"elf4j.provider.Demo\"}\n  ```\n\nJSON Customized\n\n* Pattern:\n  ```\n  {json:caller-thread,caller-detail,pretty}\n  ```\n* Output:\n  ```json\n  {\n    \"timestamp\": \"2023-10-07T22:53:46.2568507-05:00\",\n    \"message\": \"Hello, world!\",\n    \"level\": \"INFO\",\n    \"callerThread\": {\n      \"name\": \"main\",\n      \"id\": 1\n    },\n    \"callerDetail\": {\n      \"className\": \"elf4j.provider.Demo\",\n      \"methodName\": \"main\",\n      \"lineNumber\": 12,\n      \"fileName\": \"Main.java\"\n    }\n  }\n  ```\n\n#### Sample configuration file\n\n```properties\n### Zero configuration mandatory, this file can be empty - default to a line-based writer with simple log pattern\n### global no-op flag, overriding and will turn off all logging if set true\n#noop=true\n### Global minimum output level for both caller class and writer. Optional, default to TRACE.\nlevel=info\n### These override the minimum output level of all caller classes included the specified package spaces\nlevel@org.springframework=warn\nlevel@org.apache=error\n### Standard out stream type, stdout or stderr, default is stdout\nstream=stderr\n### pattern is optional, default is a simple line-based\npattern={json}\n#pattern={json:caller-thread,caller-detail,pretty}\n#pattern={timestamp:yyyy-MM-dd'T'HH:mm:ss.SSSXXX} {level:5} [{thread:id}] {class:compressed}#{method}(L{linenumber}@{filename}) - {message}\n### Max concurrency to process logs from different caller threads, default to available runtime processors\n#concurrency=20\n```\n\n### Output stream types\n\nEither stdout (the default if omitted) or stderr, configured globally.\n\n### Configuration refresh\n\n`LogServiceManager.refresh()` will reload the configuration file and apply the latest file properties during\nruntime. `LogServiceManager.refresh(java.util.Properties)` will apply the passed-in properties as the replacement of the\ncurrent properties, and the configuration file will be ignored.\n\n## Performance\n\nIt's not how fast you fill up the target log file or repository, it's how fast you relieve the application from logging\nduty back to its own business.\n\nPerformance benchmark metrics tend to be highly dependent on the nature of the work load and overall setup, but here is\na naive start (Recommend re-configuration based on individual use cases), comparing with some popular log engines:\n\n* [elf4j-benchmark](https://github.com/elf4j/elf4j-benchmark)\n\nChronological order is generally required for log events to arrive at their final destination. The usual destination of\nthe standard out streams is the system Console, where an out-of-order display would be confusing. That means log events\nneed to be processed [sequentially](https://github.com/q3769/conseq4j#concurrency-and-sequencing) - at least for those\nevents that are issued from the same application/caller thread. This inevitably imposes some limit on the log processing\nthroughput. No matter the log processing is synchronous or asynchronous to the main business workflow, if the\napplication's log issuing frequency is higher than the throughput of the log processing, then over time, the main\nworkflow should be blocked and bound (\"back-pressured\") by the log processing throughput limit.\n\nSome logging information has to be collected by the main application thread, synchronously to the business workflow. For\nexample, caller detail information such as method name, line number, or file name are performance-wise expensive to\nretrieve, yet unavailable for a different/asynchronous thread to look up. The elf4j-engine uses the main caller thread\nto synchronously collect all required information into a log event, and then hands off the event to an asynchronous\nprocess for the rest of the work. It helps, however, if the client application can do without performance-sensitive\ninformation in the first place; the default log pattern configuration does not include such caller details.\n\nThe ideal situation of asynchronous logging is for the main application to \"fire and forget\" the log events, and\ncontinue its main business flow without further blocking, in which case the log processing throughput has little impact\non the main application. That only happens, however, when there are spare threads (and/or task queue capacity) available\nfrom the async thread pool. When no spare thread is available, the log process becomes pseudo-synchronous, in which case\nnot only will the main application be back-pressured while awaiting processing time, but also the extra cost of\nfacilitating asynchronous communications will now add to that of the main workflow. By contrast, a true synchronous\nlogging without buffering will delay the main workflow in each transaction, albeit having no additional cost for\nasynchrony.\n\nFor asynchronous logging to work well, the log processing throughput should, over time, exceed the log event generation\nrate; the work queue hosting the log events should serve only as a temporary buffer when the log eventing rate is\nmomentarily higher than the log processing throughput.\n\nLeveraging\nthe [conseq4j](https://github.com/q3769/conseq4j#style-2-submit-each-task-directly-for-execution-together-with-its-sequence-key)\nconcurrent API, the elf4j-engine processes log events issued by different caller threads in parallel, and those by the\nsame caller threads in sequence. This ensures all logs of the same caller thread arrives at the log destination in\nchronological order (same order as they are issued by the thread). However, logs from different caller threads\nare [not guaranteed](https://github.com/q3769/conseq4j#concurrency-and-sequencing) of any particular order of arrival.\n\nIf omitted in configuration file, the default concurrency (maximum number of threads in parallel) for asynchronous\nprocessing is the number\nof [Runtime#availableProcessors](https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors--)\nof the current JVM at the application startup time (or when the log service is refreshed). This is the thread pool\ncapacity for log event processing.\n\nThe standard stream destinations are often redirected/replaced by the host environment or user, in which case further\nmanoeuvres may help such data channel's performance. For example, if the target repository is a log file on disk, then\n\n```shell\njava MyApplication | cat \u003elogFile\n```\n\nmay outperform\n\n```shell\njava MyApplication \u003elogFile\n```\n\ndue to the buffering effect of piping and `cat`.\n\nSuch external setups fall into the category of increasing data channel bandwidth, and are considered outside the scope\nof application-level logging.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felf4j%2Felf4j-provider","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felf4j%2Felf4j-provider","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felf4j%2Felf4j-provider/lists"}