diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md new file mode 100644 index 000000000..beb340ad6 --- /dev/null +++ b/.chglog/CHANGELOG.tpl.md @@ -0,0 +1,67 @@ + + + +{{ if .Versions -}} + +# Unreleased + +{{ if .Unreleased.CommitGroups -}} +{{ range .Unreleased.CommitGroups -}} +## {{ .Title }} + +{{ range .Commits -}} +{{ if and (not (hasPrefix .Subject "changelog rebuild")) (not (hasPrefix .Subject "layer docs update")) (not (hasPrefix .Subject "bump version to")) -}} +* {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end -}} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{ range .Versions }} + +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} +{{ range .CommitGroups -}} + +## {{ .Title }} + +{{ range .Commits -}} +{{ if and (not (hasPrefix .Subject "changelog rebuild")) (not (hasPrefix .Subject "layer docs update")) (not (hasPrefix .Subject "bump version to")) -}} +* {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end -}} +{{ end }} +{{ end -}} + +{{- if .RevertCommits -}} +## Reverts +{{ range .RevertCommits -}} +* {{ .Revert.Header }} +{{ end }} +{{ end -}} + +{{- if .MergeCommits -}} +## Pull Requests + +{{ range .MergeCommits -}} +* {{ .Header }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +## {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{- if .Versions }} +[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD +{{ range .Versions -}} +{{ if .Tag.Previous -}} +[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} +{{ end -}} diff --git a/.chglog/config.yml b/.chglog/config.yml new file mode 100644 index 000000000..4b78ec16e --- /dev/null +++ b/.chglog/config.yml @@ -0,0 +1,37 @@ +style: github +template: CHANGELOG.tpl.md +info: + title: CHANGELOG + repository_url: https://github.com/aws-powertools/powertools-lambda-java +options: + commits: + filters: + Type: + - feat + - fix + - perf + - refactor + - docs + - chore + - revert + commit_groups: + title_maps: + feat: Features + fix: Bug Fixes + perf: Performance Improvements + refactor: Code Refactoring + docs: Documentation + chore: Maintenance + revert: Regression + header: + pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$" + pattern_maps: + - Type + - Scope + - Subject + notes: + keywords: + - BREAKING CHANGE + # issues: + # prefix: + # - # diff --git a/.github/DISCUSSION_TEMPLATE/rfcs.yml b/.github/DISCUSSION_TEMPLATE/rfcs.yml new file mode 100644 index 000000000..55909514f --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/rfcs.yml @@ -0,0 +1,107 @@ +title: "RFC: " +body: + - type: markdown + attributes: + value: | + Thank you for submitting a RFC. Please add as many details as possible to help further enrich this design. + - type: input + id: relation + attributes: + label: Is this related to an existing feature request or issue? + description: Please share a link, if applicable + - type: dropdown + id: area + attributes: + label: Which area does this RFC relate to? + options: + - Tracer + - Logger + - Metrics + - Parameters + - Large Messages + - Batch Processing + - Validation + - Idempotency + - Custom Resources + - Serialization + - Other + validations: + required: true + - type: textarea + id: summary + attributes: + label: Summary + description: Please provide an overview in one or two paragraphs + validations: + required: true + - type: textarea + id: problem + attributes: + label: Use case + description: Please share the use case and motivation behind this proposal + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposal + description: Please explain the design in detail, so anyone familiar with the project could implement it + placeholder: What the user experience looks like before and after this design? + validations: + required: true + - type: textarea + id: scope + attributes: + label: Out of scope + description: Please explain what should be considered out of scope in your proposal + validations: + required: true + - type: textarea + id: challenges + attributes: + label: Potential challenges + description: Nothing is perfect. Please share what common challenges, edge cases, unresolved areas, and suggestions on how to mitigate them + validations: + required: true + - type: textarea + id: integrations + attributes: + label: Dependencies and Integrations + description: If applicable, please share whether this feature has additional dependencies, and how it might integrate with other utilities available + validations: + required: false + - type: textarea + id: alternatives + attributes: + label: Alternative solutions + description: Please describe what alternative solutions to this use case, if any + render: Markdown + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This RFC meets [Powertools for AWS Lambda (Java) Tenets](https://docs.powertools.aws.dev/lambda/java/latest/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Python](https://github.com/aws-powertools/powertools-lambda-python/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet/) + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: After creating an RFC, please wait until it is reviewed and signed-off by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected. + + Metadata information for admin purposes, please leave them empty. + + * RFC PR: + * Approved by: '' + * Reviewed by: '' + - type: input + id: notes + attributes: + label: Future readers + description: Please not edit this field + value: "Please react with 👍 and your use case to help us understand customer demand." \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index c1b7490eb..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug, triage -assignees: '' - ---- - -<!--- Provide a general summary of the issue in the Title above --> -<!--- How has this issue affected you? What are you trying to accomplish? --> - -**What were you trying to accomplish?** - -## Expected Behavior -<!--- If you're describing a bug, tell us what should happen --> -<!--- If you're suggesting a change/improvement, tell us how it should work --> - -## Current Behavior -<!--- If describing a bug, tell us what happens instead of the expected behavior --> -<!--- If suggesting a change/improvement, explain the difference from current behavior --> - -## Possible Solution -<!--- Not obligatory, but suggest a fix/reason for the bug, --> -<!--- or ideas how to implement the addition or change --> - -## Steps to Reproduce (for bugs) -<!--- Provide a link to a live example, or an unambiguous set of steps to --> -<!--- reproduce this bug. Include code to reproduce, if relevant --> -1. -2. -3. -4. - -## Environment - -* **Powertools version used**: -* **Packaging format (Layers, Maven/Gradle)**: -* **AWS Lambda function runtime:** -* **Debugging logs** - -> [How to enable debug mode](https://awslabs.github.io/aws-lambda-powertools-java/#debug-mode)** - -```text -# paste logs here -``` diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..2b0ae71e8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,82 @@ +name: Bug report +description: Report a reproducible bug to help us improve +title: "Bug: TITLE" +type: "Bug" +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for submitting a bug report. Please add as much information as possible to help us reproduce, and remove any potential sensitive data. + + Please become familiar with [our definition of bug](https://docs.powertools.aws.dev/lambda/java/processes/maintainers/#is-that-a-bug). + - type: textarea + id: expected_behaviour + attributes: + label: Expected Behaviour + description: Please share details on the behaviour you expected + validations: + required: true + - type: textarea + id: current_behaviour + attributes: + label: Current Behaviour + description: Please share details on the current issue + validations: + required: true + - type: textarea + id: code_snippet + attributes: + label: Code snippet + description: Please share a code snippet to help us reproduce the issue + render: java + validations: + required: true + - type: textarea + id: solution + attributes: + label: Possible Solution + description: If known, please suggest a potential resolution + validations: + required: false + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + description: Please share how we might be able to reproduce this issue + validations: + required: true + - type: input + id: version + attributes: + label: Powertools for AWS Lambda (Java) version + placeholder: "latest, 2.0.1" + value: latest + validations: + required: true + - type: dropdown + id: runtime + attributes: + label: AWS Lambda function runtime + options: + - "Java 8" + - "Java 11" + - "Java 17" + - "Java 21" + - "provided.al2023" + validations: + required: true + - type: textarea + id: logs + attributes: + label: Debugging logs + description: If available, please share [debugging logs](https://docs.powertools.aws.dev/lambda/lambda/#debug-mode) + render: java + validations: + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..01a8d495b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/aws-powertools/powertools-lambda-java/discussions/new + about: Ask a general question about Powertools for AWS Lambda + - name: Join Community Discord Server + url: https://discord.gg/B8zZKbbyET + about: "Check out the #java channel" diff --git a/.github/ISSUE_TEMPLATE/documentation-improvements.md b/.github/ISSUE_TEMPLATE/documentation-improvements.md deleted file mode 100644 index 8341ae4e0..000000000 --- a/.github/ISSUE_TEMPLATE/documentation-improvements.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Documentation improvements -about: Suggest a documentation update -title: '' -labels: documentation -assignees: '' - ---- - -**What were you initially searching for in the docs?** -<!-- Please help us understand how you looked for information that was either not available or unclear --> - -**Is this related to an existing part of the documentation? Please share a link** - -**Describe how we could make it clearer** - -**If you have a proposed update, please share it here** diff --git a/.github/ISSUE_TEMPLATE/documentation_improvements.yml b/.github/ISSUE_TEMPLATE/documentation_improvements.yml new file mode 100644 index 000000000..e750d5192 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_improvements.yml @@ -0,0 +1,50 @@ +name: Documentation improvements +description: Suggest a documentation update to improve everyone's experience +title: "Docs: TITLE" +labels: ["documentation", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for helping us improve everyone's experience. We review documentation updates on a case by case basis. + - type: textarea + id: search_area + attributes: + label: What were you searching in the docs? + description: Please help us understand how you looked for information that was either unclear or not available + validations: + required: true + - type: input + id: area + attributes: + label: Is this related to an existing documentation section? + description: Please share a link, if applicable + validations: + required: false + - type: textarea + id: idea + attributes: + label: How can we improve? + description: Please share your thoughts on how we can improve this experience + validations: + required: true + - type: textarea + id: suggestion + attributes: + label: Got a suggestion in mind? + description: Please suggest a proposed update + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: I understand the final update might be different from my proposed suggestion, or refused. + required: true + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index b837b7ad5..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: feature-request, triage -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] --> - -**Describe the solution you'd like** -<!-- A clear and concise description of what you want to happen. --> - -**Describe alternatives you've considered** -<!-- A clear and concise description of any alternative solutions or features you've considered. --> - -**Additional context** -<!-- Add any other context or screenshots about the feature request here. --> diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..6aaef0718 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,52 @@ +name: Feature request +description: Suggest an idea for Powertools for AWS Lambda +title: "Feature request: TITLE" +labels: ["feature-request", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to suggest an idea to the Powertools for AWS Lambda (Java) project. + - type: textarea + id: problem + attributes: + label: Use case + description: Please help us understand your use case or problem you're facing + validations: + required: true + - type: textarea + id: suggestion + attributes: + label: Solution/User Experience + description: Please share what a good solution would look like to this use case + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternative solutions + description: Please describe what alternative solutions to this use case, if any + render: Markdown + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This feature request meets [Powertools for AWS Lambda (Java) Tenets](https://docs.powertools.aws.dev/lambda/java/latest/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Python](https://github.com/aws-powertools/powertools-lambda-python/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet/) + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: After creating an issue, please wait until it is triaged and confirmed by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected. + - type: input + id: notes + attributes: + label: Future readers + description: Please not edit this field + value: "Please react with 👍 and your use case to help us understand customer demand." \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml new file mode 100644 index 000000000..1a84ed7ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -0,0 +1,67 @@ +name: Maintenance +description: Suggest an activity to help address tech debt, governance, and anything internal +title: "Maintenance: TITLE" +labels: ["internal", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for taking the time to help us improve operational excellence. + + *Future readers*: Please react with 👍 and your use case to help us understand customer demand. + - type: textarea + id: activity + attributes: + label: Summary + description: Please provide an overview in one or two paragraphs + validations: + required: true + - type: textarea + id: importance + attributes: + label: Why is this needed? + description: Please help us understand the value so we can prioritize it accordingly + validations: + required: true + - type: dropdown + id: area + attributes: + label: Which area does this relate to? + multiple: true + options: + - Automation + - Governance + - Tests + - Tracer + - Logger + - Metrics + - Parameters + - SQS Large Message Handling + - SQS Batch Processing + - Validation + - Idempotency + - Custom Resources + - Serialization + - Other + - type: textarea + id: suggestion + attributes: + label: Solution + description: If available, please share what a good solution would look like + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This request meets [Powertools for AWS Lambda (Java) Tenets](https://docs.powertools.aws.dev/lambda-java/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Python](https://github.com/aws-powertools/powertools-lambda-python/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/) + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/rfc.md b/.github/ISSUE_TEMPLATE/rfc.md deleted file mode 100644 index 1db27058d..000000000 --- a/.github/ISSUE_TEMPLATE/rfc.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -name: RFC -about: Feature design and proposals -title: 'RFC: ' -labels: RFC, triage -assignees: '' - ---- - -## Key information - -* RFC PR: (leave this empty) -* Related issue(s), if known: -* Area: (i.e. Tracer, Metrics, Logger, etc.) -* Meet [tenets](https://awslabs.github.io/aws-lambda-powertools-java/#tenets): (Yes/no) - -## Summary -[summary]: #summary - -> One paragraph explanation of the feature. - -## Motivation -[motivation]: #motivation - -> Why are we doing this? What use cases does it support? What is the expected outcome? - -## Proposal -[proposal]: #proposal - -> This is the bulk of the RFC. - -> Explain the design in enough detail for somebody familiar with Powertools to understand it, and for somebody familiar with the implementation to implement it. - -> This should get into specifics and corner-cases, and include examples of how the feature is used. Any new terminology should be defined here. - -## Drawbacks -[drawbacks]: #drawbacks - -> Why should we *not* do this? - -> Do we need additional dependencies? Impact performance/package size? - -## Rationale and alternatives -[rationale-and-alternatives]: #rationale-and-alternatives - -* **What other designs have been considered? Why not them?** -* **What is the impact of not doing this?** - -## Unresolved questions -[unresolved-questions]: #unresolved-questions - -> Optional, stash area for topics that need further development e.g. TBD diff --git a/.github/ISSUE_TEMPLATE/share_your_work.yml b/.github/ISSUE_TEMPLATE/share_your_work.yml new file mode 100644 index 000000000..f0f879225 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/share_your_work.yml @@ -0,0 +1,56 @@ +name: I Made This (showcase your work) +description: Share what you did with Powertools for AWS Lambda (Java) 💞💞. Blog post, workshops, presentation, sample apps, etc. +title: "[I Made This]: <TITLE>" +labels: ["community-content", "triage"] +body: + - type: markdown + attributes: + value: Thank you for helping spread the word out on Powertools, truly! + - type: input + id: content + attributes: + label: Link to your material + description: | + Please share the original link to your material. + + *Note: Short links will be expanded when added to Powertools for AWS Lambda (Java) documentation* + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: Describe in one paragraph what's in it for them (readers) + validations: + required: true + - type: input + id: author + attributes: + label: Preferred contact + description: What's your preferred contact? We'll list it next to this content + validations: + required: true + - type: input + id: author-social + attributes: + label: (Optional) Social Network + description: If different from preferred contact, what's your preferred contact for social interactions? + validations: + required: false + - type: textarea + id: notes + attributes: + label: (Optional) Additional notes + description: | + Any notes you might want to share with us related to this material. + + *Note: These notes are explicitly to Powertools for AWS Lambda (Java) maintainers. It will not be added to the community resources page.* + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: I understand this content may be removed from Powertools for AWS Lambda (Java) documentation if it doesn't conform with the [Code of Conduct](https://aws.github.io/code-of-conduct) + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/support_powertools.yml b/.github/ISSUE_TEMPLATE/support_powertools.yml new file mode 100644 index 000000000..9067d47ec --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support_powertools.yml @@ -0,0 +1,64 @@ +name: Support Powertools for AWS Lambda (Java) (become a reference) +description: Add your organization's name or logo to the Powertools for AWS Lambda (Java) documentation +title: "[Support Powertools for AWS Lambda (Java)]: <your organization name>" +labels: ["customer-reference", "triage"] +body: + - type: markdown + attributes: + value: | + Thank you for becoming a reference customer. Your support means a lot to us. It also helps new customers to know who's using it. + + If you would like us to also display your organization's logo, please share a link in the `Company logo` field. + - type: input + id: organization + attributes: + label: Organization Name + description: Please share the name of your organization + placeholder: ACME + validations: + required: true + - type: input + id: name + attributes: + label: Your Name + description: Please share your name + validations: + required: true + - type: input + id: job + attributes: + label: Your current position + description: Please share your current position at your company + validations: + required: true + - type: input + id: logo + attributes: + label: (Optional) Company logo + description: Company logo you want us to display. You also allow us to resize for optimal placement in the documentation. + validations: + required: false + - type: textarea + id: use_case + attributes: + label: (Optional) Use case + description: How are you using Powertools for AWS Lambda (Java) today? *features, etc.* + validations: + required: false + - type: checkboxes + id: other_languages + attributes: + label: Also using other Powertools for AWS Lambda languages? + options: + - label: Java + required: false + - label: TypeScript + required: false + - label: .NET + required: false + - type: markdown + attributes: + value: | + *By raising a Support Powertools for AWS Lambda (Python) issue, you are granting AWS permission to use your company's name (and/or logo) for the limited purpose described here. You are also confirming that you have authority to grant such permission.* + + *You can opt-out at any time by commenting or reopening this issue.* \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/tech_debt.yml b/.github/ISSUE_TEMPLATE/tech_debt.yml new file mode 100644 index 000000000..56cd4b8c7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tech_debt.yml @@ -0,0 +1,60 @@ +name: Technical debt +description: Suggest an activity to help address technical debt. +title: "Tech debt: TITLE" +labels: ["tech-debt", "triage"] +body: + - type: markdown + attributes: + value: Thank you for taking the time to help us proactively improve delivery velocity, safely. + - type: textarea + id: importance + attributes: + label: Why is this needed? + description: Please help us understand the value so we can prioritize it accordingly + validations: + required: true + - type: dropdown + id: area + attributes: + label: Which area does this relate to? + multiple: true + options: + - Tests + - Static typing + - Tracer + - Logger + - Metrics + - Middleware factory + - Parameters + - Batch processing + - Validation + - Event Source Data Classes + - Parser + - Idempotency + - Feature flags + - JMESPath functions + - Streaming + - Automation + - Other + - type: textarea + id: suggestion + attributes: + label: Suggestion + description: If available, please share what a good solution would look like + validations: + required: false + - type: checkboxes + id: acknowledgment + attributes: + label: Acknowledgment + options: + - label: This request meets [Powertools for AWS Lambda (Python) Tenets](https://docs.powertools.aws.dev/lambda/python/latest/#tenets) + required: true + - label: Should this be considered in other Powertools for AWS Lambda languages? i.e. [Python](https://github.com/aws-powertools/powertools-lambda-python/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet/) + required: false + - type: markdown + attributes: + value: | + --- + + **Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 94eb32733..f30703bb4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,25 +1,28 @@ -**Issue #, if available:** +## Summary -## Description of changes: +### Changes -<!--- One or two sentences as a summary of what's being changed --> +> Please provide a summary of what's being changed -**Checklist** +<!-- What is this PR solving? Write a clear description or reference the issue(s) it addresses. --> -<!--- Leave unchecked if your change doesn't seem to apply --> +> Please add the issue number below, if no issue is present the PR might get blocked and not be reviewed -* [ ] [Meet tenets criteria](https://awslabs.github.io/aws-lambda-powertools-java/#tenets) -* [ ] Update tests -* [ ] Update docs -* [ ] PR title follows [conventional commit semantics]() +**Issue number:** -## Breaking change checklist +<!------- +Before creating the pull request, please make sure you do the following: -<!--- Ignore if it's not a breaking change --> +- Read the Contributing Guidelines at https://github.com/aws-powertools/powertools-lambda-java/blob/main/CONTRIBUTING.md#sending-a-pull-request +- Check that there isn't already a PR that addresses the same issue. If you find a duplicate, please leave a comment under the existing PR so we can discuss how to move forward +- Check that the change meets the project's tenets https://docs.powertools.aws.dev/lambda/java/latest/#tenets +- Add a PR title that follows the conventional commit semantics - https://www.conventionalcommits.org/en/v1.0.0/ +- If relevant, add tests that prove that the change is effective and works +- Whenever relevant, make sure to comment functions/methods/types and make appropriate changes to the documentation +-------> -**RFC issue #**: - -* [ ] Migration process documented -* [ ] Implement warnings (if it can live side by side) +--- By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. + +**Disclaimer**: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. \ No newline at end of file diff --git a/.github/actions/gradle/action.yml b/.github/actions/gradle/action.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.github/actions/restore/action.yml b/.github/actions/restore/action.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.github/actions/seal/action.yml b/.github/actions/seal/action.yml new file mode 100644 index 000000000..079496c8c --- /dev/null +++ b/.github/actions/seal/action.yml @@ -0,0 +1,78 @@ +name: Seal and hash source code +description: | + Seals and creates a SHA256SUM of an artifact for storage + + Process: + 1. Create a unique name based on environment details + 2. Compress work directory or specified path + 3. Hash compressed file + 4. Upload archive using `actions/upload-artifact` + + Usage: + ```yml + - id: seal + name: Seal + uses: .github/actions/seal + with: + prefix: foo + ``` + +inputs: + prefix: + description: Prefix to use when exporting artifact + required: true +outputs: + hash: + description: SHA256SUM hash of compressed files + value: ${{ steps.hash.outputs.hash }} + artifact_name: + description: Artifact name + value: ${{ steps.artifact_name.outputs.artifact_name }} + +runs: + using: composite + steps: + - id: adjust_path + name: Adjust path + shell: bash + run: echo "${{ github.action_path }}" >> $GITHUB_PATH + + - id: artifact_name + name: Export final artifact name + env: + GITHUB_RUN_ID: ${{ github.run_id }} + ARTIFACT_PREFIX: ${{ inputs.prefix }} + shell: bash + run: | + echo "artifact_name=${ARTIFACT_PREFIX}-${GITHUB_RUN_ID}" >> "$GITHUB_OUTPUT" + + - id: compress + name: Create tarball for entire source + env: + ARTIFACT_NAME: ${{ steps.artifact_name.outputs.artifact_name }} + shell: bash + run: | + tar --exclude-vcs -cvf "${ARTIFACT_NAME}".tar * + + - id: hash + name: Hash + env: + ARTIFACT_NAME: ${{ steps.artifact_name.outputs.artifact_name }} + shell: bash + run: | + echo "hash=$(openssl dgst -sha256 -binary "${{ ARTIFACT_NAME }}".tar | openssl enc -base64)" >> "$GITHUB_OUTPUT" + + - name: Upload artifacts + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + with: + if-no-files-found: error + name: ${{ steps.artifact_name.outputs.artifact_name }} + path: ${{ steps.artifact_name.outputs.artifact_name }}.tar + retention-days: 1 + + - name: Remove archive + env: + ARTIFACT_NAME: ${{ steps.artifact_name.outputs.artifact_name }} + shell: bash + run: | + rm -f "${ARTIFACT_NAME}.tar" \ No newline at end of file diff --git a/.github/actions/version/action.yml b/.github/actions/version/action.yml new file mode 100644 index 000000000..f0f0516ee --- /dev/null +++ b/.github/actions/version/action.yml @@ -0,0 +1,53 @@ +name: Version Java Project +description: | + Versions the maven project using an input + + Process: + 1. Grab current version from project.version variable from maven + 2. Set new version using maven-versions-plugin + + Usage: + ```yml + - id: version + name: version + uses: .github/actions/version + with: + new_version: 1.20.0 + snapshot: 'false' + ``` + +inputs: + new_version: + description: New package version, expressed as SemVer (1.x.y) + required: true + snapshot: + description: New version is a SNAPSHOT release + required: true + default: 'false' + +outputs: + old_version: + description: Current version of project + value: ${{ steps.current_version.outputs.current_version}} + +runs: + using: composite + steps: + - id: current_version + name: Get current version + shell: bash + run: | + echo "current_version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_OUTPUT + + - id: replace_version + name: Replace current version + shell: bash + run: | + mvn versions:set -DnewVersion=${{ inputs.new_version }} -DprocessAllModules=true -DallowSnapshots=true + + - id: asset_version + name: Replace version for assets + if: ${{ inputs.snapshot == 'false' }} + shell: bash + run: | + grep "${{ steps.current_version.outputs.current_version }}" -r . --include build.gradle --include build.gradle.kts --include mkdocs.yml --include README.md -l | xargs sed -i 's#${{ steps.current_version.outputs.current_version }}#${{ inputs.new_version }}#' \ No newline at end of file diff --git a/.github/auto_assign-issues.yml b/.github/auto_assign-issues.yml deleted file mode 100644 index 652e18ad5..000000000 --- a/.github/auto_assign-issues.yml +++ /dev/null @@ -1,8 +0,0 @@ -addAssignees: true - -# The list of users to assign to new issues. -# If empty or not provided, the repository owner is assigned -assignees: - - msailes - - pankajagrawal16 - - stevehouel diff --git a/.github/dependabot.yml b/.github/dependabot.yml index caf24d1f9..caa9934ca 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,5 +1,22 @@ version: 2 updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: chore + + - package-ecosystem: docker + directories: + - "/powertools-e2e-tests/src/test/resources/docker" + - "/docs" + - "/examples/**" + schedule: + interval: daily + commit-message: + prefix: chore + - package-ecosystem: "maven" directory: "/" schedule: diff --git a/.github/dependency-review-config.yml b/.github/dependency-review-config.yml new file mode 100644 index 000000000..2ea218503 --- /dev/null +++ b/.github/dependency-review-config.yml @@ -0,0 +1,32 @@ +allow-licenses: + - 'Apache-1.1' + - 'Apache-2.0' + - 'ISC' + - 'MIT' + - 'MIT-0' + - 'MIT-CMU' + - 'MIT-enna' + - 'MIT-feh' + - 'MIT-Festival' + - 'MIT-Modern-Variant' + - 'MIT-open-group' + - 'MIT-testregex' + - 'MIT-Wu' + - 'BSD-1-Clause' + - 'BSD-2-Clause' + - 'BSD-2-Clause-Views' + - 'BSD-3-Clause' + - 'BSD-3-Clause-Attribution' + - 'BSD-3-Clause-Clear' + - 'BSD-3-Clause-flex' + - 'BSD-3-Clause-HP' + - 'BSD-3-Clause-LBNL' + - 'BSD-3-Clause-Modification' + - 'BSD-3-Clause-No-Military-License' + - 'BSD-3-Clause-No-Nuclear-License' + - 'BSD-3-Clause-No-Nuclear-License-2014' + - 'BSD-3-Clause-No-Nuclear-Warranty' + - 'BSD-3-Clause-Open-MPI' + # TT: D290816995 + - 'UPL-1.0' +comment-summary-in-pr: on-failure diff --git a/.github/pmd-ruleset.xml b/.github/pmd-ruleset.xml new file mode 100644 index 000000000..1bc5f3020 --- /dev/null +++ b/.github/pmd-ruleset.xml @@ -0,0 +1,647 @@ +<?xml version="1.0"?> +<ruleset name="dogfood7" + xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd"> + <description>Rules to check Powertools for Lambda</description> + <!-- + Originally copied from: https://github.com/pmd/build-tools/blob/main/src/main/resources/net/sourceforge/pmd/pmd-dogfood-config.xml + --> + + <!-- + Note: Eventually, this ruleset should include all rules and exclude those, + which we know are explicitly decided as not applicable to PMD itself. + This is the most encompassing form of RuleSet. + + For starting, we add rule by rule and use an incremental approach. + See [#361](https://github.com/pmd/pmd/issues/361). + + The original dogfood ruleset is available at: + https://github.com/pmd/pmd/blob/f9ef2c8c4bc23f1349597b827c8f072b7cade8a5/pmd-core/src/main/resources/rulesets/internal/dogfood.xml + --> + + <!-- <rule ref="category/java/bestpractices.xml/AbstractClassWithoutAbstractMethod" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AccessorClassGeneration" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AccessorMethodGeneration" /> --> + <!-- <rule ref="category/java/bestpractices.xml/ArrayIsStoredDirectly" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AvoidPrintStackTrace" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AvoidReassigningParameters" /> --> + <!-- <rule ref="category/java/bestpractices.xml/AvoidStringBufferField" /> --> + <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/CheckResultSet"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/ConstantsInInterface"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitch"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach" /> --> + <!-- <rule ref="category/java/bestpractices.xml/GuardLogStatement" /> --> + <!-- <rule ref="category/java/bestpractices.xml/JUnit4SuitesShouldUseSuiteAnnotation" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestShouldUseAfterAnnotation" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestShouldUseBeforeAnnotation" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestShouldUseTestAnnotation" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestAssertionsShouldIncludeMessage" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestContainsTooManyAsserts" /> --> + <!-- <rule ref="category/java/bestpractices.xml/UnitTestShouldIncludeAssert" /> --> + <!-- <rule ref="category/java/bestpractices.xml/JUnitUseExpected" /> --> + <rule ref="category/java/bestpractices.xml/LooseCoupling"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray" /> --> + <rule ref="category/java/bestpractices.xml/MissingOverride"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/OneDeclarationPerLine"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/LiteralsFirstInComparisons"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/PreserveStackTrace" /> --> + <rule ref="category/java/bestpractices.xml/PrimitiveWrapperInstantiation"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator" /> --> + <!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap" /> --> + <!-- <rule ref="category/java/bestpractices.xml/ReplaceVectorWithList" /> --> + <!-- <rule ref="category/java/bestpractices.xml/SimplifiableTestAssertion" /> --> + <rule ref="category/java/bestpractices.xml/NonExhaustiveSwitch"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/SystemPrintln" /> --> + <rule ref="category/java/bestpractices.xml/UnusedFormalParameter"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/UnusedImports" /> --> + <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/UnusedPrivateField"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"> + <priority>1</priority> + </rule> + <rule ref="category/java/bestpractices.xml/UseCollectionIsEmpty"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/bestpractices.xml/UseVarargs" /> --> + + <!-- <rule ref="category/java/codestyle.xml/AtLeastOneConstructor" /> --> + <!-- <rule ref="category/java/codestyle.xml/AvoidDollarSigns" /> --> + <!-- <rule ref="category/java/codestyle.xml/AvoidFinalLocalVariable" /> --> + <rule ref="category/java/codestyle.xml/AvoidProtectedFieldInFinalClass"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/AvoidProtectedMethodInFinalClassNotExtending"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/AvoidUsingNativeCode"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/BooleanGetMethodName" /> --> + <!-- <rule ref="category/java/codestyle.xml/CallSuperInConstructor" /> --> + <!-- <rule ref="category/java/codestyle.xml/ClassNamingConventions" /> --> + <!-- <rule ref="category/java/codestyle.xml/CommentDefaultAccessModifier" /> --> + <!-- <rule ref="category/java/codestyle.xml/ConfusingTernary" /> --> + <rule ref="category/java/codestyle.xml/ControlStatementBraces"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/DefaultPackage" /> --> + <!-- <rule ref="category/java/codestyle.xml/DontImportJavaLang" /> --> + <!-- <rule ref="category/java/codestyle.xml/DuplicateImports" /> --> + <!-- <rule ref="category/java/codestyle.xml/EmptyMethodInAbstractClassShouldBeAbstract" /> --> + <rule ref="category/java/codestyle.xml/ExtendsObject"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/FieldDeclarationsShouldBeAtStartOfClass" /> --> + <!-- <rule ref="category/java/codestyle.xml/FieldNamingConventions" /> --> + <rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/FormalParameterNamingConventions"> + <priority>1</priority> + </rule> + + <!-- <rule ref="category/java/codestyle.xml/GenericsNaming" /> --> + <rule ref="category/java/codestyle.xml/IdenticalCatchBranches"> + <priority>3</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/LinguisticNaming" /> --> + <!-- <rule ref="category/java/codestyle.xml/LocalHomeNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/LocalInterfaceSessionNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/LocalVariableCouldBeFinal" /> --> + <!-- <rule ref="category/java/codestyle.xml/LocalVariableNamingConventions" /> --> + <!-- <rule ref="category/java/codestyle.xml/LongVariable" /> --> + <!-- <rule ref="category/java/codestyle.xml/MDBAndSessionBeanNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/MethodArgumentCouldBeFinal" /> --> + <!-- <rule ref="category/java/codestyle.xml/MethodNamingConventions" /> --> + <!-- <rule ref="category/java/codestyle.xml/NoPackage" /> --> + <!-- <rule ref="category/java/codestyle.xml/OnlyOneReturn" /> --> + <!-- <rule ref="category/java/codestyle.xml/PackageCase" /> --> + <!-- <rule ref="category/java/codestyle.xml/PrematureDeclaration" /> --> + <!-- <rule ref="category/java/codestyle.xml/RemoteInterfaceNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/RemoteSessionInterfaceNamingConvention" /> --> + <!-- <rule ref="category/java/codestyle.xml/ShortClassName" /> --> + <!-- <rule ref="category/java/codestyle.xml/ShortMethodName" /> --> + <!-- <rule ref="category/java/codestyle.xml/ShortVariable" /> --> + <!-- <rule ref="category/java/codestyle.xml/TooManyStaticImports" /> --> + <rule ref="category/java/codestyle.xml/UnnecessaryAnnotationValueElement"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UnnecessaryConstructor"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName" /> --> + <rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UnnecessaryModifier"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UnnecessaryReturn"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UseDiamondOperator"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UselessParentheses"> + <priority>1</priority> + </rule> + <rule ref="category/java/codestyle.xml/UselessQualifiedThis"> + <priority>1</priority> + </rule> + + <!-- <rule ref="category/java/design.xml/AbstractClassWithoutAnyMethod" /> --> + <!-- <rule ref="category/java/design.xml/AvoidCatchingGenericException" /> --> + <!-- <rule ref="category/java/design.xml/AvoidDeeplyNestedIfStmts" /> --> + <!-- <rule ref="category/java/design.xml/AvoidRethrowingException" /> --> + <!-- <rule ref="category/java/design.xml/AvoidThrowingNewInstanceOfSameException" /> --> + <!-- <rule ref="category/java/design.xml/AvoidThrowingNullPointerException" /> --> + <!-- <rule ref="category/java/design.xml/AvoidThrowingRawExceptionTypes" /> --> + <rule ref="category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/CollapsibleIfStatements" /> --> + <!-- <rule ref="category/java/design.xml/CouplingBetweenObjects" /> --> + <!-- <rule ref="category/java/design.xml/CyclomaticComplexity" /> --> + <!-- <rule ref="category/java/design.xml/DataClass" /> --> + <!-- <rule ref="category/java/design.xml/DoNotExtendJavaLangError" /> --> + <!-- <rule ref="category/java/design.xml/ExceptionAsFlowControl" /> --> + <!-- <rule ref="category/java/design.xml/ExcessiveClassLength" /> --> + <!-- <rule ref="category/java/design.xml/ExcessiveImports" /> --> + <!-- <rule ref="category/java/design.xml/ExcessiveMethodLength" /> --> + <!-- <rule ref="category/java/design.xml/ExcessiveParameterList" /> --> + <!-- <rule ref="category/java/design.xml/ExcessivePublicCount" /> --> + <rule ref="category/java/design.xml/FinalFieldCouldBeStatic"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/GodClass" /> --> + <!-- <rule ref="category/java/design.xml/ImmutableField" /> --> + <!-- <rule ref="category/java/design.xml/LawOfDemeter" /> --> + <rule ref="category/java/design.xml/LogicInversion"> + <priority>1</priority> + </rule> + <!-- Don't let the Language specific APIs leak out --> + <!-- + <rule ref="category/java/design.xml/LoosePackageCoupling"> + <properties> + <property name="packages"><value>net.sourceforge.pmd.lang,net.sourceforge.pmd.lang.java,net.sourceforge.pmd.lang.jsp,net.sourceforge.pmd.lang.ecmascript,net.sourceforge.pmd.lang.cpp</value></property> + <property name="classes"> + <value>net.sourceforge.pmd.lang.Language,net.sourceforge.pmd.lang.LanguageVersion,net.sourceforge.pmd.lang.LanguageVersionDiscoverer,net.sourceforge.pmd.lang.LanguageVersionHandler,net.sourceforge.pmd.lang.Parser,net.sourceforge.pmd.lang.ast.Node</value> + </property> + </properties> + </rule> + --> + <!-- <rule ref="category/java/design.xml/ModifiedCyclomaticComplexity" /> --> + <!-- <rule ref="category/java/design.xml/NcssConstructorCount" /> --> + <!-- <rule ref="category/java/design.xml/NcssCount" /> --> + <!-- <rule ref="category/java/design.xml/NcssMethodCount" /> --> + <!-- <rule ref="category/java/design.xml/NcssTypeCount" /> --> + <!-- <rule ref="category/java/design.xml/NPathComplexity" /> --> + <!-- <rule ref="category/java/design.xml/SignatureDeclareThrowsException" /> --> + <rule ref="category/java/design.xml/SimplifiedTernary"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/SimplifyBooleanExpressions" /> --> + <rule ref="category/java/design.xml/SimplifyBooleanReturns"> + <priority>1</priority> + </rule> + <rule ref="category/java/design.xml/SimplifyConditional"> + <priority>1</priority> + </rule> + <rule ref="category/java/design.xml/SingularField"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/StdCyclomaticComplexity" /> --> + <!-- <rule ref="category/java/design.xml/SwitchDensity" /> --> + <!-- <rule ref="category/java/design.xml/TooManyFields" /> --> + <!-- <rule ref="category/java/design.xml/TooManyMethods" /> --> + <rule ref="category/java/design.xml/UselessOverridingMethod"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/design.xml/UseObjectForClearerAPI" /> --> + <rule ref="category/java/design.xml/UseUtilityClass"> + <priority>1</priority> + </rule> + + <!-- <rule ref="category/java/documentation.xml/CommentContent" /> --> + <!-- <rule ref="category/java/documentation.xml/CommentRequired" /> --> + <!-- <rule ref="category/java/documentation.xml/CommentSize" /> --> + <rule ref="category/java/documentation.xml/UncommentedEmptyConstructor"> + <priority>1</priority> + </rule> + <rule ref="category/java/documentation.xml/UncommentedEmptyMethodBody"> + <priority>1</priority> + </rule> + + <rule ref="category/java/errorprone.xml/AssignmentInOperand"> + <priority>1</priority> + <properties> + <property name="allowWhile" value="true"/> + </properties> + </rule> + <rule ref="category/java/errorprone.xml/AssignmentToNonFinalStatic"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/AvoidAccessibilityAlteration" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidAssertAsIdentifier" /> --> + <rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/AvoidCallingFinalize" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidCatchingNPE" /> --> + <rule ref="category/java/errorprone.xml/AvoidCatchingThrowable"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidEnumAsIdentifier" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidFieldNameMatchingMethodName" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidFieldNameMatchingTypeName" /> --> + <rule ref="category/java/errorprone.xml/AvoidInstanceofChecksInCatchClause"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/AvoidLiteralsInIfCondition" /> --> + <!-- <rule ref="category/java/errorprone.xml/AvoidLosingExceptionInformation" /> --> + <rule ref="category/java/errorprone.xml/AvoidMultipleUnaryOperators"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/AvoidUsingOctalValues"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/BeanMembersShouldSerialize" /> --> + <rule ref="category/java/errorprone.xml/BrokenNullCheck"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/CallSuperFirst" /> --> + <!-- <rule ref="category/java/errorprone.xml/CallSuperLast" /> --> + <rule ref="category/java/errorprone.xml/CheckSkipResult"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/ClassCastExceptionWithToArray"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/CloneMethodMustBePublic"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/CloneMethodMustImplementCloneable"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/CloneMethodReturnTypeMustMatchClassName"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/CloseResource"> + <priority>1</priority> + <properties> + <property name="closeTargets" value="close,IOUtils.closeQuietly,IOUtil.closeQuietly" /> + </properties> + </rule> + <rule ref="category/java/errorprone.xml/CompareObjectsWithEquals"> + <priority>1</priority> + <properties> + <property name="typesThatCompareByReference" value="java.lang.Enum,java.lang.Class,net.sourceforge.pmd.lang.ast.Node,net.sourceforge.pmd.lang.ast.GenericToken"/> + </properties> + </rule> + <rule ref="category/java/errorprone.xml/ComparisonWithNaN"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/ConstructorCallsOverridableMethod" /> --> + <!-- <rule ref="category/java/errorprone.xml/DataflowAnomalyAnalysis" /> --> + <rule ref="category/java/errorprone.xml/DoNotCallGarbageCollectionExplicitly"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/DoNotCallSystemExit" /> --> + <!-- <rule ref="category/java/errorprone.xml/DoNotExtendJavaLangThrowable" /> --> + <!-- <rule ref="category/java/errorprone.xml/DoNotHardCodeSDCard" /> --> + <!-- <rule ref="category/java/errorprone.xml/DoNotThrowExceptionInFinally" /> --> + <rule ref="category/java/errorprone.xml/DontImportSun"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/DontUseFloatTypeForLoopIndices"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/EmptyCatchBlock"> + <priority>1</priority> + <properties> + <property name="allowCommentedBlocks" value="true" /> + </properties> + </rule> + <!-- <rule ref="category/java/errorprone.xml/EmptyFinalizer" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyFinallyBlock" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyIfStmt" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyInitializer" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyStatementBlock" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyStatementNotInLoop" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptySwitchStatements" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptySynchronizedBlock" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyTryBlock" /> --> + <!-- <rule ref="category/java/errorprone.xml/EmptyWhileStmt" /> --> + <rule ref="category/java/errorprone.xml/EqualsNull"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/FinalizeDoesNotCallSuperFinalize" /> --> + <!-- <rule ref="category/java/errorprone.xml/FinalizeOnlyCallsSuperFinalize" /> --> + <!-- <rule ref="category/java/errorprone.xml/FinalizeOverloaded" /> --> + <!-- <rule ref="category/java/errorprone.xml/FinalizeShouldBeProtected" /> --> + <rule ref="category/java/errorprone.xml/IdempotentOperations"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/ImplicitSwitchFallThrough"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/ImportFromSamePackage" /> --> + <rule ref="category/java/errorprone.xml/InstantiationToGetClass"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/InvalidSlf4jMessageFormat" /> --> + <rule ref="category/java/errorprone.xml/JumbledIncrementer"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/JUnitSpelling" /> --> + <!-- <rule ref="category/java/errorprone.xml/JUnitStaticSuite" /> --> + <!-- <rule ref="category/java/errorprone.xml/LoggerIsNotStaticFinal" /> --> + <!-- <rule ref="category/java/errorprone.xml/MethodWithSameNameAsEnclosingClass" /> --> + <rule ref="category/java/errorprone.xml/MisplacedNullCheck"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/MissingSerialVersionUID" /> --> + <rule ref="category/java/errorprone.xml/MissingStaticMethodInNonInstantiatableClass"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/MoreThanOneLogger" /> --> + <rule ref="category/java/errorprone.xml/NonCaseLabelInSwitch"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/NonStaticInitializer"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/NullAssignment" /> --> + <rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/ProperCloneImplementation"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/ProperLogger" /> --> + <rule ref="category/java/errorprone.xml/ReturnEmptyCollectionRatherThanNull"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/ReturnEmptyCollectionRatherThanNull" /> --> + <rule ref="category/java/errorprone.xml/ReturnFromFinallyBlock"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/SimpleDateFormatNeedsLocale" /> --> + <rule ref="category/java/errorprone.xml/SingleMethodSingleton"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/SingletonClassReturningNewInstance"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/StaticEJBFieldShouldBeFinal" /> --> + <!-- <rule ref="category/java/errorprone.xml/StringBufferInstantiationWithChar" /> --> + <!-- <rule ref="category/java/errorprone.xml/SuspiciousEqualsMethodName" /> --> + <!-- <rule ref="category/java/errorprone.xml/SuspiciousHashcodeMethodName" /> --> + <rule ref="category/java/errorprone.xml/SuspiciousOctalEscape"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/TestClassWithoutTestCases" /> --> + <rule ref="category/java/errorprone.xml/UnconditionalIfStatement"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/UnnecessaryBooleanAssertion" /> --> + <!-- <rule ref="category/java/errorprone.xml/UnnecessaryCaseChange" /> --> + <rule ref="category/java/errorprone.xml/UnnecessaryConversionTemporary"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/UnusedNullCheckInEquals"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/UseCorrectExceptionLogging" /> --> + <!-- <rule ref="category/java/errorprone.xml/UseEqualsToCompareStrings" /> --> + <rule ref="category/java/errorprone.xml/UselessOperationOnImmutable"> + <priority>1</priority> + </rule> + <rule ref="category/java/errorprone.xml/UseLocaleWithCaseConversions"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/errorprone.xml/UseProperClassLoader" /> --> + + <!-- <rule ref="category/java/multithreading.xml/AvoidSynchronizedAtMethodLevel" /> --> + <rule ref="category/java/multithreading.xml/AvoidThreadGroup"> + <priority>1</priority> + </rule> + <rule ref="category/java/multithreading.xml/AvoidUsingVolatile"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/multithreading.xml/DoNotUseThreads" /> --> + <rule ref="category/java/multithreading.xml/DontCallThreadRun"> + <priority>1</priority> + </rule> + <rule ref="category/java/multithreading.xml/DoubleCheckedLocking"> + <priority>1</priority> + </rule> + <rule ref="category/java/multithreading.xml/NonThreadSafeSingleton"> + <priority>1</priority> + </rule> + <rule ref="category/java/multithreading.xml/UnsynchronizedStaticFormatter"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/multithreading.xml/UseConcurrentHashMap" /> --> + <rule ref="category/java/multithreading.xml/UseNotifyAllInsteadOfNotify"> + <priority>1</priority> + </rule> + + <!-- <rule ref="category/java/performance.xml/AddEmptyString" /> --> + <!-- <rule ref="category/java/performance.xml/AppendCharacterWithChar" /> --> + <!-- <rule ref="category/java/performance.xml/AvoidArrayLoops" /> --> + <rule ref="category/java/performance.xml/AvoidFileStream"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/performance.xml/AvoidInstantiatingObjectsInLoops" /> --> + <rule ref="category/java/performance.xml/BigIntegerInstantiation"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/performance.xml/ConsecutiveAppendsShouldReuse" /> --> + <!-- <rule ref="category/java/performance.xml/ConsecutiveLiteralAppends" /> --> + <!-- <rule ref="category/java/performance.xml/InefficientEmptyStringCheck" /> --> + <!-- <rule ref="category/java/performance.xml/InefficientStringBuffering" /> --> + <!-- <rule ref="category/java/performance.xml/InsufficientStringBufferDeclaration" /> --> + <rule ref="category/java/performance.xml/OptimizableToArrayCall"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/performance.xml/RedundantFieldInitializer" /> --> + <!-- <rule ref="category/java/performance.xml/SimplifyStartsWith" /> --> + <!-- <rule ref="category/java/performance.xml/StringInstantiation" /> --> + <!-- <rule ref="category/java/performance.xml/StringToString" /> --> + <rule ref="category/java/performance.xml/TooFewBranchesForSwitch"> + <priority>1</priority> + </rule> + <!-- <rule ref="category/java/performance.xml/UnnecessaryWrapperObjectCreation" /> --> + <!-- <rule ref="category/java/performance.xml/UseArrayListInsteadOfVector" /> --> + <!-- <rule ref="category/java/performance.xml/UseArraysAsList" /> --> + <!-- <rule ref="category/java/performance.xml/UseIndexOfChar" /> --> + <!-- <rule ref="category/java/performance.xml/UselessStringValueOf" /> --> + <!-- <rule ref="category/java/performance.xml/UseStringBufferForStringAppends" /> --> + <!-- <rule ref="category/java/performance.xml/UseStringBufferLength" /> --> + + <!-- <rule ref="category/java/security.xml/InsecureCryptoIv" /> --> + + + <!-- Note: These are the custom rule ported to PMD 7 --> + + <!-- PMD specific custom rules --> + <rule name="UseInstanceofToCompareClasses" + language="java" + since="5.0" + message="replace o.getClass().equals(MyClass.class) with o instanceof MyClass" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>replace o.getClass().equals(MyClass.class) with o instanceof MyClass. Make sure MyClass doesn't have descendants</description> + <priority>1</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//MethodCall[pmd-java:matchesSig("_#equals(java.lang.Object)")] + [MethodCall[pmd-java:matchesSig("_#getClass()")]] + [ArgumentList/ClassLiteral] +]]> + </value> + </property> + </properties> + </rule> + + <rule name="ReversedUseInstanceofToCompareClasses" + language="java" + since="5.0" + message="replace MyClass.class.equals(o.getClass()) with o instanceof MyClass" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>replace MyClass.class.equals(o.getClass()) with o instanceof MyClass. Make sure MyClass doesn't have descendants</description> + <priority>3</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//MethodCall[pmd-java:matchesSig("_#equals(java.lang.Object)")] + [ClassLiteral] + [ArgumentList/MethodCall[pmd-java:matchesSig("_#getClass()")]] +]]> + </value> + </property> + </properties> + </rule> + + <rule name="DontCallSuperVisitWhenUsingRuleChain" + language="java" + message="Don't call super.visit() when using the rulechain" + typeResolution="true" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>Calling super.visit breaks the rulechain, by starting a full visitor run from the passed node downwards. Add all needed nodes to the rulechain instead.</description> + <priority>1</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +( +(: java rule chain rule :) +//ClassDeclaration[pmd-java:typeIs('net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule')] +| +(: generic rule that implements buildTargetSelector :) +//ClassDeclaration[pmd-java:typeIs('net.sourceforge.pmd.lang.rule.AbstractRule')] + [*/MethodDeclaration[@Name = 'buildTargetSelector']] +) + (: but calling super.visit! :) + [*/MethodDeclaration//MethodCall[SuperExpression][@MethodName = 'visit']] +]]> + </value> + </property> + </properties> + </rule> + + <rule name="AlwaysCallSuperWhenNotUsingRuleChain" + language="java" + message="Always call super.visit() when not using rulechain" + typeResolution="true" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>Just returning without calling super stops visiting of nested nodes like inner classes.</description> + <priority>3</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//ClassDeclaration + [ExtendsList/ClassType[pmd-java:typeIs('net.sourceforge.pmd.lang.rule.AbstractRule')]] + [not(ExtendsList/ClassType[pmd-java:typeIs('net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule')])] + [not(*/MethodDeclaration[@Name = 'buildTargetSelector'])] + [*/MethodDeclaration[@Name = 'visit'][not(.//MethodCall[@MethodName = 'visit']/SuperExpression)]] +]]> + </value> + </property> + </properties> + </rule> + + <!-- Idea from https://github.com/pmd/pmd/pull/3609#discussion_r748292071 --> + <rule name="ReuseInvocationMatcher" + language="java" + message="Reuse InvocationMatcher" + typeResolution="true" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>Share the invocation matcher and not create a new one every time</description> + <priority>1</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//MethodCall[pmd-java:matchesSig("net.sourceforge.pmd.lang.java.types.InvocationMatcher#matchesCall(_)")] + [MethodCall[pmd-java:matchesSig("net.sourceforge.pmd.lang.java.types.InvocationMatcher#parse(java.lang.String)")]] +]]> + </value> + </property> + </properties> + </rule> + + <rule name="DoNotUseJavaUtilLogging" + language="java" + since="7.0.0" + message="Use slf4j: LoggerFactory.getLogger(MyClass.class)" + class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"> + <description>Use slf4j: LoggerFactory.getLogger(MyClass.class)</description> + <priority>1</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//ClassDeclaration[//ImportDeclaration[@ImportedName = 'java.util.logging.Logger']] +]]> + </value> + </property> + </properties> + </rule> +</ruleset> diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 5bc64b43f..a94ace711 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -1,33 +1,88 @@ -name: Build Docs +# Build Latest Docs +# +# Description: +# Builds the latest docs and stores them in S3 to be served by our docs platform +# +# The workflow allows us to build to the main location (/lambda/java/) and to an alias +# (i.e. /lambda/java/preview/) if needed +# +# Triggers: +# - workflow_dispatch +# +# Inputs: +# alias – subdirectory to store the docs in for previews or in progress work on: - pull_request: - branches: - - master - paths: - - 'docs/**' - push: - branches: - - master - paths: - - 'docs/**' + workflow_dispatch: + inputs: + version: + description: "Version to build and publish docs (1.28.0, develop)" + required: true + type: string + +name: Build Latest Docs +run-name: Build Latest Docs - ${{ inputs.version }} jobs: docs: runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + environment: Docs steps: - - uses: actions/checkout@v2 - - uses: borales/actions-yarn@v2.0.0 - - name: Set up Python - uses: actions/setup-python@v1 + - name: Checkout Repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + with: + fetch-depth: 0 + - name: Build + run: | + mkdir -p dist + docker build -t squidfunk/mkdocs-material ./docs/ + docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 with: - python-version: "3.8" - - name: Capture branch and tag - id: branch_name + aws-region: us-east-1 + role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} + - name: Deploy Docs (Version) + env: + VERSION: ${{ inputs.version }} + ALIAS: "latest" + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.VERSION }}/ + - name: Deploy Docs (Alias) + env: + VERSION: ${{ inputs.version }} + ALIAS: "latest" + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.ALIAS }}/ + - name: Deploy Docs (Version JSON) + env: + VERSION: ${{ inputs.version }} + ALIAS: "latest" + # We originally used "mike" from PyPi to manage versions for us, but since we moved to S3, we can't use it to manage versions any more. + # Instead, we're using some shell script that manages the versions. + # + # Operations: + # 1. Download the versions.json file from S3 + # 2. Find any reference to the alias and delete it from the versions file + # 3. This is voodoo (don't use JQ): + # - we assign the input as $o and the new version/alias as $n, + # - we check if the version number exists in the file already (for republishing docs) + # - if it's an alias (stage/latest/*) or old version, we do nothing and output $o (original input) + # - if it's a new version number, we add it at position 0 in the array. + # 4. Once done, we'll upload it back to S3. run: | - echo "SOURCE_BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV - echo "SOURCE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - - name: install dependencies - run: make dev-docs - - name: Build docs website - run: make build-docs-website \ No newline at end of file + aws s3 cp \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/versions.json \ + versions_old.json + jq 'del(.[].aliases[] | select(. == "${{ env.ALIAS }}"))' < versions_old.json > versions_proc.json + jq '. as $o | [{"title": "${{ env.VERSION }}", "version": "${{ env.VERSION }}", "aliases": ["${{ env.ALIAS }}"] }] as $n | $n | if .[0].title | test("[a-z]+") or any($o[].title == $n[0].title;.) then [($o | .[] | select(.title == $n[0].title).aliases += $n[0].aliases | . )] else $n + $o end' < versions_proc.json > versions.json + aws s3 cp \ + versions.json \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/versions.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 99764e5e0..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: Build - -on: - pull_request: - branches: - - master - paths: - - 'powertools-core/**' - - 'powertools-logging/**' - - 'powertools-sqs/**' - - 'powertools-tracing/**' - - 'powertools-validation/**' - - 'powertools-parameters/**' - - 'pom.xml' - - '.github/workflows/**' - push: - branches: - - master - paths: - - 'powertools-core/**' - - 'powertools-logging/**' - - 'powertools-sqs/**' - - 'powertools-tracing/**' - - 'pom.xml' - -jobs: - build: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - # test against latest update of each major Java version, as well as specific updates of LTS versions: - java: [8, 8.0.192, 11.0.x, 11.0.3, 12, 13, 15 ] - name: Java ${{ matrix.java }} - env: - OS: ${{ matrix.os }} - JAVA: ${{ matrix.java-version }} - AWS_REGION: eu-west-1 - steps: - - uses: actions/checkout@v2 - - name: Setup java - uses: actions/setup-java@v1 - with: - java-version: ${{ matrix.java }} - - name: Build with Maven - run: mvn -B package --file pom.xml - - auto-merge: - runs-on: ubuntu-latest - needs: [ build ] - if: github.base_ref == 'master' && github.actor == 'dependabot[bot]' - steps: - - uses: actions/github-script@0.2.0 - with: - script: | - github.pullRequests.createReview({ - owner: context.payload.repository.owner.login, - repo: context.payload.repository.name, - pull_number: context.payload.pull_request.number, - event: 'APPROVE' - }) - github.pullRequests.merge({ - owner: context.payload.repository.owner.login, - repo: context.payload.repository.name, - pull_number: context.payload.pull_request.number, - merge_method: 'squash' - }) - github-token: ${{ secrets.AUTOMERGE }} diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml new file mode 100644 index 000000000..339d6fab8 --- /dev/null +++ b/.github/workflows/check-build.yml @@ -0,0 +1,147 @@ +# Check Build +# +# Description: +# Runs the build for every java version we support +# +# Triggers: +# - pull_request: when a PR is sent to us +# - push: when code is pushed to a specified branch +# +# Notes: +# Builds against Java 11, 17, and 21 which are the supported versions. + +on: + workflow_dispatch: + pull_request: + paths: + - 'powertools-batch/**' + - 'powertools-core/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-kafka/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'examples/**' + - 'pom.xml' + - 'examples/pom.xml' + - '.github/workflows/**' + push: + branches: + - main + paths: + - 'powertools-batch/**' + - 'powertools-core/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-kafka/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'pom.xml' + - 'examples/**' + - 'examples/pom.xml' + - '.github/workflows/**' + +name: Build +permissions: + contents: read +run-name: Build - ${{ github.event_name }} + +jobs: + java-build: + runs-on: ubuntu-latest + strategy: + matrix: + java: + - 11 + - 17 + - 21 + - 25 + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e + with: + distribution: corretto + java-version: ${{ matrix.java }} + cache: maven + - id: build-maven + name: Build (Maven) + run: | + mvn -B -q install --file pom.xml + + graalvm-build: + runs-on: aws-powertools_ubuntu-latest_8-core + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 + with: + files: | + powertools-*/** + pom.xml + - name: Setup GraalVM + uses: graalvm/setup-graalvm@790e28947b79a9c09c3391c0f18bf8d0f102ed69 # v1.4.4 + with: + java-version: "21" + distribution: "graalvm" + cache: maven + - id: graalvm-native-test + name: GraalVM Native Test + if: steps.changed-files.outputs.any_changed == 'true' + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + run: | + # Build the entire project first to ensure test-jar dependencies are available + echo "::group::Building project dependencies" + mvn -B -q install -DskipTests + echo "::endgroup::" + + echo "Changes detected in powertools modules: $CHANGED_FILES" + + # Find modules with graalvm-native profile and run tests + find . -name "pom.xml" -path "./powertools-*" | while read module; do + if grep -q "<id>graalvm-native</id>" "$module"; then + module_dir=$(dirname "$module") + module_name=$(basename "$module_dir") + + # Check if this specific module or common dependencies changed + if echo "$CHANGED_FILES" | grep -q "$module_name/" || \ + echo " $CHANGED_FILES " | grep -q " pom.xml " || \ + echo "$CHANGED_FILES" | grep -q "powertools-common/"; then + echo "::group::Building $module_name with GraalVM" + echo "Changes detected in $module_name - running GraalVM tests" + echo "Regenerating GraalVM metadata for $module_dir" + mvn -B -q -f "$module" -Pgenerate-graalvm-files clean test + echo "Running GraalVM native tests for $module_dir" + mvn -B -q -f "$module" -Pgraalvm-native test + echo "::endgroup::" + else + echo "No changes detected in $module_name - skipping GraalVM tests" + fi + fi + done diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml new file mode 100644 index 000000000..378d48a60 --- /dev/null +++ b/.github/workflows/check-e2e.yml @@ -0,0 +1,106 @@ +# Run E2E tests for a branch +# +# Description: +# Runs E2E tests for a specified branch +# +# Triggers: +# - push +# +# Secrets: +# - E2E.AWS_IAM_ROLE + +on: + workflow_dispatch: + + push: + branches: + - main + paths: # add other modules when there are under e2e tests + - 'powertools-batch/**' + - 'powertools-core/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'pom.xml' + +name: E2E Tests +run-name: E2E Tests - ${{ github.event_name }} + +permissions: + contents: read + +jobs: + e2e: + name: End-to-end Tests (Java ${{ matrix.java }}) + runs-on: ubuntu-latest + permissions: + id-token: write + environment: E2E + strategy: + fail-fast: false + max-parallel: 4 + matrix: + java: + - 11 + - 17 + - 21 + - 25 + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'corretto' + java-version: ${{ matrix.java }} + cache: maven + - name: Setup AWS credentials + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + - name: Run e2e test with Maven + env: + JAVA_VERSION: ${{ matrix.java }} + run: mvn -DskipTests -ntp install --file pom.xml && mvn -Pe2e -B -ntp verify --file powertools-e2e-tests/pom.xml + + e2e-graal: + name: End-to-end GraalVM Tests (Java ${{ matrix.java }}) + runs-on: ubuntu-latest + permissions: + id-token: write + environment: E2E + strategy: + fail-fast: false + max-parallel: 1 + matrix: + java: + - 25 + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'corretto' + java-version: ${{ matrix.java }} + cache: maven + - name: Setup AWS credentials + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 + with: + role-to-assume: ${{ secrets.AWS_IAM_ROLE }} + aws-region: us-east-1 + - name: Run e2e-graal test with Maven + env: + JAVA_VERSION: ${{ matrix.java }} + run: mvn -DskipTests -ntp install --file pom.xml && mvn -Pe2e-graal -B -ntp verify --file powertools-e2e-tests/pom.xml diff --git a/.github/workflows/check-pmd.yml b/.github/workflows/check-pmd.yml new file mode 100644 index 000000000..7e7dce429 --- /dev/null +++ b/.github/workflows/check-pmd.yml @@ -0,0 +1,42 @@ +# Runs PMD for a Pull Request +# +# Description: +# Runs PMD (pmd.github.io) for a pull request and daily. +# This does not error on failure yet, our rules are too strong and would fail on every run +# +# Triggers: +# - pull_request +# - workflow_dispatch +# - cron: every day at 12:00PM + +on: + pull_request: + workflow_dispatch: + schedule: + - cron: '0 12 * * *' # Run daily at 12:00 UTC + +name: PMD +run-name: PMD - ${{ github.event_name }} + +permissions: + contents: read + +jobs: + pmd_analyse: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - name: Checkout Repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + java-version: 21 + distribution: corretto + cache: maven + - uses: pmd/pmd-github-action@d9c1f3c5940cbf5923f1354e83fa858b4496ebaa # v2.0.0 + with: + rulesets: '.github/pmd-ruleset.xml' + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/check-spotbugs.yml b/.github/workflows/check-spotbugs.yml new file mode 100644 index 000000000..c5c8197f9 --- /dev/null +++ b/.github/workflows/check-spotbugs.yml @@ -0,0 +1,51 @@ +# Check for Spotbug errors +# +# Description: +# Runs Spotbugs for a pull request. +# This does not error on failure yet, our rules are too strong and would fail on every run +# +# Triggers: +# - pull_request +on: + pull_request: + branches: + - main + paths: + - 'powertools-batch/**' + - 'powertools-core/**' + - 'powertools-cloudformation/**' + - 'powertools-common/**' + - 'powertools-e2e-tests/**' + - 'powertools-idempotency/**' + - 'powertools-large-messages/**' + - 'powertools-logging/**' + - 'powertools-metrics/**' + - 'powertools-kafka/**' + - 'powertools-parameters/**' + - 'powertools-serialization/**' + - 'powertools-sqs/**' + - 'powertools-tracing/**' + - 'powertools-tracing/**' + - 'powertools-validation/**' + - 'powertools-test-suite/**' + - 'pom.xml' + - '.github/workflows/**' + +name: SpotBugs +run-name: SpotBugs + +permissions: + contents: read + +jobs: + codecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + with: + distribution: 'corretto' + java-version: 21 + - name: Build with Maven for spotbugs check to mark build as fail if voilations found + run: mvn -Pbuild-with-spotbugs -B install --file pom.xml -DskipTests -Dmaven.javadoc.skip=true -Dspotbugs.failOnError=true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 9643f73e0..000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Docs - -on: - release: - types: - - published - workflow_dispatch: {} - -jobs: - docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: borales/actions-yarn@v2.0.0 - - name: Set up Python - uses: actions/setup-python@v1 - with: - python-version: "3.8" - - name: Capture branch and tag - id: branch_name - run: | - echo "SOURCE_BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV - echo "SOURCE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - - name: install dependencies - run: make dev-docs - - name: Build docs website - run: make build-docs-website - - name: Deploy all docs - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./dist diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 111be43c6..000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Publish package to the Maven Central Repository -on: - release: - types: - - published -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Maven Central Repository - uses: actions/setup-java@v1 - with: - java-version: 1.8 - server-id: ossrh - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} # Value of the GPG private key to import - gpg-passphrase: GPG_PASSPHRASE # env variable for GPG private key passphrase - - name: Publish package - run: mvn -P sign,build-extras clean deploy -DskipTests - env: - MAVEN_USERNAME: ${{ secrets.OSSRH_JIRA_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} \ No newline at end of file diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index e627dfd3c..39d453ced 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -1,15 +1,27 @@ -name: Release Drafter +# Generates release notes +# +# Description: +# Generates release notes based on pull request history. This is based on the config +# stored in .github/release-drafter.yml +# +# Triggers: +# - push: main on: push: - # branches to consider in the event; optional, defaults to all - branches: - - master + branches: [ main ] + +name: Release Drafter +run-name: Release Drafter jobs: - update_release_draft: - runs-on: ubuntu-latest + update_release: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write steps: - - uses: release-drafter/release-drafter@v5 + - name: Relase Drafter + uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..630b91321 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,329 @@ +# Release +# +# Description: +# Creates a release for the project +# +# 1. Runs a setup job to set needed variables (build_matrix & version) +# 2. Versions to the project and stores as an artifact +# 3. Run quality checks +# 4. Build +# 5. Publish to Maven Central +# 6. Create PR +# 7. Publish docs +# +# Inputs: +# - version (string): SemVer of the new release (X.Y.Z) +# - snapshot (bool): If it's a snapshot release, this skips versioning assets like docs +# - skip_checks (bool): Don't run quality checks if it's an emergency release +# - skip_publish (bool): Don't publish to maven central +# - continue_on_error (bool): Don't fail the workflow if a quality check fails +# +# Triggers: +# - workflow_dispatch +# +# Secrets: +# - RELEASE.GPG_SIGNING_KEY +# - RELEASE.OSSRH_JIRA_USERNAME +# - RELEASE.OSSRH_JIRA_PASSWORD +# - RELEASE.GPG_PASSPHRASE +# - DOCS.AWS_DOCS_ROLE_ARN +# - DOCS.AWS_DOCS_BUCKET + +on: + workflow_dispatch: + inputs: + version: + type: string + description: Semver version to release + snapshot: + type: boolean + description: Create snapshot release + default: false + skip_checks: + type: boolean + description: Skip quality checks + default: false + skip_publish: + type: boolean + description: Skip publish to Maven Central + default: false + continue_on_error: + type: boolean + description: Continue to build if there's an error in quality checks + default: false + +name: Release +run-name: Release – ${{ inputs.version }} + +permissions: + contents: read + +env: + RELEASE_COMMIT: ${{ github.sha }} + RELEASE_TAG_VERSION: ${{ inputs.version }} + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + version: ${{ format('{0}{1}', steps.version_release.outputs.version, steps.version_snapshot.outputs.version) }} + build_matrix: ${{ format('{0}{1}', steps.build_matrix_v1.outputs.build_matrix, steps.build_matrix_v2.outputs.build_matrix) }} + steps: + - id: version_snapshot + if: ${{ inputs.snapshot }} + name: Version + run: | + echo version="$(grep -q "SNAPSHOT" <<< "${{ inputs.version }}" && echo "${{ inputs.version }}" || echo "${{ inputs.version }}-SNAPSHOT")" >> "$GITHUB_OUTPUT" + - id: version_release + if: ${{ !inputs.snapshot }} + name: Version + run: | + echo version="${{ inputs.version }}" >> "$GITHUB_OUTPUT" + - id: base + name: Base + run: | + echo build_version=$(test ${{ github.ref_name }} == "main" && echo "v2" || echo "v1") >> $GITHUB_OUTPUT + - id: build_matrix_v1 + name: Build matrix (v1) + if: ${{ steps.base.outputs.build_version == 'v1' }} + run: | + echo build_matrix='["8", "11", "17", "21"]' >> "$GITHUB_OUTPUT" + - id: build_matrix_v2 + name: Build matrix (v2) + if: ${{ steps.base.outputs.build_version == 'v2' }} + run: | + echo build_matrix='["11", "17", "21"]'>> "$GITHUB_OUTPUT" + + version_seal: + runs-on: ubuntu-latest + needs: + - setup + outputs: + source_hash: ${{ steps.upload_source.outputs.artifact-digest }} + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - id: version + name: version + uses: ./.github/actions/version + with: + new_version: ${{ needs.setup.outputs.version }} + snapshot: ${{ inputs.snapshot}} + - id: upload_source + name: Upload artifacts + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + if-no-files-found: error + name: source + path: | + * + !.git/* + include-hidden-files: true + retention-days: 1 + + quality: + runs-on: aws-powertools_ubuntu-latest_8-core + needs: + - version_seal + if: ${{ inputs.skip_checks == false }} + permissions: + contents: write + id-token: write + steps: + - id: download_source + name: Download artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 + with: + name: source + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e + with: + distribution: corretto + java-version: 21 + cache: maven + # non-exhuastive, but gives a fair indication if the final build will succeed, tests will run when we build later + - name: Run unit tests + run: mvn -B test --file pom.xml + continue-on-error: ${{ inputs.continue_on_error }} + - name: Run Spotbugs + run: mvn -Pbuild-with-spotbugs -B install --file pom.xml -DskipTests -Dmaven.javadoc.skip=true -Dspotbugs.failOnError=true + continue-on-error: ${{ inputs.continue_on_error }} + - uses: pmd/pmd-github-action@d9c1f3c5940cbf5923f1354e83fa858b4496ebaa # v2.0.0 + with: + rulesets: '.github/pmd-ruleset.xml' + token: ${{ secrets.GITHUB_TOKEN }} + uploadSarifReport: false + + build: + runs-on: aws-powertools_ubuntu-latest_8-core + needs: + - setup + - quality + - version_seal + if: ${{ always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + strategy: + matrix: + java: ${{ fromJson(needs.setup.outputs.build_matrix) }} + steps: + - id: download_source + name: Download artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 + with: + name: source + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e + with: + distribution: corretto + java-version: ${{ matrix.java }} + cache: maven + - id: build-maven + name: Build (Maven) + run: | + mvn -B install --file pom.xml + + publish: + runs-on: aws-powertools_ubuntu-latest_8-core + if: ${{ github.repository == 'aws-powertools/powertools-lambda-java' && inputs.skip_publish == false && always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + needs: + - build + environment: Release + steps: + - id: download_source + name: Download artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 + with: + name: source + - name: Setup Java + uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e + with: + distribution: corretto + java-version: 21 + cache: maven + gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} + gpg-passphrase: GPG_PASSPHRASE + server-id: central + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + - name: Publish package + run: mvn -Prelease clean deploy -DskipTests + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + + create_pr: + runs-on: ubuntu-latest + if: ${{ inputs.snapshot == false && always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + needs: + - build + - publish + permissions: + pull-requests: write + contents: write + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + ref: ${{ env.RELEASE_COMMIT }} + - id: download_source + name: Download artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v4.6.1 + with: + name: source + - id: setup-git + name: Git client setup and refresh tip + run: | + git config user.name "Powertools for AWS Lambda (Java) Bot" + git config user.email "151832416+aws-powertools-bot@users.noreply.github.com" + git config pull.rebase true + git config remote.origin.url >&- + - id: tag + name: Create tag + run: | + git tag -a v${{ inputs.version }} -m "Release v${{ inputs.version }}" + git push origin v${{ inputs.version }} + - id: branch + name: Create branch and update change log + run: | + git checkout -b ci-${{ github.run_id }} + docker run -v "${PWD}":/workdir quay.io/git-chglog/git-chglog@sha256:c791b1e8264387690cce4ce32e18b4f59ca3ffd8d55cb4093dc6de74529493f4 > CHANGELOG.md + git commit -am "chore(ci): bump version to ${{ inputs.version }}" + git push origin ci-${{ github.run_id }} + - id: create_pr + name: Create PR + run: | + gh pr create \ + --title "chore(ci): bump version to ${{ inputs.version }}" \ + --body "This is an automated PR created from the following workflow: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + docs: + runs-on: ubuntu-latest + if: ${{ inputs.snapshot == false }} + needs: + - create_pr + permissions: + contents: read + id-token: write + environment: Docs + steps: + - id: checkout + name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + # Checkout PR branch to make sure we build the version-bumped docs + ref: ci-${{ github.run_id }} + - name: Build + run: | + mkdir -p dist + docker build -t squidfunk/mkdocs-material ./docs/ + docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 + with: + aws-region: us-east-1 + role-to-assume: ${{ secrets.AWS_DOCS_ROLE_ARN }} + - name: Deploy Docs (Version) + env: + VERSION: ${{ inputs.version }} + ALIAS: 'latest' + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.VERSION }}/ + - name: Deploy Docs (Alias) + env: + VERSION: ${{ inputs.version }} + ALIAS: 'latest' + run: | + aws s3 sync \ + site/ \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/${{ env.ALIAS }}/ + - name: Deploy Docs (Version JSON) + env: + VERSION: ${{ inputs.version }} + ALIAS: 'latest' + # We originally used "mike" from PyPi to manage versions for us, but since we moved to S3, we can't use it to manage versions any more. + # Instead, we're using some shell script that manages the versions. + # + # Operations: + # 1. Download the versions.json file from S3 + # 2. Find any reference to the alias and delete it from the versions file + # 3. This is voodoo (don't use JQ): + # - we assign the input as $o and the new version/alias as $n, + # - we check if the version number exists in the file already (for republishing docs) + # - if it's an alias (stage/latest/*) or old version, we do nothing and output $o (original input) + # - if it's a new version number, we add it at position 0 in the array. + # 4. Once done, we'll upload it back to S3. + run: | + aws s3 cp \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/versions.json \ + versions_old.json + jq 'del(.[].aliases[] | select(. == "${{ env.ALIAS }}"))' < versions_old.json > versions_proc.json + jq '. as $o | [{"title": "${{ env.VERSION }}", "version": "${{ env.VERSION }}", "aliases": ["${{ env.ALIAS }}"] }] as $n | $n | if .[0].title | test("[a-z]+") or any($o[].title == $n[0].title;.) then [($o | .[] | select(.title == $n[0].title).aliases += $n[0].aliases | . )] else $n + $o end' < versions_proc.json > versions.json + aws s3 cp \ + versions.json \ + s3://${{ secrets.AWS_DOCS_BUCKET }}/lambda-java/versions.json diff --git a/.github/workflows/security-dependencies-check.yml b/.github/workflows/security-dependencies-check.yml new file mode 100644 index 000000000..6729fd304 --- /dev/null +++ b/.github/workflows/security-dependencies-check.yml @@ -0,0 +1,31 @@ +# Dependency checks +# +# Description: +# Verifies that dependencies are compatible with our project +# by checking licenses and their security posture +# +# Triggers: +# - pull_request + +on: + pull_request: + +name: Verify Dependencies +run-name: Verify Dependencies – ${{ github.event_name }} + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout Repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Verify Contents + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 + with: + config-file: './.github/dependency-review-config.yml' diff --git a/.github/workflows/security-scorecard.yml b/.github/workflows/security-scorecard.yml new file mode 100644 index 000000000..b91e78c69 --- /dev/null +++ b/.github/workflows/security-scorecard.yml @@ -0,0 +1,57 @@ +# Runs OSSF +# +# Description: +# Runs OpenSSF Scorecard scan on the project +# +# Triggers: +# - branch_protection_rule +# - cron: 09:00AM +# - push +# - workflow_dispatch +# +# Secrets: +# - Security.SCORECARD_TOKEN + +on: + branch_protection_rule: + schedule: + - cron: "0 9 * * *" + push: + branches: [main] + workflow_dispatch: {} + +name: OpenSSF Scorecard +run-name: OpenSSF Scorecard + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + environment: Security + permissions: + security-events: write + id-token: write + steps: + - name: Checkout Repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + - name: Run Analysis + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + repo_token: ${{ secrets.SCORECARD_TOKEN }} + - name: Upload Results + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + - name: Upload to Code-Scanning + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.29.5 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index 20f4c17fa..eb2ea4f18 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ hs_err_pid* # Maven build target/ +native-libs/ ###################### # IntelliJ @@ -27,7 +28,6 @@ target/ classes/ out/ - ###################### # Eclipse ###################### @@ -81,6 +81,8 @@ Desktop.ini ###################### /bin/ /deploy/ +/dist/ +/site/ ###################### # Logs @@ -96,6 +98,7 @@ Desktop.ini docs/node_modules docs/.cache +.cache docs/public /example/.aws-sam/ /example/HelloWorldFunction/.aws-sam/ @@ -104,3 +107,14 @@ example/HelloWorldFunction/.gradle example/HelloWorldFunction/build /example/.gradle/ /example/.java-version +.gradle +build/ +.terraform* +terraform.tfstate* + +# LLMs +.kiro/ +.claude/ +.amazonq/ +.github/instructions + diff --git a/.mvn/README b/.mvn/README new file mode 100644 index 000000000..a851f5e55 --- /dev/null +++ b/.mvn/README @@ -0,0 +1,2 @@ +This is here purely so that we can get the root directory using maven.multiModuleProjectDirectory + diff --git a/.sonarcloud.properties b/.sonarcloud.properties new file mode 100644 index 000000000..d9a4f79cf --- /dev/null +++ b/.sonarcloud.properties @@ -0,0 +1,17 @@ +# +# Copyright 2023 Amazon.com, Inc. or its affiliates. +# Licensed under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +sonar.exclusions=examples/**/*,powertools-e2e-tests/handlers/**/* + +# Ignore code duplicates in the examples +sonar.cpd.exclusions=examples/**/*,powertools-e2e-tests/**/* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..a07df5d3a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1115 @@ +<!-- changelog is partially generated, so it doesn't follow headings and required structure, so we disable it. --> +<!-- markdownlint-disable --> + +<a name="unreleased"></a> +# Unreleased + +## Documentation + +* **logger:** Fix logging environment variables names in documentation ([#2161](https://github.com/aws-powertools/powertools-lambda-java/issues/2161)) + +## Features + +* add CRaC priming support to powertools-kafka module ([#2145](https://github.com/aws-powertools/powertools-lambda-java/issues/2145)) +* **metrics:** introduce Metrics.flushMetrics ([#2154](https://github.com/aws-powertools/powertools-lambda-java/issues/2154)) + +## Maintenance + +* bump aws.sdk.version from 2.35.6 to 2.35.7 ([#2190](https://github.com/aws-powertools/powertools-lambda-java/issues/2190)) +* bump com.networknt:json-schema-validator from 1.5.8 to 1.5.9 ([#2189](https://github.com/aws-powertools/powertools-lambda-java/issues/2189)) +* bump sam/build-java21 ([#2195](https://github.com/aws-powertools/powertools-lambda-java/issues/2195)) +* bump squidfunk/mkdocs-material in /docs ([#2194](https://github.com/aws-powertools/powertools-lambda-java/issues/2194)) +* bump com.github.spotbugs:spotbugs-maven-plugin ([#2192](https://github.com/aws-powertools/powertools-lambda-java/issues/2192)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.214.0 to 2.220.0 ([#2191](https://github.com/aws-powertools/powertools-lambda-java/issues/2191)) +* bump io.github.ascopes:protobuf-maven-plugin ([#2193](https://github.com/aws-powertools/powertools-lambda-java/issues/2193)) +* bump aws.xray.recorder.version from 2.19.0 to 2.20.0 ([#2185](https://github.com/aws-powertools/powertools-lambda-java/issues/2185)) +* bump aws.sdk.version from 2.33.2 to 2.33.5 ([#2132](https://github.com/aws-powertools/powertools-lambda-java/issues/2132)) +* bump org.apache.maven.plugins:maven-javadoc-plugin ([#2186](https://github.com/aws-powertools/powertools-lambda-java/issues/2186)) +* bump org.assertj:assertj-core from 3.27.4 to 3.27.6 ([#2184](https://github.com/aws-powertools/powertools-lambda-java/issues/2184)) +* bump aws.sdk.version from 2.34.9 to 2.35.6 ([#2183](https://github.com/aws-powertools/powertools-lambda-java/issues/2183)) +* bump actions/dependency-review-action from 4.8.0 to 4.8.1 ([#2180](https://github.com/aws-powertools/powertools-lambda-java/issues/2180)) +* bump github/codeql-action from 3.30.5 to 4.30.8 ([#2179](https://github.com/aws-powertools/powertools-lambda-java/issues/2179)) +* bump aws-actions/configure-aws-credentials from 5.0.0 to 5.1.0 ([#2177](https://github.com/aws-powertools/powertools-lambda-java/issues/2177)) +* bump com.google.protobuf:protobuf-java from 4.32.0 to 4.32.1 ([#2175](https://github.com/aws-powertools/powertools-lambda-java/issues/2175)) +* bump aws.sdk.version from 2.34.5 to 2.34.9 ([#2174](https://github.com/aws-powertools/powertools-lambda-java/issues/2174)) +* bump org.apache.commons:commons-lang3 from 3.18.0 to 3.19.0 ([#2172](https://github.com/aws-powertools/powertools-lambda-java/issues/2172)) +* bump org.apache.maven.plugins:maven-artifact-plugin ([#2171](https://github.com/aws-powertools/powertools-lambda-java/issues/2171)) +* Add User-Agent execution interceptors ([#2166](https://github.com/aws-powertools/powertools-lambda-java/issues/2166)) +* bump org.apache.kafka:kafka-clients from 4.0.0 to 4.1.0 ([#2134](https://github.com/aws-powertools/powertools-lambda-java/issues/2134)) +* bump graalvm/setup-graalvm from 1.3.6 to 1.4.1 ([#2168](https://github.com/aws-powertools/powertools-lambda-java/issues/2168)) +* bump ossf/scorecard-action from 2.4.2 to 2.4.3 ([#2165](https://github.com/aws-powertools/powertools-lambda-java/issues/2165)) +* bump squidfunk/mkdocs-material in /docs ([#2164](https://github.com/aws-powertools/powertools-lambda-java/issues/2164)) +* bump log4j.version from 2.25.1 to 2.25.2 ([#2160](https://github.com/aws-powertools/powertools-lambda-java/issues/2160)) +* bump org.apache.maven.plugins:maven-failsafe-plugin ([#2159](https://github.com/aws-powertools/powertools-lambda-java/issues/2159)) +* bump actions/dependency-review-action from 4.7.3 to 4.8.0 ([#2158](https://github.com/aws-powertools/powertools-lambda-java/issues/2158)) +* bump github/codeql-action from 3.30.1 to 3.30.5 ([#2157](https://github.com/aws-powertools/powertools-lambda-java/issues/2157)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.9.0 to 3.10.0 ([#2155](https://github.com/aws-powertools/powertools-lambda-java/issues/2155)) +* bump com.amazonaws:aws-lambda-java-runtime-interface-client ([#2149](https://github.com/aws-powertools/powertools-lambda-java/issues/2149)) +* bump aws.sdk.version from 2.33.2 to 2.34.5 ([#2156](https://github.com/aws-powertools/powertools-lambda-java/issues/2156)) +* bump org.codehaus.mojo:versions-maven-plugin ([#2148](https://github.com/aws-powertools/powertools-lambda-java/issues/2148)) +* bump squidfunk/mkdocs-material in /docs ([#2144](https://github.com/aws-powertools/powertools-lambda-java/issues/2144)) +* bump tj-actions/changed-files from 46.0.5 to 47.0.0 ([#2143](https://github.com/aws-powertools/powertools-lambda-java/issues/2143)) +* bump sam/build-java21 ([#2141](https://github.com/aws-powertools/powertools-lambda-java/issues/2141)) +* bump com.amazonaws:aws-lambda-java-core from 1.3.0 to 1.4.0 ([#2135](https://github.com/aws-powertools/powertools-lambda-java/issues/2135)) +* **deps:** Use mockito 5.20.0 ([#2181](https://github.com/aws-powertools/powertools-lambda-java/issues/2181)) +* **docs:** Add AWS docs meta tags ([#2170](https://github.com/aws-powertools/powertools-lambda-java/issues/2170)) + + +<a name="v2.4.0"></a> +## [v2.4.0] - 2025-09-09 +## Bug Fixes + +* **ci:** Update branch protection output ([#2053](https://github.com/aws-powertools/powertools-lambda-java/issues/2053)) + +## Documentation + +* Add AWS copyright footer. ([#2119](https://github.com/aws-powertools/powertools-lambda-java/issues/2119)) +* Update docs introduction +* Rename wrong POWERTOOLS_DISABLE_METRICS to correct POWERTOOLS_METRICS_DISABLED environment variable. ([#2043](https://github.com/aws-powertools/powertools-lambda-java/issues/2043)) +* update readme ([#2045](https://github.com/aws-powertools/powertools-lambda-java/issues/2045)) + +## Features + +* Support CRaC priming of powertools validation ([#2081](https://github.com/aws-powertools/powertools-lambda-java/issues/2081)) +* **graalvm:** GraalVM support for powertools-cloudformation ([#2090](https://github.com/aws-powertools/powertools-lambda-java/issues/2090)) +* **graalvm:** GraalVM support for Idempotency utility ([#2080](https://github.com/aws-powertools/powertools-lambda-java/issues/2080)) +* **logging:** Log buffering support for Logj42 and Logback ([#2103](https://github.com/aws-powertools/powertools-lambda-java/issues/2103)) + +## Maintenance + +* bump dev.aspectj:aspectj-maven-plugin from 1.13.1 to 1.14.1 ([#2099](https://github.com/aws-powertools/powertools-lambda-java/issues/2099)) +* bump dev.aspectj:aspectj-maven-plugin from 1.14 to 1.14.1 ([#2037](https://github.com/aws-powertools/powertools-lambda-java/issues/2037)) +* bump github/codeql-action from 3.29.8 to 3.29.9 ([#2038](https://github.com/aws-powertools/powertools-lambda-java/issues/2038)) +* bump org.apache.maven.plugins:maven-deploy-plugin ([#2040](https://github.com/aws-powertools/powertools-lambda-java/issues/2040)) +* bump org.yaml:snakeyaml from 2.4 to 2.5 ([#2111](https://github.com/aws-powertools/powertools-lambda-java/issues/2111)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.8.1 to 3.9.0 ([#2114](https://github.com/aws-powertools/powertools-lambda-java/issues/2114)) +* bump aws.sdk.version from 2.32.31 to 2.33.1 ([#2115](https://github.com/aws-powertools/powertools-lambda-java/issues/2115)) +* bump graalvm/setup-graalvm from 1.3.5 to 1.3.6 ([#2116](https://github.com/aws-powertools/powertools-lambda-java/issues/2116)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.213.0 to 2.214.0 ([#2117](https://github.com/aws-powertools/powertools-lambda-java/issues/2117)) +* bump aws-actions/configure-aws-credentials from 4.3.1 to 5.0.0 ([#2120](https://github.com/aws-powertools/powertools-lambda-java/issues/2120)) +* bump com.github.spotbugs:spotbugs-maven-plugin ([#2125](https://github.com/aws-powertools/powertools-lambda-java/issues/2125)) +* bump github/codeql-action from 3.30.0 to 3.30.1 ([#2126](https://github.com/aws-powertools/powertools-lambda-java/issues/2126)) +* bump squidfunk/mkdocs-material in /docs ([#2127](https://github.com/aws-powertools/powertools-lambda-java/issues/2127)) +* bump jackson.version from 2.19.2 to 2.20 ([#2097](https://github.com/aws-powertools/powertools-lambda-java/issues/2097)) +* bump aws.sdk.version from 2.32.18 to 2.32.21 ([#2041](https://github.com/aws-powertools/powertools-lambda-java/issues/2041)) +* bump aws.sdk.version from 2.32.26 to 2.32.31 ([#2098](https://github.com/aws-powertools/powertools-lambda-java/issues/2098)) +* bump github/codeql-action from 3.29.11 to 3.30.0 ([#2106](https://github.com/aws-powertools/powertools-lambda-java/issues/2106)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.212.0 to 2.213.0 ([#2100](https://github.com/aws-powertools/powertools-lambda-java/issues/2100)) +* bump org.apache.maven.plugins:maven-compiler-plugin ([#2094](https://github.com/aws-powertools/powertools-lambda-java/issues/2094)) +* bump actions/checkout from 4.2.2 to 5.0.0 ([#2087](https://github.com/aws-powertools/powertools-lambda-java/issues/2087)) +* bump org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions ([#2088](https://github.com/aws-powertools/powertools-lambda-java/issues/2088)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.8.0 to 3.8.1 ([#2085](https://github.com/aws-powertools/powertools-lambda-java/issues/2085)) +* bump com.github.spotbugs:spotbugs-maven-plugin ([#2084](https://github.com/aws-powertools/powertools-lambda-java/issues/2084)) +* bump sam/build-java21 ([#2083](https://github.com/aws-powertools/powertools-lambda-java/issues/2083)) +* bump aws.sdk.version from 2.32.30 to 2.32.31 ([#2093](https://github.com/aws-powertools/powertools-lambda-java/issues/2093)) +* bump actions/dependency-review-action from 4.7.2 to 4.7.3 ([#2092](https://github.com/aws-powertools/powertools-lambda-java/issues/2092)) +* bump aws.sdk.version from 2.32.28 to 2.32.30 ([#2089](https://github.com/aws-powertools/powertools-lambda-java/issues/2089)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.210.0 to 2.211.0 ([#2042](https://github.com/aws-powertools/powertools-lambda-java/issues/2042)) +* bump aws.sdk.version from 2.32.21 to 2.32.22 ([#2046](https://github.com/aws-powertools/powertools-lambda-java/issues/2046)) +* bump com.google.protobuf:protobuf-java from 4.31.1 to 4.32.0 ([#2050](https://github.com/aws-powertools/powertools-lambda-java/issues/2050)) +* bump aws.sdk.version from 2.32.23 to 2.32.25 ([#2054](https://github.com/aws-powertools/powertools-lambda-java/issues/2054)) +* bump squidfunk/mkdocs-material in /docs ([#2074](https://github.com/aws-powertools/powertools-lambda-java/issues/2074)) +* bump github/codeql-action from 3.29.10 to 3.29.11 ([#2073](https://github.com/aws-powertools/powertools-lambda-java/issues/2073)) +* bump log4j.version from 2.25.1 to 2.25.1 ([#2072](https://github.com/aws-powertools/powertools-lambda-java/issues/2072)) +* bump org.apache.maven.plugins:maven-shade-plugin ([#2071](https://github.com/aws-powertools/powertools-lambda-java/issues/2071)) +* bump org.graalvm.buildtools:native-maven-plugin ([#2070](https://github.com/aws-powertools/powertools-lambda-java/issues/2070)) +* bump com.amazonaws:aws-lambda-java-runtime-interface-client ([#2069](https://github.com/aws-powertools/powertools-lambda-java/issues/2069)) +* bump aws.sdk.version from 2.32.2 to 2.32.28 ([#2068](https://github.com/aws-powertools/powertools-lambda-java/issues/2068)) +* bump actions/setup-java from 4.7.1 to 5.0.0 ([#2067](https://github.com/aws-powertools/powertools-lambda-java/issues/2067)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.211.0 to 2.212.0 ([#2066](https://github.com/aws-powertools/powertools-lambda-java/issues/2066)) +* bump org.apache.maven.plugins:maven-javadoc-plugin ([#2065](https://github.com/aws-powertools/powertools-lambda-java/issues/2065)) +* bump aws.sdk.version from 2.32.25 to 2.32.27 ([#2064](https://github.com/aws-powertools/powertools-lambda-java/issues/2064)) +* bump aws.sdk.version from 2.32.22 to 2.32.23 ([#2048](https://github.com/aws-powertools/powertools-lambda-java/issues/2048)) +* bump squidfunk/mkdocs-material in /docs ([#2058](https://github.com/aws-powertools/powertools-lambda-java/issues/2058)) +* bump org.apache.maven.plugins:maven-javadoc-plugin ([#2059](https://github.com/aws-powertools/powertools-lambda-java/issues/2059)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.7.0 to 3.8.0 ([#2057](https://github.com/aws-powertools/powertools-lambda-java/issues/2057)) +* bump actions/checkout from 4.2.2 to 5.0.0 ([#2036](https://github.com/aws-powertools/powertools-lambda-java/issues/2036)) +* bump actions/dependency-review-action from 4.7.1 to 4.7.2 ([#2055](https://github.com/aws-powertools/powertools-lambda-java/issues/2055)) +* bump sam/build-java21 ([#2075](https://github.com/aws-powertools/powertools-lambda-java/issues/2075)) +* bump aws.sdk.version from 2.32.19 to 2.32.26 ([#2060](https://github.com/aws-powertools/powertools-lambda-java/issues/2060)) +* bump github/codeql-action from 3.29.9 to 3.29.10 ([#2056](https://github.com/aws-powertools/powertools-lambda-java/issues/2056)) +* **ci:** Add powertools-e2e-tests/handlers as module to capture it in GitHub actions version upgrades. ([#2063](https://github.com/aws-powertools/powertools-lambda-java/issues/2063)) +* **ci:** Fix bug where docs were released with old version during release workflow. ([#2076](https://github.com/aws-powertools/powertools-lambda-java/issues/2076)) +* **ci:** Run unit tests for GraalVM as well during build. ([#2047](https://github.com/aws-powertools/powertools-lambda-java/issues/2047)) +* **ci:** Remove non-PR triggers for verify dependencies workflow. ([#2044](https://github.com/aws-powertools/powertools-lambda-java/issues/2044)) +* **ci:** Fix circular dependency in dynamodb-local and maven packaging phases. ([#2129](https://github.com/aws-powertools/powertools-lambda-java/issues/2129)) +* **ci:** Do not use Mockito SNAPSHOT version for release. ([#2137](https://github.com/aws-powertools/powertools-lambda-java/issues/2137)) +* **ci:** Set mockito SNAPSHOT version only for Graal profiles. ([#2138](https://github.com/aws-powertools/powertools-lambda-java/issues/2138)) +* **gitignore:** add .kiro, .claude, .amazonq to prevent deletion ([#2078](https://github.com/aws-powertools/powertools-lambda-java/issues/2078)) + + +<a name="v2.3.0"></a> +## [v2.3.0] - 2025-08-12 +## Documentation + +* **examples:** Add Bazel example for core utilities ([#2022](https://github.com/aws-powertools/powertools-lambda-java/issues/2022)) +* **examples:** Add Logging and Tracing to idempotency example with correct configuration. ([#1993](https://github.com/aws-powertools/powertools-lambda-java/issues/1993)) +* **examples:** Enable end to end tracing for SQS batch example. ([#1995](https://github.com/aws-powertools/powertools-lambda-java/issues/1995)) + +## Features + +* Support CRaC priming of powertools metrics and idempotency-dynamodb ([#1861](https://github.com/aws-powertools/powertools-lambda-java/issues/1861)) + +## Maintenance + +* bump github/codeql-action from 3.29.4 to 3.29.5 ([#1992](https://github.com/aws-powertools/powertools-lambda-java/issues/1992)) +* bump org.assertj:assertj-core from 3.27.3 to 3.27.4 ([#2031](https://github.com/aws-powertools/powertools-lambda-java/issues/2031)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.208.0 to 2.210.0 ([#2030](https://github.com/aws-powertools/powertools-lambda-java/issues/2030)) +* bump aws.sdk.version from 2.32.18 to 2.32.19 ([#2029](https://github.com/aws-powertools/powertools-lambda-java/issues/2029)) +* bump co.elastic.logging:logback-ecs-encoder from 1.6.0 to 1.7.0 ([#2028](https://github.com/aws-powertools/powertools-lambda-java/issues/2028)) +* bump com.github.spotbugs:spotbugs-maven-plugin from 4.8.4.0 to 4.9.3.2 ([#2010](https://github.com/aws-powertools/powertools-lambda-java/issues/2010)) +* bump com.amazonaws:aws-lambda-java-runtime-interface-client ([#2026](https://github.com/aws-powertools/powertools-lambda-java/issues/2026)) +* bump github/codeql-action from 3.29.7 to 3.29.8 ([#2027](https://github.com/aws-powertools/powertools-lambda-java/issues/2027)) +* bump org.crac:crac from 1.4.0 to 1.5.0 ([#2025](https://github.com/aws-powertools/powertools-lambda-java/issues/2025)) +* bump aws.sdk.version from 2.32.6 to 2.32.18 ([#2024](https://github.com/aws-powertools/powertools-lambda-java/issues/2024)) +* bump org.junit.jupiter:junit-jupiter from 5.11.1 to 5.13.4 ([#2023](https://github.com/aws-powertools/powertools-lambda-java/issues/2023)) +* bump org.codehaus.mojo:exec-maven-plugin from 3.3.0 to 3.5.1 ([#2015](https://github.com/aws-powertools/powertools-lambda-java/issues/2015)) +* bump aws.sdk.version from 2.32.10 to 2.32.16 ([#2014](https://github.com/aws-powertools/powertools-lambda-java/issues/2014)) +* bump io.github.ascopes:protobuf-maven-plugin from 3.6.1 to 3.7.0 ([#2016](https://github.com/aws-powertools/powertools-lambda-java/issues/2016)) +* bump actions/download-artifact from 4.3.0 to 5.0.0 ([#2017](https://github.com/aws-powertools/powertools-lambda-java/issues/2017)) +* bump squidfunk/mkdocs-material in /docs ([#1984](https://github.com/aws-powertools/powertools-lambda-java/issues/1984)) +* bump org.apache.maven.plugins:maven-surefire-plugin ([#2013](https://github.com/aws-powertools/powertools-lambda-java/issues/2013)) +* bump aws-actions/configure-aws-credentials from 4.2.1 to 4.3.1 ([#2011](https://github.com/aws-powertools/powertools-lambda-java/issues/2011)) +* bump software.amazon.awscdk:aws-cdk-lib from 2.162.1 to 2.208.0 ([#1990](https://github.com/aws-powertools/powertools-lambda-java/issues/1990)) +* **ci:** Make E2E tests compatible with latest CDK lib version. Improve retry implementation. ([#2008](https://github.com/aws-powertools/powertools-lambda-java/issues/2008)) +* **ci:** Improve reliability of retries in TracingE2ET ([#2018](https://github.com/aws-powertools/powertools-lambda-java/issues/2018)) + + +<a name="v2.2.1"></a> +## [v2.2.1] - 2025-07-29 +## Bug Fixes + +* **parameters:** Correctly check for empty values in AppConfig Parameters Provider. ([#1982](https://github.com/aws-powertools/powertools-lambda-java/issues/1982)) + +## Maintenance + +* bump dependabot/fetch-metadata from 2.3.0 to 2.4.0 ([#1954](https://github.com/aws-powertools/powertools-lambda-java/issues/1954)) +* bump github/codeql-action from 3.29.3 to 3.29.4 ([#1978](https://github.com/aws-powertools/powertools-lambda-java/issues/1978)) +* bump org.apache.logging.log4j:log4j-transform-maven-shade-plugin-extensions ([#1977](https://github.com/aws-powertools/powertools-lambda-java/issues/1977)) +* bump aws.sdk.version from 2.31.78 to 2.32.6 ([#1976](https://github.com/aws-powertools/powertools-lambda-java/issues/1976)) +* bump com.amazonaws:aws-lambda-java-events from 3.16.0 to 3.16.1 ([#1975](https://github.com/aws-powertools/powertools-lambda-java/issues/1975)) +* bump com.networknt:json-schema-validator from 1.5.1 to 1.5.8 ([#1974](https://github.com/aws-powertools/powertools-lambda-java/issues/1974)) +* bump ossf/scorecard-action from 2.4.0 to 2.4.2 ([#1950](https://github.com/aws-powertools/powertools-lambda-java/issues/1950)) +* bump org.apache.maven.plugins:maven-compiler-plugin ([#1972](https://github.com/aws-powertools/powertools-lambda-java/issues/1972)) +* bump actions/download-artifact from 4.2.1 to 4.3.0 ([#1967](https://github.com/aws-powertools/powertools-lambda-java/issues/1967)) +* bump aws-actions/configure-aws-credentials from 2.2.0 to 4.2.1 ([#1965](https://github.com/aws-powertools/powertools-lambda-java/issues/1965)) +* bump actions/dependency-review-action from 4.5.0 to 4.7.1 ([#1968](https://github.com/aws-powertools/powertools-lambda-java/issues/1968)) +* bump actions/checkout from 3.5.3 to 4.2.2 ([#1963](https://github.com/aws-powertools/powertools-lambda-java/issues/1963)) +* bump sam/build-java21 ([#1962](https://github.com/aws-powertools/powertools-lambda-java/issues/1962)) +* bump squidfunk/mkdocs-material in /docs ([#1961](https://github.com/aws-powertools/powertools-lambda-java/issues/1961)) +* bump actions/upload-artifact from 4.5.0 to 4.6.2 ([#1953](https://github.com/aws-powertools/powertools-lambda-java/issues/1953)) +* bump github/codeql-action from 3.27.9 to 3.29.3 ([#1958](https://github.com/aws-powertools/powertools-lambda-java/issues/1958)) +* bump actions/setup-java from 3.11.0 to 4.7.1 ([#1957](https://github.com/aws-powertools/powertools-lambda-java/issues/1957)) +* **ci:** Add Docker paths via globs to dependabot and update Dockerfiles to pin sha256 ([#1960](https://github.com/aws-powertools/powertools-lambda-java/issues/1960)) +* **ci:** Remove osv workflow. ([#1973](https://github.com/aws-powertools/powertools-lambda-java/issues/1973)) +* **ci:** add new dependabot package ecosystems ([#1948](https://github.com/aws-powertools/powertools-lambda-java/issues/1948)) +* **ci:** Add GraalVM E2E tests and GH workflows ([#1945](https://github.com/aws-powertools/powertools-lambda-java/issues/1945)) + + +<a name="v2.2.0"></a> +## [v2.2.0] - 2025-07-15 +## Bug Fixes + +* **examples:** Fix GraalVM metadata after common runtime client changes ([#1935](https://github.com/aws-powertools/powertools-lambda-java/issues/1935)) + +## Features + +* **batch:** add support for batch execution in parallel with custom Executor ([#1900](https://github.com/aws-powertools/powertools-lambda-java/issues/1900)) +* **serialization:** Add GraalVM metadata configuration ([#1905](https://github.com/aws-powertools/powertools-lambda-java/issues/1905)) + +## Maintenance + +* update issue, PR, and discussion templates ([#1915](https://github.com/aws-powertools/powertools-lambda-java/issues/1915)) +* **ci:** remove v2 dependabot configuration. Restore OSSF scorecard workflow. ([#1924](https://github.com/aws-powertools/powertools-lambda-java/issues/1924)) +* **ci:** Update branch protection rules ([#1914](https://github.com/aws-powertools/powertools-lambda-java/issues/1914)) + + +<a name="v2.1.1"></a> +## [v2.1.1] - 2025-06-20 +## Bug Fixes + +* **kafka:** Handle message indices in proto data also for Glue Schema Registry ([#1907](https://github.com/aws-powertools/powertools-lambda-java/issues/1907)) + +## Maintenance + + + +<a name="v2.1.0"></a> +## [v2.1.0] - 2025-06-19 +## Bug Fixes + +* **ci:** Add maven project description to Kafka utility. ([#1903](https://github.com/aws-powertools/powertools-lambda-java/issues/1903)) +* **kafka:** Add support for confluent message indices. ([#1902](https://github.com/aws-powertools/powertools-lambda-java/issues/1902)) +* **metrics:** Do not flush when no metrics were added to avoid printing root-level _aws dict ([#1891](https://github.com/aws-powertools/powertools-lambda-java/issues/1891)) + +## Documentation + +* Announce deprecation of v1 +* Version documentation ([#1878](https://github.com/aws-powertools/powertools-lambda-java/issues/1878)) + +## Features + +* **kafka:** New Kafka utility ([#1898](https://github.com/aws-powertools/powertools-lambda-java/issues/1898)) + +## Maintenance + +* **ci:** Update workflows to make v2 the default ([#1888](https://github.com/aws-powertools/powertools-lambda-java/issues/1888)) + + +<a name="v2.0.0"></a> +## [v2.0.0] - 2025-06-12 +## Maintenance + + + +<a name="v2.0.0-RC1"></a> +## [v2.0.0-RC1] - 2025-06-11 +## Bug Fixes + +* workflow paths for examples v2 builds +* add aspectj-rt to batch e2e ([#1410](https://github.com/aws-powertools/powertools-lambda-java/issues/1410)) +* **ci:** Fix failing E2E tests and temporarily exclude TracingE2E ([#1847](https://github.com/aws-powertools/powertools-lambda-java/issues/1847)) +* **ci:** add user/pass to javasetup ([#1832](https://github.com/aws-powertools/powertools-lambda-java/issues/1832)) +* **ci:** Update control flow to allow for better skipping of things ([#1831](https://github.com/aws-powertools/powertools-lambda-java/issues/1831)) +* **ci:** Checkout repo on doc release ([#1869](https://github.com/aws-powertools/powertools-lambda-java/issues/1869)) +* **logging:** Prevent accidental overwriting of reserved keys via structured arguments +* **logging:** Escape double-quotes when serializing strings into JSON. ([#1845](https://github.com/aws-powertools/powertools-lambda-java/issues/1845)) +* **v2:** Fix params builder to provide default transformation manager ([#1549](https://github.com/aws-powertools/powertools-lambda-java/issues/1549)) + +## Documentation + +* v2 documentation maintenance fixing formatting and dependency issues as well as adding roadmap and llms.txt ([#1819](https://github.com/aws-powertools/powertools-lambda-java/issues/1819)) +* **metrics:** Add upgrade guide for re-designed Metrics utility ([#1868](https://github.com/aws-powertools/powertools-lambda-java/issues/1868)) +* **v2:** Create upgrade guide and versioning policy ([#1856](https://github.com/aws-powertools/powertools-lambda-java/issues/1856)) + +## Features + +* advanced logging ([#1539](https://github.com/aws-powertools/powertools-lambda-java/issues/1539)) +* upgraded embedded metrics library for high resolution metrics ([#1550](https://github.com/aws-powertools/powertools-lambda-java/issues/1550)) +* **cfn-custom-resource:** Add optional 'reason' field for detailed failure reporting ([#1810](https://github.com/aws-powertools/powertools-lambda-java/issues/1810)) +* **idempotency:** Add support for ReturnValuesOnConditionCheckFailure in Idempotency. ([#1821](https://github.com/aws-powertools/powertools-lambda-java/issues/1821)) +* **idempotency:** Add response hook feature ([#1814](https://github.com/aws-powertools/powertools-lambda-java/issues/1814)) +* **metrics:** New metrics module implementation with support for Metrics providers and usage without annotations ([#1863](https://github.com/aws-powertools/powertools-lambda-java/issues/1863)) +* **v2:** Add GraalVM reachability metadata for core utilities ([#1753](https://github.com/aws-powertools/powertools-lambda-java/issues/1753)) +* **v2:** parallel batch processing ([#1620](https://github.com/aws-powertools/powertools-lambda-java/issues/1620)) +* **v2:** batch validation with partial failure ([#1621](https://github.com/aws-powertools/powertools-lambda-java/issues/1621)) +* **v2:** publish snapshots ([#1655](https://github.com/aws-powertools/powertools-lambda-java/issues/1655)) +* **v2:** GraalVM support for parameters module ([#1824](https://github.com/aws-powertools/powertools-lambda-java/issues/1824)) +* **v2:** new logging module ([#1435](https://github.com/aws-powertools/powertools-lambda-java/issues/1435)) +* **v2:** Validation failures return 400s ([#1489](https://github.com/aws-powertools/powertools-lambda-java/issues/1489)) + +## Maintenance + +* Support spotbugs running anywhere ([#1537](https://github.com/aws-powertools/powertools-lambda-java/issues/1537)) +* V2 update from main ([#1365](https://github.com/aws-powertools/powertools-lambda-java/issues/1365)) +* remove Java 8 from v2 examples ([#1531](https://github.com/aws-powertools/powertools-lambda-java/issues/1531)) +* fix end 2 end build ([#1534](https://github.com/aws-powertools/powertools-lambda-java/issues/1534)) +* cleanup poms and reduce warning noise ([#1535](https://github.com/aws-powertools/powertools-lambda-java/issues/1535)) +* [V2] rename 'core' module to 'common' ([#1364](https://github.com/aws-powertools/powertools-lambda-java/issues/1364)) +* update v2 ([#1409](https://github.com/aws-powertools/powertools-lambda-java/issues/1409)) +* remove aspectj-rt from the library ([#1408](https://github.com/aws-powertools/powertools-lambda-java/issues/1408)) +* Start V2 branch ([#1346](https://github.com/aws-powertools/powertools-lambda-java/issues/1346)) +* **automation:** Update automation workflows ([#1779](https://github.com/aws-powertools/powertools-lambda-java/issues/1779)) ([#1830](https://github.com/aws-powertools/powertools-lambda-java/issues/1830)) +* **ci:** Set snapshot repository to "central" server ID +* **ci:** Publish to Maven Central instead of OSSRH instance ([#1858](https://github.com/aws-powertools/powertools-lambda-java/issues/1858)) +* **v2:** Merge down from main ([#1574](https://github.com/aws-powertools/powertools-lambda-java/issues/1574)) +* **v2:** Split parameters module up by parameter provider ([#1403](https://github.com/aws-powertools/powertools-lambda-java/issues/1403)) +* **v2:** Fix IaC lint ([#1576](https://github.com/aws-powertools/powertools-lambda-java/issues/1576)) +* **v2:** e2e tests ([#1571](https://github.com/aws-powertools/powertools-lambda-java/issues/1571)) +* **v2:** clean examples ([#1495](https://github.com/aws-powertools/powertools-lambda-java/issues/1495)) +* **v2:** document use of aws-crt-client ([#1092](https://github.com/aws-powertools/powertools-lambda-java/issues/1092)) ([#1605](https://github.com/aws-powertools/powertools-lambda-java/issues/1605)) +* **v2:** remove java 1.8 relics from the code ([#1659](https://github.com/aws-powertools/powertools-lambda-java/issues/1659)) +* **v2:** remove deprecated code ([#1624](https://github.com/aws-powertools/powertools-lambda-java/issues/1624)) +* **v2:** Remove rule preventing production release of 2.0.0 ([#1867](https://github.com/aws-powertools/powertools-lambda-java/issues/1867)) +* **v2:** Split powertools idempotency module (without redis impl) ([#1559](https://github.com/aws-powertools/powertools-lambda-java/issues/1559)) + +## Pull Requests + +* Merge pull request [#1608](https://github.com/aws-powertools/powertools-lambda-java/issues/1608) from aws-powertools/chore/v2-merge-main-down +* Merge pull request [#1525](https://github.com/aws-powertools/powertools-lambda-java/issues/1525) from aws-powertools/chore/main-into-v2 +* Merge pull request [#1494](https://github.com/aws-powertools/powertools-lambda-java/issues/1494) from aws-powertools/chore/merge-main-into-v2 +* Merge pull request [#1492](https://github.com/aws-powertools/powertools-lambda-java/issues/1492) from aws-powertools/main-into-v2-again +* Merge pull request [#1477](https://github.com/aws-powertools/powertools-lambda-java/issues/1477) from aws-powertools/chore/main-into-v2 + + +<a name="v1.20.2"></a> +## [v1.20.2] - 2025-05-20 +## Bug Fixes + +* **ci:** update release workflow ([#1854](https://github.com/aws-powertools/powertools-lambda-java/issues/1854)) +* **ci:** minor fixes for workflows ([#1829](https://github.com/aws-powertools/powertools-lambda-java/issues/1829)) + +## Documentation + +* Add version policy page and llms.txt, enable privacy plugin, fix formatting ([#1823](https://github.com/aws-powertools/powertools-lambda-java/issues/1823)) + +## Maintenance + +* **automation:** Update automation workflows ([#1779](https://github.com/aws-powertools/powertools-lambda-java/issues/1779)) + + +<a name="v1.20.1"></a> +## [v1.20.1] - 2025-04-08 +## Bug Fixes + +* Load version.properties file as resource stream to fix loading when packaged as jar. ([#1813](https://github.com/aws-powertools/powertools-lambda-java/issues/1813)) + +## Documentation + +* fix 2 typos +* Correct XML formatting for Maven configuration in Large Messages utility docs + +## Maintenance + +* Prep release 1.20.1 ([#1817](https://github.com/aws-powertools/powertools-lambda-java/issues/1817)) + + +<a name="v1.20.0"></a> +## [v1.20.0] - 2025-03-25 +## Features + +* **cfn-custom-resource:** Add optional 'reason' field for detailed failure reporting ([#1758](https://github.com/aws-powertools/powertools-lambda-java/issues/1758)) + +## Maintenance + +* Prep release 1.20.0 ([#1811](https://github.com/aws-powertools/powertools-lambda-java/issues/1811)) + + +<a name="v1.19.0"></a> +## [v1.19.0] - 2025-03-07 +## Bug Fixes + +* add workflow dispatch to OSV +* Allow empty responses as well as null response in AppConfig ([#1673](https://github.com/aws-powertools/powertools-lambda-java/issues/1673)) +* **ci:** Add workflow_dispatch to build script ([#1792](https://github.com/aws-powertools/powertools-lambda-java/issues/1792)) +* **ci:** add permissions to release workflow +* **ci:** Permissions ([#1771](https://github.com/aws-powertools/powertools-lambda-java/issues/1771)) +* **ci:** OSSF Changes ([#1769](https://github.com/aws-powertools/powertools-lambda-java/issues/1769)) + +## Documentation + +* add roadmap page and include roadmap for 2025 +* improve tracing doc for sdk instrumentation ([#1687](https://github.com/aws-powertools/powertools-lambda-java/issues/1687)) +* add link to Powertools for AWS Lambda workshop ([#1641](https://github.com/aws-powertools/powertools-lambda-java/issues/1641)) +* HelloWorldStreamFunction in examples fails with sam ([#1532](https://github.com/aws-powertools/powertools-lambda-java/issues/1532)) + +## Features + +* **build:** remove java 8 support in v2 ([#1606](https://github.com/aws-powertools/powertools-lambda-java/issues/1606)) +* **ci:** Add OSV + +## Maintenance + +* deprecate java1.8 al1 ([#1706](https://github.com/aws-powertools/powertools-lambda-java/issues/1706)) +* Testing java21 aspectj pre-release ([#1519](https://github.com/aws-powertools/powertools-lambda-java/issues/1519)) +* Remove build cruft +* SAM and Terraform IaC extracted from pr_build and simplified approach. ([#1533](https://github.com/aws-powertools/powertools-lambda-java/issues/1533)) +* Update netty version ([#1768](https://github.com/aws-powertools/powertools-lambda-java/issues/1768)) +* Set versions of transitive dependencies ([#1767](https://github.com/aws-powertools/powertools-lambda-java/issues/1767)) +* update Jackson +* Remove empty CDK test ([#1542](https://github.com/aws-powertools/powertools-lambda-java/issues/1542)) +* add openssf to repo +* remove auto-merge +* remove unecessary creds acquisition ([#1572](https://github.com/aws-powertools/powertools-lambda-java/issues/1572)) +* update version to next snapshot: 1-19.0-SNAPSHOT ([#1516](https://github.com/aws-powertools/powertools-lambda-java/issues/1516)) +* **ci:** update permissions ([#1764](https://github.com/aws-powertools/powertools-lambda-java/issues/1764)) +* **ci:** Add release environment +* **ci:** Remove RELEASE variable ([#1772](https://github.com/aws-powertools/powertools-lambda-java/issues/1772)) +* **deps:** update JSII to 1.108 ([#1791](https://github.com/aws-powertools/powertools-lambda-java/issues/1791)) +* **deps:** Update deps for jackson ([#1793](https://github.com/aws-powertools/powertools-lambda-java/issues/1793)) +* **docs:** load self hosted mermaid.js + +## Pull Requests + +* Merge pull request [#1720](https://github.com/aws-powertools/powertools-lambda-java/issues/1720) from aws-powertools/chore/docs_script_self + + +<a name="v1.18.0"></a> +## [v1.18.0] - 2023-11-16 +## Bug Fixes + +* get trace id from system property when env var is not set ([#1503](https://github.com/aws-powertools/powertools-lambda-java/issues/1503)) +* Fix schema validation unit test build issues ([#1456](https://github.com/aws-powertools/powertools-lambda-java/issues/1456)) + +## Documentation + +* Update gradle configuration readme ([#1359](https://github.com/aws-powertools/powertools-lambda-java/issues/1359)) +* Adding Kotlin example. ([#1454](https://github.com/aws-powertools/powertools-lambda-java/issues/1454)) +* apply line highlight only for default light mode ([#1453](https://github.com/aws-powertools/powertools-lambda-java/issues/1453)) +* Add Serveless Framework example ([#1363](https://github.com/aws-powertools/powertools-lambda-java/issues/1363)) +* Fix link to SQS large message migration guide ([#1422](https://github.com/aws-powertools/powertools-lambda-java/issues/1422)) +* Change link to absolute versioned path for examples ([#1374](https://github.com/aws-powertools/powertools-lambda-java/issues/1374)) +* **customer-reference:** add Vertex Pharmaceuticals as a customer reference ([#1486](https://github.com/aws-powertools/powertools-lambda-java/issues/1486)) +* **logging:** align example cloudwatch example to correct output from code: lambda_request_id --> function_request_id ([#1411](https://github.com/aws-powertools/powertools-lambda-java/issues/1411)) + +## Features + +* ALC ([#1514](https://github.com/aws-powertools/powertools-lambda-java/issues/1514)) +* Add support for POWERTOOLS_LOGGER_LOG_EVENT ([#1510](https://github.com/aws-powertools/powertools-lambda-java/issues/1510)) +* Terraform example ([#1478](https://github.com/aws-powertools/powertools-lambda-java/issues/1478)) + +## Maintenance + +* Addition of Warn Message If Invalid Annotation Key While Tracing [#1511](https://github.com/aws-powertools/powertools-lambda-java/issues/1511) ([#1512](https://github.com/aws-powertools/powertools-lambda-java/issues/1512)) +* artifacts size on good branches ([#1493](https://github.com/aws-powertools/powertools-lambda-java/issues/1493)) +* add missing projects and improve workflow ([#1487](https://github.com/aws-powertools/powertools-lambda-java/issues/1487)) +* java21 support in our build ([#1488](https://github.com/aws-powertools/powertools-lambda-java/issues/1488)) +* Reporting size of the jars in GitHub comments ([#1196](https://github.com/aws-powertools/powertools-lambda-java/issues/1196)) +* secure github actions using hash instead of versions ([#1232](https://github.com/aws-powertools/powertools-lambda-java/issues/1232)) + + +<a name="v1.17.0"></a> +## [v1.17.0] - 2023-08-21 +## Bug Fixes + +* Roll log4j shade transformer forwards ([#1376](https://github.com/aws-powertools/powertools-lambda-java/issues/1376)) +* Rollback doc changes ([#1323](https://github.com/aws-powertools/powertools-lambda-java/issues/1323)) +* use default credentials provider for all provided SDK clients ([#1303](https://github.com/aws-powertools/powertools-lambda-java/issues/1303)) + +## Documentation + +* Adding CDK example ([#1321](https://github.com/aws-powertools/powertools-lambda-java/issues/1321)) +* improve contributing guide ([#1334](https://github.com/aws-powertools/powertools-lambda-java/issues/1334)) +* Add maintainers guide ([#1326](https://github.com/aws-powertools/powertools-lambda-java/issues/1326)) +* versioning - fix typo ([#1322](https://github.com/aws-powertools/powertools-lambda-java/issues/1322)) +* add support for docs versioning ([#1239](https://github.com/aws-powertools/powertools-lambda-java/issues/1239)) ([#1293](https://github.com/aws-powertools/powertools-lambda-java/issues/1293)) +* Started cleaning up example doc ([#1291](https://github.com/aws-powertools/powertools-lambda-java/issues/1291)) + +## Features + +* Add Batch Processor module ([#1317](https://github.com/aws-powertools/powertools-lambda-java/issues/1317)) +* large message in SQS and SNS ([#1310](https://github.com/aws-powertools/powertools-lambda-java/issues/1310)) + +## Maintenance + +* Fix missing version change pieces ([#1382](https://github.com/aws-powertools/powertools-lambda-java/issues/1382)) +* apply checkstyle again ([#1339](https://github.com/aws-powertools/powertools-lambda-java/issues/1339)) +* Add powertools specific user-agent-suffix to the AWS SDK v2 clients ([#1306](https://github.com/aws-powertools/powertools-lambda-java/issues/1306)) +* checkstyle formater & linter ([#1316](https://github.com/aws-powertools/powertools-lambda-java/issues/1316)) +* update poms to SNAPSHOT version for dev ([#1299](https://github.com/aws-powertools/powertools-lambda-java/issues/1299)) + + +<a name="v1.16.1"></a> +## [v1.16.1] - 2023-07-19 +## Bug Fixes + +* idempotency timeout bug ([#1285](https://github.com/aws-powertools/powertools-lambda-java/issues/1285)) +* ParamManager cannot provide default SSM & Secrets providers ([#1282](https://github.com/aws-powertools/powertools-lambda-java/issues/1282)) +* Handle batch failures in FIFO queues correctly ([#1183](https://github.com/aws-powertools/powertools-lambda-java/issues/1183)) +* examples shouldn't be deployed to mvn central ([#1253](https://github.com/aws-powertools/powertools-lambda-java/issues/1253)) + +## Documentation + +* update README.md ([#1294](https://github.com/aws-powertools/powertools-lambda-java/issues/1294)) +* adding our customer references ([#1287](https://github.com/aws-powertools/powertools-lambda-java/issues/1287)) +* update documentation for aspectJ ([#1273](https://github.com/aws-powertools/powertools-lambda-java/issues/1273)) + +## Maintenance + +* **unit-test:** Add missing unit tests in modules with low coverage ([#1264](https://github.com/aws-powertools/powertools-lambda-java/issues/1264)) + + +<a name="v1.16.0"></a> +## [v1.16.0] - 2023-06-29 +## Bug Fixes + +* e2e tests on JDK8 ([#1225](https://github.com/aws-powertools/powertools-lambda-java/issues/1225)) +* codecov URL ([#1222](https://github.com/aws-powertools/powertools-lambda-java/issues/1222)) +* remove GH pages ([#1211](https://github.com/aws-powertools/powertools-lambda-java/issues/1211)) +* update references to other variants +* missing idempotency key should not persist any data ([#1201](https://github.com/aws-powertools/powertools-lambda-java/issues/1201)) +* **docs:** add site_url to docs + +## Features + +* Add AppConfig provider to parameters module ([#1104](https://github.com/aws-powertools/powertools-lambda-java/issues/1104)) +* end-to-end tests for core modules and idempotency ([#970](https://github.com/aws-powertools/powertools-lambda-java/issues/970)) +* **docs:** adds S3 Docs uploader + +## Maintenance + +* Update docs base origin url ([#1238](https://github.com/aws-powertools/powertools-lambda-java/issues/1238)) +* E2E tests GitHub action ([#1175](https://github.com/aws-powertools/powertools-lambda-java/issues/1175)) +* add all java versions and use corretto for build ([#1191](https://github.com/aws-powertools/powertools-lambda-java/issues/1191)) +* Change repo URL to the new location ([#1171](https://github.com/aws-powertools/powertools-lambda-java/issues/1171)) +* Swap implementation of `aspectj-maven-plugin` to support Java 17 ([#1172](https://github.com/aws-powertools/powertools-lambda-java/issues/1172)) +* update e2e-tests with latest Powertools version ([#1173](https://github.com/aws-powertools/powertools-lambda-java/issues/1173)) +* rename project from Powertools to Powertools for AWS Lambda (Java) ([#1169](https://github.com/aws-powertools/powertools-lambda-java/issues/1169)) +* **ci:** add workflow to dispatch analytics fetching ([#1143](https://github.com/aws-powertools/powertools-lambda-java/issues/1143)) + + +<a name="v1.15.0"></a> +## [v1.15.0] - 2023-03-21 +## Bug Fixes + +* **cloudformation-module:** Use physicalResourceId when not provided by custom resource ([#1082](https://github.com/aws-powertools/powertools-lambda-java/issues/1082)) + +## Documentation + +* **cloudformation-module:** Improve Docs ([#1090](https://github.com/aws-powertools/powertools-lambda-java/issues/1090)) +* **plugin:** fix mdocs and git revision plugin integration ([#1066](https://github.com/aws-powertools/powertools-lambda-java/issues/1066)) + +## Features + +* Add DynamoDB provider to parameters module ([#1091](https://github.com/aws-powertools/powertools-lambda-java/issues/1091)) + +## Maintenance + +* update the project version to 1.15.0 ([#1097](https://github.com/aws-powertools/powertools-lambda-java/issues/1097)) +* **governance:** update issue templates ([#1062](https://github.com/aws-powertools/powertools-lambda-java/issues/1062)) +* **metrics:** deprecate withMetricLogger in favor of withMetricsLogger ([#1060](https://github.com/aws-powertools/powertools-lambda-java/issues/1060)) + + +<a name="v1.14.0"></a> +## [v1.14.0] - 2023-02-17 +## Documentation + +* **home:** update powertools definition + +## Features + +* **metrics:** introduce MetricsUtils.withMetricsLogger utility ([#1000](https://github.com/aws-powertools/powertools-lambda-java/issues/1000)) + +## Maintenance + +* update the project version to 1.14.0 ([#1052](https://github.com/aws-powertools/powertools-lambda-java/issues/1052)) + + +<a name="v1.13.0"></a> +## [v1.13.0] - 2022-12-14 +## Bug Fixes + +* envelope is not taken into account with built-in types ([#960](https://github.com/aws-powertools/powertools-lambda-java/issues/960)) + +## Documentation + +* add missing grammar article ([#976](https://github.com/aws-powertools/powertools-lambda-java/issues/976)) + +## Features + +* **idempotency:** handle lambda timeout scenarios for INPROGRESS records ([#933](https://github.com/aws-powertools/powertools-lambda-java/issues/933)) + +## Maintenance + +* update the project version to 1.13.0 ([#1018](https://github.com/aws-powertools/powertools-lambda-java/issues/1018)) + + +<a name="v1.12.3"></a> +## [v1.12.3] - 2022-07-12 +## Maintenance + +* **ci:** fix build ([#853](https://github.com/aws-powertools/powertools-lambda-java/issues/853)) +* **ci:** Address GitHub workaround for CVE-2022-24765. +* **ci:** upgrade to checkout v3 + + +<a name="v1.12.2"></a> +## [v1.12.2] - 2022-04-29 +## Bug Fixes + +* remove local implementation of PayloadS3Pointer.java and use payloadoffloading-common ([#851](https://github.com/aws-powertools/powertools-lambda-java/issues/851)) + + +<a name="v1.12.1"></a> +## [v1.12.1] - 2022-04-21 +## Bug Fixes + +* disable idempotency doesn't disable dynamodb client creation in persistent store ([#796](https://github.com/aws-powertools/powertools-lambda-java/issues/796)) + +## Maintenance + +* correct bug fix release number +* **docs:** additional rename of project name ([#781](https://github.com/aws-powertools/powertools-lambda-java/issues/781)) ([#789](https://github.com/aws-powertools/powertools-lambda-java/issues/789)) + +## Reverts +* chore: correct bug fix release number + + +<a name="v1.12.0"></a> +## [v1.12.0] - 2022-03-01 +## Bug Fixes + +* **docs:** fix title for custom resources page ([#763](https://github.com/aws-powertools/powertools-lambda-java/issues/763)) + +## Features + +* Easy Event Deserialization ([#757](https://github.com/aws-powertools/powertools-lambda-java/issues/757)) + +## Maintenance + +* remove examples from the project as it was moved to aws-lambda-powertools-examples ([#772](https://github.com/aws-powertools/powertools-lambda-java/issues/772)) +* remove SQS and Idempotency examples ([#754](https://github.com/aws-powertools/powertools-lambda-java/issues/754)) +* downgrade release plugin to validate release fail issue +* downgrade release plugin to validate release fail issue + + +<a name="v1.11.0"></a> +## [v1.11.0] - 2022-02-16 +## Maintenance + +* **docs:** FAQ for Kotlin projects +* **docs:** Clarify test config needed for Tracing module ([#735](https://github.com/aws-powertools/powertools-lambda-java/issues/735)) +* **docs:** typo in CHANGELOG.md + + +<a name="v1.10.3"></a> +## [v1.10.3] - 2022-02-01 +## Bug Fixes + +* Prevent message to be marked as success if failed sending to DLQ ([#731](https://github.com/aws-powertools/powertools-lambda-java/issues/731)) +* **gradle:** Fix gradle example and docs to work with java 12+ ([#703](https://github.com/aws-powertools/powertools-lambda-java/issues/703)) + +## Maintenance + +* move core utilities example to aws-samples/aws-lambda-powertools-examples ([#733](https://github.com/aws-powertools/powertools-lambda-java/issues/733)) +* Update readme to refer examples repo +* **ci:** set release env variable for auto closing issue + + +<a name="v1.10.2"></a> +## [v1.10.2] - 2022-01-07 +## Features + +* **tracing:** ability to override object mapper ([#698](https://github.com/aws-powertools/powertools-lambda-java/issues/698)) + +## Maintenance + +* Automate release prep ([#696](https://github.com/aws-powertools/powertools-lambda-java/issues/696)) +* **automation:** find replace pom.xml at all levels +* **automation:** find replace pom.xml at all levels +* **docs:** Add FAQs section to docs with information about Lombok support. ([#680](https://github.com/aws-powertools/powertools-lambda-java/issues/680)) + + +<a name="v1.10.1"></a> +## [v1.10.1] - 2022-01-06 +## Features + +* **ci:** auto-notify & close issues on release + +## Maintenance + +* prep release 1.10.1 +* action to automate release prep +* action to automate release prep +* **docs:** Latest version of aspectj.post-compile-weaving +* **docs:** Full gradle config in example for each module +* **docs:** use free fair gradle aspect plugin ([#679](https://github.com/aws-powertools/powertools-lambda-java/issues/679)) + + +<a name="v1.10.0"></a> +## [v1.10.0] - 2021-12-27 + +<a name="v1.9.1"></a> +## [v1.9.1] - 2021-12-27 +## Bug Fixes + +* support batch size greater than 10 processing ([#667](https://github.com/aws-powertools/powertools-lambda-java/issues/667)) + +## Features + +* Json layout modern implementation ([#670](https://github.com/aws-powertools/powertools-lambda-java/issues/670)) + +## Maintenance + +* prep release 1.10.0 ([#671](https://github.com/aws-powertools/powertools-lambda-java/issues/671)) +* Fix docs layout + + +<a name="v1.8.3"></a> +## [v1.8.3] - 2021-12-21 + +<a name="v1.9.0"></a> +## [v1.9.0] - 2021-12-21 +## Features + +* **tracing:** add service annotation ([#655](https://github.com/aws-powertools/powertools-lambda-java/issues/655)) + +## Maintenance + +* prep release 1.9.0 ([#666](https://github.com/aws-powertools/powertools-lambda-java/issues/666)) +* localise abstract json layout implementation ([#664](https://github.com/aws-powertools/powertools-lambda-java/issues/664)) +* Update edit url prefix on doc +* Update docs to reflect latest gradle plugin fix ([#656](https://github.com/aws-powertools/powertools-lambda-java/issues/656)) + + +<a name="v1.8.2"></a> +## [v1.8.2] - 2021-12-15 +## Maintenance + +* prep release 1.8.2 ([#653](https://github.com/aws-powertools/powertools-lambda-java/issues/653)) + + +<a name="v1.8.1"></a> +## [v1.8.1] - 2021-12-10 +## Documentation + +* **tenets:** update Idiomatic tenet to Progressive ([#615](https://github.com/aws-powertools/powertools-lambda-java/issues/615)) + +## Maintenance + +* prep release 1.8.1 ([#647](https://github.com/aws-powertools/powertools-lambda-java/issues/647)) + + +<a name="v1.8.0"></a> +## [v1.8.0] - 2021-11-05 +## Bug Fixes + +* LoggingAspect precedence to be last for accepting mutated args ([#567](https://github.com/aws-powertools/powertools-lambda-java/issues/567)) + +## Features + +* create Java cfn-response equivalent ([#558](https://github.com/aws-powertools/powertools-lambda-java/issues/558)) ([#560](https://github.com/aws-powertools/powertools-lambda-java/issues/560)) + +## Maintenance + +* prep release 1.8.0 ([#608](https://github.com/aws-powertools/powertools-lambda-java/issues/608)) +* Fix failing build and auto merge only when build is success +* spotbug check ([#565](https://github.com/aws-powertools/powertools-lambda-java/issues/565)) +* Fix/Ignore spotbugs violations + + +<a name="v1.7.3"></a> +## [v1.7.3] - 2021-09-14 +## Bug Fixes + +* reset cold start only when placed on handler ([#508](https://github.com/aws-powertools/powertools-lambda-java/issues/508)) + +## Documentation + +* **batch-processing:** Support for moving non retryable msg to DLQ ([#531](https://github.com/aws-powertools/powertools-lambda-java/issues/531)) + +## Features + +* **batch-processing:** move non retry-able message to DLQ ([#500](https://github.com/aws-powertools/powertools-lambda-java/issues/500)) + +## Maintenance + +* prep release 1.7.3 + + +<a name="v1.7.2"></a> +## [v1.7.2] - 2021-08-03 +## Documentation + +* status badges + +## Maintenance + +* prep release 1.7.2 ([#490](https://github.com/aws-powertools/powertools-lambda-java/issues/490)) +* Upgrade to latest(1.14.0) aspectj-maven-plugin ([#489](https://github.com/aws-powertools/powertools-lambda-java/issues/489)) +* Logging and SQS utility Optimisations ([#484](https://github.com/aws-powertools/powertools-lambda-java/issues/484)) +* wait for merge until check is green +* wait for merge until check is green +* wait for spotbugs check before automerge + + +<a name="v1.7.1"></a> +## [v1.7.1] - 2021-07-06 +## Maintenance + +* prep release 1.7.1 ([#459](https://github.com/aws-powertools/powertools-lambda-java/issues/459)) + + +<a name="v1.7.0"></a> +## [v1.7.0] - 2021-07-05 +## Features + +* Clear logger state ([#453](https://github.com/aws-powertools/powertools-lambda-java/issues/453)) + +## Maintenance + +* prep release 1.7.0 ([#457](https://github.com/aws-powertools/powertools-lambda-java/issues/457)) + + +<a name="v1.6.0"></a> +## [v1.6.0] - 2021-06-20 +## Features + +* [#421](https://github.com/aws-powertools/powertools-lambda-java/issues/421) Support for Boolean and Number type as value in TracingUtils putAnnotation ([#423](https://github.com/aws-powertools/powertools-lambda-java/issues/423)) +* logger-Remove custom keys interface ([#395](https://github.com/aws-powertools/powertools-lambda-java/issues/395)) + +## Maintenance + +* prep release 1.6.0 ([#436](https://github.com/aws-powertools/powertools-lambda-java/issues/436)) +* setup-java v2 ([#353](https://github.com/aws-powertools/powertools-lambda-java/issues/353)) +* add JDK 16 to build matrix ([#367](https://github.com/aws-powertools/powertools-lambda-java/issues/367)) + + +<a name="v1.5.0"></a> +## [v1.5.0] - 2021-03-31 +## Features + +* remove deprecated attributes on Tracing annotation ([#330](https://github.com/aws-powertools/powertools-lambda-java/issues/330)) + +## Maintenance + +* prep release 1.5.0 ([#345](https://github.com/aws-powertools/powertools-lambda-java/issues/345)) +* rename automerge workflow name +* fix auto merge dependabot PR ([#342](https://github.com/aws-powertools/powertools-lambda-java/issues/342)) + + +<a name="v1.4.0"></a> +## [v1.4.0] - 2021-03-11 +## Bug Fixes + +* null check on dimension + +## Features + +* Default dimensions from powertools instead of emf library ([#317](https://github.com/aws-powertools/powertools-lambda-java/issues/317)) + +## Maintenance + +* prep release 1.4.0 ([#324](https://github.com/aws-powertools/powertools-lambda-java/issues/324)) + + +<a name="v1.3.0"></a> +## [v1.3.0] - 2021-03-05 +## Bug Fixes + +* powertools specific log level env var to not conflict with the system LOG_LEVEL ([#306](https://github.com/aws-powertools/powertools-lambda-java/issues/306)) +* **example:** Update the example to v1.2.0 ([#288](https://github.com/aws-powertools/powertools-lambda-java/issues/288)) + +## Documentation + +* ability to override object mapper used for logging event ([#303](https://github.com/aws-powertools/powertools-lambda-java/issues/303)) + +## Features + +* single metric utility method to pick default namespace ([#305](https://github.com/aws-powertools/powertools-lambda-java/issues/305)) +* ability to override object mapper used for logging event ([#302](https://github.com/aws-powertools/powertools-lambda-java/issues/302)) +* respect code guru profile handler implementation ([#304](https://github.com/aws-powertools/powertools-lambda-java/issues/304)) +* capture metrics even when handler results in exception ([#286](https://github.com/aws-powertools/powertools-lambda-java/issues/286)) + +## Maintenance + +* Prep release 1.3.0 ([#316](https://github.com/aws-powertools/powertools-lambda-java/issues/316)) +* migrate docs from gatsby to mkdocs ([#308](https://github.com/aws-powertools/powertools-lambda-java/issues/308)) +* consistent field names for trace and req id with logger ([#309](https://github.com/aws-powertools/powertools-lambda-java/issues/309)) + + +<a name="v1.2.0"></a> +## [v1.2.0] - 2021-01-13 +## Code Refactoring + +* replace Apache Commons Logging with SLF4J ([#212](https://github.com/aws-powertools/powertools-lambda-java/issues/212)) + +## Documentation + +* shadow sidebar to remain expanded ([#208](https://github.com/aws-powertools/powertools-lambda-java/issues/208)) + +## Features + +* support for env variable in tracing capture modes ([#249](https://github.com/aws-powertools/powertools-lambda-java/issues/249)) +* custom segment names ([#221](https://github.com/aws-powertools/powertools-lambda-java/issues/221)) + +## Maintenance + +* Prep release 1.2.0 ([#250](https://github.com/aws-powertools/powertools-lambda-java/issues/250)) +* Consistent env variable names for tracing ([#251](https://github.com/aws-powertools/powertools-lambda-java/issues/251)) +* update docs dependencies ([#214](https://github.com/aws-powertools/powertools-lambda-java/issues/214)) + + +<a name="v1.1.0"></a> +## [v1.1.0] - 2020-12-03 +## Documentation + +* add source code link in nav bar ([#199](https://github.com/aws-powertools/powertools-lambda-java/issues/199)) + +## Features + +* Parameters injection ([#201](https://github.com/aws-powertools/powertools-lambda-java/issues/201)) + +## Maintenance + +* Prep release 1.1.0 ([#205](https://github.com/aws-powertools/powertools-lambda-java/issues/205)) + + +<a name="v1.0.1"></a> +## [v1.0.1] - 2020-11-26 +## Bug Fixes + +* fixing dependencies security issues ([#169](https://github.com/aws-powertools/powertools-lambda-java/issues/169)) + +## Maintenance + +* Prep for release (1.0.1) ([#198](https://github.com/aws-powertools/powertools-lambda-java/issues/198)) +* upgrade AspectjGradlePlugin to latest and example project to java 11 ([#189](https://github.com/aws-powertools/powertools-lambda-java/issues/189)) + +## Performance Improvements + +* Make UrlConnectionHttpClient default client for params fetch ([#185](https://github.com/aws-powertools/powertools-lambda-java/issues/185)) + + +<a name="v1.0.0"></a> +## [v1.0.0] - 2020-11-04 +## Bug Fixes + +* **docs:** Correct url and not for gradle ([#158](https://github.com/aws-powertools/powertools-lambda-java/issues/158)) + +## Code Refactoring + +* Rename helpers for validation module ([#167](https://github.com/aws-powertools/powertools-lambda-java/issues/167)) +* Rename annotations for GA ([#165](https://github.com/aws-powertools/powertools-lambda-java/issues/165)) + + +<a name="v0.6.0-beta"></a> +## [v0.6.0-beta] - 2020-10-27 +## Documentation + +* Update all the environment variables used ([#127](https://github.com/aws-powertools/powertools-lambda-java/issues/127)) + +## Features + +* log aws request id ([#133](https://github.com/aws-powertools/powertools-lambda-java/issues/133)) + +## Maintenance + +* Prepare for 0.6.0-beta ([#155](https://github.com/aws-powertools/powertools-lambda-java/issues/155)) +* gradle example ([#147](https://github.com/aws-powertools/powertools-lambda-java/issues/147)) + + +<a name="v0.5.0-beta"></a> +## [v0.5.0-beta] - 2020-10-06 +## Features + +* SQS Partial batch Utility ([#120](https://github.com/aws-powertools/powertools-lambda-java/issues/120)) + +## Maintenance + +* Prepare for 0.5.0-beta ([#124](https://github.com/aws-powertools/powertools-lambda-java/issues/124)) + + +<a name="v0.4.0-beta"></a> +## [v0.4.0-beta] - 2020-10-02 +## Bug Fixes + +* Log event via object mapper and not depend on toString ([#113](https://github.com/aws-powertools/powertools-lambda-java/issues/113)) + +## Features + +* integration with CloudWatch ServiceLens [#88](https://github.com/aws-powertools/powertools-lambda-java/issues/88) ([#111](https://github.com/aws-powertools/powertools-lambda-java/issues/111)) + + +<a name="v0.3.1-beta"></a> +## [v0.3.1-beta] - 2020-09-25 +## Bug Fixes + +* Removing Log4J dependencies from the tracing module. ([#106](https://github.com/aws-powertools/powertools-lambda-java/issues/106)) +* Removing v1 Java SDK dependencies for X-Ray ([#105](https://github.com/aws-powertools/powertools-lambda-java/issues/105)) + +## Documentation + +* fixes to the documentation ([#102](https://github.com/aws-powertools/powertools-lambda-java/issues/102)) + + +<a name="v0.3.0-beta"></a> +## [v0.3.0-beta] - 2020-09-22 +## Features + +* Metrics utility ([#91](https://github.com/aws-powertools/powertools-lambda-java/issues/91)) + + +<a name="v0.2.0-beta"></a> +## [v0.2.0-beta] - 2020-09-01 +## Bug Fixes + +* **general:** clean up typos and code ([#62](https://github.com/aws-powertools/powertools-lambda-java/issues/62)) + +## Documentation + +* Readme update ([#72](https://github.com/aws-powertools/powertools-lambda-java/issues/72)) +* Update SQS large payload docs ([#60](https://github.com/aws-powertools/powertools-lambda-java/issues/60)) +* fixing documentation ([#59](https://github.com/aws-powertools/powertools-lambda-java/issues/59)) + +## Features + +* Utility without annotation ([#61](https://github.com/aws-powertools/powertools-lambda-java/issues/61)) +* SQS Large message handling ([#55](https://github.com/aws-powertools/powertools-lambda-java/issues/55)) + + +<a name="v0.1.0-beta"></a> +## v0.1.0-beta - 2020-08-31 +## Bug Fixes + +* Fixing security issues on package.json dependencies ([#22](https://github.com/aws-powertools/powertools-lambda-java/issues/22)) +* Fixing package versions for security purpose ([#16](https://github.com/aws-powertools/powertools-lambda-java/issues/16)) +* Rename Java ArtifactId and GroupId to be compliant with new AWS standard. +* docs pipeline +* Fix Readme.md documentation and remove unecessary parts. + +## Code Refactoring + +* consistent naming of powertools tracing and initial docs. ([#48](https://github.com/aws-powertools/powertools-lambda-java/issues/48)) +* consistent naming of powertools. ([#46](https://github.com/aws-powertools/powertools-lambda-java/issues/46)) +* Split Tracing and Logging packages Dependency split ([#45](https://github.com/aws-powertools/powertools-lambda-java/issues/45)) +* move groupId from software.aws.lambda to softwarte.amazon.lambda ([#23](https://github.com/aws-powertools/powertools-lambda-java/issues/23)) + +## Documentation + +* Adding license to each source file ([#44](https://github.com/aws-powertools/powertools-lambda-java/issues/44)) +* Initial javadocs for PowerLogger class. ([#43](https://github.com/aws-powertools/powertools-lambda-java/issues/43)) +* Implement Logger and Tracer part ([#27](https://github.com/aws-powertools/powertools-lambda-java/issues/27)) +* Initial javadocs for our logging annotation. ([#40](https://github.com/aws-powertools/powertools-lambda-java/issues/40)) +* update maven artifactId and groupId to new one +* Improving README file +* Init project documentation ci: init Github actions flow + +## Pull Requests + +* Merge pull request [#1](https://github.com/aws-powertools/powertools-lambda-java/issues/1) from stevehouel/master + + +[Unreleased]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.4.0...HEAD +[v2.4.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.3.0...v2.4.0 +[v2.3.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.2.1...v2.3.0 +[v2.2.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.2.0...v2.2.1 +[v2.2.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.1.1...v2.2.0 +[v2.1.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.1.0...v2.1.1 +[v2.1.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.0.0...v2.1.0 +[v2.0.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v2.0.0-RC1...v2.0.0 +[v2.0.0-RC1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.20.2...v2.0.0-RC1 +[v1.20.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.20.1...v1.20.2 +[v1.20.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.20.0...v1.20.1 +[v1.20.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.19.0...v1.20.0 +[v1.19.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.18.0...v1.19.0 +[v1.18.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.17.0...v1.18.0 +[v1.17.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.16.1...v1.17.0 +[v1.16.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.16.0...v1.16.1 +[v1.16.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.15.0...v1.16.0 +[v1.15.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.14.0...v1.15.0 +[v1.14.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.13.0...v1.14.0 +[v1.13.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.12.3...v1.13.0 +[v1.12.3]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.12.2...v1.12.3 +[v1.12.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.12.1...v1.12.2 +[v1.12.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.12.0...v1.12.1 +[v1.12.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.11.0...v1.12.0 +[v1.11.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.10.3...v1.11.0 +[v1.10.3]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.10.2...v1.10.3 +[v1.10.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.10.1...v1.10.2 +[v1.10.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.10.0...v1.10.1 +[v1.10.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.9.1...v1.10.0 +[v1.9.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.8.3...v1.9.1 +[v1.8.3]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.9.0...v1.8.3 +[v1.9.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.8.2...v1.9.0 +[v1.8.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.8.1...v1.8.2 +[v1.8.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.8.0...v1.8.1 +[v1.8.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.7.3...v1.8.0 +[v1.7.3]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.7.2...v1.7.3 +[v1.7.2]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.7.1...v1.7.2 +[v1.7.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.7.0...v1.7.1 +[v1.7.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.6.0...v1.7.0 +[v1.6.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.5.0...v1.6.0 +[v1.5.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.4.0...v1.5.0 +[v1.4.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.3.0...v1.4.0 +[v1.3.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.2.0...v1.3.0 +[v1.2.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.1.0...v1.2.0 +[v1.1.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.0.1...v1.1.0 +[v1.0.1]: https://github.com/aws-powertools/powertools-lambda-java/compare/v1.0.0...v1.0.1 +[v1.0.0]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.6.0-beta...v1.0.0 +[v0.6.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.5.0-beta...v0.6.0-beta +[v0.5.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.4.0-beta...v0.5.0-beta +[v0.4.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.3.1-beta...v0.4.0-beta +[v0.3.1-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.3.0-beta...v0.3.1-beta +[v0.3.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.2.0-beta...v0.3.0-beta +[v0.2.0-beta]: https://github.com/aws-powertools/powertools-lambda-java/compare/v0.1.0-beta...v0.2.0-beta diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 914e0741d..080643027 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,58 +1,130 @@ +<!-- markdownlint-disable MD043 MD041 --> +# Table of contents <!-- omit in toc --> + +- [Contributing Guidelines](#contributing-guidelines) + - [Reporting Bugs/Feature Requests](#reporting-bugsfeature-requests) + - [Contributing via Pull Requests](#contributing-via-pull-requests) + - [Dev setup](#dev-setup) + - [Local documentation](#local-documentation) + - [Conventions](#conventions) + - [General terminology and practices](#general-terminology-and-practices) + - [Testing definition](#testing-definition) + - [Finding contributions to work on](#finding-contributions-to-work-on) + - [Code of Conduct](#code-of-conduct) + - [Security issue notifications](#security-issue-notifications) + - [Troubleshooting](#troubleshooting) + - [Licensing](#licensing) + # Contributing Guidelines -Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional -documentation, we greatly value feedback and contributions from our community. +<!-- markdownlint-disable MD013 --> +Thank you for your interest in contributing to our project. Whether it's a [bug report](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=bug%2C+triage&projects=&template=bug_report.md&title=Bug%3A+TITLE), [new feature](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=feature-request%2C+triage&projects=&template=feature_request.md&title=Feature+request%3A+TITLE) or [additional documentation](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=documentation%2Ctriage&projects=&template=documentation_improvements.yml&title=Docs%3A+TITLE), we greatly value feedback and contributions from our community. +<!-- markdownlint-enable MD013 --> + +We encourage contributions from the community and we will work with contributors to merge their pull requests. +Rarely, we may close pull requests that do not meet our guidelines specified in CONTRIBUTING.md, or will require unreasonable effort to meet our quality bar. Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. - ## Reporting Bugs/Feature Requests -We welcome you to use the GitHub issue tracker to report bugs or suggest features. - -When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already -reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: - -* A reproducible test case or series of steps -* The version of our code being used -* Any modifications you've made relevant to the bug -* Anything unusual about your environment or deployment +We welcome you to use the GitHub issue tracker to report bugs, suggest features, or documentation improvements. +<!-- markdownlint-disable MD013 --> +[When filing an issue](https://github.com/aws-powertools/powertools-lambda-java/issues/new/choose), please check [existing open](https://github.com/aws-powertools/powertools-lambda-java/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc), or [recently closed](https://github.com/aws-powertools/powertools-lambda-java/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed) issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. +<!-- markdownlint-enable MD013 --> ## Contributing via Pull Requests + Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: -1. You are working against the latest source on the *master* branch. -2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. -3. You open an issue to discuss any significant work - we would hate for your time to be wasted. +1. You are working on a fork. [Fork the repository](https://github.com/aws-powertools/powertools-lambda-java/fork). +2. You are working against the latest source on the **main** branch. +3. You've checked existing open, and recently merged pull requests to make sure someone else hasn't addressed the problem already. +4. You've opened an [issue](https://github.com/aws-powertools/powertools-lambda-java/issues/new/choose) before you begin any implementation. We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful. + +### Dev setup + +We recommend using [IntelliJ IDEA](https://www.jetbrains.com/idea/) from JetBrains. +A community version is available and largely enough for our purpose. + +#### Code Formatting + +We strongly recommend installing the CheckStyle-IDEA plugin and apply the provided [checkstyle.xml](checkstyle.xml) in order to comply with our formatting rules: + +1. Install the [CheckStyle-IDEA plugin](https://plugins.jetbrains.com/plugin/1065-checkstyle-idea) and restart IntelliJ. + +2. After installing the plugin, open the preferences (`⌘,` on macOS, or `Ctrl+Alt+S` on Windows/Linux) and search for _Code Style_. Click on the gear icon near the scheme and import checkstyle configuration. Click on "Apply" and "OK". +![](docs/media/intellij_checkstyle_1.png) + +3. Select the code you've created (module, package, class) and reformat code: `⌘⌥L` (macOS), or `Ctrl+Alt+L` (Windows/Linux). + +4. Apply the reformat, optimize imports, rearrange and cleanup to your code and only to java files: +![](docs/media/intellij_checkstyle_3.png) + +#### License headers +All the java files should contain the licence/copyright header. You can copy paste it from the [license-header](license-header) file. -To send us a pull request, please: +### Creating the pull request -1. Fork the repository. -2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure local tests pass. -4. Commit to your fork using clear commit messages. -5. Send us a pull request, answering any default questions in the pull request interface. -6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. +To send us a pull request, please follow these steps: + +1. Create a new branch to focus on the specific change you are contributing e.g. `improv/logger-debug-sampling` +2. Run all tests, and code baseline checks: `mvn clean verify -P build-with-spotbugs` +3. Commit to your fork using clear commit messages. +4. Send us a pull request with a [conventional semantic title](.github/semantic.yml), and answering any default questions in the pull request interface. +5. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). +### Local documentation + +If you work on the documentation, you may find useful to display it locally while editing, using the following command: + +- **Docs website**: `make docs-local-docker` + +## Conventions + +### General terminology and practices + +| Category | Convention | +|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Style guide** | We use Checkstyle and Sonar to enforce beyond good practices. | +| **Exceptions** | Specific exceptions live within the utilities themselves and use `Exception` suffix e.g. `IdempotencyKeyException`. | +| **Git commits** | We follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). We do not enforce conventional commits on contributors to lower the entry bar. Instead, we enforce a conventional PR title so our label automation and changelog are generated correctly. | +| **API documentation** | API reference docs are generated from Javadoc which should have examples to allow developers to have what they need within their own IDE. Documentation website covers the wider usage, tips, and strive to be concise. | +| **Documentation** | We treat it like a product. We sub-divide content aimed at getting started (80% of customers) vs advanced usage (20%). We also ensure customers know how to unit test their code when using our features. | + +### Testing definition + +We group tests in different categories + +| Test | When to write | Notes | Speed | +| ----------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | +| Unit tests | Verify the smallest possible unit works. | Networking access is prohibited. Prefer Functional tests given our complexity. | Lightning fast (nsec to ms) | +| End-to-end tests | Gain confidence that a Lambda function with our code operates as expected. | It simulates how customers configure, deploy, and run their Lambda function - Event Source configuration, IAM permissions, etc. | Slow (minutes) | + +**NOTE**: Unit tests are mandatory. End-to-end tests should be created for new modules. +We apply the principle of the [test pyramid](https://martinfowler.com/articles/practical-test-pyramid.html), having a majority of unit tests to cover most cases (standard, edge, errors, ...) and generally one end-to-end test to verify the standard case in the target environment. ## Finding contributions to work on -Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/help wanted/invalid/question/documentation), [looking at any 'good first issue' issues is a great place to start](https://github.com/aws-powertools/powertools-lambda-java/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). ## Code of Conduct + This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact -opensource-codeofconduct@amazon.com with any additional questions or comments. - +<opensource-codeofconduct@amazon.com> with any additional questions or comments. ## Security issue notifications + If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. +## Troubleshooting + ## Licensing diff --git a/GraalVM.md b/GraalVM.md new file mode 100644 index 000000000..bbddb5e3b --- /dev/null +++ b/GraalVM.md @@ -0,0 +1,86 @@ +# GraalVM Compatibility for AWS Lambda Powertools Java + +## Table of Contents +- [Overview](#overview) +- [Prerequisites](#prerequisites) +- [General Implementation Steps](#general-implementation-steps) +- [Known Issues and Solutions](#known-issues-and-solutions) +- [Reference Implementation](#reference-implementation) + +## Overview +This documentation provides guidance for adding GraalVM support for AWS Lambda Powertools Java modules and using the modules in Lambda functions. + +## Prerequisites +- GraalVM 21+ installation +- Maven 3.x + +## General Implementation Steps +GraalVM native image compilation requires complete knowledge of an application's dynamic features at build time. The GraalVM reachability metadata (GRM) JSON files are essential because Java applications often use features that are determined at runtime, such as reflection, dynamic proxy classes, resource loading, and JNI (Java Native Interface). The metadata files tell GraalVM which classes need reflection access, which resources need to be included in the native image, and which proxy classes need to be generated. + +In order to generate the metadata reachability files for Powertools for Lambda, follow these general steps. + +1. **Add Maven Profiles** + - Add profile for generating GraalVM reachability metadata files. You can find an example of this in profile `generate-graalvm-files` of this [pom.xml](powertools-common/pom.xml). + - Add another profile for running the tests in the native image. You can find and example of this in profile `graalvm-native` of this [pom.xml](powertools-common/pom.xml). + +2. **Generate Reachability Metadata** + - Set the `JAVA_HOME` environment variable to use GraalVM + - Run tests with `-Pgenerate-graalvm-files` profile. +```shell +mvn -Pgenerate-graalvm-files clean test +``` + +3. **Validate Native Image Tests** + - Set the `JAVA_HOME` environment variable to use GraalVM + - Run tests with `-Pgraalvm-native` profile. This will build a GraalVM native image and run the JUnit tests. +```shell +mvn -Pgraalvm-native clean test +``` + +4. **Clean Up Metadata** + - GraalVM metadata reachability files generated in Step 2 contains references to the test scoped dependencies as well. + - Remove the references in generated metadata files for the following (and any other references to test scoped resources and classes): + - JUnit + - Mockito + - ByteBuddy + +## Known Issues and Solutions +1. **Mockito Compatibility** + - Powertools uses Mockito 5.x which uses “inline mock maker” as the default. This mock maker does not play well with GraalVM. Mockito [recommends](https://github.com/mockito/mockito/releases/tag/v5.0.0) using subclass mock maker with GraalVM. Therefore `generate-graalvm-files` profile uses subclass mock maker instead of inline mock maker. + - Subclass mock maker does not support testing static methods. Tests have therefore been modified to use [JUnit Pioneer](https://junit-pioneer.org/docs/environment-variables/) to inject the environment variables in the scope of the test's execution. + +2. **Log4j Compatibility** + - Version 2.22.1 fails with this error +``` +java.lang.InternalError: com.oracle.svm.core.jdk.UnsupportedFeatureError: Defining hidden classes at runtime is not supported. +``` + - This has been [fixed](https://github.com/apache/logging-log4j2/discussions/2364#discussioncomment-8950077) in Log4j 2.24.x. PT has been updated to use this version of Log4j + +3. **Test Class Organization** + - **Issue**: Anonymous inner classes and lambda expressions in Mockito matchers cause `NoSuchMethodError` in GraalVM native tests + - **Solution**: + - Extract static inner test classes to separate concrete classes in the same package as the class under test + - Replace lambda expressions in `ArgumentMatcher` with concrete implementations + - Use `mockito-subclass` dependency in GraalVM profiles + - **Example**: Replace `argThat(resp -> resp.getStatus() != expectedStatus)` with: + ```java + argThat(new ArgumentMatcher<Response>() { + @Override + public boolean matches(Response resp) { + return resp != null && resp.getStatus() != expectedStatus; + } + }) + ``` + +4. **Package Visibility Issues** + - **Issue**: Test handler classes cannot access package-private methods when placed in subpackages + - **Solution**: Place test handler classes in the same package as the class under test, not in subpackages like `handlers/` + - **Example**: Use `software.amazon.lambda.powertools.cloudformation` instead of `software.amazon.lambda.powertools.cloudformation.handlers` + +5. **Test Stubs Best Practice** + - **Best Practice**: Avoid mocking where possible and use concrete test stubs provided by `powertools-common` package + - **Solution**: Use `TestLambdaContext` and other test stubs from `powertools-common` test-jar instead of Mockito mocks + - **Implementation**: Add `powertools-common` test-jar dependency and replace `mock(Context.class)` with `new TestLambdaContext()` + +## Reference Implementation +Working example is available in the [examples](examples/powertools-examples-core-utilities/sam-graalvm). diff --git a/Makefile b/Makefile index 290fecdaa..8fa41a398 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,15 @@ - -dev-docs: - cd docs && yarn install - build-docs: @$(MAKE) build-docs-website -build-docs-website: dev-docs +build-docs-website: mkdir -p dist - cd docs && yarn build - cp -R docs/public/* dist/ + docker build -t squidfunk/mkdocs-material ./docs/ + docker run --rm -t -v ${PWD}:/docs squidfunk/mkdocs-material build + cp -R site/* dist/ -docs-local: - cd docs && yarn start +docs-local-docker: + docker build -t squidfunk/mkdocs-material ./docs/ + docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material test: mvn test diff --git a/Priming.md b/Priming.md new file mode 100644 index 000000000..d7597ad8c --- /dev/null +++ b/Priming.md @@ -0,0 +1,59 @@ +# Automatic Priming for AWS Lambda Powertools Java + +## Table of Contents +- [Overview](#overview) +- [Implementation Steps](#general-implementation-steps) +- [Known Issues](#known-issues-and-solutions) +- [Reference Implementation](#reference-implementation) + +## Overview +Priming is the process of preloading dependencies and initializing resources during the INIT phase, rather than during the INVOKE phase to further optimize startup performance with SnapStart. +This is required because Java frameworks that use dependency injection load classes into memory when these classes are explicitly invoked, which typically happens during Lambda’s INVOKE phase. + +This documentation provides guidance for automatic class priming in Powertools for AWS Lambda Java modules. + + +## Implementation Steps +Classes are proactively loaded using Java runtime hooks which are part of the open source [CRaC (Coordinated Restore at Checkpoint) project](https://openjdk.org/projects/crac/). +Implementations across the project use the `beforeCheckpoint()` hook, to prime Snapstart-enabled Java functions via Class Priming. +In order to generate the `classloaded.txt` file for a Java module in this project, follow these general steps. + +1. **Add Maven Profile** + - Add maven test profile with the following VM argument for generating classes loaded files. + ```shell + -Xlog:class+load=info:classesloaded.txt + ``` + - You can find an example of this in `generate-classesloaded-file` profile in this [pom.xml](powertools-metrics/pom.xml). + +2. **Generate classes loaded file** + - Run tests with `-Pgenerate-classesloaded-file` profile. + ```shell + mvn -Pgenerate-classesloaded-file clean test + ``` + - This will generate a file named `classesloaded.txt` in the target directory of the module. + +3. **Cleanup the file** + - The classes loaded file generated in Step 2 has the format + `[0.054s][info][class,load] java.lang.Object source: shared objects file` + but we are only interested in `java.lang.Object` - the fully qualified class name. + - To strip the lines to include only the fully qualified class name, + Use the following regex to replace with empty string. + - `^\[[\[\]0-9.a-z,]+ ` (to replace the left part) + - `( source: )[0-9a-z :/._$-]+` (to replace the right part) + +4. **Add file to resources** + - Move the cleaned-up file to the corresponding `src/main/resources` directory of the module. See [example](powertools-metrics/src/main/resources/classesloaded.txt). + +5. **Register and checkpoint** + - A class, usually the entry point of the module, should register the CRaC resource in the constructor. [Example](powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java) + - Note that AspectJ aspect is not suitable for this purpose, as it does not work with CRaC. + - Add the `beforeCheckpoint()` hook in the same class to invoke `ClassPreLoader.preloadClasses()`. The `ClassPreLoader` class is implemented in `powertools-common` module. + - This will ensure that the classes are already pre-loaded by the Snapstart RESTORE operation leading to a shorter INIT duration. + + +## Known Issues +- This is a manual process at the moment, but it can be automated in the future. +- `classesloaded.txt` file includes test classes as well because the file is generated while running tests. This is not a problem because all the classes that are not found are ignored by `ClassPreLoader.preloadClasses()`. Also `beforeCheckpoint()` hook is not time-sensitive, it only runs once when a new Lambda version gets published. + +## Reference Implementation +Working example is available in the [powertools-metrics](powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsFactory.java). diff --git a/README.md b/README.md index a3c5a5a0c..4c02e2d1f 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,60 @@ -# AWS Lambda Powertools (Java) +# Powertools for AWS Lambda (Java) -![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) +![aws provider](https://img.shields.io/badge/provider-AWS-orange?logo=amazon-aws&color=ff9900) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=aws-powertools_powertools-lambda-java&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=aws-powertools_powertools-lambda-java) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=aws-powertools_powertools-lambda-java&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=aws-powertools_powertools-lambda-java) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-java/badge)](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-java) ![Maven Central](https://img.shields.io/maven-central/v/software.amazon.lambda/powertools-parent) [![codecov.io](https://codecov.io/github/aws-powertools/powertools-lambda-java/branch/main/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/powertools-lambda-java) -A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. ([AWS Lambda Powertools Python](https://github.com/awslabs/aws-lambda-powertools-python) is also available). -**[📜Documentation](https://awslabs.github.io/aws-lambda-powertools-java/)** | **[Feature request](https://github.com/awslabs/aws-lambda-powertools-java/issues/new?assignees=&labels=feature-request%2C+triage&template=feature_request.md&title=)** | **[🐛Bug Report](https://github.com/awslabs/aws-lambda-powertools-java/issues/new?assignees=&labels=bug%2C+triage&template=bug_report.md&title=)** | **[Detailed blog post](https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-aws-lambda-powertools-java/)** +Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless best practices and increase developer velocity. -### Installation +> Also available in [Python](https://github.com/aws-powertools/powertools-lambda-python), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet). -Powertools is available in Maven Central. You can use your favourite dependency management tool to install it +**[📜Documentation](https://docs.powertools.aws.dev/lambda-java/latest)** | **[Feature request](https://github.com/aws-powertools/powertools-lambda-java/issues/new?template=feature_request.yml)** | **[🐛Bug Report](https://github.com/aws-powertools/powertools-lambda-java/issues/new?template=bug_report.yml)** | **[Detailed blog post](https://aws.amazon.com/blogs/compute/introducing-v2-of-powertools-for-aws-lambda-java/)** -* [maven](https://maven.apache.org/): +## Installation + +Powertools for AWS Lambda (Java) is available in Maven Central. You can use your favourite dependency management tool to install it + +### Maven: ```xml <dependencies> ... <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> - <version>1.1.0</version> + <version>2.9.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>1.1.0</version> + <artifactId>powertools-logging-log4j</artifactId> + <version>2.9.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> - <version>1.1.0</version> + <version>2.9.0</version> </dependency> ... </dependencies> ``` -And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project: +Next, configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project. +<details> + <summary><b>Maven</b></summary> + ```xml <build> <plugins> ... <plugin> - <groupId>org.codehaus.mojo</groupId> + <groupId>dev.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.11</version> + <version>1.14</version> <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> + <source>11</source> + <target>11</target> + <complianceLevel>11</complianceLevel> <aspectLibraries> <aspectLibrary> <groupId>software.amazon.lambda</groupId> @@ -62,6 +70,14 @@ And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambd </aspectLibrary> </aspectLibraries> </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> <executions> <execution> <goals> @@ -74,16 +90,97 @@ And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambd </plugins> </build> ``` -**Note:** If you are working with Lambda on runtime post java8, please refer [issue](https://github.com/awslabs/aws-lambda-powertools-java/issues/50) for workaround +</details> + +<details> +<summary><b>Gradle</b></summary> + +```groovy + + plugins { + id 'java' + id 'io.freefair.aspectj.post-compile-weaving' version '8.2.2' + } + + // the freefair aspect plugins targets gradle 8.2.1 + // https://docs.freefair.io/gradle-plugins/8.2.2/reference/ + wrapper { + gradleVersion = "8.2.1" + } + + repositories { + mavenCentral() + } + + dependencies { + aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' + implementation 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' + implementation "org.aspectj:aspectjrt:1.9.22" + } + + sourceCompatibility = 11 + targetCompatibility = 11 +``` +</details> + + +### Java Compatibility +Powertools for AWS Lambda (Java) supports all Java versions from 11 to 25 in line with the [corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). + +For the modules that provide annotations, Powertools for AWS Lambda (Java) leverages the **aspectj** library. +You may need to add the appropriate version of `aspectjrt` to your dependencies based on the JDK used for building your function: + +```xml +<dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>1.9.??</version> +</dependency> +``` + +<details> + <summary><b>JDK - aspectj dependency matrix</b></summary> + +Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/release/JavaVersionCompatibility.adoc) to understand which AspectJ version to use based on your JDK version: + +| JDK version | aspectj version | +|-------------|------------------------| +| `11-17` | `1.9.20.1` (or higher) | +| `21` | `1.9.21` (or higher) | +| `25` | `1.9.25` (or higher) | + +</details> + +## Examples + +See the latest release of the **[examples](https://github.com/aws-powertools/powertools-lambda-java/tree/main/examples)** for example projects showcasing usage of different utilities. + +Have a demo project to contribute which showcase usage of different utilities from powertools? We are happy to accept it [here](CONTRIBUTING.md#security-issue-notifications). + +## How to support Powertools for AWS Lambda (Java)? + +### Becoming a reference customer + +Knowing which companies are using this library is important to help prioritize the project internally. If your company is using Powertools for AWS Lambda (Java), you can request to have your name and logo added to the README file by raising a [Support Powertools for AWS Lambda (Java) (become a reference)](https://github.com/aws-powertools/powertools-lambda-java/issues/new?template=support_powertools.yml) issue. + +The following companies, among others, use Powertools: + +* [Capital One](https://www.capitalone.com/) +* [CPQi (Exadel Financial Services)](https://cpqi.com/) +* [Europace AG](https://europace.de/) +* [Vertex Pharmaceuticals](https://www.vrtx.com/) -## Example +## Connect -See **[example](./example/README.md)** for maven or gradle configurations including all features, and a SAM template with all Powertools env vars. +- **Powertools for AWS Lambda on Discord**: `#java` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: <aws-powertools-maintainers@amazon.com> -## Credits +## Security disclosures -* [Gatsby Apollo Theme for Docs](https://github.com/apollographql/gatsby-theme-apollo/tree/master/packages/gatsby-theme-apollo-docs) +If you think you’ve found a potential security issue, please do not post it in the Issues. Instead, please follow the instructions [here](https://aws.amazon.com/security/vulnerability-reporting/) or [email AWS security directly](mailto:aws-security@amazon.com). ## License -This library is licensed under the Apache License, Version 2.0. See the LICENSE file. +This library is licensed under the MIT-0 License. See the [LICENSE](https://github.com/aws-powertools/powertools-lambda-java/blob/main/LICENSE) file. diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 000000000..680d8808c --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,427 @@ +<?xml version="1.0"?> +<!DOCTYPE module PUBLIC + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> + +<!-- + Checkstyle configuration that checks the Google coding conventions from Google Java Style + that can be found at https://google.github.io/styleguide/javaguide.html + + Checkstyle is very configurable. Be sure to read the documentation at + http://checkstyle.org (or in your downloaded distribution). + + To completely disable a check, just comment it out or delete it from the file. + To suppress certain violations please review suppression filters. + + Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov. + --> + +<module name="Checker"> + <module name="SuppressWarningsFilter"/> + + <property name="charset" value="UTF-8"/> + + <property name="severity" value="warning"/> + + <property name="fileExtensions" value="java"/> + <!-- Excludes all 'module-info.java' files --> + <!-- See https://checkstyle.org/filefilters/index.html --> + <module name="BeforeExecutionExclusionFileFilter"> + <property name="fileNamePattern" value="module\-info\.java$"/> + </module> + <!-- https://checkstyle.org/filters/suppressionfilter.html --> + <module name="SuppressionFilter"> + <property name="file" value="${org.checkstyle.google.suppressionfilter.config}" + default="checkstyle-suppressions.xml" /> + <property name="optional" value="true"/> + </module> + + <!-- Checks for whitespace --> + <!-- See http://checkstyle.org/checks/whitespace/index.html --> + <module name="FileTabCharacter"> + <property name="eachLine" value="true"/> + </module> + + <module name="LineLength"> + <property name="fileExtensions" value="java"/> + <property name="max" value="120"/> + <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/> + </module> + + <module name="FileLength"/> + + <module name="Header"> + <property name="headerFile" value="${basedir}/license-header"/> + <property name="severity" value="error"/> + </module> + + <module name="TreeWalker"> + <module name="OuterTypeFilename"/> + <module name="IllegalTokenText"> + <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/> + <property name="format" + value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/> + <property name="message" + value="Consider using special escape sequence instead of octal value or Unicode escaped value."/> + </module> + <module name="AvoidEscapedUnicodeCharacters"> + <property name="allowEscapesForControlCharacters" value="true"/> + <property name="allowByTailComment" value="true"/> + <property name="allowNonPrintableEscapes" value="true"/> + </module> + <module name="AvoidStarImport"> + <property name="severity" value="error"/> + </module> + <module name="OneTopLevelClass"> + <property name="severity" value="error"/> + </module> + <module name="NoLineWrap"> + <property name="severity" value="info"/> + <property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/> + </module> + <module name="EmptyBlock"> + <property name="option" value="TEXT"/> + <property name="tokens" + value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/> + </module> + <module name="EqualsAvoidNull"> + <property name="severity" value="error"/> + </module> + <module name="EqualsHashCode"> + <property name="severity" value="error"/> + </module> + <module name="NeedBraces"> + <property name="tokens" + value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/> + </module> + <module name="LeftCurly"> + <property name="tokens" + value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF, + INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT, + LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, + LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF, + OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/> + </module> + <module name="RightCurly"> + <property name="id" value="RightCurlySame"/> + <property name="tokens" + value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, + LITERAL_DO"/> + </module> + <module name="RightCurly"> + <property name="id" value="RightCurlyAlone"/> + <property name="option" value="alone"/> + <property name="tokens" + value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, + INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF, + COMPACT_CTOR_DEF, LITERAL_SWITCH"/> + </module> + <module name="SuppressionXpathSingleFilter"> + <!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 --> + <property name="id" value="RightCurlyAlone"/> + <property name="query" value="//RCURLY[parent::SLIST[count(./*)=1] + or preceding-sibling::*[last()][self::LCURLY]]"/> + </module> + <module name="WhitespaceAfter"> + <property name="tokens" + value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_RETURN, + LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, LITERAL_FINALLY, DO_WHILE, ELLIPSIS, + LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_CATCH, LAMBDA, + LITERAL_YIELD, LITERAL_CASE"/> + </module> + <module name="WhitespaceAround"> + <property name="allowEmptyConstructors" value="true"/> + <property name="allowEmptyLambdas" value="true"/> + <property name="allowEmptyMethods" value="true"/> + <property name="allowEmptyTypes" value="true"/> + <property name="allowEmptyLoops" value="true"/> + <property name="ignoreEnhancedForColon" value="false"/> + <property name="tokens" + value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, + BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND, + LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, + LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, + LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, + NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR, + SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/> + <message key="ws.notFollowed" + value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks + may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/> + <message key="ws.notPreceded" + value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/> + </module> + <module name="OneStatementPerLine"/> + <module name="MultipleVariableDeclarations"/> + <module name="ArrayTypeStyle"/> + <module name="MissingSwitchDefault"/> + <module name="FallThrough"> + <property name="severity" value="error"/> + </module> + <module name="UpperEll"/> + <module name="ModifierOrder"/> + <module name="EmptyLineSeparator"> + <property name="tokens" + value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF, + STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF, + COMPACT_CTOR_DEF"/> + <property name="allowNoEmptyLineBetweenFields" value="true"/> + </module> + <module name="SeparatorWrap"> + <property name="id" value="SeparatorWrapDot"/> + <property name="tokens" value="DOT"/> + <property name="option" value="nl"/> + </module> + <module name="SeparatorWrap"> + <property name="id" value="SeparatorWrapComma"/> + <property name="tokens" value="COMMA"/> + <property name="option" value="EOL"/> + </module> + <module name="SeparatorWrap"> + <!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 --> + <property name="id" value="SeparatorWrapEllipsis"/> + <property name="tokens" value="ELLIPSIS"/> + <property name="option" value="EOL"/> + </module> + <module name="SeparatorWrap"> + <!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 --> + <property name="id" value="SeparatorWrapArrayDeclarator"/> + <property name="tokens" value="ARRAY_DECLARATOR"/> + <property name="option" value="EOL"/> + </module> + <module name="SeparatorWrap"> + <property name="id" value="SeparatorWrapMethodRef"/> + <property name="tokens" value="METHOD_REF"/> + <property name="option" value="nl"/> + </module> + <module name="PackageName"> + <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/> + <message key="name.invalidPattern" + value="Package name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="TypeName"> + <property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, + ANNOTATION_DEF, RECORD_DEF"/> + <message key="name.invalidPattern" + value="Type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="MemberName"> + <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/> + <message key="name.invalidPattern" + value="Member name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="ParameterName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Parameter name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="LambdaParameterName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="CatchParameterName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Catch parameter name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="LocalVariableName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Local variable name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="PatternVariableName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Pattern variable name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="ClassTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Class type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="RecordComponentName"> + <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> + <message key="name.invalidPattern" + value="Record component name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="RecordTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Record type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="MethodTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Method type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="InterfaceTypeParameterName"> + <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/> + <message key="name.invalidPattern" + value="Interface type name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="NoFinalizer"/> + <module name="GenericWhitespace"> + <message key="ws.followed" + value="GenericWhitespace ''{0}'' is followed by whitespace."/> + <message key="ws.preceded" + value="GenericWhitespace ''{0}'' is preceded with whitespace."/> + <message key="ws.illegalFollow" + value="GenericWhitespace ''{0}'' should followed by whitespace."/> + <message key="ws.notPreceded" + value="GenericWhitespace ''{0}'' is not preceded with whitespace."/> + </module> + <module name="Indentation"> + <property name="basicOffset" value="4"/> + <property name="braceAdjustment" value="4"/> + <property name="caseIndent" value="4"/> + <property name="throwsIndent" value="8"/> + <property name="lineWrappingIndentation" value="8"/> + <property name="arrayInitIndent" value="4"/> + </module> + <module name="AbbreviationAsWordInName"> + <property name="ignoreFinal" value="false"/> + <property name="allowedAbbreviationLength" value="4"/> + <property name="tokens" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF, + PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF, + RECORD_COMPONENT_DEF"/> + </module> + <module name="NoWhitespaceBeforeCaseDefaultColon"/> + <module name="OverloadMethodsDeclarationOrder"/> + <module name="VariableDeclarationUsageDistance"/> + <module name="CustomImportOrder"> + <property name="sortImportsInGroupAlphabetically" value="true"/> + <property name="separateLineBetweenGroups" value="true"/> + <property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/> + <property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/> + </module> + <module name="MethodParamPad"> + <property name="tokens" + value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF, + SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/> + </module> + <module name="NoWhitespaceBefore"> + <property name="tokens" + value="COMMA, SEMI, POST_INC, POST_DEC, DOT, + LABELED_STAT, METHOD_REF"/> + <property name="allowLineBreaks" value="true"/> + </module> + <module name="ParenPad"> + <property name="tokens" + value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF, + EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW, + LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL, + METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA, + RECORD_DEF"/> + </module> + <module name="OperatorWrap"> + <property name="option" value="NL"/> + <property name="tokens" + value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, + LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF, + TYPE_EXTENSION_AND "/> + </module> + <module name="AnnotationLocation"> + <property name="id" value="AnnotationLocationMostCases"/> + <property name="tokens" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, + RECORD_DEF, COMPACT_CTOR_DEF"/> + </module> + <module name="AnnotationLocation"> + <property name="id" value="AnnotationLocationVariables"/> + <property name="tokens" value="VARIABLE_DEF"/> + <property name="allowSamelineMultipleAnnotations" value="true"/> + </module> + <module name="NonEmptyAtclauseDescription"> + <property name="severity" value="info"/> + </module> + <module name="InvalidJavadocPosition"> + <property name="severity" value="info"/> + </module> + <module name="JavadocTagContinuationIndentation"> + <property name="severity" value="ignore"/> + </module> + <module name="SummaryJavadoc"> + <property name="severity" value="ignore"/> + <property name="forbiddenSummaryFragments" + value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/> + </module> + <module name="JavadocParagraph"> + <property name="severity" value="ignore"/> + </module> + <module name="RequireEmptyLineBeforeBlockTagGroup"/> + <module name="AtclauseOrder"> + <property name="tagOrder" value="@param, @return, @throws, @deprecated"/> + <property name="target" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/> + </module> + <module name="JavadocMethod"> + <property name="severity" value="info"/> + <property name="accessModifiers" value="public"/> + <property name="allowMissingParamTags" value="true"/> + <property name="allowMissingReturnTag" value="true"/> + <property name="allowedAnnotations" value="Override, Test"/> + <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/> + </module> + <module name="MissingJavadocMethod"> + <property name="severity" value="info"/> + <property name="scope" value="public"/> + <property name="minLineCount" value="1"/> + <property name="allowedAnnotations" value="Override, Test"/> + <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, + COMPACT_CTOR_DEF"/> + </module> + <module name="MissingJavadocType"> + <property name="severity" value="info"/> + <property name="scope" value="protected"/> + <property name="tokens" + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, + RECORD_DEF, ANNOTATION_DEF"/> + <property name="excludeScope" value="nothing"/> + </module> + <module name="MethodName"> + <property name="format" value="^[a-z][a-z0-9]\w*$"/> + <message key="name.invalidPattern" + value="Method name ''{0}'' must match pattern ''{1}''."/> + </module> + <module name="SingleLineJavadoc"> + <property name="severity" value="ignore"/> + </module> + <module name="EmptyCatchBlock"> + <property name="exceptionVariableName" value="expected"/> + </module> + <module name="UnusedImports"> + <property name="severity" value="error"/> + </module> + <module name="UnusedLocalVariable"> + <property name="severity" value="error"/> + </module> + <module name="CommentsIndentation"> + <property name="severity" value="info"/> + <property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/> + </module> + <module name="CyclomaticComplexity" /> + <module name="DefaultComesLast"> + <property name="severity" value="error"/> + </module> + <!-- https://checkstyle.org/filters/suppressionxpathfilter.html --> + <module name="SuppressionXpathFilter"> + <property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}" + default="checkstyle-xpath-suppressions.xml" /> + <property name="optional" value="true"/> + </module> + <module name="SuppressWarningsHolder" /> + <module name="SuppressionCommentFilter"> + <property name="offCommentFormat" value="CHECKSTYLE.OFF\: ([\w\|]+)" /> + <property name="onCommentFormat" value="CHECKSTYLE.ON\: ([\w\|]+)" /> + <property name="checkFormat" value="$1" /> + </module> + <module name="SuppressWithNearbyCommentFilter"> + <property name="commentFormat" value="CHECKSTYLE.SUPPRESS\: ([\w\|]+)"/> + <!-- $1 refers to the first match group in the regex defined in commentFormat --> + <property name="checkFormat" value="$1"/> + <!-- The check is suppressed in the next line of code after the comment --> + <property name="influenceFormat" value="1"/> + </module> + </module> +</module> diff --git a/docs/.prettierignore b/docs/.prettierignore deleted file mode 100644 index 58d06c368..000000000 --- a/docs/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -.cache -package.json -package-lock.json -public diff --git a/docs/.prettierrc b/docs/.prettierrc deleted file mode 100644 index 33d2cfa3f..000000000 --- a/docs/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "arrowParens": "avoid", - "semi": false -} diff --git a/docs/Dockerfile b/docs/Dockerfile new file mode 100644 index 000000000..56c4c19ea --- /dev/null +++ b/docs/Dockerfile @@ -0,0 +1,4 @@ +FROM squidfunk/mkdocs-material@sha256:980e11fed03b8e7851e579be9f34b1210f516c9f0b4da1a1457f21a460bd6628 + +COPY requirements.txt /tmp/ +RUN pip install --require-hashes -r /tmp/requirements.txt diff --git a/docs/FAQs.md b/docs/FAQs.md new file mode 100644 index 000000000..cea4b774f --- /dev/null +++ b/docs/FAQs.md @@ -0,0 +1,261 @@ +--- +title: FAQs +description: Frequently Asked Questions +--- + +## How can I use Powertools for AWS Lambda (Java) with Lombok? + +Many utilities in this library use `aspectj-maven-plugin` to compile-time weave (CTW) aspects into the project. In case you want to use `Lombok` or other compile-time preprocessor for your project, it is required to change `aspectj-maven-plugin` configuration to enable in-place weaving feature. Otherwise the plugin will ignore changes introduced by `Lombok` and will use `.java` files as a source. + +Alternatively, you can use the [functional approach](./usage-patterns.md#functional-approach) which does not require AspectJ configuration. + +To enable in-place weaving feature you need to use following `aspectj-maven-plugin` configuration: + +```xml hl_lines="2-6" +<configuration> + <forceAjcCompile>true</forceAjcCompile> + <sources/> + <weaveDirectories> + <weaveDirectory>${project.build.directory}/classes</weaveDirectory> + </weaveDirectories> + ... + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> +</configuration> +``` + +## How can I use Powertools for AWS Lambda (Java) with Kotlin projects? + +Many utilities use `aspectj-maven-plugin` to compile-time weave (CTW) aspects into the project. When using it with Kotlin projects, it is required to `forceAjcCompile`. +No explicit configuration should be required for gradle projects. + +Alternatively, you can use the [functional approach](./usage-patterns.md#functional-approach) which does not require AspectJ configuration. + +To enable `forceAjcCompile` you need to use following `aspectj-maven-plugin` configuration: + +```xml hl_lines="2" +<configuration> + <forceAjcCompile>true</forceAjcCompile> + ... + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> +</configuration> +``` + +## How can I use Powertools for AWS Lambda (Java) with the AWS CRT HTTP Client? + +Utilities relying on AWS SDK clients use the `url-connection-client` as the default HTTP client. The `url-connection-client` is a lightweight HTTP client, which keeps the impact on Lambda cold starts to a minimum. +With the [announcement](https://aws.amazon.com/blogs/developer/announcing-availability-of-the-aws-crt-http-client-in-the-aws-sdk-for-java-2-x/) of the `aws-crt-client` a new HTTP client has been released, which offers faster SDK startup time and smaller memory footprint. + +Unfortunately, replacing the `url-connection-client` dependency with the `aws-crt-client` will not immediately improve the lambda cold start performance and memory footprint, +as the default version of the dependency contains native system libraries for all supported runtimes and architectures (Linux, MacOS, Windows, AMD64, ARM64, etc). This makes the CRT client portable, without the user having to consider _where_ their code will run, but comes at the cost of JAR size. + +### Configuring dependencies + +Using the `aws-crt-client` in your project requires the exclusion of the `url-connection-client` transitive dependency from the `powertools-*` dependency. + +```xml +<dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-parameters</artifactId> + <version>2.0.0</version> + <exclusions> + <exclusion> + <groupId>software.amazon.awssdk</groupId> + <artifactId>url-connection-client</artifactId> + </exclusion> + </exclusions> +</dependency> +``` + +Next, add the `aws-crt-client` and exclude the "generic" `aws-crt` dependency (contains all runtime libraries). +Instead, set a specific classifier of the `aws-crt` to use the one for your target runtime: either `linux-x86_64` for a Lambda configured for x86 or `linux-aarch_64` for Lambda using arm64. + +!!! note "You will need to add a separate maven profile to build and debug locally when your development environment does not share the target architecture you are using in Lambda." +By specifying the specific target runtime, we prevent other target runtimes from being included in the jar file, resulting in a smaller Lambda package and improved cold start times. + +```xml +<dependencies> + <dependency> + <groupId>software.amazon.awssdk</groupId> + <artifactId>aws-crt-client</artifactId> + <version>2.23.21</version> + <exclusions> + <exclusion> + <groupId>software.amazon.awssdk.crt</groupId> + <artifactId>aws-crt</artifactId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>software.amazon.awssdk.crt</groupId> + <artifactId>aws-crt</artifactId> + <version>0.29.9</version> + <classifier>linux-x86_64</classifier> + </dependency> +</dependencies> +``` + +### Explicitly set the AWS CRT HTTP Client + +After configuring the dependencies, it's required to explicitly specify the AWS SDK HTTP client. +Depending on the utility you are using, there is a different way to configure the SDK client. + +The following example shows how to use the Parameters module while leveraging the AWS CRT Client. + +```java hl_lines="16 23-24" +import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.awssdk.services.ssm.SsmClient; +import software.amazon.awssdk.http.crt.AwsCrtHttpClient; +import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; + +public class RequestHandlerWithParams implements RequestHandler<String, String> { + + // Get an instance of the SSMProvider with a custom HTTP client (aws crt). + SSMProvider ssmProvider = SSMProvider + .builder() + .withClient( + SsmClient.builder() + .httpClient(AwsCrtHttpClient.builder().build()) + .build() + ) + .build(); + + public String handleRequest(String input, Context context) { + // Retrieve a single param + String value = ssmProvider + .get("/my/secret"); + // We might instead want to retrieve multiple parameters at once, returning a Map of key/value pairs + // .getMultiple("/my/secret/path"); + + // Return the result + return value; + } +} +``` + +The `aws-crt-client` was considered for adoption as the default HTTP client in Powertools for AWS Lambda (Java) as mentioned in [Move SDK http client to CRT](https://github.com/aws-powertools/powertools-lambda-java/issues/1092), +but due to the impact on the developer experience it was decided to stick with the `url-connection-client`. + +## How can I use Powertools for AWS Lambda (Java) with GraalVM? + +Core utilities, i.e. [logging](./core/logging.md), [metrics](./core/metrics.md) and [tracing](./core/tracing.md), include the [GraalVM Reachability Metadata (GRM)](https://www.graalvm.org/latest/reference-manual/native-image/metadata/) in the `META-INF` directories of the respective JARs. You can find a working example of Serverless Application Model (SAM) based application in the [examples](../examples/powertools-examples-core-utilities/sam-graalvm/README.md) directory. + +Below, you find typical steps you need to follow in a Maven based Java project: + +### Set the environment to use GraalVM + +```shell +export JAVA_HOME=<path to GraalVM> +``` + +### Use log4j `>2.24.0` + +Log4j version `2.24.0` adds [support for GraalVM](https://github.com/apache/logging-log4j2/issues/1539#issuecomment-2106766878). Depending on your project's dependency hierarchy, older version of log4j might be included in the final dependency graph. Make sure version `>2.24.0` of these dependencies are used by your Maven project: + +```xml +<dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + <version>${log4j.version}</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-layout-template-json</artifactId> + <version>${log4j.version}</version> + </dependency> +</dependencies> + +``` + +### Add the AWS Lambda Java Runtime Interface Client dependency + +The Runtime Interface Client allows your function to receive invocation events from Lambda, send the response back to Lambda, and report errors to the Lambda service. Add the below dependency to your Maven project: + +```xml +<dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-lambda-java-runtime-interface-client</artifactId> + <version>2.1.1</version> +</dependency> +``` + +Also include the AWS Lambda GRM files by copying the `com.amazonaws` [directory](../examples/powertools-examples-core-utilities/sam-graalvm/src/main/resources/META-INF/native-image/) in your project's `META-INF/native-image` directory + +### Build the native image + +Use the `native-maven-plugin` to build the native image. You can do this by adding the plugin to your `pom.xml` and creating a build profile called `native-image` that can build the native image of your Lambda function: + +```xml +<profiles> + <profile> + <id>native-image</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>0.10.1</version> + <extensions>true</extensions> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>build</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + <configuration> + <imageName>your-project-name</imageName> + <mainClass>com.amazonaws.services.lambda.runtime.api.client.AWSLambda</mainClass> + <buildArgs> + <!-- required for AWS Lambda Runtime Interface Client --> + <arg>--enable-url-protocols=http</arg> + <arg>--add-opens java.base/java.util=ALL-UNNAMED</arg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> +</profiles> +``` + +Create a Docker image using a `Dockerfile` like [this](../examples/powertools-examples-core-utilities/sam-graalvm/Dockerfile) to create an x86 based build image. + +```shell +docker build --platform linux/amd64 . -t your-org/your-app-graalvm-builder +``` + +Create the native image of you Lambda function using the Docker command below. + +```shell +docker run --platform linux/amd64 -it -v `pwd`:`pwd` -w `pwd` -v ~/.m2:/root/.m2 your-org/your-app-graalvm-builder mvn clean -Pnative-image package + +``` + +The native image is created in the `target/` directory. diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 000000000..c2705ba58 --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1,2 @@ +[comment]: <> (Includes Changelog content entire file as a snippet) +--8<-- "CHANGELOG.md" diff --git a/docs/content/core/logging.mdx b/docs/content/core/logging.mdx deleted file mode 100644 index 6d6591f9d..000000000 --- a/docs/content/core/logging.mdx +++ /dev/null @@ -1,210 +0,0 @@ ---- -title: Logging -description: Core utility ---- - -import Note from "../../src/components/Note" - -Logger provides an opinionated logger with output structured as JSON. - -**Key features** - -* Capture key fields from Lambda context, cold start and structures logging output as JSON -* Log Lambda event when instructed (disabled by default) - - Enable explicitly via annotation param -* Append additional keys to structured log at any point in time - -## Initialization - -Powertools extends the functionality of Log4J. Below is an example log4j2.xml file, with the LambdaJsonLayout configured. - -```xml -<?xml version="1.0" encoding="UTF-8"?> -<Configuration packages="com.amazonaws.services.lambda.runtime.log4j2"> - <Appenders> - <Console name="JsonAppender" target="SYSTEM_OUT"> - <LambdaJsonLayout compact="true" eventEol="true"/> - </Console> - </Appenders> - <Loggers> - <Logger name="JsonLogger" level="INFO" additivity="false"> - <AppenderRef ref="JsonAppender"/> - </Logger> - <Root level="info"> - <AppenderRef ref="JsonAppender"/> - </Root> - </Loggers> -</Configuration> -``` - -You can also override log level by setting `LOG_LEVEL` env var - Here is an example using AWS Serverless Application Model (SAM) - -```yaml:title=template.yaml -Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - ... - Runtime: java8 - Environment: - Variables: - LOG_LEVEL: DEBUG # highlight-line -``` - -You can also explicitly set a service name via `POWERTOOLS_SERVICE_NAME` env var. This sets **service** key that will be present across all log statements. - -## Standard structured keys - -Your Logger will always include the following keys to your structured logging: - -Key | Type | Example | Description -------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------- -**timestamp** | String | "2020-05-24 18:17:33,774" | Timestamp of actual log statement -**level** | String | "INFO" | Logging level -**coldStart** | Boolean | true| ColdStart value. -**service** | String | "payment" | Service name defined. "service_undefined" will be used if unknown -**samplingRate** | int | 0.1 | Debug logging sampling rate in percentage e.g. 10% in this case -**message** | String | "Collecting payment" | Log statement value. Unserializable JSON values will be casted to string -**functionName**| String | "example-powertools-HelloWorldFunction-1P1Z6B39FLU73" -**functionVersion**| String | "12" -**functionMemorySize**| String | "128" -**functionArn**| String | "arn:aws:lambda:eu-west-1:012345678910:function:example-powertools-HelloWorldFunction-1P1Z6B39FLU73" -**xray_trace_id**| String | "1-5759e988-bd862e3fe1be46a994272793" | X-Ray Trace ID when Lambda function has enabled Tracing -**function_request_id**| String | "899856cb-83d1-40d7-8611-9e78f15f32f4"" | AWS Request ID from lambda context - -## Capturing context Lambda info - -You can enrich your structured logs with key Lambda context information via `logEvent` annotation parameter. - -```java:title=App.java -package helloworld; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.logging.LoggingUtils; -import software.amazon.lambda.logging.Logging; -... - -/** - * Handler for requests to Lambda function. - */ -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - Logger log = LogManager.getLogger(); - - @Logging - public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... - } -} -``` - -You can also explicitly log any incoming event using `logEvent` param. - -<Note type="warning"> - This is disabled by default to prevent sensitive info being logged. -</Note><br/> - -```java:title=App.java -package helloworld; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.logging.LoggingUtils; -import software.amazon.lambda.logging.Logging; -... - -/** - * Handler for requests to Lambda function. - */ -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - Logger log = LogManager.getLogger(); - - @Logging(logEvent = true) - public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... - } -} -``` - -## Appending additional keys - -You can append your own keys to your existing Logger via `appendKey`. - -```java:title=App.java -package helloworld; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.logging.LoggingUtils; -import software.amazon.lambda.logging.Logging; -... - -/** - * Handler for requests to Lambda function. - */ -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - Logger log = LogManager.getLogger(); - - @Logging(logEvent = true) - public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... - LoggingUtils.appendKey("test", "willBeLogged"); - ... - - ... - Map<String, String> customKeys = new HashMap<>(); - customKeys.put("test", "value"); - customKeys.put("test1", "value1"); - - LoggingUtils.appendKeys(customKeys); - ... - } -} -``` - -## Sampling debug logs - -You can dynamically set a percentage of your logs to **DEBUG** level via env var `POWERTOOLS_LOGGER_SAMPLE_RATE` or -via `samplingRate` attribute on annotation. - -Configuration on environment variable is given precedence over sampling rate configuration on annotation, provided it's in valid value range. - -```java:title=App.java -package helloworld; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.logging.LoggingUtils; -import software.amazon.lambda.logging.Logging; -... - -/** - * Handler for requests to Lambda function. - */ -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - Logger log = LogManager.getLogger(); - - @Logging(samplingRate = 0.5) - public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { - ... - } -} -``` - -You can also override sampling by setting `POWERTOOLS_LOGGER_SAMPLE_RATE` env var - Here is an example using AWS Serverless Application Model (SAM) - -```yaml:title=template.yaml -Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - ... - Runtime: java8 - Environment: - Variables: - POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 # highlight-line -``` \ No newline at end of file diff --git a/docs/content/core/metrics.mdx b/docs/content/core/metrics.mdx deleted file mode 100644 index e0ad09e56..000000000 --- a/docs/content/core/metrics.mdx +++ /dev/null @@ -1,156 +0,0 @@ ---- -title: Metrics -description: Core utility ---- - -Metrics creates custom metrics asynchronously by logging metrics to standard output following Amazon CloudWatch Embedded Metric Format (EMF). - -These metrics can be visualized through [Amazon CloudWatch Console](https://console.aws.amazon.com/cloudwatch/). - -**Key features** - -* Aggregate up to 100 metrics using a single CloudWatch EMF object (large JSON blob) -* Validate against common metric definitions mistakes (metric unit, values, max dimensions, max metrics, etc) -* Metrics are created asynchronously by the CloudWatch service, no custom stacks needed -* Context manager to create a one off metric with a different dimension - -## Initialization - -Set `POWERTOOLS_SERVICE_NAME` and `POWERTOOLS_METRICS_NAMESPACE` env vars as a start - Here is an example using AWS Serverless Application Model (SAM) - -```yaml:title=template.yaml -Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - ... - Runtime: java8 - Environment: - Variables: - POWERTOOLS_SERVICE_NAME: payment # highlight-line - POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline # highlight-line -``` - -We recommend you use your application or main service as a metric namespace. -You can explicitly set a namespace name an annotation variable `namespace` param or via `POWERTOOLS_METRICS_NAMESPACE` env var. - -This sets **namespace** key that will be used for all metrics. -You can also pass a service name via `service` param or `POWERTOOLS_SERVICE_NAME` env var. This will create a dimension with the service name. - -```java:title=Handler.java -package example; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestHandler; -import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; -import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.metrics.Metrics; -import software.amazon.lambda.powertools.metrics.MetricsUtils; - -public class MetricsEnabledHandler implements RequestHandler<Object, Object> { - - MetricsLogger metricsLogger = MetricsUtils.metricsLogger(); - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking") - public Object handleRequest(Object input, Context context) { - ... - } -} -``` - -You can initialize Metrics anywhere in your code as many times as you need - It'll keep track of your aggregate metrics in memory. - -## Creating metrics - -You can create metrics using `putMetric`, and manually create dimensions for all your aggregate metrics using `add_dimension`. - -```java:title=Handler.java -public class MetricsEnabledHandler implements RequestHandler<Object, Object> { - - MetricsLogger metricsLogger = MetricsUtils.metricsLogger(); - - @Override - @Metrics(namespace = "ExampleApplication", service = "booking") - public Object handleRequest(Object input, Context context) { - // highlight-start - metricsLogger.putDimensions(DimensionSet.of("environment", "prod")); - metricsLogger.putMetric("SuccessfulBooking", 1, Unit.COUNT); - // highlight-end - ... - } -} -``` - -The `Unit` enum facilitate finding a supported metric unit by CloudWatch. - -CloudWatch EMF supports a max of 100 metrics. Metrics utility will flush all metrics when adding the 100th metric while subsequent metrics will be aggregated into a new EMF object, for your convenience. - -## Creating a metric with a different dimension - -CloudWatch EMF uses the same dimensions across all your metrics. Use `single_metric` if you have a metric that should have different dimensions. - -<Note type="info"> - Generally, this would be an edge case since you <a href="https://aws.amazon.com/cloudwatch/pricing/">pay for unique metric</a>. Keep the following formula in mind: - <br/><br/> - <strong>unique metric = (metric_name + dimension_name + dimension_value)</strong> -</Note><br/> - -```java:title=Handler.java -withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); -}); -``` - -## Adding metadata - -You can use `putMetadata` for advanced use cases, where you want to metadata as part of the serialized metrics object. - -<Note type="info"> - <strong>This will not be available during metrics visualization</strong> - Use <strong>dimensions</strong> for this purpose -</Note><br/> - -```java:title=Handler.java -@Metrics(namespace = "ServerlessAirline", service = "payment") -public APIGatewayProxyResponseEvent handleRequest(Object input, Context context) { - metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); - metricsLogger().putMetadata("booking_id", "1234567890"); // highlight-line - ... -} -``` - -This will be available in CloudWatch Logs to ease operations on high cardinal data. - -The `@Metrics` annotation **validates**, **serializes**, and **flushes** all your metrics. During metrics validation, if no metrics are provided no exception will be raised. - -If metrics are provided, and any of the following criteria are not met, `ValidationException` exception will be raised: - -* Minimum of 1 dimension -* Maximum of 9 dimensions - -If you want to ensure that at least one metric is emitted, you can pass `raiseOnEmptyMetrics = true` to the **@Metrics** annotation: - -```java:title=Handler.java -@Metrics(raiseOnEmptyMetrics = true) -public Object handleRequest(Object input, Context context) { -... -} -``` - -## Capturing cold start metric - -You can capture cold start metrics automatically with `@Metrics` via the `captureColdStart` variable. - -```java:title=Handler.java -@Metrics(captureColdStart = true) -public Object handleRequest(Object input, Context context) { -... -} -``` - -If it's a cold start invocation, this feature will: - -* Create a separate EMF blob solely containing a metric named `ColdStart` -* Add `FunctionName` and `Service` dimensions - -This has the advantage of keeping cold start metric separate from your application metrics. \ No newline at end of file diff --git a/docs/content/core/tracing.mdx b/docs/content/core/tracing.mdx deleted file mode 100644 index 0e17bbc16..000000000 --- a/docs/content/core/tracing.mdx +++ /dev/null @@ -1,150 +0,0 @@ ---- -title: Tracing -description: Core utility ---- - -import Note from "../../src/components/Note" - -Powertools tracing is an opinionated thin wrapper for [AWS X-Ray Java SDK](https://github.com/aws/aws-xray-sdk-java/) -a provides functionality to reduce the overhead of performing common tracing tasks. - -![Tracer showcase](../media/tracer_utility_showcase.png) - - **Key Features** - - * Capture cold start as annotation, and responses as well as full exceptions as metadata - * Helper methods to improve the developer experience of creating new X-Ray subsegments. - * Better developer experience when developing with multiple threads. - -Initialization -Your AWS Lambda function must have permission to send traces to AWS X-Ray - Here is an example using AWS Serverless Application Model (SAM) - -```yaml:title=template.yaml -Resources: - HelloWorldFunction: - Type: AWS::Serverless::Function - Properties: - ... - Runtime: java8 - - Tracing: Active - Environment: - Variables: - POWERTOOLS_SERVICE_NAME: example -``` - -The Powertools service name is used as the X-Ray namespace. This can be set using the environment variable -`POWERTOOLS_SERVICE_NAME` - -To enable Powertools tracing to your function add the @Tracing annotation to your handleRequest method or on -any method will capture the method as a separate subsegment automatically. - -```java:title=LambdaHandler.java -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - @Tracing - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - businessLogic1(); - - businessLogic2(); - } - - @Tracing - public void businessLogic1(){ - - } - - @Tracing - public void businessLogic2(){ - - } -} -``` - -By default this annotation will automatically record method responses and exceptions. - -<Note type="warning"> - <strong>Returning sensitive information from your Lambda handler or functions, where Tracer is used?</strong> - <br/><br/> - You can disable Tracer from capturing their responses and exception as tracing metadata with <strong><code>captureResponse=false</code></strong> and <strong><code>captureError=false</code></strong> -</Note><br/> - -```java:title=HandlerWithoutCapturingResponseOrError.java -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - @Tracing(captureError = false, captureResponse = false) - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - ... - } -``` - -### Annotations - -Annotations are key-values indexed by AWS X-Ray on a per trace basis. You can use them to filter traces as well as to create [Trace Groups](https://aws.amazon.com/about-aws/whats-new/2018/11/aws-xray-adds-the-ability-to-group-traces/). - -You can add annotations using `putAnnotation()` method from TracingUtils and it will be correctly inject for the subsegment in concern. - -```java -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - @Tracing - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - TracingUtils.putAnnotation("annotation", "value"); - } -} -``` - -### Metadata - -Metadata are non-indexed values that can add additional context for an operation. - -You can add metadata using `putMetadata()` method from TracingUtils and it will be correctly inject for the subsegment in concern. - -```java -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - @Tracing - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - TracingUtils.putMetadata("content", "value"); - } -} -``` - -## Utilities - -Tracer modules comes with certain utility method when you don't want to use annotation for capturing a code block -under a subsegment, or you are doing multithreaded programming. Refer examples below. - -```java:title=InlineSubsegmentCapture.java -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - TracingUtils.withSubsegment("loggingResponse", subsegment -> { - // Some business logic - }); - - TracingUtils.withSubsegment("localNamespace", "loggingResponse", subsegment -> { - // Some business logic - }); - } -} -``` - -```java:title=ThreadedProgramming.java -public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - // Extract existing trace data - Entity traceEntity = AWSXRay.getTraceEntity(); - - Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> { - // Business logic in separate thread - })); - } -} -``` - -## Instrumenting SDK clients and HTTP calls - -User should make sure to instrument the SDK clients explicitly based on the function dependency. Refer details on -[how to instrument SDK client with Xray](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-awssdkclients.html) and [outgoing http calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-httpclients.html). diff --git a/docs/content/dummy.md b/docs/content/dummy.md deleted file mode 100644 index 5b7bc5dbf..000000000 --- a/docs/content/dummy.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Dummy title -description: Dummy description ---- - - -dummy content \ No newline at end of file diff --git a/docs/content/index.mdx b/docs/content/index.mdx deleted file mode 100644 index c949fe3cc..000000000 --- a/docs/content/index.mdx +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: Homepage -description: AWS Lambda Powertools Java ---- - -import Note from "../src/components/Note" - -Powertools is a suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier. - -<Note type="info"> - <strong>Looking for a quick run through of the core utilities?</strong><br/><br/> - Check out <a href="https://aws.amazon.com/blogs/opensource/simplifying-serverless-best-practices-with-aws-lambda-powertools-java/">this detailed blog post</a> with a practical example. -</Note> - -## Install - -Powertools dependencies are available in Maven Central. You can use your favourite dependency management tool to install it - -* [maven](https://maven.apache.org/): -```xml -<dependencies> - ... - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - <version>1.1.0</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - <version>1.1.0</version> - </dependency> - <dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - <version>1.1.0</version> - </dependency> - ... -</dependencies> -``` - -And configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project: - -```xml -<build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.11</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-tracing</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-logging</artifactId> - </aspectLibrary> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-metrics</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> -</build> -``` -**Note:** If you are working with lambda function on runtime post java8, please refer [issue](https://github.com/awslabs/aws-lambda-powertools-java/issues/50) for workaround - -* [gradle](https://gradle.org): -```groovy -plugins{ - id 'java' - id 'aspectj.AspectjGradlePlugin' version '0.0.6' -} -repositories { - jcenter() -} -dependencies { - implementation 'software.amazon.lambda:powertools-tracing:1.1.0' - aspectpath 'software.amazon.lambda:powertools-tracing:1.1.0' - implementation 'software.amazon.lambda:powertools-logging:1.1.0' - aspectpath 'software.amazon.lambda:powertools-logging:1.1.0' - implementation 'software.amazon.lambda:powertools-metrics:1.1.0' - aspectpath 'software.amazon.lambda:powertools-metrics:1.1.0' -} -``` - -**Note:** - -Please add `aspectjVersion = '1.9.6'` to the `gradle.properties` file. The aspectj plugin works at the moment with gradle 5.x only if -you are using `java 8` as runtime. Please refer to [open issue](https://github.com/awslabs/aws-lambda-powertools-java/issues/146) for more details. - -**Quick hello world examples using SAM CLI** -You can use [SAM](https://aws.amazon.com/serverless/sam/) to quickly setup a serverless project including AWS Lambda Powertools Java. - -```bash:title=hello_world.sh -sam init --location gh:aws-samples/cookiecutter-aws-sam-powertools-java -``` -For more information about the project and available options refer to this [repository](https://github.com/aws-samples/cookiecutter-aws-sam-powertools-java/blob/main/README.md) - -## Environment variables - -**Environment variables** used across suite of utilities. - -Environment variable | Description | Utility -------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------- -**POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All -**POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) -**POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logging](./core/logging) -**LOG_LEVEL** | Sets logging level | [Logging](./core/logger) - -## Tenets - -* **AWS Lambda only** – We optimise for AWS Lambda function environments and supported runtimes only. Utilities might work with web frameworks and non-Lambda environments, though they are not officially supported. -* **Eases the adoption of best practices** – The main priority of the utilities is to facilitate best practices adoption, as defined in the AWS Well-Architected Serverless Lens; all other functionality is optional. -* **Keep it lean** – Additional dependencies are carefully considered for security and ease of maintenance, and prevent negatively impacting startup time. -* **We strive for backwards compatibility** – New features and changes should keep backwards compatibility. If a breaking change cannot be avoided, the deprecation and migration process should be clearly defined. -* **We work backwards from the community** – We aim to strike a balance of what would work best for 80% of customers. Emerging practices are considered and discussed via Requests for Comment (RFCs) -* **Idiomatic** – Utilities follow programming language idioms and language-specific best practices. - -_`*` Core utilities are Tracer, Logger and Metrics. Optional utilities may vary across languages._ \ No newline at end of file diff --git a/docs/content/utilities/batch.mdx b/docs/content/utilities/batch.mdx deleted file mode 100644 index 00b636828..000000000 --- a/docs/content/utilities/batch.mdx +++ /dev/null @@ -1,251 +0,0 @@ ---- -title: SQS Batch Processing -description: Utility ---- - - -import Note from "../../src/components/Note" - -The SQS batch processing utility provides a way to handle partial failures when processing batches of messages from SQS. - -**Key Features** - -* Prevent successfully processed messages from being returned to SQS -* A simple interface for individually processing messages from a batch - -**Background** - -When using SQS as a Lambda event source mapping, Lambda functions can be triggered with a batch of messages from SQS. - -If your function fails to process any message from the batch, the entire batch returns to your SQS queue, and your Lambda function will be triggered with the same batch again. - -With this utility, messages within a batch will be handled individually - only messages that were not successfully processed -are returned to the queue. - -<Note type="warning"> - While this utility lowers the chance of processing messages more than once, it is not guaranteed. We recommend implementing processing logic in an idempotent manner wherever possible. - <br/><br/> - More details on how Lambda works with SQS can be found in the <a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html">AWS documentation</a> -</Note> - -## Install - -To install this utility, add the following dependency to your project. - -```xml -<dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - <version>1.1.0</version> -</dependency> -``` - -And configure the aspectj-maven-plugin to compile-time weave (CTW) the -aws-lambda-powertools-java aspects into your project. You may already have this -plugin in your pom. In that case add the dependency to the `aspectLibraries` -section. - -```xml -<build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.11</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <!-- highlight-start --> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - </aspectLibrary> - <!-- highlight-end --> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> -</build> -``` - -**IAM Permissions** - -This utility requires additional permissions to work as expected. Lambda functions using this utility require the `sqs:GetQueueUrl` and `sqs:DeleteMessageBatch` permission. - -## Processing messages from SQS - -You can use either **[SqsBatch annotation](#sqsbatch-annotation)**, or **[SqsUtils Utility API](#sqsutils-utility-api)** as a fluent API. - -Both have nearly the same behaviour when it comes to processing messages from the batch: - -* **Entire batch has been successfully processed**, where your Lambda handler returned successfully, we will let SQS delete the batch to optimize your cost -* **Entire Batch has been partially processed successfully**, where exceptions were raised within your `SqsMessageHandler` interface implementation, we will: - - **1)** Delete successfully processed messages from the queue by directly calling `sqs:DeleteMessageBatch` - - **2)** Raise `SQSBatchProcessingException` to ensure failed messages return to your SQS queue - -The only difference is that **SqsUtils Utility API** will give you access to return from the processed messages if you need. Exception `SQSBatchProcessingException` thrown from the -utility will have access to both successful and failed messaged along with failure exceptions. - -## Functional Interface SqsMessageHandler - -Both [annotation](#sqsbatch-annotation) and [SqsUtils Utility API](#sqsutils-utility-api) requires an implementation of functional interface `SqsMessageHandler`. - -This implementation is responsible for processing each individual message from the batch, and to raise an exception if unable to process any of the messages sent. - -**Any non-exception/successful return from your record handler function** will instruct utility to queue up each individual message for deletion. - -### SqsBatch annotation - -When using this annotation, you need provide a class implementation of `SqsMessageHandler` that will process individual messages from the batch - It should raise an exception if it is unable to process the record. - -All records in the batch will be passed to this handler for processing, even if exceptions are thrown - Here's the behaviour after completing the batch: - -* **Any successfully processed messages**, we will delete them from the queue via `sqs:DeleteMessageBatch` -* **Any unprocessed messages detected**, we will raise `SQSBatchProcessingException` to ensure failed messages return to your SQS queue - -<Note type="warning"> - You will not have accessed to the <strong>processed messages</strong> within the Lambda Handler - all processing logic will and should be performed by the implemented <code>SqsMessageHandler#process()</code> function. -</Note><br/> - -```java:title=App.java -public class AppSqsEvent implements RequestHandler<SQSEvent, String> { - @Override - @SqsBatch(SampleMessageHandler.class) // highlight-line - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } - - public class SampleMessageHandler implements SqsMessageHandler<Object> { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - } - } -} -``` - -### SqsUtils Utility API - -If you require access to the result of processed messages, you can use this utility. - -The result from calling <code>SqsUtils#batchProcessor()</code> on the context manager will be a list of all the return values from your <code>SqsMessageHandler#process()</code> function. - -```java:title=App.java -public class AppSqsEvent implements RequestHandler<SQSEvent, List<String>> { - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - List<String> returnValues = SqsUtils.batchProcessor(input, SampleMessageHandler.class); // highlight-line - - return returnValues; - } - - public class SampleMessageHandler implements SqsMessageHandler<String> { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - } - } -} -``` - -You can also use the utility in a more functional way` by providing inline implementation of functional interface <code>SqsMessageHandler#process()</code> - -```java:title=App.java -public class AppSqsEvent implements RequestHandler<SQSEvent, List<String>> { - - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - // highlight-start - List<String> returnValues = SqsUtils.batchProcessor(input, (message) -> { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - }); - // highlight-end - - return returnValues; - } -} -``` - -## Passing custom SqsClient - -If you need to pass custom SqsClient such as region to the SDK, you can pass your own `SqsClient` to be used by utility either for -**[SqsBatch annotation](#sqsbatch-annotation)**, or **[SqsUtils Utility API](#sqsutils-utility-api)**. - -```java:title=App.java - -public class AppSqsEvent implements RequestHandler<SQSEvent, List<String>> { - // highlight-start - static { - SqsUtils.overrideSqsClient(SqsClient.builder() - .build()); - } - // highlight-end - - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - List<String> returnValues = SqsUtils.batchProcessor(input, SampleMessageHandler.class); - - return returnValues; - } - - public class SampleMessageHandler implements SqsMessageHandler<String> { - - @Override - public String process(SQSMessage message) { - // This will be called for each individual message from a batch - // It should raise an exception if the message was not processed successfully - String returnVal = doSomething(message.getBody()); - return returnVal; - } - } -} - -``` - -## Suppressing exceptions - -If you want to disable the default behavior where `SQSBatchProcessingException` is raised if there are any exception, you can pass the `suppressException` boolean argument. - -**Within SqsBatch annotation** - -```java:title=App.java -... - @Override - @SqsBatch(value = SampleMessageHandler.class, suppressException = true) // highlight-line - public String handleRequest(SQSEvent input, Context context) { - return "{\"statusCode\": 200}"; - } -``` - -**Within SqsUtils Utility API** - -```java:title=App.java - @Override - public List<String> handleRequest(SQSEvent input, Context context) { - List<String> returnValues = SqsUtils.batchProcessor(input, true, SampleMessageHandler.class); // highlight-line - - return returnValues; - } -``` diff --git a/docs/content/utilities/parameters.mdx b/docs/content/utilities/parameters.mdx deleted file mode 100644 index 8804f2093..000000000 --- a/docs/content/utilities/parameters.mdx +++ /dev/null @@ -1,369 +0,0 @@ ---- -title: Parameters -description: Utility ---- - - - -The parameters utility provides a way to retrieve parameter values from -[AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) or -[AWS Secrets Manager](https://aws.amazon.com/secrets-manager/). It also provides a base class to create your parameter provider implementation. - -**Key features** - -* Retrieve one or multiple parameters from the underlying provider -* Cache parameter values for a given amount of time (defaults to 5 seconds) -* Transform parameter values from JSON or base 64 encoded strings - -## Install - -To install this utility, add the following dependency to your project. - -```xml -<dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - <version>1.1.0</version> -</dependency> -``` - -**IAM Permissions** - -This utility requires additional permissions to work as expected. See the table below: - -Provider | Function/Method | IAM Permission -------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------- -SSM Parameter Store | `SSMProvider.get(String)` `SSMProvider.get(String, Class)` | `ssm:GetParameter` -SSM Parameter Store | `SSMProvider.getMultiple(String)` | `ssm:GetParametersByPath` -Secrets Manager | `SecretsProvider.get(String)` `SecretsProvider.get(String, Class)` | `secretsmanager:GetSecretValue` - -## SSM Parameter Store - -You can retrieve a single parameter using SSMProvider.get() and pass the key of the parameter. -For multiple parameters, you can use SSMProvider.getMultiple() and pass the path to retrieve them all. - -```java:title=AppWithSSM.java - -public class AppWithSSM implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get an instance of the SSM Provider - SSMProvider ssmProvider = ParamManager.getSsmProvider(); - - // Retrieve a single parameter - String value = ssmProvider.get("/my/parameter"); - - // Retrieve multiple parameters from a path prefix - // This returns a Map with the parameter name as key - Map<String, String> values = ssmProvider.getMultiple("/my/path/prefix"); - -} -``` - -Alternatively, you can retrieve an instance of a provider and configure its underlying SDK client, -in order to get data from other regions or use specific credentials: - -```java - SsmClient client = SsmClient.builder().region(Region.EU_CENTRAL_1).build(); - SSMProvider ssmProvider = ParamManager.getSsmProvider(client); -``` -### Additional arguments - -The AWS Systems Manager Parameter Store provider supports two additional arguments for the `get()` and `getMultiple()` methods: - -| Option | Default | Description | -|---------------|---------|-------------| -| **withDecryption()** | `False` | Will automatically decrypt the parameter. | -| **recursive()** | `False` | For `getMultiple()` only, will fetch all parameter values recursively based on a path prefix. | - -**Example:** - -```java:title=AppWithSSM.java - -public class AppWithSSM implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get an instance of the SSM Provider - SSMProvider ssmProvider = ParamManager.getSsmProvider(); - - // Retrieve a single parameter and decrypt it - String value = ssmProvider.withDecryption().get("/my/parameter"); - - // Retrieve multiple parameters recursively from a path prefix - Map<String, String> values = ssmProvider.recursive().getMultiple("/my/path/prefix"); - -} -``` - -## Secrets Manager - -```java:title=AppWithSecrets.java - -public class AppWithSecrets implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - // Get an instance of the Secrets Provider - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); - - // Retrieve a single secret - String value = secretsProvider.get("/my/secret"); - -} -``` - -Alternatively, you can retrieve an instance of a provider and configure its underlying SDK client, -in order to get data from other regions or use specific credentials: - -```java - SecretsManagerClient client = SecretsManagerClient.builder().region(Region.EU_CENTRAL_1).build(); - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(client); -``` - -## Advanced configuration - -### Caching - -By default, all parameters and their corresponding values are cached for 5 seconds. - -You can customize this default value using: -```java - provider.defaultMaxAge(int, ChronoUnit) -``` - -You can also customize this value for each parameter with: -```java - provider.withMaxAge(int, ChronoUnit).get() -``` - -### Transform values - -Parameter values can be transformed using ```withTransformation(transformerClass)```. -Base64 and JSON transformations are provided: - -```java - String value = provider - .withTransformation(Transformer.base64) - .get("/my/parameter/b64"); -``` - -For more complex transformation, you need to specify how to deserialize: - -```java - MyObj object = provider - .withTransformation(Transformer.json) - .get("/my/parameter/json", MyObj.class); -``` - -**Note**: ```SSMProvider.getMultiple()``` does not support transformation and will return simple Strings. - -**Write your own Transformer** - -You can write your own transformer, by implementing the ```Transformer``` interface and the ```applyTransformation()``` method. -For example, if you wish to deserialize XML into an object: - -```java:title=XmlTransformer.java -public class XmlTransformer<T> implements Transformer<T> { - - private final XmlMapper mapper = new XmlMapper(); - - @Override - public T applyTransformation(String value, Class<T> targetClass) throws TransformationException { - try { - return mapper.readValue(value, targetClass); - } catch (IOException e) { - throw new TransformationException(e); - } - } -} -``` - -Then use it like this: - -```java -MyObj object = provider - .withTransformation(XmlTransformer.class) - .get("/my/parameter/xml", MyObj.class); -``` - -### Fluent API - -To simplify the use of the library, you can chain all method calls before a get. - -**Example:** - -```java -ssmProvider - .defaultMaxAge(10, SECONDS) // will set 10 seconds as the default cache TTL - .withMaxAge(1, MINUTES) // will set the cache TTL for this value at 1 minute - .withTransformation(json) // json is a static import from Transformer.json - .withDecryption() // enable decryption of the parameter value - .get("/my/param", MyObj.class); // finally get the value -``` - -## Create your own provider -You can create your own custom parameter store provider by inheriting the ```BaseProvider``` class and implementing the -```String getValue(String key)``` method to retrieve data from your underlying store. - -All transformation and caching logic is handled by the get() methods in the base class. - -Here is an example implementation using S3 as a custom parameter store: - -```java:title=S3Provider.java -public class S3Provider extends BaseProvider { - - private final S3Client client; - private String bucket; - - S3Provider(CacheManager cacheManager) { - this(cacheManager, S3Client.create()); - } - - S3Provider(CacheManager cacheManager, S3Client client) { - super(cacheManager); - this.client = client; - } - - public S3Provider withBucket(String bucket) { - this.bucket = bucket; - return this; - } - - @Override - protected String getValue(String key) { - if (bucket == null) { - throw new IllegalStateException("A bucket must be specified, using withBucket() method"); - } - - GetObjectRequest request = GetObjectRequest.builder().bucket(bucket).key(key).build(); - ResponseBytes<GetObjectResponse> response = client.getObject(request, ResponseTransformer.toBytes()); - return response.asUtf8String(); - } - - @Override - protected Map<String, String> getMultipleValues(String path) { - if (bucket == null) { - throw new IllegalStateException("A bucket must be specified, using withBucket() method"); - } - - ListObjectsV2Request listRequest = ListObjectsV2Request.builder().bucket(bucket).prefix(path).build(); - List<S3Object> s3Objects = client.listObjectsV2(listRequest).contents(); - - Map<String, String> result = new HashMap<>(); - s3Objects.forEach(s3Object -> { - result.put(s3Object.key(), getValue(s3Object.key())); - }); - - return result; - } - - @Override - protected void resetToDefaults() { - super.resetToDefaults(); - bucket = null; - } - -} -``` -And then use it like this : - -```java -S3Provider provider = new S3Provider(ParamManager.getCacheManager()); -provider.setTransformationManager(ParamManager.getTransformationManager()); // optional, needed for transformations -String value = provider.withBucket("myBucket").get("myKey"); -``` - -## Annotation -You can make use of the annotation ```@Param``` to inject a parameter value in a variable. - -```java -@Param(key = "/my/parameter") -private String value; -``` -By default it will use ```SSMProvider``` to retrieve the value from AWS System Manager Parameter Store. -You could specify a different provider as long as it extends ```BaseProvider``` and/or a ```Transformer```. -For example: - -```java -@Param(key = "/my/parameter/json", provider = SecretsProvider.class, transformer = JsonTransformer.class) -private ObjectToDeserialize value; -``` - -In this case ```SecretsProvider``` will be used to retrieve a raw value that is then trasformed into the target Object by using ```JsonTransformer```. -To show the convenience of the annotation compare the following two code snippets. - -```java:title=AppWithoutAnnotation.java - -public class AppWithoutAnnotation implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - // Get an instance of the SSM Provider - SSMProvider ssmProvider = ParamManager.getSsmProvider(); - - // Retrieve a single parameter - ObjectToDeserialize value = ssmProvider - .withTransformation(Transformer.json) - .get("/my/parameter/json"); - -} -``` -And with the usage of ```@Param``` - -```java:title=AppWithAnnotation.java -public class AppWithAnnotation implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - @Param(key = "/my/parameter/json" transformer = JsonTransformer.class) - ObjectToDeserialize value; - -} -``` - -### Install - -If you want to use the ```@Param``` annotation in your project add configuration to compile-time weave (CTW) the powertools-parameters aspects into your project. - -* [maven](https://maven.apache.org/): -```xml -<build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.11</version> - <configuration> - ... - <aspectLibraries> - ... - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-parameters</artifactId> - </aspectLibrary> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> -</build> -``` -**Note:** If you are working with lambda function on runtime post java8, please refer [issue](https://github.com/awslabs/aws-lambda-powertools-java/issues/50) for workaround - -* [gradle](https://gradle.org): -```groovy -plugins{ - id 'java' - id 'aspectj.AspectjGradlePlugin' version '0.0.6' -} -repositories { - jcenter() -} -dependencies { - ... - implementation 'software.amazon.lambda:powertools-parameters:1.1.0' - aspectpath 'software.amazon.lambda:powertools-parameters:1.1.0' -} -``` - -**Note:** - -Please add `aspectjVersion = '1.9.6'` to the `gradle.properties` file. The aspectj plugin works at the moment with gradle 5.x only if -you are using `java 8` as runtime. Please refer to [open issue](https://github.com/awslabs/aws-lambda-powertools-java/issues/146) for more details. \ No newline at end of file diff --git a/docs/content/utilities/sqs_large_message_handling.mdx b/docs/content/utilities/sqs_large_message_handling.mdx deleted file mode 100644 index 6cb80e124..000000000 --- a/docs/content/utilities/sqs_large_message_handling.mdx +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: SQS Large Message Handling -description: Utility ---- - -The large message handling utility handles SQS messages which have had their payloads -offloaded to S3 due to them being larger than the SQS maximum. - -The utility automatically retrieves messages which have been offloaded to S3 using the -[amazon-sqs-java-extended-client-lib](https://github.com/awslabs/amazon-sqs-java-extended-client-lib) -client library. Once the message payloads have been processed successful the -utility can delete the message payloads from S3. - -This utility is compatible with versions *1.1.0+* of amazon-sqs-java-extended-client-lib. - -```xml -<dependency> - <groupId>com.amazonaws</groupId> - <artifactId>amazon-sqs-java-extended-client-lib</artifactId> - <version>1.1.0</version> -</dependency> -``` - -## Install - -To install this utility, add the following dependency to your project. - -```xml -<dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - <version>1.1.0</version> -</dependency> -``` - -And configure the aspectj-maven-plugin to compile-time weave (CTW) the -aws-lambda-powertools-java aspects into your project. You may already have this -plugin in your pom. In that case add the dependency to the `aspectLibraries` -section. - -```xml -<build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.11</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <!-- highlight-start --> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-sqs</artifactId> - </aspectLibrary> - <!-- highlight-end --> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> -</build> -``` - -The annotation `@SqsLargeMessage` should be used with the handleRequest method of a class -which implements `com.amazonaws.services.lambda.runtime.RequestHandler` with -`com.amazonaws.services.lambda.runtime.events.SQSEvent` as the first parameter. - -```java -public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { - - @Override - @SqsLargeMessage - public String handleRequest(SQSEvent sqsEvent, Context context) { - // process messages - - return "ok"; - } -} -``` - -`@SqsLargeMessage` creates a default S3 Client `AmazonS3 amazonS3 = AmazonS3ClientBuilder.defaultClient()`. -When the Lambda function is invoked with an event from SQS, each record received -in the SQSEvent will be checked to see if it's body contains a payload which has -been offloaded to S3. If it does then `getObject(bucket, key)` will be called, -and the payload retrieved. If there is an error during this process then the -function will fail with a `FailedProcessingLargePayloadException` exception. - -If the request handler method returns without error then each payload will be -deleted from S3 using `deleteObject(bucket, key)` - -To disable deletion of payloads setting the following annotation parameter: - -```java -@SqsLargeMessage(deletePayloads=false) -public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { - -} -``` - -## Utility - -If you want to avoid using annotation and have control over error that can happen during payload enrichment. - -`SqsUtils.enrichedMessageFromS3()` provides you access with list of `SQSMessage` object enriched from S3 payload. -Original `SQSEvent` object is never mutated. You can also control if the S3 payload should be deleted after successful -processing. You can enrich messages from S3 with below code: - -```java -public class SqsMessageHandler implements RequestHandler<SQSEvent, String> { - - @Override - public String handleRequest(SQSEvent sqsEvent, Context context) { - - Map<String, String> sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> { - // Some business logic - Map<String, String> someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - // Do not delete payload after processing. - Map<String, String> sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, false, sqsMessages -> { - // Some business logic - Map<String, String> someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); - - // Better control over exception during enrichment - try { - // Do not delete payload after processing. - SqsUtils.enrichedMessageFromS3(sqsEvent, false, sqsMessages -> { - // Some business logic - }); - } catch (FailedProcessingLargePayloadException e) { - // handle any exception. - } - - return "ok"; - } -} -``` \ No newline at end of file diff --git a/docs/content/utilities/validation.mdx b/docs/content/utilities/validation.mdx deleted file mode 100644 index db187359a..000000000 --- a/docs/content/utilities/validation.mdx +++ /dev/null @@ -1,332 +0,0 @@ ---- -title: Validation -description: Utility ---- - - -import Note from "../../src/components/Note" - -This utility provides JSON Schema validation for payloads held within events and response used in AWS Lambda. - -**Key features** -* Validate incoming events and responses -* Built-in validation for most common events (API Gateway, SNS, SQS, ...) -* JMESPath support validate only a sub part of the event - -## Install - -To install this utility, add the following dependency to your project. - -```xml -<dependency> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-validation</artifactId> - <version>1.1.0</version> -</dependency> -``` - -And configure the aspectj-maven-plugin to compile-time weave (CTW) the -aws-lambda-powertools-java aspects into your project. You may already have this -plugin in your pom. In that case add the dependency to the `aspectLibraries` -section. - -```xml -<build> - <plugins> - ... - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>aspectj-maven-plugin</artifactId> - <version>1.11</version> - <configuration> - <source>1.8</source> - <target>1.8</target> - <complianceLevel>1.8</complianceLevel> - <aspectLibraries> - <!-- highlight-start --> - <aspectLibrary> - <groupId>software.amazon.lambda</groupId> - <artifactId>powertools-validation</artifactId> - </aspectLibrary> - <!-- highlight-end --> - </aspectLibraries> - </configuration> - <executions> - <execution> - <goals> - <goal>compile</goal> - </goals> - </execution> - </executions> - </plugin> - ... - </plugins> -</build> -``` - -## Validating events - -You can validate inbound and outbound events using `@Validation` annotation. - -You can also use the `Validator#validate()` methods, if you want more control over the validation process such as handling a validation error. - -We support JSON schema version 4, 6, 7 and 201909 (from [jmespath-jackson library](https://github.com/burtcorp/jmespath-java)). - -### @Validation annotation - -`@Validation` annotation is used to validate either inbound events or functions' response. - -It will fail fast with `ValidationException` if an event or response doesn't conform with given JSON Schema. - -While it is easier to specify a json schema file in the classpath (using the notation `"classpath:/path/to/schema.json"`), you can also provide a JSON String containing the schema. - -```java -public class MyFunctionHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - @Override - @Validation(inboundSchema = "classpath:/schema_in.json", outboundSchema = "classpath:/schema_out.json") - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - // ... - return something; - } -} -``` - -**NOTE**: It's not a requirement to validate both inbound and outbound schemas - You can either use one, or both. - -### Validate function - -Validate standalone function is used within the Lambda handler, or any other methods that perform data validation. - -You can also gracefully handle schema validation errors by catching `ValidationException`. - -```java -import static software.amazon.lambda.powertools.validation.ValidationUtils.*; - -public class MyFunctionHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { - - @Override - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - try { - validate(input, "classpath:/schema.json"); - } catch (ValidationException ex) { - // do something before throwing it - throw ex; - } - - // ... - return something; - } -} -``` -**NOTE**: Schemas are stored in memory for reuse, to avoid loading them from file each time. - -## Built-in events and responses - -For the following events and responses, the Validator will automatically perform validation on the content. - -** Events ** - - Type of event | Class | Path to content | - ------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- - API Gateway REST | APIGatewayProxyRequestEvent | `body` - API Gateway HTTP | APIGatewayV2HTTPEvent | `body` - Application Load Balancer | ApplicationLoadBalancerRequestEvent | `body` - Cloudformation Custom Resource | CloudFormationCustomResourceEvent | `resourceProperties` - CloudWatch Logs | CloudWatchLogsEvent | `awslogs.powertools_base64_gzip(data)` - EventBridge / Cloudwatch | ScheduledEvent | `detail` - Kafka | KafkaEvent | `records[*][*].value` - Kinesis | KinesisEvent | `Records[*].kinesis.powertools_base64(data)` - Kinesis Firehose | KinesisFirehoseEvent | `Records[*].powertools_base64(data)` - Kinesis Analytics from Firehose | KinesisAnalyticsFirehoseInputPreprocessingEvent | `Records[*].powertools_base64(data)` - Kinesis Analytics from Streams | KinesisAnalyticsStreamsInputPreprocessingEvent | `Records[*].powertools_base64(data)` - SNS | SNSEvent | `Records[*].Sns.Message` - SQS | SQSEvent | `Records[*].body` - -** Responses ** - - Type of response | Class | Path to content (envelope) - ------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- - API Gateway REST | APIGatewayProxyResponseEvent} | `body` - API Gateway HTTP | APIGatewayV2HTTPResponse} | `body` - API Gateway WebSocket | APIGatewayV2WebSocketResponse} | `body` - Load Balancer | ApplicationLoadBalancerResponseEvent} | `body` - Kinesis Analytics | KinesisAnalyticsInputPreprocessingResponse} | `Records[*].powertools_base64(data)`` - -## Custom events and responses - -You can also validate any Event or Response type, once you have the appropriate schema. - -Sometimes, you might want to validate only a portion of it - This is where the envelope parameter is for. - -Envelopes are [JMESPath expressions](https://jmespath.org/tutorial.html) to extract a portion of JSON you want before applying JSON Schema validation. - -Here is a custom event where we only want to validate each products: - -```json -{ - "basket": { - "products" : [ - { - "id": 43242, - "name": "FooBar XY", - "price": 258 - }, - { - "id": 765, - "name": "BarBaz AB", - "price": 43.99 - } - ] - } -} -``` - -Here is how you'd use the `envelope` parameter to extract the payload inside the products key before validating: - -```java -public class MyCustomEventHandler implements RequestHandler<MyCustomEvent, String> { - - @Override - @Validation(inboundSchema = "classpath:/my_custom_event_schema.json", - envelope = "basket.products[*]") - public String handleRequest(MyCustomEvent input, Context context) { - return "OK"; - } -} -``` - -This is quite powerful because you can use JMESPath Query language to extract records from -[arrays, slice and dice](https://jmespath.org/tutorial.html#list-and-slice-projections), -to [pipe expressions](https://jmespath.org/tutorial.html#pipe-expressions) -and [function](https://jmespath.org/tutorial.html#functions) expressions, where you'd extract what you need before validating the actual payload. - -## JMESPath functions - -JMESPath functions ensure to make an operation on a specific part of the json.validate - -Powertools provides two built-in functions: - -### powertools_base64 function - -Use `powertools_base64` function to decode any base64 data. - -This sample will decode the base64 value within the data key, and decode the JSON string into a valid JSON before we can validate it: - -```json -{ - "data" : "ewogICJpZCI6IDQzMjQyLAogICJuYW1lIjogIkZvb0JhciBYWSIsCiAgInByaWNlIjogMjU4Cn0=" -} -``` - -```java -public class MyEventHandler implements RequestHandler<MyEvent, String> { - - @Override - public String handleRequest(MyEvent myEvent, Context context) { - validate(myEvent, "classpath:/schema.json", "powertools_base64(data)"); - return "OK"; - } -} -``` - -### powertools_base64_gzip function - -Use `powertools_base64_gzip` function to decompress and decode base64 data. - -This sample will decompress and decode base64 data: - -```json -{ - "data" : "H4sIAAAAAAAA/6vmUlBQykxRslIwMTYyMdIBcfMSc1OBAkpu+flOiUUKEZFKYOGCosxkkLiRqQVXLQDnWo6bOAAAAA==" -} -``` - -```java -public class MyEventHandler implements RequestHandler<MyEvent, String> { - - @Override - public String handleRequest(MyEvent myEvent, Context context) { - validate(myEvent, "classpath:/schema.json", "powertools_base64_gzip(data)"); - return "OK"; - } -} -``` - -**NOTE:** You don't need any function to transform a JSON String into a JSON object, powertools-validation will do it for you. -In the 2 previous example, data contains JSON. Just provide the function to transform the base64 / gzipped / ... string into a clear JSON string. - -### Bring your own JMESPath function - -<Note type="warning"> -This should only be used for advanced use cases where you have special formats not covered by the built-in functions. -New functions will be added to the 2 built-in ones. -</Note> - - -Your function must extend `io.burt.jmespath.function.BaseFunction`, take a String as parameter and return a String. -You can read the [doc](https://github.com/burtcorp/jmespath-java#adding-custom-functions) for more information. - -Here is an example that takes some xml and transform it into json: -```java -public class XMLFunction extends BaseFunction { - public Base64Function() { - super("powertools_xml", ArgumentConstraints.typeOf(JmesPathType.STRING)); - } - - @Override - protected <T> T callFunction(Adapter<T> runtime, List<FunctionArgument<T>> arguments) { - T value = arguments.get(0).value(); - String xmlString = runtime.toString(value); - - String jsonString = // ... transform xmlString to json - - return runtime.createString(jsonString); - } -} -``` - -Once your function is created, you need to add it to powertools: - -```java -ValidationConfig.get().addFunction(new XMLFunction()); -``` - -You can then use it to do your validation: -```java -public class MyXMLEventHandler implements RequestHandler<MyEventWithXML, String> { - - @Override - public String handleRequest(MyEventWithXML myEvent, Context context) { - validate(myEvent, "classpath:/schema.json", "powertools_xml(path.to.xml_data)"); - return "OK"; - } -} -``` -or using annotation: -```java -public class MyXMLEventHandler implements RequestHandler<MyEventWithXML, String> { - - @Override - @Validation(inboundSchema="classpath:/schema.json", envelope="powertools_xml(path.to.xml_data)") - public String handleRequest(MyEventWithXML myEvent, Context context) { - return "OK"; - } -} -``` - -## Change the schema version -By default, powertools-validation is configured with [V7](https://json-schema.org/draft-07/json-schema-release-notes.html). -You can use the `ValidationConfig` to change that behaviour: - -```java -ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V4); -``` - -## Advanced ObjectMapper settings -If you need to configure the Jackson ObjectMapper, you can use the `ValidationConfig`: - -```java -ObjectMapper objectMapper= ValidationConfig.get().getObjectMapper(); -// update (de)serializationConfig or other properties -``` \ No newline at end of file diff --git a/docs/core/logging.md b/docs/core/logging.md new file mode 100644 index 000000000..8358087d2 --- /dev/null +++ b/docs/core/logging.md @@ -0,0 +1,1635 @@ +--- +title: Logging +description: Core utility +--- + +Logging provides an opinionated logger with output structured as JSON. + +## Key features + +* Leverages standard logging libraries: [_SLF4J_](https://www.slf4j.org/){target="_blank"} as the API, and [_log4j2_](https://logging.apache.org/log4j/2.x/){target="_blank"} or [_logback_](https://logback.qos.ch/){target="_blank"} for the implementation +* Captures key fields from Lambda context, cold start and structures logging output as JSON +* Optionally logs Lambda request +* Optionally logs Lambda response +* Optionally supports log sampling by including a configurable percentage of DEBUG logs in logging output +* Optionally supports buffering lower level logs and flushing them on error or manually +* Allows additional keys to be appended to the structured log at any point in time +* GraalVM support + + +## Getting started + +???+ tip + You can find complete examples in the [project repository](https://github.com/aws-powertools/powertools-lambda-java/tree/v2/examples/powertools-examples-core-utilities){target="_blank"}. + +### Installation +Depending on preference, you must choose to use either _log4j2_ or _logback_ as your log provider. If you use the AspectJ annotation approach, you must configure _aspectj_ to weave the code and make sure the annotation is processed. If you prefer the [functional approach](../usage-patterns.md#functional-approach), AspectJ configuration is not required. + +#### Maven +=== "log4j2" + + ```xml hl_lines="3-12 30-33" + <dependencies> + ... + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + ... + </dependencies> + ... + <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> + <build> + <plugins> + ... + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14</version> + <configuration> + <source>11</source> <!-- or higher --> + <target>11</target> <!-- or higher --> + <complianceLevel>11</complianceLevel> <!-- or higher --> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + ... + </plugins> + </build> + ``` + +=== "logback" + + ```xml hl_lines="3-12 30-33" + <dependencies> + ... + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-logback</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + ... + </dependencies> + ... + <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> + <build> + <plugins> + ... + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14</version> + <configuration> + <source>11</source> <!-- or higher --> + <target>11</target> <!-- or higher --> + <complianceLevel>11</complianceLevel> <!-- or higher --> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + ... + </plugins> + </build> + ``` + +#### Gradle + +=== "log4j2" + + ```groovy hl_lines="3 11-12" + plugins { + id 'java' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach + } + + repositories { + mavenCentral() + } + + dependencies { + aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' + } + + sourceCompatibility = 11 + targetCompatibility = 11 + ``` + +=== "logback" + + ```groovy hl_lines="3 11-12" + plugins { + id 'java' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach + } + + repositories { + mavenCentral() + } + + dependencies { + aspect 'software.amazon.lambda:powertools-logging:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-logging-logback:{{ powertools.version }}' + } + + sourceCompatibility = 11 + targetCompatibility = 11 + ``` + + +### Configuration + +#### Main environment variables + +The logging module requires two settings: + +| Environment variable | Setting | Description | +|---------------------------|-------------------|-------------------------------------------------------------------------------------------------------------| +| `POWERTOOLS_LOG_LEVEL` | **Logging level** | Sets how verbose Logger should be. If not set, will use the [Logging configuration](#logging-configuration) | +| `POWERTOOLS_SERVICE_NAME` | **Service** | Sets service key that will be included in all log statements (Default is `service_undefined`) | + +Here is an example using AWS Serverless Application Model (SAM): + +=== "template.yaml" +``` yaml hl_lines="10 11" +Resources: + PaymentFunction: + Type: AWS::Serverless::Function + Properties: + MemorySize: 512 + Timeout: 20 + Runtime: java17 + Environment: + Variables: + POWERTOOLS_LOG_LEVEL: WARN + POWERTOOLS_SERVICE_NAME: payment +``` + +There are some other environment variables which can be set to modify Logging's settings at a global scope: + +| Environment variable | Type | Description | +|---------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------| +| `POWERTOOLS_LOGGER_SAMPLE_RATE` | float | Configure the sampling rate at which `DEBUG` logs should be included. See [sampling rate](#sampling-debug-logs) | +| `POWERTOOLS_LOGGER_LOG_EVENT` | boolean | Specify if the incoming Lambda event should be logged. See [Logging event](#logging-incoming-event) | +| `POWERTOOLS_LOGGER_LOG_RESPONSE` | boolean | Specify if the Lambda response should be logged. See [logging response](#logging-handler-response) | +| `POWERTOOLS_LOGGER_LOG_ERROR` | boolean | Specify if a Lambda uncaught exception should be logged. See [logging exception](#logging-handler-uncaught-exception ) | + +#### Logging configuration + +Powertools for AWS Lambda (Java) simply extends the functionality of the underlying library you choose (_log4j2_ or _logback_). +You can leverage the standard configuration files (_log4j2.xml_ or _logback.xml_): + +=== "log4j2.xml" + + With log4j2, we leverage the [`JsonTemplateLayout`](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html){target="_blank"} + to provide structured logging. A default template is provided in powertools ([_LambdaJsonLayout.json_](https://github.com/aws-powertools/powertools-lambda-java/blob/4444b4bce8eb1cc19880d1c1ef07188d97de9126/powertools-logging/powertools-logging-log4j/src/main/resources/LambdaJsonLayout.json){target="_blank"}): + + ```xml hl_lines="5" + <?xml version="1.0" encoding="UTF-8"?> + <Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Logger name="com.example" level="debug" additivity="false"> + <AppenderRef ref="JsonAppender"/> + </Logger> + <Root level="info"> + <AppenderRef ref="JsonAppender"/> + </Root> + </Loggers> + </Configuration> + ``` + +=== "logback.xml" + + With logback, we leverage a custom [Encoder](https://logback.qos.ch/manual/encoders.html){target="_blank"} + to provide structured logging: + + ```xml hl_lines="4 5" + <?xml version="1.0" encoding="UTF-8"?> + <configuration> + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + </encoder> + </appender> + <logger name="com.example" level="DEBUG" additivity="false"> + <appender-ref ref="console" /> + </logger> + <root level="INFO"> + <appender-ref ref="console" /> + </root> + </configuration> + ``` + +## Log level +Log level is generally configured in the `log4j2.xml` or `logback.xml`. But this level is static and needs a redeployment of the function to be changed. +Powertools for AWS Lambda permits to change this level dynamically thanks to an environment variable `POWERTOOLS_LOG_LEVEL`. + +We support the following log levels (SLF4J levels): `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`. +If the level is set to `CRITICAL` (supported in log4j but not logback), we revert it back to `ERROR`. +If the level is set to any other value, we set it to the default value (`INFO`). + +### AWS Lambda Advanced Logging Controls (ALC) + +!!!question "When is it useful?" + When you want to set a logging policy to drop informational or verbose logs for one or all AWS Lambda functions, regardless of runtime and logger used. + +<!-- markdownlint-disable MD013 --> +With [AWS Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced){target="_blank"}, you can enforce a minimum log level that Lambda will accept from your application code. + +When enabled, you should keep your own log level and ALC log level in sync to avoid data loss. + +Here's a sequence diagram to demonstrate how ALC will drop both `INFO` and `DEBUG` logs emitted from `Logger`, when ALC log level is stricter than `Logger`. +<!-- markdownlint-enable MD013 --> + +```mermaid +sequenceDiagram + participant Lambda service + participant Lambda function + participant Application Logger + + Note over Lambda service: AWS_LAMBDA_LOG_LEVEL="WARN" + Note over Application Logger: POWERTOOLS_LOG_LEVEL="DEBUG" + + Lambda service->>Lambda function: Invoke (event) + Lambda function->>Lambda function: Calls handler + Lambda function->>Application Logger: logger.error("Something happened") + Lambda function-->>Application Logger: logger.debug("Something happened") + Lambda function-->>Application Logger: logger.info("Something happened") + Lambda service--xLambda service: DROP INFO and DEBUG logs + Lambda service->>CloudWatch Logs: Ingest error logs +``` + +### Priority of log level settings in Powertools for AWS Lambda + +We prioritise log level settings in this order: + +1. `AWS_LAMBDA_LOG_LEVEL` environment variable +2. `POWERTOOLS_LOG_LEVEL` environment variable +3. level defined in the `log4j2.xml` or `logback.xml` files + +If you set `POWERTOOLS_LOG_LEVEL` lower than ALC, we will emit a warning informing you that your messages will be discarded by Lambda. + +!!! note + With ALC enabled, we are unable to increase the minimum log level below the `AWS_LAMBDA_LOG_LEVEL` environment variable value, see [AWS Lambda service documentation](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-log-level){target="_blank"} for more details. + +## Basic Usage + +You can use Powertools for AWS Lambda Logging with either the `@Logging` annotation or the functional API: + +=== "@Logging annotation" + + ```java hl_lines="8 10 12 14" + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.Logging; + // ... other imports + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.info("Collecting payment"); + // ... + LOGGER.debug("order={}, amount={}", order.getId(), order.getAmount()); + // ... + } + } + ``` + +=== "Functional API" + + ```java hl_lines="8 11 12 14 17" + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + // ... other imports + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + LOGGER.info("Collecting payment"); + // ... + LOGGER.debug("order={}, amount={}", order.getId(), order.getAmount()); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` + +## Standard structured keys + +Your logs will always include the following keys in your structured logging: + +| Key | Type | Example | Description | +|-------------------|--------|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| +| **timestamp** | String | "2023-12-01T14:49:19.293Z" | Timestamp of actual log statement, by default uses default AWS Lambda timezone (UTC) | +| **level** | String | "INFO" | Logging level (any level supported by _SLF4J_ (i.e. `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`) | +| **service** | String | "payment" | Service name defined, by default `service_undefined` | +| **sampling_rate** | float | 0.1 | Debug logging sampling rate in percentage e.g. 10% in this case (logged if not 0) | +| **message** | String | "Collecting payment" | Log statement value. Unserializable JSON values will be casted to string | +| **xray_trace_id** | String | "1-5759e988-bd862e3fe1be46a994272793" | X-Ray Trace ID when [Tracing is enabled](https://docs.aws.amazon.com/lambda/latest/dg/services-xray.html){target="_blank"} | +| **error** | Map | `{ "name": "InvalidAmountException", "message": "Amount must be superior to 0", "stack": "at..." }` | Eventual exception (e.g. when doing `logger.error("Error", new InvalidAmountException("Amount must be superior to 0"));`) | + +???+ note + If you emit a log message with a key that matches one of the [standard structured keys](#standard-structured-keys) or one of the [additional structured keys](#additional-structured-keys), the Logger will log a warning message and ignore the key. + +## Additional structured keys + +### Logging Lambda context information +The following keys will also be added to all your structured logs (unless [configured otherwise](#more-customization_1)): + +| Key | Type | Example | Description | +|--------------------------|---------|----------------------------------------------------------------------------------------|------------------------------------| +| **cold_start** | Boolean | false | ColdStart value | +| **function_name** | String | "example-PaymentFunction-1P1Z6B39FLU73" | Name of the function | +| **function_version** | String | "12" | Version of the function | +| **function_memory_size** | String | "512" | Memory configure for the function | +| **function_arn** | String | "arn:aws:lambda:eu-west-1:012345678910:function:example-PaymentFunction-1P1Z6B39FLU73" | ARN of the function | +| **function_request_id** | String | "899856cb-83d1-40d7-8611-9e78f15f32f4"" | AWS Request ID from lambda context | + +### Logging additional keys + +#### Logging a correlation ID + +You can set a correlation ID using the `correlationIdPath` parameter by passing a [JMESPath expression](https://jmespath.org/tutorial.html){target="_blank"}, +including our custom [JMESPath Functions](../utilities/serialization.md#built-in-functions). + +=== "@Logging annotation" + + ```java hl_lines="5" + public class AppCorrelationIdPath implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationIdPath.class); + + @Logging(correlationIdPath = "headers.my_request_id_header") + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + LOGGER.info("Collecting payment") + // ... + } + } + ``` + +=== "Functional API" + + ```java hl_lines="6" + public class AppCorrelationIdPath implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationIdPath.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, "headers.my_request_id_header", input, () -> { + // ... + LOGGER.info("Collecting payment"); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` +=== "Example HTTP Event" + + ```json hl_lines="3" + { + "headers": { + "my_request_id_header": "correlation_id_value" + } + } + ``` + +=== "CloudWatch Logs" + + ```json hl_lines="6" + { + "level": "INFO", + "message": "Collecting payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "service": "payment", + "correlation_id": "correlation_id_value" + } + ``` + +**Known correlation IDs** + +To ease routine tasks like extracting correlation ID from popular event sources, +we provide [built-in JMESPath expressions](#built-in-correlation-id-expressions). + +=== "@Logging annotation" + + ```java hl_lines="1 7" + import software.amazon.lambda.powertools.logging.CorrelationIdPaths; + + public class AppCorrelationId implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationId.class); + + @Logging(correlationIdPath = CorrelationIdPaths.API_GATEWAY_REST) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + LOGGER.info("Collecting payment") + // ... + } + } + ``` + +=== "Functional API" + + ```java hl_lines="1 8" + import software.amazon.lambda.powertools.logging.CorrelationIdPaths; + + public class AppCorrelationId implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppCorrelationId.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, CorrelationIdPaths.API_GATEWAY_REST, input, () -> { + // ... + LOGGER.info("Collecting payment"); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` + +=== "Example Event" + + ```json hl_lines="3" + { + "requestContext": { + "requestId": "correlation_id_value" + } + } + ``` + +=== "Example CloudWatch Logs" + + ```json hl_lines="6" + { + "level": "INFO", + "message": "Collecting payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "service": "payment", + "correlation_id": "correlation_id_value" + } + ``` + +#### Custom keys + +**Using StructuredArguments** + +To append additional keys in your logs, you can use the `StructuredArguments` class: + +=== "PaymentFunction.java" + + ```java hl_lines="1 2 11 17" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entries; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + LOGGER.info("Collecting payment", entry("orderId", order.getId())); + + // ... + Map<String, String> customKeys = new HashMap<>(); + customKeys.put("paymentId", payment.getId()); + customKeys.put("amount", payment.getAmount); + LOGGER.info("Payment successful", entries(customKeys)); + } + } + ``` + +=== "CloudWatch Logs for PaymentFunction" + + ```json hl_lines="7 16-18" + { + "level": "INFO", + "message": "Collecting payment", + "service": "payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "orderId": "41376" + } + ... + { + "level": "INFO", + "message": "Payment successful", + "service": "payment", + "timestamp": "2023-12-01T14:49:20.118Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "orderId": "41376", + "paymentId": "3245", + "amount": 345.99 + } + ``` + +`StructuredArguments` provides several options: + + - `entry` to add one key and value into the log structure. Note that value can be any object type. + - `entries` to add multiple keys and values (from a Map) into the log structure. Note that values can be any object type. + - `json` to add a key and raw json (string) as value into the log structure. + - `array` to add one key and multiple values into the log structure. Note that values can be any object type. + +=== "OrderFunction.java" + + ```java hl_lines="1 2 11 17" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.array; + + public class OrderFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + LOGGER.info("Processing order", entry("order", order), array("products", productList)); + // ... + } + } + ``` + +=== "CloudWatch Logs for OrderFunction" + + ```json hl_lines="7 13" + { + "level": "INFO", + "message": "Processing order", + "service": "payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "order": { + "orderId": 23542, + "amount": 459.99, + "date": "2023-12-01T14:49:19.018Z", + "customerId": 328496 + }, + "products": [ + { + "productId": 764330, + "name": "product1", + "quantity": 1, + "price": 300 + }, + { + "productId": 798034, + "name": "product42", + "quantity": 1, + "price": 159.99 + } + ] + } + ``` + +???+ tip "Use arguments without log placeholders" + As shown in the example above, you can use arguments (with `StructuredArguments`) without placeholders (`{}`) in the message. + If you add the placeholders, the arguments will be logged both as an additional field and also as a string in the log message, using the `toString()` method. + + === "Function1.java" + + ```java + LOGGER.info("Processing {}", entry("order", order)); + ``` + + === "Order.java" + + ```java hl_lines="5" + public class Order { + // ... + + @Override + public String toString() { + return "Order{" + + "orderId=" + id + + ", amount=" + amount + + ", date='" + date + '\'' + + ", customerId=" + customerId + + '}'; + } + } + ``` + + === "CloudWatch Logs Function1" + + ```json hl_lines="3 7" + { + "level": "INFO", + "message": "Processing order=Order{orderId=23542, amount=459.99, date='2023-12-01T14:49:19.018Z', customerId=328496}", + "service": "payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "order": { + "orderId": 23542, + "amount": 459.99, + "date": "2023-12-01T14:49:19.018Z", + "customerId": 328496 + } + } + ``` + + You can also combine structured arguments with non structured ones. For example: + + === "Function2.java" + ```java + LOGGER.info("Processing order {}", order.getOrderId(), entry("order", order)); + ``` + + === "CloudWatch Logs Function2" + ```json + { + "level": "INFO", + "message": "Processing order 23542", + "service": "payment", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "order": { + "orderId": 23542, + "amount": 459.99, + "date": "2023-12-01T14:49:19.018Z", + "customerId": 328496 + } + } + ``` + +???+ warning "Do not use reserved keys in `StructuredArguments`" + If the key name of your structured argument matches any of the [standard structured keys](#standard-structured-keys) or any of the [additional structured keys](#additional-structured-keys), the Logger will log a warning message and ignore the key. This is to protect you from accidentally overwriting reserved keys such as the log level or Lambda context information. + +**Using MDC** + +Mapped Diagnostic Context (MDC) is essentially a Key-Value store. It is supported by the [SLF4J API](https://www.slf4j.org/manual.html#mdc){target="_blank"}, +[logback](https://logback.qos.ch/manual/mdc.html){target="_blank"} and log4j (known as [ThreadContext](https://logging.apache.org/log4j/2.x/manual/thread-context.html){target="_blank"}). You can use the following standard: + +`MDC.put("key", "value");` + +???+ warning "Custom keys stored in the MDC are persisted across warm invocations" + Always set additional keys as part of your handler method to ensure they have the latest value, or explicitly clear them with [`clearState=true`](#clearing-state). + +???+ warning "Do not add reserved keys to MDC" + Avoid adding any of the keys listed in [standard structured keys](#standard-structured-keys) and [additional structured keys](#additional-structured-keys) to your MDC. This may cause unindented behavior and will overwrite the context set by the Logger. Unlike with `StructuredArguments`, the Logger will **not** ignore reserved keys set via MDC. + + +### Removing additional keys + +You can remove additional keys added with the MDC using `MDC.remove("key")`. + +#### Clearing state + +Logger is commonly initialized in the global scope. Due to [Lambda Execution Context reuse](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html){target="_blank"}, +this means that custom keys, added with the MDC can be persisted across invocations. You can clear state using `clearState=true` on the `@Logging` annotation, or use the functional API which handles cleanup automatically. + +=== "@Logging annotation" + + ```java hl_lines="5 8" + public class CreditCardFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(CreditCardFunction.class); + + @Logging(clearState = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + MDC.put("cardNumber", card.getId()); + LOGGER.info("Updating card information"); + // ... + } + } + ``` + +=== "#1 Request" + + ```json hl_lines="7" + { + "level": "INFO", + "message": "Updating card information", + "service": "card", + "timestamp": "2023-12-01T14:49:19.293Z", + "xray_trace_id": "1-6569f266-4b0c7f97280dcd8428d3c9b5", + "cardNumber": "6818 8419 9395 5322" + } + ``` + +=== "#2 Request" + + ```json hl_lines="7" + { + "level": "INFO", + "message": "Updating card information", + "service": "card", + "timestamp": "2023-12-01T14:49:20.213Z", + "xray_trace_id": "2-7a518f43-5e9d2b1f6cfd5e8b3a4e1f9c", + "cardNumber": "7201 6897 6685 3285" + } + ``` + +`clearState` is based on `MDC.clear()`. State clearing is automatically done at the end of the execution of the handler if set to `true`. + +???+ tip + When using the functional API with `PowertoolsLogging.withLogging()`, state is automatically cleared at the end of execution, so you don't need to manage it manually. + + +## Logging incoming event + +When debugging in non-production environments, you can log the incoming event using the `@Logging` annotation with the `logEvent` parameter, via the `POWERTOOLS_LOGGER_LOG_EVENT` environment variable, or manually with the functional API. + +???+ warning + This is disabled by default to prevent sensitive info being logged. + +=== "@Logging annotation" + + ```java hl_lines="5" + public class AppLogEvent implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogEvent.class); + + @Logging(logEvent = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + } + } + ``` + +=== "Functional API" + + ```java hl_lines="1 9" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + + public class AppLogEvent implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogEvent.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + LOGGER.info("Handler Event", entry("event", input)); + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` + +???+ note + If you use this on a RequestStreamHandler, the SDK must duplicate input streams in order to log them when used together with the `@Logging` annotation. + +## Logging handler response + +When debugging in non-production environments, you can log the response using the `@Logging` annotation with the `logResponse` parameter, via the `POWERTOOLS_LOGGER_LOG_RESPONSE` environment variable, or manually with the functional API. + +???+ warning + This is disabled by default to prevent sensitive info being logged. + +=== "@Logging annotation" + + ```java hl_lines="5" + public class AppLogResponse implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); + + @Logging(logResponse = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + } + } + ``` + +=== "Functional API" + + ```java hl_lines="1 11" + import static software.amazon.lambda.powertools.logging.argument.StructuredArguments.entry; + + public class AppLogResponse implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogResponse.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + // ... + APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent().withStatusCode(200); + LOGGER.info("Handler Response", entry("response", response)); + return response; + }); + } + } + ``` + +???+ note + If you use this on a RequestStreamHandler, Powertools must duplicate output streams in order to log them when used together with the `@Logging` annotation. + +## Logging handler uncaught exception +By default, AWS Lambda logs any uncaught exception that might happen in the handler. However, this log is not structured +and does not contain any additional context. When using the `@Logging` annotation, you can enable structured exception logging +with `logError` param or via `POWERTOOLS_LOGGER_LOG_ERROR` env var. + +???+ warning + This is disabled by default to prevent double logging. + +???+ note + This feature is only available when using the `@Logging` annotation. When using the functional API, you must catch and log exceptions manually using try-catch blocks. + +=== "@Logging annotation" + + ```java hl_lines="5" + public class AppLogError implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogError.class); + + @Logging(logError = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // ... + } + } + ``` + +=== "Functional API" + + ```java hl_lines="1 9 12-13" + import org.slf4j.MarkerFactory; + + public class AppLogError implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppLogError.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + try { + // ... + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } catch (Exception e) { + LOGGER.error(MarkerFactory.getMarker("FATAL"), "Exception in Lambda Handler", e); + throw e; + } + }); + } + } + ``` + +## Advanced + +### Buffering logs + +Log buffering enables you to buffer logs for a specific request or invocation. Enable log buffering by configuring the `BufferingAppender` in your logging configuration. You can buffer logs at the `WARNING`, `INFO` or `DEBUG` level, and flush them automatically on error or manually as needed. + +!!! tip "This is useful when you want to reduce the number of log messages emitted while still having detailed logs when needed, such as when troubleshooting issues." + +=== "log4j2.xml" + + ```xml hl_lines="7-12 16 19" + <?xml version="1.0" encoding="UTF-8"?> + <Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + <BufferingAppender name="BufferedJsonAppender" + maxBytes="20480" + bufferAtVerbosity="DEBUG" + flushOnErrorLog="true"> + <AppenderRef ref="JsonAppender"/> + </BufferingAppender> + </Appenders> + <Loggers> + <Logger name="com.example" level="debug" additivity="false"> + <AppenderRef ref="BufferedJsonAppender"/> + </Logger> + <Root level="debug"> + <AppenderRef ref="BufferedJsonAppender"/> + </Root> + </Loggers> + </Configuration> + ``` + +=== "logback.xml" + + ```xml hl_lines="6-11 13 16" + <?xml version="1.0" encoding="UTF-8"?> + <configuration> + <appender name="JsonAppender" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder" /> + </appender> + <appender name="BufferedJsonAppender" class="software.amazon.lambda.powertools.logging.logback.BufferingAppender"> + <maxBytes>20480</maxBytes> + <bufferAtVerbosity>DEBUG</bufferAtVerbosity> + <flushOnErrorLog>true</flushOnErrorLog> + <appender-ref ref="JsonAppender" /> + </appender> + <logger name="com.example" level="DEBUG" additivity="false"> + <appender-ref ref="BufferedJsonAppender" /> + </logger> + <root level="DEBUG"> + <appender-ref ref="BufferedJsonAppender" /> + </root> + </configuration> + ``` + +=== "PaymentFunction.java" + + ```java hl_lines="8 12" + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import software.amazon.lambda.powertools.logging.Logging; + // ... other imports + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("a debug log"); // this is buffered + LOGGER.info("an info log"); // this is not buffered + + // do stuff + + // Buffer is automatically cleared at the end of the method by @Logging annotation + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +#### Configuring the buffer + +When configuring log buffering, you have options to fine-tune how logs are captured, stored, and emitted. You can configure the following parameters in the `BufferingAppender` configuration: + +| Parameter | Description | Configuration | +| --------------------- | ----------------------------------------------- | ---------------------------- | +| `maxBytes` | Maximum size of the log buffer in bytes | `int` (default: 20480 bytes) | +| `bufferAtVerbosity` | Minimum log level to buffer | `DEBUG` (default), `INFO`, `WARNING` | +| `flushOnErrorLog` | Automatically flush buffer when `ERROR` or `FATAL` level logs are emitted | `true` (default), `false` | + +!!! warning "Logger Level Configuration" + To use log buffering effectively, you must set your logger levels to the same level as `bufferAtVerbosity` or more verbose for the logging framework to capture and forward logs to the `BufferingAppender`. For example, if you want to buffer `DEBUG` level logs and emit `INFO`+ level logs directly, you must: + + - Set your logger levels to `DEBUG` in your log4j2.xml or logback.xml configuration + - Set `POWERTOOLS_LOG_LEVEL=DEBUG` if using the environment variable (see [Log level](#log-level) section for more details) + + If you want to sample `INFO` and `WARNING` logs but not `DEBUG` logs, set your log level to `INFO` and `bufferAtVerbosity` to `WARNING`. This allows you to define the lower and upper bounds for buffering. All logs with a more severe level than `bufferAtVerbosity` will be emitted directly. + +=== "log4j2.xml - Buffer at WARNING level" + + ```xml hl_lines="9 14-15 18" + <?xml version="1.0" encoding="UTF-8"?> + <Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + <BufferingAppender name="BufferedJsonAppender" + maxBytes="20480" + bufferAtVerbosity="WARNING"> + <AppenderRef ref="JsonAppender"/> + </BufferingAppender> + </Appenders> + <Loggers> + <!-- Intentionally set to DEBUG to forward all logs to BufferingAppender --> + <Logger name="com.example" level="debug" additivity="false"> + <AppenderRef ref="BufferedJsonAppender"/> + </Logger> + <Root level="debug"> + <AppenderRef ref="BufferedJsonAppender"/> + </Root> + </Loggers> + </Configuration> + ``` + +=== "PaymentFunction.java - Buffer at WARNING level" + + ```java hl_lines="7-9 13" + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.warn("a warning log"); // this is buffered + LOGGER.info("an info log"); // this is buffered + LOGGER.debug("a debug log"); // this is buffered + + // do stuff + + // Buffer is automatically cleared at the end of the method by @Logging annotation + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +=== "log4j2.xml - Disable flush on error" + + ```xml hl_lines="9" + <?xml version="1.0" encoding="UTF-8"?> + <Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> + <BufferingAppender name="BufferedJsonAppender" + maxBytes="20480" + flushOnErrorLog="false"> + <AppenderRef ref="JsonAppender"/> + </BufferingAppender> + </Appenders> + <Loggers> + <Logger name="com.example" level="debug" additivity="false"> + <AppenderRef ref="BufferedJsonAppender"/> + </Logger> + <Root level="debug"> + <AppenderRef ref="BufferedJsonAppender"/> + </Root> + </Loggers> + </Configuration> + ``` + +=== "PaymentFunction.java - Manual flush required" + + ```java hl_lines="1 16 19-20" + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("a debug log"); // this is buffered + + // do stuff + + try { + throw new RuntimeException("Something went wrong"); + } catch (RuntimeException error) { + LOGGER.error("An error occurred", error); // Logs won't be flushed here + } + + // Manually flush buffered logs + PowertoolsLogging.flushBuffer(); + + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +!!! note "Disabling `flushOnErrorLog` will not flush the buffer when logging an error. This is useful when you want to control when the buffer is flushed by calling the flush method manually." + +#### Manual buffer control + +You can manually control the log buffer using the `PowertoolsLogging` utility class, which provides a backend-independent API that works with both Log4j2 and Logback: + +=== "Manual flush" + + ```java hl_lines="1 12-13" + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("Processing payment"); // this is buffered + LOGGER.info("Payment validation complete"); // this is buffered + + // Manually flush all buffered logs + PowertoolsLogging.flushBuffer(); + + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +=== "Manual clear" + + ```java hl_lines="1 12-13" + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("Processing payment"); // this is buffered + LOGGER.info("Payment validation complete"); // this is buffered + + // Manually clear buffered logs without outputting them + PowertoolsLogging.clearBuffer(); + + return new APIGatewayProxyResponseEvent().withStatusCode(200); + } + } + ``` + +**Available methods:** + +- `#!java PowertoolsLogging.flushBuffer()` - Outputs all buffered logs and clears the buffer +- `#!java PowertoolsLogging.clearBuffer()` - Discards all buffered logs without outputting them + +#### Flushing on exceptions + +Use the `@Logging` annotation to automatically flush buffered logs when an uncaught exception is raised in your Lambda function. This is enabled by default (`flushBufferOnUncaughtError = true`), but you can explicitly configure it if needed. + +???+ warning + This feature is only available when using the `@Logging` annotation. When using the functional API, you must manually flush the buffer in exception handlers. + +=== "@Logging annotation" + + ```java hl_lines="5 11" + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + @Logging(flushBufferOnUncaughtError = true) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + LOGGER.debug("a debug log"); // this is buffered + + // do stuff + + throw new RuntimeException("Something went wrong"); // Logs will be flushed here + } + } + ``` + +=== "Functional API" + + ```java hl_lines="14" + import software.amazon.lambda.powertools.logging.PowertoolsLogging; + + public class PaymentFunction implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(PaymentFunction.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, () -> { + try { + LOGGER.debug("a debug log"); // this is buffered + // do stuff + throw new RuntimeException("Something went wrong"); + } catch (Exception e) { + PowertoolsLogging.flushBuffer(); // Manually flush buffered logs + throw e; + } + }); + } + } + ``` + +#### Buffering workflows + +##### Manual flush + +<center> +```mermaid +sequenceDiagram + participant Client + participant Lambda + participant Logger + participant CloudWatch + Client->>Lambda: Invoke Lambda + Lambda->>Logger: Initialize with DEBUG level buffering + Logger-->>Lambda: Logger buffer ready + Lambda->>Logger: logger.debug("First debug log") + Logger-->>Logger: Buffer first debug log + Lambda->>Logger: logger.info("Info log") + Logger->>CloudWatch: Directly log info message + Lambda->>Logger: logger.debug("Second debug log") + Logger-->>Logger: Buffer second debug log + Lambda->>Logger: Manual flush call + Logger->>CloudWatch: Emit buffered logs to stdout + Lambda->>Client: Return execution result +``` +<i>Flushing buffer manually</i> +</center> + +##### Flushing when logging an error + +<center> +```mermaid +sequenceDiagram + participant Client + participant Lambda + participant Logger + participant CloudWatch + Client->>Lambda: Invoke Lambda + Lambda->>Logger: Initialize with DEBUG level buffering + Logger-->>Lambda: Logger buffer ready + Lambda->>Logger: logger.debug("First log") + Logger-->>Logger: Buffer first debug log + Lambda->>Logger: logger.debug("Second log") + Logger-->>Logger: Buffer second debug log + Lambda->>Logger: logger.debug("Third log") + Logger-->>Logger: Buffer third debug log + Lambda->>Lambda: Exception occurs + Lambda->>Logger: logger.error("Error details") + Logger->>CloudWatch: Emit error log + Logger->>CloudWatch: Emit buffered debug logs + Lambda->>Client: Raise exception +``` +<i>Flushing buffer when an error happens</i> +</center> + +##### Flushing on exception + +This works when using the `@Logging` annotation which automatically clears the buffer at the end of method execution. + +<center> +```mermaid +sequenceDiagram + participant Client + participant Lambda + participant Logger + participant CloudWatch + Client->>Lambda: Invoke Lambda + Lambda->>Logger: Using @Logging annotation + Logger-->>Lambda: Logger context injected + Lambda->>Logger: logger.debug("First log") + Logger-->>Logger: Buffer first debug log + Lambda->>Logger: logger.debug("Second log") + Logger-->>Logger: Buffer second debug log + Lambda->>Lambda: Uncaught Exception + Lambda->>CloudWatch: Automatically emit buffered debug logs + Lambda->>Client: Raise uncaught exception +``` +<i>Flushing buffer when an uncaught exception happens</i> +</center> + +#### Buffering FAQs + +1. **Does the buffer persist across Lambda invocations?** No, each Lambda invocation has its own buffer. The buffer is initialized when the Lambda function is invoked and is cleared after the function execution completes or when flushed manually. +2. **Are my logs buffered during cold starts (INIT phase)?** No, we never buffer logs during cold starts. This is because we want to ensure that logs emitted during this phase are always available for debugging and monitoring purposes. The buffer is only used during the execution of the Lambda function. +3. **How can I prevent log buffering from consuming excessive memory?** You can limit the size of the buffer by setting the `maxBytes` option in the `BufferingAppender` configuration. This will ensure that the buffer does not grow indefinitely. +4. **What happens if the log buffer reaches its maximum size?** Older logs are removed from the buffer to make room for new logs. This means that if the buffer is full, you may lose some logs if they are not flushed before the buffer reaches its maximum size. When this happens, we emit a warning when flushing the buffer to indicate that some logs have been dropped. +5. **How is the log size of a log line calculated?** The log size is calculated based on the size of the log line in bytes. This includes the size of the log message, any exception (if present), the log line location, additional keys, and the timestamp. +6. **What timestamp is used when I flush the logs?** The timestamp is the original time when the log record was created. If you create a log record at 11:00:10 and flush it at 11:00:25, the log line will retain its original timestamp of 11:00:10. +7. **What happens if I try to add a log line that is bigger than max buffer size?** The log will be emitted directly to standard output and not buffered. When this happens, we emit a warning to indicate that the log line was too big to be buffered. +8. **What happens if Lambda times out without flushing the buffer?** Logs that are still in the buffer will be lost. +9. **How does the `BufferingAppender` work with different appenders?** The `BufferingAppender` is designed to wrap arbitrary appenders, providing maximum flexibility. You can wrap console appenders, file appenders, or any custom appenders with buffering functionality. + +## Sampling debug logs + +You can dynamically set a percentage of your logs to`DEBUG` level to be included in the logger output, regardless of configured log level, using the`POWERTOOLS_LOGGER_SAMPLE_RATE` environment variable, +via the `samplingRate` attribute on the `@Logging` annotation, or as a parameter in the functional API. + +!!! info + Configuration via environment variable is given precedence over sampling rate configuration, provided it's in valid value range. + +=== "@Logging annotation" + + ```java hl_lines="5" + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + @Logging(samplingRate = 0.5) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + // will eventually be logged based on the sampling rate + LOGGER.debug("Handle payment"); + } + } + ``` + +=== "Functional API" + + ```java hl_lines="6" + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + return PowertoolsLogging.withLogging(context, 0.5, () -> { + // will eventually be logged based on the sampling rate + LOGGER.debug("Handle payment"); + return new APIGatewayProxyResponseEvent().withStatusCode(200); + }); + } + } + ``` + +=== "Sampling via environment variable" + + ```yaml hl_lines="8" + Resources: + PaymentFunction: + Type: AWS::Serverless::Function + Properties: + ... + Environment: + Variables: + POWERTOOLS_LOGGER_SAMPLE_RATE: 0.5 + + ``` + +## Built-in Correlation ID expressions + +You can use any of the following built-in JMESPath expressions with the `@Logging` annotation or the functional API: + +???+ note "Note: Any object key named with `-` must be escaped" + For example, **`request.headers."x-amzn-trace-id"`**. + +| Name | Expression | Description | +|-------------------------------|-------------------------------------|---------------------------------| +| **API_GATEWAY_REST** | `"requestContext.requestId"` | API Gateway REST API request ID | +| **API_GATEWAY_HTTP** | `"requestContext.requestId"` | API Gateway HTTP API request ID | +| **APPSYNC_RESOLVER** | `request.headers."x-amzn-trace-id"` | AppSync X-Ray Trace ID | +| **APPLICATION_LOAD_BALANCER** | `headers."x-amzn-trace-id"` | ALB X-Ray Trace ID | +| **EVENT_BRIDGE** | `"id"` | EventBridge Event ID | + + +## Customising fields in logs + +Powertools for AWS Lambda comes with default json structure ([standard fields](#standard-structured-keys) & [lambda context fields](#logging-lambda-context-information)). + +You can go further and customize which fields you want to keep in your logs or not. The configuration varies according to the underlying logging library. + +### Log4j2 configuration +Log4j2 configuration is done in _log4j2.xml_ and leverages `JsonTemplateLayout`: + +```xml + <Console name="console" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> + </Console> +``` + +The `JsonTemplateLayout` is automatically configured with the provided template: + +??? example "LambdaJsonLayout.json" + ```json + { + "level": { + "$resolver": "level", + "field": "name" + }, + "message": { + "$resolver": "message" + }, + "error": { + "message": { + "$resolver": "exception", + "field": "message" + }, + "name": { + "$resolver": "exception", + "field": "className" + }, + "stack": { + "$resolver": "exception", + "field": "stackTrace", + "stackTrace": { + "stringified": true + } + } + }, + "cold_start": { + "$resolver": "powertools", + "field": "cold_start" + }, + "function_arn": { + "$resolver": "powertools", + "field": "function_arn" + }, + "function_memory_size": { + "$resolver": "powertools", + "field": "function_memory_size" + }, + "function_name": { + "$resolver": "powertools", + "field": "function_name" + }, + "function_request_id": { + "$resolver": "powertools", + "field": "function_request_id" + }, + "function_version": { + "$resolver": "powertools", + "field": "function_version" + }, + "sampling_rate": { + "$resolver": "powertools", + "field": "sampling_rate" + }, + "service": { + "$resolver": "powertools", + "field": "service" + }, + "timestamp": { + "$resolver": "timestamp", + "pattern": { + "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + } + }, + "xray_trace_id": { + "$resolver": "powertools", + "field": "xray_trace_id" + }, + "correlation_id": { + "$resolver": "powertools", + "field": "correlation_id" + }, + "": { + "$resolver": "powertools" + } + } + ``` + +You can create your own template and leverage the [PowertoolsResolver](https://github.com/aws-powertools/powertools-lambda-java/tree/v2/powertools-logging/powertools-logging-log4j/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/PowertoolsResolver.java){target="_blank"} +and any other resolver to log the desired fields with the desired format. Some examples of customization are given below: + +#### Customising date format + +Utility by default emits `timestamp` field in the logs in format `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` and in system default timezone. +If you need to customize format and timezone, you can update your template.json or by configuring `log4j2.component.properties` as shown in examples below: + +=== "my-custom-template.json" + + ```json + { + "timestamp": { + "$resolver": "timestamp", + "pattern": { + "format": "yyyy-MM-dd HH:mm:ss", + "timeZone": "Europe/Paris", + } + }, + } + ``` + +=== "log4j2.component.properties" + + ```properties hl_lines="1 2" + log4j.layout.jsonTemplate.timestampFormatPattern=yyyy-MM-dd'T'HH:mm:ss.SSSZz + log4j.layout.jsonTemplate.timeZone=Europe/Oslo + ``` + +See [`TimestampResolver` documentation](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#event-template-resolver-timestamp){target="_blank"} for more details. + +???+ warning "Lambda Advanced Logging Controls date format" + When using the Lambda ALC, you must have a date format compatible with the [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) + +#### More customization +You can also customize how [exceptions are logged](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#event-template-resolver-exception){target="_blank"}, and much more. +See the [JSON Layout template documentation](https://logging.apache.org/log4j/2.x/manual/json-template-layout.html){target="_blank"} for more details. + +### Logback configuration +Logback configuration is done in _logback.xml_ and the `LambdaJsonEncoder`: + +```xml + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + </encoder> + </appender> +``` + +The `LambdaJsonEncoder` can be customized in different ways: + +#### Customising date format +Utility by default emits `timestamp` field in the logs in format `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` and in system default timezone. +If you need to customize format and timezone, you can change use the following: + +```xml + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + <timestampFormat>yyyy-MM-dd HH:mm:ss</timestampFormat> + <timestampFormatTimezoneId>Europe/Paris</timestampFormatTimezoneId> + </encoder> +``` + +#### More customization + +- You can use a standard `ThrowableHandlingConverter` to customize the exception format (default is no converter). Example: + +```xml + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter"> + <maxDepthPerThrowable>30</maxDepthPerThrowable> + <maxLength>2048</maxLength> + <shortenedClassNameLength>20</shortenedClassNameLength> + <exclude>sun\.reflect\..*\.invoke.*</exclude> + <exclude>net\.sf\.cglib\.proxy\.MethodProxy\.invoke</exclude> + <evaluator class="myorg.MyCustomEvaluator"/> + <rootCauseFirst>true</rootCauseFirst> + <inlineHash>true</inlineHash> + </throwableConverter> + </encoder> +``` + +- You can choose to add information about threads (default is `false`): + +```xml + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + <includeThreadInfo>true</includeThreadInfo> + </encoder> +``` + +- You can even choose to remove Powertools information from the logs like function name, arn: + +```xml + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaJsonEncoder"> + <includePowertoolsInfo>false</includePowertoolsInfo> + </encoder> +``` + +## Elastic Common Schema (ECS) Support + +Utility also supports [Elastic Common Schema(ECS)](https://www.elastic.co/guide/en/ecs/current/ecs-reference.html){target="_blank"} format. +The field emitted in logs will follow specs from [ECS](https://www.elastic.co/guide/en/ecs/current/ecs-reference.html){target="_blank"} together with field captured by utility as mentioned [above](#standard-structured-keys). + +### Log4j2 configuration + +Use `LambdaEcsLayout.json` as `eventTemplateUri` when configuring `JsonTemplateLayout`. + +=== "log4j2.xml" + + ```xml hl_lines="5" + <?xml version="1.0" encoding="UTF-8"?> + <Configuration> + <Appenders> + <Console name="JsonAppender" target="SYSTEM_OUT"> + <JsonTemplateLayout eventTemplateUri="classpath:LambdaEcsLayout.json" /> + </Console> + </Appenders> + <Loggers> + <Root level="info"> + <AppenderRef ref="JsonAppender"/> + </Root> + </Loggers> + </Configuration> + ``` + +### Logback configuration + +Use the `LambdaEcsEncoder` rather than the `LambdaJsonEncoder` when configuring the appender: + +=== "logback.xml" + + ```xml hl_lines="3" + <configuration> + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="software.amazon.lambda.powertools.logging.logback.LambdaEcsEncoder"> + </encoder> + </appender> + <root level="INFO"> + <appender-ref ref="console" /> + </root> + </configuration> + ``` diff --git a/docs/core/metrics.md b/docs/core/metrics.md new file mode 100644 index 000000000..e7f7bd87f --- /dev/null +++ b/docs/core/metrics.md @@ -0,0 +1,691 @@ +--- +title: Metrics +description: Core utility +--- + +Metrics creates custom metrics asynchronously by logging metrics to standard output following [Amazon CloudWatch Embedded Metric Format (EMF)](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html). + +These metrics can be visualized through [Amazon CloudWatch Console](https://aws.amazon.com/cloudwatch/). + +## Key features + +- Aggregate up to 100 metrics using a single [CloudWatch EMF](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html){target="\_blank"} object (large JSON blob) +- Validating your metrics against common metric definitions mistakes (for example, metric unit, values, max dimensions, max metrics) +- Metrics are created asynchronously by the CloudWatch service. You do not need any custom stacks, and there is no impact to Lambda function latency +- Support for creating one off metrics with different dimensions +- GraalVM support + +## Terminologies + +If you're new to Amazon CloudWatch, there are some terminologies you must be aware of before using this utility: + +- **Namespace**. It's the highest level container that will group multiple metrics from multiple services for a given application, for example `ServerlessAirline`. +- **Dimensions**. Metrics metadata in key-value format. They help you slice and dice metrics visualization, for example `ColdStart` metric by `service`. +- **Metric**. It's the name of the metric, for example: `SuccessfulBooking` or `UpdatedBooking`. +- **Unit**. It's a value representing the unit of measure for the corresponding metric, for example: `Count` or `Seconds`. +- **Resolution**. It's a value representing the storage resolution for the corresponding metric. Metrics can be either `Standard` or `High` resolution. Read more about CloudWatch Periods [here](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Resolution_definition). + +Visit the AWS documentation for a complete explanation for [Amazon CloudWatch concepts](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html). + +<figure> + <img src="../../media/metrics_terminology.png" /> + <figcaption>Metric terminology, visually explained</figcaption> +</figure> + +## Install + +=== "Maven" + + ```xml hl_lines="3-7 16 18 24-27" + <dependencies> + ... + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + ... + </dependencies> + ... + <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> + <build> + <plugins> + ... + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14</version> + <configuration> + <source>11</source> <!-- or higher --> + <target>11</target> <!-- or higher --> + <complianceLevel>11</complianceLevel> <!-- or higher --> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + ... + </plugins> + </build> + ``` + +=== "Gradle" + + ```groovy hl_lines="3 11 12" + plugins { + id 'java' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach + } + + repositories { + mavenCentral() + } + + dependencies { + aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' // Use this instead of 'aspect' when using the functional approach + } + + sourceCompatibility = 11 + targetCompatibility = 11 + ``` + +## Getting started + +Metrics has three global settings that will be used across all metrics emitted. Use your application or main service as the metric namespace to easily group all metrics: + +| Setting | Description | Environment variable | Decorator parameter | +| -------------------- | ------------------------------------------------------------------------------- | ---------------------------------- | ------------------- | +| **Metric namespace** | Logical container where all metrics will be placed e.g. `ServerlessAirline` | `POWERTOOLS_METRICS_NAMESPACE` | `namespace` | +| **Service** | Optionally, sets **service** metric dimension across all metrics e.g. `payment` | `POWERTOOLS_SERVICE_NAME` | `service` | +| **Function name** | Function name used as dimension for the cold start metric | `POWERTOOLS_METRICS_FUNCTION_NAME` | `functionName` | +| **Disable Metrics** | Optionally, disables all metrics flushing | `POWERTOOLS_METRICS_DISABLED` | N/A | + +!!! tip "Use your application or main service as the metric namespace to easily group all metrics" + +!!! info "`POWERTOOLS_METRICS_DISABLED` will not disable default metrics created by AWS services." + +### Order of Precedence of `Metrics` configuration + +The `Metrics` Singleton can be configured by three different interfaces. The following order of precedence applies: + +1. `@FlushMetrics` annotation +2. `MetricsBuilder` using Builder pattern (see [Advanced section](#usage-without-flushmetrics-annotation)) +3. Environment variables (recommended) + +For most use-cases, we recommend using Environment variables and only overwrite settings in code where needed using either the `@FlushMetrics` annotation or `MetricsBuilder` if the annotation cannot be used. + +=== "@FlushMetrics annotation" + + ```java hl_lines="9" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + // ... + } + } + ``` + +=== "MetricsBuilder" + + ```java hl_lines="7-8" + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsBuilder; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsBuilder.builder() + .withNamespace("ServerlessAirline") + .withService("payment") + .build(); + + @Override + public Object handleRequest(Object input, Context context) { + // ... + metrics.flush(); + } + } + ``` + +=== "Environment variables" + + ```yaml hl_lines="9 10" + Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function + Properties: + ... + Runtime: java11 + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: payment + POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline + ``` + +`Metrics` is implemented as a Singleton to keep track of your aggregate metrics in memory and make them accessible anywhere in your code. The `@FlushMetrics` annotation automatically flushes metrics at the end of the Lambda handler execution. Alternatively, you can use the functional approach and manually flush metrics using `metrics.flush()`. + +!!!info "Read more about the functional approach in the [advanced section below](#usage-without-flushmetrics-annotation)." + +## Creating metrics + +You can create metrics using `addMetric`, and manually create dimensions for all your aggregate metrics using `addDimension`. Anywhere in your code, you can access the current `Metrics` Singleton using the `MetricsFactory`. + +=== "MetricsEnabledHandler.java" + + ```java hl_lines="13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + metrics.addDimension("environment", "prod"); + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + // ... + } + } + ``` + +!!! tip "The `MetricUnit` enum facilitates finding a supported metric unit by CloudWatch." + +<!-- prettier-ignore-start --> +!!! note "Metrics dimensions" + CloudWatch EMF supports a max of 9 dimensions per metric. The Metrics utility will validate this limit when adding dimensions. +<!-- prettier-ignore-end --> + +### Adding high-resolution metrics + +You can create [high-resolution metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html#high-resolution-metrics) +passing a `#!java MetricResolution.HIGH` to the `addMetric` method. If nothing is passed `#!java MetricResolution.STANDARD` will be used. + +=== "HigResMetricsHandler.java" + + ```java hl_lines="3 13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.model.MetricResolution; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + // ... + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT, MetricResolution.HIGH); + } + } + ``` + +<!-- prettier-ignore-start --> +!!! info "When is it useful?" + High-resolution metrics are data with a granularity of one second and are very useful in several situations such as telemetry, time series, real-time incident management, and others. +<!-- prettier-ignore-end --> + +### Adding dimensions + +You can add dimensions to your metrics using the `addDimension` method. You can either pass key-value pairs or you can create higher cardinality dimensions using `DimensionSet`. + +=== "KeyValueDimensionHandler.java" + + ```java hl_lines="3 13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.model.MetricResolution; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + metrics.addDimension("Dimension", "Value"); + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + } + } + ``` + +=== "HighCardinalityDimensionHandler.java" + + ```java hl_lines="4 13-14" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.model.MetricResolution; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + + public class MetricsEnabledHandler implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + // You can add up to 30 dimensions in a single DimensionSet + metrics.addDimension(DimensionSet.of("Dimension1", "Value1", "Dimension2", "Value2")); + metrics.addMetric("SuccessfulBooking", 1, MetricUnit.COUNT); + } + } + ``` + +### Flushing metrics + +The `@FlushMetrics` annotation **validates**, **serializes**, and **flushes** all your metrics. During metrics validation, +if no metrics are provided no exception will be raised. If metrics are provided, and any of the following criteria are +not met, `IllegalStateException` or `IllegalArgumentException` exceptions will be raised. + +<!-- prettier-ignore-start --> +!!! tip "Metric validation" + - Maximum of 30 dimensions (`Service` default dimension counts as a regular dimension) + - Dimension keys and values cannot be null or empty + - Metric values must be valid numbers +<!-- prettier-ignore-end --> + +If you want to ensure that at least one metric is emitted, you can pass `raiseOnEmptyMetrics = true` to the `@FlushMetrics` annotation: + +=== "MetricsRaiseOnEmpty.java" + + ```java hl_lines="6" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + + public class MetricsRaiseOnEmpty implements RequestHandler<Object, Object> { + + @Override + @FlushMetrics(raiseOnEmptyMetrics = true) + public Object handleRequest(Object input, Context context) { + ... + } + } + ``` + +## Capturing cold start metric + +You can capture cold start metrics automatically with `@FlushMetrics` via the `captureColdStart` variable. + +=== "MetricsColdStart.java" + + ```java hl_lines="6" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + + public class MetricsColdStart implements RequestHandler<Object, Object> { + + @Override + @FlushMetrics(captureColdStart = true) + public Object handleRequest(Object input, Context context) { + ... + } + } + ``` + +If it's a cold start invocation, this feature will: + +- Create a separate EMF blob solely containing a metric named `ColdStart` +- Add `FunctionName` and `Service` dimensions + +This has the advantage of keeping cold start metric separate from your application metrics. + +You can also specify a custom function name to be used in the cold start metric: + +=== "MetricsColdStartCustomFunction.java" + + ```java hl_lines="6" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + + public class MetricsColdStartCustomFunction implements RequestHandler<Object, Object> { + + @Override + @FlushMetrics(captureColdStart = true, functionName = "CustomFunction") + public Object handleRequest(Object input, Context context) { + ... + } + } + ``` + +<!-- prettier-ignore-start --> +!!!tip "You can overwrite the default `Service` and `FunctionName` dimensions of the cold start metric" + Set `#!java @FlushMetrics(captureColdStart = false)` and use the `captureColdStartMetric` method manually: + + ```java hl_lines="6 8" + public class MetricsColdStartCustomFunction implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(captureColdStart = false) + public Object handleRequest(Object input, Context context) { + metrics.captureColdStartMetric(context, DimensionSet.of("CustomDimension", "CustomValue")); + ... + } + } + ``` +<!-- prettier-ignore-end --> + +## Advanced + +### Adding metadata + +You can use `addMetadata` for advanced use cases, where you want to add metadata as part of the serialized metrics object. + +<!-- prettier-ignore-start --> +!!! info + This will not be available during metrics visualization, use Dimensions for this purpose. + +!!! info + Adding metadata with a key that is the same as an existing metric will be ignored. +<!-- prettier-ignore-end --> + +=== "App.java" + + ```java hl_lines="13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + + public class App implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "booking-service") + public Object handleRequest(Object input, Context context) { + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); + metrics.addMetadata("booking_id", "1234567890"); // Needs to be added BEFORE flushing + ... + } + } + ``` + +This will be available in CloudWatch Logs to ease operations on high cardinal data. + +### Setting default dimensions + +By default, all metrics emitted via module captures `Service` as one of the default dimensions. This is either specified via `POWERTOOLS_SERVICE_NAME` environment variable or via `service` attribute on `Metrics` annotation. + +If you wish to set custom default dimensions, it can be done via `#!java metrics.setDefaultDimensions()`. You can also use the `MetricsBuilder` instead of the `MetricsFactory` to configure **and** retrieve the `Metrics` Singleton at the same time (see `MetricsBuilder.java` tab). + +=== "App.java" + + ```java hl_lines="13" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + + public class App implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + metrics.setDefaultDimensions(DimensionSet.of("CustomDimension", "booking", "Environment", "prod")); + ... + } + } + ``` + +=== "MetricsBuilder.java" + + ```java hl_lines="8-10" + import software.amazon.lambda.powertools.metrics.FlushMetrics; + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + + public class App implements RequestHandler<Object, Object> { + + private static final Metrics metrics = MetricsBuilder.builder() + .withDefaultDimensions(DimensionSet.of("CustomDimension", "booking", "Environment", "prod")) + .build(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + metrics.addMetric("CustomMetric1", 1, MetricUnit.COUNT); + ... + } + } + ``` + +<!-- prettier-ignore-start --> +!!!note + Overwriting the default dimensions will also overwrite the default `Service` dimension. If you wish to keep `Service` in your default dimensions, you need to add it manually. +<!-- prettier-ignore-end --> + +### Creating metrics with different configuration + +You can create metrics with different configurations e.g. different namespace and/or dimensions using `flushMetrics()`: + +=== "App.java" + + ```java hl_lines="12-22" + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsFactory; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; + + public class App implements RequestHandler<Object, Object> { + private static final Metrics metrics = MetricsFactory.getMetricsInstance(); + + @Override + @FlushMetrics(namespace = "ServerlessAirline", service = "payment") + public Object handleRequest(Object input, Context context) { + metrics.flushMetrics((customMetrics) -> { + customMetrics.addMetric("CustomMetric", 1, MetricUnit.COUNT); + // To optionally set a different namespace + customMetrics.setNamespace("CustomNamespace"); + // To optionally set different default dimensions + customMetrics.setDefaultDimensions(DimensionSet.of("CustomDefaultDimension", "value")); + // To optionally append additional dimensions to default dimensions + customMetrics.addDimension(DimensionSet.of("CustomDimension", "value")); + // To optionally add metadata + customMetrics.addMetadata("CustomMetadata", "value")); + }); + } + } + ``` + +<!-- prettier-ignore-start --> +!!! info + Generally, this would be an edge case since you [pay for unique metric](https://aws.amazon.com/cloudwatch/pricing). Keep the following formula in mind: + + **unique metric = (metric_name + dimension_name + dimension_value)** +<!-- prettier-ignore-end --> + +### Usage without `@FlushMetrics` annotation + +You can use the **functional API** approach (see [usage patterns](../usage-patterns.md#functional-approach)) to work with Metrics without the `@FlushMetrics` annotation. The `Metrics` Singleton provides all configuration options via `MetricsBuilder`. This approach eliminates the AspectJ runtime dependency and is useful if you work in an environment or with a framework that does not leverage the vanilla Lambda `handleRequest` method. + +!!!info "The environment variables for Service and Namespace configuration still apply but can be overwritten with `MetricsBuilder` if needed." + +The following example shows how to configure a custom `Metrics` Singleton using the Builder pattern. With the functional approach, you must manually flush metrics using `metrics.flush()`. + +=== "App.java" + + ```java hl_lines="7-12 19 24" + import software.amazon.lambda.powertools.metrics.Metrics; + import software.amazon.lambda.powertools.metrics.MetricsBuilder; + import software.amazon.lambda.powertools.metrics.model.DimensionSet; + import software.amazon.lambda.powertools.metrics.model.MetricUnit; + + public class App implements RequestHandler<Object, Object> { + // Create and configure a Metrics singleton using the functional approach + private static final Metrics metrics = MetricsBuilder.builder() + .withNamespace("ServerlessAirline") + .withRaiseOnEmptyMetrics(true) + .withService("payment") + .build(); + + @Override + public Object handleRequest(Object input, Context context) { + // You can manually capture the cold start metric + // Lambda context is an optional argument if not available in your environment + // Dimensions are also optional. + metrics.captureColdStartMetric(context, DimensionSet.of("FunctionName", "MyFunction", "Service", "payment")); + + // Add metrics + metrics.addMetric("CustomMetric", 1, MetricUnit.COUNT); + // Manually flush metrics + metrics.flush(); + } + } + ``` + +## Testing your code + +### Suppressing metrics output + +If you would like to suppress metrics output during your unit tests, you can use the `POWERTOOLS_METRICS_DISABLED` environment variable. For example, using Maven you can set in your build plugins: + +```xml +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <POWERTOOLS_METRICS_DISABLED>true</POWERTOOLS_METRICS_DISABLED> + </environmentVariables> + </configuration> +</plugin> +``` + +### Asserting EMF output + +When unit testing your code, you can run assertions against the output generated by the `Metrics` Singleton. For the `EmfMetricsLogger`, you can assert the generated JSON blob following the [CloudWatch EMF specification](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html) against your expected output. + +Make sure to set a test metrics namespace and service name to run assertions against metrics. For example, by setting the following environment variables in your tests: + +```xml +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <POWERTOOLS_SERVICE_NAME>TestService</POWERTOOLS_SERVICE_NAME> + <POWERTOOLS_METRICS_NAMESPACE>TestNamespace</POWERTOOLS_METRICS_NAMESPACE> + </environmentVariables> + </configuration> +</plugin> +``` + +Consider the following example where we redirect the standard output to a custom `PrintStream`. We use the Jackson library to parse the EMF output into a `JsonNode` and run assertions against that. + +```java hl_lines="35 40 56-72" +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import software.amazon.lambda.powertools.metrics.model.MetricUnit; + +@ExtendWith(MockitoExtension.class) +class MetricsTestExample { + + @Mock + Context lambdaContext; + + private final PrintStream standardOut = System.out; + private ByteArrayOutputStream outputStreamCaptor; + private final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp() { + outputStreamCaptor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @AfterEach + void tearDown() throws Exception { + System.setOut(standardOut); + } + + @Test + void shouldCaptureMetricsFromAnnotatedHandler() throws Exception { + // Given + RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation(); + Map<String, Object> input = new HashMap<>(); + + // When + handler.handleRequest(input, lambdaContext); + + // Then + String emfOutput = outputStreamCaptor.toString().trim(); + String[] jsonLines = emfOutput.split("\n"); + + // First JSON object should be the cold start metric + JsonNode coldStartNode = objectMapper.readTree(jsonLines[0]); + assertThat(coldStartNode.has("ColdStart")).isTrue(); + assertThat(coldStartNode.get("ColdStart").asDouble()).isEqualTo(1.0); + assertThat(coldStartNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("TestNamespace"); + assertThat(coldStartNode.has("Service")).isTrue(); + assertThat(coldStartNode.get("Service").asText()).isEqualTo("TestService"); + + // Second JSON object should be the regular metric + JsonNode regularNode = objectMapper.readTree(jsonLines[1]); + assertThat(regularNode.has("test-metric")).isTrue(); + assertThat(regularNode.get("test-metric").asDouble()).isEqualTo(100.0); + assertThat(regularNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText()) + .isEqualTo("TestNamespace"); + assertThat(regularNode.has("Service")).isTrue(); + assertThat(regularNode.get("Service").asText()).isEqualTo("TestService"); + } + + static class HandlerWithMetricsAnnotation implements RequestHandler<Map<String, Object>, String> { + @Override + @FlushMetrics(captureColdStart = true) + public String handleRequest(Map<String, Object> input, Context context) { + Metrics metrics = MetricsFactory.getMetricsInstance(); + metrics.addMetric("test-metric", 100, MetricUnit.COUNT); + return "OK"; + } + } +} +``` diff --git a/docs/core/tracing.md b/docs/core/tracing.md new file mode 100644 index 000000000..95fbe6d06 --- /dev/null +++ b/docs/core/tracing.md @@ -0,0 +1,449 @@ +--- +title: Tracing +description: Core utility +--- + +The Tracing utility is an opinionated thin wrapper for [AWS X-Ray Java SDK](https://github.com/aws/aws-xray-sdk-java/) +a provides functionality to reduce the overhead of performing common tracing tasks. + +![Tracing showcase](../media/tracing_utility_showcase.png) + + **Key Features** + + * Capture cold start as annotation, and responses as well as full exceptions as metadata + * Helper methods to improve the developer experience of creating new X-Ray subsegments. + * Better developer experience when developing with multiple threads. + * Auto patch supported modules by AWS X-Ray + * GraalVM support + +## Install + +=== "Maven" + + ```xml hl_lines="3-7 25-28" + <dependencies> + ... + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + ... + </dependencies> + ... + <!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> + <build> + <plugins> + ... + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14</version> + <configuration> + <source>11</source> <!-- or higher --> + <target>11</target> <!-- or higher --> + <complianceLevel>11</complianceLevel> <!-- or higher --> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + ... + </plugins> + </build> + ``` + +=== "Gradle" + + ```groovy hl_lines="3 11 12" + plugins { + id 'java' + id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0' // Not needed when using the functional approach + } + + repositories { + mavenCentral() + } + + dependencies { + aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' // Not needed when using the functional approach + implementation 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' // Use this instead of 'aspect' when using the functional approach + } + + sourceCompatibility = 11 // or higher + targetCompatibility = 11 // or higher + ``` + +## Initialization + +Before your use this utility, your AWS Lambda function [must have permissions](https://docs.aws.amazon.com/lambda/latest/dg/services-xray.html#services-xray-permissions) to send traces to AWS X-Ray. + +> Example using AWS Serverless Application Model (SAM) + +=== "template.yaml" + + ```yaml hl_lines="8 11" + Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function + Properties: + ... + Runtime: java11 + + Tracing: Active + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: example + ``` + +The Powertools for AWS Lambda (Java) service name is used as the X-Ray namespace. This can be set using the environment variable +`POWERTOOLS_SERVICE_NAME` + +### Lambda handler + +You can enable tracing using either the `@Tracing` annotation or the functional API. + +**With the `@Tracing` annotation**, add it to your `handleRequest` method or any method to capture it as a separate subsegment automatically. You can optionally customize the segment name that appears in traces. + +**With the functional API**, use `TracingUtils.withSubsegment()` to manually create subsegments without AspectJ configuration. + +=== "@Tracing annotation" + + ```java hl_lines="3 10 15" + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Tracing + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + businessLogic1(); + + businessLogic2(); + } + + @Tracing + public void businessLogic1(){ + + } + + @Tracing + public void businessLogic2(){ + + } + } + ``` + +=== "Functional API" + + ```java hl_lines="1 6 7 8 10 11 12" + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.withSubsegment("businessLogic1", subsegment -> { + // Business logic 1 + }); + + TracingUtils.withSubsegment("businessLogic2", subsegment -> { + // Business logic 2 + }); + } + } + ``` + +=== "Custom Segment names" + + ```java hl_lines="3" + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Tracing(segmentName="yourCustomName") + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + ... + } + ``` + +When using the `@Tracing` annotation, the utility performs these additional tasks to ease operations: + + * Creates a `ColdStart` annotation to easily filter traces that have had an initialization overhead. + * Creates a `Service` annotation if service parameter or `POWERTOOLS_SERVICE_NAME` is set. + * Captures any response, or full exceptions generated by the handler, and include as tracing metadata. + +By default, the `@Tracing` annotation uses `captureMode=ENVIRONMENT_VAR`, which means it will only record method responses and exceptions if you set +the environment variables `POWERTOOLS_TRACER_CAPTURE_RESPONSE` and `POWERTOOLS_TRACER_CAPTURE_ERROR` to `true`. You can override this behavior by +specifying a different `captureMode` to always record response, exception, both, or neither. + +!!! note + When using the functional API with `TracingUtils.withSubsegment()`, response and exception capture is not automatic. You can manually add metadata using `TracingUtils.putMetadata()` as needed. + +!!! warning "Returning sensitive information from your Lambda handler or functions?" + When using the `@Tracing` annotation, you can disable it from capturing responses and exceptions as tracing metadata with **`captureMode=DISABLED`** + or globally by setting the environment variables **`POWERTOOLS_TRACER_CAPTURE_RESPONSE`** and **`POWERTOOLS_TRACER_CAPTURE_ERROR`** to **`false`**. + When using the functional API, you have full control over what metadata is captured. + +=== "@Tracing annotation - Disable on method" + + ```java hl_lines="3" + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Tracing(captureMode=CaptureMode.DISABLED) + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + ... + } + ``` + +=== "@Tracing annotation - Disable Globally" + + ```yaml hl_lines="11 12" + Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function + Properties: + ... + Runtime: java11 + + Tracing: Active + Environment: + Variables: + POWERTOOLS_TRACER_CAPTURE_RESPONSE: false + POWERTOOLS_TRACER_CAPTURE_ERROR: false + ``` + +=== "Functional API" + + ```java hl_lines="6 7 8" + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.withSubsegment("businessLogic", subsegment -> { + // With functional API, you control what metadata is captured + }); + } + ``` + +### Annotations & Metadata + +**Annotations** are key-values associated with traces and indexed by AWS X-Ray. You can use them to filter traces and to +create [Trace Groups](https://aws.amazon.com/about-aws/whats-new/2018/11/aws-xray-adds-the-ability-to-group-traces/) to slice and dice your transactions. + +**Metadata** are key-values also associated with traces but not indexed by AWS X-Ray. You can use them to add additional +context for an operation using any native object. + +=== "Annotations" + + You can add annotations using `putAnnotation()` method from TracingUtils + ```java hl_lines="8" + import software.amazon.lambda.powertools.tracing.Tracing; + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Tracing + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.putAnnotation("annotation", "value"); + } + } + ``` + +=== "Metadata" + + You can add metadata using `putMetadata()` method from TracingUtils + ```java hl_lines="8" + import software.amazon.lambda.powertools.tracing.Tracing; + import software.amazon.lambda.powertools.tracing.TracingUtils; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + @Tracing + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + TracingUtils.putMetadata("content", "value"); + } + } + ``` + +## Override default object mapper + +You can optionally choose to override default object mapper which is used to serialize method response and exceptions when enabled. You might +want to supply custom object mapper in order to control how serialisation is done, for example, when you want to log only +specific fields from received event due to security. + +=== "App.java" + + ```java hl_lines="10-14" + import software.amazon.lambda.powertools.tracing.Tracing; + import software.amazon.lambda.powertools.tracing.TracingUtils; + import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE; + + /** + * Handler for requests to Lambda function. + */ + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + static { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule simpleModule = new SimpleModule(); + objectMapper.registerModule(simpleModule); + + TracingUtils.defaultObjectMapper(objectMapper); + } + + @Tracing(captureMode = RESPONSE) + public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { + ... + } + } + ``` + +## Advanced usage + +### Multi-threaded programming + +When working with multiple threads, you need to pass the trace entity to ensure proper trace context propagation. + +=== "Multi-threaded example" + + ```java hl_lines="7 9 10 11" + import static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; + + public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { + + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { + // Extract existing trace data + Entity traceEntity = AWSXRay.getTraceEntity(); + + Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> { + // Business logic in separate thread + })); + } + } + ``` + +## Instrumenting SDK clients and HTTP calls + +### AWS SDK for Java 2.x + +Powertools for AWS Lambda (Java) includes the `aws-xray-recorder-sdk-aws-sdk-v2-instrumentor` library, which **automatically instruments all AWS SDK v2 clients** when you add the `powertools-tracing` dependency to your project. This means downstream calls to AWS services are traced without any additional configuration. + +If you need more control over which clients are instrumented, you can manually add the `TracingInterceptor` to specific clients: + +=== "Manual instrumentation (optional)" + + ```java hl_lines="1 2 3 8 9 10 11" + import com.amazonaws.xray.interceptors.TracingInterceptor; + import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; + import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + + public class LambdaHandler { + private DynamoDbClient client = DynamoDbClient.builder() + .region(Region.US_WEST_2) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .addExecutionInterceptor(new TracingInterceptor()) + .build() + ) + .build(); + // ... + } + ``` + +For more details, refer to the [AWS X-Ray documentation on tracing AWS SDK calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-awssdkclients.html) and [outgoing HTTP calls](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-httpclients.html). + +## Testing your code + +When using `@Tracing` annotation, your Junit test cases needs to be configured to create parent Segment required by [AWS X-Ray SDK for Java](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html). + +Below are two ways in which you can configure your tests. + +#### Configure environment variable on project level (Recommended) + +You can choose to configure environment variable on project level for your test cases run. This is recommended approach as it will avoid the need of configuring each test case specifically. + +Below are examples configuring your maven/gradle projects. You can choose to configure it differently as well as long as you are making sure that environment variable `LAMBDA_TASK_ROOT` is set. This variable is +used internally via AWS X-Ray SDK to configure itself properly for lambda runtime. + +=== "Maven (pom.xml)" + + ```xml + <build> + ... + <plugins> + <!-- Configures environment variable to avoid initialization of AWS X-Ray segments for each tests--> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <LAMBDA_TASK_ROOT>handler</LAMBDA_TASK_ROOT> + </environmentVariables> + </configuration> + </plugin> + </plugins> + </build> + + ``` + +=== "Gradle (build.gradle)" + + ```json + // Configures environment variable to avoid initialization of AWS X-Ray segments for each tests + test { + environment "LAMBDA_TASK_ROOT", "handler" + } + ``` + +#### Configure test cases (Not Recommended) + +You can choose to configure each of your test case instead as well if you choose not to configure environment variable on project level. +Below is an example configuration needed for each test case. + +=== "AppTest.java" + + ```java hl_lines="10 11 12 17 18 19 20 21 22 23 24" + import com.amazonaws.xray.AWSXRay; + import org.junit.After; + import org.junit.Before; + import org.junit.Test; + + public class AppTest { + + @Before + public void setup() { + if(null == System.getenv("LAMBDA_TASK_ROOT")) { + AWSXRay.beginSegment("test"); + } + } + + @After + public void tearDown() { + // Needed when using sam build --use-container + if (AWSXRay.getCurrentSubsegmentOptional().isPresent()) { + AWSXRay.endSubsegment(); + } + + if(null == System.getenv("LAMBDA_TASK_ROOT")) { + AWSXRay.endSegment(); + } + } + + @Test + public void successfulResponse() { + // test logic + } + ``` diff --git a/docs/gatsby-browser.js b/docs/gatsby-browser.js deleted file mode 100644 index b5c868e23..000000000 --- a/docs/gatsby-browser.js +++ /dev/null @@ -1,27 +0,0 @@ -import "./src/styles/global.css" -import Amplify from 'aws-amplify'; -import { Analytics, AWSKinesisFirehoseProvider } from '@aws-amplify/analytics'; -import awsconfig from './src/config'; - -export const onRouteUpdate = ({ location, prevLocation }) => { - Analytics.record({ - data: { - url: window.location.href, - section: location.pathname, - previous: prevLocation ? prevLocation.pathname : null, - language: 'java' - }, - streamName: awsconfig.aws_kinesis_firehose_stream_name - }, 'AWSKinesisFirehose') -} - -export const onClientEntry = () => { - Analytics.addPluggable(new AWSKinesisFirehoseProvider()); - Amplify.configure(awsconfig); - - Analytics.configure({ - AWSKinesisFirehose: { - region: awsconfig.aws_project_region - } - }); -} diff --git a/docs/gatsby-config.js b/docs/gatsby-config.js deleted file mode 100644 index 0c9734559..000000000 --- a/docs/gatsby-config.js +++ /dev/null @@ -1,75 +0,0 @@ -const docsWebsite = "https://awslabs.github.io/aws-lambda-powertools-java" - -module.exports = { - pathPrefix: '/aws-lambda-powertools-java', - siteMetadata: { - title: 'AWS Lambda Powertools Java', - description: 'A suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier', - author: `Amazon Web Services`, - siteName: 'AWS Lambda Powertools Java', - siteUrl: `${docsWebsite}` - }, - plugins: [ - { - resolve: 'gatsby-theme-apollo-docs', - options: { - root: __dirname, - menuTitle: 'Helpful resources', - githubRepo: 'awslabs/aws-lambda-powertools-java', - baseUrl: `${docsWebsite}`, - logoLink: `${docsWebsite}`, - sidebarCategories: { - null: [ - 'index' - ], - 'Core utilities': [ - 'core/logging', - 'core/tracing', - 'core/metrics' - ], - 'Utilities': [ - 'utilities/sqs_large_message_handling', - 'utilities/batch', - 'utilities/parameters', - 'utilities/validation' - ], - }, - navConfig: { - 'Serverless Best Practices video': { - url: 'https://www.youtube.com/watch?v=9IYpGTS7Jy0', - description: 'AWS re:Invent ARC307: Serverless architectural patterns & best practices - Origins of Powertools', - }, - 'AWS Well-Architected Serverless Lens': { - url: 'https://d1.awsstatic.com/whitepapers/architecture/AWS-Serverless-Applications-Lens.pdf', - description: 'AWS Well-Architected Serverless Applications Lens whitepaper', - }, - 'Amazon Builders Library': { - url: 'https://aws.amazon.com/builders-library/', - description: 'Collection of living articles covering topics across architecture, software delivery, and operations' - }, - 'AWS CDK Patterns': { - url: 'https://cdkpatterns.com/patterns/', - description: "CDK Patterns maintained by Matt Coulter (@nideveloper)" - } - }, - footerNavConfig: { - Serverless: { - href: 'https://aws.amazon.com/serverless/' - }, - 'AWS SAM Docs': { - href: 'https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html', - } - } - } - }, - { - resolve: `gatsby-plugin-catch-links`, - options: { - excludePattern: /\/aws-lambda-powertools-java/, - }, - }, - 'gatsby-plugin-antd', - 'gatsby-remark-autolink-headers', - 'gatsby-plugin-sitemap' - ] -}; diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..655c16e03 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,231 @@ +--- +title: Homepage +description: Powertools for AWS Lambda (Java) +--- + +Powertools for AWS Lambda (Java) is a developer toolkit to implement Serverless best practices and increase developer velocity. + +???+ tip + Powertools for AWS Lambda is also available for [Python](https://docs.powertools.aws.dev/lambda/python/latest/){target="_blank"}, [TypeScript](https://docs.powertools.aws.dev/lambda/typescript/latest/){target="_blank"}, and [.NET](https://docs.powertools.aws.dev/lambda/dotnet/){target="_blank"} + + +???+ tip "Looking for a quick run through of the core utilities?" + Check out [this detailed blog post](https://aws.amazon.com/blogs/compute/introducing-v2-of-powertools-for-aws-lambda-java/) with a practical example. To dive deeper, + the [Powertools for AWS Lambda (Java) workshop](https://catalog.us-east-1.prod.workshops.aws/workshops/a7011c82-e4af-4a52-80fa-fcd61f1dacd9/en-US/introduction) is a great next step. + +## Tenets + +This project separates core utilities that will be available in other runtimes vs general utilities that might not be available across all runtimes. + +* **AWS Lambda only** – We optimise for AWS Lambda function environments and supported runtimes only. Utilities might work with web frameworks and non-Lambda environments, though they are not officially supported. +* **Eases the adoption of best practices** – The main priority of the utilities is to facilitate best practices adoption, as defined in the AWS Well-Architected Serverless Lens; all other functionality is optional. +* **Keep it lean** – Additional dependencies are carefully considered for security and ease of maintenance, and prevent negatively impacting startup time. +* **We strive for backwards compatibility** – New features and changes should keep backwards compatibility. If a breaking change cannot be avoided, the deprecation and migration process should be clearly defined. +* **We work backwards from the community** – We aim to strike a balance of what would work best for 80% of customers. Emerging practices are considered and discussed via Requests for Comment (RFCs) +* **Progressive** - Utilities are designed to be incrementally adoptable for customers at any stage of their Serverless journey. They follow language idioms and their community’s common practices. + +## Install + +<!-- TODO: Uncomment when SAM template is updated - https://github.com/aws-powertools/powertools-lambda-java/issues/1889 +**Quick hello world example using SAM CLI** + +You can use [SAM](https://aws.amazon.com/serverless/sam/) to quickly setup a serverless project including Powertools for AWS Lambda (Java). + +```bash +sam init + +Which template source would you like to use? + 1 - AWS Quick Start Templates + 2 - Custom Template Location +Choice: 1 + +Choose an AWS Quick Start application template + 1 - Hello World Example + 2 - Data processing + 3 - Hello World Example with Powertools for AWS Lambda + 4 - Multi-step workflow + 5 - Scheduled task + 6 - Standalone function + 7 - Serverless API + 8 - Infrastructure event management + 9 - Lambda Response Streaming + 10 - Serverless Connector Hello World Example + 11 - Multi-step workflow with Connectors + 12 - Full Stack + 13 - Lambda EFS example + 14 - DynamoDB Example + 15 - Machine Learning +Template: 3 + +Which runtime would you like to use? + 1 - dotnet6 + 2 - java17 + 3 - java11 + 4 - java8.al2 + 5 - java8 + 6 - nodejs18.x + 7 - nodejs16.x + 8 - nodejs14.x + 9 - python3.9 + 10 - python3.8 + 11 - python3.7 + 12 - python3.10 +Runtime: 2, 3, 4 or 5 +``` +--> + +Powertools for AWS Lambda (Java) dependencies are available in Maven Central. You can use your favourite dependency management tool to install it + +* [Maven](https://maven.apache.org/) +* [Gradle](https://gradle.org) + +=== "Maven" + + ```xml + <dependencies> + ... + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging-log4j</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + <dependency> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + <version>{{ powertools.version }}</version> + </dependency> + <!-- Make sure to include AspectJ runtime compatible with plugin version --> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>1.9.22</version> + </dependency> + ... + </dependencies> + ... + <!-- Configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project --> + <!-- Note: This AspectJ configuration is not needed when using the functional approach --> + <build> + <plugins> + ... + <plugin> + <groupId>dev.aspectj</groupId> + <artifactId>aspectj-maven-plugin</artifactId> + <version>1.14</version> + <configuration> + <source>11</source> <!-- or higher --> + <target>11</target> <!-- or higher --> + <complianceLevel>11</complianceLevel> <!-- or higher --> + <aspectLibraries> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-tracing</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-logging</artifactId> + </aspectLibrary> + <aspectLibrary> + <groupId>software.amazon.lambda</groupId> + <artifactId>powertools-metrics</artifactId> + </aspectLibrary> + </aspectLibraries> + </configuration> + <dependencies> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjtools</artifactId> + <!-- AspectJ compiler version, in sync with runtime --> + <version>1.9.22</version> + </dependency> + </dependencies> + <executions> + <execution> + <goals> + <goal>compile</goal> + </goals> + </execution> + </executions> + </plugin> + ... + </plugins> + </build> + ``` + +=== "Gradle" + + ```groovy + + plugins { + id 'java' + id 'io.freefair.aspectj.post-compile-weaving' version '8.2.2' + } + + // the freefair aspect plugins targets gradle 8.2.1 + // https://docs.freefair.io/gradle-plugins/8.2.2/reference/ + wrapper { + gradleVersion = "8.2.1" + } + + repositories { + mavenCentral() + } + + dependencies { + // Note: This AspectJ configuration is not needed when using the functional approach + aspect 'software.amazon.lambda:powertools-logging-log4j:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-tracing:{{ powertools.version }}' + aspect 'software.amazon.lambda:powertools-metrics:{{ powertools.version }}' + } + + sourceCompatibility = 11 + targetCompatibility = 11 + ``` + +???+ tip "Don't want to use AspectJ?" + Powertools for AWS Lambda (Java) now provides a functional API that doesn't require AspectJ configuration. Learn more about the [functional approach](./usage-patterns.md#functional-approach). + +### Java Compatibility +Powertools for AWS Lambda (Java) supports all Java versions from 11 to 25 in line with the [corresponding Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). + +In addition to the functional approach, [Logging](./core/logging.md), [Metrics](./core/metrics.md), [Tracing](./core/tracing.md), [Parameters](./utilities/parameters.md), [Idempotency](./utilities/idempotency.md), [Validation](./utilities/validation.md), and [Large Messages](./utilities/large_messages.md) utilities support annotations using AspectJ, which require configuration of the `aspectjrt` runtime library. + +You may need to add the appropriate version of `aspectjrt` to your dependencies based on the JDK used for building your function: + +```xml +<dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>1.9.??</version> +</dependency> +``` + +Use the following [dependency matrix](https://github.com/eclipse-aspectj/aspectj/blob/master/docs/release/JavaVersionCompatibility.adoc) to understand which AspectJ version to use based on your JDK version: + +| JDK version | aspectj version | +|-------------|------------------------| +| `11-17` | `1.9.20.1` (or higher) | +| `21` | `1.9.21` (or higher) | +| `25` | `1.9.25` (or higher) | + +## Environment variables + +!!! info + Explicit parameters take precedence over environment variables. + +| Environment variable | Description | Utility | +| -------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------- | +| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | +| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) | +| **POWERTOOLS_METRICS_FUNCTION_NAME** | Function name used as dimension for the cold start metric | [Metrics](./core/metrics) | +| **POWERTOOLS_METRICS_DISABLED** | Disables all flushing of metrics | [Metrics](./core/metrics) | +| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logging](./core/logging) | +| **POWERTOOLS_LOG_LEVEL** | Sets logging level | [Logging](./core/logging) | +| **POWERTOOLS_LOGGER_LOG_EVENT** | Enables/Disables whether to log the incoming event when using the aspect | [Logging](./core/logging) | +| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Enables/Disables tracing mode to capture method response | [Tracing](./core/tracing) | +| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Enables/Disables tracing mode to capture method error | [Tracing](./core/tracing) | diff --git a/docs/media/aws-logo-light.svg b/docs/media/aws-logo-light.svg new file mode 100644 index 000000000..982571b86 --- /dev/null +++ b/docs/media/aws-logo-light.svg @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 304 182" style="enable-background:new 0 0 304 182;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#FFFFFF;} + .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;} +</style> +<g> + <path class="st0" d="M86.4,66.4c0,3.7,0.4,6.7,1.1,8.9c0.8,2.2,1.8,4.6,3.2,7.2c0.5,0.8,0.7,1.6,0.7,2.3c0,1-0.6,2-1.9,3l-6.3,4.2 + c-0.9,0.6-1.8,0.9-2.6,0.9c-1,0-2-0.5-3-1.4C76.2,90,75,88.4,74,86.8c-1-1.7-2-3.6-3.1-5.9c-7.8,9.2-17.6,13.8-29.4,13.8 + c-8.4,0-15.1-2.4-20-7.2c-4.9-4.8-7.4-11.2-7.4-19.2c0-8.5,3-15.4,9.1-20.6c6.1-5.2,14.2-7.8,24.5-7.8c3.4,0,6.9,0.3,10.6,0.8 + c3.7,0.5,7.5,1.3,11.5,2.2v-7.3c0-7.6-1.6-12.9-4.7-16c-3.2-3.1-8.6-4.6-16.3-4.6c-3.5,0-7.1,0.4-10.8,1.3c-3.7,0.9-7.3,2-10.8,3.4 + c-1.6,0.7-2.8,1.1-3.5,1.3c-0.7,0.2-1.2,0.3-1.6,0.3c-1.4,0-2.1-1-2.1-3.1v-4.9c0-1.6,0.2-2.8,0.7-3.5c0.5-0.7,1.4-1.4,2.8-2.1 + c3.5-1.8,7.7-3.3,12.6-4.5c4.9-1.3,10.1-1.9,15.6-1.9c11.9,0,20.6,2.7,26.2,8.1c5.5,5.4,8.3,13.6,8.3,24.6V66.4z M45.8,81.6 + c3.3,0,6.7-0.6,10.3-1.8c3.6-1.2,6.8-3.4,9.5-6.4c1.6-1.9,2.8-4,3.4-6.4c0.6-2.4,1-5.3,1-8.7v-4.2c-2.9-0.7-6-1.3-9.2-1.7 + c-3.2-0.4-6.3-0.6-9.4-0.6c-6.7,0-11.6,1.3-14.9,4c-3.3,2.7-4.9,6.5-4.9,11.5c0,4.7,1.2,8.2,3.7,10.6 + C37.7,80.4,41.2,81.6,45.8,81.6z M126.1,92.4c-1.8,0-3-0.3-3.8-1c-0.8-0.6-1.5-2-2.1-3.9L96.7,10.2c-0.6-2-0.9-3.3-0.9-4 + c0-1.6,0.8-2.5,2.4-2.5h9.8c1.9,0,3.2,0.3,3.9,1c0.8,0.6,1.4,2,2,3.9l16.8,66.2l15.6-66.2c0.5-2,1.1-3.3,1.9-3.9c0.8-0.6,2.2-1,4-1 + h8c1.9,0,3.2,0.3,4,1c0.8,0.6,1.5,2,1.9,3.9l15.8,67l17.3-67c0.6-2,1.3-3.3,2-3.9c0.8-0.6,2.1-1,3.9-1h9.3c1.6,0,2.5,0.8,2.5,2.5 + c0,0.5-0.1,1-0.2,1.6c-0.1,0.6-0.3,1.4-0.7,2.5l-24.1,77.3c-0.6,2-1.3,3.3-2.1,3.9c-0.8,0.6-2.1,1-3.8,1h-8.6c-1.9,0-3.2-0.3-4-1 + c-0.8-0.7-1.5-2-1.9-4L156,23l-15.4,64.4c-0.5,2-1.1,3.3-1.9,4c-0.8,0.7-2.2,1-4,1H126.1z M254.6,95.1c-5.2,0-10.4-0.6-15.4-1.8 + c-5-1.2-8.9-2.5-11.5-4c-1.6-0.9-2.7-1.9-3.1-2.8c-0.4-0.9-0.6-1.9-0.6-2.8v-5.1c0-2.1,0.8-3.1,2.3-3.1c0.6,0,1.2,0.1,1.8,0.3 + c0.6,0.2,1.5,0.6,2.5,1c3.4,1.5,7.1,2.7,11,3.5c4,0.8,7.9,1.2,11.9,1.2c6.3,0,11.2-1.1,14.6-3.3c3.4-2.2,5.2-5.4,5.2-9.5 + c0-2.8-0.9-5.1-2.7-7c-1.8-1.9-5.2-3.6-10.1-5.2L246,52c-7.3-2.3-12.7-5.7-16-10.2c-3.3-4.4-5-9.3-5-14.5c0-4.2,0.9-7.9,2.7-11.1 + c1.8-3.2,4.2-6,7.2-8.2c3-2.3,6.4-4,10.4-5.2c4-1.2,8.2-1.7,12.6-1.7c2.2,0,4.5,0.1,6.7,0.4c2.3,0.3,4.4,0.7,6.5,1.1 + c2,0.5,3.9,1,5.7,1.6c1.8,0.6,3.2,1.2,4.2,1.8c1.4,0.8,2.4,1.6,3,2.5c0.6,0.8,0.9,1.9,0.9,3.3v4.7c0,2.1-0.8,3.2-2.3,3.2 + c-0.8,0-2.1-0.4-3.8-1.2c-5.7-2.6-12.1-3.9-19.2-3.9c-5.7,0-10.2,0.9-13.3,2.8c-3.1,1.9-4.7,4.8-4.7,8.9c0,2.8,1,5.2,3,7.1 + c2,1.9,5.7,3.8,11,5.5l14.2,4.5c7.2,2.3,12.4,5.5,15.5,9.6c3.1,4.1,4.6,8.8,4.6,14c0,4.3-0.9,8.2-2.6,11.6 + c-1.8,3.4-4.2,6.4-7.3,8.8c-3.1,2.5-6.8,4.3-11.1,5.6C264.4,94.4,259.7,95.1,254.6,95.1z"/> + <g> + <path class="st1" d="M273.5,143.7c-32.9,24.3-80.7,37.2-121.8,37.2c-57.6,0-109.5-21.3-148.7-56.7c-3.1-2.8-0.3-6.6,3.4-4.4 + c42.4,24.6,94.7,39.5,148.8,39.5c36.5,0,76.6-7.6,113.5-23.2C274.2,133.6,278.9,139.7,273.5,143.7z"/> + <path class="st1" d="M287.2,128.1c-4.2-5.4-27.8-2.6-38.5-1.3c-3.2,0.4-3.7-2.4-0.8-4.5c18.8-13.2,49.7-9.4,53.3-5 + c3.6,4.5-1,35.4-18.6,50.2c-2.7,2.3-5.3,1.1-4.1-1.9C282.5,155.7,291.4,133.4,287.2,128.1z"/> + </g> +</g> +</svg> diff --git a/docs/media/idempotent_sequence.png b/docs/media/idempotent_sequence.png new file mode 100644 index 000000000..92593184a Binary files /dev/null and b/docs/media/idempotent_sequence.png differ diff --git a/docs/media/idempotent_sequence_exception.png b/docs/media/idempotent_sequence_exception.png new file mode 100644 index 000000000..4cf065993 Binary files /dev/null and b/docs/media/idempotent_sequence_exception.png differ diff --git a/docs/media/intellij_checkstyle_1.png b/docs/media/intellij_checkstyle_1.png new file mode 100644 index 000000000..322e24744 Binary files /dev/null and b/docs/media/intellij_checkstyle_1.png differ diff --git a/docs/media/intellij_checkstyle_2.png b/docs/media/intellij_checkstyle_2.png new file mode 100644 index 000000000..7fc82187b Binary files /dev/null and b/docs/media/intellij_checkstyle_2.png differ diff --git a/docs/media/intellij_checkstyle_3.png b/docs/media/intellij_checkstyle_3.png new file mode 100644 index 000000000..6e08dde62 Binary files /dev/null and b/docs/media/intellij_checkstyle_3.png differ diff --git a/docs/media/metrics_terminology.png b/docs/media/metrics_terminology.png new file mode 100644 index 000000000..a20fafd6a Binary files /dev/null and b/docs/media/metrics_terminology.png differ diff --git a/docs/content/media/tracer_utility_showcase.png b/docs/media/tracing_utility_showcase.png similarity index 100% rename from docs/content/media/tracer_utility_showcase.png rename to docs/media/tracing_utility_showcase.png diff --git a/docs/overrides/assets/images/powertools_docs_thumbnail.png b/docs/overrides/assets/images/powertools_docs_thumbnail.png new file mode 100644 index 000000000..6c21eaf03 Binary files /dev/null and b/docs/overrides/assets/images/powertools_docs_thumbnail.png differ diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 000000000..5f7a59c02 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block outdated %} +You're not viewing the latest version. +<a href="{{ '../' ~ base_url }}"> + <strong>Click here to go to latest.</strong> +</a> +{% endblock %} + +{% block extrahead %} + <meta name="guide-name" content="Powertools for AWS Lambda (Java)"> + <meta name="service-name" content="Powertools for AWS Lambda"> +{% endblock %} diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index 0449b133f..000000000 --- a/docs/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "scripts": { - "start": "gatsby develop", - "build": "gatsby build --prefix-paths" - }, - "dependencies": { - "@aws-amplify/analytics": "^3.3.6", - "antd": "^4.7.0", - "aws-amplify": "^3.3.3", - "gatsby": "^2.24.77", - "gatsby-plugin-antd": "^2.2.0", - "gatsby-plugin-catch-links": "^2.3.15", - "gatsby-plugin-sitemap": "^2.4.16", - "gatsby-remark-autolink-headers": "^2.3.15", - "gatsby-theme-apollo-docs": "^4.5.3", - "react": "^16.13.1", - "react-dom": "^16.13.1" - }, - "resolutions": { - "bl": "^4.0.3", - "semver": "^7.3.2", - "node-fetch": "^2.6.1", - "graphql": "^14.7.0" - }, - "keywords": [], - "license": "Apache-2.0", - "repository": "https://github.com/awslabs/aws-lambda-powertools-java", - "name": "aws-lambda-powertools-java", - "description": "Powertools is a suite of utilities for AWS Lambda Functions that makes tracing with AWS X-Ray, structured logging and creating custom metrics asynchronously easier.", - "devDependencies": {} -} diff --git a/docs/processes/maintainers.md b/docs/processes/maintainers.md new file mode 100644 index 000000000..f2839c532 --- /dev/null +++ b/docs/processes/maintainers.md @@ -0,0 +1,249 @@ +--- +title: Maintainers playbook +description: Process +--- + +<!-- markdownlint-disable MD043 --> + +## Overview + +!!! note "Please treat this content as a living document." + +This is document explains who the maintainers are, their responsibilities, and how they should be doing it. If you're interested in contributing, + see [CONTRIBUTING](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CONTRIBUTING.md){target="_blank"}. + +## Current Maintainers + +| Maintainer | GitHub ID | Affiliation | +| --------------- | -------------------------------------------------------------------- | ----------- | +| Philipp Page | [phipag](https://github.com/phipag){target="\_blank" rel="nofollow"} | Amazon | + +## Emeritus + +Previous active maintainers who contributed to this project. + +| Maintainer | GitHub ID | Affiliation | +| --------------------- | -------------------------------------------------------------------------------------- | ------------- | +| Simon Thulbourn | [sthulb](https://github.com/sthulb){target="\_blank" rel="nofollow"} | Former Amazon | +| Jerome Van Der Linden | [jeromevdl](https://github.com/jeromevdl){target="\_blank" rel="nofollow"} | Amazon | +| Michele Ricciardi | [mriccia](https://github.com/mriccia){target="\_blank" rel="nofollow"} | Amazon | +| Scott Gerring | [scottgerring](https://github.com/scottgerring){target="\_blank" rel="nofollow"} | DataDog | +| Mark Sailes | [msailes](https://github.com/msailes){target="\_blank" rel="nofollow"} | Former Amazon | +| Pankaj Agrawal | [pankajagrawal16](https://github.com/pankajagrawal16){target="\_blank" rel="nofollow"} | Former Amazon | +| Steve Houel | [stevehouel](https://github.com/stevehouel){target="\_blank" rel="nofollow"} | Amazon | + +## Labels + +These are the most common labels used by maintainers to triage issues, pull requests (PR), and for project management: + +| Label | Usage | Notes | +|----------------------------------|---------------------------------------------------------------------------------------------------|----------------------------------------------------| +| triage | New issues that require maintainers review | Issue template | +| bug | Unexpected, reproducible and unintended software behavior | PR/Release automation; Doc snippets are excluded; | +| documentation | Documentation improvements | PR/Release automation; Doc additions, fixes, etc.; | +| duplicate | Dupe of another issue | | +| enhancement | New or enhancements to existing features | Issue template | +| RFC | Technical design documents related to a feature request | Issue template | +| help wanted | Tasks you want help from anyone to move forward | Bandwidth, complex topics, etc. | +| feature-parity | Adding features present in other Powertools for Lambda libraries | | +| good first issue | Somewhere for new contributors to start | | +| governance | Issues related to project governance - contributor guides, automation, etc. | | +| question | Issues that are raised to ask questions | | +| maven | Related to the build system | | +| need-more-information | Missing information before making any calls | | +| status/staged-next-release | Changes are merged and will be available once the next release is made. | | +| status/staged-next-major-release | Contains breaking changes - merged changes will be available once the next major release is made. | | +| blocked | Issues or PRs that are blocked for varying reasons | Timeline is uncertain | +| priority:1 | Critical - needs urgent attention | | +| priority:2 | High - core feature, or affects 60%+ of users | | +| priority:3 | Neutral - not a core feature, or affects < 40% of users | | +| priority:4 | Low - nice to have | | +| priority:5 | Low - idea for later | | +| invalid | This doesn't seem right | | +| size/XS | PRs between 0-9 LOC | PR automation | +| size/S | PRs between 10-29 LOC | PR automation | +| size/M | PRs between 30-99 LOC | PR automation | +| size/L | PRs between 100-499 LOC | PR automation | +| size/XL | PRs between 500-999 LOC, often PRs that grown with feedback | PR automation | +| size/XXL | PRs with 1K+ LOC, largely documentation related | PR automation | +| dependencies | Changes that touch dependencies, e.g. Dependabot, etc. | PR/ automation | +| maintenance | Address outstanding tech debt | | + +## Maintainer Responsibilities + +Maintainers are active and visible members of the community, and have +[maintain-level permissions on a repository](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization){target="_blank"}. +Use those privileges to serve the community and evolve code as follows. + +Be aware of recurring ambiguous situations and [document them](#common-scenarios) to help your fellow maintainers. + +### Uphold Code of Conduct + +<!-- markdownlint-disable-next-line MD013 --> +Model the behavior set forward by the +[Code of Conduct](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CODE_OF_CONDUCT.md){target="_blank"} +and raise any violations to other maintainers and admins. There could be unusual circumstances where inappropriate +behavior does not immediately fall within the [Code of Conduct](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CODE_OF_CONDUCT.md){target="_blank"}. + +These might be nuanced and should be handled with extra care - when in doubt, do not engage and reach out to other maintainers +and admins. + +### Prioritize Security + +Security is your number one priority. Maintainer's Github keys must be password protected securely and any reported +security vulnerabilities are addressed before features or bugs. + +Note that this repository is monitored and supported 24/7 by Amazon Security, see +[Security disclosures](https://github.com/aws-powertools/powertools-lambda-java/){target="_blank"} for details. + +### Review Pull Requests + +Review pull requests regularly, comment, suggest, reject, merge and close. Accept only high quality pull-requests. +Provide code reviews and guidance on incoming pull requests. + +PRs are [labeled](#labels) based on file changes and semantic title. Pay attention to whether labels reflect the current +state of the PR and correct accordingly. + +Use and enforce [semantic versioning](https://semver.org/){target="_blank" rel="nofollow"} pull request titles, as these will be used for +[CHANGELOG](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CHANGELOG.md){target="_blank"} +and [Release notes](https://github.com/aws-powertools/powertools-lambda-java/releases) - make sure they communicate their +intent at the human level. + +For issues linked to a PR, make sure `status/staged-next-release` label is applied to them when merging. +[Upon release](#releasing-a-new-version), these issues will be notified which release version contains their change. + +See [Common scenarios](#common-scenarios) section for additional guidance. + +### Triage New Issues + +Manage [labels](#labels), review issues regularly, and create new labels as needed by the project. Remove `triage` +label when you're able to confirm the validity of a request, a bug can be reproduced, etc. +Give priority to the original author for implementation, unless it is a sensitive task that is best handled by maintainers. + +Make sure issues are assigned to our [board of activities](https://github.com/orgs/aws-powertools/projects/4). + +Use our [labels](#labels) to signal good first issues to new community members, and to set expectation that this might +need additional feedback from the author, other customers, experienced community members and/or maintainers. + +Be aware of [casual contributors](https://opensource.com/article/17/10/managing-casual-contributors){target="_blank" rel="nofollow"} and recurring contributors. +Provide the experience and attention you wish you had if you were starting in open source. + +See [Common scenarios](#common-scenarios) section for additional guidance. + +### Triage Bug Reports + +Be familiar with [our definition of bug](#is-that-a-bug). If it's not a bug, you can close it or adjust its title and +labels - always communicate the reason accordingly. + +For bugs caused by upstream dependencies, replace `bug` with `bug-upstream` label. Ask the author whether they'd like to +raise the issue upstream or if they prefer us to do so. + +Assess the impact and make the call on whether we need an emergency release. Contact other [maintainers](#current-maintainers) when in doubt. + +See [Common scenarios](#common-scenarios) section for additional guidance. + +### Triage RFCs + +RFC is a collaborative process to help us get to the most optimal solution given the context. Their purpose is to ensure +everyone understands what this context is, their trade-offs, and alternative solutions that were part of the research +before implementation begins. + +Make sure you ask these questions in mind when reviewing: + +- Does it use our [RFC template](https://github.com/aws-powertools/powertools-lambda-java/issues/new?assignees=&labels=RFC%2C+triage&projects=&template=rfc.md&title=RFC%3A+)? +- Does it match our [Tenets](https://docs.powertools.aws.dev/lambda/java/latest/#tenets)? +- Does the proposal address the use case? If so, is the recommended usage explicit? +- Does it focus on the mechanics to solve the use case over fine-grained implementation details? +- Can anyone familiar with the code base implement it? +- If approved, are they interested in contributing? Do they need any guidance? +- Does this significantly increase the overall project maintenance? Do we have the skills to maintain it? +- If we can't take this use case, are there alternative projects we could recommend? Or does it call for a new project altogether? + +When necessary, be upfront that the time to review, approve, and implement a RFC can vary - +see [Contribution is stuck](#contribution-is-stuck). Some RFCs may be further updated after implementation, as certain areas become clearer. + +Some examples using our initial and new RFC templates: #92, #94, #95, #991, #1226 + +### Releasing a new version + +!!! note "The release process is currently a long, multi-step process. The team is in the process of automating at it." + +Firstly, make sure the commit history in the `main` branch **(1)** it's up to date, **(2)** commit messages are semantic, +and **(3)** commit messages have their respective area, for example `feat: <change>`, `chore: ...`). + +**Looks good, what's next?** + +Kickoff the `Prepare for maven central release` workflow with the intended rekease version. Once this has completed, it will +draft a Pull Request named something like `chore: Prep release 1.19.0`. the PR will **(1)** roll all of the POM versions +forward to the new release version and **(2)** release notes. + +Once this is done, check out the branch and clean up the release notes. These will be used both in the +[CHANGELOG.md file](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CHANGELOG.md) +file and the [published github release information](https://github.com/aws-powertools/powertools-lambda-java/releases), +and you can use the existing release notes to see how changes are summarized. + +Next, commit and push, wait for the build to complete, and merge to main. Once main has built successfully (i.e. build, tests and end-to-end tests should pass), create a +tagged release from the Github UI, using the same release notes. + +Next, run the `Publish package to the Maven Central Repository` action to release the library. + +Finally, by hand, create a PR rolling all of the POMs onto the next snapshot version (e.g. `1.20.0-SNAPSHOT`). + + +### Add Continuous Integration Checks + +Add integration checks that validate pull requests and pushes to ease the burden on Pull Request reviewers. +Continuously revisit areas of improvement to reduce operational burden in all parties involved. + +### Negative Impact on the Project +<!-- markdownlint-disable-next-line MD013 --> +Actions that negatively impact the project will be handled by the admins, in coordination with other maintainers, +in balance with the urgency of the issue. Examples would be +[Code of Conduct](https://github.com/aws-powertools/powertools-lambda-java/blob/main/CODE_OF_CONDUCT.md){target="_blank"} +violations, deliberate harmful or malicious actions, spam, monopolization, and security risks. + +## Common scenarios + +These are recurring ambiguous situations that new and existing maintainers may encounter. They serve as guidance. +It is up to each maintainer to follow, adjust, or handle in a different manner as long as +[our conduct is consistent](#uphold-code-of-conduct) + +### Contribution is stuck + +A contribution can get stuck often due to lack of bandwidth and language barrier. For bandwidth issues, +check whether the author needs help. Make sure you get their permission before pushing code into their existing PR - +do not create a new PR unless strictly necessary. + +For language barrier and others, offer a 1:1 chat to get them unblocked. Often times, English might not be their +primary language, and writing in public might put them off, or come across not the way they intended to be. + +In other cases, you may have constrained capacity. Use `help wanted` label when you want to signal other maintainers +and external contributors that you could use a hand to move it forward. + +### Insufficient feedback or information + +When in doubt, use the `need-more-information` label to signal more context and feedback are necessary before proceeding. + +### Crediting contributions + +We credit all contributions as part of each [release note](https://github.com/aws-powertools/powertools-lambda-java/releases){target="_blank"} +as an automated process. If you find contributors are missing from the release note you're producing, please add them manually. + +### Is that a bug? + +A bug produces incorrect or unexpected results at runtime that differ from its intended behavior. +Bugs must be reproducible. They directly affect customers experience at runtime despite following its recommended usage. + +Documentation snippets, use of internal components, or unadvertised functionalities are not considered bugs. + +### Mentoring contributions + +Always favor mentoring issue authors to contribute, unless they're not interested or the implementation is sensitive (_e.g., complexity, time to release, etc._). + +Make use of `help wanted` and `good first issue` to signal additional contributions the community can help. + +### Long running issues or PRs + +Try offering a 1:1 call in the attempt to get to a mutual understanding and clarify areas that maintainers could help. + +In the rare cases where both parties don't have the bandwidth or expertise to continue, it's best to use the `revisit-in-3-months` label. By then, see if it's possible to break the PR or issue in smaller chunks, and eventually close if there is no progress. diff --git a/docs/processes/versioning.md b/docs/processes/versioning.md new file mode 100644 index 000000000..d20269001 --- /dev/null +++ b/docs/processes/versioning.md @@ -0,0 +1,61 @@ +--- +title: Versioning and maintenance policy +description: Versioning and maintenance policy for Powertools for AWS Lambda (Python) +--- + +### Overview + +This document outlines the maintenance policy for Powertools for AWS Lambda and their underlying dependencies. AWS regularly provides Powertools for AWS Lambda with updates that may contain new features, enhancements, bug fixes, security patches, or documentation updates. Updates may also address changes with dependencies, language runtimes, and operating systems. Powertools for AWS Lambda is published to package managers (e.g. PyPi, NPM, Maven, NuGet), and are available as source code on GitHub. + +We recommend users to stay up-to-date with Powertools for AWS Lambda releases to keep up with the latest features, security updates, and underlying dependencies. Continued use of an unsupported Powertools for AWS Lambda version is not recommended and is done at the user’s discretion. + +!!! info "For brevity, we will interchangeably refer to Powertools for AWS Lambda as "SDK" _(Software Development Toolkit)_." + +### Versioning + +Powertools for AWS Lambda release versions are in the form of X.Y.Z where X represents the major version. Increasing the major version of an SDK indicates that this SDK underwent significant and substantial changes to support new idioms and patterns in the language. Major versions are introduced when public interfaces _(e.g. classes, methods, types, etc.)_, behaviors, or semantics have changed. Applications need to be updated in order for them to work with the newest SDK version. It is important to update major versions carefully and in accordance with the upgrade guidelines provided by AWS. + +### SDK major version lifecycle + +The lifecycle for major Powertools for AWS Lambda versions consists of 5 phases, which are outlined below. + +- **Developer Preview** (Phase 0) - During this phase, SDKs are not supported, should not be used in production environments, and are meant for early access and feedback purposes only. It is possible for future releases to introduce breaking changes. Once AWS identifies a release to be a stable product, it may mark it as a Release Candidate. Release Candidates are ready for GA release unless significant bugs emerge, and will receive full AWS support. +- **General Availability (GA)** (Phase 1) - During this phase, SDKs are fully supported. AWS will provide regular SDK releases that include support for new features, enhancements, as well as bug and security fixes. AWS will support the GA version of an SDK for _at least 24 months_, unless otherwise specified. +- **Maintenance Announcement** (Phase 2) - AWS will make a public announcement at least 6 months before an SDK enters maintenance mode. During this period, the SDK will continue to be fully supported. Typically, maintenance mode is announced at the same time as the next major version is transitioned to GA. +- **Maintenance** (Phase 3) - During the maintenance mode, AWS limits SDK releases to address critical bug fixes and security issues only. An SDK will not receive API updates for new or existing services, or be updated to support new regions. Maintenance mode has a _default duration of 6 months_, unless otherwise specified. +- **End-of-Support** (Phase 4) - When an SDK reaches end-of support, it will no longer receive updates or releases. Previously published releases will continue to be available via public package managers and the code will remain on GitHub. The GitHub repository may be archived. Use of an SDK which has reached end-of-support is done at the user’s discretion. We recommend users upgrade to the new major version. + +!!! note "Please note that the timelines shown below are illustrative and not binding" + +![Maintenance policy timelines](https://docs.aws.amazon.com/images/sdkref/latest/guide/images/maint-policy.png) + +### Dependency lifecycle + +Most AWS SDKs have underlying dependencies, such as language runtimes, AWS Lambda runtime, or third party libraries and frameworks. These dependencies are typically tied to the language community or the vendor who owns that particular component. Each community or vendor publishes their own end-of-support schedule for their product. + +The following terms are used to classify underlying third party dependencies: + +- [**AWS Lambda Runtime**](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html): Examples include `java17`, `nodejs20.x`, `python3.13`, etc. +- **Language Runtime**: Examples include Java 17, Python 3.13, NodeJS 20, .NET Core, etc. +- **Third party Library**: Examples include Jackson Project, AWS X-Ray SDK, AWS Encryption SDK, etc. + +Powertools for AWS Lambda follows the [AWS Lambda Runtime deprecation policy cycle](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtime-support-policy), when it comes to Language Runtime. This means we will stop supporting their respective deprecated Language Runtime _(e.g., `java8`)_ without increasing the major SDK version. + +!!! note "AWS reserves the right to stop support for an underlying dependency without increasing the major SDK version" + +### Communication methods + +Maintenance announcements are communicated in several ways: + +- A pinned GitHub Request For Comments (RFC) issue indicating the campaign for the next major version. The RFC will outline the path to end-of-support, specify campaign timelines, and upgrade guidance. +- AWS SDK documentation, such as API reference documentation, user guides, SDK product marketing pages, and GitHub readme(s) are updated to indicate the campaign timeline and provide guidance on upgrading affected applications. +- Deprecation warnings are added to the SDKs, outlining the path to end-of-support and linking to the upgrade guide. + +To see the list of available major versions of Powertools for AWS Lambda and where they are in their maintenance lifecycle, see the [version support matrix](#version-support-matrix). + +### Version support matrix + +| SDK | Major version | Current Phase | General Availability Date | Notes | +| -------------------------------- | ------------- | -------------------- | ------------------------- | ------------------------------------------------------------------------------------------------- | +| Powertools for AWS Lambda (Java) | 2.x | General Availability | 06/12/2025 | See [Release notes](https://github.com/aws-powertools/powertools-lambda-java/releases/tag/v2.0.0) | +| Powertools for AWS Lambda (Java) | 1.x | End-of-life | 11/04/2020 | See [announcement](https://github.com/aws-powertools/powertools-lambda-java/issues/1895) | diff --git a/docs/requirements.in b/docs/requirements.in new file mode 100644 index 000000000..e8acc7112 --- /dev/null +++ b/docs/requirements.in @@ -0,0 +1,3 @@ +mkdocs-git-revision-date-plugin==0.3.2 +mkdocs-macros-plugin==1.3.7 +mkdocs-llmstxt==0.2.0 diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..16529cf3b --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,296 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --generate-hashes --output-file=requirements.txt requirements.in +# +beautifulsoup4==4.13.3 \ + --hash=sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b \ + --hash=sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16 + # via + # markdownify + # mkdocs-llmstxt +click==8.1.8 \ + --hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \ + --hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a + # via mkdocs +ghp-import==2.1.0 \ + --hash=sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619 \ + --hash=sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343 + # via mkdocs +gitdb==4.0.12 \ + --hash=sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571 \ + --hash=sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf + # via gitpython +gitpython==3.1.44 \ + --hash=sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110 \ + --hash=sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269 + # via mkdocs-git-revision-date-plugin +hjson==3.1.0 \ + --hash=sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75 \ + --hash=sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89 + # via + # mkdocs-macros-plugin + # super-collections +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # mkdocs + # mkdocs-git-revision-date-plugin + # mkdocs-macros-plugin +markdown==3.7 \ + --hash=sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2 \ + --hash=sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803 + # via mkdocs +markdown-it-py==3.0.0 \ + --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \ + --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb + # via mdformat +markdownify==1.1.0 \ + --hash=sha256:32a5a08e9af02c8a6528942224c91b933b4bd2c7d078f9012943776fc313eeef \ + --hash=sha256:449c0bbbf1401c5112379619524f33b63490a8fa479456d41de9dc9e37560ebd + # via mkdocs-llmstxt +markupsafe==3.0.2 \ + --hash=sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4 \ + --hash=sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30 \ + --hash=sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0 \ + --hash=sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9 \ + --hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \ + --hash=sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13 \ + --hash=sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028 \ + --hash=sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca \ + --hash=sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557 \ + --hash=sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832 \ + --hash=sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0 \ + --hash=sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b \ + --hash=sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579 \ + --hash=sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a \ + --hash=sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c \ + --hash=sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff \ + --hash=sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c \ + --hash=sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22 \ + --hash=sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094 \ + --hash=sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb \ + --hash=sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e \ + --hash=sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5 \ + --hash=sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a \ + --hash=sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d \ + --hash=sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a \ + --hash=sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b \ + --hash=sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8 \ + --hash=sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 \ + --hash=sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c \ + --hash=sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144 \ + --hash=sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f \ + --hash=sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87 \ + --hash=sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d \ + --hash=sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93 \ + --hash=sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf \ + --hash=sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158 \ + --hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \ + --hash=sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb \ + --hash=sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48 \ + --hash=sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171 \ + --hash=sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c \ + --hash=sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6 \ + --hash=sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd \ + --hash=sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d \ + --hash=sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1 \ + --hash=sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d \ + --hash=sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca \ + --hash=sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a \ + --hash=sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29 \ + --hash=sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe \ + --hash=sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798 \ + --hash=sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c \ + --hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 \ + --hash=sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f \ + --hash=sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f \ + --hash=sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a \ + --hash=sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178 \ + --hash=sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0 \ + --hash=sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79 \ + --hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 \ + --hash=sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50 + # via + # jinja2 + # mkdocs +mdformat==0.7.22 \ + --hash=sha256:61122637c9e1d9be1329054f3fa216559f0d1f722b7919b060a8c2a4ae1850e5 \ + --hash=sha256:eef84fa8f233d3162734683c2a8a6222227a229b9206872e6139658d99acb1ea + # via mkdocs-llmstxt +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +mergedeep==1.3.4 \ + --hash=sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8 \ + --hash=sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307 + # via + # mkdocs + # mkdocs-get-deps +mkdocs==1.6.1 \ + --hash=sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2 \ + --hash=sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e + # via + # mkdocs-git-revision-date-plugin + # mkdocs-macros-plugin +mkdocs-get-deps==0.2.0 \ + --hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \ + --hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134 + # via mkdocs +mkdocs-git-revision-date-plugin==0.3.2 \ + --hash=sha256:2e67956cb01823dd2418e2833f3623dee8604cdf223bddd005fe36226a56f6ef + # via -r requirements.in +mkdocs-llmstxt==0.2.0 \ + --hash=sha256:104f10b8101167d6baf7761942b4743869be3d8f8a8d909f4e9e0b63307f709e \ + --hash=sha256:907de892e0c8be74002e8b4d553820c2b5bbcf03cc303b95c8bca48fb49c1a29 + # via -r requirements.in +mkdocs-macros-plugin==1.3.7 \ + --hash=sha256:02432033a5b77fb247d6ec7924e72fc4ceec264165b1644ab8d0dc159c22ce59 \ + --hash=sha256:17c7fd1a49b94defcdb502fd453d17a1e730f8836523379d21292eb2be4cb523 + # via -r requirements.in +packaging==24.2 \ + --hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \ + --hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f + # via + # mkdocs + # mkdocs-macros-plugin +pathspec==0.12.1 \ + --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \ + --hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712 + # via + # mkdocs + # mkdocs-macros-plugin +platformdirs==4.3.7 \ + --hash=sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94 \ + --hash=sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351 + # via mkdocs-get-deps +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via + # ghp-import + # mkdocs-macros-plugin +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via + # mkdocs + # mkdocs-get-deps + # mkdocs-macros-plugin + # pyyaml-env-tag +pyyaml-env-tag==0.1 \ + --hash=sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb \ + --hash=sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069 + # via mkdocs +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via + # markdownify + # python-dateutil +smmap==5.0.2 \ + --hash=sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5 \ + --hash=sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e + # via gitdb +soupsieve==2.6 \ + --hash=sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb \ + --hash=sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9 + # via beautifulsoup4 +super-collections==0.5.3 \ + --hash=sha256:907d35b25dc4070910e8254bf2f5c928348af1cf8a1f1e8259e06c666e902cff \ + --hash=sha256:94c1ec96c0a0d5e8e7d389ed8cde6882ac246940507c5e6b86e91945c2968d46 + # via mkdocs-macros-plugin +termcolor==3.0.1 \ + --hash=sha256:a6abd5c6e1284cea2934443ba806e70e5ec8fd2449021be55c280f8a3731b611 \ + --hash=sha256:da1ed4ec8a5dc5b2e17476d859febdb3cccb612be1c36e64511a6f2485c10c69 + # via mkdocs-macros-plugin +typing-extensions==4.13.2 \ + --hash=sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c \ + --hash=sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef + # via beautifulsoup4 +watchdog==6.0.0 \ + --hash=sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a \ + --hash=sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2 \ + --hash=sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f \ + --hash=sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c \ + --hash=sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c \ + --hash=sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c \ + --hash=sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0 \ + --hash=sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13 \ + --hash=sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134 \ + --hash=sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa \ + --hash=sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e \ + --hash=sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379 \ + --hash=sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a \ + --hash=sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11 \ + --hash=sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282 \ + --hash=sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b \ + --hash=sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f \ + --hash=sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c \ + --hash=sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112 \ + --hash=sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948 \ + --hash=sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881 \ + --hash=sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860 \ + --hash=sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3 \ + --hash=sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680 \ + --hash=sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26 \ + --hash=sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26 \ + --hash=sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e \ + --hash=sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8 \ + --hash=sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c \ + --hash=sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2 + # via mkdocs diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 000000000..4ef1ef06e --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,142 @@ +--- +title: Roadmap +description: Public roadmap for Powertools for AWS Lambda (Java) +--- + +## Overview + +Our public roadmap outlines the high level direction we are working towards. We update this document when our priorities change: security and stability are our top priority. + +### Key areas + +Security and operational excellence take precedence above all else. This means bug fixing, stability, customer's support, and internal compliance may delay one or more key areas below. + +!!! info "We may choose to re-prioritize or defer items based on customer feedback, security, and operational impacts, and business value." + +#### Release Security (p0) + +Our top priority is to establish the processes and infrastructure needed for a fully automated and secure end-to-end release process of new versions to Maven Central. + +- [x] [Implement GitHub workflows](https://github.com/aws-powertools/powertools-lambda-java/issues/1231){target="\_blank"} and create infrastructure to release to Maven Central +- [x] [Implement end-to-end tests](https://github.com/aws-powertools/powertools-lambda-java/issues/1815){target="\_blank"} +- [x] Implement [OpenSSF Scorecard](https://openssf.org/projects/scorecard/){target="\_blank"} + +#### `v2` Release: Consistency and Ecosystem (p1) + +As part of a new major version `v2` release, we prioritize the Java project's consistency of core utilities (Logging, Metrics, Tracing) with the other runtimes (Python, TypeScript, .NET). Additionally, we will focus on integrating the library with popular technologies and frameworks from the Java and AWS ecosystem. Particularly, we aim at leveraging new techniques to allow customers to reduce Lambda cold-start time. The `v2` release will also drop support for Java 8 moving to Java 11 as the baseline. + +##### Core Utilities + +- [x] [Review public interfaces and reduce public API surface area](https://github.com/aws-powertools/powertools-lambda-java/issues/1283){target="\_blank"} +- [x] [Release Logging `v2` module](https://github.com/aws-powertools/powertools-lambda-java/issues/965){target="\_blank"} allowing customers to choose the logging framework and adding support for logging deeply nested objects as JSON +- [x] [Support high resolution metrics](https://github.com/aws-powertools/powertools-lambda-java/issues/1041){target="\_blank"} +- [x] [Improve modularity of metrics module](https://github.com/aws-powertools/powertools-lambda-java/issues/1848){target="\_blank"} to remove coupling with EMF library and enable future support for additional metrics providers / backends + +##### Ecosystem + +- [x] [Add GraalVM support for core utilities](https://github.com/aws-powertools/powertools-lambda-java/issues/764){target="\_blank"} +- [ ] [Implement priming using CRaC to improve AWS Snapstart support](https://github.com/aws-powertools/powertools-lambda-java/issues/1588){target="\_blank"} +- [ ] [Evaluate integration with popular Java frameworks such as Micronaut, Spring Cloud Function, or Quarkus](https://github.com/aws-powertools/powertools-lambda-java/issues/1701){target="\_blank"} + +##### Other + +- [x] [Validation module integration with HTTP requests](https://github.com/aws-powertools/powertools-lambda-java/issues/1298){target="\_blank"} +- [x] [Support validation module from within the batch module](https://github.com/aws-powertools/powertools-lambda-java/issues/1496){target="\_blank"} +- [x] [Add support for parallel processing in Batch Processing utility](https://github.com/aws-powertools/powertools-lambda-java/issues/1540){target="\_blank"} +- [ ] [Documentation: Review and improve documentation to be consistent with other runtimes](https://github.com/aws-powertools/powertools-lambda-java/issues/1352){target="\_blank"} + +#### Feature Parity (p2) + +If priorities `p0` and `p1` are addressed, we will also focus on feature parity of non-core utilities. This allows customers to achieve better standardization of their development processes across different Powertools runtimes. + +- [ ] [Re-evaluate if there is a need for adding a lightweight customer Powertools event handler](https://github.com/aws-powertools/powertools-lambda-java/issues/1103){target="\_blank"} +- [ ] [Add comprehensive GraalVM support for all utilities](){target="\_blank"} +- [ ] [Add Feature Flags module](https://github.com/aws-powertools/powertools-lambda-java/issues/1086){target="\_blank"} +- [ ] [Add S3 Streaming module](https://github.com/aws-powertools/powertools-lambda-java/issues/1085){target="\_blank"} +- [ ] Add support for Data Masking during JSON serialization + +### Missing something? + +You can help us prioritize by [upvoting existing feature requests](https://github.com/aws-powertools/powertools-lambda-java/issues?q=is%3Aissue%20state%3Aopen%20label%3Aenhancement){target="\_blank"}, +leaving a comment on what use cases it could unblock for you, and by joining our discussions on Discord. + +[![Join our Discord](https://dcbadge.vercel.app/api/server/B8zZKbbyET)](https://discord.gg/B8zZKbbyET){target="\_blank"} + +### Roadmap status definition + +<center> +```mermaid +graph LR + Ideas --> Backlog --> Work["Working on it"] --> Merged["Coming soon"] --> Shipped +``` +<i>Visual representation</i> +</center> + +Within our [public board](https://github.com/orgs/aws-powertools/projects/4/){target="\_blank"}, you'll see the following values in the `Status` column: + +- **Ideas**. Incoming and existing feature requests that are not being actively considered yet. These will be reviewed + when bandwidth permits. +- **Backlog**. Accepted feature requests or enhancements that we want to work on. +- **Working on it**. Features or enhancements we're currently either researching or implementing it. +- **Coming soon**. Any feature, enhancement, or bug fixes that have been merged and are coming in the next release. +- **Shipped**. Features or enhancements that are now available in the most recent release. + +> Tasks or issues with empty `Status` will be categorized in upcoming review cycles. + +### Process + +<center> +```mermaid +graph LR + PFR[Feature request] --> Triage{Need RFC?} + Triage --> |Complex/major change or new utility?| RFC[Ask or write RFC] --> Approval{Approved?} + Triage --> |Minor feature or enhancement?| NoRFC[No RFC required] --> Approval + Approval --> |Yes| Backlog + Approval --> |No | Reject["Inform next steps"] + Backlog --> |Prioritized| Implementation + Backlog --> |Defer| WelcomeContributions["help-wanted label"] +``` +<i>Visual representation</i> +</center> + +Our end-to-end mechanism follows four major steps: + +- **Feature Request**. Ideas start with a [feature request](https://github.com/aws-powertools/powertools-lambda-java/issues/new?template=feature_request.md){target="\_blank"} to outline their use case at a high level. For complex use cases, maintainers might ask for/write a + RFC. + - Maintainers review requests based on [project tenets](index.md#tenets){target="\_blank"}, customers reaction (👍), + and use cases. +- **Request-for-comments (RFC)**. Design proposals use + our [RFC template](https://github.com/aws-powertools/powertools-lambda-java/issues/new?q=is%3Aissue+state%3Aopen+label%3Aenhancement&template=rfc.md){target="\_blank"} to describe its implementation, challenges, developer experience, dependencies, and alternative solutions. + - This helps refine the initial idea with community feedback before a decision is made. +- **Decision**. After carefully reviewing and discussing them, maintainers make a final decision on whether to start + implementation, defer or reject it, and update everyone with the next steps. +- **Implementation**. For approved features, maintainers give priority to the original authors for implementation unless + it is a sensitive task that is best handled by maintainers. + +!!! info "See [Maintainers](./processes/maintainers.md){target="\_blank"} document to understand how we triage issues and pull requests, labels and governance." + +### Disclaimer + +The Powertools for AWS Lambda (Java) team values feedback and guidance from its community of users, although final +decisions on inclusion into the project will be made by AWS. + +We determine the high-level direction for our open roadmap based on customer feedback and popularity (👍🏽 and comments), +security and operational impacts, and business value. Where features don’t meet our goals and longer-term strategy, we +will communicate that clearly and openly as quickly as possible with an explanation of why the decision was made. + +### FAQs + +**Q: Why did you build this?** + +A: We know that our customers are making decisions and plans based on what we are developing, and we want to provide our +customers the insights they need to plan. + +**Q: Why are there no dates on your roadmap?** + +A: Because job zero is security and operational stability, we can't provide specific target dates for features. The +roadmap is subject to change at any time, and roadmap issues in this repository do not guarantee a feature will be +launched as proposed. + +**Q: How can I provide feedback or ask for more information?** + +A: For existing features, you can directly comment on issues. For anything else, please open an issue. diff --git a/docs/src/assets/aws-logo.svg b/docs/src/assets/aws-logo.svg deleted file mode 100644 index 92d2f3b7d..000000000 --- a/docs/src/assets/aws-logo.svg +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg viewBox="0 0 116 70" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <title>aws-logo - - - - diff --git a/docs/src/components/Note/index.js b/docs/src/components/Note/index.js deleted file mode 100644 index 5bd3b4c55..000000000 --- a/docs/src/components/Note/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react' -import { Alert } from 'antd' - - -/** - * Note is a React component that renders an Alert box message - * - * @param {object} obj - * @param {string} obj.type - Type of alert (success, info, warning, error) - * @param {string} obj.title - Title for the alert - * @param {string} obj.children - Alert message - */ -const Note = ({ type = 'info', title = '', children }) => { - return ( - - ) -} - -export default Note diff --git a/docs/src/config.js b/docs/src/config.js deleted file mode 100644 index e158be4bb..000000000 --- a/docs/src/config.js +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint-disable */ - -const awsmobile = { - "aws_project_region": "eu-west-1", - "aws_cognito_identity_pool_id": "eu-west-1:3df3caec-4bb6-4891-b154-ee940c8264b8", - "aws_cognito_region": "eu-west-1", - "aws_kinesis_firehose_stream_name": "ClickStreamKinesisFirehose-OGX7PQdrynUo", - -}; - - -export default awsmobile; diff --git a/docs/src/gatsby-theme-apollo-core/components/flex-wrapper.js b/docs/src/gatsby-theme-apollo-core/components/flex-wrapper.js deleted file mode 100644 index 94e1cd42e..000000000 --- a/docs/src/gatsby-theme-apollo-core/components/flex-wrapper.js +++ /dev/null @@ -1,10 +0,0 @@ -import styled from '@emotion/styled'; - -const FlexWrapper = styled.div({ - display: 'flex', - minHeight: '100vh', - maxWidth: 1600, - margin: '0 auto' -}); - -export default FlexWrapper; diff --git a/docs/src/gatsby-theme-apollo-core/components/logo.js b/docs/src/gatsby-theme-apollo-core/components/logo.js deleted file mode 100644 index 29b5f669f..000000000 --- a/docs/src/gatsby-theme-apollo-core/components/logo.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import styled from '@emotion/styled'; -import { ReactComponent as AWSLogo } from '../../assets/aws-logo.svg'; - - -const Wrapper = styled.div({ - display: 'flex', - padding: '0 60px', - height: '5vh' -}); - -const StyledAwsIcon = styled(AWSLogo)({ - width: '100%' -}); - -export default function Logo() { - return ( - - - - ); -} diff --git a/docs/src/gatsby-theme-apollo-docs/components/docset-switcher.js b/docs/src/gatsby-theme-apollo-docs/components/docset-switcher.js deleted file mode 100644 index e4a0f3b60..000000000 --- a/docs/src/gatsby-theme-apollo-docs/components/docset-switcher.js +++ /dev/null @@ -1,264 +0,0 @@ - -import PropTypes from 'prop-types'; -import React, { Fragment, useEffect, useMemo, useRef } from 'react'; -import styled from '@emotion/styled'; -import useKey from 'react-use/lib/useKey'; -import useWindowSize from 'react-use/lib/useWindowSize'; -import { IconTwitter } from '@apollo/space-kit/icons/IconTwitter'; -import { IconYoutube } from '@apollo/space-kit/icons/IconYoutube'; -import { boxShadow } from './search'; -import { breakpoints, colors, smallCaps } from 'gatsby-theme-apollo-core'; -import { size, transparentize } from 'polished'; - -const Wrapper = styled.div({ - width: '100%', - height: '100%', - backgroundColor: transparentize(0.5, colors.text2), - overflow: 'auto', - position: 'fixed', - top: 0, - left: 0, - zIndex: 3, - perspective: '1000px', - transitionProperty: 'opacity, visibility', - transitionDuration: '150ms', - transitionTimingFunction: 'ease-in-out' -}); - -const transitionDuration = 150; // in ms -const Menu = styled.div({ - width: 700, - marginBottom: 24, - borderRadius: 4, - boxShadow, - backgroundColor: 'white', - overflow: 'hidden', - position: 'absolute', - transformOrigin: '25% 25%', - transition: `transform ${transitionDuration}ms ease-in-out`, - outline: 'none', - [breakpoints.md]: { - width: 450 - }, - [breakpoints.sm]: { - width: 'calc(100vw - 48px)' - } -}); - -const MenuTitle = styled.h6(smallCaps, { - margin: 24, - marginBottom: 0, - fontSize: 13, - fontWeight: 600, - color: colors.text3 -}); - -const StyledNav = styled.nav({ - display: 'flex', - flexWrap: 'wrap', - margin: 12 -}); - -const NavItem = styled.div({ - display: 'block', - width: '50%', - [breakpoints.md]: { - width: '100%' - } -}); - -const NavItemInner = styled.a({ - display: 'block', - height: '100%', - padding: 12, - borderRadius: 4, - color: colors.text1, - textDecoration: 'none', - backgroundColor: 'transparent', - transitionProperty: 'color, background-color', - transitionDuration: '150ms', - transitionTimingFunction: 'ease-in-out', - '@media (hover: hover)': { - ':hover': { - color: 'white', - backgroundColor: colors.primary, - p: { - color: colors.primaryLight - } - } - } -}); - -export const NavItemTitle = styled.h4({ - marginBottom: 8, - fontWeight: 600, - color: 'inherit' -}); - -export const NavItemDescription = styled.p({ - marginBottom: 0, - fontSize: 14, - lineHeight: 1.5, - color: colors.text3, - transition: 'color 150ms ease-in-out' -}); - -const FooterNav = styled.nav({ - display: 'flex', - alignItems: 'center', - padding: '16px 24px', - backgroundColor: colors.background, - [breakpoints.md]: { - display: 'block' - } -}); - -const FooterNavItem = styled.a({ - color: colors.text2, - textDecoration: 'none', - ':hover': { - color: colors.text3 - }, - ':not(:last-child)': { - marginRight: 24 - } -}); - -const SocialLinks = styled.div({ - display: 'flex', - marginLeft: 'auto', - [breakpoints.md]: { - marginTop: 8 - } -}); - -const SocialLink = styled.a({ - color: colors.text2, - ':hover': { - color: colors.text3 - }, - ':not(:last-child)': { - marginRight: 24 - }, - svg: { - ...size(24), - display: 'block', - fill: 'currentColor' - } -}); - -export default function DocsetSwitcher(props) { - const menuRef = useRef(null); - const { width } = useWindowSize(); - useKey('Escape', props.onClose); - - useEffect(() => { - if (props.open) { - // focus the menu after it has been transitioned in - setTimeout(() => { - menuRef.current.focus(); - }, transitionDuration); - } - }, [props.open]); - - const { current } = props.buttonRef; - const menuStyles = useMemo(() => { - if (!current) { - return null; - } - - const { top, left, height } = current.getBoundingClientRect(); - return { - top: top + height + 2, - left - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [current, width, props.open]); - - function handleWrapperClick(event) { - if (event.target === event.currentTarget) { - props.onClose(); - } - } - - const hasSocialUrls = Boolean( - props.spectrumUrl || props.twitterUrl || props.youtubeUrl - ); - return ( - - - {props.siteName} - - {props.navItems.map(navItem => ( - - - {navItem.title} - {navItem.description} - - - ))} - - {(props.footerNavConfig || hasSocialUrls) && ( - - - {props.footerNavConfig && - Object.entries(props.footerNavConfig).map(([text, props]) => ( - - {text} - - ))} - {hasSocialUrls && ( - - {props.twitterUrl && ( - - - - )} - {props.youtubeUrl && ( - - - - )} - - )} - - - )} - - - ); -} - -DocsetSwitcher.propTypes = { - open: PropTypes.bool.isRequired, - onClose: PropTypes.func.isRequired, - buttonRef: PropTypes.object.isRequired, - siteName: PropTypes.string.isRequired, - navItems: PropTypes.array.isRequired, - footerNavConfig: PropTypes.object.isRequired, - spectrumUrl: PropTypes.string, - twitterUrl: PropTypes.string, - youtubeUrl: PropTypes.string -}; diff --git a/docs/src/gatsby-theme-apollo-docs/components/header-button.js b/docs/src/gatsby-theme-apollo-docs/components/header-button.js deleted file mode 100644 index 5830a97de..000000000 --- a/docs/src/gatsby-theme-apollo-docs/components/header-button.js +++ /dev/null @@ -1,2 +0,0 @@ -// Shadow Header built-in component and remove `Launch Graph Manager` -export default () => null; diff --git a/docs/src/gatsby-theme-apollo-docs/components/header.js b/docs/src/gatsby-theme-apollo-docs/components/header.js deleted file mode 100644 index 25cd50807..000000000 --- a/docs/src/gatsby-theme-apollo-docs/components/header.js +++ /dev/null @@ -1,37 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import styled from '@emotion/styled'; -import { HEADER_HEIGHT } from '../utils'; -import { breakpoints } from 'gatsby-theme-apollo-core'; - -const Wrapper = styled.header({ - position: 'sticky', - top: 0, - left: 0, - zIndex: 1 -}); - -const InnerWrapper = styled.div({ - display: 'flex', - alignItems: 'center', - height: HEADER_HEIGHT, - padding: '0 56px', - backgroundColor: 'white', - [breakpoints.md]: { - padding: '0 24px' - } -}); - -export default function Header(props) { - return ( - - {props.beforeContent} - {props.children} - - ); -} - -Header.propTypes = { - beforeContent: PropTypes.node.isRequired, - children: PropTypes.node.isRequired -}; diff --git a/docs/src/gatsby-theme-apollo-docs/components/mobile-logo.js b/docs/src/gatsby-theme-apollo-docs/components/mobile-logo.js deleted file mode 100644 index 93b73a25f..000000000 --- a/docs/src/gatsby-theme-apollo-docs/components/mobile-logo.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import styled from '@emotion/styled'; - -import { ReactComponent as Logo } from '../../assets/aws-logo.svg'; - -const Wrapper = styled.div({ - display: 'flex', - height: '5vh' -}); - -const AwsMobileLogo = styled(Logo)({ - height: 'auto' -}); - -export function MobileLogo() { - return ( - - - - ); -} diff --git a/docs/src/gatsby-theme-apollo-docs/components/multi-code-block.js b/docs/src/gatsby-theme-apollo-docs/components/multi-code-block.js deleted file mode 100644 index c9346ebb5..000000000 --- a/docs/src/gatsby-theme-apollo-docs/components/multi-code-block.js +++ /dev/null @@ -1,127 +0,0 @@ -import PropTypes from 'prop-types'; -import React, {createContext, useContext, useMemo} from 'react'; -import styled from '@emotion/styled'; -import {trackCustomEvent} from 'gatsby-plugin-google-analytics'; - -export const GA_EVENT_CATEGORY_CODE_BLOCK = 'Code Block'; -export const MultiCodeBlockContext = createContext({}); -export const SelectedLanguageContext = createContext(); - -const Container = styled.div({ - position: 'relative' -}); - -const langLabels = { - js: 'JavaScript', - ts: 'TypeScript', - 'hooks-js': 'Hooks (JS)', - 'hooks-ts': 'Hooks (TS)' -}; - -function getUnifiedLang(language) { - switch (language) { - case 'js': - case 'jsx': - case 'javascript': - return 'js'; - case 'ts': - case 'tsx': - case 'typescript': - return 'ts'; - default: - return language; - } -} - -function getLang(child) { - return getUnifiedLang(child.props['data-language']); -} - -export function MultiCodeBlock(props) { - const {codeBlocks, titles} = useMemo(() => { - const defaultState = { - codeBlocks: {}, - titles: {} - }; - - if (!Array.isArray(props.children)) { - return defaultState; - } - - return props.children.reduce((acc, child, index, array) => { - const lang = getLang(child); - if (lang) { - return { - ...acc, - codeBlocks: { - ...acc.codeBlocks, - [lang]: child - } - }; - } - - if (child.props.className === 'gatsby-code-title') { - const nextNode = array[index + 1]; - const title = child.props.children; - const lang = getLang(nextNode); - if (nextNode && title && lang) { - return { - ...acc, - titles: { - ...acc.titles, - [lang]: title - } - }; - } - } - - return acc; - }, defaultState); - }, [props.children]); - - const languages = useMemo(() => Object.keys(codeBlocks), [codeBlocks]); - const [selectedLanguage, setSelectedLanguage] = useContext( - SelectedLanguageContext - ); - - if (!languages.length) { - return props.children; - } - - function handleLanguageChange(language) { - setSelectedLanguage(language); - trackCustomEvent({ - category: GA_EVENT_CATEGORY_CODE_BLOCK, - action: 'Change language', - label: language - }); - } - - const defaultLanguage = languages[0]; - const renderedLanguage = - selectedLanguage in codeBlocks ? selectedLanguage : defaultLanguage; - - return ( - - ({ - lang, - label: - // try to find a label or capitalize the provided lang - langLabels[lang] || lang.charAt(0).toUpperCase() + lang.slice(1) - })), - onLanguageChange: handleLanguageChange - }} - > -
{titles[renderedLanguage]}
- {codeBlocks[renderedLanguage]} -
-
- ); -} - -MultiCodeBlock.propTypes = { - children: PropTypes.node.isRequired -}; diff --git a/docs/src/gatsby-theme-apollo-docs/components/page-content.js b/docs/src/gatsby-theme-apollo-docs/components/page-content.js deleted file mode 100644 index 442efe7f6..000000000 --- a/docs/src/gatsby-theme-apollo-docs/components/page-content.js +++ /dev/null @@ -1,233 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { useRef, useState } from 'react'; -import SectionNav from './section-nav'; -import styled from '@emotion/styled'; -import useMount from 'react-use/lib/useMount'; -import { HEADER_HEIGHT } from '../utils'; -import { IconGithub } from '@apollo/space-kit/icons/IconGithub'; -import { PageNav, breakpoints, colors } from 'gatsby-theme-apollo-core'; -import { withPrefix } from 'gatsby'; - -const Wrapper = styled.div({ - display: 'flex', - alignItems: 'flex-start' -}); - -const InnerWrapper = styled.div({ - flexGrow: 1, - width: 0 -}); - -const BodyContent = styled.div({ - // style all anchors with an href and no prior classes - // this helps avoid anchors with names and styled buttons - 'a[href]:not([class])': { - color: colors.primary, - textDecoration: 'none', - ':hover': { - textDecoration: 'underline' - }, - code: { - color: 'inherit' - } - }, - [['h1', 'h2', 'h3', 'h4', 'h5', 'h6']]: { - code: { - whiteSpace: 'normal' - }, - a: { - color: 'inherit', - textDecoration: 'none', - ':hover': { - color: colors.text2 - }, - '&.anchor': { - display: 'none' - } - } - }, - '*:not(style) +': { - [['h2', 'h3', 'h4']]: { - marginTop: 56 - } - }, - img: { - display: 'block', - maxWidth: '100%', - margin: '0 auto' - }, - '.mermaid svg': { - maxWidth: '100%' - } -}); - -const Aside = styled.aside({ - display: 'flex', - flexDirection: 'column', - flexShrink: 0, - width: 240, - maxHeight: `calc(100vh - ${HEADER_HEIGHT}px)`, - marginTop: -36, - padding: '40px 0', - marginLeft: 40, - position: 'sticky', - top: HEADER_HEIGHT, - [breakpoints.lg]: { - display: 'none' - }, - [breakpoints.md]: { - display: 'block' - }, - [breakpoints.sm]: { - display: 'none' - } -}); - -const AsideHeading = styled.h4({ - fontWeight: 600 -}); - -const AsideLinkWrapper = styled.h5({ - display: 'flex', - marginBottom: 0, - ':not(:last-child)': { - marginBottom: 16 - } -}); - -const AsideLinkInner = styled.a({ - display: 'flex', - alignItems: 'center', - color: colors.text2, - textDecoration: 'none', - ':hover': { - color: colors.text3 - }, - svg: { - width: 20, - height: 20, - marginRight: 6, - fill: 'currentColor' - } -}); - -function AsideLink(props) { - return ( - - - - ); -} - -const EditLink = styled.div({ - display: 'none', - marginTop: 48, - justifyContent: 'flex-end', - [breakpoints.lg]: { - display: 'flex' - }, - [breakpoints.md]: { - display: 'none' - }, - [breakpoints.sm]: { - display: 'flex', - marginTop: 24 - } -}); - -export default function PageContent(props) { - const contentRef = useRef(null); - const [imagesToLoad, setImagesToLoad] = useState(0); - const [imagesLoaded, setImagesLoaded] = useState(0); - - useMount(() => { - if (props.hash) { - // turn numbers at the beginning of the hash to unicode - // see https://stackoverflow.com/a/20306237/8190832 - const hash = props.hash.toLowerCase().replace(/^#(\d)/, '#\\3$1 '); - try { - const hashElement = contentRef.current.querySelector(hash); - if (hashElement) { - hashElement.scrollIntoView(); - } - } catch (error) { - // let errors pass - } - } - - let toLoad = 0; - const images = contentRef.current.querySelectorAll('img'); - images.forEach(image => { - if (!image.complete) { - image.addEventListener('load', handleImageLoad); - toLoad++; - } - }); - - setImagesToLoad(toLoad); - }); - - function handleImageLoad() { - setImagesLoaded(prevImagesLoaded => prevImagesLoaded + 1); - } - - const pageIndex = props.pages.findIndex(page => { - const prefixedPath = withPrefix(page.path); - return ( - prefixedPath === props.pathname || - prefixedPath.replace(/\/$/, '') === props.pathname - ); - }); - - const githubUrl = props.githubUrl.replace("tree/", "blob/") - .replace("/content/", "/docs/content/") - const sourceUrl = /.+?(?=tree)/.exec(props.githubUrl) - - const editLink = githubUrl && ( - - Edit on GitHub - - ); - - return ( - - - - {props.children} - - {editLink} - - - - - ); -} - -PageContent.propTypes = { - children: PropTypes.node.isRequired, - pathname: PropTypes.string.isRequired, - githubUrl: PropTypes.string.isRequired, - pages: PropTypes.array.isRequired, - hash: PropTypes.string.isRequired, - title: PropTypes.string.isRequired, - graphManagerUrl: PropTypes.string, - headings: PropTypes.array.isRequired, - spectrumUrl: PropTypes.string -}; - diff --git a/docs/src/gatsby-theme-apollo-docs/components/page-layout.js b/docs/src/gatsby-theme-apollo-docs/components/page-layout.js deleted file mode 100644 index 7c97481ab..000000000 --- a/docs/src/gatsby-theme-apollo-docs/components/page-layout.js +++ /dev/null @@ -1,312 +0,0 @@ -import '../prism.less'; -import 'prismjs/plugins/line-numbers/prism-line-numbers.css'; -import DocsetSwitcher from './docset-switcher'; -import Header from './header'; -import HeaderButton from './header-button'; -import PropTypes from 'prop-types'; -import React, {createContext, useMemo, useRef, useState} from 'react'; -import Search from './search'; -import styled from '@emotion/styled'; -import useLocalStorage from 'react-use/lib/useLocalStorage'; -import {Button} from '@apollo/space-kit/Button'; -import { - FlexWrapper, - Layout, - MenuButton, - Sidebar, - SidebarNav, - breakpoints, - colors, - useResponsiveSidebar -} from 'gatsby-theme-apollo-core'; -import {Helmet} from 'react-helmet'; -import {IconLayoutModule} from '@apollo/space-kit/icons/IconLayoutModule'; -import {Link, graphql, navigate, useStaticQuery} from 'gatsby'; -import {MobileLogo} from './mobile-logo'; -import {Select} from './select'; -import {SelectedLanguageContext} from './multi-code-block'; -import {getSpectrumUrl, getVersionBasePath} from '../utils'; -import {groupBy} from 'lodash'; -import {size} from 'polished'; -import {trackCustomEvent} from 'gatsby-plugin-google-analytics'; - -const Main = styled.main({ - flexGrow: 1 -}); - -const ButtonWrapper = styled.div({ - flexGrow: 1 -}); - -const StyledButton = styled(Button)({ - width: '100%', - ':not(:hover)': { - backgroundColor: colors.background - } -}); - -const StyledIcon = styled(IconLayoutModule)(size(16), { - marginLeft: 'auto' -}); - -const MobileNav = styled.div({ - display: 'none', - [breakpoints.md]: { - display: 'flex', - alignItems: 'center', - marginRight: 32, - color: colors.text1 - } -}); - -const HeaderInner = styled.span({ - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - marginBottom: 32 -}); - -const Eyebrow = styled.div({ - flexShrink: 0, - padding: '8px 56px', - backgroundColor: colors.background, - color: colors.primary, - fontSize: 14, - position: 'sticky', - top: 0, - a: { - color: 'inherit', - fontWeight: 600 - }, - [breakpoints.md]: { - padding: '8px 24px' - } -}); - -function getVersionLabel(version) { - return `v${version}`; -} - -const GA_EVENT_CATEGORY_SIDEBAR = 'Sidebar'; - -function handleToggleAll(expanded) { - trackCustomEvent({ - category: GA_EVENT_CATEGORY_SIDEBAR, - action: 'Toggle all', - label: expanded ? 'expand' : 'collapse' - }); -} - -function handleToggleCategory(label, expanded) { - trackCustomEvent({ - category: GA_EVENT_CATEGORY_SIDEBAR, - action: 'Toggle category', - label, - value: Number(expanded) - }); -} - -export const NavItemsContext = createContext(); - -export default function PageLayout(props) { - const data = useStaticQuery( - graphql` - { - site { - siteMetadata { - title - siteName - } - } - } - ` - ); - - const { - sidebarRef, - openSidebar, - sidebarOpen, - handleWrapperClick, - handleSidebarNavLinkClick - } = useResponsiveSidebar(); - - const buttonRef = useRef(null); - const [menuOpen, setMenuOpen] = useState(false); - const selectedLanguageState = useLocalStorage('docs-lang'); - - function openMenu() { - setMenuOpen(true); - } - - function closeMenu() { - setMenuOpen(false); - } - - const {pathname} = props.location; - const {siteName, title} = data.site.siteMetadata; - const { - subtitle, - sidebarContents, - versions, - versionDifference, - versionBasePath, - defaultVersion - } = props.pageContext; - const { - spectrumHandle, - twitterHandle, - youtubeUrl, - navConfig = {}, - footerNavConfig, - logoLink, - algoliaApiKey, - algoliaIndexName, - menuTitle - } = props.pluginOptions; - - const {navItems, navCategories} = useMemo(() => { - const navItems = Object.entries(navConfig).map(([title, navItem]) => ({ - ...navItem, - title - })); - return { - navItems, - navCategories: Object.entries(groupBy(navItems, 'category')) - }; - }, [navConfig]); - - const hasNavItems = navItems.length > 0; - const sidebarTitle = ( - {subtitle || siteName} - ); - - return ( - - - - - - - - - {hasNavItems ? ( - - - {sidebarTitle} - - - - ) : ( - sidebarTitle - )} - {versions && versions.length > 0 && ( -