diff --git a/.reuse/dep5 b/.reuse/dep5 deleted file mode 100644 index ea929c65..00000000 --- a/.reuse/dep5 +++ /dev/null @@ -1,30 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: CF Java Logging Support -Upstream-Contact: -Source: https://github.com/SAP/cf-java-logging-support -Disclaimer: The code in this project may include calls to APIs (“API Calls”) of - SAP or third-party products or services developed outside of this project - (“External Products”). - “APIs” means application programming interfaces, as well as their respective - specifications and implementing code that allows software to communicate with - other software. - API Calls to External Products are not licensed under the open source license - that governs this project. The use of such API Calls and related External - Products are subject to applicable additional agreements with the relevant - provider of the External Products. In no event shall the open source license - that governs this project grant any rights in or to any External Products,or - alter, expand or supersede any terms of the applicable additional agreements. - If you have a valid license agreement with SAP for the use of a particular SAP - External Product, then you may make use of any API Calls included in this - project’s code for that SAP External Product, subject to the terms of such - license agreement. If you do not have a valid license agreement for the use of - a particular SAP External Product, then you may only make use of any API Calls - in this project for that SAP External Product for your internal, non-productive - and non-commercial test and evaluation of such API Calls. Nothing herein grants - you any rights to use or access any SAP External Product, or provide any third - parties the right to use of access any SAP External Product, through API Calls. - - -Files: * -Copyright: 2020 SAP SE or an SAP affiliate company and CF Java Logging Support contributors -License: Apache-2.0 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..290fe79e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,86 @@ +# SAP Open Source Code of Conduct + +SAP adopts the [Contributor's Covenant 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) +across our open source projects to ensure a welcoming and open culture for everyone involved. + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [ospo@sap.com](mailto:ospo@sap.com) (SAP Open Source Program Office). All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of actions. + +**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c8d947e..dda7d3d1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,34 @@ +# Contributing to an SAP Open Source Project + +## General Remarks + +You are welcome to contribute content (code, documentation etc.) to this open source project. + +There are some important things to know: + +1. You must **comply to the license of this project**, **accept the Developer Certificate of Origin** (see below) before being able to contribute. The acknowledgement to the DCO will usually be requested from you as part of your first pull request to this project. +2. Please **adhere to our [Code of Conduct](CODE_OF_CONDUCT.md)**. +3. If you plan to use **generative AI for your contribution**, please see our guideline below. +4. **Not all proposed contributions can be accepted**. Some features may fit another project better or doesn't fit the general direction of this project. Of course, this doesn't apply to most bug fixes, but a major feature implementation for instance needs to be discussed with one of the maintainers first. Possibly, one who touched the related code or module recently. The more effort you invest, the better you should clarify in advance whether the contribution will match the project's direction. The best way would be to just open an issue to discuss the feature you plan to implement (make it clear that you intend to contribute). We will then forward the proposal to the respective code owner. This avoids disappointment. + ## Developer Certificate of Origin (DCO) -Due to legal reasons, contributors will be asked to accept a DCO when they create the first pull request to this project. This happens in an automated fashion during the submission process. SAP uses [the standard DCO text of the Linux Foundation](https://developercertificate.org/). \ No newline at end of file +Contributors will be asked to accept a DCO before they submit the first pull request to this projects, this happens in an automated fashion during the submission process. SAP uses [the standard DCO text of the Linux Foundation](https://developercertificate.org/). + +## Contributing with AI-generated code + +As artificial intelligence evolves, AI-generated code is becoming valuable for many software projects, including open-source initiatives. While we recognize the potential benefits of incorporating AI-generated content into our open-source projects there a certain requirements that need to be reflected and adhered to when making contributions. + +Please see our [guideline for AI-generated code contributions to SAP Open Source Software Projects](CONTRIBUTING_USING_GENAI.md) for these requirements. + +## How to Contribute + +1. Make sure the change is welcome (see [General Remarks](#general-remarks)). +2. Create a branch by forking the repository and apply your change. +3. Commit and push your change on that branch. +4. Create a pull request in the repository using this branch. +5. Follow the link posted by the CLA assistant to your pull request and accept it, as described above. +6. Wait for our code review and approval, possibly enhancing your change on request. + - Note that the maintainers have many duties. So, depending on the required effort for reviewing, testing, and clarification, this may take a while. +7. Once the change has been approved and merged, we will inform you in a comment. +8. Celebrate! \ No newline at end of file diff --git a/CONTRIBUTING_USING_GENAI.md b/CONTRIBUTING_USING_GENAI.md new file mode 100644 index 00000000..6c7ae912 --- /dev/null +++ b/CONTRIBUTING_USING_GENAI.md @@ -0,0 +1,12 @@ +# Guideline for AI-generated code contributions to SAP Open Source Software Projects + +As artificial intelligence evolves, AI-generated code is becoming valuable for many software projects, including open-source initiatives. While we recognize the potential benefits of incorporating AI-generated content into our open-source projects there are certain requirements that need to be reflected and adhered to when making contributions. + +When using AI-generated code contributions in OSS Projects, their usage needs to align with Open-Source Software values and legal requirements. We have established these essential guidelines to help contributors navigate the complexities of using AI tools while maintaining compliance with open-source licenses and the broader [Open-Source Definition](https://opensource.org/osd). + +AI-generated code or content can be contributed to SAP Open Source Software projects if the following conditions are met: + +1. **Compliance with AI Tool Terms and Conditions**: Contributors must ensure that the AI tool's terms and conditions do not impose any restrictions on the tool's output that conflict with the project's open-source license or intellectual property policies. This includes ensuring that the AI-generated content adheres to the [Open-Source Definition](https://opensource.org/osd). +2. **Filtering Similar Suggestions**: Contributors must use features provided by AI tools to suppress responses that are similar to third-party materials or flag similarities. We only accept contributions from AI tools with such filtering options. If the AI tool flags any similarities, contributors must review and ensure compliance with the licensing terms of such materials before including them in the project. +3. **Management of Third-Party Materials**: If the AI tool's output includes pre-existing copyrighted materials, including open-source code authored or owned by third parties, contributors must verify that they have the necessary permissions from the original owners. This typically involves ensuring that there is an open-source license or public domain declaration that is compatible with the project's licensing policies. Contributors must also provide appropriate notice and attribution for these third-party materials, along with relevant information about the applicable license terms. +4. **Employer Policies Compliance**: If AI-generated content is contributed in the context of employment, contributors must also adhere to their employer’s policies. This ensures that all contributions are made with proper authorization and respect for relevant corporate guidelines. \ No newline at end of file diff --git a/README.md b/README.md index f2cfa6ac..1d0f7ff7 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,11 @@ All in all, you should do the following: And 4. Adjust your logging configuration accordingly. -Let's say you want to make use of the *servlet filter* feature, then you need to add the following dependency to your POM with property `cf-logging-version` referring to the latest nexus version (currently `3.8.2`): +Let's say you want to make use of the *servlet filter* feature, then you need to add the following dependency to your POM with property `cf-logging-version` referring to the latest nexus version (currently `3.8.5`): ```xml - 3.8.2 + 3.8.5 ``` diff --git a/REUSE.toml b/REUSE.toml new file mode 100644 index 00000000..35c8e192 --- /dev/null +++ b/REUSE.toml @@ -0,0 +1,11 @@ +version = 1 +SPDX-PackageName = "CF Java Logging Support" +SPDX-PackageSupplier = "" +SPDX-PackageDownloadLocation = "https://github.com/SAP/cf-java-logging-support" +SPDX-PackageComment = "The code in this project may include calls to APIs (“API Calls”) of\n SAP or third-party products or services developed outside of this project\n (“External Products”).\n “APIs” means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project’s code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls." + +[[annotations]] +path = "**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2020 SAP SE or an SAP affiliate company and CF Java Logging Support contributors" +SPDX-License-Identifier = "Apache-2.0" diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..a9c532eb --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,42 @@ + + +# SAP Open Source Security Policy + +SAP takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, including our primary [SAP](https://github.com/SAP), [SAP-docs](https://github.com/SAP-docs) organizations as well as [our other GitHub organizations and projects](https://opensource.sap.com). + +If you believe you have found a security vulnerability in any SAP-owned repository, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them via the SAP Trust Center at [https://www.sap.com/about/trust-center/security/incident-management.html](https://www.sap.com/about/trust-center/security/incident-management.html). + +If you prefer to submit via email, please send an email to [secure@sap.com](mailto:secure@sap.com). If possible, encrypt your message with our PGP key; please download it from the [SAP Trust Center](https://www.sap.com/keyblock). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + - The repository name or URL + - Type of issue (buffer overflow, SQL injection, cross-site scripting, etc.) + - Full paths of the source file(s) related to the manifestation of the issue + - The location of the affected source code (tag/branch/commit or direct URL) + - Any particular configuration required to reproduce the issue + - Step-by-step instructions to reproduce the issue + - Proof-of-concept or exploit code (if possible) + - Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. + +## Disclosure Guidelines + +We like to ask you to follow the [Disclosure Guidelines for SAP Security Advisories](https://www.sap.com/documents/2022/02/9ccd9ca0-167e-0010-bca6-c68f7e60039b.html). + +## SAP Internal Response Process + +As an SAP employee, please check our internal open source security response process ([go/oss-security-response](https://go.sap.corp/oss-security-response)) for further details on how to handle security incidents. + + \ No newline at end of file diff --git a/cf-java-logging-support-core/beats/app-logs/docs/fields.asciidoc b/cf-java-logging-support-core/beats/app-logs/docs/fields.asciidoc index 79ec5823..69414cc0 100644 --- a/cf-java-logging-support-core/beats/app-logs/docs/fields.asciidoc +++ b/cf-java-logging-support-core/beats/app-logs/docs/fields.asciidoc @@ -498,8 +498,8 @@ type: object example: "#cf": { "string": [ - {"l":"some_label", "v":"some_value", "i": 0}, - {"l":"other_label", "v":"other_value", "i": 1} + {"k":"some_key", "v":"some_value", "i": 0}, + {"k":"other_key", "v":"other_value", "i": 1} ] } @@ -507,7 +507,7 @@ example: "#cf": { required: False An object containing collections of non-standard fields. -The field "string" contains custom fields with label "l", value "v" and an index "i". +The field "string" contains custom fields with key "k", value "v" and an index "i". The index can be used for field order during parsing. NOTE: As this is "custom" there are no predefined fields here! diff --git a/cf-java-logging-support-core/beats/app-logs/etc/fields.yml b/cf-java-logging-support-core/beats/app-logs/etc/fields.yml index f01bee13..434d9740 100644 --- a/cf-java-logging-support-core/beats/app-logs/etc/fields.yml +++ b/cf-java-logging-support-core/beats/app-logs/etc/fields.yml @@ -348,13 +348,13 @@ app-logs: example: | "#cf": { "string": [ - {"l":"some_label", "v":"some_value", "i": 0}, - {"l":"other_label", "v":"other_value", "i": 1} + {"k":"some_key", "v":"some_value", "i": 0}, + {"k":"other_key", "v":"other_value", "i": 1} ] } description: | An object containing collections of non-standard fields. - The field "string" contains custom fields with label "l", value "v" and an index "i". + The field "string" contains custom fields with key "k", value "v" and an index "i". The index can be used for field order during parsing. NOTE: As this is "custom" there are no predefined fields here! diff --git a/cf-java-logging-support-core/pom.xml b/cf-java-logging-support-core/pom.xml index 3b46b59a..69ae88c1 100644 --- a/cf-java-logging-support-core/pom.xml +++ b/cf-java-logging-support-core/pom.xml @@ -32,7 +32,7 @@ com.sap.hcp.cf.logging cf-java-logging-support-parent - 3.8.2 + 3.8.5 ../pom.xml diff --git a/cf-java-logging-support-jersey/pom.xml b/cf-java-logging-support-jersey/pom.xml index fb6f73a1..52cee09e 100644 --- a/cf-java-logging-support-jersey/pom.xml +++ b/cf-java-logging-support-jersey/pom.xml @@ -9,7 +9,7 @@ ../pom.xml com.sap.hcp.cf.logging cf-java-logging-support-parent - 3.8.2 + 3.8.5 cf-java-logging-support-jersey diff --git a/cf-java-logging-support-log4j2/pom.xml b/cf-java-logging-support-log4j2/pom.xml index 2eebf15c..8fe8c1d9 100644 --- a/cf-java-logging-support-log4j2/pom.xml +++ b/cf-java-logging-support-log4j2/pom.xml @@ -11,7 +11,7 @@ ../pom.xml com.sap.hcp.cf.logging cf-java-logging-support-parent - 3.8.2 + 3.8.5 diff --git a/cf-java-logging-support-logback/pom.xml b/cf-java-logging-support-logback/pom.xml index a42fb4f9..502a87f9 100644 --- a/cf-java-logging-support-logback/pom.xml +++ b/cf-java-logging-support-logback/pom.xml @@ -10,7 +10,7 @@ ../pom.xml com.sap.hcp.cf.logging cf-java-logging-support-parent - 3.8.2 + 3.8.5 @@ -25,6 +25,11 @@ cf-java-logging-support-core ${project.version} + + org.springframework.boot + spring-boot + ${springboot.version} + com.sap.hcp.cf.logging cf-java-logging-support-core diff --git a/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatter.java b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatter.java new file mode 100644 index 00000000..f399a7cf --- /dev/null +++ b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatter.java @@ -0,0 +1,39 @@ +package com.sap.hcp.cf.logback.boot; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import com.sap.hcp.cf.logback.encoder.JsonEncoder; +import org.springframework.boot.logging.structured.StructuredLogFormatter; + +/** + * A Logback events formatter for structured logging in SpringBoot + * configuration, suited + * for Cloud Foundry applications. This class can be used as a value for + * {@code logging.structured.format.console} + * or @{code logging.structured.format.file} in a SpringBoot applications. + *

+ * This is a simple wrapper around the {@code JsonEncoder} created for Logback. + * This formatter does not accept any configuration parameters and uses the default + * settings coming with JsonEncoder. The JSON output will contain the same fields + * as produced with {@code JsonEncoder}, but you don't have to use logback.xml + * (or logback-spring.xml) to use it. + *

+ * Example usage with SpringBoot ({@code application.properties}): + * + *

