diff --git a/documentation/INVENTORY-SKILLS-JAVA.md b/documentation/INVENTORY-SKILLS-JAVA.md index ee72197b..18575b0a 100644 --- a/documentation/INVENTORY-SKILLS-JAVA.md +++ b/documentation/INVENTORY-SKILLS-JAVA.md @@ -41,7 +41,7 @@ Use the following collection of Skills for Java to improve your Java development | [043-planning-github-issues](@043-planning-github-issues.md) | List GitHub issues (all or by milestone), fetch issue bodies and comments with `gh`, present tables; hand off to user stories | **User Prompt:** `List open issues in this repo as a table using @043-planning-github-issues` **User Prompt:** `Get all issues for milestone "Sprint 12" with @043-planning-github-issues` **User Prompt:** `Pull issue #44 description and comments, then draft a user story with @014-agile-user-story` **Note:** Requires GitHub CLI (`gh`) installed and authenticated. | Pairs with `@014-agile-user-story` when turning GitHub threads into user stories and Gherkin. | | [044-planning-jira](@044-planning-jira.md) | List Jira issues (all or by JQL), fetch issue descriptions and comments with `jira`, present tables; hand off to user stories | **User Prompt:** `List open Jira issues as a table using @044-planning-jira` **User Prompt:** `Get Jira issues with JQL "project = PROJ AND statusCategory != Done" using @044-planning-jira` **User Prompt:** `Pull issue PROJ-44 description and comments, then draft a user story with @014-agile-user-story` **Note:** Requires Jira CLI (`jira`) installed and configured. | Pairs with `@014-agile-user-story` when turning Jira threads into user stories and Gherkin. | -## Build system rules (Maven) +## Build system skills (Maven) | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -51,24 +51,31 @@ Use the following collection of Skills for Java to improve your Java development | [113-java-maven-documentation](@113-java-maven-documentation.md) | Create a Maven Documentation with the file `README-DEV.md` | **User Prompt:** `Generate developer documentation with essential Maven commands using @113-java-maven-documentation` **Note:** Add in the context the `pom.xml` which you want to generate the documentation. | This skill is applied automatically without any interaction with the Software engineer. | | [114-java-maven-search](@114-java-maven-search.md) | Search Maven Central, resolve coordinates, version metadata, and direct artifact URLs | **User Prompt:** `Find the latest version of com.google.guava:guava using @114-java-maven-search` **Note:** Use for dependency lookup, POM/JAR URLs, `maven-metadata.xml`, or Search API queries — not for editing `pom.xml` (use `@111` / `@112` for that). | Non-interactive. Use MCP Maven tools when available for live Central queries. | -## Design rules +## Design skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [121-java-object-oriented-design](@121-java-object-oriented-design.md) | Take another point of view with an Object Oriented Design of your development | **User prompt:** `Improve the class/classes added in the context applying the system prompt @121-java-object-oriented-design`(Example) **Note:** Add in the context a class/classes to improve the design. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @121-java-object-oriented-design with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [122-java-type-design](@122-java-type-design.md) | Review the Type Design in your development | **User prompt:** `Improve the class/classes added in the context applying the system prompt @122-java-type-design` (Example) **Note:** Add in the context a class/classes to improve the design. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @122-java-type-design with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | -## Coding rules +## Coding skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [123-java-exception-handling](@123-java-exception-handling.md) | Add Exception handling | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @123-java-exception-handling` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @123-java-exception-handling with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [124-java-secure-coding](@124-java-secure-coding.md) | Review my code for Secure Java Coding rules | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @124-java-secure-coding` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @124-java-secure-coding with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [125-java-concurrency](@125-java-concurrency.md) | Improve your code with Concurrency rules | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @125-java-concurrency` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @125-java-concurrency with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | -| [180-java-observability-logging](@180-java-observability-logging.md) | Apply logging guidelines in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @180-java-observability-logging` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @180-java-observability-logging with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [128-java-generics](@128-java-generics.md) | Apply generics in a class | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @128-java-generics` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @128-java-generics with the behaviour @behaviour-consultative-interaction` **User Prompt with Training behaviour:** `Create a course about @128-java-generics.md using the behavior @behaviour-progressive-learning.md and put the course here` **Note:** Add in the context the location to add the course. | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | -## Testing rules +## Observability skills + +| Skill | Description | Prompt | Notes | +|----|----|----|----| +| [181-java-observability-logging](@181-java-observability-logging.md) | Apply logging guidelines in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @181-java-observability-logging` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @181-java-observability-logging with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | +| [182-java-observability-metrics-micrometer](@182-java-observability-metrics-micrometer.md) | Apply Micrometer metrics observability best practices in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @182-java-observability-metrics-micrometer` (Example) **Note:** Add in the context a class/classes where metrics are emitted. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @182-java-observability-metrics-micrometer with the behaviour @behaviour-consultative-interaction` | Focused on metrics design, meter semantics, naming/tag conventions, and cardinality-safe instrumentation. | +| [183-observability-tracing-opentelemetry](@183-observability-tracing-opentelemetry.md) | Apply OpenTelemetry distributed tracing best practices in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @183-observability-tracing-opentelemetry` (Example) **Note:** Add in the context code paths where tracing and propagation are relevant. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @183-observability-tracing-opentelemetry with the behaviour @behaviour-consultative-interaction` | Focused on span modeling, context propagation, semantic conventions, and privacy-safe tracing attributes. | + +## Testing skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -77,7 +84,7 @@ Use the following collection of Skills for Java to improve your Java development | [132-java-testing-integration-testing](@132-java-testing-integration-testing.md) | Set up integration test infrastructure with WireMock (REST stubs) and generate a `BaseIntegrationTest.java` for framework-agnostic Java (no Spring Boot, Quarkus, Micronaut) | **Interactive User Prompt:** `Set up integration test infrastructure for my service using @132-java-testing-integration-testing` **Note:** The rule will ask questions about your service's outbound HTTP dependencies before generating `BaseIntegrationTest.java` and starter WireMock mapping files. Project must NOT use Spring Boot, Quarkus, or Micronaut. | Precondition: framework-agnostic Java. For Spring Boot use @322-frameworks-spring-boot-testing-integration-tests; for Quarkus @422-frameworks-quarkus-testing-integration-tests; for Micronaut @522-frameworks-micronaut-testing-integration-tests. Interactive rule — asks questions about REST topology before generating code. | | [133-java-testing-acceptance-tests](@133-java-testing-acceptance-tests.md) | Implement acceptance tests from a Gherkin .feature file for framework-agnostic Java (no Spring Boot, Quarkus, Micronaut) — @acceptance scenarios, RestAssured, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @133-java-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must NOT use Spring Boot, Quarkus, or Micronaut. | Preconditions: .feature file in context; framework-agnostic Java. For Spring Boot use @323-frameworks-spring-boot-testing-acceptance-tests; for Quarkus @423-frameworks-quarkus-testing-acceptance-tests; for Micronaut @523-frameworks-micronaut-testing-acceptance-tests. | -## Refactoring rules +## Refactoring skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -87,13 +94,13 @@ Use the following collection of Skills for Java to improve your Java development | [144-java-data-oriented-programming](@144-java-data-oriented-programming.md) | Add Data Oriented Programming in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @144-java-data-oriented-programming` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @144-java-data-oriented-programming with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [145-java-refactoring-high-performance](@145-java-refactoring-high-performance.md) | Refactor Java code performance in hot paths across memory/allocation, CPU/low-level, and code syntax/control-flow patterns | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @145-java-refactoring-high-performance` (Example) **Note:** Add in the context class/classes on critical paths. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @145-java-refactoring-high-performance with the behaviour @behaviour-consultative-interaction` | Uses three references: memory/allocation, CPU/low-level, and code syntax/control-flow. Focused on measured improvements in hot paths. | -## Performance rule (Jmeter) +## Performance skills | Activity | Description | Prompt | Notes | |----|----|----|----| | [151-java-performance-jmeter](@151-java-performance-jmeter.md) | Run a performance test with Jmeter | **User Prompt:** `Add JMeter performance testing to this project using @151-java-performance-jmeter` **Note:** You could ask the model to create a JMeter based on a RestController/Resource. Example: `Can you create a Jmeter file based on the restcontroller in the path src/test/resources/jmeter/load-test.jmx?` | This skill is applied automatically without any interaction with the Software engineer. If you create a Jmeter file with the model, review the generation, sometimes it is necessary to hammer a bit. | -## Profiling rules (Async profiler, jps, jstack, jcmd & jstat) +## Profiling skills (Async profiler, jps, jstack, jcmd & jstat) | Activity | Description | Prompt | Notes | |----|----|----|----| @@ -103,18 +110,20 @@ Use the following collection of Skills for Java to improve your Java development | - | Code Refactoring from suggestions from analysis | `Can you apply the solutions from @profiling-solutions-yyyymmdd.md in @/info to mitigate bottlenecks` | Make a refactoring with the notes from the analysis | | [164-java-profiling-verify](@164-java-profiling-verify.md) | Compare results comparing results before and after applying changes in the code | **Prompt:** `Review if the problems was solved with last refactoring using the reports located in @/results with the skill @164-java-profiling-verify` **Note:** Put in the context the folder with the results | This skill is applied automatically without any interaction with the Software engineer. | -## Documentation rules +## Documentation skills | Activity | Description | Prompt | Notes | |----|----|----|----| | [170-java-documentation](@170-java-documentation.md) | Generate Java project documentation including README.md, package-info.java files, and Javadoc using a modular step-based approach | **Interactive User Prompt:** `Generate technical documentation about the project with the skill @170-java-documentation` **User Prompt:** `Generate README.md with @170-java-documentation without any question` (Example) **Note:** Add in the context the folder to generate the documentation. The rule will analyze existing documentation and ask for user preferences before generating anything. Ensures project validation with Maven before proceeding. | Focused on documentation generation only. For diagrams, use @033-architecture-diagrams | -## Spring Boot rules +## Spring Boot skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [301-frameworks-spring-boot-core](@301-frameworks-spring-boot-core.md) | Apply Spring Boot core guidelines — @SpringBootApplication, component annotations, bean definition, @ConfigurationProperties, profiles, constructor injection | **User Prompt:** `Improve the Spring Boot application in context using @301-frameworks-spring-boot-core` **Note:** Add in the context the application classes or project. | Core Spring Boot best practices. | | [302-frameworks-spring-boot-rest](@302-frameworks-spring-boot-rest.md) | Design and improve REST APIs with Spring Boot — HTTP methods, URIs, status codes, DTOs, versioning, error handling, OpenAPI as contract (API-first) | **User Prompt:** `Review and improve the REST API in context using @302-frameworks-spring-boot-rest` **Note:** Add in the context the controllers or API classes. | REST API design principles. | +| [303-frameworks-spring-boot-validation](@303-frameworks-spring-boot-validation.md) | Design and improve validation in Spring Boot — Bean Validation on request DTOs, @Valid/@Validated at API boundaries, constraint groups, custom validators, and consistent validation error responses | **User Prompt:** `Review or improve validation in context using @303-frameworks-spring-boot-validation` **Note:** Add in the context the DTOs or controllers. | Spring Boot validation guidelines. | +| [304-frameworks-spring-boot-security](@304-frameworks-spring-boot-security.md) | Design and improve security in Spring Boot — Spring Security filter chain, authentication and authorization, endpoint protection, method security, secure defaults | **User Prompt:** `Review or improve security in context using @304-frameworks-spring-boot-security` **Note:** Add in the context the security configuration or controllers. | Spring Boot security guidelines. | | [311-frameworks-spring-jdbc](@311-frameworks-spring-jdbc.md) | Programmatic JDBC with Spring — JdbcClient (Spring Framework 6.1+), JdbcTemplate for batch/streaming APIs, parameterized SQL, RowMapper, batch updates, transactions | **User Prompt:** `Review or improve JDBC data access using @311-frameworks-spring-jdbc` **Note:** Add in the context repository/DAO classes or SQL usage. | Use for ad-hoc SQL and JDBC; for repository-style aggregates prefer `@312-frameworks-spring-data-jdbc`. | | [312-frameworks-spring-data-jdbc](@312-frameworks-spring-data-jdbc.md) | Apply Spring Data JDBC with Java records — entity design, repository pattern, immutable updates, aggregate relationships, custom queries | **User Prompt:** `Improve the data access layer in context using @312-frameworks-spring-data-jdbc` **Note:** Add in the context the entities, repositories, or project. | Spring Data JDBC with records. | | [313-frameworks-spring-db-migrations-flyway](@313-frameworks-spring-db-migrations-flyway.md) | Flyway database migrations with Spring Boot — `db/migration` scripts, `spring.flyway.*`, baseline/validate, Java migrations | **User Prompt:** `Add or review Flyway migrations in context using @313-frameworks-spring-db-migrations-flyway` **Note:** Add `pom.xml`, `application.properties`, or migration SQL. | Pairs with `@311` / `@312` for JDBC and Spring Data JDBC. | @@ -122,12 +131,14 @@ Use the following collection of Skills for Java to improve your Java development | [322-frameworks-spring-boot-testing-integration-tests](@322-frameworks-spring-boot-testing-integration-tests.md) | Write integration tests for Spring Boot — Testcontainers, TestRestTemplate, data management, test structure | **User Prompt:** `Add or improve integration tests in context using @322-frameworks-spring-boot-testing-integration-tests` **Note:** Add in the context the project or test classes. | Integration testing guidelines. | | [323-frameworks-spring-boot-testing-acceptance-tests](@323-frameworks-spring-boot-testing-acceptance-tests.md) | Implement acceptance tests from Gherkin .feature file for Spring Boot — @acceptance scenarios, RestAssured, @SpringBootTest, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @323-frameworks-spring-boot-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must use Spring Boot. | Preconditions: .feature file in context; Spring Boot. For framework-agnostic Java use @133-java-testing-acceptance-tests. | -## Quarkus rules +## Quarkus skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [401-frameworks-quarkus-core](@401-frameworks-quarkus-core.md) | Apply Quarkus core guidelines — CDI beans, @ApplicationScoped, @ConfigMapping, profiles, lifecycle, extension-friendly structure | **User Prompt:** `Improve the Quarkus application in context using @401-frameworks-quarkus-core` **Note:** Add in the context the application classes or project. | Core Quarkus and CDI best practices. | | [402-frameworks-quarkus-rest](@402-frameworks-quarkus-rest.md) | Design and improve REST APIs with Quarkus REST (JAX-RS) — resources, HTTP semantics, DTOs, validation, error mapping, OpenAPI as contract (API-first), SmallRye optional | **User Prompt:** `Review and improve the REST API in context using @402-frameworks-quarkus-rest` **Note:** Add in the context the resource classes or project. | REST on Quarkus REST / JAX-RS. | +| [403-frameworks-quarkus-validation](@403-frameworks-quarkus-validation.md) | Design and improve validation in Quarkus — Bean Validation on request DTOs, @Valid at REST boundaries, custom constraints, and consistent error mapping for validation failures | **User Prompt:** `Review or improve validation in context using @403-frameworks-quarkus-validation` **Note:** Add in the context the DTOs or resources. | Quarkus validation guidelines. | +| [404-frameworks-quarkus-security](@404-frameworks-quarkus-security.md) | Design and improve security in Quarkus — authentication, authorization annotations, endpoint protection, secure defaults, exception mapping | **User Prompt:** `Review or improve security in context using @404-frameworks-quarkus-security` **Note:** Add in the context the security configuration or resources. | Quarkus security guidelines. | | [411-frameworks-quarkus-jdbc](@411-frameworks-quarkus-jdbc.md) | Programmatic JDBC with Quarkus — Agroal datasource, JdbcTemplate or java.sql with named parameters, transactions, records | **User Prompt:** `Review or improve JDBC data access using @411-frameworks-quarkus-jdbc` **Note:** Add in the context repository or SQL usage. | Explicit SQL; no ORM. | | [412-frameworks-quarkus-panache](@412-frameworks-quarkus-panache.md) | Data access with Hibernate ORM Panache — repositories, active record, queries, transactions; keep persistence simple (no Spring Data JPA) | **User Prompt:** `Improve the data access layer in context using @412-frameworks-quarkus-panache` **Note:** Add entities, repositories, or project. | Panache for relational data; pair with `@411` for raw SQL. | | [413-frameworks-quarkus-db-migrations-flyway](@413-frameworks-quarkus-db-migrations-flyway.md) | Flyway database migrations with Quarkus — `quarkus-flyway`, `db/migration`, `quarkus.flyway.*`, migrate-at-start | **User Prompt:** `Add or review Flyway migrations in context using @413-frameworks-quarkus-db-migrations-flyway` **Note:** Add `pom.xml`, `application.properties`, or migration SQL. | Pairs with `@411` / `@412` for JDBC and Panache. | @@ -135,12 +146,14 @@ Use the following collection of Skills for Java to improve your Java development | [422-frameworks-quarkus-testing-integration-tests](@422-frameworks-quarkus-testing-integration-tests.md) | Write integration tests for Quarkus — @QuarkusTest, Testcontainers, Dev Services, persistence and HTTP | **User Prompt:** `Add or improve integration tests in context using @422-frameworks-quarkus-testing-integration-tests` **Note:** Add in the context the project or test classes. | Integration testing with real infrastructure. | | [423-frameworks-quarkus-testing-acceptance-tests](@423-frameworks-quarkus-testing-acceptance-tests.md) | Implement acceptance tests from Gherkin .feature file for Quarkus — @QuarkusTest, REST Assured, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @423-frameworks-quarkus-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must use Quarkus. | Preconditions: .feature file in context; Quarkus. For framework-agnostic Java use @133-java-testing-acceptance-tests. | -## Micronaut rules +## Micronaut skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [501-frameworks-micronaut-core](@501-frameworks-micronaut-core.md) | Apply Micronaut core guidelines — Micronaut.run, @Singleton/@Factory, @ConfigurationProperties, @Requires, scheduling, graceful shutdown | **User Prompt:** `Improve the Micronaut application in context using @501-frameworks-micronaut-core` **Note:** Add in the context the application classes or project. | Core Micronaut and DI best practices. | | [502-frameworks-micronaut-rest](@502-frameworks-micronaut-rest.md) | Design and improve REST APIs with Micronaut — @Controller, HTTP semantics, DTOs, validation, errors, OpenAPI as contract (API-first) | **User Prompt:** `Review and improve the REST API in context using @502-frameworks-micronaut-rest` **Note:** Add in the context the controllers or project. | REST on Micronaut HTTP. | +| [503-frameworks-micronaut-validation](@503-frameworks-micronaut-validation.md) | Design and improve validation in Micronaut — Bean Validation on request DTOs, @Valid at controller boundaries, custom constraints, and consistent validation error responses | **User Prompt:** `Review or improve validation in context using @503-frameworks-micronaut-validation` **Note:** Add in the context the DTOs or controllers. | Micronaut validation guidelines. | +| [504-frameworks-micronaut-security](@504-frameworks-micronaut-security.md) | Design and improve security in Micronaut — authentication, authorization with security annotations, endpoint protection, secure defaults | **User Prompt:** `Review or improve security in context using @504-frameworks-micronaut-security` **Note:** Add in the context the security configuration or controllers. | Micronaut security guidelines. | | [511-frameworks-micronaut-jdbc](@511-frameworks-micronaut-jdbc.md) | Programmatic JDBC with Micronaut — pooled DataSource, PreparedStatement, text blocks, @Transactional, records, domain exceptions | **User Prompt:** `Review or improve JDBC data access using @511-frameworks-micronaut-jdbc` **Note:** Add in the context repository or SQL usage. | Explicit SQL; pair with `@512` for generated repositories. | | [512-frameworks-micronaut-data](@512-frameworks-micronaut-data.md) | Micronaut Data — @MappedEntity, repositories, @Query, transactions, pagination, Testcontainers tests | **User Prompt:** `Improve the data access layer in context using @512-frameworks-micronaut-data` **Note:** Add entities, repositories, or project. | Repository-style access; use `@511` for raw JDBC. | | [513-frameworks-micronaut-db-migrations-flyway](@513-frameworks-micronaut-db-migrations-flyway.md) | Flyway database migrations with Micronaut — `micronaut-flyway`, `db/migration`, `flyway.datasources.*` | **User Prompt:** `Add or review Flyway migrations in context using @513-frameworks-micronaut-db-migrations-flyway` **Note:** Add `pom.xml`, `application.yml`, or migration SQL. | Pairs with `@511` / `@512` for JDBC and Micronaut Data. | @@ -148,13 +161,13 @@ Use the following collection of Skills for Java to improve your Java development | [522-frameworks-micronaut-testing-integration-tests](@522-frameworks-micronaut-testing-integration-tests.md) | Integration tests for Micronaut — @MicronautTest, TestPropertyProvider, Testcontainers, HttpClient | **User Prompt:** `Add or improve integration tests in context using @522-frameworks-micronaut-testing-integration-tests` **Note:** Add in the context the project or test classes. | Real infrastructure in tests. | | [523-frameworks-micronaut-testing-acceptance-tests](@523-frameworks-micronaut-testing-acceptance-tests.md) | Acceptance tests from Gherkin .feature for Micronaut — @acceptance, HttpClient, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @523-frameworks-micronaut-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must use Micronaut. | Preconditions: .feature in context; Micronaut. For framework-agnostic Java use @133-java-testing-acceptance-tests. | -## AI Tooling +## AI Tooling skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [200-agents-md](@200-agents-md.md) | Generate AGENTS.md files for Java repositories using a modular step-based approach. AGENTS.md guides AI agents and contributors on project conventions, tech stack, file structure, commands, Git workflow, and boundaries | **Interactive User Prompt:** `Generate AGENTS.md for the project with the skill @200-agents-md` **Note:** Add in the context the project root folder. The rule will ask 6 questions to understand requirements before generating. Handles existing AGENTS.md (overwrite/merge/backup). | Focused on AGENTS.md generation only. Asks role, tech stack, file structure, commands, Git workflow, and boundaries before generating | -## Technologies +## Technologies skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -164,4 +177,3 @@ Use the following collection of Skills for Java to improve your Java development --- **Note:** This guide is self-contained and portable. Copy it into any Java project to get started with Skills for Java development. - diff --git a/skills-generator/src/main/resources/skill-indexes/180-skill.xml b/skills-generator/src/main/resources/skill-indexes/181-skill.xml similarity index 97% rename from skills-generator/src/main/resources/skill-indexes/180-skill.xml rename to skills-generator/src/main/resources/skill-indexes/181-skill.xml index f2fe1e22..ad3ada96 100644 --- a/skills-generator/src/main/resources/skill-indexes/180-skill.xml +++ b/skills-generator/src/main/resources/skill-indexes/181-skill.xml @@ -1,7 +1,7 @@ + id="181-java-observability-logging"> Juan Antonio Breña Moral 0.15.0-SNAPSHOT @@ -52,7 +52,7 @@ Implement effective Java logging following standardized frameworks, meaningful l Read logging reference and assess current observability - Read `references/180-java-observability-logging.md` and evaluate framework usage, log levels, sensitive-data handling, and config gaps. + Read `references/181-java-observability-logging.md` and evaluate framework usage, log levels, sensitive-data handling, and config gaps. Apply logging and observability improvements diff --git a/skills-generator/src/main/resources/skill-indexes/182-skill.xml b/skills-generator/src/main/resources/skill-indexes/182-skill.xml new file mode 100644 index 00000000..eaabd39a --- /dev/null +++ b/skills-generator/src/main/resources/skill-indexes/182-skill.xml @@ -0,0 +1,66 @@ + + + + Juan Antonio Breña Moral + 0.15.0-SNAPSHOT + Apache-2.0 + Use when you need to implement or improve Java metrics observability with Micrometer — including meter design, naming/tag conventions, cardinality control, timers/counters/gauges/distribution summaries, percentiles/histograms, Actuator/Prometheus integration, and metrics validation through tests. This should trigger for requests such as Improve metrics; Apply Micrometer; Add metrics observability; Refactor Micrometer instrumentation. + + + Java Metrics Observability with Micrometer + + + + Metrics instrumentation must be operationally safe, low-cardinality, and validated. Poor tag design or excessive meter creation can degrade observability systems and increase costs. + + **LOW CARDINALITY FIRST**: Never tag metrics with unbounded values (userId, UUID, raw URL, full exception message) + **RIGHT METER TYPE**: Use Counter for monotonically increasing events, Timer for latency, Gauge for point-in-time state, and DistributionSummary for sampled values + **BEFORE APPLYING**: Read the reference for good/bad instrumentation examples and anti-patterns + **VERIFY**: Run `./mvnw clean verify` or `mvn clean verify` after changes + + + + + + Improve metrics + Apply Micrometer + Add metrics observability + Refactor Micrometer instrumentation + + + + + + Define measurement goals and meter contract + Identify key service indicators (throughput, latency, error ratio, saturation) and map each to stable metric names, units, and low-cardinality tags. + + + Select meter types and instrument code paths + Apply Counter/Timer/Gauge/DistributionSummary/LongTaskTimer where appropriate, ensuring consistent naming conventions and reusable tags. + + + Harden instrumentation for production + Control cardinality, avoid dynamic meter churn, configure histogram/percentile strategy only where needed, and align export settings with the telemetry backend. + + + Validate and operationalize metrics + Verify metrics in tests and runtime endpoints, confirm expected labels/units, and ensure dashboards/alerts can consume the emitted series. + + + diff --git a/skills-generator/src/main/resources/skill-indexes/183-skill.xml b/skills-generator/src/main/resources/skill-indexes/183-skill.xml new file mode 100644 index 00000000..7e7a89a9 --- /dev/null +++ b/skills-generator/src/main/resources/skill-indexes/183-skill.xml @@ -0,0 +1,67 @@ + + + + Juan Antonio Breña Moral + 0.15.0-SNAPSHOT + Apache-2.0 + Use when you need to implement or improve distributed tracing with OpenTelemetry in Java — including trace/span modeling, context propagation, semantic conventions, span attributes/events/status, sampling strategy, baggage usage, privacy safeguards, and backend integration with OTLP collectors. This should trigger for requests such as Improve tracing; Apply OpenTelemetry tracing; Add distributed tracing; Refactor tracing instrumentation. + + + Java Distributed Tracing with OpenTelemetry + + + + Tracing instrumentation must preserve context correctly and avoid leaking sensitive data. Over-instrumentation and high-cardinality attributes can harm cost and signal quality. + + **PROPAGATION FIRST**: Ensure context propagation across all sync/async boundaries before adding extra span detail + **NO SENSITIVE DATA**: Never store secrets, credentials, tokens, raw payloads, or PII in span attributes/events + **LOW CARDINALITY ATTRIBUTES**: Avoid unbounded values in attributes that are used for aggregation/search + **VERIFY**: Run `./mvnw clean verify` or `mvn clean verify` after applying tracing changes + + + + + + Improve tracing + Apply OpenTelemetry tracing + Add distributed tracing + Refactor tracing instrumentation + + + + + + Define trace model and critical flows + Identify high-value request and async flows, define operation boundaries, and choose span names/attributes aligned with semantic conventions. + + + Instrument and propagate context + Add OpenTelemetry spans to key boundaries and ensure trace context is propagated across HTTP clients/servers, messaging, and executor-based async work. + + + Harden span data and sampling + Record status/errors/events consistently, remove sensitive data, control attribute cardinality, and configure sampling/exporters according to environment needs. + + + Validate traces end-to-end + Verify parent-child relationships, propagation continuity, and backend visibility through tests and runtime checks. + + + diff --git a/skills-generator/src/main/resources/skill-references/180-java-observability-logging.xml b/skills-generator/src/main/resources/skill-references/180-java-observability-logging.xml deleted file mode 100644 index ec4fbf08..00000000 --- a/skills-generator/src/main/resources/skill-references/180-java-observability-logging.xml +++ /dev/null @@ -1,1432 +0,0 @@ - - - - - Juan Antonio Breña Moral - 0.15.0-SNAPSHOT - Apache-2.0 - Java Logging Best Practices - Use when you need to implement or improve Java logging and observability — including selecting SLF4J with Logback/Log4j2, applying proper log levels, parameterized logging, secure logging without sensitive data exposure, environment-specific configuration, log aggregation and monitoring, or validating logging through tests. - - - You are a Senior software engineer with extensive experience in Java software development - - - Effective Java logging involves selecting a standard framework (SLF4J with Logback/Log4j2), using appropriate log levels (ERROR, WARN, INFO, DEBUG, TRACE), - and adhering to core practices like parameterized logging, proper exception handling, and avoiding sensitive data exposure. - Configuration should be environment-specific with clear output formats. - Security is paramount: mask sensitive data, control log access, and ensure secure transmission. - Implement centralized log aggregation, monitoring, and alerting for proactive issue detection. - Finally, logging behavior and its impact should be validated through comprehensive testing. - - ### Implementing These Principles - - These guidelines are built upon the following core principles: - - 1. **Standardized Framework Selection**: Utilize a widely accepted logging facade (preferably SLF4J) and a robust underlying implementation (Logback or Log4j2). This promotes consistency, flexibility, and access to advanced logging features. - 2. **Meaningful and Consistent Log Levels**: Employ logging levels (ERROR, WARN, INFO, DEBUG, TRACE) deliberately and consistently to categorize the severity and importance of messages. This allows for effective filtering, monitoring, and targeted issue diagnosis. - 3. **Adherence to Core Logging Practices**: Follow fundamental best practices such as using parameterized logging (avoiding string concatenation for performance and clarity), always logging exceptions with their stack traces, never logging sensitive data directly (PII, credentials). - 4. **Thoughtful and Flexible Configuration**: Manage logging configuration externally (e.g., `logback.xml`, `log4j2.xml`). Tailor configurations for different environments (dev, test, prod) with appropriate log levels for various packages, clear and informative output formats (including timestamps, levels, logger names, thread info), and robust log rotation and retention policies. - 5. **Security-Conscious Logging**: Prioritize security in all logging activities. Actively mask or filter sensitive information, control access to log files and log management systems, use secure protocols for transmitting logs, and ensure compliance with relevant data protection regulations (e.g., GDPR, HIPAA). - 6. **Proactive Log Monitoring and Alerting**: Implement centralized log aggregation systems (e.g., ELK Stack, Splunk, Grafana Loki). Establish automated alerts based on log patterns, error rates, or specific critical events to enable proactive issue detection and rapid response. - 7. **Comprehensive Logging Validation Through Testing**: Integrate logging into the testing strategy. Assert that critical log messages (especially errors and warnings) are generated as expected under specific conditions, verify log formats, test log level filtering, and assess any performance impact of logging. - - Remember, good logging in Java is about operational excellence - making your application's behavior transparent and debuggable while maintaining security and performance. - - - - - Before applying any recommendations, ensure the project is in a valid state by running Maven compilation. - Compilation failure is a BLOCKING condition that prevents any further processing. - - - **MANDATORY**: Run `./mvnw compile` or `mvn compile` before applying any change - **PREREQUISITE**: Project must compile successfully and pass basic validation checks before any optimization - **CRITICAL SAFETY**: If compilation fails, IMMEDIATELY STOP and DO NOT CONTINUE with any recommendations - **BLOCKING CONDITION**: Compilation errors must be resolved by the user before proceeding with any object-oriented design improvements - **NO EXCEPTIONS**: Under no circumstances should design recommendations be applied to a project that fails to compile - - - - - - - - Choose an Appropriate Logging Framework - Select a Standard Logging Facade and Implementation - - - Using a standard logging facade like SLF4J allows for flexibility in choosing and switching an underlying logging implementation. The primary recommendation is SLF4J with Logback for its robustness and feature-richness. - - - - - - - - - - - - - - Understand and Use Logging Levels Correctly - Apply Appropriate Logging Levels for Messages - - - Use logging levels consistently to categorize the severity and importance of log messages. ERROR for critical issues, WARN for potentially harmful situations, INFO for important business events, DEBUG for detailed information, and TRACE for fine-grained debugging. - - - - - - - - - - - - - - Adhere to Core Logging Practices - Implement Fundamental Best Practices - - - Follow core practices including using parameterized logging, proper exception handling, avoiding sensitive data exposure, and implementing performance considerations for logging operations. - - - - dataItems) { - logger.info("Processing {} data items", dataItems.size()); - - for (int i = 0; i < dataItems.size(); i++) { - String item = dataItems.get(i); - - try { - processDataItem(item); - - // GOOD: Log progress periodically, not for every item - if (i % 1000 == 0) { - logger.debug("Processed {} of {} items", i, dataItems.size()); - } - - } catch (Exception e) { - // GOOD: Log error but continue processing - logger.warn("Failed to process item at index {}: {}", i, item, e); - } - } - - logger.info("Completed processing {} data items", dataItems.size()); - } - - // Utility methods - private String maskCreditCard(String cardNumber) { - if (cardNumber == null || cardNumber.length() < 8) { - return "****"; - } - return cardNumber.substring(0, 4) + "****" + cardNumber.substring(cardNumber.length() - 4); - } - - private String generateCorrelationId() { - return "TXN-" + System.currentTimeMillis() + "-" + Thread.currentThread().getId(); - } - - private String buildComplexDebugInfo(String userId, String amount) { - // Simulate expensive debug information building - return String.format("User: %s, Amount: %s, Timestamp: %d", - userId, amount, System.currentTimeMillis()); - } - - private void validatePaymentRequest(String userId, String amount, String cardNumber) - throws ValidationException { - if (Objects.isNull(userId) || userId.trim().isEmpty()) { - throw new ValidationException("INVALID_USER_ID"); - } - // More validation... - } - - private void processPaymentInternal(String userId, String amount, String cardNumber) - throws PaymentProcessingException { - // Simulate payment processing - if ("fail".equals(userId)) { - throw new PaymentProcessingException("Payment gateway error"); - } - } - - private void processDataItem(String item) { - // Simulate data processing - if ("error".equals(item)) { - throw new RuntimeException("Processing failed for item: " + item); - } - } - - // Exception classes - private static class ValidationException extends Exception { - private final String validationError; - - public ValidationException(String validationError) { - super("Validation failed: " + validationError); - this.validationError = validationError; - } - - public String getValidationError() { - return validationError; - } - } - - private static class PaymentProcessingException extends RuntimeException { - public PaymentProcessingException(String message) { - super(message); - } - - public PaymentProcessingException(String message, Throwable cause) { - super(message, cause); - } - } -}]]> - - - items) { - // BAD: Logging every single item in large dataset - for (String item : items) { - logger.debug("Processing item: " + item); // Will flood logs - processItem(item); - logger.debug("Completed item: " + item); // Even more noise - } - } - - public void handleUserLogin(String username, String password) { - // BAD: Logging passwords - NEVER do this! - logger.debug("User login attempt: username=" + username + ", password=" + password); - - try { - authenticateUser(username, password); - // BAD: Inconsistent logging format - logger.info("User " + username + " logged in successfully"); - } catch (AuthenticationException e) { - // BAD: Using wrong log level and exposing sensitive info - logger.error("Login failed for " + username + " with password " + password); - } - } - - public void performDatabaseOperation(String query, String connectionString) { - // BAD: Logging database connection strings (may contain credentials) - logger.debug("Executing query: " + query + " on connection: " + connectionString); - - try { - executeQuery(query); - } catch (SQLException e) { - // BAD: Not using parameterized logging with exception - logger.error("SQL error: " + e.getMessage() + " for query: " + query); - // Stack trace is lost! - } - } - - // BAD: No try-with-resources or proper cleanup for MDC - public void badMDCUsage(String userId) { - org.slf4j.MDC.put("userId", userId); - logger.info("Processing for user"); - - // ... some processing ... - - // BAD: Forgot to clear MDC - memory leak! - // MDC.clear() or MDC.remove("userId") is missing - } - - // Helper methods with poor exception handling - private String buildExpensiveDebugString(String userId, String amount, String cardNumber) { - // Simulate expensive operation - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 10000; i++) { - sb.append("Debug info for ").append(userId).append(" "); - } - return sb.toString(); - } - - private void validatePayment(String userId, String amount) throws ValidationException { - if (userId == null) throw new ValidationException("Invalid user"); - } - - private void processPaymentTransaction(String userId, String amount, String cardNumber) { - // Simulate processing - } - - private void processItem(String item) { - // Simulate processing - } - - private void authenticateUser(String username, String password) throws AuthenticationException { - if ("baduser".equals(username)) { - throw new AuthenticationException("Invalid credentials"); - } - } - - private void executeQuery(String query) throws SQLException { - if (query.contains("DROP")) { - throw new SQLException("Invalid query"); - } - } - - // Exception classes - private static class ValidationException extends Exception { - public ValidationException(String message) { super(message); } - } - private static class AuthenticationException extends Exception { - public AuthenticationException(String message) { super(message); } - } - private static class SQLException extends Exception { - public SQLException(String message) { super(message); } - } -}]]> - - - - - - - Follow Configuration Best Practices - Configure Your Logging Framework Thoughtfully - - - Proper configuration is key to effective logging. Use separate configurations per environment, implement different log levels for different packages, and include comprehensive output formats with timestamps, levels, logger names, and thread information. - - - - - - - - - - - - - - Implement Secure Logging Practices - Ensure Logs Do Not Compromise Security - - - Actively mask or filter sensitive data, control access to log files, use secure transmission protocols, and comply with data protection regulations when logging information. - - - - \"'&]", "*"); - - // Limit length to prevent log flooding - if (sanitized.length() > 100) { - sanitized = sanitized.substring(0, 97) + "..."; - } - - return sanitized; - } - - private String maskEmail(String email) { - if (email == null || !EMAIL_PATTERN.matcher(email).matches()) { - return "[INVALID_EMAIL]"; - } - - int atIndex = email.indexOf('@'); - if (atIndex < 2) { - return "**@" + email.substring(atIndex + 1); - } - - return email.substring(0, 2) + "***@" + email.substring(atIndex + 1); - } - - private String maskCreditCard(String cardNumber) { - if (cardNumber == null || cardNumber.length() < 8) { - return "[INVALID_CARD]"; - } - - String digitsOnly = cardNumber.replaceAll("[^\\d]", ""); - if (digitsOnly.length() < 8) { - return "[INVALID_CARD]"; - } - - return digitsOnly.substring(0, 4) + "****" + - digitsOnly.substring(digitsOnly.length() - 4); - } - - private String hashSensitiveData(String data) { - if (data == null) return "[null]"; - - // Use a secure hash for audit purposes (not for security) - return "HASH_" + Math.abs(data.hashCode()); - } - - private String generateSecureCorrelationId() { - return "CORR_" + System.currentTimeMillis() + "_" + - Thread.currentThread().getId(); - } - - // Mock classes and methods - private void validateUserData(String username, String email, String ssn, String creditCard) - throws ValidationException { - if (username == null || username.trim().isEmpty()) { - throw new ValidationException("INVALID_USERNAME"); - } - } - - private void createUserAccount(String username, String email) { - // Account creation logic - } - - private void validatePaymentMethod(String paymentMethod) { - // Payment validation logic - } - - private ChargeResult processCharge(PaymentRequest request) throws PaymentException { - // Payment processing logic - return new ChargeResult("TXN_" + System.currentTimeMillis()); - } - - // Mock classes - private static class PaymentRequest { - private String amount = "100.00"; - private String paymentMethod = "card"; - private String merchantId = "MERCHANT_123"; - - public String getAmount() { return amount; } - public String getPaymentMethod() { return paymentMethod; } - public String getMerchantId() { return merchantId; } - } - - private static class ChargeResult { - private final String transactionId; - public ChargeResult(String transactionId) { this.transactionId = transactionId; } - public String getTransactionId() { return transactionId; } - } - - private static class ValidationException extends Exception { - private final String errorCode; - public ValidationException(String errorCode) { - super("Validation failed: " + errorCode); - this.errorCode = errorCode; - } - public String getErrorCode() { return errorCode; } - } - - private static class SecurityException extends Exception { - private final String violationType; - public SecurityException(String violationType) { - super("Security violation: " + violationType); - this.violationType = violationType; - } - public String getViolationType() { return violationType; } - } - - private static class FraudDetectedException extends Exception { - private final int riskScore; - public FraudDetectedException(int riskScore) { - super("Fraud detected with risk score: " + riskScore); - this.riskScore = riskScore; - } - public int getRiskScore() { return riskScore; } - } - - private static class PaymentException extends Exception { - private final String errorType; - public PaymentException(String errorType) { - super("Payment error: " + errorType); - this.errorType = errorType; - } - public String getErrorType() { return errorType; } - } -}]]> - - - - - - - - - - Establish Effective Log Monitoring and Alerting - Implement Log Aggregation, Monitoring, and Alerting - - - Set up centralized log aggregation, implement alerts based on log patterns, use structured logging for querying, and regularly analyze logs to identify trends and issues. - - - - ")) { - // Log a security-sensitive event that monitoring tools can pick up for alerts. - logger.warn("SECURITY_ALERT: Potential XSS attempt detected in payload for endpoint: {}", endpoint); - // Handle error, e.g., return 400 Bad Request - return; - } - - // Simulate some processing - if (endpoint.equals("/critical_op")) { - if (Math.random() > 0.9) { // Simulate a sporadic critical failure - throw new RuntimeException("Critical operation failure detected!"); - } - } - - logger.info("API request processed successfully"); - - // Structured logging for metrics and monitoring - logger.info("METRICS: endpoint={}, duration={}, status=success", endpoint, System.currentTimeMillis() % 1000); - - } catch (RuntimeException e) { - // This ERROR log (especially with stack trace) would be a key candidate for alerting. - logger.error("ALERT: Unhandled exception during API request processing", e); - // Could trigger immediate alert in monitoring system - } finally { - MDC.clear(); - } - } - - public void performHealthCheck() { - try { - // Simulate health checks - checkDatabase(); - checkExternalService(); - - logger.info("HEALTH_CHECK: All systems operational"); - - } catch (Exception e) { - logger.error("HEALTH_CHECK: System health check failed", e); - // This would trigger alerts for operational teams - } - } - - private void checkDatabase() { - // Simulate database check - if (Math.random() > 0.95) { - throw new RuntimeException("Database connection failed"); - } - } - - private void checkExternalService() { - // Simulate external service check - if (Math.random() > 0.98) { - throw new RuntimeException("External service unavailable"); - } - } - - public static void main(String[] args) { - MonitoredService service = new MonitoredService(); - - // These logs would be searchable in monitoring systems by: - // - requestId for request tracing - // - endpoint for API-specific analysis - // - SECURITY_ALERT for security incident detection - // - HEALTH_CHECK for system monitoring - // - METRICS for performance analysis - - service.handleApiRequest("/user_data", "{\"data\": \"normal\"}"); - service.handleApiRequest("/submit_form", "payload with "); - - for (int i = 0; i < 20; i++) { - service.handleApiRequest("/critical_op", "some_data"); - } - - service.performHealthCheck(); - } -}]]> - - - 0.5) { - throw new Exception("Something went wrong randomly!"); - } - logger.info("Work finished."); - } catch (Exception e) { - // Logs an error, but if logs are only on local disk and not monitored, - // this critical issue might go unnoticed for a long time. - logger.error("Error during work", e); - } - } - - public static void main(String[] args) { - UnmonitoredService service = new UnmonitoredService(); - for (int i = 0; i < 5; i++) { - service.doWork(); - } - // Problem: Logs are likely just going to console or a local file. - // - No central aggregation: Difficult to search across instances or time. - // - No alerts: Critical errors might be missed until users report them. - // - No analysis: Trends or recurring non-fatal issues are hard to spot. - } -}]]> - - - - - - - Incorporate Logging in Testing - Validate Logging Behavior and Impact During Testing - - - Ensure logging works as expected and doesn't negatively impact the application. Assert that specific log messages are generated, verify log formats, test different logging levels, and check performance impact. - - - - - - - - - - - - - - - **ANALYZE** Java code to identify specific logging issues and categorize them by impact (CRITICAL, SECURITY, PERFORMANCE, MAINTAINABILITY, OBSERVABILITY) and logging area (framework selection, log levels, security, configuration, performance) - **CATEGORIZE** logging improvements found: Framework Issues (inconsistent frameworks vs standardized SLF4J, outdated implementations vs modern logging), Log Level Problems (inappropriate levels vs proper level usage, missing contextual information vs structured logging), Security Issues (sensitive data exposure vs secure logging practices, plaintext credentials vs masked data), Configuration Problems (hardcoded settings vs environment-specific configuration, missing structured formats vs JSON/structured output), and Performance Issues (synchronous logging vs asynchronous patterns, excessive logging overhead vs optimized performance, string concatenation vs parameterized messages) - **APPLY** logging best practices directly by implementing the most appropriate improvements for each identified issue: Standardize on SLF4J facade with appropriate implementation (Logback/Log4j2), implement proper log level usage with contextual information, secure sensitive data through masking and filtering, configure environment-specific settings with external configuration files, optimize performance through asynchronous logging and parameterized messages, and establish structured logging with JSON output for better parsing and analysis - **IMPLEMENT** comprehensive logging refactoring using proven patterns: Migrate to SLF4J facade with consistent logger declarations, establish proper log level hierarchy (ERROR for failures, WARN for degraded service, INFO for business events, DEBUG for troubleshooting), implement secure logging practices with data masking and filtering, configure environment-specific logging with external properties, optimize performance through async appenders and lazy evaluation, and integrate structured logging with correlation IDs and contextual metadata - **REFACTOR** code systematically following the logging improvement roadmap: First standardize logging framework usage through SLF4J adoption, then optimize log level usage and add proper contextual information, implement security measures for sensitive data protection, configure environment-specific logging settings with external configuration, optimize logging performance through asynchronous patterns and parameterized messages, and establish comprehensive monitoring and alerting integration - **EXPLAIN** the applied logging improvements and their benefits: Framework standardization benefits through SLF4J facade adoption and consistent logger usage, observability enhancements via proper log levels and structured output, security improvements through sensitive data masking and secure logging practices, performance optimizations from asynchronous logging and parameterized messages, and operational benefits from environment-specific configuration and monitoring integration - **VALIDATE** that all applied logging refactoring compiles successfully, maintains existing functionality, eliminates security vulnerabilities, follows logging best practices, and achieves the intended observability and performance improvements through comprehensive testing and verification - - - - - - **BLOCKING SAFETY CHECK**: ALWAYS run `./mvnw compile` before ANY logging recommendations - **CRITICAL VALIDATION**: Execute `./mvnw clean verify` to ensure all tests pass after logging changes - **SECURITY VERIFICATION**: Validate that no sensitive data (passwords, PII, tokens) is logged directly - **PERFORMANCE MONITORING**: Ensure logging configurations don't introduce significant performance overhead - **CONFIGURATION VALIDATION**: Verify logging configuration files are syntactically correct and environment-appropriate - **ROLLBACK READINESS**: Ensure all logging changes can be easily reverted without system disruption - **INCREMENTAL SAFETY**: Apply logging improvements incrementally, validating after each modification - **LOG LEVEL VERIFICATION**: Confirm log levels are appropriate for production environments (avoid DEBUG/TRACE in prod) - **DEPENDENCY COMPATIBILITY**: Verify logging framework dependencies don't conflict with existing project dependencies - **OPERATIONAL CONTINUITY**: Ensure logging changes don't break existing monitoring, alerting, or log aggregation systems - - - diff --git a/skills-generator/src/main/resources/skill-references/181-java-observability-logging.xml b/skills-generator/src/main/resources/skill-references/181-java-observability-logging.xml new file mode 100644 index 00000000..bc7dd612 --- /dev/null +++ b/skills-generator/src/main/resources/skill-references/181-java-observability-logging.xml @@ -0,0 +1,91 @@ + + + + + Juan Antonio Breña Moral + 0.15.0-SNAPSHOT + Apache-2.0 + Java Logging Best Practices + Use when you need to implement or improve Java logging and observability — including selecting SLF4J with Logback/Log4j2, applying proper log levels, parameterized logging, secure logging without sensitive data exposure, environment-specific configuration, log aggregation and monitoring, or validating logging through tests. + + + You are a Senior software engineer with extensive experience in Java software development + + + Effective Java logging requires a consistent framework (SLF4J facade with Logback/Log4j2), + deliberate log level usage (ERROR, WARN, INFO, DEBUG, TRACE), and secure operational practices. + Logging must provide useful diagnostics, avoid sensitive data leakage, and stay sustainable in production. + + + + + Logging improvements must preserve security and operational stability. + + + **NO SENSITIVE DATA**: Never log passwords, tokens, secrets, or personal data in plain text + **LEVEL DISCIPLINE**: Use consistent levels and avoid noisy INFO/DEBUG misuse in hot paths + **PARAMETERIZED LOGGING**: Prefer placeholders instead of string concatenation + **VALIDATE**: Confirm log behavior through tests and run full verification after changes + + + + + + + + Use Structured and Parameterized Logging + Prefer stable templates and explicit context + + + + + + + + + + + + + + + **ANALYZE** logging issues by level semantics, framework usage, and security risk + **APPLY** SLF4J-based structured logging and replace anti-patterns + **EXPLAIN** operational impact of each logging improvement + **VALIDATE** compilation/tests and verify no sensitive data is logged + + + + + + **SECURITY CHECK**: redact or remove sensitive fields before logging + **NOISE CONTROL**: avoid excessive logs in loops and performance-critical paths + **CONSISTENCY**: keep logger naming, formats, and level usage uniform + + + diff --git a/skills-generator/src/main/resources/skill-references/182-java-observability-metrics-micrometer.xml b/skills-generator/src/main/resources/skill-references/182-java-observability-metrics-micrometer.xml new file mode 100644 index 00000000..4a028ded --- /dev/null +++ b/skills-generator/src/main/resources/skill-references/182-java-observability-metrics-micrometer.xml @@ -0,0 +1,492 @@ + + + + + Juan Antonio Breña Moral + 0.15.0-SNAPSHOT + Apache-2.0 + Java Metrics Observability with Micrometer + Use when you need to implement or improve Java metrics observability with Micrometer — including meter design, naming/tag conventions, cardinality control, timers/counters/gauges/distribution summaries, percentiles/histograms, Actuator/Prometheus integration, and metrics validation through tests. + + + You are a Senior software engineer with extensive experience in Java software development + + + Effective Java metrics observability with Micrometer starts by defining a clear metric contract for service-level signals: + throughput, latency, errors, and saturation. + Instrumentation must be stable over time, use semantic names, and avoid unbounded labels. + The right meter type (Counter, Timer, Gauge, DistributionSummary, LongTaskTimer) should match the behavior being measured. + Histograms and percentiles should be enabled selectively to control overhead and cost. + Finally, metrics should be validated through tests and runtime checks to ensure dashboards and alerts remain reliable. + + + + + Metrics instrumentation must avoid high-cardinality dimensions and dynamic meter churn. + Uncontrolled tagging can overload telemetry backends and make observability unreliable. + + + **CARDINALITY SAFETY**: Never use user-controlled, unique, or high-cardinality tag values (UUID, email, request id, full path with IDs) + **METER SEMANTICS**: Select meter types based on behavior (Counter for events, Timer for latency, Gauge for state, DistributionSummary for value distributions) + **INSTRUMENTATION SCOPE**: Prefer business and platform signals tied to SLOs over noisy low-value metrics + **VALIDATION**: Verify expected metrics names, tags, and values through automated tests and runtime checks + + + + + + + + Select the Right Meter Type + Use Counters, Timers, Gauges and Summaries with Correct Semantics + + + + + + + + + + + + + + Use Stable Naming and Low-Cardinality Tags + Keep Metric Schema Predictable for Dashboards and Alerts + + + + ALLOWED_RESULTS = Set.of("approved", "declined", "error"); + private final MeterRegistry registry; + + public PaymentMetrics(MeterRegistry registry) { + this.registry = registry; + } + + public void recordResult(String provider, String result) { + String safeProvider = normalizeProvider(provider); // bounded set + String safeResult = ALLOWED_RESULTS.contains(result) ? result : "error"; + + Counter.builder("payment.authorization.requests") + .tag("provider", safeProvider) + .tag("result", safeResult) + .register(registry) + .increment(); + } + + private String normalizeProvider(String provider) { + String normalized = provider == null ? "" : provider.toLowerCase(Locale.ROOT); + return switch (normalized) { + case "adyen", "stripe", "paypal" -> normalized; + default -> "unknown"; + }; + } +} +]]> + + + + + + + + + + Validate Instrumentation with Tests + Assert Metric Presence, Tags, and Values + + + + + + + + + + + + + + Track Long-Running Jobs with LongTaskTimer + Measure Active Task Duration and Concurrency for Batch and Background Work + + + + + + + + + + + + + + Configure Histograms and Percentiles Selectively + Enable SLO-Oriented Buckets and Percentiles Only Where They Add Value + + + + + + + + + + + + + + Expose Metrics via Spring Boot Actuator and Prometheus + Configure Scrape Endpoint, Common Tags, and Prometheus Naming + + + + + + + commonTags() { + // Apply common tags to all meters in addition to application.yml tags + return registry -> registry.config() + .commonTags("region", System.getenv().getOrDefault("REGION", "unknown")); + } +} +]]> + + + + + + + + + + Use Annotation-Based Observation for Service Methods + Prefer @Observed with low-cardinality key values for concise instrumentation + + + + + + + + + + + + + + + **ANALYZE** current instrumentation and classify issues by cardinality, semantic correctness, naming consistency, and operational value + **DESIGN** a stable metric contract: names, units, bounded tags, meter types, and where each metric is emitted + **APPLY** Micrometer best practices directly in code with minimal, explicit, and reusable instrumentation points + **EXPLAIN** how each metric supports dashboards, alerts, and SLO/SLA reporting + **VALIDATE** correctness through tests and runtime endpoint checks, including expected labels and values + + + + + + **CARDINALITY GUARDRAIL**: Reject unbounded labels and normalize dynamic values into bounded buckets + **NO METER CHURN**: Avoid building meters in tight loops with dynamic names/tags + **BACKEND COMPATIBILITY**: Ensure names and tags remain compatible with Prometheus/OpenTelemetry conventions + **OPERABILITY CHECK**: Confirm metrics are scrapeable/exported and usable in dashboards and alerts + **REGRESSION SAFETY**: Keep existing metric names stable unless migration is explicitly planned + + + diff --git a/skills-generator/src/main/resources/skill-references/183-observability-tracing-opentelemetry.xml b/skills-generator/src/main/resources/skill-references/183-observability-tracing-opentelemetry.xml new file mode 100644 index 00000000..725255ee --- /dev/null +++ b/skills-generator/src/main/resources/skill-references/183-observability-tracing-opentelemetry.xml @@ -0,0 +1,442 @@ + + + + + Juan Antonio Breña Moral + 0.15.0-SNAPSHOT + Apache-2.0 + Java Distributed Tracing with OpenTelemetry + Use when you need to implement or improve distributed tracing with OpenTelemetry in Java — including trace/span modeling, context propagation, semantic conventions, span attributes/events/status, sampling strategy, baggage usage, privacy safeguards, and backend integration with OTLP collectors. + + + You are a Senior software engineer with extensive experience in Java software development + + + Effective distributed tracing with OpenTelemetry in Java requires clear span boundaries, reliable context propagation, + semantic consistency, and strict privacy controls. + Traces should help diagnose latency, failure causes, and cross-service dependencies without introducing excessive overhead. + + + + + Tracing data must remain safe and operationally useful. Incorrect propagation or sensitive attributes reduce trust in telemetry. + + + **CONTEXT CONTINUITY**: Ensure trace context is propagated across HTTP/messaging/async boundaries + **SEMANTIC CONSISTENCY**: Use stable span names and OpenTelemetry semantic conventions where applicable + **DATA MINIMIZATION**: Avoid high-cardinality and sensitive attributes in spans/events + **SAMPLING STRATEGY**: Configure sampling per environment to balance cost and diagnostic depth + + + + + + + + Create Spans with Clear Boundaries + Model request and dependency calls as parent/child spans + + + + + + + + + + + + + + Propagate Context and Keep Attributes Safe + Ensure continuity and avoid cardinality/privacy problems + + + + { + Span.current().addEvent("async.task.started"); + // do work with the captured parent trace context + })); + } +} +]]> + + + + + + + + + + Apply OpenTelemetry Semantic Conventions + Use Standard Attribute Names for HTTP, Database, and Messaging Spans + + + + + + + + + + + + + + Configure Sampling Strategy per Environment + Balance Diagnostic Depth Against Cost and Overhead + + + + Sampler.traceIdRatioBased(0.05); // 5% in prod + case "staging" -> Sampler.traceIdRatioBased(0.50); // 50% in staging + default -> Sampler.alwaysOn(); // 100% in dev/test + }; + + SdkTracerProvider tracerProvider = SdkTracerProvider.builder() + .setSampler(Sampler.parentBased(sampler)) + .addSpanProcessor(BatchSpanProcessor.builder( + OtlpGrpcSpanExporter.builder() + .setEndpoint("http://otel-collector:4317") + .build()) + .build()) + .build(); + + return OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .buildAndRegisterGlobal(); + } +} +]]> + + + + + + + + + + Validate Span Correctness with Tests + Assert Span Name, Kind, Status, and Attributes Using the In-Memory Exporter + + + + spans = exporter.getFinishedSpanItems(); + assertThat(spans).hasSizeGreaterThanOrEqualTo(1); + + SpanData root = spans.stream() + .filter(s -> s.getName().equals("checkout.process")) + .findFirst() + .orElseThrow(); + + assertThat(root.getKind()).isEqualTo(SpanKind.INTERNAL); + assertThat(root.getStatus().getStatusCode()).isEqualTo(StatusCode.OK); + assertThat(root.getAttributes().get(AttributeKey.booleanKey("order.id.present"))).isTrue(); + assertThat(root.getAttributes().asMap()).doesNotContainKey(AttributeKey.stringKey("order.id")); + } +} +]]> + + + + + + + + + + Use Annotation-Based Instrumentation for Service Methods + Leverage @WithSpan and @SpanAttribute for concise, consistent tracing + + + + + + + + + + + + + + + **ANALYZE** tracing gaps across span modeling, propagation, and semantic consistency + **APPLY** OpenTelemetry instrumentation with clear parent/child relationships and stable names + **HARDEN** attributes/events/status and remove sensitive or high-cardinality data + **VALIDATE** end-to-end trace continuity in tests and runtime backends + + + + + + **PRIVACY FIRST**: Never place secrets or PII in trace payloads + **PROPAGATION CHECK**: Verify traceId/spanId continuity across service boundaries + **CARDINALITY CONTROL**: Keep attribute values bounded and query-friendly + **PERF AWARENESS**: Avoid unnecessary spans in hot loops and configure sampling appropriately + + + diff --git a/skills-generator/src/main/resources/skill-references/assets/java-system-prompts-java-list-template.md b/skills-generator/src/main/resources/skill-references/assets/java-system-prompts-java-list-template.md index 544719f9..18575b0a 100644 --- a/skills-generator/src/main/resources/skill-references/assets/java-system-prompts-java-list-template.md +++ b/skills-generator/src/main/resources/skill-references/assets/java-system-prompts-java-list-template.md @@ -41,7 +41,7 @@ Use the following collection of Skills for Java to improve your Java development | [043-planning-github-issues](@043-planning-github-issues.md) | List GitHub issues (all or by milestone), fetch issue bodies and comments with `gh`, present tables; hand off to user stories | **User Prompt:** `List open issues in this repo as a table using @043-planning-github-issues` **User Prompt:** `Get all issues for milestone "Sprint 12" with @043-planning-github-issues` **User Prompt:** `Pull issue #44 description and comments, then draft a user story with @014-agile-user-story` **Note:** Requires GitHub CLI (`gh`) installed and authenticated. | Pairs with `@014-agile-user-story` when turning GitHub threads into user stories and Gherkin. | | [044-planning-jira](@044-planning-jira.md) | List Jira issues (all or by JQL), fetch issue descriptions and comments with `jira`, present tables; hand off to user stories | **User Prompt:** `List open Jira issues as a table using @044-planning-jira` **User Prompt:** `Get Jira issues with JQL "project = PROJ AND statusCategory != Done" using @044-planning-jira` **User Prompt:** `Pull issue PROJ-44 description and comments, then draft a user story with @014-agile-user-story` **Note:** Requires Jira CLI (`jira`) installed and configured. | Pairs with `@014-agile-user-story` when turning Jira threads into user stories and Gherkin. | -## Build system rules (Maven) +## Build system skills (Maven) | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -51,24 +51,31 @@ Use the following collection of Skills for Java to improve your Java development | [113-java-maven-documentation](@113-java-maven-documentation.md) | Create a Maven Documentation with the file `README-DEV.md` | **User Prompt:** `Generate developer documentation with essential Maven commands using @113-java-maven-documentation` **Note:** Add in the context the `pom.xml` which you want to generate the documentation. | This skill is applied automatically without any interaction with the Software engineer. | | [114-java-maven-search](@114-java-maven-search.md) | Search Maven Central, resolve coordinates, version metadata, and direct artifact URLs | **User Prompt:** `Find the latest version of com.google.guava:guava using @114-java-maven-search` **Note:** Use for dependency lookup, POM/JAR URLs, `maven-metadata.xml`, or Search API queries — not for editing `pom.xml` (use `@111` / `@112` for that). | Non-interactive. Use MCP Maven tools when available for live Central queries. | -## Design rules +## Design skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [121-java-object-oriented-design](@121-java-object-oriented-design.md) | Take another point of view with an Object Oriented Design of your development | **User prompt:** `Improve the class/classes added in the context applying the system prompt @121-java-object-oriented-design`(Example) **Note:** Add in the context a class/classes to improve the design. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @121-java-object-oriented-design with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [122-java-type-design](@122-java-type-design.md) | Review the Type Design in your development | **User prompt:** `Improve the class/classes added in the context applying the system prompt @122-java-type-design` (Example) **Note:** Add in the context a class/classes to improve the design. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @122-java-type-design with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | -## Coding rules +## Coding skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [123-java-exception-handling](@123-java-exception-handling.md) | Add Exception handling | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @123-java-exception-handling` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @123-java-exception-handling with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [124-java-secure-coding](@124-java-secure-coding.md) | Review my code for Secure Java Coding rules | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @124-java-secure-coding` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @124-java-secure-coding with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [125-java-concurrency](@125-java-concurrency.md) | Improve your code with Concurrency rules | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @125-java-concurrency` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @125-java-concurrency with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | -| [180-java-observability-logging](@180-java-observability-logging.md) | Apply logging guidelines in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @180-java-observability-logging` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @180-java-observability-logging with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [128-java-generics](@128-java-generics.md) | Apply generics in a class | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @128-java-generics` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @128-java-generics with the behaviour @behaviour-consultative-interaction` **User Prompt with Training behaviour:** `Create a course about @128-java-generics.md using the behavior @behaviour-progressive-learning.md and put the course here` **Note:** Add in the context the location to add the course. | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | -## Testing rules +## Observability skills + +| Skill | Description | Prompt | Notes | +|----|----|----|----| +| [181-java-observability-logging](@181-java-observability-logging.md) | Apply logging guidelines in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @181-java-observability-logging` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @181-java-observability-logging with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | +| [182-java-observability-metrics-micrometer](@182-java-observability-metrics-micrometer.md) | Apply Micrometer metrics observability best practices in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @182-java-observability-metrics-micrometer` (Example) **Note:** Add in the context a class/classes where metrics are emitted. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @182-java-observability-metrics-micrometer with the behaviour @behaviour-consultative-interaction` | Focused on metrics design, meter semantics, naming/tag conventions, and cardinality-safe instrumentation. | +| [183-observability-tracing-opentelemetry](@183-observability-tracing-opentelemetry.md) | Apply OpenTelemetry distributed tracing best practices in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @183-observability-tracing-opentelemetry` (Example) **Note:** Add in the context code paths where tracing and propagation are relevant. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @183-observability-tracing-opentelemetry with the behaviour @behaviour-consultative-interaction` | Focused on span modeling, context propagation, semantic conventions, and privacy-safe tracing attributes. | + +## Testing skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -77,7 +84,7 @@ Use the following collection of Skills for Java to improve your Java development | [132-java-testing-integration-testing](@132-java-testing-integration-testing.md) | Set up integration test infrastructure with WireMock (REST stubs) and generate a `BaseIntegrationTest.java` for framework-agnostic Java (no Spring Boot, Quarkus, Micronaut) | **Interactive User Prompt:** `Set up integration test infrastructure for my service using @132-java-testing-integration-testing` **Note:** The rule will ask questions about your service's outbound HTTP dependencies before generating `BaseIntegrationTest.java` and starter WireMock mapping files. Project must NOT use Spring Boot, Quarkus, or Micronaut. | Precondition: framework-agnostic Java. For Spring Boot use @322-frameworks-spring-boot-testing-integration-tests; for Quarkus @422-frameworks-quarkus-testing-integration-tests; for Micronaut @522-frameworks-micronaut-testing-integration-tests. Interactive rule — asks questions about REST topology before generating code. | | [133-java-testing-acceptance-tests](@133-java-testing-acceptance-tests.md) | Implement acceptance tests from a Gherkin .feature file for framework-agnostic Java (no Spring Boot, Quarkus, Micronaut) — @acceptance scenarios, RestAssured, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @133-java-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must NOT use Spring Boot, Quarkus, or Micronaut. | Preconditions: .feature file in context; framework-agnostic Java. For Spring Boot use @323-frameworks-spring-boot-testing-acceptance-tests; for Quarkus @423-frameworks-quarkus-testing-acceptance-tests; for Micronaut @523-frameworks-micronaut-testing-acceptance-tests. | -## Refactoring rules +## Refactoring skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -87,13 +94,13 @@ Use the following collection of Skills for Java to improve your Java development | [144-java-data-oriented-programming](@144-java-data-oriented-programming.md) | Add Data Oriented Programming in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @144-java-data-oriented-programming` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @144-java-data-oriented-programming with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [145-java-refactoring-high-performance](@145-java-refactoring-high-performance.md) | Refactor Java code performance in hot paths across memory/allocation, CPU/low-level, and code syntax/control-flow patterns | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @145-java-refactoring-high-performance` (Example) **Note:** Add in the context class/classes on critical paths. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @145-java-refactoring-high-performance with the behaviour @behaviour-consultative-interaction` | Uses three references: memory/allocation, CPU/low-level, and code syntax/control-flow. Focused on measured improvements in hot paths. | -## Performance rules +## Performance skills | Activity | Description | Prompt | Notes | |----|----|----|----| | [151-java-performance-jmeter](@151-java-performance-jmeter.md) | Run a performance test with Jmeter | **User Prompt:** `Add JMeter performance testing to this project using @151-java-performance-jmeter` **Note:** You could ask the model to create a JMeter based on a RestController/Resource. Example: `Can you create a Jmeter file based on the restcontroller in the path src/test/resources/jmeter/load-test.jmx?` | This skill is applied automatically without any interaction with the Software engineer. If you create a Jmeter file with the model, review the generation, sometimes it is necessary to hammer a bit. | -## Profiling rules (Async profiler, jps, jstack, jcmd & jstat) +## Profiling skills (Async profiler, jps, jstack, jcmd & jstat) | Activity | Description | Prompt | Notes | |----|----|----|----| @@ -103,13 +110,13 @@ Use the following collection of Skills for Java to improve your Java development | - | Code Refactoring from suggestions from analysis | `Can you apply the solutions from @profiling-solutions-yyyymmdd.md in @/info to mitigate bottlenecks` | Make a refactoring with the notes from the analysis | | [164-java-profiling-verify](@164-java-profiling-verify.md) | Compare results comparing results before and after applying changes in the code | **Prompt:** `Review if the problems was solved with last refactoring using the reports located in @/results with the skill @164-java-profiling-verify` **Note:** Put in the context the folder with the results | This skill is applied automatically without any interaction with the Software engineer. | -## Documentation rules +## Documentation skills | Activity | Description | Prompt | Notes | |----|----|----|----| | [170-java-documentation](@170-java-documentation.md) | Generate Java project documentation including README.md, package-info.java files, and Javadoc using a modular step-based approach | **Interactive User Prompt:** `Generate technical documentation about the project with the skill @170-java-documentation` **User Prompt:** `Generate README.md with @170-java-documentation without any question` (Example) **Note:** Add in the context the folder to generate the documentation. The rule will analyze existing documentation and ask for user preferences before generating anything. Ensures project validation with Maven before proceeding. | Focused on documentation generation only. For diagrams, use @033-architecture-diagrams | -## Spring Boot rules +## Spring Boot skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -124,7 +131,7 @@ Use the following collection of Skills for Java to improve your Java development | [322-frameworks-spring-boot-testing-integration-tests](@322-frameworks-spring-boot-testing-integration-tests.md) | Write integration tests for Spring Boot — Testcontainers, TestRestTemplate, data management, test structure | **User Prompt:** `Add or improve integration tests in context using @322-frameworks-spring-boot-testing-integration-tests` **Note:** Add in the context the project or test classes. | Integration testing guidelines. | | [323-frameworks-spring-boot-testing-acceptance-tests](@323-frameworks-spring-boot-testing-acceptance-tests.md) | Implement acceptance tests from Gherkin .feature file for Spring Boot — @acceptance scenarios, RestAssured, @SpringBootTest, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @323-frameworks-spring-boot-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must use Spring Boot. | Preconditions: .feature file in context; Spring Boot. For framework-agnostic Java use @133-java-testing-acceptance-tests. | -## Quarkus rules +## Quarkus skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -139,7 +146,7 @@ Use the following collection of Skills for Java to improve your Java development | [422-frameworks-quarkus-testing-integration-tests](@422-frameworks-quarkus-testing-integration-tests.md) | Write integration tests for Quarkus — @QuarkusTest, Testcontainers, Dev Services, persistence and HTTP | **User Prompt:** `Add or improve integration tests in context using @422-frameworks-quarkus-testing-integration-tests` **Note:** Add in the context the project or test classes. | Integration testing with real infrastructure. | | [423-frameworks-quarkus-testing-acceptance-tests](@423-frameworks-quarkus-testing-acceptance-tests.md) | Implement acceptance tests from Gherkin .feature file for Quarkus — @QuarkusTest, REST Assured, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @423-frameworks-quarkus-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must use Quarkus. | Preconditions: .feature file in context; Quarkus. For framework-agnostic Java use @133-java-testing-acceptance-tests. | -## Micronaut rules +## Micronaut skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -154,13 +161,13 @@ Use the following collection of Skills for Java to improve your Java development | [522-frameworks-micronaut-testing-integration-tests](@522-frameworks-micronaut-testing-integration-tests.md) | Integration tests for Micronaut — @MicronautTest, TestPropertyProvider, Testcontainers, HttpClient | **User Prompt:** `Add or improve integration tests in context using @522-frameworks-micronaut-testing-integration-tests` **Note:** Add in the context the project or test classes. | Real infrastructure in tests. | | [523-frameworks-micronaut-testing-acceptance-tests](@523-frameworks-micronaut-testing-acceptance-tests.md) | Acceptance tests from Gherkin .feature for Micronaut — @acceptance, HttpClient, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @523-frameworks-micronaut-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must use Micronaut. | Preconditions: .feature in context; Micronaut. For framework-agnostic Java use @133-java-testing-acceptance-tests. | -## AI Tooling +## AI Tooling skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [200-agents-md](@200-agents-md.md) | Generate AGENTS.md files for Java repositories using a modular step-based approach. AGENTS.md guides AI agents and contributors on project conventions, tech stack, file structure, commands, Git workflow, and boundaries | **Interactive User Prompt:** `Generate AGENTS.md for the project with the skill @200-agents-md` **Note:** Add in the context the project root folder. The rule will ask 6 questions to understand requirements before generating. Handles existing AGENTS.md (overwrite/merge/backup). | Focused on AGENTS.md generation only. Asks role, tech stack, file structure, commands, Git workflow, and boundaries before generating | -## Technologies +## Technologies skills | Skill | Description | Prompt | Notes | |----|----|----|----| diff --git a/skills-generator/src/main/resources/skills.xml b/skills-generator/src/main/resources/skills.xml index cb9f4aeb..028635b8 100644 --- a/skills-generator/src/main/resources/skills.xml +++ b/skills-generator/src/main/resources/skills.xml @@ -202,9 +202,19 @@ 170-java-documentation - + - 180-java-observability-logging + 181-java-observability-logging + + + + + 182-java-observability-metrics-micrometer + + + + + 183-observability-tracing-opentelemetry diff --git a/skills/001-skills-inventory/references/001-skills-inventory.md b/skills/001-skills-inventory/references/001-skills-inventory.md index 55605279..123da636 100644 --- a/skills/001-skills-inventory/references/001-skills-inventory.md +++ b/skills/001-skills-inventory/references/001-skills-inventory.md @@ -62,7 +62,7 @@ Use the following collection of Skills for Java to improve your Java development | [043-planning-github-issues](@043-planning-github-issues.md) | List GitHub issues (all or by milestone), fetch issue bodies and comments with `gh`, present tables; hand off to user stories | **User Prompt:** `List open issues in this repo as a table using @043-planning-github-issues` **User Prompt:** `Get all issues for milestone "Sprint 12" with @043-planning-github-issues` **User Prompt:** `Pull issue #44 description and comments, then draft a user story with @014-agile-user-story` **Note:** Requires GitHub CLI (`gh`) installed and authenticated. | Pairs with `@014-agile-user-story` when turning GitHub threads into user stories and Gherkin. | | [044-planning-jira](@044-planning-jira.md) | List Jira issues (all or by JQL), fetch issue descriptions and comments with `jira`, present tables; hand off to user stories | **User Prompt:** `List open Jira issues as a table using @044-planning-jira` **User Prompt:** `Get Jira issues with JQL "project = PROJ AND statusCategory != Done" using @044-planning-jira` **User Prompt:** `Pull issue PROJ-44 description and comments, then draft a user story with @014-agile-user-story` **Note:** Requires Jira CLI (`jira`) installed and configured. | Pairs with `@014-agile-user-story` when turning Jira threads into user stories and Gherkin. | -## Build system rules (Maven) +## Build system skills (Maven) | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -72,24 +72,31 @@ Use the following collection of Skills for Java to improve your Java development | [113-java-maven-documentation](@113-java-maven-documentation.md) | Create a Maven Documentation with the file `README-DEV.md` | **User Prompt:** `Generate developer documentation with essential Maven commands using @113-java-maven-documentation` **Note:** Add in the context the `pom.xml` which you want to generate the documentation. | This skill is applied automatically without any interaction with the Software engineer. | | [114-java-maven-search](@114-java-maven-search.md) | Search Maven Central, resolve coordinates, version metadata, and direct artifact URLs | **User Prompt:** `Find the latest version of com.google.guava:guava using @114-java-maven-search` **Note:** Use for dependency lookup, POM/JAR URLs, `maven-metadata.xml`, or Search API queries — not for editing `pom.xml` (use `@111` / `@112` for that). | Non-interactive. Use MCP Maven tools when available for live Central queries. | -## Design rules +## Design skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [121-java-object-oriented-design](@121-java-object-oriented-design.md) | Take another point of view with an Object Oriented Design of your development | **User prompt:** `Improve the class/classes added in the context applying the system prompt @121-java-object-oriented-design`(Example) **Note:** Add in the context a class/classes to improve the design. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @121-java-object-oriented-design with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [122-java-type-design](@122-java-type-design.md) | Review the Type Design in your development | **User prompt:** `Improve the class/classes added in the context applying the system prompt @122-java-type-design` (Example) **Note:** Add in the context a class/classes to improve the design. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @122-java-type-design with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | -## Coding rules +## Coding skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [123-java-exception-handling](@123-java-exception-handling.md) | Add Exception handling | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @123-java-exception-handling` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @123-java-exception-handling with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [124-java-secure-coding](@124-java-secure-coding.md) | Review my code for Secure Java Coding rules | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @124-java-secure-coding` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @124-java-secure-coding with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [125-java-concurrency](@125-java-concurrency.md) | Improve your code with Concurrency rules | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @125-java-concurrency` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @125-java-concurrency with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | -| [180-java-observability-logging](@180-java-observability-logging.md) | Apply logging guidelines in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @180-java-observability-logging` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @180-java-observability-logging with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [128-java-generics](@128-java-generics.md) | Apply generics in a class | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @128-java-generics` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @128-java-generics with the behaviour @behaviour-consultative-interaction` **User Prompt with Training behaviour:** `Create a course about @128-java-generics.md using the behavior @behaviour-progressive-learning.md and put the course here` **Note:** Add in the context the location to add the course. | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | -## Testing rules +## Observability skills + +| Skill | Description | Prompt | Notes | +|----|----|----|----| +| [181-java-observability-logging](@181-java-observability-logging.md) | Apply logging guidelines in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @181-java-observability-logging` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @181-java-observability-logging with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | +| [182-java-observability-metrics-micrometer](@182-java-observability-metrics-micrometer.md) | Apply Micrometer metrics observability best practices in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @182-java-observability-metrics-micrometer` (Example) **Note:** Add in the context a class/classes where metrics are emitted. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @182-java-observability-metrics-micrometer with the behaviour @behaviour-consultative-interaction` | Focused on metrics design, meter semantics, naming/tag conventions, and cardinality-safe instrumentation. | +| [183-observability-tracing-opentelemetry](@183-observability-tracing-opentelemetry.md) | Apply OpenTelemetry distributed tracing best practices in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @183-observability-tracing-opentelemetry` (Example) **Note:** Add in the context code paths where tracing and propagation are relevant. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @183-observability-tracing-opentelemetry with the behaviour @behaviour-consultative-interaction` | Focused on span modeling, context propagation, semantic conventions, and privacy-safe tracing attributes. | + +## Testing skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -98,7 +105,7 @@ Use the following collection of Skills for Java to improve your Java development | [132-java-testing-integration-testing](@132-java-testing-integration-testing.md) | Set up integration test infrastructure with WireMock (REST stubs) and generate a `BaseIntegrationTest.java` for framework-agnostic Java (no Spring Boot, Quarkus, Micronaut) | **Interactive User Prompt:** `Set up integration test infrastructure for my service using @132-java-testing-integration-testing` **Note:** The rule will ask questions about your service's outbound HTTP dependencies before generating `BaseIntegrationTest.java` and starter WireMock mapping files. Project must NOT use Spring Boot, Quarkus, or Micronaut. | Precondition: framework-agnostic Java. For Spring Boot use @322-frameworks-spring-boot-testing-integration-tests; for Quarkus @422-frameworks-quarkus-testing-integration-tests; for Micronaut @522-frameworks-micronaut-testing-integration-tests. Interactive rule — asks questions about REST topology before generating code. | | [133-java-testing-acceptance-tests](@133-java-testing-acceptance-tests.md) | Implement acceptance tests from a Gherkin .feature file for framework-agnostic Java (no Spring Boot, Quarkus, Micronaut) — @acceptance scenarios, RestAssured, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @133-java-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must NOT use Spring Boot, Quarkus, or Micronaut. | Preconditions: .feature file in context; framework-agnostic Java. For Spring Boot use @323-frameworks-spring-boot-testing-acceptance-tests; for Quarkus @423-frameworks-quarkus-testing-acceptance-tests; for Micronaut @523-frameworks-micronaut-testing-acceptance-tests. | -## Refactoring rules +## Refactoring skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -108,13 +115,13 @@ Use the following collection of Skills for Java to improve your Java development | [144-java-data-oriented-programming](@144-java-data-oriented-programming.md) | Add Data Oriented Programming in your development | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @144-java-data-oriented-programming` (Example) **Note:** Add in the context a class/classes. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @144-java-data-oriented-programming with the behaviour @behaviour-consultative-interaction` | You can use the System prompt in a purist way or add the Behaviours to customize the final behaviour. | | [145-java-refactoring-high-performance](@145-java-refactoring-high-performance.md) | Refactor Java code performance in hot paths across memory/allocation, CPU/low-level, and code syntax/control-flow patterns | **User Prompt:** `Improve the class/classes added in the context applying the system prompt @145-java-refactoring-high-performance` (Example) **Note:** Add in the context class/classes on critical paths. **User Prompt with Consultative Interactive Behaviour:** `Improve the class/classes added in the context applying the system prompt @145-java-refactoring-high-performance with the behaviour @behaviour-consultative-interaction` | Uses three references: memory/allocation, CPU/low-level, and code syntax/control-flow. Focused on measured improvements in hot paths. | -## Performance rules +## Performance skills | Activity | Description | Prompt | Notes | |----|----|----|----| | [151-java-performance-jmeter](@151-java-performance-jmeter.md) | Run a performance test with Jmeter | **User Prompt:** `Add JMeter performance testing to this project using @151-java-performance-jmeter` **Note:** You could ask the model to create a JMeter based on a RestController/Resource. Example: `Can you create a Jmeter file based on the restcontroller in the path src/test/resources/jmeter/load-test.jmx?` | This skill is applied automatically without any interaction with the Software engineer. If you create a Jmeter file with the model, review the generation, sometimes it is necessary to hammer a bit. | -## Profiling rules (Async profiler, jps, jstack, jcmd & jstat) +## Profiling skills (Async profiler, jps, jstack, jcmd & jstat) | Activity | Description | Prompt | Notes | |----|----|----|----| @@ -124,13 +131,13 @@ Use the following collection of Skills for Java to improve your Java development | - | Code Refactoring from suggestions from analysis | `Can you apply the solutions from @profiling-solutions-yyyymmdd.md in @/info to mitigate bottlenecks` | Make a refactoring with the notes from the analysis | | [164-java-profiling-verify](@164-java-profiling-verify.md) | Compare results comparing results before and after applying changes in the code | **Prompt:** `Review if the problems was solved with last refactoring using the reports located in @/results with the skill @164-java-profiling-verify` **Note:** Put in the context the folder with the results | This skill is applied automatically without any interaction with the Software engineer. | -## Documentation rules +## Documentation skills | Activity | Description | Prompt | Notes | |----|----|----|----| | [170-java-documentation](@170-java-documentation.md) | Generate Java project documentation including README.md, package-info.java files, and Javadoc using a modular step-based approach | **Interactive User Prompt:** `Generate technical documentation about the project with the skill @170-java-documentation` **User Prompt:** `Generate README.md with @170-java-documentation without any question` (Example) **Note:** Add in the context the folder to generate the documentation. The rule will analyze existing documentation and ask for user preferences before generating anything. Ensures project validation with Maven before proceeding. | Focused on documentation generation only. For diagrams, use @033-architecture-diagrams | -## Spring Boot rules +## Spring Boot skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -145,7 +152,7 @@ Use the following collection of Skills for Java to improve your Java development | [322-frameworks-spring-boot-testing-integration-tests](@322-frameworks-spring-boot-testing-integration-tests.md) | Write integration tests for Spring Boot — Testcontainers, TestRestTemplate, data management, test structure | **User Prompt:** `Add or improve integration tests in context using @322-frameworks-spring-boot-testing-integration-tests` **Note:** Add in the context the project or test classes. | Integration testing guidelines. | | [323-frameworks-spring-boot-testing-acceptance-tests](@323-frameworks-spring-boot-testing-acceptance-tests.md) | Implement acceptance tests from Gherkin .feature file for Spring Boot — @acceptance scenarios, RestAssured, @SpringBootTest, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @323-frameworks-spring-boot-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must use Spring Boot. | Preconditions: .feature file in context; Spring Boot. For framework-agnostic Java use @133-java-testing-acceptance-tests. | -## Quarkus rules +## Quarkus skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -160,7 +167,7 @@ Use the following collection of Skills for Java to improve your Java development | [422-frameworks-quarkus-testing-integration-tests](@422-frameworks-quarkus-testing-integration-tests.md) | Write integration tests for Quarkus — @QuarkusTest, Testcontainers, Dev Services, persistence and HTTP | **User Prompt:** `Add or improve integration tests in context using @422-frameworks-quarkus-testing-integration-tests` **Note:** Add in the context the project or test classes. | Integration testing with real infrastructure. | | [423-frameworks-quarkus-testing-acceptance-tests](@423-frameworks-quarkus-testing-acceptance-tests.md) | Implement acceptance tests from Gherkin .feature file for Quarkus — @QuarkusTest, REST Assured, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @423-frameworks-quarkus-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must use Quarkus. | Preconditions: .feature file in context; Quarkus. For framework-agnostic Java use @133-java-testing-acceptance-tests. | -## Micronaut rules +## Micronaut skills | Skill | Description | Prompt | Notes | |----|----|----|----| @@ -175,13 +182,13 @@ Use the following collection of Skills for Java to improve your Java development | [522-frameworks-micronaut-testing-integration-tests](@522-frameworks-micronaut-testing-integration-tests.md) | Integration tests for Micronaut — @MicronautTest, TestPropertyProvider, Testcontainers, HttpClient | **User Prompt:** `Add or improve integration tests in context using @522-frameworks-micronaut-testing-integration-tests` **Note:** Add in the context the project or test classes. | Real infrastructure in tests. | | [523-frameworks-micronaut-testing-acceptance-tests](@523-frameworks-micronaut-testing-acceptance-tests.md) | Acceptance tests from Gherkin .feature for Micronaut — @acceptance, HttpClient, Testcontainers, WireMock | **Interactive User Prompt:** `Implement acceptance tests from my Gherkin feature file using @523-frameworks-micronaut-testing-acceptance-tests` **Note:** Add the .feature file to context. Project must use Micronaut. | Preconditions: .feature in context; Micronaut. For framework-agnostic Java use @133-java-testing-acceptance-tests. | -## AI Tooling +## AI Tooling skills | Skill | Description | Prompt | Notes | |----|----|----|----| | [200-agents-md](@200-agents-md.md) | Generate AGENTS.md files for Java repositories using a modular step-based approach. AGENTS.md guides AI agents and contributors on project conventions, tech stack, file structure, commands, Git workflow, and boundaries | **Interactive User Prompt:** `Generate AGENTS.md for the project with the skill @200-agents-md` **Note:** Add in the context the project root folder. The rule will ask 6 questions to understand requirements before generating. Handles existing AGENTS.md (overwrite/merge/backup). | Focused on AGENTS.md generation only. Asks role, tech stack, file structure, commands, Git workflow, and boundaries before generating | -## Technologies +## Technologies skills | Skill | Description | Prompt | Notes | |----|----|----|----| diff --git a/skills/180-java-observability-logging/references/180-java-observability-logging.md b/skills/180-java-observability-logging/references/180-java-observability-logging.md deleted file mode 100644 index 6a832340..00000000 --- a/skills/180-java-observability-logging/references/180-java-observability-logging.md +++ /dev/null @@ -1,1426 +0,0 @@ ---- -name: 180-java-observability-logging -description: Use when you need to implement or improve Java logging and observability — including selecting SLF4J with Logback/Log4j2, applying proper log levels, parameterized logging, secure logging without sensitive data exposure, environment-specific configuration, log aggregation and monitoring, or validating logging through tests. -license: Apache-2.0 -metadata: - author: Juan Antonio Breña Moral - version: 0.15.0-SNAPSHOT ---- -# Java Logging Best Practices - -## Role - -You are a Senior software engineer with extensive experience in Java software development - -## Goal - -Effective Java logging involves selecting a standard framework (SLF4J with Logback/Log4j2), using appropriate log levels (ERROR, WARN, INFO, DEBUG, TRACE), -and adhering to core practices like parameterized logging, proper exception handling, and avoiding sensitive data exposure. -Configuration should be environment-specific with clear output formats. -Security is paramount: mask sensitive data, control log access, and ensure secure transmission. -Implement centralized log aggregation, monitoring, and alerting for proactive issue detection. -Finally, logging behavior and its impact should be validated through comprehensive testing. - -### Implementing These Principles - -These guidelines are built upon the following core principles: - -1. **Standardized Framework Selection**: Utilize a widely accepted logging facade (preferably SLF4J) and a robust underlying implementation (Logback or Log4j2). This promotes consistency, flexibility, and access to advanced logging features. -2. **Meaningful and Consistent Log Levels**: Employ logging levels (ERROR, WARN, INFO, DEBUG, TRACE) deliberately and consistently to categorize the severity and importance of messages. This allows for effective filtering, monitoring, and targeted issue diagnosis. -3. **Adherence to Core Logging Practices**: Follow fundamental best practices such as using parameterized logging (avoiding string concatenation for performance and clarity), always logging exceptions with their stack traces, never logging sensitive data directly (PII, credentials). -4. **Thoughtful and Flexible Configuration**: Manage logging configuration externally (e.g., `logback.xml`, `log4j2.xml`). Tailor configurations for different environments (dev, test, prod) with appropriate log levels for various packages, clear and informative output formats (including timestamps, levels, logger names, thread info), and robust log rotation and retention policies. -5. **Security-Conscious Logging**: Prioritize security in all logging activities. Actively mask or filter sensitive information, control access to log files and log management systems, use secure protocols for transmitting logs, and ensure compliance with relevant data protection regulations (e.g., GDPR, HIPAA). -6. **Proactive Log Monitoring and Alerting**: Implement centralized log aggregation systems (e.g., ELK Stack, Splunk, Grafana Loki). Establish automated alerts based on log patterns, error rates, or specific critical events to enable proactive issue detection and rapid response. -7. **Comprehensive Logging Validation Through Testing**: Integrate logging into the testing strategy. Assert that critical log messages (especially errors and warnings) are generated as expected under specific conditions, verify log formats, test log level filtering, and assess any performance impact of logging. - -Remember, good logging in Java is about operational excellence - making your application's behavior transparent and debuggable while maintaining security and performance. - -## Constraints - -Before applying any recommendations, ensure the project is in a valid state by running Maven compilation. Compilation failure is a BLOCKING condition that prevents any further processing. - -- **MANDATORY**: Run `./mvnw compile` or `mvn compile` before applying any change -- **PREREQUISITE**: Project must compile successfully and pass basic validation checks before any optimization -- **CRITICAL SAFETY**: If compilation fails, IMMEDIATELY STOP and DO NOT CONTINUE with any recommendations -- **BLOCKING CONDITION**: Compilation errors must be resolved by the user before proceeding with any object-oriented design improvements -- **NO EXCEPTIONS**: Under no circumstances should design recommendations be applied to a project that fails to compile - -## Examples - -### Table of contents - -- Example 1: Choose an Appropriate Logging Framework -- Example 2: Understand and Use Logging Levels Correctly -- Example 3: Adhere to Core Logging Practices -- Example 4: Follow Configuration Best Practices -- Example 5: Implement Secure Logging Practices -- Example 6: Establish Effective Log Monitoring and Alerting -- Example 7: Incorporate Logging in Testing - -### Example 1: Choose an Appropriate Logging Framework - -Title: Select a Standard Logging Facade and Implementation -Description: Using a standard logging facade like SLF4J allows for flexibility in choosing and switching an underlying logging implementation. The primary recommendation is SLF4J with Logback for its robustness and feature-richness. - -**Good example:** - -```java -// GOOD: Using SLF4J facade with proper logger declaration -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.Objects; - -public class UserService { - // Logger declared using SLF4J - static final per class - private static final Logger logger = LoggerFactory.getLogger(UserService.class); - - public void performAction(String input) { - // SLF4J parameterized logging - efficient and readable - logger.info("Performing action with input: {}", input); - - if (Objects.isNull(input) || input.isEmpty()) { - logger.warn("Input is null or empty, using default behavior"); - input = "default"; - } - - try { - processInput(input); - logger.debug("Action completed successfully for input: {}", input); - } catch (ProcessingException e) { - logger.error("Failed to process input: {}", input, e); - throw e; - } - } - - public void performComplexOperation(String userId, String operation) { - // Using MDC (Mapped Diagnostic Context) for contextual logging - try (var mdcCloseable = org.slf4j.MDC.putCloseable("userId", userId)) { - logger.info("Starting operation: {}", operation); - - // All log statements in this block will include userId context - performInternalSteps(operation); - - logger.info("Operation completed successfully"); - } catch (Exception e) { - logger.error("Operation failed", e); - throw e; - } - } - - private void processInput(String input) throws ProcessingException { - // Simulate processing that might fail - if ("error".equals(input)) { - throw new ProcessingException("Invalid input: " + input); - } - logger.trace("Processing step completed for: {}", input); - } - - private void performInternalSteps(String operation) { - logger.debug("Executing internal step 1 for operation: {}", operation); - // ... business logic - logger.debug("Executing internal step 2 for operation: {}", operation); - // ... more business logic - } - - private static class ProcessingException extends Exception { - public ProcessingException(String message) { - super(message); - } - } -} -``` - -**Bad example:** - -```java -// AVOID: Using System.out.println or direct logging implementation -public class BadLoggingService { - - public void performAction(String input) { - // BAD: Using System.out.println - no control, no levels, no formatting - System.out.println("Starting action with: " + input); - - if (input == null || input.isEmpty()) { - // BAD: Using System.err - not integrated with logging framework - System.err.println("Warning: Input is null or empty!"); - } - - try { - processInput(input); - System.out.println("Action completed for: " + input); - } catch (Exception e) { - // BAD: Just printing stack trace to stderr - e.printStackTrace(); - // No structured logging, no log levels, no context - } - } - - // BAD: Directly using concrete logging implementation - private java.util.logging.Logger julLogger = - java.util.logging.Logger.getLogger(BadLoggingService.class.getName()); - - public void anotherMethod() { - // BAD: Tied to specific implementation, less flexible - julLogger.info("Using JUL directly"); - } - - // BAD: Multiple logging frameworks in same class - private org.apache.logging.log4j.Logger log4jLogger = - org.apache.logging.log4j.LogManager.getLogger(BadLoggingService.class); - - public void confusedLogging() { - julLogger.info("Using JUL"); - log4jLogger.info("Using Log4j"); - System.out.println("Using System.out"); - // Inconsistent and confusing! - } -} -``` - -### Example 2: Understand and Use Logging Levels Correctly - -Title: Apply Appropriate Logging Levels for Messages -Description: Use logging levels consistently to categorize the severity and importance of log messages. ERROR for critical issues, WARN for potentially harmful situations, INFO for important business events, DEBUG for detailed information, and TRACE for fine-grained debugging. - -**Good example:** - -```java -// GOOD: Proper usage of logging levels -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.Objects; - -public class OrderProcessor { - private static final Logger logger = LoggerFactory.getLogger(OrderProcessor.class); - - public void processOrder(String orderId, String customerId) { - logger.trace("Entering processOrder with orderId: {}, customerId: {}", orderId, customerId); - - // INFO: Important business events - logger.info("Processing order {} for customer {}", orderId, customerId); - - try { - // Validate inputs - if (Objects.isNull(orderId) || orderId.trim().isEmpty()) { - // WARN: Potentially harmful situation that can be recovered - logger.warn("Order ID is null or empty for customer {}. Using generated ID.", customerId); - orderId = generateOrderId(); - } - - // DEBUG: Detailed information for development/troubleshooting - logger.debug("Validating order {} inventory", orderId); - validateInventory(orderId); - - logger.debug("Calculating pricing for order {}", orderId); - calculatePricing(orderId); - - logger.debug("Processing payment for order {}", orderId); - processPayment(orderId); - - // INFO: Successful completion of important business operation - logger.info("Order {} processed successfully for customer {}", orderId, customerId); - - } catch (InventoryException e) { - // WARN: Expected business exception that can be handled - logger.warn("Insufficient inventory for order {}. Will backorder.", orderId, e); - handleBackorder(orderId); - - } catch (PaymentException e) { - // ERROR: Critical failure requiring immediate attention - logger.error("Payment processing failed for order {}. Customer: {}", - orderId, customerId, e); - throw new OrderProcessingException("Payment failed", e); - - } catch (Exception e) { - // ERROR: Unexpected critical error - logger.error("Unexpected error processing order {} for customer {}", - orderId, customerId, e); - throw new OrderProcessingException("Unexpected error", e); - } - - logger.trace("Exiting processOrder for orderId: {}", orderId); - } - - public void performHealthCheck() { - logger.debug("Starting health check"); - - try { - checkDatabaseConnection(); - checkExternalServices(); - - // INFO: System status information - logger.info("Health check passed - all systems operational"); - - } catch (HealthCheckException e) { - // ERROR: System health issue requiring immediate attention - logger.error("Health check failed - system may be degraded", e); - } - } - - // Example methods - private String generateOrderId() { return "ORD-" + System.currentTimeMillis(); } - private void validateInventory(String orderId) throws InventoryException { /* ... */ } - private void calculatePricing(String orderId) { /* ... */ } - private void processPayment(String orderId) throws PaymentException { /* ... */ } - private void handleBackorder(String orderId) { /* ... */ } - private void checkDatabaseConnection() throws HealthCheckException { /* ... */ } - private void checkExternalServices() throws HealthCheckException { /* ... */ } - - // Exception classes - private static class InventoryException extends Exception { - public InventoryException(String message) { super(message); } - } - private static class PaymentException extends Exception { - public PaymentException(String message) { super(message); } - } - private static class OrderProcessingException extends RuntimeException { - public OrderProcessingException(String message, Throwable cause) { super(message, cause); } - } - private static class HealthCheckException extends Exception { - public HealthCheckException(String message) { super(message); } - } -} -``` - -**Bad example:** - -```java -// AVOID: Misusing logging levels -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class BadLevelUsage { - private static final Logger logger = LoggerFactory.getLogger(BadLevelUsage.class); - - public void processUser(String userId) { - // BAD: Using INFO for debug-level details - logger.info("Method entry: processUser with parameter: " + userId); - logger.info("Creating new StringBuilder object"); - logger.info("Checking if userId is null"); - - if (userId == null) { - // BAD: Using ERROR for a normal business condition - logger.error("User ID is null!"); // This might be expected/handled - return; - } - - // BAD: Using WARN for normal flow information - logger.warn("User ID is not null, proceeding with processing"); - - try { - String result = processUserData(userId); - - // BAD: Using ERROR for successful operations - logger.error("Successfully processed user: " + userId + " with result: " + result); - - } catch (Exception e) { - // BAD: Using INFO for critical errors - logger.info("An error occurred: " + e.getMessage()); - // Should be ERROR or WARN depending on severity - } - - // BAD: Overuse of TRACE for everything - logger.trace("About to return from method"); - logger.trace("Setting return value to void"); - logger.trace("Method execution completed"); - // Too much noise - not useful - } - - public void anotherBadExample(String data) { - // BAD: String concatenation instead of parameterized logging - logger.info("Processing data: " + data + " at time: " + System.currentTimeMillis()); - - // BAD: Using DEBUG for important business events - logger.debug("Payment of $1000 processed for customer ABC123"); - // This should be INFO - it's an important business event - - // BAD: Using same level for different severity issues - logger.warn("Configuration file not found, using defaults"); // This is OK for WARN - logger.warn("Database connection lost"); // This should be ERROR - logger.warn("User entered invalid email format"); // This might be INFO or DEBUG - } - - private String processUserData(String userId) { - return "processed-" + userId; - } -} -``` - -### Example 3: Adhere to Core Logging Practices - -Title: Implement Fundamental Best Practices -Description: Follow core practices including using parameterized logging, proper exception handling, avoiding sensitive data exposure, and implementing performance considerations for logging operations. - -**Good example:** - -```java -// GOOD: Core logging best practices -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; -import java.util.Objects; - -public class SecureTransactionService { - private static final Logger logger = LoggerFactory.getLogger(SecureTransactionService.class); - - public void processPayment(String userId, String amount, String cardNumber) { - // Set correlation ID for request tracing - String correlationId = generateCorrelationId(); - MDC.put("correlationId", correlationId); - - try { - logger.info("Processing payment for user: {}, amount: {}", userId, amount); - - // GOOD: Mask sensitive information before logging - String maskedCard = maskCreditCard(cardNumber); - logger.debug("Processing payment with card: {}", maskedCard); - - validatePaymentRequest(userId, amount, cardNumber); - - if (logger.isDebugEnabled()) { - // GOOD: Guard clause for expensive operations - String debugInfo = buildComplexDebugInfo(userId, amount); - logger.debug("Payment validation details: {}", debugInfo); - } - - processPaymentInternal(userId, amount, cardNumber); - - logger.info("Payment processed successfully for user: {}, correlation: {}", - userId, correlationId); - - } catch (ValidationException e) { - // GOOD: Log exception with context but don't expose sensitive data - logger.warn("Payment validation failed for user: {}, reason: {}, correlation: {}", - userId, e.getValidationError(), correlationId, e); - throw e; - - } catch (PaymentProcessingException e) { - // GOOD: Log critical error with full context - logger.error("Payment processing failed for user: {}, amount: {}, correlation: {}", - userId, amount, correlationId, e); - throw e; - - } catch (Exception e) { - // GOOD: Catch unexpected exceptions and log with context - logger.error("Unexpected error during payment processing for user: {}, correlation: {}", - userId, correlationId, e); - throw new PaymentProcessingException("Unexpected error", e); - - } finally { - // GOOD: Clean up MDC to prevent memory leaks - MDC.remove("correlationId"); - } - } - - public void processLargeDataSet(List dataItems) { - logger.info("Processing {} data items", dataItems.size()); - - for (int i = 0; i < dataItems.size(); i++) { - String item = dataItems.get(i); - - try { - processDataItem(item); - - // GOOD: Log progress periodically, not for every item - if (i % 1000 == 0) { - logger.debug("Processed {} of {} items", i, dataItems.size()); - } - - } catch (Exception e) { - // GOOD: Log error but continue processing - logger.warn("Failed to process item at index {}: {}", i, item, e); - } - } - - logger.info("Completed processing {} data items", dataItems.size()); - } - - // Utility methods - private String maskCreditCard(String cardNumber) { - if (cardNumber == null || cardNumber.length() < 8) { - return "****"; - } - return cardNumber.substring(0, 4) + "****" + cardNumber.substring(cardNumber.length() - 4); - } - - private String generateCorrelationId() { - return "TXN-" + System.currentTimeMillis() + "-" + Thread.currentThread().getId(); - } - - private String buildComplexDebugInfo(String userId, String amount) { - // Simulate expensive debug information building - return String.format("User: %s, Amount: %s, Timestamp: %d", - userId, amount, System.currentTimeMillis()); - } - - private void validatePaymentRequest(String userId, String amount, String cardNumber) - throws ValidationException { - if (Objects.isNull(userId) || userId.trim().isEmpty()) { - throw new ValidationException("INVALID_USER_ID"); - } - // More validation... - } - - private void processPaymentInternal(String userId, String amount, String cardNumber) - throws PaymentProcessingException { - // Simulate payment processing - if ("fail".equals(userId)) { - throw new PaymentProcessingException("Payment gateway error"); - } - } - - private void processDataItem(String item) { - // Simulate data processing - if ("error".equals(item)) { - throw new RuntimeException("Processing failed for item: " + item); - } - } - - // Exception classes - private static class ValidationException extends Exception { - private final String validationError; - - public ValidationException(String validationError) { - super("Validation failed: " + validationError); - this.validationError = validationError; - } - - public String getValidationError() { - return validationError; - } - } - - private static class PaymentProcessingException extends RuntimeException { - public PaymentProcessingException(String message) { - super(message); - } - - public PaymentProcessingException(String message, Throwable cause) { - super(message, cause); - } - } -} -``` - -**Bad example:** - -```java -// AVOID: Poor logging practices -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PoorLoggingPractices { - private static final Logger logger = LoggerFactory.getLogger(PoorLoggingPractices.class); - - public void processPayment(String userId, String amount, String cardNumber, String ssn) { - // BAD: String concatenation instead of parameterized logging - logger.info("Processing payment for user: " + userId + " amount: " + amount); - - // BAD: Logging sensitive information directly - logger.debug("Credit card: " + cardNumber + ", SSN: " + ssn); - - try { - validatePayment(userId, amount); - - // BAD: Expensive operation without guard clause - logger.debug("Payment details: " + buildExpensiveDebugString(userId, amount, cardNumber)); - - processPaymentTransaction(userId, amount, cardNumber); - - } catch (Exception e) { - // BAD: Swallowing exception without proper logging - logger.info("Payment failed: " + e.getMessage()); - // Lost the stack trace and context! - - // BAD: Logging sensitive data in error message - logger.error("Payment failed for card: " + cardNumber + " and SSN: " + ssn); - } - } - - public void processLargeDataSet(List items) { - // BAD: Logging every single item in large dataset - for (String item : items) { - logger.debug("Processing item: " + item); // Will flood logs - processItem(item); - logger.debug("Completed item: " + item); // Even more noise - } - } - - public void handleUserLogin(String username, String password) { - // BAD: Logging passwords - NEVER do this! - logger.debug("User login attempt: username=" + username + ", password=" + password); - - try { - authenticateUser(username, password); - // BAD: Inconsistent logging format - logger.info("User " + username + " logged in successfully"); - } catch (AuthenticationException e) { - // BAD: Using wrong log level and exposing sensitive info - logger.error("Login failed for " + username + " with password " + password); - } - } - - public void performDatabaseOperation(String query, String connectionString) { - // BAD: Logging database connection strings (may contain credentials) - logger.debug("Executing query: " + query + " on connection: " + connectionString); - - try { - executeQuery(query); - } catch (SQLException e) { - // BAD: Not using parameterized logging with exception - logger.error("SQL error: " + e.getMessage() + " for query: " + query); - // Stack trace is lost! - } - } - - // BAD: No try-with-resources or proper cleanup for MDC - public void badMDCUsage(String userId) { - org.slf4j.MDC.put("userId", userId); - logger.info("Processing for user"); - - // ... some processing ... - - // BAD: Forgot to clear MDC - memory leak! - // MDC.clear() or MDC.remove("userId") is missing - } - - // Helper methods with poor exception handling - private String buildExpensiveDebugString(String userId, String amount, String cardNumber) { - // Simulate expensive operation - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 10000; i++) { - sb.append("Debug info for ").append(userId).append(" "); - } - return sb.toString(); - } - - private void validatePayment(String userId, String amount) throws ValidationException { - if (userId == null) throw new ValidationException("Invalid user"); - } - - private void processPaymentTransaction(String userId, String amount, String cardNumber) { - // Simulate processing - } - - private void processItem(String item) { - // Simulate processing - } - - private void authenticateUser(String username, String password) throws AuthenticationException { - if ("baduser".equals(username)) { - throw new AuthenticationException("Invalid credentials"); - } - } - - private void executeQuery(String query) throws SQLException { - if (query.contains("DROP")) { - throw new SQLException("Invalid query"); - } - } - - // Exception classes - private static class ValidationException extends Exception { - public ValidationException(String message) { super(message); } - } - private static class AuthenticationException extends Exception { - public AuthenticationException(String message) { super(message); } - } - private static class SQLException extends Exception { - public SQLException(String message) { super(message); } - } -} -``` - -### Example 4: Follow Configuration Best Practices - -Title: Configure Your Logging Framework Thoughtfully -Description: Proper configuration is key to effective logging. Use separate configurations per environment, implement different log levels for different packages, and include comprehensive output formats with timestamps, levels, logger names, and thread information. - -**Good example:** - -```java -// Configuration shown through usage - actual config would be in logback.xml or log4j2.xml -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; - -public class ConfiguredLoggerExample { - private static final Logger appLogger = LoggerFactory.getLogger(ConfiguredLoggerExample.class); - private static final Logger performanceLogger = LoggerFactory.getLogger("performance." + ConfiguredLoggerExample.class.getName()); - - public void performBusinessOperation(String operationId, String userId) { - // Using MDC for contextual information - MDC.put("operationId", operationId); - MDC.put("userId", userId); - - try { - long startTime = System.currentTimeMillis(); - appLogger.info("Starting business operation"); - - // Simulate work - Thread.sleep(100); - - long duration = System.currentTimeMillis() - startTime; - performanceLogger.info("Operation completed in {} ms", duration); - - appLogger.info("Business operation completed successfully"); - - } catch (InterruptedException e) { - appLogger.warn("Operation interrupted", e); - Thread.currentThread().interrupt(); - } catch (Exception e) { - appLogger.error("Business operation failed", e); - throw new RuntimeException("Operation failed", e); - } finally { - // Always clear MDC - MDC.clear(); - } - } - - public void demonstrateLogLevels() { - // These will be filtered based on configuration - appLogger.trace("This is trace level - very detailed"); - appLogger.debug("This is debug level - detailed for development"); - appLogger.info("This is info level - important business events"); - appLogger.warn("This is warn level - potentially harmful situations"); - appLogger.error("This is error level - critical issues"); - } - - public static void main(String[] args) { - ConfiguredLoggerExample example = new ConfiguredLoggerExample(); - example.performBusinessOperation("OP123", "user456"); - example.demonstrateLogLevels(); - } -} -``` - -**Bad example:** - -```java -// Poor configuration consequences -public class BadConfigConsequences { - private static final Logger logger = LoggerFactory.getLogger(BadConfigConsequences.class); - - public void performOperation() { - // If configuration is bad (everything at TRACE), this floods logs - logger.trace("Entering method"); - logger.trace("Creating variables"); - logger.trace("About to check condition"); - - logger.info("User logged in."); - logger.error("Failed to connect to DB.", new RuntimeException("DB connection timeout")); - - // With bad pattern configuration, output might be: - // User logged in. - // Failed to connect to DB. - // Problem: No timestamp, no level, no logger name, no thread info - } - - public static void main(String[] args) { - BadConfigConsequences example = new BadConfigConsequences(); - example.performOperation(); - } -} -``` - -### Example 5: Implement Secure Logging Practices - -Title: Ensure Logs Do Not Compromise Security -Description: Actively mask or filter sensitive data, control access to log files, use secure transmission protocols, and comply with data protection regulations when logging information. - -**Good example:** - -```java -// GOOD: Secure logging practices -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.regex.Pattern; -import java.util.Objects; - -public class SecureLoggingService { - private static final Logger logger = LoggerFactory.getLogger(SecureLoggingService.class); - - // Patterns for detecting sensitive data - private static final Pattern CREDIT_CARD_PATTERN = Pattern.compile("\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}"); - private static final Pattern SSN_PATTERN = Pattern.compile("\\d{3}-\\d{2}-\\d{4}"); - private static final Pattern EMAIL_PATTERN = Pattern.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"); - - public void processUserRegistration(String username, String email, String ssn, String creditCard) { - // GOOD: Sanitize all user inputs before logging - String sanitizedUsername = sanitizeForLogging(username); - String maskedEmail = maskEmail(email); - String hashedSSN = hashSensitiveData(ssn); - String maskedCard = maskCreditCard(creditCard); - - logger.info("Processing registration for user: {}, email: {}", sanitizedUsername, maskedEmail); - logger.debug("User data validation - SSN hash: {}, card mask: {}", hashedSSN, maskedCard); - - try { - validateUserData(username, email, ssn, creditCard); - createUserAccount(username, email); - - // GOOD: Log successful operation without sensitive data - logger.info("User registration completed successfully for: {}", sanitizedUsername); - - // GOOD: Security event logging for audit trail - logger.info("SECURITY_EVENT: New user registration - username: {}, timestamp: {}", - sanitizedUsername, System.currentTimeMillis()); - - } catch (ValidationException e) { - // GOOD: Log validation failure without exposing sensitive validation details - logger.warn("User registration failed validation for user: {}, error code: {}", - sanitizedUsername, e.getErrorCode()); - - } catch (SecurityException e) { - // GOOD: Security incidents logged with careful information control - logger.error("SECURITY_ALERT: Registration security violation for user: {}, violation type: {}", - sanitizeForLogging(username), e.getViolationType()); - // Don't log the actual violation details that might help attackers - - } catch (Exception e) { - // GOOD: Generic error logging without sensitive context - logger.error("User registration failed for user: {} due to system error", - sanitizedUsername, e); - } - } - - public void processPayment(String userId, PaymentRequest request) { - String correlationId = generateSecureCorrelationId(); - - try { - // GOOD: Log business operation with masked sensitive data - logger.info("Processing payment - user: {}, amount: {}, correlation: {}", - userId, request.getAmount(), correlationId); - - // GOOD: Validate and log without exposing card details - validatePaymentMethod(request.getPaymentMethod()); - logger.debug("Payment method validated for correlation: {}", correlationId); - - ChargeResult result = processCharge(request); - - // GOOD: Log success with minimal necessary information - logger.info("Payment processed successfully - correlation: {}, transaction: {}", - correlationId, result.getTransactionId()); - - // GOOD: Business intelligence logging (aggregated, non-sensitive) - logger.info("METRICS: Payment processed - amount: {}, merchant: {}, timestamp: {}", - request.getAmount(), request.getMerchantId(), System.currentTimeMillis()); - - } catch (FraudDetectedException e) { - // GOOD: Security event with controlled information - logger.error("SECURITY_ALERT: Fraud detected - correlation: {}, risk_score: {}, user: {}", - correlationId, e.getRiskScore(), userId); - // Don't log fraud detection details that could help bypass detection - - } catch (PaymentException e) { - // GOOD: Business error with safe context - logger.error("Payment failed - correlation: {}, error_type: {}", - correlationId, e.getErrorType(), e); - } - } - - // Secure data masking utilities - private String sanitizeForLogging(String input) { - if (input == null) return "[null]"; - - // Remove potentially dangerous characters for log injection protection - String sanitized = input.replaceAll("[\r\n\t]", "_") - .replaceAll("[<>\"'&]", "*"); - - // Limit length to prevent log flooding - if (sanitized.length() > 100) { - sanitized = sanitized.substring(0, 97) + "..."; - } - - return sanitized; - } - - private String maskEmail(String email) { - if (email == null || !EMAIL_PATTERN.matcher(email).matches()) { - return "[INVALID_EMAIL]"; - } - - int atIndex = email.indexOf('@'); - if (atIndex < 2) { - return "**@" + email.substring(atIndex + 1); - } - - return email.substring(0, 2) + "***@" + email.substring(atIndex + 1); - } - - private String maskCreditCard(String cardNumber) { - if (cardNumber == null || cardNumber.length() < 8) { - return "[INVALID_CARD]"; - } - - String digitsOnly = cardNumber.replaceAll("[^\\d]", ""); - if (digitsOnly.length() < 8) { - return "[INVALID_CARD]"; - } - - return digitsOnly.substring(0, 4) + "****" + - digitsOnly.substring(digitsOnly.length() - 4); - } - - private String hashSensitiveData(String data) { - if (data == null) return "[null]"; - - // Use a secure hash for audit purposes (not for security) - return "HASH_" + Math.abs(data.hashCode()); - } - - private String generateSecureCorrelationId() { - return "CORR_" + System.currentTimeMillis() + "_" + - Thread.currentThread().getId(); - } - - // Mock classes and methods - private void validateUserData(String username, String email, String ssn, String creditCard) - throws ValidationException { - if (username == null || username.trim().isEmpty()) { - throw new ValidationException("INVALID_USERNAME"); - } - } - - private void createUserAccount(String username, String email) { - // Account creation logic - } - - private void validatePaymentMethod(String paymentMethod) { - // Payment validation logic - } - - private ChargeResult processCharge(PaymentRequest request) throws PaymentException { - // Payment processing logic - return new ChargeResult("TXN_" + System.currentTimeMillis()); - } - - // Mock classes - private static class PaymentRequest { - private String amount = "100.00"; - private String paymentMethod = "card"; - private String merchantId = "MERCHANT_123"; - - public String getAmount() { return amount; } - public String getPaymentMethod() { return paymentMethod; } - public String getMerchantId() { return merchantId; } - } - - private static class ChargeResult { - private final String transactionId; - public ChargeResult(String transactionId) { this.transactionId = transactionId; } - public String getTransactionId() { return transactionId; } - } - - private static class ValidationException extends Exception { - private final String errorCode; - public ValidationException(String errorCode) { - super("Validation failed: " + errorCode); - this.errorCode = errorCode; - } - public String getErrorCode() { return errorCode; } - } - - private static class SecurityException extends Exception { - private final String violationType; - public SecurityException(String violationType) { - super("Security violation: " + violationType); - this.violationType = violationType; - } - public String getViolationType() { return violationType; } - } - - private static class FraudDetectedException extends Exception { - private final int riskScore; - public FraudDetectedException(int riskScore) { - super("Fraud detected with risk score: " + riskScore); - this.riskScore = riskScore; - } - public int getRiskScore() { return riskScore; } - } - - private static class PaymentException extends Exception { - private final String errorType; - public PaymentException(String errorType) { - super("Payment error: " + errorType); - this.errorType = errorType; - } - public String getErrorType() { return errorType; } - } -} -``` - -**Bad example:** - -```java -// AVOID: Insecure logging practices -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class InsecureLoggingService { - private static final Logger logger = LoggerFactory.getLogger(InsecureLoggingService.class); - - public void processUserRegistration(String username, String password, String email, - String ssn, String creditCard) { - // BAD: Logging passwords - NEVER do this! - logger.debug("User registration: username={}, password={}, email={}", - username, password, email); - - // BAD: Logging full SSN and credit card numbers - logger.info("Processing registration for SSN: {} with credit card: {}", ssn, creditCard); - - try { - validateUser(username, password, email, ssn, creditCard); - createAccount(username, password); - - } catch (ValidationException e) { - // BAD: Exposing sensitive validation details - logger.error("Validation failed for user {} with password {} - details: {}", - username, password, e.getValidationDetails()); - - } catch (SecurityException e) { - // BAD: Logging detailed security information that could help attackers - logger.error("Security violation for user {}: attack vector: {}, payload: {}, " + - "system_path: {}, internal_error: {}", - username, e.getAttackVector(), e.getPayload(), - e.getSystemPath(), e.getInternalError()); - } - } - - public void processPayment(String userId, String cardNumber, String cvv, String amount) { - // BAD: Logging complete payment card information - logger.info("Processing payment: user={}, card={}, cvv={}, amount={}", - userId, cardNumber, cvv, amount); - - try { - validateCard(cardNumber, cvv); - - // BAD: Logging sensitive database connection info - logger.debug("Connecting to payment DB with connection string: {}", - getDatabaseConnectionString()); - - processTransaction(cardNumber, cvv, amount); - - } catch (FraudException e) { - // BAD: Exposing fraud detection algorithms and thresholds - logger.error("Fraud detected for card {}: algorithm={}, threshold={}, " + - "risk_factors={}, detection_rules={}", - cardNumber, e.getAlgorithm(), e.getThreshold(), - e.getRiskFactors(), e.getDetectionRules()); - - } catch (PaymentException e) { - // BAD: Including full stack trace with sensitive system information - logger.error("Payment failed for card {} with full system details", cardNumber, e); - } - } - - public void handleSystemError(String operation, Exception e) { - // BAD: Logging system internals that could help attackers - logger.error("System error in operation {}: java_version={}, system_properties={}, " + - "environment_variables={}, file_paths={}", - operation, System.getProperty("java.version"), - System.getProperties(), System.getenv(), - System.getProperty("user.dir")); - - // BAD: Full stack trace might expose sensitive system information - e.printStackTrace(); // Goes to stderr, not controlled by logging config - } - - public void logUserActivity(String userId, String sessionId, String ipAddress, - String userAgent, String requestData) { - // BAD: Logging PII and potentially sensitive request data - logger.info("User activity: user={}, session={}, ip={}, userAgent={}, request={}", - userId, sessionId, ipAddress, userAgent, requestData); - - // BAD: No data retention consideration - // This could violate GDPR, CCPA, or other privacy regulations - } - - public void authenticateUser(String username, String password, String loginToken) { - // BAD: Logging authentication credentials - logger.debug("Authentication attempt: username={}, password={}, token={}", - username, password, loginToken); - - try { - boolean success = authenticate(username, password, loginToken); - if (!success) { - // BAD: Detailed failure information that could help brute force attacks - logger.warn("Login failed for {} - password_attempts={}, last_success={}, " + - "account_locked={}, failed_reasons={}", - username, getPasswordAttempts(username), - getLastSuccessfulLogin(username), isAccountLocked(username), - getFailureReasons(username)); - } - } catch (Exception e) { - // BAD: Logging authentication errors with sensitive context - logger.error("Authentication system error for user {} with credentials: " + - "password={}, token={}", username, password, loginToken, e); - } - } - - // Mock methods with security issues - private String getDatabaseConnectionString() { - return "jdbc:mysql://prod-db:3306/payments?user=admin&password=secret123"; - } - - private void validateUser(String username, String password, String email, String ssn, String creditCard) - throws ValidationException { /* ... */ } - private void createAccount(String username, String password) { /* ... */ } - private void validateCard(String cardNumber, String cvv) { /* ... */ } - private void processTransaction(String cardNumber, String cvv, String amount) - throws PaymentException { /* ... */ } - private boolean authenticate(String username, String password, String token) { return true; } - private int getPasswordAttempts(String username) { return 3; } - private long getLastSuccessfulLogin(String username) { return System.currentTimeMillis(); } - private boolean isAccountLocked(String username) { return false; } - private String getFailureReasons(String username) { return "invalid_password"; } - - // Exception classes with sensitive information exposure - private static class ValidationException extends Exception { - private final String validationDetails; - public ValidationException(String details) { - this.validationDetails = details; - } - public String getValidationDetails() { return validationDetails; } - } - - private static class SecurityException extends Exception { - private final String attackVector, payload, systemPath, internalError; - public SecurityException(String attackVector, String payload, String systemPath, String internalError) { - this.attackVector = attackVector; - this.payload = payload; - this.systemPath = systemPath; - this.internalError = internalError; - } - public String getAttackVector() { return attackVector; } - public String getPayload() { return payload; } - public String getSystemPath() { return systemPath; } - public String getInternalError() { return internalError; } - } - - private static class FraudException extends Exception { - private final String algorithm, threshold, riskFactors, detectionRules; - public FraudException(String algorithm, String threshold, String riskFactors, String detectionRules) { - this.algorithm = algorithm; - this.threshold = threshold; - this.riskFactors = riskFactors; - this.detectionRules = detectionRules; - } - public String getAlgorithm() { return algorithm; } - public String getThreshold() { return threshold; } - public String getRiskFactors() { return riskFactors; } - public String getDetectionRules() { return detectionRules; } - } - - private static class PaymentException extends Exception { - public PaymentException(String message) { super(message); } - } -} -``` - -### Example 6: Establish Effective Log Monitoring and Alerting - -Title: Implement Log Aggregation, Monitoring, and Alerting -Description: Set up centralized log aggregation, implement alerts based on log patterns, use structured logging for querying, and regularly analyze logs to identify trends and issues. - -**Good example:** - -```java -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; -import java.util.Objects; -import java.util.UUID; - -public class MonitoredService { - private static final Logger logger = LoggerFactory.getLogger(MonitoredService.class); - - public void handleApiRequest(String endpoint, String payload) { - String requestId = UUID.randomUUID().toString(); - MDC.put("requestId", requestId); - MDC.put("endpoint", endpoint); - - try { - logger.info("API request received"); - - if (payload.contains(""); - - for (int i = 0; i < 20; i++) { - service.handleApiRequest("/critical_op", "some_data"); - } - - service.performHealthCheck(); - } -} -``` - -**Bad example:** - -```java -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.Objects; - -public class UnmonitoredService { - private static final Logger logger = LoggerFactory.getLogger(UnmonitoredService.class); - - public void doWork() { - try { - logger.info("Work started."); - // ... some logic ... - if (Math.random() > 0.5) { - throw new Exception("Something went wrong randomly!"); - } - logger.info("Work finished."); - } catch (Exception e) { - // Logs an error, but if logs are only on local disk and not monitored, - // this critical issue might go unnoticed for a long time. - logger.error("Error during work", e); - } - } - - public static void main(String[] args) { - UnmonitoredService service = new UnmonitoredService(); - for (int i = 0; i < 5; i++) { - service.doWork(); - } - // Problem: Logs are likely just going to console or a local file. - // - No central aggregation: Difficult to search across instances or time. - // - No alerts: Critical errors might be missed until users report them. - // - No analysis: Trends or recurring non-fatal issues are hard to spot. - } -} -``` - -### Example 7: Incorporate Logging in Testing - -Title: Validate Logging Behavior and Impact During Testing -Description: Ensure logging works as expected and doesn't negatively impact the application. Assert that specific log messages are generated, verify log formats, test different logging levels, and check performance impact. - -**Good example:** - -```java -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.Objects; - -// ---- System Under Test ---- -class ImportantService { - private static final Logger logger = LoggerFactory.getLogger(ImportantService.class); - - public void processImportantData(String dataId) { - if (Objects.isNull(dataId)) { - logger.error("Data ID is null, cannot process."); - throw new IllegalArgumentException("Data ID cannot be null"); - } - if (dataId.startsWith("invalid_")) { - logger.warn("Received potentially invalid data ID: {}", dataId); - // continue processing with caution - } - logger.info("Processing data for ID: {}", dataId); - // ... actual processing ... - } -} - -// ---- Test Example (Conceptual - would use JUnit/TestNG) ---- -public class LoggingTestExample { - - // This would be actual test code using testing frameworks - public void demonstrateLoggingTests() { - ImportantService service = new ImportantService(); - - // Test 1: Verify ERROR log for null input - try { - service.processImportantData(null); - // Should not reach here - } catch (IllegalArgumentException e) { - // In real test, would capture logs and assert: - // - ERROR level log contains "Data ID is null" - // - Only one ERROR log entry - System.out.println("Test 1 passed: ERROR log generated for null input"); - } - - // Test 2: Verify WARN log for invalid input - service.processImportantData("invalid_XYZ"); - // In real test, would assert: - // - WARN level log contains "Received potentially invalid data ID: invalid_XYZ" - // - INFO level log contains "Processing data for ID: invalid_XYZ" - System.out.println("Test 2 passed: WARN and INFO logs generated for invalid input"); - - // Test 3: Verify INFO log for valid input - service.processImportantData("valid_123"); - // In real test, would assert: - // - Only INFO level log contains "Processing data for ID: valid_123" - // - No WARN or ERROR logs - System.out.println("Test 3 passed: INFO log generated for valid input"); - } - - public void performanceTestExample() { - ImportantService service = new ImportantService(); - - // Performance test: measure logging overhead - long startTime = System.currentTimeMillis(); - - for (int i = 0; i < 1000; i++) { - service.processImportantData("test_" + i); - } - - long duration = System.currentTimeMillis() - startTime; - System.out.println("Performance test: 1000 operations completed in " + duration + "ms"); - - // In real test, would assert that logging overhead is within acceptable limits - // e.g., assert duration < 1000; // Less than 1ms per operation including logging - } - - public static void main(String[] args) { - LoggingTestExample test = new LoggingTestExample(); - test.demonstrateLoggingTests(); - test.performanceTestExample(); - - System.out.println("In real implementation, use JUnit/TestNG with LogCapture utilities"); - System.out.println("Example dependencies: ch.qos.logback.classic.Logger with ListAppender"); - } -} -``` - -**Bad example:** - -```java -// Code that is hard to test for logging behavior -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.Objects; - -public class UntestableLogging { - private static final Logger logger = LoggerFactory.getLogger(UntestableLogging.class); - - public void complexOperation(String input) { - // ... many lines of code ... - if (input.equals("problem")) { - // Log message is deeply embedded, possibly conditional, hard to trigger specifically - // and verify without significant effort or refactoring. - logger.warn("A specific problem occurred with input: {}", input); - } - // ... more code ... - // No clear separation of concerns, making it hard to isolate and test logging. - } - - public void methodWithSideEffects(String data) { - // Expensive logging operation always executed - logger.debug("Processing data: {}", buildExpensiveLogMessage(data)); - - // No way to test if logging is impacting performance - // No way to verify log content without running expensive operations - } - - private String buildExpensiveLogMessage(String data) { - // Simulate expensive operation that shouldn't run in production - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 10000; i++) { - sb.append("Debug info: ").append(data).append(" iteration: ").append(i); - } - return sb.toString(); - } - - public static void main(String[] args) { - UntestableLogging ul = new UntestableLogging(); - ul.complexOperation("test"); - ul.complexOperation("problem"); - ul.methodWithSideEffects("example"); - - // Problem: Without proper test utilities or testable design, - // verifying that the WARN message is logged correctly (and only when expected) - // is difficult. Developers might skip testing logging, leading to unverified log output. - } -} -``` - -## Output Format - -- **ANALYZE** Java code to identify specific logging issues and categorize them by impact (CRITICAL, SECURITY, PERFORMANCE, MAINTAINABILITY, OBSERVABILITY) and logging area (framework selection, log levels, security, configuration, performance) -- **CATEGORIZE** logging improvements found: Framework Issues (inconsistent frameworks vs standardized SLF4J, outdated implementations vs modern logging), Log Level Problems (inappropriate levels vs proper level usage, missing contextual information vs structured logging), Security Issues (sensitive data exposure vs secure logging practices, plaintext credentials vs masked data), Configuration Problems (hardcoded settings vs environment-specific configuration, missing structured formats vs JSON/structured output), and Performance Issues (synchronous logging vs asynchronous patterns, excessive logging overhead vs optimized performance, string concatenation vs parameterized messages) -- **APPLY** logging best practices directly by implementing the most appropriate improvements for each identified issue: Standardize on SLF4J facade with appropriate implementation (Logback/Log4j2), implement proper log level usage with contextual information, secure sensitive data through masking and filtering, configure environment-specific settings with external configuration files, optimize performance through asynchronous logging and parameterized messages, and establish structured logging with JSON output for better parsing and analysis -- **IMPLEMENT** comprehensive logging refactoring using proven patterns: Migrate to SLF4J facade with consistent logger declarations, establish proper log level hierarchy (ERROR for failures, WARN for degraded service, INFO for business events, DEBUG for troubleshooting), implement secure logging practices with data masking and filtering, configure environment-specific logging with external properties, optimize performance through async appenders and lazy evaluation, and integrate structured logging with correlation IDs and contextual metadata -- **REFACTOR** code systematically following the logging improvement roadmap: First standardize logging framework usage through SLF4J adoption, then optimize log level usage and add proper contextual information, implement security measures for sensitive data protection, configure environment-specific logging settings with external configuration, optimize logging performance through asynchronous patterns and parameterized messages, and establish comprehensive monitoring and alerting integration -- **EXPLAIN** the applied logging improvements and their benefits: Framework standardization benefits through SLF4J facade adoption and consistent logger usage, observability enhancements via proper log levels and structured output, security improvements through sensitive data masking and secure logging practices, performance optimizations from asynchronous logging and parameterized messages, and operational benefits from environment-specific configuration and monitoring integration -- **VALIDATE** that all applied logging refactoring compiles successfully, maintains existing functionality, eliminates security vulnerabilities, follows logging best practices, and achieves the intended observability and performance improvements through comprehensive testing and verification - -## Safeguards - -- **BLOCKING SAFETY CHECK**: ALWAYS run `./mvnw compile` before ANY logging recommendations -- **CRITICAL VALIDATION**: Execute `./mvnw clean verify` to ensure all tests pass after logging changes -- **SECURITY VERIFICATION**: Validate that no sensitive data (passwords, PII, tokens) is logged directly -- **PERFORMANCE MONITORING**: Ensure logging configurations don't introduce significant performance overhead -- **CONFIGURATION VALIDATION**: Verify logging configuration files are syntactically correct and environment-appropriate -- **ROLLBACK READINESS**: Ensure all logging changes can be easily reverted without system disruption -- **INCREMENTAL SAFETY**: Apply logging improvements incrementally, validating after each modification -- **LOG LEVEL VERIFICATION**: Confirm log levels are appropriate for production environments (avoid DEBUG/TRACE in prod) -- **DEPENDENCY COMPATIBILITY**: Verify logging framework dependencies don't conflict with existing project dependencies -- **OPERATIONAL CONTINUITY**: Ensure logging changes don't break existing monitoring, alerting, or log aggregation systems \ No newline at end of file diff --git a/skills/180-java-observability-logging/SKILL.md b/skills/181-java-observability-logging/SKILL.md similarity index 94% rename from skills/180-java-observability-logging/SKILL.md rename to skills/181-java-observability-logging/SKILL.md index de209938..f06ca100 100644 --- a/skills/180-java-observability-logging/SKILL.md +++ b/skills/181-java-observability-logging/SKILL.md @@ -1,5 +1,5 @@ --- -name: 180-java-observability-logging +name: 181-java-observability-logging description: Use when you need to implement or improve Java logging and observability — including selecting SLF4J with Logback/Log4j2, applying proper log levels (ERROR, WARN, INFO, DEBUG, TRACE), parameterized logging, secure logging without sensitive data exposure, environment-specific configuration, log aggregation and monitoring, or validating logging through tests. This should trigger for requests such as Improve logging; Apply logging; Refactor logging; Add logging support. Part of cursor-rules-java project license: Apache-2.0 metadata: @@ -46,7 +46,7 @@ Run `./mvnw compile` or `mvn compile` and stop immediately if compilation fails. 2. **Read logging reference and assess current observability** -Read `references/180-java-observability-logging.md` and evaluate framework usage, log levels, sensitive-data handling, and config gaps. +Read `references/181-java-observability-logging.md` and evaluate framework usage, log levels, sensitive-data handling, and config gaps. 3. **Apply logging and observability improvements** @@ -58,4 +58,4 @@ Run `./mvnw clean verify` or `mvn clean verify` after applying improvements. ## Reference -For detailed guidance, examples, and constraints, see [references/180-java-observability-logging.md](references/180-java-observability-logging.md). +For detailed guidance, examples, and constraints, see [references/181-java-observability-logging.md](references/181-java-observability-logging.md). diff --git a/skills/181-java-observability-logging/references/181-java-observability-logging.md b/skills/181-java-observability-logging/references/181-java-observability-logging.md new file mode 100644 index 00000000..12f34f6d --- /dev/null +++ b/skills/181-java-observability-logging/references/181-java-observability-logging.md @@ -0,0 +1,85 @@ +--- +name: 181-java-observability-logging +description: Use when you need to implement or improve Java logging and observability — including selecting SLF4J with Logback/Log4j2, applying proper log levels, parameterized logging, secure logging without sensitive data exposure, environment-specific configuration, log aggregation and monitoring, or validating logging through tests. +license: Apache-2.0 +metadata: + author: Juan Antonio Breña Moral + version: 0.15.0-SNAPSHOT +--- +# Java Logging Best Practices + +## Role + +You are a Senior software engineer with extensive experience in Java software development + +## Goal + +Effective Java logging requires a consistent framework (SLF4J facade with Logback/Log4j2), +deliberate log level usage (ERROR, WARN, INFO, DEBUG, TRACE), and secure operational practices. +Logging must provide useful diagnostics, avoid sensitive data leakage, and stay sustainable in production. + +## Constraints + +Logging improvements must preserve security and operational stability. + +- **NO SENSITIVE DATA**: Never log passwords, tokens, secrets, or personal data in plain text +- **LEVEL DISCIPLINE**: Use consistent levels and avoid noisy INFO/DEBUG misuse in hot paths +- **PARAMETERIZED LOGGING**: Prefer placeholders instead of string concatenation +- **VALIDATE**: Confirm log behavior through tests and run full verification after changes + +## Examples + +### Table of contents + +- Example 1: Use Structured and Parameterized Logging + +### Example 1: Use Structured and Parameterized Logging + +Title: Prefer stable templates and explicit context +Description: + +**Good example:** + +```java +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class OrderService { + private static final Logger logger = LoggerFactory.getLogger(OrderService.class); + + public void process(String orderId) { + logger.info("Processing order {}", orderId); + try { + // business logic + logger.debug("Order {} processed successfully", orderId); + } catch (RuntimeException ex) { + logger.error("Order {} failed", orderId, ex); + throw ex; + } + } +} +``` + +**Bad example:** + +```java +public final class OrderServiceBad { + public void process(String orderId, String token) { + // BAD: no framework, no level semantics, leaks sensitive data + System.out.println("order=" + orderId + ", token=" + token); + } +} +``` + +## Output Format + +- **ANALYZE** logging issues by level semantics, framework usage, and security risk +- **APPLY** SLF4J-based structured logging and replace anti-patterns +- **EXPLAIN** operational impact of each logging improvement +- **VALIDATE** compilation/tests and verify no sensitive data is logged + +## Safeguards + +- **SECURITY CHECK**: redact or remove sensitive fields before logging +- **NOISE CONTROL**: avoid excessive logs in loops and performance-critical paths +- **CONSISTENCY**: keep logger naming, formats, and level usage uniform \ No newline at end of file diff --git a/skills/182-java-observability-metrics-micrometer/SKILL.md b/skills/182-java-observability-metrics-micrometer/SKILL.md new file mode 100644 index 00000000..d5e0c353 --- /dev/null +++ b/skills/182-java-observability-metrics-micrometer/SKILL.md @@ -0,0 +1,61 @@ +--- +name: 182-java-observability-metrics-micrometer +description: Use when you need to implement or improve Java metrics observability with Micrometer — including meter design, naming/tag conventions, cardinality control, timers/counters/gauges/distribution summaries, percentiles/histograms, Actuator/Prometheus integration, and metrics validation through tests. This should trigger for requests such as Improve metrics; Apply Micrometer; Add metrics observability; Refactor Micrometer instrumentation. Part of cursor-rules-java project +license: Apache-2.0 +metadata: + author: Juan Antonio Breña Moral + version: 0.15.0-SNAPSHOT +--- +# Java Metrics Observability with Micrometer + +Implement effective Java metrics instrumentation with Micrometer by defining meaningful service-level metrics, controlling cardinality, selecting the right meter type, and exposing production-ready telemetry for dashboards and alerting. + +**What is covered in this Skill?** + +- Metrics-first observability with Micrometer in Java applications +- Meter selection: Counter, Timer, DistributionSummary, Gauge, LongTaskTimer +- Naming and tagging conventions with low-cardinality dimensions +- Cardinality and meter lifecycle safeguards to prevent time-series explosion +- Histogram/percentile strategy and SLO-oriented metrics design +- Integration guidance for Actuator + Prometheus/OpenTelemetry pipelines +- Testing and verification of metrics registration and values + +**Scope:** Application-level metrics design and instrumentation quality for Java services, with emphasis on operationally useful and cost-efficient telemetry. + +## Constraints + +Metrics instrumentation must be operationally safe, low-cardinality, and validated. Poor tag design or excessive meter creation can degrade observability systems and increase costs. + +- **LOW CARDINALITY FIRST**: Never tag metrics with unbounded values (userId, UUID, raw URL, full exception message) +- **RIGHT METER TYPE**: Use Counter for monotonically increasing events, Timer for latency, Gauge for point-in-time state, and DistributionSummary for sampled values +- **BEFORE APPLYING**: Read the reference for good/bad instrumentation examples and anti-patterns +- **VERIFY**: Run `./mvnw clean verify` or `mvn clean verify` after changes + +## When to use this skill + +- Improve metrics +- Apply Micrometer +- Add metrics observability +- Refactor Micrometer instrumentation + +## Workflow + +1. **Define measurement goals and meter contract** + +Identify key service indicators (throughput, latency, error ratio, saturation) and map each to stable metric names, units, and low-cardinality tags. + +2. **Select meter types and instrument code paths** + +Apply Counter/Timer/Gauge/DistributionSummary/LongTaskTimer where appropriate, ensuring consistent naming conventions and reusable tags. + +3. **Harden instrumentation for production** + +Control cardinality, avoid dynamic meter churn, configure histogram/percentile strategy only where needed, and align export settings with the telemetry backend. + +4. **Validate and operationalize metrics** + +Verify metrics in tests and runtime endpoints, confirm expected labels/units, and ensure dashboards/alerts can consume the emitted series. + +## Reference + +For detailed guidance, examples, and constraints, see [references/182-java-observability-metrics-micrometer.md](references/182-java-observability-metrics-micrometer.md). diff --git a/skills/182-java-observability-metrics-micrometer/references/182-java-observability-metrics-micrometer.md b/skills/182-java-observability-metrics-micrometer/references/182-java-observability-metrics-micrometer.md new file mode 100644 index 00000000..328f1d7a --- /dev/null +++ b/skills/182-java-observability-metrics-micrometer/references/182-java-observability-metrics-micrometer.md @@ -0,0 +1,461 @@ +--- +name: 182-java-observability-metrics-micrometer +description: Use when you need to implement or improve Java metrics observability with Micrometer — including meter design, naming/tag conventions, cardinality control, timers/counters/gauges/distribution summaries, percentiles/histograms, Actuator/Prometheus integration, and metrics validation through tests. +license: Apache-2.0 +metadata: + author: Juan Antonio Breña Moral + version: 0.15.0-SNAPSHOT +--- +# Java Metrics Observability with Micrometer + +## Role + +You are a Senior software engineer with extensive experience in Java software development + +## Goal + +Effective Java metrics observability with Micrometer starts by defining a clear metric contract for service-level signals: +throughput, latency, errors, and saturation. +Instrumentation must be stable over time, use semantic names, and avoid unbounded labels. +The right meter type (Counter, Timer, Gauge, DistributionSummary, LongTaskTimer) should match the behavior being measured. +Histograms and percentiles should be enabled selectively to control overhead and cost. +Finally, metrics should be validated through tests and runtime checks to ensure dashboards and alerts remain reliable. + +## Constraints + +Metrics instrumentation must avoid high-cardinality dimensions and dynamic meter churn. Uncontrolled tagging can overload telemetry backends and make observability unreliable. + +- **CARDINALITY SAFETY**: Never use user-controlled, unique, or high-cardinality tag values (UUID, email, request id, full path with IDs) +- **METER SEMANTICS**: Select meter types based on behavior (Counter for events, Timer for latency, Gauge for state, DistributionSummary for value distributions) +- **INSTRUMENTATION SCOPE**: Prefer business and platform signals tied to SLOs over noisy low-value metrics +- **VALIDATION**: Verify expected metrics names, tags, and values through automated tests and runtime checks + +## Examples + +### Table of contents + +- Example 1: Select the Right Meter Type +- Example 2: Use Stable Naming and Low-Cardinality Tags +- Example 3: Validate Instrumentation with Tests +- Example 4: Track Long-Running Jobs with LongTaskTimer +- Example 5: Configure Histograms and Percentiles Selectively +- Example 6: Expose Metrics via Spring Boot Actuator and Prometheus +- Example 7: Use Annotation-Based Observation for Service Methods + +### Example 1: Select the Right Meter Type + +Title: Use Counters, Timers, Gauges and Summaries with Correct Semantics +Description: + +**Good example:** + +```java +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import java.time.Duration; +import java.util.concurrent.atomic.AtomicInteger; + +public final class CheckoutMetrics { + + private final Counter checkoutSuccess; + private final Counter checkoutFailure; + private final Timer checkoutLatency; + private final DistributionSummary payloadBytes; + private final AtomicInteger queueDepth; + + public CheckoutMetrics(MeterRegistry registry) { + this.checkoutSuccess = Counter.builder("checkout.requests") + .description("Total checkout requests") + .tag("outcome", "success") + .register(registry); + this.checkoutFailure = Counter.builder("checkout.requests") + .description("Total checkout requests") + .tag("outcome", "failure") + .register(registry); + this.checkoutLatency = Timer.builder("checkout.request.duration") + .description("Checkout request duration") + .serviceLevelObjectives( + Duration.ofMillis(100), + Duration.ofMillis(300), + Duration.ofSeconds(1)) + .publishPercentileHistogram() + .minimumExpectedValue(Duration.ofMillis(1)) + .maximumExpectedValue(Duration.ofSeconds(5)) + .register(registry); + this.payloadBytes = DistributionSummary.builder("checkout.payload.bytes") + .description("Payload size of checkout requests") + .baseUnit("bytes") + .register(registry); + this.queueDepth = new AtomicInteger(0); + Gauge.builder("checkout.queue.depth", queueDepth, AtomicInteger::get) + .description("Number of checkout requests waiting in the queue") + .register(registry); + } + + public void recordSuccess(long nanos, int bytes) { + checkoutSuccess.increment(); + checkoutLatency.record(Duration.ofNanos(nanos)); + payloadBytes.record(bytes); + } + + public void recordFailure(long nanos) { + checkoutFailure.increment(); + checkoutLatency.record(Duration.ofNanos(nanos)); + } + + public void setQueueDepth(int depth) { + queueDepth.set(Math.max(depth, 0)); + } +} +``` + +**Bad example:** + +```java +import io.micrometer.core.instrument.MeterRegistry; + +public final class BadMetrics { + + public void record(MeterRegistry registry, String userId, long latencyMillis) { + // BAD: every signal is a counter without clear semantics + registry.counter("checkout.metric", "type", "latency").increment(latencyMillis); + + // BAD: high cardinality tag + registry.counter("checkout.requests", "userId", userId).increment(); + + // BAD: dynamic meter names create unbounded meter churn + registry.counter("checkout.requests." + userId).increment(); + + // BAD: timer should be used for latency, not counter with duration values + registry.counter("checkout.duration.millis").increment(latencyMillis); + } +} +``` + +### Example 2: Use Stable Naming and Low-Cardinality Tags + +Title: Keep Metric Schema Predictable for Dashboards and Alerts +Description: + +**Good example:** + +```java +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import java.util.Locale; +import java.util.Set; + +public final class PaymentMetrics { + + private static final Set ALLOWED_RESULTS = Set.of("approved", "declined", "error"); + private final MeterRegistry registry; + + public PaymentMetrics(MeterRegistry registry) { + this.registry = registry; + } + + public void recordResult(String provider, String result) { + String safeProvider = normalizeProvider(provider); // bounded set + String safeResult = ALLOWED_RESULTS.contains(result) ? result : "error"; + + Counter.builder("payment.authorization.requests") + .tag("provider", safeProvider) + .tag("result", safeResult) + .register(registry) + .increment(); + } + + private String normalizeProvider(String provider) { + String normalized = provider == null ? "" : provider.toLowerCase(Locale.ROOT); + return switch (normalized) { + case "adyen", "stripe", "paypal" -> normalized; + default -> "unknown"; + }; + } +} +``` + +**Bad example:** + +```java +import io.micrometer.core.instrument.MeterRegistry; + +public final class PaymentMetricsBad { + public void record(MeterRegistry registry, String fullUrl, String requestId, String exceptionMessage) { + // BAD: unbounded tags explode cardinality + registry.counter("payment.requests", + "url", fullUrl, + "requestId", requestId, + "error", exceptionMessage).increment(); + } +} +``` + +### Example 3: Validate Instrumentation with Tests + +Title: Assert Metric Presence, Tags, and Values +Description: + +**Good example:** + +```java +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + +class PaymentMetricsTest { + + @Test + void shouldRecordApprovedPayment() { + var registry = new SimpleMeterRegistry(); + var metrics = new PaymentMetrics(registry); + + metrics.recordResult("stripe", "approved"); + metrics.recordResult("stripe", "approved"); + + double count = registry.get("payment.authorization.requests") + .tag("provider", "stripe") + .tag("result", "approved") + .counter() + .count(); + + assertThat(count).isEqualTo(2.0d); + } +} +``` + +**Bad example:** + +```java +class PaymentMetricsTestBad { + // BAD: no metric assertions; instrumentation can silently break. +} +``` + +### Example 4: Track Long-Running Jobs with LongTaskTimer + +Title: Measure Active Task Duration and Concurrency for Batch and Background Work +Description: + +**Good example:** + +```java +import io.micrometer.core.instrument.LongTaskTimer; +import io.micrometer.core.instrument.MeterRegistry; + +public final class ReportGenerationService { + + private final LongTaskTimer activeReports; + + public ReportGenerationService(MeterRegistry registry) { + this.activeReports = LongTaskTimer.builder("report.generation.active") + .description("Tracks reports actively being generated") + .tag("type", "scheduled") + .register(registry); + } + + public void generate(String reportId) { + LongTaskTimer.Sample sample = activeReports.start(); + try { + // long-running report generation + doGenerateReport(reportId); + } finally { + sample.stop(); + } + } + + private void doGenerateReport(String reportId) { + // simulate work + } +} +``` + +**Bad example:** + +```java +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; + +public final class ReportGenerationServiceBad { + + private final MeterRegistry registry; + private final Timer reportTimer; + + public ReportGenerationServiceBad(MeterRegistry registry) { + this.registry = registry; + // BAD: Timer measures completed durations; it cannot track how long + // currently-running tasks have been active (use LongTaskTimer for that) + this.reportTimer = Timer.builder("report.generation.duration").register(registry); + } + + public void generate(String reportId) { + Timer.Sample sample = Timer.start(registry); + try { + doGenerateReport(reportId); + } finally { + // This only records after completion — no visibility into in-flight tasks + sample.stop(reportTimer); + } + } + + private void doGenerateReport(String reportId) { } +} +``` + +### Example 5: Configure Histograms and Percentiles Selectively + +Title: Enable SLO-Oriented Buckets and Percentiles Only Where They Add Value +Description: + +**Good example:** + +```java +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import java.time.Duration; + +public final class ApiLatencyMetrics { + + private final Timer apiLatency; + + public ApiLatencyMetrics(MeterRegistry registry) { + this.apiLatency = Timer.builder("api.request.duration") + .description("HTTP API request latency") + .tag("service", "checkout") + // Publish histogram buckets for Prometheus histogram_quantile() + .publishPercentileHistogram() + // Declare SLO thresholds to generate histogram buckets aligned to SLOs + .serviceLevelObjectives( + Duration.ofMillis(100), + Duration.ofMillis(300), + Duration.ofSeconds(1)) + // Cap the histogram range to avoid very large bucket counts + .minimumExpectedValue(Duration.ofMillis(1)) + .maximumExpectedValue(Duration.ofSeconds(5)) + .register(registry); + } + + public void record(Runnable operation) { + apiLatency.record(operation); + } +} +``` + +**Bad example:** + +```java +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; + +public final class ApiLatencyMetricsBad { + + public ApiLatencyMetricsBad(MeterRegistry registry) { + // BAD: publishPercentiles() computes percentiles in-process (non-aggregatable) + // and publishPercentileHistogram() without bounds creates unbounded buckets + Timer.builder("api.request.duration") + .publishPercentiles(0.5, 0.95, 0.99) // not aggregatable across instances + .publishPercentileHistogram() // no min/max bounds — costly + .register(registry); + } +} +``` + +### Example 6: Expose Metrics via Spring Boot Actuator and Prometheus + +Title: Configure Scrape Endpoint, Common Tags, and Prometheus Naming +Description: + +**Good example:** + +```yaml +# application.yml +management: + endpoints: + web: + exposure: + include: health,info,prometheus + metrics: + tags: + application: ${spring.application.name} + environment: ${spring.profiles.active:default} + distribution: + percentiles-histogram: + http.server.requests: true + slo: + http.server.requests: 100ms,300ms,1s +``` + +**Bad example:** + +```yaml +# BAD: exposes all endpoints and no common tags — loses context in multi-instance deployments +management: + endpoints: + web: + exposure: + include: "*" +``` + + +### Example 7: Use Annotation-Based Observation for Service Methods + +Title: Prefer @Observed with low-cardinality key values for concise instrumentation +Description: + +**Good example:** + +```java +import io.micrometer.observation.annotation.Observed; +import org.springframework.stereotype.Service; + +@Service +public class InvoiceService { + + @Observed( + name = "invoice.generate", + contextualName = "invoice-generate", + lowCardinalityKeyValues = {"channel", "api", "outcome", "success"}) + public String generateInvoice(String orderId) { + // business logic + return "INV-" + orderId; + } +} +``` + +**Bad example:** + +```java +import io.micrometer.observation.annotation.Observed; +import org.springframework.stereotype.Service; + +@Service +public class InvoiceServiceBad { + + @Observed( + name = "invoice.generate", + // BAD: high-cardinality and sensitive values must not be encoded as metric dimensions + lowCardinalityKeyValues = {"user.id", "93f8b9d0-5c4f-4fd4-aeb2-1de0d0b16e8a", "email", "user@example.com"}) + public String generateInvoice(String orderId) { + return "INV-" + orderId; + } +} +``` + +## Output Format + +- **ANALYZE** current instrumentation and classify issues by cardinality, semantic correctness, naming consistency, and operational value +- **DESIGN** a stable metric contract: names, units, bounded tags, meter types, and where each metric is emitted +- **APPLY** Micrometer best practices directly in code with minimal, explicit, and reusable instrumentation points +- **EXPLAIN** how each metric supports dashboards, alerts, and SLO/SLA reporting +- **VALIDATE** correctness through tests and runtime endpoint checks, including expected labels and values + +## Safeguards + +- **CARDINALITY GUARDRAIL**: Reject unbounded labels and normalize dynamic values into bounded buckets +- **NO METER CHURN**: Avoid building meters in tight loops with dynamic names/tags +- **BACKEND COMPATIBILITY**: Ensure names and tags remain compatible with Prometheus/OpenTelemetry conventions +- **OPERABILITY CHECK**: Confirm metrics are scrapeable/exported and usable in dashboards and alerts +- **REGRESSION SAFETY**: Keep existing metric names stable unless migration is explicitly planned \ No newline at end of file diff --git a/skills/183-observability-tracing-opentelemetry/SKILL.md b/skills/183-observability-tracing-opentelemetry/SKILL.md new file mode 100644 index 00000000..51eaae74 --- /dev/null +++ b/skills/183-observability-tracing-opentelemetry/SKILL.md @@ -0,0 +1,62 @@ +--- +name: 183-observability-tracing-opentelemetry +description: Use when you need to implement or improve distributed tracing with OpenTelemetry in Java — including trace/span modeling, context propagation, semantic conventions, span attributes/events/status, sampling strategy, baggage usage, privacy safeguards, and backend integration with OTLP collectors. This should trigger for requests such as Improve tracing; Apply OpenTelemetry tracing; Add distributed tracing; Refactor tracing instrumentation. Part of cursor-rules-java project +license: Apache-2.0 +metadata: + author: Juan Antonio Breña Moral + version: 0.15.0-SNAPSHOT +--- +# Java Distributed Tracing with OpenTelemetry + +Implement robust distributed tracing in Java with OpenTelemetry by modeling meaningful spans, preserving context propagation, and instrumenting critical business and infrastructure paths with low-overhead, privacy-safe telemetry. + +**What is covered in this Skill?** + +- OpenTelemetry tracing fundamentals for Java services +- Span design: boundaries, parent/child relationships, and operation naming +- Context propagation across HTTP, messaging, async tasks, and thread boundaries +- Semantic conventions and stable attribute naming +- Error/status/event recording best practices +- Sampling strategy and performance/cost trade-offs +- Privacy and security controls for trace attributes +- Testing and verification of trace propagation and span correctness + +**Scope:** Distributed tracing quality in application and integration layers, focused on diagnosability, consistency, and operational safety. + +## Constraints + +Tracing instrumentation must preserve context correctly and avoid leaking sensitive data. Over-instrumentation and high-cardinality attributes can harm cost and signal quality. + +- **PROPAGATION FIRST**: Ensure context propagation across all sync/async boundaries before adding extra span detail +- **NO SENSITIVE DATA**: Never store secrets, credentials, tokens, raw payloads, or PII in span attributes/events +- **LOW CARDINALITY ATTRIBUTES**: Avoid unbounded values in attributes that are used for aggregation/search +- **VERIFY**: Run `./mvnw clean verify` or `mvn clean verify` after applying tracing changes + +## When to use this skill + +- Improve tracing +- Apply OpenTelemetry tracing +- Add distributed tracing +- Refactor tracing instrumentation + +## Workflow + +1. **Define trace model and critical flows** + +Identify high-value request and async flows, define operation boundaries, and choose span names/attributes aligned with semantic conventions. + +2. **Instrument and propagate context** + +Add OpenTelemetry spans to key boundaries and ensure trace context is propagated across HTTP clients/servers, messaging, and executor-based async work. + +3. **Harden span data and sampling** + +Record status/errors/events consistently, remove sensitive data, control attribute cardinality, and configure sampling/exporters according to environment needs. + +4. **Validate traces end-to-end** + +Verify parent-child relationships, propagation continuity, and backend visibility through tests and runtime checks. + +## Reference + +For detailed guidance, examples, and constraints, see [references/183-observability-tracing-opentelemetry.md](references/183-observability-tracing-opentelemetry.md). diff --git a/skills/183-observability-tracing-opentelemetry/references/183-observability-tracing-opentelemetry.md b/skills/183-observability-tracing-opentelemetry/references/183-observability-tracing-opentelemetry.md new file mode 100644 index 00000000..c3a06744 --- /dev/null +++ b/skills/183-observability-tracing-opentelemetry/references/183-observability-tracing-opentelemetry.md @@ -0,0 +1,432 @@ +--- +name: 183-observability-tracing-opentelemetry +description: Use when you need to implement or improve distributed tracing with OpenTelemetry in Java — including trace/span modeling, context propagation, semantic conventions, span attributes/events/status, sampling strategy, baggage usage, privacy safeguards, and backend integration with OTLP collectors. +license: Apache-2.0 +metadata: + author: Juan Antonio Breña Moral + version: 0.15.0-SNAPSHOT +--- +# Java Distributed Tracing with OpenTelemetry + +## Role + +You are a Senior software engineer with extensive experience in Java software development + +## Goal + +Effective distributed tracing with OpenTelemetry in Java requires clear span boundaries, reliable context propagation, +semantic consistency, and strict privacy controls. +Traces should help diagnose latency, failure causes, and cross-service dependencies without introducing excessive overhead. + +## Constraints + +Tracing data must remain safe and operationally useful. Incorrect propagation or sensitive attributes reduce trust in telemetry. + +- **CONTEXT CONTINUITY**: Ensure trace context is propagated across HTTP/messaging/async boundaries +- **SEMANTIC CONSISTENCY**: Use stable span names and OpenTelemetry semantic conventions where applicable +- **DATA MINIMIZATION**: Avoid high-cardinality and sensitive attributes in spans/events +- **SAMPLING STRATEGY**: Configure sampling per environment to balance cost and diagnostic depth + +## Examples + +### Table of contents + +- Example 1: Create Spans with Clear Boundaries +- Example 2: Propagate Context and Keep Attributes Safe +- Example 3: Apply OpenTelemetry Semantic Conventions +- Example 4: Configure Sampling Strategy per Environment +- Example 5: Validate Span Correctness with Tests +- Example 6: Use Annotation-Based Instrumentation for Service Methods + +### Example 1: Create Spans with Clear Boundaries + +Title: Model request and dependency calls as parent/child spans +Description: + +**Good example:** + +```java +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; + +public final class CheckoutTracing { + + private final Tracer tracer; + + public CheckoutTracing(OpenTelemetry openTelemetry) { + this(openTelemetry.getTracer("checkout-service")); + } + + CheckoutTracing(Tracer tracer) { + this.tracer = tracer; + } + + public void checkout(String orderId) { + Span parent = tracer.spanBuilder("checkout.process") + .setSpanKind(SpanKind.INTERNAL) + .startSpan(); + try (Scope ignored = parent.makeCurrent()) { + parent.setAttribute("order.id.present", orderId != null); + + callInventory(); + callPayment(); + + parent.setStatus(StatusCode.OK); + } catch (RuntimeException ex) { + parent.recordException(ex); + parent.setStatus(StatusCode.ERROR, "Checkout failed"); + throw ex; + } finally { + parent.end(); + } + } + + private void callInventory() { + Span span = tracer.spanBuilder("inventory.reserve").setSpanKind(SpanKind.CLIENT).startSpan(); + try (Scope ignored = span.makeCurrent()) { + // HTTP call / client SDK call + span.setStatus(StatusCode.OK); + } catch (RuntimeException ex) { + span.recordException(ex); + span.setStatus(StatusCode.ERROR); + throw ex; + } finally { + span.end(); + } + } + + private void callPayment() { + Span span = tracer.spanBuilder("payment.charge").setSpanKind(SpanKind.CLIENT).startSpan(); + try (Scope ignored = span.makeCurrent()) { + span.setStatus(StatusCode.OK); + } catch (RuntimeException ex) { + span.recordException(ex); + span.setStatus(StatusCode.ERROR, "Payment charge failed"); + throw ex; + } finally { + span.end(); + } + } +} +``` + +**Bad example:** + +```java +public final class CheckoutTracingBad { + public void checkout(String userEmail, String token) { + // BAD: no spans/context, impossible to diagnose flow latency + // BAD: sensitive values often leak when teams later add naive attributes + System.out.println("processing " + userEmail + " with token " + token); + } +} +``` + +### Example 2: Propagate Context and Keep Attributes Safe + +Title: Ensure continuity and avoid cardinality/privacy problems +Description: + +**Good example:** + +```java +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import java.util.concurrent.Executor; + +public final class AsyncFlow { + private final Executor executor; + + public AsyncFlow(Executor executor) { + this.executor = executor; + } + + public void submitTask() { + Context captured = Context.current(); + executor.execute(captured.wrap(() -> { + Span.current().addEvent("async.task.started"); + // do work with the captured parent trace context + })); + } +} +``` + +**Bad example:** + +```java +import io.opentelemetry.api.trace.Span; + +public final class AsyncFlowBad { + public void submitTask(String userId, String jwtToken) { + // BAD: no context propagation, detached spans + // BAD: high cardinality + sensitive attributes + Span.current().setAttribute("user.id", userId); + Span.current().setAttribute("auth.token", jwtToken); + } +} +``` + +### Example 3: Apply OpenTelemetry Semantic Conventions + +Title: Use Standard Attribute Names for HTTP, Database, and Messaging Spans +Description: + +**Good example:** + +```java +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; + +public final class OrderRepository { + + private static final String DB_SYSTEM_NAME = "db.system.name"; + private static final String DB_NAMESPACE = "db.namespace"; + private static final String DB_OPERATION_NAME = "db.operation.name"; + private static final String DB_COLLECTION_NAME = "db.collection.name"; + + private final Tracer tracer; + + public OrderRepository(OpenTelemetry openTelemetry) { + this.tracer = openTelemetry.getTracer("order-service"); + } + + public void insertOrder(String orderId, int quantity) { + Span span = tracer.spanBuilder("INSERT orders") + .setSpanKind(SpanKind.CLIENT) + .setAttribute(DB_SYSTEM_NAME, "postgresql") + .setAttribute(DB_NAMESPACE, "shop") + .setAttribute(DB_OPERATION_NAME, "INSERT") + .setAttribute(DB_COLLECTION_NAME, "orders") + .startSpan(); + try (Scope ignored = span.makeCurrent()) { + // execute INSERT + span.setStatus(StatusCode.OK); + } catch (RuntimeException ex) { + span.recordException(ex); + span.setStatus(StatusCode.ERROR, "DB insert failed"); + throw ex; + } finally { + span.end(); + } + } +} +``` + +**Bad example:** + +```java +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; + +public final class OrderRepositoryBad { + + public void insertOrder(String orderId) { + Span span = GlobalOpenTelemetry + .getTracer("repo") + .spanBuilder("insert") // BAD: vague name, no kind + .startSpan(); + try { + // execute INSERT + // BAD: custom attribute names instead of semantic conventions + span.setAttribute("db", "postgres"); + span.setAttribute("query_type", "INSERT"); + // BAD: high-cardinality business identifiers do not belong in span attributes + span.setAttribute("order.id", orderId); + } finally { + span.end(); // BAD: no status set, exceptions not recorded + } + } +} +``` + +### Example 4: Configure Sampling Strategy per Environment + +Title: Balance Diagnostic Depth Against Cost and Overhead +Description: + +**Good example:** + +```java +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; + +public final class TracingConfig { + + public static OpenTelemetry buildForEnvironment(String environment) { + Sampler sampler = switch (environment) { + case "production" -> Sampler.traceIdRatioBased(0.05); // 5% in prod + case "staging" -> Sampler.traceIdRatioBased(0.50); // 50% in staging + default -> Sampler.alwaysOn(); // 100% in dev/test + }; + + SdkTracerProvider tracerProvider = SdkTracerProvider.builder() + .setSampler(Sampler.parentBased(sampler)) + .addSpanProcessor(BatchSpanProcessor.builder( + OtlpGrpcSpanExporter.builder() + .setEndpoint("http://otel-collector:4317") + .build()) + .build()) + .build(); + + return OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .buildAndRegisterGlobal(); + } +} +``` + +**Bad example:** + +```java +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.samplers.Sampler; + +public final class TracingConfigBad { + + public static SdkTracerProvider build() { + // BAD: always-on sampling in production causes cost explosion + // BAD: no BatchSpanProcessor — synchronous export blocks request threads + return SdkTracerProvider.builder() + .setSampler(Sampler.alwaysOn()) + .build(); + } +} +``` + +### Example 5: Validate Span Correctness with Tests + +Title: Assert Span Name, Kind, Status, and Attributes Using the In-Memory Exporter +Description: + +**Good example:** + +```java +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class CheckoutTracingTest { + + private InMemorySpanExporter exporter; + private CheckoutTracing sut; + + @BeforeEach + void setUp() { + exporter = InMemorySpanExporter.create(); + SdkTracerProvider provider = SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(exporter)) + .build(); + OpenTelemetry otel = OpenTelemetrySdk.builder() + .setTracerProvider(provider) + .build(); + sut = new CheckoutTracing(otel); + } + + @Test + void shouldRecordCheckoutSpanOnSuccess() { + sut.checkout("order-42"); + + List spans = exporter.getFinishedSpanItems(); + assertThat(spans).hasSizeGreaterThanOrEqualTo(1); + + SpanData root = spans.stream() + .filter(s -> s.getName().equals("checkout.process")) + .findFirst() + .orElseThrow(); + + assertThat(root.getKind()).isEqualTo(SpanKind.INTERNAL); + assertThat(root.getStatus().getStatusCode()).isEqualTo(StatusCode.OK); + assertThat(root.getAttributes().get(AttributeKey.booleanKey("order.id.present"))).isTrue(); + assertThat(root.getAttributes().asMap()).doesNotContainKey(AttributeKey.stringKey("order.id")); + } +} +``` + +**Bad example:** + +```java +class CheckoutTracingTestBad { + // BAD: no span assertions; tracing can silently break without test coverage. + // BAD: using GlobalOpenTelemetry in tests leads to shared state and flaky results. +} +``` + + +### Example 6: Use Annotation-Based Instrumentation for Service Methods + +Title: Leverage @WithSpan and @SpanAttribute for concise, consistent tracing +Description: + +**Good example:** + +```java +import io.opentelemetry.instrumentation.annotations.SpanAttribute; +import io.opentelemetry.instrumentation.annotations.WithSpan; + +public final class ShippingService { + + @WithSpan("shipping.create.label") + public String createLabel( + @SpanAttribute("shipping.provider") String provider, + @SpanAttribute("shipping.expedited") boolean expedited) { + // business logic + return "LBL-123"; + } +} +``` + +**Bad example:** + +```java +import io.opentelemetry.api.trace.Span; + +public final class ShippingServiceBad { + + public String createLabel(String provider, String customerEmail, String requestId) { + // BAD: no explicit span boundary for this business operation + // BAD: sensitive and high-cardinality attributes + Span.current().setAttribute("customer.email", customerEmail); + Span.current().setAttribute("request.id", requestId); + Span.current().setAttribute("provider", provider); + return "LBL-123"; + } +} +``` + +## Output Format + +- **ANALYZE** tracing gaps across span modeling, propagation, and semantic consistency +- **APPLY** OpenTelemetry instrumentation with clear parent/child relationships and stable names +- **HARDEN** attributes/events/status and remove sensitive or high-cardinality data +- **VALIDATE** end-to-end trace continuity in tests and runtime backends + +## Safeguards + +- **PRIVACY FIRST**: Never place secrets or PII in trace payloads +- **PROPAGATION CHECK**: Verify traceId/spanId continuity across service boundaries +- **CARDINALITY CONTROL**: Keep attribute values bounded and query-friendly +- **PERF AWARENESS**: Avoid unnecessary spans in hot loops and configure sampling appropriately \ No newline at end of file