{"id":20490831,"url":"https://github.com/osoco/java-logging","last_synced_at":"2026-01-26T22:46:10.737Z","repository":{"id":26333546,"uuid":"108263810","full_name":"osoco/java-logging","owner":"osoco","description":"A (yet another) Logging framework for Java / Groovy.","archived":false,"fork":false,"pushed_at":"2024-10-18T07:53:44.000Z","size":134,"stargazers_count":1,"open_issues_count":3,"forks_count":1,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-11-15T17:18:50.576Z","etag":null,"topics":["java","logging-library"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/osoco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-25T11:59:53.000Z","updated_at":"2021-04-24T11:54:00.000Z","dependencies_parsed_at":"2022-08-25T08:50:30.235Z","dependency_job_id":null,"html_url":"https://github.com/osoco/java-logging","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osoco%2Fjava-logging","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osoco%2Fjava-logging/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osoco%2Fjava-logging/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osoco%2Fjava-logging/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/osoco","download_url":"https://codeload.github.com/osoco/java-logging/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234153137,"owners_count":18787747,"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":["java","logging-library"],"created_at":"2024-11-15T17:18:40.865Z","updated_at":"2025-09-25T05:30:39.725Z","avatar_url":"https://github.com/osoco.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# java-logging\nA (yet another) Logging framework for Java / Groovy.\n\n# Getting Started\n\nThis framework provides your application multiple logging capabilities, while hiding you the details of how it's working internally.\n\nYou express your logging preferences using the `@LoggingPreferences` annotation. Within that execution context, your preferences will be honored.\nTo log a message, you just need to call `LoggingFactory.getInstance().createLogging()`. It will return a `Logging` instance which gives you the methods you are expecting: `info(String)`, `debug(String)` and the like.\n\nBesides that, the `Logging` instance also provides you a way to pass additional context information so that the logging mechanism can optionally use it. By calling `Logging#getLoggingContext()`, you get a `LoggingContext`, which is a Map-like API storing the information locally to the thread.\n\n# Why another logging library\n\nThis library allows your code to express its logging preferences via annotations, and then use a minimal API.\nBy preferences, we mean \"if possible, use ElasticSearch and Log4J. If any of them fails, then use SLF4J and System.out\".\n\nIn practice, we've found that using DDD allowed us to reuse the code in different scenarios. Frequently, those scenarios\ndiffer in the runtime infrastructure. Some of them use ElasticSearch, some of them delegate logging to CloudWatch (via AWS-Lambda logger).\nSo we came up with a solution that has some benefits:\n\n- You declare your logging preferences.\n- You're not bound to any logging framework. Ours is just a thin layer that just resolves your preferences to the logging solutions available at runtime.\n- The logging API is the bare minimum: logging and logging context (for MDC/NDC capabilities).\n- You still use your current logging framework.\n- You still configure your logging as you need.\n\n# Runtime flags\n\nBy default, the framework will automatically discover logging configurations and producer implementations. Additionally, it finds out which logging preferences are defined, based on the methods in the stack. In some scenarios, such as AWS Lambda, that behavior is undesirable. You can switch them off using the following environment variables / system properties:\n- *AUTOMATICALLY_DISCOVER_LOGGING_CONFIGURATIONS* / *automatically.discover.logging.configurations*: Set to `false` to disable runtime discovery of logging configurations.\n- *AUTOMATICALLY_DISCOVER_LOGGING_ANNOTATIONS* / *automatically.discover.logging.annotations*: Set to `false` to disable runtime discovery of logging annotations.\n- *AUTOMATICALLY_DISCOVER_LOGGING_CONFIGURATION_PRODUCERS* / *automatically.discover.logging.configuration.producers*: Set to `false` to disable runtime discovery of logging configuration producers.\n- *DEFAULT_PREFERRED_LOGGING* / *default.preferred.logging*: Set to `aws-lambda` in your AWS Lambda functions.\n\n\n# Prerequisites\n\nFirst, add Java-Logging as dependency in your `pom.xml` or `build.gradle`.\n\n## Maven dependency\n\nThe dependency is already in maven-central. Just add the dependency to your `pom.xml`.\n\n```\n\u003cdependency\u003e\n  \u003cgroupId\u003ees.osoco.logging\u003c/groupId\u003e\n  \u003cartifactId\u003ejava-logging\u003c/artifactId\u003e\n  \u003cversion\u003e0.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Gradle coordinates\n\nSimilarly, the Gradle coordinates are:\n\n```\ndependencies {\n    compile(es.osoco.logging:java-logging:0.2)\n}\n```\n\n# Usage\n\nWhen your code needs to log anything, first import the required classes:\n```\nimport es.osoco.logging.Logging;\nimport es.osoco.logging.LoggingFactory;\n```\n\nThen, retrieve the `Logging` instance using the `LoggingFactory`:\n```\nLogging logging = LogFactory.createLogging();\n```\n\nOnce you have the `Logging` instance, use it for, well, logging:\n```\nlogging.info(\"Use case started\");\n```\n\n## Logging preferences\n\nWhile the underlying logging configuration is auto-discovered at runtime, you should\nspecify your preferences. You do so by using the `@LoggingPreferences` annotation either in a method or a class.\nYour preferences will define which logging mechanism will be used, within the execution context they are defined.\nThe general use case is to define your preferences in the application's entry point(s).\nThat way, they'll \"stick\" to all the code running within that execution flow.\n\nFor example, you could express your preferences in a `static void main()` method:\n```\nimport es.osoco.logging.LoggingFactory;\nimport es.osoco.logging.annotations.LoggingPreferences;\n\npublic class EntryPoint {\n    @LoggingPreferences(preferred=\"ElasticSearch\", fallback=\"System.err\")\n    public static void main(String[] args) {\n        LoggingFactory.getInstance().createLogging().info(\"Application started\");\n    }\n}\n```\n\n### A more complex scenario\n\nLet's say your code is modelled after DDD principles, and at the application level you have\ndifferent services or use cases interacting with your domain classes.\nFor illustration purposes, let's say your application is listening both RabbitMQ messages, and HTTP requests for a REST API routed by an AWS API Gateway, spawning an AWS Lambda with your code.\n\n```\npublic class RabbitMQAdapter implements ...\n...\n    public void onMessage(...) {\n        // run the use case\n    }\n...\n}\n```\n```\npublic class HttpServerAdapter implements ...\n...\n    public void onGet(...) {\n        // run the use case\n    }\n...\n}\n```\n\nIn this scenario, let's say you'd like to log differently depending on the adapter:  when the application receives messages from the RabbitMQ queue, you want to send the logs to a ElasticSearch server, whereas the Lambda should use the AWS-Lambda built-in logging mechanism itself.\n\nYou can accomplish that using `@LoggingPreferences` annotations:\n```\n@LoggingPreferences(preferred=\"ElasticSearch\")\npublic class RabbitMQAdapter implements ...\n```\n```\n@LoggingPreferences(preferred=\"aws-lambda\")\npublic class HttpServerAdapter implements ...\n```\n\n# Design concepts\n\nThis library uses two concepts: Logging Configuration, and Logging Adapters. Logging Configurations are abstractions to represent required configurations needed by Logging Adapters, but they don't know who uses them. Logging Adapters are the materializations of the `Logging` interface the client uses at runtime.\n\n\n## Logging Configuration\n\nLogging Configurations are responsible to provide whatever information is needed for a Logging Adapter to work correctly. It includes file paths, remote server IPs and ports, and so on. They include the necessary checks as well. For example, file permissions, if the remote servers are up and running, etc.\n\nLogging Configurations can be explicitly defined, and automatically discovered.\n\nIf your application can provide logging configuration automatically by itself, wrap that logic in a method, and annotate it with a `@LoggingConfigurationProducer` annotation.\nFor example:\n```\n    @LoggingConfigurationProducer(key=\"logstash\")\n    public LoggingConfiguration initLogging() {\n        return LogstashLoggingConfiguration(\"logstash\", ...);\n    }\n```\nThe first time the library is loaded, it will auto-discover all `@LoggingConfigurationProducer` annotations, run the annotated methods, and collect the results into `LoggingConfigurationRegistry`.\n\nThe `LoggingConfigurationRegistry` discovers all child classes of `LoggingConfigurationListener`, and notifies them whenever a new `LoggingConfiguration` is collected.\n\nYou can also provide your own logging configuration whenever it's available. For example, the AWS-Lambda `Logger` instance is only available in the context of a Lambda execution. In such cases, you'll need to provide a `LoggingConfiguration` yourself.\nFor example, in an AWS-Lambda `RequestHandler`, you'd want to call\n```\nnew AwsLambdaLoggingConfigurationProducer().configureLogging(context.getLogger());\n```\n\nThere's no API for `LoggingConfigurationProducer`, since there's no way for the library to automatically discover them (the ones that can be auto-discovered should use the `@LoggingConfigurationProducer` annotation). Fear not. Your code just needs to call:\n\n```\nLoggingConfigurationRegistry.getInstance().put(\"your-key\", yourLoggingConfiguration);\n```\n\n## Logging Adapters\n\nThe main responsibility of a `LoggingConfigurationListener` is to check if the new `LoggingConfiguration` is meaningful for him. If so, it will create a `LoggingAdapterBuilder` instance, and publish it into the `LoggingAdapterBuilderRegistry`.\n\nSuch registry simply maps keys with logging adapter builders. Builders know how to build `LoggingAdapter`s using the `LoggingConfiguration` information.\n\n## LoggingFactory\n\nThe `LoggingFactory` resolves which logging keys are bound to the runtime context, based on the current stack trace. Once that keys (*preferred* and *fallback*) are known, it creates a composite instance after asking the relevant builders to create the `LoggingAdapter`s. That composite logging then delegates the actual logging calls to the adapters: the log message is broadcasted to all preferred mechanisms. In case any of them fails, the same message is broadcasted to all fallback mechanisms.\n\n# Build from source\n\nTo build the artifact(s) yourself, just install Maven (2 or 3) and run:\n\n```\nmvn install\n```\n\nIt will generate the artifacts under the `target/` folder.\n\n# Running the tests\n\nJava-Logging uses Spock as testing framework. To run the specifications, run\n\n```\nmvn test\n```\n\nor\n\n```\ngradle test\n```\n\n# Contributing\n\nPlease read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.\n\n# Authors\n\nShibata team at OSOCO. See the `\u003cdevelopers\u003e` section in the Maven `pom.xml`.\n\n# License\n\nThis project is licensed under the GPLv3 License - see the LICENSE.md file for details\n\n# Acknowledgments\n\nThe idea behind this library arose spontaneously after a refactoring of a DDD project. It used its own AWS-Lambda-based Logging, and it\nmodelled it after the ports-and-adapters approach borrowed from hexagonal architectures.\n\nWe wanted to reuse that module in other contexts, so we stole the Logging code and started implementing this library.\n\nThe design was heavily influenced by [Pharo](https://pharo.org) (a Smalltalk dialect we at [OSOCO](http://www.osoco.es) are big fans of), and was made possible thanks to the awesome [fast-classpath-scanner](https://github.com/lukehutch/fast-classpath-scanner) library from [lukehutch](https://github.com/lukehutch).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosoco%2Fjava-logging","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fosoco%2Fjava-logging","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosoco%2Fjava-logging/lists"}