+ * logging.structured.format.console = com.sap.hcp.cf.logback.boot.CloudFoundryStructuredLogFormatter
+ * 
+ */ +public class CloudFoundryStructuredLogFormatter implements StructuredLogFormatter { + + private final JsonEncoder jsonEncoder; + + public CloudFoundryStructuredLogFormatter() { + this.jsonEncoder = JsonEncoder.createStarted(); + } + + @Override + public String format(ILoggingEvent event) { + return jsonEncoder.getJson(event); + } + +} diff --git a/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/encoder/JsonEncoder.java b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/encoder/JsonEncoder.java index 45efe064..dd286419 100644 --- a/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/encoder/JsonEncoder.java +++ b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/encoder/JsonEncoder.java @@ -21,7 +21,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.jr.ob.JSON; import com.fasterxml.jackson.jr.ob.JSON.Builder; @@ -78,6 +77,19 @@ public JsonEncoder() { logbackContextFieldSuppliers.add(new RequestRecordFieldSupplier()); } + /** + * Creates a new encoder instance in the {@code started} state, with default settings. + * This means that the encoder is ready to encode log events once this method returns. + * + * @return a new {@link JsonEncoder} instance in the started state + */ + public static JsonEncoder createStarted() { + JsonEncoder encoder = new JsonEncoder(); + encoder.start(); + + return encoder; + } + /** *

* Adds a field to the "#cf" object in the generated output. If the log @@ -286,7 +298,11 @@ public byte[] encode(ILoggingEvent event) { return getJson(event).getBytes(charset); } - private String getJson(ILoggingEvent event) { + public String getJson(ILoggingEvent event) { + if(!isStarted()) { + throw new IllegalStateException("Encoder is not started. Please call start() before encoding."); + } + try (StringWriter writer = new StringWriter()) { ObjectComposer> oc = json.composeTo(writer).startObject(); addMarkers(oc, event); diff --git a/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatterTest.java b/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatterTest.java new file mode 100644 index 00000000..5abd2921 --- /dev/null +++ b/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/boot/CloudFoundryStructuredLogFormatterTest.java @@ -0,0 +1,32 @@ +package com.sap.hcp.cf.logback.boot; + +import static org.junit.Assert.assertTrue; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.Test; + +public class CloudFoundryStructuredLogFormatterTest { + + CloudFoundryStructuredLogFormatter underTest = new CloudFoundryStructuredLogFormatter(); + + @Test + public void testFormat() throws Exception { + LoggingEvent event = new LoggingEvent(); + event.setLevel(Level.DEBUG); + event.setLoggerName("my-logger"); + event.setMessage("This is some very important log message"); + event.setThreadName("thread-1"); + + String result = underTest.format(event); + + assertTrue(result.startsWith("{")); + assertTrue(result.endsWith("}\n")); + assertTrue(result.contains("\"level\":\"DEBUG\"")); + assertTrue(result.contains("\"logger\":\"my-logger\"")); + assertTrue(result.contains("\"msg\":\"This is some very important log message\"")); + assertTrue(result.contains("\"thread\":\"thread-1\"")); + + } + +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/dependency-reduced-pom.xml b/cf-java-logging-support-opentelemetry-agent-extension/dependency-reduced-pom.xml index d8fb0ea7..b5f1e80a 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/dependency-reduced-pom.xml +++ b/cf-java-logging-support-opentelemetry-agent-extension/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ cf-java-logging-support-parent com.sap.hcp.cf.logging - 3.8.2 + 3.8.4 4.0.0 cf-java-logging-support-opentelemetry-agent-extension @@ -22,15 +22,16 @@ - io.pivotal.cfenv:java-cfenv + io.opentelemetry.contrib:opentelemetry-cloudfoundry-resources - io/pivotal/cfenv/** + io/opentelemetry/contrib/cloudfoundry/resources/CloudFoundryResource.class io.opentelemetry + io.opentelemetry.semconv com.fasterxml.jackson.core com.squareup.okhttp3 com.squareup.okio @@ -70,6 +71,24 @@ 1.31.0 compile + + com.fasterxml.jackson.core + jackson-core + 2.18.2 + compile + + + uk.org.webcompere + system-stubs-junit4 + 2.1.8 + test + + + system-stubs-core + uk.org.webcompere + + + org.slf4j slf4j-api diff --git a/cf-java-logging-support-opentelemetry-agent-extension/pom.xml b/cf-java-logging-support-opentelemetry-agent-extension/pom.xml index 203692a7..19175f71 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/pom.xml +++ b/cf-java-logging-support-opentelemetry-agent-extension/pom.xml @@ -12,13 +12,13 @@ cf-java-logging-support-parent com.sap.hcp.cf.logging - 3.8.2 + 3.8.5 11 11 - 1.31.0 + 1.50.0 @@ -55,9 +55,20 @@ opentelemetry-exporter-otlp - io.pivotal.cfenv - java-cfenv - 2.5.0 + io.opentelemetry.contrib + opentelemetry-cloudfoundry-resources + 1.46.0-alpha + + + com.fasterxml.jackson.core + jackson-core + ${jackson-jr.version} + + + uk.org.webcompere + system-stubs-junit4 + 2.1.8 + test @@ -76,15 +87,18 @@ - io.pivotal.cfenv:java-cfenv + io.opentelemetry.contrib:opentelemetry-cloudfoundry-resources - io/pivotal/cfenv/** + + io/opentelemetry/contrib/cloudfoundry/resources/CloudFoundryResource.class + io.opentelemetry + io.opentelemetry.semconv com.fasterxml.jackson.core com.squareup.okhttp3 com.squareup.okio @@ -106,4 +120,4 @@ - \ No newline at end of file + diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudFoundryResourceProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudFoundryResourceProvider.java index ae753a6c..783c0215 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudFoundryResourceProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudFoundryResourceProvider.java @@ -1,25 +1,18 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext; import com.sap.hcf.cf.logging.opentelemetry.agent.ext.attributes.CloudFoundryResourceCustomizer; +import io.opentelemetry.contrib.cloudfoundry.resources.CloudFoundryResource; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.resources.Resource; -import io.pivotal.cfenv.core.CfEnv; public class CloudFoundryResourceProvider implements ResourceProvider { - private final CloudFoundryResourceCustomizer customizer; - - public CloudFoundryResourceProvider() { - this(new CfEnv()); - } - - public CloudFoundryResourceProvider(CfEnv cfEnv) { - this.customizer = new CloudFoundryResourceCustomizer(cfEnv); - } + private final CloudFoundryResourceCustomizer customizer = new CloudFoundryResourceCustomizer(); @Override public Resource createResource(ConfigProperties configProperties) { - return customizer.apply(null, configProperties); + Resource original = CloudFoundryResource.get(); + return customizer.apply(original, configProperties); } } diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudLoggingConfigurationCustomizerProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudLoggingConfigurationCustomizerProvider.java index e87a54fc..bf27c707 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudLoggingConfigurationCustomizerProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudLoggingConfigurationCustomizerProvider.java @@ -3,21 +3,18 @@ import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudLoggingBindingPropertiesSupplier; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; -import io.pivotal.cfenv.core.CfEnv; import java.util.logging.Logger; public class CloudLoggingConfigurationCustomizerProvider implements AutoConfigurationCustomizerProvider { private static final Logger LOG = Logger.getLogger(CloudLoggingConfigurationCustomizerProvider.class.getName()); - private static final String VERSION = "3.8.2"; - private static final CfEnv cfEnv = new CfEnv(); + private static final String VERSION = "3.8.4"; @Override public void customize(AutoConfigurationCustomizer autoConfiguration) { LOG.info("Initializing SAP BTP Observability extension " + VERSION); - autoConfiguration - .addPropertiesSupplier(new CloudLoggingBindingPropertiesSupplier()); + autoConfiguration.addPropertiesSupplier(new CloudLoggingBindingPropertiesSupplier()); // ConfigurableLogRecordExporterProvider } diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/attributes/CloudFoundryResourceCustomizer.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/attributes/CloudFoundryResourceCustomizer.java index 3a2795eb..f20de379 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/attributes/CloudFoundryResourceCustomizer.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/attributes/CloudFoundryResourceCustomizer.java @@ -1,24 +1,36 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.attributes; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.resources.ResourceBuilder; -import io.pivotal.cfenv.core.CfApplication; -import io.pivotal.cfenv.core.CfEnv; -import java.util.Optional; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.logging.Logger; public class CloudFoundryResourceCustomizer implements BiFunction { - private static final String OTEL_JAVAAGENT_EXTENSION_SAP_CF_RESOURCE_ENABLED = "otel.javaagent.extension.sap.cf.resource.enabled"; + private static final String OTEL_JAVAAGENT_EXTENSION_SAP_CF_RESOURCE_ENABLED = + "otel.javaagent.extension.sap.cf.resource.enabled"; + + private static final String OTEL_JAVAAGENT_EXTENTION_CF_RESOURCE_FORMAT = + "otel.javaagent.extension.sap.cf.resource.format"; private static final Logger LOG = Logger.getLogger(CloudFoundryResourceCustomizer.class.getName()); - private final CfEnv cfEnv; - public CloudFoundryResourceCustomizer(CfEnv cfEnv) { - this.cfEnv = cfEnv; - } + private static final Map SAP_CF_RESOURCE_ATTRIBUTES = new HashMap() {{ + put("cloudfoundry.app.id", "sap.cf.app_id"); + put("cloudfoundry.app.instance.id", "sap.cf.instance_id"); + put("cloudfoundry.app.name", "sap.cf.app_name"); + put("cloudfoundry.org.id", "sap.cf.org_id"); + put("cloudfoundry.org.name", "sap.cf.org_name"); + put("cloudfoundry.process.id", "sap.cf.process.id"); + put("cloudfoundry.process.type", "sap.cf.process.type"); + put("cloudfoundry.space.id", "sap.cf.space_id"); + put("cloudfoundry.space.name", "sap.cf.space_name"); + }}; @Override public Resource apply(Resource resource, ConfigProperties configProperties) { @@ -27,32 +39,62 @@ public Resource apply(Resource resource, ConfigProperties configProperties) { LOG.config("CF resource attributes are disabled by configuration."); return Resource.empty(); } - if (!cfEnv.isInCf()) { + + if (resource == null || resource.getAttributes().isEmpty()) { LOG.config("Not running in CF. Cannot obtain CF resource attributes."); - return Resource.empty(); + return resource; + } + + String format = configProperties.getString(OTEL_JAVAAGENT_EXTENTION_CF_RESOURCE_FORMAT, "SAP"); + if (!format.equalsIgnoreCase("SAP")) { + return resource; + } + + ResourceBuilder builder = Resource.builder(); + resource.getAttributes().asMap().forEach(addAttribute(builder)); + String appId = resource.getAttribute(AttributeKey.stringKey("cloudfoundry.app.id")); + builder.put("sap.cf.source_id", appId); + String appName = resource.getAttribute(AttributeKey.stringKey("cloudfoundry.app.name")); + String serviceName = resource.getAttribute(AttributeKey.stringKey("service.name")); + if (serviceName == null) { + builder.put("service.name", appName); } - CfApplication cfApp = cfEnv.getApp(); - ResourceBuilder rb = Resource.builder(); - rb.put("service.name", cfApp.getApplicationName()); - rb.put("sap.cf.source_id", cfApp.getApplicationId()); - rb.put("sap.cf.instance_id", cfApp.getInstanceIndex()); - rb.put("sap.cf.app_id", cfApp.getApplicationId()); - rb.put("sap.cf.app_name", cfApp.getApplicationName()); - rb.put("sap.cf.space_id", cfApp.getSpaceId()); - rb.put("sap.cf.space_name", cfApp.getSpaceName()); - rb.put("sap.cf.org_id", getString(cfApp, "organization_id")); - rb.put("sap.cf.org_name", getString(cfApp, "organization_name")); - rb.put("sap.cf.process.id", getString(cfApp, "process_id")); - rb.put("sap.cf.process.type", getString(cfApp, "process_type")); - return rb.build(); + return builder.build(); + } + + private BiConsumer, Object> addAttribute(ResourceBuilder builder) { + return (k, v) -> { + switch (k.getType()) { + case BOOLEAN: + builder.put(rename(k), (boolean) v); + break; + case BOOLEAN_ARRAY: + builder.put(rename(k), (boolean[]) v); + break; + case DOUBLE: + builder.put(rename(k), (double) v); + break; + case DOUBLE_ARRAY: + builder.put(rename(k), (double[]) v); + break; + case LONG: + builder.put(rename(k), (long) v); + break; + case LONG_ARRAY: + builder.put(rename(k), (long[]) v); + break; + case STRING: + builder.put(rename(k), (String) v); + break; + case STRING_ARRAY: + builder.put(rename(k), (String[]) v); + break; + } + }; } - private String getString(CfApplication cfApp, String key) { - return Optional.ofNullable(cfApp) - .map(CfApplication::getMap) - .map(m -> m.get(key)) - .filter(String.class::isInstance) - .map(String.class::cast) - .orElse(""); + private String rename(AttributeKey key) { + String name = key.getKey(); + return SAP_CF_RESOURCE_ATTRIBUTES.getOrDefault(name, name); } } diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryCredentials.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryCredentials.java new file mode 100644 index 00000000..f08cc7c0 --- /dev/null +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryCredentials.java @@ -0,0 +1,44 @@ +package com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding; + +import java.util.Map; +import java.util.TreeMap; + +public class CloudFoundryCredentials { + + private final Map properties; + + private CloudFoundryCredentials(Builder builder) { + this.properties = builder.properties; + } + + public String getString(String key) { + return properties.get(key); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private final Map properties = new TreeMap<>(); + + private Builder() { + } + + public CloudFoundryCredentials build() { + return new CloudFoundryCredentials(this); + } + + public Builder add(String key, String value) { + if (isNotBlank(key) && isNotBlank(value)) { + properties.put(key, value); + } + return this; + } + + private boolean isNotBlank(String string) { + return string != null && !string.trim().isEmpty(); + } + } +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServiceInstance.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServiceInstance.java new file mode 100644 index 00000000..c82f7c5c --- /dev/null +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServiceInstance.java @@ -0,0 +1,78 @@ +package com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding; + +import java.util.ArrayList; +import java.util.List; + +public class CloudFoundryServiceInstance { + + private final String name; + private final String label; + private final List tags; + private final CloudFoundryCredentials credentials; + + private CloudFoundryServiceInstance(String name, String label, CloudFoundryCredentials credentials, + List tags) { + this.name = name; + this.label = label; + this.credentials = credentials; + this.tags = tags; + } + + public String getName() { + return name; + } + + public String getLabel() { + return label; + } + + public CloudFoundryCredentials getCredentials() { + return credentials; + } + + public List getTags() { + return tags; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String name; + private String label; + private final List tags = new ArrayList<>(); + private CloudFoundryCredentials credentials; + + private Builder() { + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder label(String label) { + this.label = label; + return this; + } + + public Builder tag(String tag) { + if (tag != null && !tag.trim().isEmpty()) { + tags.add(tag); + } + return this; + } + + public Builder credentials(CloudFoundryCredentials credentials) { + this.credentials = credentials; + return this; + } + + public CloudFoundryServiceInstance build() { + return new CloudFoundryServiceInstance(name, label, credentials, tags); + } + + } +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapter.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapter.java index 03c8f1f4..82193209 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapter.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapter.java @@ -1,17 +1,37 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding; -import io.pivotal.cfenv.core.CfEnv; -import io.pivotal.cfenv.core.CfService; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.function.Consumer; +import java.util.logging.Logger; import java.util.stream.Stream; class CloudFoundryServicesAdapter { - private final CfEnv cfEnv; + private static final Logger LOG = Logger.getLogger(CloudFoundryServicesAdapter.class.getName()); - CloudFoundryServicesAdapter(CfEnv cfEnv) { - this.cfEnv = cfEnv; + private static final String VCAP_SERVICES = "VCAP_SERVICES"; + private static final String SERVICE_NAME = "name"; + private static final String SERVICE_TAGS = "tags"; + private static final String SERVICE_CREDENTIALS = "credentials"; + private static final String SERVICE_CREDENTIALS_ENDPOINT = "ingest-otlp-endpoint"; + private static final String SERVICE_CREDENTIALS_CLIENT_KEY = "ingest-otlp-key"; + + private final String vcapServicesJson; + + public CloudFoundryServicesAdapter() { + this(System.getenv(VCAP_SERVICES)); + } + + CloudFoundryServicesAdapter(String vcapServicesJson) { + this.vcapServicesJson = vcapServicesJson; } /** @@ -19,21 +39,132 @@ class CloudFoundryServicesAdapter { * check will be performed during search. User-provided service instances will be preferred unless the * {@code userProvidedLabel is null or empty. Provided only null values will return all service instances. * - * @param serviceLabels the labels of services - * @param serviceTags the tags of services + * @param serviceLabels + * the labels of services + * @param serviceTags + * the tags of services * @return a stream of service instances present in the CloudFoundry environment variable VCAP_SERVICES */ - Stream stream(List serviceLabels, List serviceTags) { - Stream services; - if (serviceLabels == null || serviceLabels.isEmpty()) - services = cfEnv.findAllServices().stream(); - else { - services = serviceLabels.stream().flatMap(l -> cfEnv.findServicesByLabel(l).stream()); + Stream stream(List serviceLabels, List serviceTags) { + if (vcapServicesJson == null) { + LOG.info("No environment variable " + VCAP_SERVICES + "found. Skipping service binding detection."); + return Stream.empty(); + } + try (JsonParser parser = new JsonFactory().createParser(vcapServicesJson)) { + parser.nextToken(); + List services = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_OBJECT) { + String label = parser.currentName(); + if (isNullOrEmpty(serviceLabels) || serviceLabels.contains(label)) { + parseServiceInstances(parser, label, serviceInstance -> { + if (serviceInstance.getName() != null) { + if (hasServiceTag(serviceTags, serviceInstance.getTags())) { + services.add(serviceInstance); + } + } + }); + } else { + parser.skipChildren(); + } + } + if (isNullOrEmpty(serviceLabels)) { + return services.stream(); + } else { + return services.stream().sorted(byLabels(serviceLabels)); + } + } catch (JsonParseException cause) { + LOG.warning("Invalid JSON content in environment variable " + VCAP_SERVICES); + } catch (IOException cause) { + LOG.warning("Cannot parse content of environment variable " + VCAP_SERVICES); + } + return Stream.empty(); + } + + private static void parseServiceInstances(JsonParser parser, String label, + Consumer consumer) throws IOException { + if (parser.nextToken() == JsonToken.START_ARRAY) { + while (parser.nextToken() != JsonToken.END_ARRAY) { + if (parser.currentToken() == JsonToken.START_OBJECT) { + CloudFoundryServiceInstance serviceInstance = parseServiceInstance(label, parser); + consumer.accept(serviceInstance); + } + } + } + } + + private static boolean isNullOrEmpty(List items) { + return items == null || items.isEmpty(); + } + + private static CloudFoundryServiceInstance parseServiceInstance(String label, JsonParser parser) + throws IOException { + CloudFoundryServiceInstance.Builder builder = CloudFoundryServiceInstance.builder().label(label); + while (parser.nextToken() != JsonToken.END_OBJECT) { + switch (parser.currentName()) { + case SERVICE_NAME: + parseServiceName(builder, parser); + break; + case SERVICE_TAGS: + parserServiceTags(parser, builder); + break; + case SERVICE_CREDENTIALS: + parseServiceCredentials(parser, builder); + break; + default: + parser.skipChildren(); + } + } + return builder.build(); + } + + private static void parseServiceName(CloudFoundryServiceInstance.Builder builder, JsonParser parser) + throws IOException { + builder.name(parser.getValueAsString()); + } + + private static void parserServiceTags(JsonParser parser, CloudFoundryServiceInstance.Builder builder) + throws IOException { + if (parser.nextToken() == JsonToken.START_ARRAY) { + while (parser.nextToken() != JsonToken.END_ARRAY) { + builder.tag(parser.getValueAsString()); + } + } + } + + private static void parseServiceCredentials(JsonParser parser, CloudFoundryServiceInstance.Builder builder) + throws IOException { + if (parser.nextToken() == JsonToken.START_OBJECT) { + CloudFoundryCredentials.Builder credentials = CloudFoundryCredentials.builder(); + while (parser.nextToken() != JsonToken.END_OBJECT) { + if (parser.currentToken().isScalarValue()) { + credentials.add(parser.currentName(), parser.getValueAsString()); + } else { + parser.skipChildren(); + } + } + builder.credentials(credentials.build()); } - if (serviceTags == null || serviceTags.isEmpty()) { - return services; + } + + private int getIndex(List serviceLabels, String label) { + if (label == null) { + return Integer.MAX_VALUE; } - return services.filter(svc -> svc.existsByTagIgnoreCase(serviceTags.toArray(new String[0]))); + int index = serviceLabels.indexOf(label); + return index >= 0 ? index : Integer.MAX_VALUE; } + private boolean hasServiceTag(List requiredTags, List instanceTags) { + if (isNullOrEmpty(requiredTags)) { + return true; + } + if (isNullOrEmpty(instanceTags)) { + return false; + } + return instanceTags.containsAll(requiredTags); + } + + private Comparator byLabels(List serviceLabels) { + return (l, r) -> getIndex(serviceLabels, l.getLabel()) - getIndex(serviceLabels, r.getLabel()); + } } diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingBindingPropertiesSupplier.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingBindingPropertiesSupplier.java index 62dec656..34220088 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingBindingPropertiesSupplier.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingBindingPropertiesSupplier.java @@ -2,8 +2,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.pivotal.cfenv.core.CfEnv; -import io.pivotal.cfenv.core.CfService; import java.io.File; import java.io.FileWriter; @@ -26,7 +24,7 @@ public class CloudLoggingBindingPropertiesSupplier implements Supplier get() { - return cloudLoggingServicesProvider.get() - .findFirst() - .map(this::createEndpointConfiguration).orElseGet(Collections::emptyMap); + return cloudLoggingServicesProvider.get().findFirst().map(this::createEndpointConfiguration) + .orElseGet(Collections::emptyMap); } - private Map createEndpointConfiguration(CfService svc) { + private Map createEndpointConfiguration(CloudFoundryServiceInstance svc) { LOG.config("Using service " + svc.getName() + " (" + svc.getLabel() + ")"); String endpoint = svc.getCredentials().getString(OTLP_ENDPOINT); diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingServicesProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingServicesProvider.java index 4ed5616d..1c7a2ef2 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingServicesProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingServicesProvider.java @@ -1,8 +1,6 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.pivotal.cfenv.core.CfEnv; -import io.pivotal.cfenv.core.CfService; import java.util.List; import java.util.function.Supplier; @@ -12,16 +10,16 @@ import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; -public class CloudLoggingServicesProvider implements Supplier> { +public class CloudLoggingServicesProvider implements Supplier> { private static final String DEFAULT_USER_PROVIDED_LABEL = "user-provided"; private static final String DEFAULT_CLOUD_LOGGING_LABEL = "cloud-logging"; private static final String DEFAULT_CLOUD_LOGGING_TAG = "Cloud Logging"; - private final List services; + private final List services; public CloudLoggingServicesProvider(ConfigProperties config) { - this(config, new CloudFoundryServicesAdapter(new CfEnv())); + this(config, new CloudFoundryServicesAdapter()); } CloudLoggingServicesProvider(ConfigProperties config, CloudFoundryServicesAdapter adapter) { @@ -31,21 +29,24 @@ public CloudLoggingServicesProvider(ConfigProperties config) { } private String getUserProvidedLabel(ConfigProperties config) { - return config.getString("otel.javaagent.extension.sap.cf.binding.user-provided.label", DEFAULT_USER_PROVIDED_LABEL); + return config.getString("otel.javaagent.extension.sap.cf.binding.user-provided.label", + DEFAULT_USER_PROVIDED_LABEL); } private String getCloudLoggingLabel(ConfigProperties config) { - String fromOwnProperties = System.getProperty("com.sap.otel.extension.cloud-logging.label", DEFAULT_CLOUD_LOGGING_LABEL); + String fromOwnProperties = + System.getProperty("com.sap.otel.extension.cloud-logging.label", DEFAULT_CLOUD_LOGGING_LABEL); return config.getString("otel.javaagent.extension.sap.cf.binding.cloud-logging.label", fromOwnProperties); } private String getCloudLoggingTag(ConfigProperties config) { - String fromOwnProperties = System.getProperty("com.sap.otel.extension.cloud-logging.tag", DEFAULT_CLOUD_LOGGING_TAG); + String fromOwnProperties = + System.getProperty("com.sap.otel.extension.cloud-logging.tag", DEFAULT_CLOUD_LOGGING_TAG); return config.getString("otel.javaagent.extension.sap.cf.binding.cloud-logging.tag", fromOwnProperties); } @Override - public Stream get() { + public Stream get() { return services.stream(); } } diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/DynatraceServiceProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/DynatraceServiceProvider.java index cfd2c139..b0af6f4b 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/DynatraceServiceProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/DynatraceServiceProvider.java @@ -1,8 +1,6 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.pivotal.cfenv.core.CfEnv; -import io.pivotal.cfenv.core.CfService; import java.util.List; import java.util.function.Supplier; @@ -10,16 +8,16 @@ import static java.util.Arrays.asList; import static java.util.Collections.singletonList; -public class DynatraceServiceProvider implements Supplier { +public class DynatraceServiceProvider implements Supplier { private static final String DEFAULT_USER_PROVIDED_LABEL = "user-provided"; private static final String DEFAULT_DYNATRACE_LABEL = "dynatrace"; private static final String DEFAULT_DYNATRACE_TAG = "dynatrace"; - private final CfService service; + private final CloudFoundryServiceInstance service; public DynatraceServiceProvider(ConfigProperties config) { - this(config, new CloudFoundryServicesAdapter(new CfEnv())); + this(config, new CloudFoundryServicesAdapter()); } DynatraceServiceProvider(ConfigProperties config, CloudFoundryServicesAdapter adapter) { @@ -29,7 +27,8 @@ public DynatraceServiceProvider(ConfigProperties config) { } private String getUserProvidedLabel(ConfigProperties config) { - return config.getString("otel.javaagent.extension.sap.cf.binding.user-provided.label", DEFAULT_USER_PROVIDED_LABEL); + return config.getString("otel.javaagent.extension.sap.cf.binding.user-provided.label", + DEFAULT_USER_PROVIDED_LABEL); } private String getDynatraceLabel(ConfigProperties config) { @@ -41,7 +40,7 @@ private String getDynatraceTag(ConfigProperties config) { } @Override - public CfService get() { + public CloudFoundryServiceInstance get() { return service; } } diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingCredentials.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingCredentials.java index 30dd39de..634ac46f 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingCredentials.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingCredentials.java @@ -1,11 +1,11 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; -import io.pivotal.cfenv.core.CfCredentials; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryCredentials; import java.nio.charset.StandardCharsets; import java.util.logging.Logger; -class CloudLoggingCredentials { +public class CloudLoggingCredentials { private static final Logger LOG = Logger.getLogger(CloudLoggingCredentials.class.getName()); @@ -17,7 +17,6 @@ class CloudLoggingCredentials { private static final String CRED_OTLP_SERVER_CERT = "server-ca"; private static final String CLOUD_LOGGING_ENDPOINT_PREFIX = "https://"; - private String endpoint; private byte[] clientKey; private byte[] clientCert; @@ -30,8 +29,12 @@ static CloudLoggingCredentials.Parser parser() { return PARSER; } - private static byte[] getPEMBytes(CfCredentials credentials, String key) { + private static byte[] getPEMBytes(CloudFoundryCredentials credentials, String key) { String raw = credentials.getString(key); + return getPEMBytes(raw); + } + + private static byte[] getPEMBytes(String raw) { return raw == null ? null : raw.trim().replace("\\n", "\n").getBytes(StandardCharsets.UTF_8); } @@ -45,22 +48,26 @@ private static boolean isNullOrEmpty(byte[] bytes) { public boolean validate() { if (isBlank(endpoint)) { - LOG.warning("Credential \"" + CRED_OTLP_ENDPOINT + "\" not found. Skipping cloud-logging exporter configuration"); + LOG.warning( + "Credential \"" + CRED_OTLP_ENDPOINT + "\" not found. Skipping cloud-logging exporter configuration"); return false; } if (isNullOrEmpty(clientKey)) { - LOG.warning("Credential \"" + CRED_OTLP_CLIENT_KEY + "\" not found. Skipping cloud-logging exporter configuration"); + LOG.warning( + "Credential \"" + CRED_OTLP_CLIENT_KEY + "\" not found. Skipping cloud-logging exporter configuration"); return false; } if (isNullOrEmpty(clientCert)) { - LOG.warning("Credential \"" + CRED_OTLP_CLIENT_CERT + "\" not found. Skipping cloud-logging exporter configuration"); + LOG.warning( + "Credential \"" + CRED_OTLP_CLIENT_CERT + "\" not found. Skipping cloud-logging exporter configuration"); return false; } if (isNullOrEmpty(serverCert)) { - LOG.warning("Credential \"" + CRED_OTLP_SERVER_CERT + "\" not found. Skipping cloud-logging exporter configuration"); + LOG.warning( + "Credential \"" + CRED_OTLP_SERVER_CERT + "\" not found. Skipping cloud-logging exporter configuration"); return false; } return true; @@ -83,7 +90,7 @@ public byte[] getServerCert() { } static class Parser { - CloudLoggingCredentials parse(CfCredentials cfCredentials) { + CloudLoggingCredentials parse(CloudFoundryCredentials cfCredentials) { CloudLoggingCredentials parsed = new CloudLoggingCredentials(); String rawEndpoint = cfCredentials.getString(CRED_OTLP_ENDPOINT); parsed.endpoint = isBlank(rawEndpoint) ? null : CLOUD_LOGGING_ENDPOINT_PREFIX + rawEndpoint; diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingLogsExporterProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingLogsExporterProvider.java index d9ff17c2..aa9a0e96 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingLogsExporterProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingLogsExporterProvider.java @@ -1,5 +1,6 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryServiceInstance; import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudLoggingServicesProvider; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder; @@ -7,7 +8,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider; import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.logs.export.LogRecordExporter; -import io.pivotal.cfenv.core.CfService; import java.time.Duration; import java.util.List; @@ -20,14 +20,15 @@ public class CloudLoggingLogsExporterProvider implements ConfigurableLogRecordEx private static final Logger LOG = Logger.getLogger(CloudLoggingLogsExporterProvider.class.getName()); - private final Function> servicesProvider; + private final Function> servicesProvider; private final CloudLoggingCredentials.Parser credentialParser; public CloudLoggingLogsExporterProvider() { this(config -> new CloudLoggingServicesProvider(config).get(), CloudLoggingCredentials.parser()); } - CloudLoggingLogsExporterProvider(Function> serviceProvider, CloudLoggingCredentials.Parser credentialParser) { + CloudLoggingLogsExporterProvider(Function> serviceProvider, + CloudLoggingCredentials.Parser credentialParser) { this.servicesProvider = serviceProvider; this.credentialParser = credentialParser; } @@ -49,14 +50,13 @@ public String getName() { @Override public LogRecordExporter createExporter(ConfigProperties config) { - List exporters = servicesProvider.apply(config) - .map(svc -> createExporter(config, svc)) - .filter(exp -> !(exp instanceof NoopLogRecordExporter)) - .collect(Collectors.toList()); + List exporters = servicesProvider.apply(config).map(svc -> createExporter(config, svc)) + .filter(exp -> !(exp instanceof NoopLogRecordExporter)) + .collect(Collectors.toList()); return LogRecordExporter.composite(exporters); } - private LogRecordExporter createExporter(ConfigProperties config, CfService service) { + private LogRecordExporter createExporter(ConfigProperties config, CloudFoundryServiceInstance service) { LOG.info("Creating logs exporter for service binding " + service.getName() + " (" + service.getLabel() + ")"); CloudLoggingCredentials credentials = credentialParser.parse(service.getCredentials()); if (!credentials.validate()) { @@ -64,11 +64,9 @@ private LogRecordExporter createExporter(ConfigProperties config, CfService serv } OtlpGrpcLogRecordExporterBuilder builder = OtlpGrpcLogRecordExporter.builder(); - builder.setEndpoint(credentials.getEndpoint()) - .setCompression(getCompression(config)) - .setClientTls(credentials.getClientKey(), credentials.getClientCert()) - .setTrustedCertificates(credentials.getServerCert()) - .setRetryPolicy(RetryPolicy.getDefault()); + builder.setEndpoint(credentials.getEndpoint()).setCompression(getCompression(config)) + .setClientTls(credentials.getClientKey(), credentials.getClientCert()) + .setTrustedCertificates(credentials.getServerCert()).setRetryPolicy(RetryPolicy.getDefault()); Duration timeOut = getTimeOut(config); if (timeOut != null) { diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProvider.java index 6d3e27d8..8e310f84 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProvider.java @@ -1,5 +1,6 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryServiceInstance; import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudLoggingServicesProvider; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder; @@ -13,8 +14,6 @@ import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil; -import io.pivotal.cfenv.core.CfCredentials; -import io.pivotal.cfenv.core.CfService; import java.time.Duration; import java.util.List; @@ -30,14 +29,15 @@ public class CloudLoggingMetricsExporterProvider implements ConfigurableMetricEx private static final Logger LOG = Logger.getLogger(CloudLoggingMetricsExporterProvider.class.getName()); - private final Function> servicesProvider; + private final Function> servicesProvider; private final CloudLoggingCredentials.Parser credentialParser; public CloudLoggingMetricsExporterProvider() { this(config -> new CloudLoggingServicesProvider(config).get(), CloudLoggingCredentials.parser()); } - CloudLoggingMetricsExporterProvider(Function> serviceProvider, CloudLoggingCredentials.Parser credentialParser) { + CloudLoggingMetricsExporterProvider(Function> serviceProvider, + CloudLoggingCredentials.Parser credentialParser) { this.servicesProvider = serviceProvider; this.credentialParser = credentialParser; } @@ -59,14 +59,14 @@ private static AggregationTemporalitySelector getAggregationTemporalitySelector( } AggregationTemporalitySelector temporalitySelector; switch (temporalityStr.toLowerCase(Locale.ROOT)) { - case "cumulative": - return AggregationTemporalitySelector.alwaysCumulative(); - case "delta": - return AggregationTemporalitySelector.deltaPreferred(); - case "lowmemory": - return AggregationTemporalitySelector.lowMemory(); - default: - throw new ConfigurationException("Unrecognized aggregation temporality: " + temporalityStr); + case "cumulative": + return AggregationTemporalitySelector.alwaysCumulative(); + case "delta": + return AggregationTemporalitySelector.deltaPreferred(); + case "lowmemory": + return AggregationTemporalitySelector.lowMemory(); + default: + throw new ConfigurationException("Unrecognized aggregation temporality: " + temporalityStr); } } @@ -74,16 +74,17 @@ private static DefaultAggregationSelector getDefaultAggregationSelector(ConfigPr String defaultHistogramAggregation = config.getString("otel.exporter.cloud-logging.metrics.default.histogram.aggregation"); if (defaultHistogramAggregation == null) { - return DefaultAggregationSelector.getDefault().with(InstrumentType.HISTOGRAM, Aggregation.defaultAggregation()); + return DefaultAggregationSelector.getDefault() + .with(InstrumentType.HISTOGRAM, Aggregation.defaultAggregation()); } if (AggregationUtil.aggregationName(Aggregation.base2ExponentialBucketHistogram()) - .equalsIgnoreCase(defaultHistogramAggregation)) { - return - DefaultAggregationSelector.getDefault() - .with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram()); + .equalsIgnoreCase(defaultHistogramAggregation)) { + return DefaultAggregationSelector.getDefault().with(InstrumentType.HISTOGRAM, + Aggregation.base2ExponentialBucketHistogram()); } else if (AggregationUtil.aggregationName(explicitBucketHistogram()) - .equalsIgnoreCase(defaultHistogramAggregation)) { - return DefaultAggregationSelector.getDefault().with(InstrumentType.HISTOGRAM, Aggregation.explicitBucketHistogram()); + .equalsIgnoreCase(defaultHistogramAggregation)) { + return DefaultAggregationSelector.getDefault() + .with(InstrumentType.HISTOGRAM, Aggregation.explicitBucketHistogram()); } else { throw new ConfigurationException( "Unrecognized default histogram aggregation: " + defaultHistogramAggregation); @@ -97,29 +98,27 @@ public String getName() { @Override public MetricExporter createExporter(ConfigProperties config) { - List exporters = servicesProvider.apply(config) - .map(svc -> createExporter(config, svc)) - .filter(exp -> !(exp instanceof NoopMetricExporter)) - .collect(Collectors.toList()); - return MultiMetricExporter.composite(exporters, getAggregationTemporalitySelector(config), getDefaultAggregationSelector(config)); + List exporters = servicesProvider.apply(config).map(svc -> createExporter(config, svc)) + .filter(exp -> !(exp instanceof NoopMetricExporter)) + .collect(Collectors.toList()); + return MultiMetricExporter.composite(exporters, getAggregationTemporalitySelector(config), + getDefaultAggregationSelector(config)); } - private MetricExporter createExporter(ConfigProperties config, CfService service) { - LOG.info("Creating metrics exporter for service binding " + service.getName() + " (" + service.getLabel() + ")"); - CfCredentials cfCredentials = service.getCredentials(); - CloudLoggingCredentials credentials = credentialParser.parse(cfCredentials); + private MetricExporter createExporter(ConfigProperties config, CloudFoundryServiceInstance service) { + LOG.info( + "Creating metrics exporter for service binding " + service.getName() + " (" + service.getLabel() + ")"); + CloudLoggingCredentials credentials = credentialParser.parse(service.getCredentials()); if (!credentials.validate()) { return NoopMetricExporter.getInstance(); } OtlpGrpcMetricExporterBuilder builder = OtlpGrpcMetricExporter.builder(); - builder.setEndpoint(credentials.getEndpoint()) - .setCompression(getCompression(config)) - .setClientTls(credentials.getClientKey(), credentials.getClientCert()) - .setTrustedCertificates(credentials.getServerCert()) - .setRetryPolicy(RetryPolicy.getDefault()) - .setAggregationTemporalitySelector(getAggregationTemporalitySelector(config)) - .setDefaultAggregationSelector(getDefaultAggregationSelector(config)); + builder.setEndpoint(credentials.getEndpoint()).setCompression(getCompression(config)) + .setClientTls(credentials.getClientKey(), credentials.getClientCert()) + .setTrustedCertificates(credentials.getServerCert()).setRetryPolicy(RetryPolicy.getDefault()) + .setAggregationTemporalitySelector(getAggregationTemporalitySelector(config)) + .setDefaultAggregationSelector(getDefaultAggregationSelector(config)); Duration timeOut = getTimeOut(config); if (timeOut != null) { diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingSpanExporterProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingSpanExporterProvider.java index 1c88b228..d62af7e9 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingSpanExporterProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingSpanExporterProvider.java @@ -1,5 +1,6 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryServiceInstance; import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudLoggingServicesProvider; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; @@ -7,8 +8,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.trace.export.SpanExporter; -import io.pivotal.cfenv.core.CfCredentials; -import io.pivotal.cfenv.core.CfService; import java.time.Duration; import java.util.List; @@ -21,14 +20,15 @@ public class CloudLoggingSpanExporterProvider implements ConfigurableSpanExporte private static final Logger LOG = Logger.getLogger(CloudLoggingSpanExporterProvider.class.getName()); - private final Function> servicesProvider; + private final Function> servicesProvider; private final CloudLoggingCredentials.Parser credentialParser; public CloudLoggingSpanExporterProvider() { this(config -> new CloudLoggingServicesProvider(config).get(), CloudLoggingCredentials.parser()); } - CloudLoggingSpanExporterProvider(Function> serviceProvider, CloudLoggingCredentials.Parser credentialParser) { + CloudLoggingSpanExporterProvider(Function> serviceProvider, + CloudLoggingCredentials.Parser credentialParser) { this.servicesProvider = serviceProvider; this.credentialParser = credentialParser; } @@ -50,27 +50,23 @@ public String getName() { @Override public SpanExporter createExporter(ConfigProperties config) { - List exporters = servicesProvider.apply(config) - .map(svc -> createExporter(config, svc)) - .filter(exp -> !(exp instanceof NoopSpanExporter)) - .collect(Collectors.toList()); + List exporters = servicesProvider.apply(config).map(svc -> createExporter(config, svc)) + .filter(exp -> !(exp instanceof NoopSpanExporter)) + .collect(Collectors.toList()); return SpanExporter.composite(exporters); } - private SpanExporter createExporter(ConfigProperties config, CfService service) { + private SpanExporter createExporter(ConfigProperties config, CloudFoundryServiceInstance service) { LOG.info("Creating span exporter for service binding " + service.getName() + " (" + service.getLabel() + ")"); - CfCredentials cfCredentials = service.getCredentials(); - CloudLoggingCredentials credentials = credentialParser.parse(cfCredentials); + CloudLoggingCredentials credentials = credentialParser.parse(service.getCredentials()); if (!credentials.validate()) { return NoopSpanExporter.getInstance(); } OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder(); - builder.setEndpoint(credentials.getEndpoint()) - .setCompression(getCompression(config)) - .setClientTls(credentials.getClientKey(), credentials.getClientCert()) - .setTrustedCertificates(credentials.getServerCert()) - .setRetryPolicy(RetryPolicy.getDefault()); + builder.setEndpoint(credentials.getEndpoint()).setCompression(getCompression(config)) + .setClientTls(credentials.getClientKey(), credentials.getClientCert()) + .setTrustedCertificates(credentials.getServerCert()).setRetryPolicy(RetryPolicy.getDefault()); Duration timeOut = getTimeOut(config); if (timeOut != null) { diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java index bb858a1e..307a97b9 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/main/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProvider.java @@ -1,5 +1,7 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryCredentials; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryServiceInstance; import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.DynatraceServiceProvider; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder; @@ -9,13 +11,14 @@ import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector; import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil; -import io.pivotal.cfenv.core.CfService; import java.time.Duration; +import java.util.Locale; import java.util.function.Function; import java.util.logging.Logger; @@ -26,13 +29,14 @@ public class DynatraceMetricsExporterProvider implements ConfigurableMetricExpor public static final String CRED_DYNATRACE_APIURL = "apiurl"; public static final String DT_APIURL_METRICS_SUFFIX = "/v2/otlp/v1/metrics"; private static final Logger LOG = Logger.getLogger(DynatraceMetricsExporterProvider.class.getName()); - private final Function serviceProvider; + private static final AggregationTemporalitySelector ALWAYS_DELTA = instrumentType -> AggregationTemporality.DELTA; + private final Function serviceProvider; public DynatraceMetricsExporterProvider() { this(config -> new DynatraceServiceProvider(config).get()); } - public DynatraceMetricsExporterProvider(Function serviceProvider) { + public DynatraceMetricsExporterProvider(Function serviceProvider) { this.serviceProvider = serviceProvider; } @@ -46,20 +50,41 @@ private static Duration getTimeOut(ConfigProperties config) { return timeout != null ? timeout : config.getDuration("otel.exporter.dynatrace.timeout"); } + private static AggregationTemporalitySelector getAggregationTemporalitySelector(ConfigProperties config) { + String temporalityStr = config.getString("otel.exporter.dynatrace.metrics.temporality.preference"); + if (temporalityStr == null) { + return ALWAYS_DELTA; + } + AggregationTemporalitySelector temporalitySelector; + switch (temporalityStr.toLowerCase(Locale.ROOT)) { + case "cumulative": + return AggregationTemporalitySelector.alwaysCumulative(); + case "delta": + return AggregationTemporalitySelector.deltaPreferred(); + case "lowmemory": + return AggregationTemporalitySelector.lowMemory(); + case "always_delta": + return ALWAYS_DELTA; + default: + throw new ConfigurationException("Unrecognized aggregation temporality: " + temporalityStr); + } + } + private static DefaultAggregationSelector getDefaultAggregationSelector(ConfigProperties config) { String defaultHistogramAggregation = config.getString("otel.exporter.dynatrace.metrics.default.histogram.aggregation"); if (defaultHistogramAggregation == null) { - return DefaultAggregationSelector.getDefault().with(InstrumentType.HISTOGRAM, Aggregation.defaultAggregation()); + return DefaultAggregationSelector.getDefault() + .with(InstrumentType.HISTOGRAM, Aggregation.defaultAggregation()); } if (AggregationUtil.aggregationName(Aggregation.base2ExponentialBucketHistogram()) - .equalsIgnoreCase(defaultHistogramAggregation)) { - return - DefaultAggregationSelector.getDefault() - .with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram()); + .equalsIgnoreCase(defaultHistogramAggregation)) { + return DefaultAggregationSelector.getDefault().with(InstrumentType.HISTOGRAM, + Aggregation.base2ExponentialBucketHistogram()); } else if (AggregationUtil.aggregationName(explicitBucketHistogram()) - .equalsIgnoreCase(defaultHistogramAggregation)) { - return DefaultAggregationSelector.getDefault().with(InstrumentType.HISTOGRAM, Aggregation.explicitBucketHistogram()); + .equalsIgnoreCase(defaultHistogramAggregation)) { + return DefaultAggregationSelector.getDefault() + .with(InstrumentType.HISTOGRAM, Aggregation.explicitBucketHistogram()); } else { throw new ConfigurationException( "Unrecognized default histogram aggregation: " + defaultHistogramAggregation); @@ -77,44 +102,51 @@ public String getName() { @Override public MetricExporter createExporter(ConfigProperties config) { - CfService cfService = serviceProvider.apply(config); + CloudFoundryServiceInstance cfService = serviceProvider.apply(config); if (cfService == null) { LOG.info("No dynatrace service binding found. Skipping metrics exporter registration."); return NoopMetricExporter.getInstance(); } - LOG.info("Creating metrics exporter for service binding " + cfService.getName() + " (" + cfService.getLabel() + ")"); + LOG.info( + "Creating metrics exporter for service binding " + cfService.getName() + " (" + cfService.getLabel() + ")"); - String apiUrl = cfService.getCredentials().getString(CRED_DYNATRACE_APIURL); + CloudFoundryCredentials credentials = cfService.getCredentials(); + if (credentials == null) { + LOG.warning("No credentials found Skipping dynatrace exporter configuration"); + return NoopMetricExporter.getInstance(); + } + String apiUrl = credentials.getString(CRED_DYNATRACE_APIURL); if (isBlank(apiUrl)) { - LOG.warning("Credential \"" + CRED_DYNATRACE_APIURL + "\" not found. Skipping dynatrace exporter configuration"); + LOG.warning( + "Credential \"" + CRED_DYNATRACE_APIURL + "\" not found. Skipping dynatrace exporter configuration"); return NoopMetricExporter.getInstance(); } String tokenName = config.getString("otel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name"); if (isBlank(tokenName)) { - LOG.warning("Configuration \"otel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name\" not found. Skipping dynatrace exporter configuration"); + LOG.warning( + "Configuration \"otel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name\" not found. Skipping dynatrace exporter configuration"); return NoopMetricExporter.getInstance(); } - String apiToken = cfService.getCredentials().getString(tokenName); + String apiToken = credentials.getString(tokenName); if (isBlank(apiUrl)) { LOG.warning("Credential \"" + tokenName + "\" not found. Skipping dynatrace exporter configuration"); return NoopMetricExporter.getInstance(); } OtlpHttpMetricExporterBuilder builder = OtlpHttpMetricExporter.builder(); - builder.setEndpoint(apiUrl + DT_APIURL_METRICS_SUFFIX) - .setCompression(getCompression(config)) - .addHeader("Authorization", "Api-Token " + apiToken) - .setRetryPolicy(RetryPolicy.getDefault()) - .setAggregationTemporalitySelector(AggregationTemporalitySelector.alwaysCumulative()) - .setDefaultAggregationSelector(getDefaultAggregationSelector(config)); + builder.setEndpoint(apiUrl + DT_APIURL_METRICS_SUFFIX).setCompression(getCompression(config)) + .addHeader("Authorization", "Api-Token " + apiToken).setRetryPolicy(RetryPolicy.getDefault()) + .setAggregationTemporalitySelector(getAggregationTemporalitySelector(config)) + .setDefaultAggregationSelector(getDefaultAggregationSelector(config)); Duration timeOut = getTimeOut(config); if (timeOut != null) { builder.setTimeout(timeOut); } - LOG.info("Created metrics exporter for service binding " + cfService.getName() + " (" + cfService.getLabel() + ")"); + LOG.info( + "Created metrics exporter for service binding " + cfService.getName() + " (" + cfService.getLabel() + ")"); return builder.build(); } diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudFoundryResourceProviderTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudFoundryResourceProviderTest.java index 042d7880..4ab872a7 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudFoundryResourceProviderTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/CloudFoundryResourceProviderTest.java @@ -1,22 +1,55 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import org.junit.Rule; import org.junit.Test; +import uk.org.webcompere.systemstubs.rules.EnvironmentVariablesRule; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collections; import java.util.ServiceLoader; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class CloudFoundryResourceProviderTest { + @Rule + public EnvironmentVariablesRule environmentVariablesRule = new EnvironmentVariablesRule(); + @Test public void canLoadViaSPI() { ServiceLoader loader = ServiceLoader.load(ResourceProvider.class); Stream providers = StreamSupport.stream(loader.spliterator(), false); assertTrue(CloudFoundryResourceProvider.class.getName() + " not loaded via SPI", - providers.anyMatch(p -> p instanceof CloudFoundryResourceProvider)); + providers.anyMatch(p -> p instanceof CloudFoundryResourceProvider)); } -} \ No newline at end of file + @Test + public void generatesResource() throws Exception { + String vcapApplication = + new String(Files.readAllBytes(Paths.get(getClass().getResource("vcap_application.json").toURI()))); + environmentVariablesRule.set("VCAP_APPLICATION", vcapApplication); + + CloudFoundryResourceProvider provider = new CloudFoundryResourceProvider(); + Resource resource = provider.createResource(DefaultConfigProperties.createFromMap(Collections.emptyMap())); + + assertEquals("test-app-id", resource.getAttribute(AttributeKey.stringKey("sap.cf.app_id"))); + assertEquals("test-application", resource.getAttribute(AttributeKey.stringKey("sap.cf.app_name"))); + assertEquals("42", resource.getAttribute(AttributeKey.stringKey("sap.cf.instance_id"))); + assertEquals("test-org-id", resource.getAttribute(AttributeKey.stringKey("sap.cf.org_id"))); + assertEquals("test-org", resource.getAttribute(AttributeKey.stringKey("sap.cf.org_name"))); + assertEquals("test-process-id", resource.getAttribute(AttributeKey.stringKey("sap.cf.process.id"))); + assertEquals("test-process-type", resource.getAttribute(AttributeKey.stringKey("sap.cf.process.type"))); + assertEquals("test-app-id", resource.getAttribute(AttributeKey.stringKey("sap.cf.source_id"))); + assertEquals("test-space-id", resource.getAttribute(AttributeKey.stringKey("sap.cf.space_id"))); + assertEquals("test-space", resource.getAttribute(AttributeKey.stringKey("sap.cf.space_name"))); + assertEquals("test-application", resource.getAttribute(AttributeKey.stringKey("service.name"))); + } +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/attributes/CloudFoundryResourceCustomizerTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/attributes/CloudFoundryResourceCustomizerTest.java index 08946408..7259db74 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/attributes/CloudFoundryResourceCustomizerTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/attributes/CloudFoundryResourceCustomizerTest.java @@ -3,63 +3,67 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.resources.Resource; -import io.pivotal.cfenv.core.CfApplication; -import io.pivotal.cfenv.core.CfEnv; import org.junit.Test; -import org.mockito.Mockito; +import java.util.Collections; import java.util.HashMap; -import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; public class CloudFoundryResourceCustomizerTest { + private static final Resource DEFAULT_CF_RESOURCE = + Resource.builder().put("cloudfoundry.app.id", "test-app-id").put("cloudfoundry.app.instance.id", "42") + .put("cloudfoundry.app.name", "test-application").put("cloudfoundry.org.id", "test-org-id") + .put("cloudfoundry.org.name", "test-org").put("cloudfoundry.process.id", "test-process-id") + .put("cloudfoundry.process.type", "test-process-type").put("cloudfoundry.space.id", "test-space-id") + .put("cloudfoundry.space.name", "test-space").build(); + @Test public void emptyResourceWhenNotInCf() { - CloudFoundryResourceCustomizer customizer = new CloudFoundryResourceCustomizer(new CfEnv()); - Resource resource = customizer.apply(Resource.builder().build(), DefaultConfigProperties.create(new HashMap<>())); + CloudFoundryResourceCustomizer customizer = new CloudFoundryResourceCustomizer(); + Resource resource = + customizer.apply(Resource.builder().build(), DefaultConfigProperties.create(new HashMap<>())); assertTrue(resource.getAttributes().isEmpty()); } @Test public void emptyResourceWhenDisabledByProperty() { - CfEnv cfEnv = Mockito.mock(CfEnv.class); - when(cfEnv.isInCf()).thenReturn(true); HashMap properties = new HashMap<>(); properties.put("otel.javaagent.extension.sap.cf.resource.enabled", "false"); - CloudFoundryResourceCustomizer customizer = new CloudFoundryResourceCustomizer(cfEnv); + CloudFoundryResourceCustomizer customizer = new CloudFoundryResourceCustomizer(); Resource resource = customizer.apply(Resource.builder().build(), DefaultConfigProperties.create(properties)); assertTrue(resource.getAttributes().isEmpty()); } @Test public void fillsResourceFromVcapApplication() { - CfEnv cfEnv = Mockito.mock(CfEnv.class); - when(cfEnv.isInCf()).thenReturn(true); - Map applicationData = new HashMap<>(); - applicationData.put("application_name", "test-application"); - applicationData.put("space_name", "test-space"); - applicationData.put("organization_name", "test-org"); - applicationData.put("application_id", "test-app-id"); - applicationData.put("instance_index", 42); - applicationData.put("process_id", "test-process-id"); - applicationData.put("process_type", "test-process-type"); - when(cfEnv.getApp()).thenReturn(new CfApplication(applicationData)); - - CloudFoundryResourceCustomizer customizer = new CloudFoundryResourceCustomizer(cfEnv); - Resource resource = customizer.apply(Resource.builder().build(), DefaultConfigProperties.create(new HashMap<>())); + CloudFoundryResourceCustomizer customizer = new CloudFoundryResourceCustomizer(); + Resource resource = + customizer.apply(DEFAULT_CF_RESOURCE, DefaultConfigProperties.create(Collections.emptyMap())); assertEquals("test-application", resource.getAttribute(AttributeKey.stringKey("service.name"))); assertEquals("test-application", resource.getAttribute(AttributeKey.stringKey("sap.cf.app_name"))); + assertEquals("test-app-id", resource.getAttribute(AttributeKey.stringKey("sap.cf.app_id"))); assertEquals("test-space", resource.getAttribute(AttributeKey.stringKey("sap.cf.space_name"))); assertEquals("test-org", resource.getAttribute(AttributeKey.stringKey("sap.cf.org_name"))); - assertEquals("test-app-id", resource.getAttribute(AttributeKey.stringKey("sap.cf.source_id"))); - assertEquals(42, resource.getAttribute(AttributeKey.longKey("sap.cf.instance_id")).longValue()); + assertEquals("42", resource.getAttribute(AttributeKey.stringKey("sap.cf.instance_id"))); assertEquals("test-process-id", resource.getAttribute(AttributeKey.stringKey("sap.cf.process.id"))); assertEquals("test-process-type", resource.getAttribute(AttributeKey.stringKey("sap.cf.process.type"))); + assertEquals("test-app-id", resource.getAttribute(AttributeKey.stringKey("sap.cf.source_id"))); + assertEquals("test-application", resource.getAttribute(AttributeKey.stringKey("service.name"))); + } + + @Test + public void keepsOriginalResourceOnOTelResourceFormat() { + CloudFoundryResourceCustomizer customizer = new CloudFoundryResourceCustomizer(); + HashMap config = new HashMap() {{ + put("otel.javaagent.extension.sap.cf.resource.format", "opentelemetry"); + }}; + Resource resource = customizer.apply(DEFAULT_CF_RESOURCE, DefaultConfigProperties.create(config)); + + assertEquals(DEFAULT_CF_RESOURCE, resource); } -} \ No newline at end of file +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapterTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapterTest.java index 2077d810..50d35304 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapterTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudFoundryServicesAdapterTest.java @@ -1,7 +1,5 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding; -import io.pivotal.cfenv.core.CfEnv; -import io.pivotal.cfenv.core.CfService; import org.hamcrest.FeatureMatcher; import org.jetbrains.annotations.NotNull; import org.junit.Test; @@ -16,31 +14,29 @@ import static org.hamcrest.Matchers.*; public class CloudFoundryServicesAdapterTest { - private static final String DEFAULT_VCAP_APPLICATION = "{}"; - private static final String DEFAULT_VCAP_SERVICES = "{" + - "\"managed-find-me-service\":[" + - "{\"label\":\"managed-find-me-service\", \"tags\":[\"Find Me!\"],\"name\":\"managed-find-me1\"}," + - "{\"label\":\"managed-find-me-service\", \"tags\":[\"Find Me!\"],\"name\":\"managed-find-me2\"}," + - "{\"label\":\"managed-find-me-service\", \"tags\":[\"You can't see me!\"],\"name\":\"managed-other\"}" + - "]," + - "\"managed-notice-me-not-service\":[" + - "{\"label\":\"managed-notice-me-not-service\", \"tags\":[\"Find Me!\"],\"name\":\"managed-other1\"}," + - "{\"label\":\"managed-notice-me-not-service\", \"tags\":[\"You can't see me!\"],\"name\":\"managed-other2\"}" + - "]," + - "\"user-provided\":[" + - "{\"label\":\"user-provided\", \"tags\":[\"Find Me!\"],\"name\":\"ups-find-me1\"}," + - "{\"label\":\"user-provided\", \"tags\":[\"Find Me!\"],\"name\":\"ups-find-me2\"}," + - "{\"label\":\"user-provided\", \"tags\":[\"You can't see me!\"],\"name\":\"ups-other\"}" + - "]}"; + private static final String DEFAULT_VCAP_SERVICES = "{" + "\"managed-find-me-service\":[" // + + "{\"label\":\"managed-find-me-service\", \"tags\":[\"Find Me!\"],\"name\":\"managed-find-me1\"}," // + + "{\"label\":\"managed-find-me-service\", \"tags\":[\"Find Me!\"],\"name\":\"managed-find-me2\"}," // + + "{\"label\":\"managed-find-me-service\", \"tags\":[\"You can't see me!\"],\"name\":\"managed-other\"}" // + + "]," // + + "\"managed-notice-me-not-service\":[" // + + "{\"label\":\"managed-notice-me-not-service\", \"tags\":[\"Find Me!\"],\"name\":\"managed-other1\"}," // + + "{\"label\":\"managed-notice-me-not-service\", \"tags\":[\"You can't see me!\"],\"name\":\"managed-other2\"}" // + + "]," // + + "\"user-provided\":[" // + + "{\"label\":\"user-provided\", \"tags\":[\"Find Me!\"],\"name\":\"ups-find-me1\"}," // + + "{\"label\":\"user-provided\", \"tags\":[\"Find Me!\"],\"name\":\"ups-find-me2\"}," // + + "{\"label\":\"user-provided\", \"tags\":[\"You can't see me!\"],\"name\":\"ups-other\"}" // + + "]}"; - private static final CfEnv DEFAULT_CF_ENV = new CfEnv(DEFAULT_VCAP_APPLICATION, DEFAULT_VCAP_SERVICES); - private static final CloudFoundryServicesAdapter DEFAULT_ADAPTER = new CloudFoundryServicesAdapter(DEFAULT_CF_ENV); + private static final CloudFoundryServicesAdapter DEFAULT_ADAPTER = + new CloudFoundryServicesAdapter(DEFAULT_VCAP_SERVICES); @NotNull - private static FeatureMatcher withName(String expected) { - return new FeatureMatcher(equalTo(expected), "name", "name") { + private static FeatureMatcher withName(String expected) { + return new FeatureMatcher(equalTo(expected), "name", "name") { @Override - protected String featureValueOf(CfService cfService) { + protected String featureValueOf(CloudFoundryServiceInstance cfService) { return cfService.getName(); } }; @@ -48,66 +44,50 @@ protected String featureValueOf(CfService cfService) { @Test public void getsAllServicesWithNullParameters() { - List services = DEFAULT_ADAPTER.stream(null, null).collect(toList()); - assertThat(services, allOf( - hasItem(withName("managed-find-me1")), - hasItem(withName("managed-find-me2")), - hasItem(withName("managed-other")), - hasItem(withName("managed-other1")), - hasItem(withName("managed-other2")), - hasItem(withName("ups-find-me1")), - hasItem(withName("ups-find-me2")), - hasItem(withName("ups-other")) - )); + List services = DEFAULT_ADAPTER.stream(null, null).collect(toList()); + assertThat(services, allOf(hasItem(withName("managed-find-me1")), hasItem(withName("managed-find-me2")), + hasItem(withName("managed-other")), hasItem(withName("managed-other1")), + hasItem(withName("managed-other2")), hasItem(withName("ups-find-me1")), + hasItem(withName("ups-find-me2")), hasItem(withName("ups-other")))); } @Test public void filtersBySingleLabel() { - List services = DEFAULT_ADAPTER.stream(Collections.singletonList("managed-find-me-service"), emptyList()).collect(toList()); - assertThat(services, allOf( - hasItem(withName("managed-find-me1")), - hasItem(withName("managed-find-me2")), - hasItem(withName("managed-other")) - )); + List services = + DEFAULT_ADAPTER.stream(Collections.singletonList("managed-find-me-service"), emptyList()) + .collect(toList()); + assertThat(services, allOf(hasItem(withName("managed-find-me1")), hasItem(withName("managed-find-me2")), + hasItem(withName("managed-other")))); assertThat(services, hasSize(3)); } @Test public void priotizesByServiceLabel() { - List services = DEFAULT_ADAPTER.stream(asList("user-provided", "managed-find-me-service"), emptyList()).collect(toList()); - assertThat(services, contains( - withName("ups-find-me1"), - withName("ups-find-me2"), - withName("ups-other"), - withName("managed-find-me1"), - withName("managed-find-me2"), - withName("managed-other") - )); + List services = + DEFAULT_ADAPTER.stream(asList("user-provided", "managed-find-me-service"), emptyList()) + .collect(toList()); + assertThat(services, contains(withName("ups-find-me1"), withName("ups-find-me2"), withName("ups-other"), + withName("managed-find-me1"), withName("managed-find-me2"), + withName("managed-other"))); } @Test public void filtersBySingleTag() { - List services = DEFAULT_ADAPTER.stream(emptyList(), Collections.singletonList("Find Me!")).collect(toList()); - assertThat(services, allOf( - hasItem(withName("managed-find-me1")), - hasItem(withName("managed-find-me2")), - hasItem(withName("managed-other1")), - hasItem(withName("ups-find-me1")), - hasItem(withName("ups-find-me2")) - )); + List services = + DEFAULT_ADAPTER.stream(emptyList(), Collections.singletonList("Find Me!")).collect(toList()); + assertThat(services, allOf(hasItem(withName("managed-find-me1")), hasItem(withName("managed-find-me2")), + hasItem(withName("managed-other1")), hasItem(withName("ups-find-me1")), + hasItem(withName("ups-find-me2")))); assertThat(services, hasSize(5)); } @Test public void standardUseCase() { - List services = DEFAULT_ADAPTER.stream(asList("user-provided", "managed-find-me-service"), Collections.singletonList("Find Me!")).collect(toList()); - assertThat(services, contains( - withName("ups-find-me1"), - withName("ups-find-me2"), - withName("managed-find-me1"), - withName("managed-find-me2") - )); + List services = + DEFAULT_ADAPTER.stream(asList("user-provided", "managed-find-me-service"), + Collections.singletonList("Find Me!")).collect(toList()); + assertThat(services, contains(withName("ups-find-me1"), withName("ups-find-me2"), withName("managed-find-me1"), + withName("managed-find-me2"))); } - -} \ No newline at end of file +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingBindingPropertiesSupplierTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingBindingPropertiesSupplierTest.java index 6039d1d5..65210972 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingBindingPropertiesSupplierTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingBindingPropertiesSupplierTest.java @@ -1,6 +1,5 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding; -import io.pivotal.cfenv.core.CfService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -45,18 +44,10 @@ public class CloudLoggingBindingPropertiesSupplierTest { private CloudLoggingBindingPropertiesSupplier propertiesSupplier; private static void assertFileContent(String expected, String filename) throws IOException { - String contents = Files.readAllLines(Paths.get(filename)) - .stream() - .collect(Collectors.joining("\n")); + String contents = Files.readAllLines(Paths.get(filename)).stream().collect(Collectors.joining("\n")); assertThat(contents, is(equalTo(expected))); } - - private static CfService createCfService(Map properties, Map credentials) { - return new CfService(new HashMap(properties) {{ - put("credentials", credentials); - }}); - } - + @Test public void emptyWithoutBindings() { when(servicesProvider.get()).thenReturn(Stream.empty()); @@ -66,8 +57,14 @@ public void emptyWithoutBindings() { @Test public void extractsBinding() throws Exception { - when(servicesProvider.get()).thenReturn(Stream.of(createCfService(BINDING, CREDENTIALS))); - CloudLoggingBindingPropertiesSupplier propertiesSupplier = new CloudLoggingBindingPropertiesSupplier(servicesProvider); + CloudFoundryCredentials credentials = + CloudFoundryCredentials.builder().add("ingest-otlp-endpoint", "test-endpoint") + .add("ingest-otlp-key", "test-client-key") + .add("ingest-otlp-cert", "test-client-cert").add("server-ca", "test-server-cert") + .build(); + when(servicesProvider.get()).thenReturn(Stream.of(defaultInstance().credentials(credentials).build())); + CloudLoggingBindingPropertiesSupplier propertiesSupplier = + new CloudLoggingBindingPropertiesSupplier(servicesProvider); Map properties = propertiesSupplier.get(); @@ -82,13 +79,19 @@ public void extractsBinding() throws Exception { assertFileContent("test-server-cert", properties.get("otel.exporter.otlp.certificate")); } + private static CloudFoundryServiceInstance.Builder defaultInstance() { + return CloudFoundryServiceInstance.builder().name("test-name").label("user-provided").tag("Cloud Logging"); + } + @Test public void emptyWithoutEndpoint() { - HashMap credentials = new HashMap(CREDENTIALS) {{ - remove("ingest-otlp-endpoint"); - }}; - when(servicesProvider.get()).thenReturn(Stream.of(createCfService(BINDING, credentials))); - CloudLoggingBindingPropertiesSupplier propertiesSupplier = new CloudLoggingBindingPropertiesSupplier(servicesProvider); + CloudFoundryCredentials credentials = + CloudFoundryCredentials.builder().add("ingest-otlp-key", "test-client-key") + .add("ingest-otlp-cert", "test-client-cert").add("server-ca", "test-server-cert") + .build(); + when(servicesProvider.get()).thenReturn(Stream.of(defaultInstance().credentials(credentials).build())); + CloudLoggingBindingPropertiesSupplier propertiesSupplier = + new CloudLoggingBindingPropertiesSupplier(servicesProvider); Map properties = propertiesSupplier.get(); @@ -97,11 +100,13 @@ public void emptyWithoutEndpoint() { @Test public void emptyWithoutClientCert() { - HashMap credentials = new HashMap(CREDENTIALS) {{ - remove("ingest-otlp-cert"); - }}; - when(servicesProvider.get()).thenReturn(Stream.of(createCfService(BINDING, credentials))); - CloudLoggingBindingPropertiesSupplier propertiesSupplier = new CloudLoggingBindingPropertiesSupplier(servicesProvider); + CloudFoundryCredentials credentials = + CloudFoundryCredentials.builder().add("ingest-otlp-endpoint", "test-endpoint") + .add("ingest-otlp-key", "test-client-key").add("server-ca", "test-server-cert") + .build(); + when(servicesProvider.get()).thenReturn(Stream.of(defaultInstance().credentials(credentials).build())); + CloudLoggingBindingPropertiesSupplier propertiesSupplier = + new CloudLoggingBindingPropertiesSupplier(servicesProvider); Map properties = propertiesSupplier.get(); @@ -110,11 +115,13 @@ public void emptyWithoutClientCert() { @Test public void emptyWithoutClientKey() { - HashMap credentials = new HashMap(CREDENTIALS) {{ - remove("ingest-otlp-key"); - }}; - when(servicesProvider.get()).thenReturn(Stream.of(createCfService(BINDING, credentials))); - CloudLoggingBindingPropertiesSupplier propertiesSupplier = new CloudLoggingBindingPropertiesSupplier(servicesProvider); + CloudFoundryCredentials credentials = + CloudFoundryCredentials.builder().add("ingest-otlp-endpoint", "test-endpoint") + .add("ingest-otlp-cert", "test-client-cert").add("server-ca", "test-server-cert") + .build(); + when(servicesProvider.get()).thenReturn(Stream.of(defaultInstance().credentials(credentials).build())); + CloudLoggingBindingPropertiesSupplier propertiesSupplier = + new CloudLoggingBindingPropertiesSupplier(servicesProvider); Map properties = propertiesSupplier.get(); @@ -123,14 +130,16 @@ public void emptyWithoutClientKey() { @Test public void emptyWithoutServerCert() { - HashMap credentials = new HashMap(CREDENTIALS) {{ - remove("server-ca"); - }}; - when(servicesProvider.get()).thenReturn(Stream.of(createCfService(BINDING, credentials))); - CloudLoggingBindingPropertiesSupplier propertiesSupplier = new CloudLoggingBindingPropertiesSupplier(servicesProvider); + CloudFoundryCredentials credentials = + CloudFoundryCredentials.builder().add("ingest-otlp-endpoint", "test-endpoint") + .add("ingest-otlp-key", "test-client-key") + .add("ingest-otlp-cert", "test-client-cert").build(); + when(servicesProvider.get()).thenReturn(Stream.of(defaultInstance().credentials(credentials).build())); + CloudLoggingBindingPropertiesSupplier propertiesSupplier = + new CloudLoggingBindingPropertiesSupplier(servicesProvider); Map properties = propertiesSupplier.get(); assertTrue(properties.isEmpty()); } -} \ No newline at end of file +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingServicesProviderTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingServicesProviderTest.java index 73a90834..df31bcf1 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingServicesProviderTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/CloudLoggingServicesProviderTest.java @@ -1,7 +1,6 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.pivotal.cfenv.core.CfService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,7 +27,7 @@ public class CloudLoggingServicesProviderTest { private CloudFoundryServicesAdapter adapter; @Mock - private CfService mockService; + private CloudFoundryServiceInstance mockService; @Before public void setUp() throws Exception { @@ -53,7 +52,8 @@ public void customLabel() { CloudLoggingServicesProvider provider = new CloudLoggingServicesProvider(config, adapter); assertThat(provider.get().collect(toList()), contains(mockService)); - verify(adapter).stream(asList("unknown-label", "not-cloud-logging"), Collections.singletonList("Cloud Logging")); + verify(adapter).stream(asList("unknown-label", "not-cloud-logging"), + Collections.singletonList("Cloud Logging")); } @Test @@ -64,7 +64,8 @@ public void customTag() { CloudLoggingServicesProvider provider = new CloudLoggingServicesProvider(emptyProperties, adapter); assertThat(provider.get().collect(toList()), contains(mockService)); - verify(adapter).stream(asList("user-provided", "cloud-logging"), Collections.singletonList("NOT Cloud Logging")); + verify(adapter).stream(asList("user-provided", "cloud-logging"), + Collections.singletonList("NOT Cloud Logging")); } -} \ No newline at end of file +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/DynatraceServicesProviderTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/DynatraceServicesProviderTest.java index 179f215e..ecde8568 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/DynatraceServicesProviderTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/binding/DynatraceServicesProviderTest.java @@ -1,7 +1,6 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.pivotal.cfenv.core.CfService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,7 +26,7 @@ public class DynatraceServicesProviderTest { private CloudFoundryServicesAdapter adapter; @Mock - private CfService mockService; + private CloudFoundryServiceInstance mockService; @Before public void setUp() throws Exception { @@ -66,4 +65,4 @@ public void customTag() { verify(adapter).stream(asList("user-provided", "dynatrace"), Collections.singletonList("NOT dynatrace")); } -} \ No newline at end of file +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingCredentialsTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingCredentialsTest.java index c79961a9..40e96f94 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingCredentialsTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingCredentialsTest.java @@ -1,12 +1,9 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; -import io.pivotal.cfenv.core.CfCredentials; -import org.jetbrains.annotations.NotNull; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryCredentials; import org.junit.Test; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -15,83 +12,78 @@ public class CloudLoggingCredentialsTest { - private static final String VALID_CLIENT_CERT = "-----BEGIN CERTIFICATE-----\n" + - "Base-64-Encoded Certificate\n" + - "-----END CERTIFICATE-----\n"; + private static final String VALID_CLIENT_CERT = + "-----BEGIN CERTIFICATE-----\n" + "Base-64-Encoded Certificate\n" + "-----END CERTIFICATE-----\n"; - private static final String VALID_CLIENT_KEY = "-----BEGIN PRIVATE KEY-----\n" + - "Base-64-Encoded Private Key\n" + - "-----END PRIVATE KEY-----\n"; + private static final String VALID_CLIENT_KEY = + "-----BEGIN PRIVATE KEY-----\n" + "Base-64-Encoded Private Key\n" + "-----END PRIVATE KEY-----\n"; - private static final String VALID_SERVER_CERT = "-----BEGIN CERTIFICATE-----\n" + - "Base-64-Encoded Server Certificate\n" + - "-----END CERTIFICATE-----\n"; + private static final String VALID_SERVER_CERT = + "-----BEGIN CERTIFICATE-----\n" + "Base-64-Encoded Server Certificate\n" + "-----END CERTIFICATE-----\n"; private static final CloudLoggingCredentials.Parser PARSER = CloudLoggingCredentials.parser(); - @NotNull - private static Map getValidCredData() { - Map credData = new HashMap<>(); - credData.put("ingest-otlp-endpoint", "test-endpoint"); - credData.put("ingest-otlp-cert", VALID_CLIENT_CERT); - credData.put("ingest-otlp-key", VALID_CLIENT_KEY); - credData.put("server-ca", VALID_SERVER_CERT); - return credData; - } - @Test public void validCredentials() { - Map credData = getValidCredData(); - CfCredentials cfCredentials = new CfCredentials(credData); - CloudLoggingCredentials credentials = PARSER.parse(cfCredentials); + CloudFoundryCredentials.Builder builder = + CloudFoundryCredentials.builder().add("ingest-otlp-endpoint", "test-endpoint") + .add("ingest-otlp-cert", VALID_CLIENT_CERT) + .add("ingest-otlp-key", VALID_CLIENT_KEY).add("server-ca", VALID_SERVER_CERT); + CloudLoggingCredentials credentials = PARSER.parse(builder.build()); assertTrue("Credentials should be valid", credentials.validate()); } @Test public void missingEndpoint() { - Map credData = getValidCredData(); - credData.remove("ingest-otlp-endpoint"); - CfCredentials cfCredentials = new CfCredentials(credData); - CloudLoggingCredentials credentials = PARSER.parse(cfCredentials); + CloudFoundryCredentials.Builder builder = + CloudFoundryCredentials.builder().add("ingest-otlp-cert", VALID_CLIENT_CERT) + .add("ingest-otlp-key", VALID_CLIENT_KEY).add("server-ca", VALID_SERVER_CERT); + CloudLoggingCredentials credentials = PARSER.parse(builder.build()); assertFalse("Credentials should be invalid", credentials.validate()); } @Test public void missingClientKey() { - Map credData = getValidCredData(); - credData.remove("ingest-otlp-key"); - CfCredentials cfCredentials = new CfCredentials(credData); - CloudLoggingCredentials credentials = PARSER.parse(cfCredentials); + CloudFoundryCredentials.Builder builder = + CloudFoundryCredentials.builder().add("ingest-otlp-endpoint", "test-endpoint") + .add("ingest-otlp-cert", VALID_CLIENT_CERT).add("server-ca", VALID_SERVER_CERT); + CloudLoggingCredentials credentials = PARSER.parse(builder.build()); assertFalse("Credentials should be invalid", credentials.validate()); } @Test public void missingClientCert() { - Map credData = getValidCredData(); - credData.remove("ingest-otlp-cert"); - CfCredentials cfCredentials = new CfCredentials(credData); - CloudLoggingCredentials credentials = PARSER.parse(cfCredentials); + CloudFoundryCredentials.Builder builder = + CloudFoundryCredentials.builder().add("ingest-otlp-endpoint", "test-endpoint") + .add("ingest-otlp-key", VALID_CLIENT_KEY).add("server-ca", VALID_SERVER_CERT); + CloudLoggingCredentials credentials = PARSER.parse(builder.build()); assertFalse("Credentials should be invalid", credentials.validate()); } @Test public void missingServerCert() { - Map credData = getValidCredData(); - credData.remove("server-ca"); - CfCredentials cfCredentials = new CfCredentials(credData); - CloudLoggingCredentials credentials = PARSER.parse(cfCredentials); + CloudFoundryCredentials.Builder builder = + CloudFoundryCredentials.builder().add("ingest-otlp-endpoint", "test-endpoint") + .add("ingest-otlp-cert", VALID_CLIENT_CERT) + .add("ingest-otlp-key", VALID_CLIENT_KEY); + CloudLoggingCredentials credentials = PARSER.parse(builder.build()); assertFalse("Credentials should be invalid", credentials.validate()); } @Test public void parsesCorrectly() { - Map credData = getValidCredData(); - CfCredentials cfCredentials = new CfCredentials(credData); - CloudLoggingCredentials credentials = PARSER.parse(cfCredentials); + CloudFoundryCredentials.Builder builder = + CloudFoundryCredentials.builder().add("ingest-otlp-endpoint", "test-endpoint") + .add("ingest-otlp-cert", VALID_CLIENT_CERT) + .add("ingest-otlp-key", VALID_CLIENT_KEY).add("server-ca", VALID_SERVER_CERT); + CloudLoggingCredentials credentials = PARSER.parse(builder.build()); assertThat(credentials.getEndpoint(), equalTo("https://test-endpoint")); - assertThat(new String(credentials.getClientCert(), StandardCharsets.UTF_8), equalTo("-----BEGIN CERTIFICATE-----\nBase-64-Encoded Certificate\n-----END CERTIFICATE-----")); - assertThat(new String(credentials.getClientKey(), StandardCharsets.UTF_8), equalTo("-----BEGIN PRIVATE KEY-----\nBase-64-Encoded Private Key\n-----END PRIVATE KEY-----")); - assertThat(new String(credentials.getServerCert(), StandardCharsets.UTF_8), equalTo("-----BEGIN CERTIFICATE-----\nBase-64-Encoded Server Certificate\n-----END CERTIFICATE-----")); + assertThat(new String(credentials.getClientCert(), StandardCharsets.UTF_8), + equalTo("-----BEGIN CERTIFICATE-----\nBase-64-Encoded Certificate\n-----END CERTIFICATE-----")); + assertThat(new String(credentials.getClientKey(), StandardCharsets.UTF_8), + equalTo("-----BEGIN PRIVATE KEY-----\nBase-64-Encoded Private Key\n-----END PRIVATE KEY-----")); + assertThat(new String(credentials.getServerCert(), StandardCharsets.UTF_8), + equalTo("-----BEGIN CERTIFICATE-----\nBase-64-Encoded Server Certificate\n-----END CERTIFICATE-----")); } } diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingLogsExporterProviderTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingLogsExporterProviderTest.java index a20afe76..0653d603 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingLogsExporterProviderTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingLogsExporterProviderTest.java @@ -1,9 +1,9 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryServiceInstance; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider; import io.opentelemetry.sdk.logs.export.LogRecordExporter; -import io.pivotal.cfenv.core.CfService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -14,7 +14,6 @@ import org.mockito.stubbing.Answer; import java.io.IOException; -import java.util.Collections; import java.util.ServiceLoader; import java.util.function.Function; import java.util.stream.Stream; @@ -31,7 +30,7 @@ public class CloudLoggingLogsExporterProviderTest { @Mock - private Function> servicesProvider; + private Function> servicesProvider; @Mock private CloudLoggingCredentials.Parser credentialParser; @@ -55,10 +54,11 @@ public Object answer(InvocationOnMock invocation) throws Throwable { @Test public void canLoadViaSPI() { - ServiceLoader loader = ServiceLoader.load(ConfigurableLogRecordExporterProvider.class); + ServiceLoader loader = + ServiceLoader.load(ConfigurableLogRecordExporterProvider.class); Stream providers = StreamSupport.stream(loader.spliterator(), false); assertTrue(CloudLoggingLogsExporterProvider.class.getName() + " not loaded via SPI", - providers.anyMatch(p -> p instanceof CloudLoggingLogsExporterProvider)); + providers.anyMatch(p -> p instanceof CloudLoggingLogsExporterProvider)); } @Test @@ -71,8 +71,7 @@ public void registersNoopExporterWithoutBindings() { @Test public void registersNoopExporterWithInvalidBindings() { - CfService genericCfService = new CfService(Collections.emptyMap()); - when(servicesProvider.apply(config)).thenReturn(Stream.of(genericCfService)); + when(servicesProvider.apply(config)).thenReturn(Stream.of(CloudFoundryServiceInstance.builder().build())); CloudLoggingCredentials cloudLoggingCredentials = mock(CloudLoggingCredentials.class); when(credentialParser.parse(any())).thenReturn(cloudLoggingCredentials); when(cloudLoggingCredentials.validate()).thenReturn(false); @@ -83,8 +82,8 @@ public void registersNoopExporterWithInvalidBindings() { @Test public void registersExportersWithValidBindings() throws IOException { - CfService genericCfService = new CfService(Collections.emptyMap()); - CfService cloudLoggingService = new CfService(Collections.emptyMap()); + CloudFoundryServiceInstance genericCfService = CloudFoundryServiceInstance.builder().build(); + CloudFoundryServiceInstance cloudLoggingService = CloudFoundryServiceInstance.builder().build(); when(servicesProvider.apply(config)).thenReturn(Stream.of(genericCfService, cloudLoggingService)); CloudLoggingCredentials invalidCredentials = mock(CloudLoggingCredentials.class); when(invalidCredentials.validate()).thenReturn(false); @@ -97,7 +96,8 @@ public void registersExportersWithValidBindings() throws IOException { when(credentialParser.parse(any())).thenReturn(invalidCredentials).thenReturn(validCredentials); LogRecordExporter exporter = exporterProvider.createExporter(config); assertThat(exporter, is(notNullValue())); - assertThat(exporter.toString(), both(containsString("OtlpGrpcLogRecordExporter")).and(containsString("https://otlp-example.sap"))); + assertThat(exporter.toString(), + both(containsString("OtlpGrpcLogRecordExporter")).and(containsString("https://otlp-example.sap"))); } -} \ No newline at end of file +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProviderTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProviderTest.java index 6eccff8f..5210a459 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProviderTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingMetricsExporterProviderTest.java @@ -1,9 +1,9 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryServiceInstance; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider; import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.pivotal.cfenv.core.CfService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -14,7 +14,6 @@ import org.mockito.stubbing.Answer; import java.io.IOException; -import java.util.Collections; import java.util.ServiceLoader; import java.util.function.Function; import java.util.stream.Stream; @@ -31,7 +30,7 @@ public class CloudLoggingMetricsExporterProviderTest { @Mock - private Function> servicesProvider; + private Function> servicesProvider; @Mock private CloudLoggingCredentials.Parser credentialParser; @@ -55,10 +54,11 @@ public Object answer(InvocationOnMock invocation) throws Throwable { @Test public void canLoadViaSPI() { - ServiceLoader loader = ServiceLoader.load(ConfigurableMetricExporterProvider.class); + ServiceLoader loader = + ServiceLoader.load(ConfigurableMetricExporterProvider.class); Stream providers = StreamSupport.stream(loader.spliterator(), false); assertTrue(CloudLoggingMetricsExporterProvider.class.getName() + " not loaded via SPI", - providers.anyMatch(p -> p instanceof CloudLoggingMetricsExporterProvider)); + providers.anyMatch(p -> p instanceof CloudLoggingMetricsExporterProvider)); } @Test @@ -71,8 +71,7 @@ public void registersNoopExporterWithoutBindings() { @Test public void registersNoopExporterWithInvalidBindings() { - CfService genericCfService = new CfService(Collections.emptyMap()); - when(servicesProvider.apply(config)).thenReturn(Stream.of(genericCfService)); + when(servicesProvider.apply(config)).thenReturn(Stream.of(CloudFoundryServiceInstance.builder().build())); CloudLoggingCredentials cloudLoggingCredentials = mock(CloudLoggingCredentials.class); when(credentialParser.parse(any())).thenReturn(cloudLoggingCredentials); when(cloudLoggingCredentials.validate()).thenReturn(false); @@ -83,8 +82,8 @@ public void registersNoopExporterWithInvalidBindings() { @Test public void registersExportersWithValidBindings() throws IOException { - CfService genericCfService = new CfService(Collections.emptyMap()); - CfService cloudLoggingService = new CfService(Collections.emptyMap()); + CloudFoundryServiceInstance genericCfService = CloudFoundryServiceInstance.builder().build(); + CloudFoundryServiceInstance cloudLoggingService = CloudFoundryServiceInstance.builder().build(); when(servicesProvider.apply(config)).thenReturn(Stream.of(genericCfService, cloudLoggingService)); CloudLoggingCredentials invalidCredentials = mock(CloudLoggingCredentials.class); when(invalidCredentials.validate()).thenReturn(false); @@ -97,7 +96,8 @@ public void registersExportersWithValidBindings() throws IOException { when(credentialParser.parse(any())).thenReturn(invalidCredentials).thenReturn(validCredentials); MetricExporter exporter = exporterProvider.createExporter(config); assertThat(exporter, is(notNullValue())); - assertThat(exporter.toString(), both(containsString("OtlpGrpcMetricExporter")).and(containsString("https://otlp-example.sap"))); + assertThat(exporter.toString(), + both(containsString("OtlpGrpcMetricExporter")).and(containsString("https://otlp-example.sap"))); } -} \ No newline at end of file +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingSpanExporterProviderTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingSpanExporterProviderTest.java index b1057c49..313d00db 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingSpanExporterProviderTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/CloudLoggingSpanExporterProviderTest.java @@ -1,9 +1,9 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryServiceInstance; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; import io.opentelemetry.sdk.trace.export.SpanExporter; -import io.pivotal.cfenv.core.CfService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -14,7 +14,6 @@ import org.mockito.stubbing.Answer; import java.io.IOException; -import java.util.Collections; import java.util.ServiceLoader; import java.util.function.Function; import java.util.stream.Stream; @@ -31,7 +30,7 @@ public class CloudLoggingSpanExporterProviderTest { @Mock - private Function> servicesProvider; + private Function> servicesProvider; @Mock private CloudLoggingCredentials.Parser credentialParser; @@ -55,10 +54,11 @@ public Object answer(InvocationOnMock invocation) throws Throwable { @Test public void canLoadViaSPI() { - ServiceLoader loader = ServiceLoader.load(ConfigurableSpanExporterProvider.class); + ServiceLoader loader = + ServiceLoader.load(ConfigurableSpanExporterProvider.class); Stream providers = StreamSupport.stream(loader.spliterator(), false); assertTrue(CloudLoggingSpanExporterProvider.class.getName() + " not loaded via SPI", - providers.anyMatch(p -> p instanceof CloudLoggingSpanExporterProvider)); + providers.anyMatch(p -> p instanceof CloudLoggingSpanExporterProvider)); } @Test @@ -71,8 +71,7 @@ public void registersNoopExporterWithoutBindings() { @Test public void registersNoopExporterWithInvalidBindings() { - CfService genericCfService = new CfService(Collections.emptyMap()); - when(servicesProvider.apply(config)).thenReturn(Stream.of(genericCfService)); + when(servicesProvider.apply(config)).thenReturn(Stream.of(CloudFoundryServiceInstance.builder().build())); CloudLoggingCredentials cloudLoggingCredentials = mock(CloudLoggingCredentials.class); when(credentialParser.parse(any())).thenReturn(cloudLoggingCredentials); when(cloudLoggingCredentials.validate()).thenReturn(false); @@ -83,8 +82,8 @@ public void registersNoopExporterWithInvalidBindings() { @Test public void registersExportersWithValidBindings() throws IOException { - CfService genericCfService = new CfService(Collections.emptyMap()); - CfService cloudLoggingService = new CfService(Collections.emptyMap()); + CloudFoundryServiceInstance genericCfService = CloudFoundryServiceInstance.builder().build(); + CloudFoundryServiceInstance cloudLoggingService = CloudFoundryServiceInstance.builder().build(); when(servicesProvider.apply(config)).thenReturn(Stream.of(genericCfService, cloudLoggingService)); CloudLoggingCredentials invalidCredentials = mock(CloudLoggingCredentials.class); when(invalidCredentials.validate()).thenReturn(false); @@ -97,7 +96,8 @@ public void registersExportersWithValidBindings() throws IOException { when(credentialParser.parse(any())).thenReturn(invalidCredentials).thenReturn(validCredentials); SpanExporter exporter = exporterProvider.createExporter(config); assertThat(exporter, is(notNullValue())); - assertThat(exporter.toString(), both(containsString("OtlpGrpcSpanExporter")).and(containsString("https://otlp-example.sap"))); + assertThat(exporter.toString(), + both(containsString("OtlpGrpcSpanExporter")).and(containsString("https://otlp-example.sap"))); } -} \ No newline at end of file +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProviderTest.java b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProviderTest.java index c7c64e80..a3cb5955 100644 --- a/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProviderTest.java +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/java/com/sap/hcf/cf/logging/opentelemetry/agent/ext/exporter/DynatraceMetricsExporterProviderTest.java @@ -1,9 +1,10 @@ package com.sap.hcf.cf.logging.opentelemetry.agent.ext.exporter; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryCredentials; +import com.sap.hcf.cf.logging.opentelemetry.agent.ext.binding.CloudFoundryServiceInstance; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider; import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.pivotal.cfenv.core.CfService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -15,9 +16,6 @@ import org.mockito.stubbing.Answer; import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.ServiceLoader; import java.util.function.Function; import java.util.stream.Stream; @@ -33,7 +31,7 @@ public class DynatraceMetricsExporterProviderTest { @Mock - private Function servicesProvider; + private Function servicesProvider; @Mock private ConfigProperties config; @@ -43,7 +41,8 @@ public class DynatraceMetricsExporterProviderTest { @Before public void setUp() { - when(config.getString("otel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name")).thenReturn("ingest-token"); + when(config.getString("otel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name")).thenReturn( + "ingest-token"); when(config.getString(any(), any())).thenAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { @@ -55,10 +54,11 @@ public Object answer(InvocationOnMock invocation) throws Throwable { @Test public void canLoadViaSPI() { - ServiceLoader loader = ServiceLoader.load(ConfigurableMetricExporterProvider.class); + ServiceLoader loader = + ServiceLoader.load(ConfigurableMetricExporterProvider.class); Stream providers = StreamSupport.stream(loader.spliterator(), false); assertTrue(DynatraceMetricsExporterProviderTest.class.getName() + " not loaded via SPI", - providers.anyMatch(p -> p instanceof DynatraceMetricsExporterProvider)); + providers.anyMatch(p -> p instanceof DynatraceMetricsExporterProvider)); } @Test @@ -71,8 +71,16 @@ public void registersNoopExporterWithoutBindings() { @Test public void registersNoopExporterWithInvalidBindings() { - CfService genericCfService = new CfService(Collections.emptyMap()); - Mockito.when(servicesProvider.apply(config)).thenReturn(genericCfService); + Mockito.when(servicesProvider.apply(config)).thenReturn(CloudFoundryServiceInstance.builder().build()); + MetricExporter exporter = exporterProvider.createExporter(config); + assertThat(exporter, is(notNullValue())); + assertThat(exporter.toString(), containsString("Noop")); + } + + @Test + public void registersNoopExporterWithInvalidBindingsFromEmptyCredentials() { + Mockito.when(servicesProvider.apply(config)).thenReturn( + CloudFoundryServiceInstance.builder().credentials(CloudFoundryCredentials.builder().build()).build()); MetricExporter exporter = exporterProvider.createExporter(config); assertThat(exporter, is(notNullValue())); assertThat(exporter.toString(), containsString("Noop")); @@ -80,19 +88,15 @@ public void registersNoopExporterWithInvalidBindings() { @Test public void registersExportersWithValidBindings() throws IOException { - Map credentials = new HashMap() {{ - put("apiurl", "https://example.dt/api"); - put("ingest-token", "secret"); - }}; - CfService dynatraceService = new CfService(new HashMap() {{ - put("name", "test-dt"); - put("label", "dynatrace"); - put("credentials", credentials); - }}); - when(servicesProvider.apply(config)).thenReturn(dynatraceService); + CloudFoundryServiceInstance dynatraceServiceInstance = + CloudFoundryServiceInstance.builder().name("test-dt").label("dynatrace").credentials( + CloudFoundryCredentials.builder().add("apiurl", "https://example.dt/api") + .add("ingest-token", "secret").build()).build(); + when(servicesProvider.apply(config)).thenReturn(dynatraceServiceInstance); MetricExporter exporter = exporterProvider.createExporter(config); assertThat(exporter, is(notNullValue())); - assertThat(exporter.toString(), both(containsString("OtlpHttpMetricExporter")).and(containsString("https://example.dt/api/v2/otlp/v1/metrics,"))); + assertThat(exporter.toString(), both(containsString("OtlpHttpMetricExporter")).and( + containsString("https://example.dt/api/v2/otlp/v1/metrics,"))); } -} \ No newline at end of file +} diff --git a/cf-java-logging-support-opentelemetry-agent-extension/src/test/resources/com/sap/hcf/cf/logging/opentelemetry/agent/ext/vcap_application.json b/cf-java-logging-support-opentelemetry-agent-extension/src/test/resources/com/sap/hcf/cf/logging/opentelemetry/agent/ext/vcap_application.json new file mode 100644 index 00000000..eba0c1b9 --- /dev/null +++ b/cf-java-logging-support-opentelemetry-agent-extension/src/test/resources/com/sap/hcf/cf/logging/opentelemetry/agent/ext/vcap_application.json @@ -0,0 +1,19 @@ +{ + "application_id": "test-app-id", + "application_name": "test-application", + "application_uris": [ + "test-application.example.com" + ], + "cf_api": "https://api.cf.example.com", + "limits": { + "fds": 256 + }, + "instance_index": 42, + "organization_id": "test-org-id", + "organization_name": "test-org", + "process_id": "test-process-id", + "process_type": "test-process-type", + "space_id": "test-space-id", + "space_name": "test-space", + "users": null +} diff --git a/cf-java-logging-support-servlet-jakarta/pom.xml b/cf-java-logging-support-servlet-jakarta/pom.xml index dc1d6c74..0a7036dc 100644 --- a/cf-java-logging-support-servlet-jakarta/pom.xml +++ b/cf-java-logging-support-servlet-jakarta/pom.xml @@ -10,7 +10,7 @@ com.sap.hcp.cf.logging cf-java-logging-support-parent - 3.8.2 + 3.8.5 ../pom.xml @@ -19,7 +19,7 @@ 3.5.0 3.3.0 3.1.0 - 11.0.14 + 11.0.24 diff --git a/cf-java-logging-support-servlet/pom.xml b/cf-java-logging-support-servlet/pom.xml index 071a50bb..0ccf5f3f 100644 --- a/cf-java-logging-support-servlet/pom.xml +++ b/cf-java-logging-support-servlet/pom.xml @@ -9,13 +9,13 @@ com.sap.hcp.cf.logging cf-java-logging-support-parent - 3.8.2 + 3.8.5 ../pom.xml 3.1.0 - 9.4.51.v20230217 + 9.4.56.v20240826 diff --git a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/AddHttpHeadersToLogContextFilter.java b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/AddHttpHeadersToLogContextFilter.java index 66c4f994..cbf4ccd6 100644 --- a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/AddHttpHeadersToLogContextFilter.java +++ b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/AddHttpHeadersToLogContextFilter.java @@ -6,7 +6,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; @@ -23,8 +22,8 @@ */ public class AddHttpHeadersToLogContextFilter extends AbstractLoggingFilter { - private List headers; - private List fields; + private final List headers; + private final List fields; /** * The default constructor uses {@link HttpHeaders#propagated()} to define @@ -53,16 +52,16 @@ public AddHttpHeadersToLogContextFilter(HttpHeader... headers) { */ public AddHttpHeadersToLogContextFilter(List list, HttpHeader... custom) { Stream allHeaders = Stream.concat(list.stream(), Arrays.stream(custom)); - this.headers = unmodifiableList(allHeaders.filter(HttpHeader::isPropagated).collect(toList())); - this.fields = unmodifiableList(headers.stream().map(HttpHeader::getField).filter(Objects::nonNull).collect( - toList())); + this.headers = unmodifiableList(allHeaders + .filter(HttpHeader::isPropagated).filter(h -> h.getField() != null).collect(toList())); + this.fields = unmodifiableList(headers.stream().map(HttpHeader::getField).collect(toList())); } @Override protected void beforeFilter(HttpServletRequest request, HttpServletResponse response) { for (HttpHeader header: headers) { String headerValue = HttpHeaderUtilities.getHeaderValue(request, header); - if (header.getField() != null && headerValue != null) { + if (headerValue != null) { LogContext.add(header.getField(), headerValue); } } diff --git a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/CorrelationIdFilter.java b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/CorrelationIdFilter.java index a7772514..4853be18 100644 --- a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/CorrelationIdFilter.java +++ b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/CorrelationIdFilter.java @@ -2,10 +2,8 @@ import static com.sap.hcp.cf.logging.common.customfields.CustomField.customField; import static com.sap.hcp.cf.logging.common.request.HttpHeaders.W3C_TRACEPARENT; -import static java.util.Optional.ofNullable; import java.util.UUID; -import java.util.function.Predicate; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -27,8 +25,8 @@ public class CorrelationIdFilter extends AbstractLoggingFilter { private static final Logger LOG = LoggerFactory.getLogger(CorrelationIdFilter.class); - private HttpHeader correlationHeader; - private HttpHeader traceparentHeader; + private final HttpHeader correlationHeader; + private final HttpHeader traceparentHeader; public CorrelationIdFilter() { this(HttpHeaders.CORRELATION_ID); @@ -65,23 +63,26 @@ private String determineCorrelationId(HttpServletRequest request) { return correlationId; } - private boolean isBlankOrDefault(String value) { + private static boolean isBlankOrDefault(String value) { return value == null || value.isEmpty() || value.equals(Defaults.UNKNOWN); } private String getCorrelationIdFromTraceparent(HttpServletRequest request) { String traceparent = HttpHeaderUtilities.getHeaderValue(request, traceparentHeader); - return ofNullable(traceparent).filter(not(this::isBlankOrDefault)).map(this::parseTraceparent).orElse( - null); + return isBlankOrDefault(traceparent) ? null : parseTraceparent(traceparent); } - private Predicate not(Predicate p) { - return p.negate(); - } - - private String parseTraceparent(String value) { - String[] tokens = value.split("-"); - return tokens.length >= 2 ? tokens[1] : null; + private static String parseTraceparent(String value) { + int idx1 = value.indexOf('-'); + if (idx1 != -1) { + ++idx1; + int idx2 = value.indexOf('-', idx1); + if (idx2 != -1) { + // return string between first and second '-'. + return value.substring(idx1, idx2); + } + } + return null; } private void addCorrelationIdHeader(HttpServletResponse response, String correlationId) { diff --git a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/DynamicLogLevelFilter.java b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/DynamicLogLevelFilter.java index 0c727e98..b6c549af 100644 --- a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/DynamicLogLevelFilter.java +++ b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/DynamicLogLevelFilter.java @@ -56,7 +56,13 @@ public DynamicLogLevelFilter() { * use a lambda: {@code () -> config} */ public DynamicLogLevelFilter(ConcurrentInitializer configuration) { - this.configuration = configuration; + this.configuration = new LazyInitializer() { + + @Override + protected DynamicLogLevelConfiguration initialize() throws ConcurrentException { + return configuration.get(); + } + }; this.processor = new LazyInitializer() { @Override diff --git a/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-core/pom.xml b/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-core/pom.xml index a574ad7c..67c73e3d 100644 --- a/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-core/pom.xml +++ b/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-core/pom.xml @@ -4,7 +4,7 @@ com.sap.hcp.cf.logging cf-java-monitoring-custom-metrics-clients - 3.8.2 + 3.8.5 cf-custom-metrics-clients-core diff --git a/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-java/pom.xml b/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-java/pom.xml index 02491ec8..cd5b8641 100644 --- a/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-java/pom.xml +++ b/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-java/pom.xml @@ -5,7 +5,7 @@ com.sap.hcp.cf.logging cf-java-monitoring-custom-metrics-clients - 3.8.2 + 3.8.5 cf-custom-metrics-clients-java diff --git a/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-spring-boot/pom.xml b/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-spring-boot/pom.xml index 4e5cc15e..dc784f6e 100644 --- a/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-spring-boot/pom.xml +++ b/cf-java-monitoring-custom-metrics-clients/cf-custom-metrics-clients-spring-boot/pom.xml @@ -4,7 +4,7 @@ com.sap.hcp.cf.logging cf-java-monitoring-custom-metrics-clients - 3.8.2 + 3.8.5 cf-custom-metrics-clients-spring-boot diff --git a/cf-java-monitoring-custom-metrics-clients/pom.xml b/cf-java-monitoring-custom-metrics-clients/pom.xml index 7e4cc0e0..21d4d94c 100644 --- a/cf-java-monitoring-custom-metrics-clients/pom.xml +++ b/cf-java-monitoring-custom-metrics-clients/pom.xml @@ -9,7 +9,7 @@ com.sap.hcp.cf.logging cf-java-logging-support-parent - 3.8.2 + 3.8.5 ../pom.xml diff --git a/pom.xml b/pom.xml index 7bf36948..09e1ba38 100644 --- a/pom.xml +++ b/pom.xml @@ -1,14 +1,16 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.sap.hcp.cf.logging cf-java-logging-support-parent - 3.8.2 + 3.8.5 pom Cloud Foundry Java logging support components - Supports the creation of structured log messages and the collection of request metrics for Java applications running on Cloud Foundry + Supports the creation of structured log messages and the collection of request metrics for Java + applications running on Cloud Foundry + WolfgangTheilmann @@ -24,76 +26,76 @@ SAP SE https://github.com/SAP - - deyanzhelyazkov - Deyan Zhelyazkov - deyan.zhelyazkov@sap.com - SAP SE - https://github.com/SAP - - - angelts - Angel Tsanev - angel.tsanev@sap.com - SAP SE - https://github.com/SAP - - - martein - Martin Nikolov - martin.nikolov@sap.com - SAP SE - https://github.com/SAP - - - HariG - Hariharan Gandhi - hariharan.gandhi@sap.com - SAP SE - https://github.com/SAP - - - haraldfuchs - Harald Fuchs - harald.fuchs@sap.com - SAP SE - https://github.com/SAP - - - nicklas-dohrn - Nicklas Dohrn - nicklas.dohrn@sap.com - SAP SE - https://github.com/SAP - - - bgoerzig - Benny Goerzig - benny.goerzig@sap.com - SAP SE - https://github.com/SAP - - - juergen-walter - Juergen Walter - juergen.walter@sap.com - SAP SE - https://github.com/SAP - - - D070188 - Xiaozhong Zhang - xiaozhong.zhang@sap.com - SAP SE - https://github.com/SAP - - - p-singh - Prabhjot Singh - prabhjot.singh02@sap.com - SAP SE - https://github.com/SAP - + + deyanzhelyazkov + Deyan Zhelyazkov + deyan.zhelyazkov@sap.com + SAP SE + https://github.com/SAP + + + angelts + Angel Tsanev + angel.tsanev@sap.com + SAP SE + https://github.com/SAP + + + martein + Martin Nikolov + martin.nikolov@sap.com + SAP SE + https://github.com/SAP + + + HariG + Hariharan Gandhi + hariharan.gandhi@sap.com + SAP SE + https://github.com/SAP + + + haraldfuchs + Harald Fuchs + harald.fuchs@sap.com + SAP SE + https://github.com/SAP + + + nicklas-dohrn + Nicklas Dohrn + nicklas.dohrn@sap.com + SAP SE + https://github.com/SAP + + + bgoerzig + Benny Goerzig + benny.goerzig@sap.com + SAP SE + https://github.com/SAP + + + juergen-walter + Juergen Walter + juergen.walter@sap.com + SAP SE + https://github.com/SAP + + + D070188 + Xiaozhong Zhang + xiaozhong.zhang@sap.com + SAP SE + https://github.com/SAP + + + p-singh + Prabhjot Singh + prabhjot.singh02@sap.com + SAP SE + https://github.com/SAP + @@ -125,7 +127,7 @@ UTF-8 - 2.14.2 + 2.18.2 1.7.36 1.2.13 2.20.0 @@ -142,9 +144,10 @@ 3.5.0 1.6 8 - 3.19.4 - 2.13.4.1 + 4.4.0 + 2.18.2 4.5.14 + 3.4.5 @@ -186,18 +189,18 @@ ${mockito.version} test - - org.openjdk.jmh - jmh-core - ${jmh.version} - test - - - org.openjdk.jmh - jmh-generator-annprocess - ${jmh.version} - test - + + org.openjdk.jmh + jmh-core + ${jmh.version} + test + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + test + diff --git a/sample-spring-boot/manifest-otel-javaagent.yml b/sample-spring-boot/manifest-otel-javaagent.yml index 6af20a37..53c59d29 100644 --- a/sample-spring-boot/manifest-otel-javaagent.yml +++ b/sample-spring-boot/manifest-otel-javaagent.yml @@ -5,9 +5,9 @@ applications: # - name: otel-sample-app instances: 1 - path: target/sample-app-spring-boot-3.8.1.jar + path: target/sample-app-spring-boot-3.8.4.jar buildpack: sap_java_buildpack - memory: 256M + memory: 1024M random-route: true env: # Set LOG_*: true to activate logging of respective field @@ -16,7 +16,7 @@ applications: LOG_REFERER: false JBP_CONFIG_COMPONENTS: "jres: ['com.sap.xs.java.buildpack.jre.SAPMachineJRE']" JBP_CONFIG_SAP_MACHINE_JRE: '{ use_offline_repository: false, version: 17.+ }' - JBP_CONFIG_JAVA_OPTS: '[from_environment: false, java_opts: ''-javaagent:BOOT-INF/lib/opentelemetry-javaagent-1.31.0.jar -Dotel.javaagent.extensions=BOOT-INF/lib/cf-java-logging-support-opentelemetry-agent-extension-3.8.1.jar -Dotel.logs.exporter=cloud-logging -Dotel.metrics.exporter=cloud-logging,dynatrace -Dotel.traces.exporter=cloud-logging -Dotel.instrumentation.logback-appender.experimental.capture-mdc-attributes=* -Dotel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes=true -Dotel.instrumentation.logback-appender.experimental.capture-code-attributes=true -Dotel.instrumentation.logback-appender.experimental-log-attributes=true -Dotel.experimental.resource.disabled-keys=process.command_line,process.command_args,process.executable.path -Dotel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name=ingest_token'']' + JBP_CONFIG_JAVA_OPTS: '[from_environment: false, java_opts: ''-javaagent:BOOT-INF/lib/opentelemetry-javaagent-2.15.0.jar -Dotel.javaagent.extensions=BOOT-INF/lib/cf-java-logging-support-opentelemetry-agent-extension-3.8.4.jar -Dotel.logs.exporter=cloud-logging -Dotel.metrics.exporter=cloud-logging,dynatrace -Dotel.traces.exporter=cloud-logging -Dotel.instrumentation.logback-appender.experimental.capture-mdc-attributes=* -Dotel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes=true -Dotel.instrumentation.logback-appender.experimental.capture-code-attributes=true -Dotel.instrumentation.logback-appender.experimental-log-attributes=true -Dotel.experimental.resource.disabled-keys=process.command_line,process.command_args,process.executable.path -Dotel.javaagent.extension.sap.cf.binding.dynatrace.metrics.token-name=ingest_token'']' services: - cloud-logging - dynatrace-service diff --git a/sample-spring-boot/manifest.yml b/sample-spring-boot/manifest.yml index 251107bb..bb6d611d 100644 --- a/sample-spring-boot/manifest.yml +++ b/sample-spring-boot/manifest.yml @@ -5,7 +5,7 @@ applications: # - name: logging-sample-app instances: 1 - path: target/sample-app-spring-boot-3.8.2.jar + path: target/sample-app-spring-boot-3.8.5.jar env: # Set LOG_*: true to activate logging of respective field LOG_SENSITIVE_CONNECTION_DATA: false diff --git a/sample-spring-boot/pom.xml b/sample-spring-boot/pom.xml index 0fcefc94..26db955d 100644 --- a/sample-spring-boot/pom.xml +++ b/sample-spring-boot/pom.xml @@ -1,257 +1,257 @@ - 4.0.0 - sample-app-spring-boot - sample-app-spring-boot - Logging Sample App for Spring Boot - - com.sap.hcp.cf.logging - cf-java-logging-support-parent - 3.8.2 - ../pom.xml - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + sample-app-spring-boot + sample-app-spring-boot + Logging Sample App for Spring Boot + + com.sap.hcp.cf.logging + cf-java-logging-support-parent + 3.8.5 + ../pom.xml + - - 11 - UTF-8 - 11 - 11 - 2.7.9 - 1.5 - 0bzhBRNUXBR5 - - 0bzhBRNUXBR5 - - jwt-token - CN=cf-java-logging-support, OU=None, O=SAP, - L=Unknown, ST=Unknown, C=Unknown - - + + 11 + UTF-8 + 11 + 11 + 2.7.9 + 1.5 + 0bzhBRNUXBR5 + + 0bzhBRNUXBR5 + + jwt-token + CN=cf-java-logging-support, OU=None, O=SAP, + L=Unknown, ST=Unknown, C=Unknown + + - - - - org.springframework.boot - spring-boot-dependencies - ${spring.boot.version} - pom - import - - - + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-logging - - - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-logging - - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.security - spring-security-test - test - + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-test + test + - - - com.sap.hcp.cf.logging - cf-java-logging-support-servlet - ${project.version} - + + + com.sap.hcp.cf.logging + cf-java-logging-support-servlet + ${project.version} + - - - com.sap.hcp.cf.logging - cf-java-logging-support-opentelemetry-agent-extension - ${project.version} - - - io.opentelemetry.javaagent - opentelemetry-javaagent - 1.31.0 - + + + com.sap.hcp.cf.logging + cf-java-logging-support-opentelemetry-agent-extension + ${project.version} + + + io.opentelemetry.javaagent + opentelemetry-javaagent + 2.15.0 + - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + - - - - src/main/resources - true - - - ${basedir}/target/generated-resources/keystore - - + + + + src/main/resources + true + + + ${basedir}/target/generated-resources/keystore + + - - - org.springframework.boot - spring-boot-maven-plugin - ${spring.boot.version} - - - - repackage - - - - - - + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + - - - initialze-keystore - - - ${basedir}/target/generated-resources/keystore/token_keystore.jks - - - - - - org.codehaus.mojo - keytool-maven-plugin - ${keytool.plugin.version} - - - create-key-pair - - generateKeyPair - - generate-resources - - - - ${basedir}/target/generated-resources/keystore/token_keystore.jks - ${keystore.token.store_password} - ${keystore.token.key_password} - ${keystore.token.key_alias} - ${keystore.token.dname} - SHA256withRSA - - 100 - RSA - 2048 - - - - - - - logback - - - !log4j2 - - - - - org.springframework.boot - spring-boot-starter-logging - - - com.sap.hcp.cf.logging - cf-java-logging-support-logback - ${project.version} - - - - - log4j2 - - - log4j2 - - - - - org.apache.logging.log4j - log4j-api - ${log4j2.version} - - - org.apache.logging.log4j - log4j-core - ${log4j2.version} - - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j2.version} - - - org.apache.logging.log4j - log4j-jul - ${log4j2.version} - - - org.springframework.boot - spring-boot-starter-log4j2 - - - org.apache.logging.log4j - log4j-api - - - org.apache.logging.log4j - log4j-core - - - org.apache.logging.log4j - log4j-slf4j-impl - - - org.apache.logging.log4j - log4j-jul - - - - - com.sap.hcp.cf.logging - cf-java-logging-support-log4j2 - ${project.version} - - - - + + + initialze-keystore + + + ${basedir}/target/generated-resources/keystore/token_keystore.jks + + + + + + org.codehaus.mojo + keytool-maven-plugin + ${keytool.plugin.version} + + + create-key-pair + + generateKeyPair + + generate-resources + + + + ${basedir}/target/generated-resources/keystore/token_keystore.jks + ${keystore.token.store_password} + ${keystore.token.key_password} + ${keystore.token.key_alias} + ${keystore.token.dname} + SHA256withRSA + + 100 + RSA + 2048 + + + + + + + logback + + + !log4j2 + + + + + org.springframework.boot + spring-boot-starter-logging + + + com.sap.hcp.cf.logging + cf-java-logging-support-logback + ${project.version} + + + + + log4j2 + + + log4j2 + + + + + org.apache.logging.log4j + log4j-api + ${log4j2.version} + + + org.apache.logging.log4j + log4j-core + ${log4j2.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j2.version} + + + org.apache.logging.log4j + log4j-jul + ${log4j2.version} + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-jul + + + + + com.sap.hcp.cf.logging + cf-java-logging-support-log4j2 + ${project.version} + + + + diff --git a/sample/manifest.yml b/sample/manifest.yml index ca716019..9186c136 100644 --- a/sample/manifest.yml +++ b/sample/manifest.yml @@ -7,7 +7,7 @@ applications: instances: 1 buildpack: sap_java_buildpack memory: 256M - path: target/logging-sample-app-3.8.2.war + path: target/logging-sample-app-3.8.5.war env: RANDOM_SLEEP: true # Set LOG_*: true to activate logging of respective field diff --git a/sample/pom.xml b/sample/pom.xml index 9b6c2f4a..5dbb2fa1 100644 --- a/sample/pom.xml +++ b/sample/pom.xml @@ -6,7 +6,7 @@ com.sap.hcp.cf.logging cf-java-logging-support-parent - 3.8.2 + 3.8.5 ../pom.xml