├── .gitattributes ├── testobserve ├── tests │ ├── src │ │ └── test │ │ │ ├── resources │ │ │ ├── listener_tests │ │ │ │ ├── .gitignore │ │ │ │ ├── Ballerina.toml │ │ │ │ ├── listener_endpoint_test.bal │ │ │ │ └── Dependencies.toml │ │ │ ├── logging.properties │ │ │ └── testng.xml │ │ │ └── java │ │ │ └── io │ │ │ └── ballerina │ │ │ └── stdlib │ │ │ └── testobserve │ │ │ └── ListenerEndpointTest.java │ └── build.gradle ├── ballerina │ ├── Ballerina.toml │ ├── Dependencies.toml │ ├── utils.bal │ ├── listener_endpoint_caller.bal │ ├── listener_endpoint.bal │ └── build.gradle └── native │ ├── src │ └── main │ │ ├── java │ │ ├── module-info.java │ │ └── org │ │ │ └── ballerina │ │ │ └── testobserve │ │ │ ├── StandardExtensionPackageRepositoryProvider.java │ │ │ ├── listenerendpoint │ │ │ ├── Constants.java │ │ │ ├── Utils.java │ │ │ ├── Endpoint.java │ │ │ └── Resource.java │ │ │ └── NativeUtils.java │ │ └── resources │ │ └── META-INF │ │ └── native-image │ │ └── org.ballerina │ │ └── testobserve │ │ ├── native-image.properties │ │ └── reflect-config.json │ ├── spotbugs-exclude.xml │ └── build.gradle ├── integration-tests ├── src │ └── test │ │ ├── resources │ │ ├── observability │ │ │ ├── metrics_tests │ │ │ │ ├── .gitignore │ │ │ │ ├── Config.toml │ │ │ │ ├── Ballerina.toml │ │ │ │ ├── commons.bal │ │ │ │ ├── 03_custom_metric_tags.bal │ │ │ │ ├── Dependencies.toml │ │ │ │ ├── 01_main_function.bal │ │ │ │ └── 02_resource_function.bal │ │ │ └── tracing_tests │ │ │ │ ├── .gitignore │ │ │ │ ├── Config.toml │ │ │ │ ├── Ballerina.toml │ │ │ │ ├── 07_span_context.bal │ │ │ │ ├── modules │ │ │ │ └── utils │ │ │ │ │ ├── observable_adder.bal │ │ │ │ │ └── mock_client_endpoint.bal │ │ │ │ ├── Dependencies.toml │ │ │ │ ├── commons.bal │ │ │ │ ├── 04_observability_annotation.bal │ │ │ │ ├── 01_main_function.bal │ │ │ │ ├── 03_remote_call.bal │ │ │ │ ├── 06_custom_trace_spans.bal │ │ │ │ ├── 02_resource_function.bal │ │ │ │ └── 05_concurrency.bal │ │ └── testng.xml │ │ └── java │ │ └── io │ │ └── ballerina │ │ └── stdlib │ │ └── observability │ │ ├── BaseTest.java │ │ ├── tracing │ │ ├── SpanContextTestCase.java │ │ ├── TracingBaseTestCase.java │ │ └── ObservableAnnotationTestCase.java │ │ └── ObservabilityBaseTest.java └── build.gradle ├── codecov.yml ├── ballerina ├── Module.md ├── Ballerina.toml ├── Package.md ├── init.bal └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .github ├── pull_request_template.md ├── CODEOWNERS └── workflows │ ├── trivy-scan.yml │ ├── build-timestamped-master.yml │ ├── pull-request.yml │ └── publish-release.yml ├── native ├── spotbugs-exclude.xml ├── src │ └── main │ │ └── java │ │ ├── module-info.java │ │ └── io │ │ └── ballerina │ │ └── stdlib │ │ └── observe │ │ ├── internal │ │ ├── ObservabilityInternalSystemPackageRepositoryProvider.java │ │ └── NativeFunctions.java │ │ └── observers │ │ ├── BallerinaTracingObserver.java │ │ ├── BallerinaMetricsLogsObserver.java │ │ └── BallerinaMetricsObserver.java └── build.gradle ├── .gitignore ├── issue_template.md ├── gradle.properties ├── settings.gradle ├── pull_request_template.md ├── gradlew.bat ├── README.md ├── gradlew └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | # Ensure all Java files use LF. 2 | *.java eol=lf 3 | -------------------------------------------------------------------------------- /testobserve/tests/src/test/resources/listener_tests/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/metrics_tests/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | fixes: 2 | - "ballerinai/observe/*/::ballerina/" 3 | 4 | ignore: 5 | - "**/tests" 6 | -------------------------------------------------------------------------------- /ballerina/Module.md: -------------------------------------------------------------------------------- 1 | ## Module Overview 2 | 3 | This module provides the Ballerina observability initialization related implementation. 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ballerina-platform/module-ballerinai-observe/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /testobserve/tests/src/test/resources/listener_tests/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "ballerina_test" 3 | name = "listener_tests" 4 | version = "0.0.1" 5 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | ## Examples 4 | 5 | ## Checklist 6 | - [ ] Linked to an issue 7 | - [ ] Updated the changelog 8 | - [ ] Added tests -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/metrics_tests/Config.toml: -------------------------------------------------------------------------------- 1 | [ballerina.observe] 2 | metricsEnabled=true # Flag to enable Metrics 3 | metricsReporter="mock" 4 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/Config.toml: -------------------------------------------------------------------------------- 1 | [ballerina.observe] 2 | tracingEnabled=true # Flag to enable Tracing 3 | tracingProvider="mock" 4 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/metrics_tests/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "intg_tests" 3 | name = "metrics_tests" 4 | version = "0.0.1" 5 | 6 | [build-options] 7 | observabilityIncluded=true 8 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "intg_tests" 3 | name = "tracing_tests" 4 | version = "0.0.1" 5 | 6 | [build-options] 7 | observabilityIncluded=true 8 | -------------------------------------------------------------------------------- /testobserve/ballerina/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "ballerina" 3 | name = "testobserve" 4 | version = "0.0.0" 5 | 6 | [[platform.java21.dependency]] 7 | path = "../native/build/libs/testobserve-native-1.7.0-SNAPSHOT-all.jar" 8 | -------------------------------------------------------------------------------- /ballerina/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | org = "ballerinai" 3 | name = "observe" 4 | version = "1.7.0" 5 | distribution = "2201.13.0" 6 | 7 | [platform.java21] 8 | graalvmCompatible = true 9 | 10 | [[platform.java21.dependency]] 11 | path = "../native/build/libs/observe-internal-native-1.7.0.jar" 12 | -------------------------------------------------------------------------------- /native/spotbugs-exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # See: https://help.github.com/articles/about-codeowners/ 5 | 6 | # These owners will be the default owners for everything in the repo. 7 | * @chathurace @NipunaMadhushan @keizer619 8 | -------------------------------------------------------------------------------- /testobserve/native/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module io.ballerina.observability.test.utils { 2 | requires com.google.gson; 3 | requires io.ballerina.runtime; 4 | requires io.netty.transport; 5 | requires io.netty.buffer; 6 | requires io.netty.codec.http; 7 | requires io.ballerina.lang; 8 | } 9 | -------------------------------------------------------------------------------- /native/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module io.ballerina.observability { 2 | requires io.ballerina.lang; 3 | requires io.ballerina.runtime; 4 | requires io.opentelemetry.api; 5 | requires io.opentelemetry.context; 6 | 7 | uses io.ballerina.runtime.observability.metrics.spi.MetricProvider; 8 | uses io.ballerina.runtime.observability.tracer.spi.TracerProvider; 9 | } 10 | -------------------------------------------------------------------------------- /ballerina/Package.md: -------------------------------------------------------------------------------- 1 | ## Package Overview 2 | 3 | The `observe` library is one of the internal library packages, it provides internal support for observing code. 4 | 5 | ## Report Issues 6 | 7 | To report bugs, request new features, start new discussions, view project boards, etc., go to the Ballerina standard library parent repository 8 | 9 | ## Useful Links 10 | 11 | * Chat live with us via our Discord server. 12 | * Post all technical questions on Stack Overflow with the #ballerina tag. 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | !gradle/wrapper/gradle-wrapper.jar 15 | *.jar 16 | *.war 17 | *.nar 18 | *.ear 19 | *.zip 20 | *.tar.gz 21 | *.rar 22 | 23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 24 | hs_err_pid* 25 | 26 | # Java related ignores 27 | build/ 28 | target/ 29 | .gradle/ 30 | 31 | # MacOS 32 | *.DS_Store 33 | 34 | # IDE-related files 35 | .idea 36 | *.iml 37 | *.ipr 38 | *.iws 39 | .code 40 | .project 41 | .settings 42 | .vscode 43 | 44 | # Ballerina related ignores 45 | Ballerina.lock 46 | velocity.log* 47 | Dependencies.toml 48 | -------------------------------------------------------------------------------- /testobserve/ballerina/Dependencies.toml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE. DO NOT MODIFY. 2 | 3 | # This file is auto-generated by Ballerina for managing dependency versions. 4 | # It should not be modified by hand. 5 | 6 | [ballerina] 7 | dependencies-toml-version = "2" 8 | distribution-version = "2201.13.0" 9 | 10 | [[package]] 11 | org = "ballerina" 12 | name = "jballerina.java" 13 | version = "0.0.0" 14 | modules = [ 15 | {org = "ballerina", packageName = "jballerina.java", moduleName = "jballerina.java"} 16 | ] 17 | 18 | [[package]] 19 | org = "ballerina" 20 | name = "testobserve" 21 | version = "0.0.0" 22 | dependencies = [ 23 | {org = "ballerina", name = "jballerina.java"} 24 | ] 25 | modules = [ 26 | {org = "ballerina", packageName = "testobserve", moduleName = "testobserve"} 27 | ] 28 | 29 | -------------------------------------------------------------------------------- /testobserve/native/src/main/resources/META-INF/native-image/org.ballerina/testobserve/native-image.properties: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. 2 | # 3 | # WSO2 LLC. licenses this file to you under the Apache License, 4 | # Version 2.0 (the "License"); you may not use this file except 5 | # in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, 11 | # software distributed under the License is distributed on an 12 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | # KIND, either express or implied. See the License for the 14 | # specific language governing permissions and limitations 15 | # under the License. 16 | 17 | Args = --initialize-at-build-time=org.slf4j 18 | -------------------------------------------------------------------------------- /issue_template.md: -------------------------------------------------------------------------------- 1 | **Description:** 2 | 3 | 4 | **Suggested Labels:** 5 | 6 | 7 | **Suggested Assignees:** 8 | 9 | 10 | **Affected Product Version:** 11 | 12 | **OS, DB, other environment details and versions:** 13 | 14 | **Steps to reproduce:** 15 | 16 | 17 | **Related Issues:** 18 | -------------------------------------------------------------------------------- /.github/workflows/trivy-scan.yml: -------------------------------------------------------------------------------- 1 | name: Trivy 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 0 * * *' 7 | 8 | jobs: 9 | ubuntu-build: 10 | name: Build on Ubuntu 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up JDK 21 15 | uses: actions/setup-java@v3 16 | with: 17 | distribution: 'adopt' 18 | java-version: 21 19 | - name: Build with Gradle 20 | env: 21 | packageUser: ${{ github.actor }} 22 | packagePAT: ${{ secrets.GITHUB_TOKEN }} 23 | run: ./gradlew build -x check -x test --stacktrace --console=plain 24 | # Disabled since we are not using any external libraries 25 | # - name: Run Trivy vulnerability scanner 26 | # uses: aquasecurity/trivy-action@master 27 | # with: 28 | # scan-type: 'fs' 29 | # scan-ref: '/github/workspace/ballerina/lib' 30 | # format: 'table' 31 | # exit-code: '1' 32 | -------------------------------------------------------------------------------- /testobserve/ballerina/utils.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | # Sleep the current strand. 20 | # 21 | # + millis - The number of milliseconds to sleep for 22 | public isolated function sleep(int millis) = @java:Method { 23 | name: "sleep", 24 | 'class: "org.ballerina.testobserve.NativeUtils" 25 | } external; 26 | -------------------------------------------------------------------------------- /testobserve/tests/src/test/resources/logging.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | # 4 | # WSO2 Inc. licenses this file to you under the Apache License, 5 | # Version 2.0 (the "License"); you may not use this file except 6 | # in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | # 18 | 19 | .level=INFO 20 | handlers=java.util.logging.ConsoleHandler 21 | 22 | java.util.logging.ConsoleHandler.level=INFO 23 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 24 | java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS,%1$tL] %4$s {%3$s} - %5$s %n 25 | 26 | org.apache.directory.level=OFF 27 | -------------------------------------------------------------------------------- /testobserve/tests/src/test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /testobserve/ballerina/listener_endpoint_caller.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | public client class Caller { 20 | remote isolated function respond(string message) returns error? { 21 | return externRespond(self, message); 22 | } 23 | } 24 | 25 | isolated function externRespond(Caller callerObj, string message) returns error? = @java:Method { 26 | 'class: "org.ballerina.testobserve.listenerendpoint.Endpoint", 27 | name: "respond" 28 | } external; 29 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/metrics_tests/commons.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/observe.mockextension; 18 | import ballerina/testobserve; 19 | 20 | service /metricsRegistry on new testobserve:Listener(10090) { 21 | resource function post getMetrics(testobserve:Caller caller) { 22 | mockextension:Metrics metrics = mockextension:getMetrics(); 23 | json metricsJson = checkpanic metrics.cloneWithType(json); 24 | checkpanic caller->respond(metricsJson.toJsonString()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | org.gradle.caching=true 16 | group=io.ballerina 17 | version=1.7.1-SNAPSHOT 18 | 19 | ballerinaLangVersion=2201.13.0 20 | spotbugsPluginVersion=6.0.18 21 | shadowJarPluginVersion=8.1.1 22 | downloadPluginVersion=5.4.0 23 | releasePluginVersion=2.8.0 24 | ballerinaTomlParserVersion=1.2.2 25 | ballerinaGradlePluginVersion=2.3.0 26 | checkstylePluginVersion=10.12.0 27 | openTelemetryVersion=1.7.0 28 | nettyCodecVersion=4.1.118.Final 29 | gsonVersion=2.10.1 30 | 31 | observeVersion=1.7.0 32 | 33 | # Test Dependency Versions 34 | testngVersion=7.6.1 35 | slf4jVersion=1.7.26 36 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/07_span_context.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | import ballerina/observe; 19 | 20 | @display { label: "testServiceSeven" } 21 | service /testServiceSeven on new testobserve:Listener(19097) { 22 | resource function get resourceOne(testobserve:Caller caller) { 23 | 24 | map spanContext = observe:getSpanContext(); 25 | json spanContextJson = checkpanic spanContext.cloneWithType(json); 26 | checkpanic caller->respond(spanContextJson.toJsonString()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/modules/utils/observable_adder.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/observe; 18 | 19 | public type ObservableAdderClass object { 20 | @observe:Observable 21 | public function getSum() returns int; 22 | }; 23 | 24 | public class ObservableAdder { 25 | private int firstNumber; 26 | private int secondNumber; 27 | 28 | public function init(int a, int b) { 29 | self.firstNumber = a; 30 | self.secondNumber = b; 31 | } 32 | 33 | public function getSum() returns int { 34 | return self.firstNumber + self.secondNumber; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/metrics_tests/03_custom_metric_tags.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | import ballerina/observe; 19 | 20 | service /testServiceTwo on new testobserve:Listener(10092) { 21 | # Resource function for test custom tags 22 | resource function post testAddTagToMetrics(testobserve:Caller caller) { 23 | // Add a custom tag to span, this should not be included in system metrics 24 | error? res = observe:addTagToSpan("tracing", "Tracing Value"); 25 | // Add a custom tag to system metrics 26 | _ = checkpanic observe:addTagToMetrics("metric", "Metric Value"); 27 | checkpanic caller->respond("Invocation Successful"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /testobserve/native/src/main/java/org/ballerina/testobserve/StandardExtensionPackageRepositoryProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * WSO2 Inc. licenses this file to you under the Apache License, 4 | * Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, 11 | * software distributed under the License is distributed on an 12 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | * KIND, either express or implied. See the License for the 14 | * specific language governing permissions and limitations 15 | * under the License. 16 | * 17 | */ 18 | 19 | package org.ballerina.testobserve; 20 | 21 | import org.ballerinalang.annotation.JavaSPIService; 22 | import org.ballerinalang.spi.SystemPackageRepositoryProvider; 23 | import org.wso2.ballerinalang.compiler.packaging.repo.JarRepo; 24 | import org.wso2.ballerinalang.compiler.packaging.repo.Repo; 25 | 26 | /** 27 | * This is the class which registers the functions defined with the ballerina. 28 | */ 29 | @JavaSPIService("org.ballerinalang.spi.SystemPackageRepositoryProvider") 30 | public class StandardExtensionPackageRepositoryProvider implements SystemPackageRepositoryProvider { 31 | 32 | @Override 33 | public Repo loadRepository() { 34 | return new JarRepo(SystemPackageRepositoryProvider.getClassUri(this)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/build-timestamped-master.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | if: github.repository_owner == 'ballerina-platform' 13 | steps: 14 | - name: Checkout Repository 15 | uses: actions/checkout@v3 16 | - name: Set up JDK 21 17 | uses: actions/setup-java@v3 18 | with: 19 | distribution: 'adopt' 20 | java-version: 21 21 | - name: Change to Timestamped Version 22 | run: | 23 | startTime=$(TZ="Asia/Kolkata" date +'%Y%m%d-%H%M00') 24 | latestCommit=$(git log -n 1 --pretty=format:"%h") 25 | VERSION=$((grep -w 'version' | cut -d= -f2) < gradle.properties | rev | cut --complement -d- -f1 | rev) 26 | updatedVersion=$VERSION-$startTime-$latestCommit 27 | echo $updatedVersion 28 | sed -i "s/version=\(.*\)/version=$updatedVersion/g" gradle.properties 29 | - name: Grant execute permission for gradlew 30 | run: chmod +x gradlew 31 | - name: Build with Gradle 32 | env: 33 | packageUser: ${{ secrets.BALLERINA_BOT_USERNAME }} 34 | packagePAT: ${{ secrets.BALLERINA_BOT_TOKEN }} 35 | publishUser: ${{ secrets.BALLERINA_BOT_USERNAME }} 36 | publishPAT: ${{ secrets.BALLERINA_BOT_TOKEN }} 37 | run: | 38 | ./gradlew publish --scan --no-daemon --stacktrace --console=plain 39 | - name: Generate CodeCov Report 40 | uses: codecov/codecov-action@v1 41 | -------------------------------------------------------------------------------- /testobserve/tests/src/test/resources/listener_tests/listener_endpoint_test.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | import ballerina/lang.'int; 19 | 20 | service /testServiceOne on new testobserve:Listener(29091) { 21 | resource function post resourceOne(testobserve:Caller caller) { 22 | error? ret = caller->respond("Hello from Resource One"); 23 | } 24 | 25 | resource function post resourceTwo(testobserve:Caller caller, string body) returns error? { 26 | int numberCount = check 'int:fromString(body); 27 | var sum = 0; 28 | foreach var i in 1 ... numberCount { 29 | sum = sum + i; 30 | } 31 | error? ret = caller->respond("Sum of numbers: " + sum.toString()); 32 | } 33 | 34 | resource function post resourceThree(testobserve:Caller caller) returns error? { 35 | error e = error("Test Error"); 36 | panic e; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /native/src/main/java/io/ballerina/stdlib/observe/internal/ObservabilityInternalSystemPackageRepositoryProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | package io.ballerina.stdlib.observe.internal; 20 | 21 | import org.ballerinalang.annotation.JavaSPIService; 22 | import org.ballerinalang.spi.SystemPackageRepositoryProvider; 23 | import org.wso2.ballerinalang.compiler.packaging.repo.JarRepo; 24 | import org.wso2.ballerinalang.compiler.packaging.repo.Repo; 25 | 26 | /** 27 | * This represents the standard Ballerina built-in system package repository provider. 28 | * 29 | * @since 2.0.0 30 | */ 31 | @JavaSPIService("org.ballerinalang.spi.SystemPackageRepositoryProvider") 32 | public class ObservabilityInternalSystemPackageRepositoryProvider implements SystemPackageRepositoryProvider { 33 | 34 | @Override 35 | public Repo loadRepository() { 36 | return new JarRepo(SystemPackageRepositoryProvider.getClassUri(this)); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /testobserve/native/src/main/java/org/ballerina/testobserve/listenerendpoint/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package org.ballerina.testobserve.listenerendpoint; 20 | 21 | import io.ballerina.runtime.api.Module; 22 | 23 | import static io.ballerina.runtime.api.constants.RuntimeConstants.BALLERINA_BUILTIN_PKG_PREFIX; 24 | 25 | /** 26 | * Mock Listener related constants. 27 | */ 28 | public class Constants { 29 | public static final Module TEST_OBSERVE_PACKAGE = new Module(BALLERINA_BUILTIN_PKG_PREFIX, "testobserve", 30 | "0"); 31 | public static final String MOCK_LISTENER_ERROR_TYPE = "MockListenerError"; 32 | public static final String CALLER_TYPE_NAME = "Caller"; 33 | 34 | // Keys used for storing native data inside Ballerina Objects 35 | public static final String WEB_SERVER_NATIVE_DATA_KEY = "_mock_listener_web_server"; 36 | public static final String NETTY_CONTEXT_NATIVE_DATA_KEY = "_mock_listener_netty_context"; 37 | } 38 | -------------------------------------------------------------------------------- /testobserve/tests/src/test/resources/listener_tests/Dependencies.toml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE. DO NOT MODIFY. 2 | 3 | # This file is auto-generated by Ballerina for managing dependency versions. 4 | # It should not be modified by hand. 5 | 6 | [ballerina] 7 | dependencies-toml-version = "2" 8 | distribution-version = "2201.8.0-20230726-145300-b2bdf796" 9 | 10 | [[package]] 11 | org = "ballerina" 12 | name = "jballerina.java" 13 | version = "0.0.0" 14 | 15 | [[package]] 16 | org = "ballerina" 17 | name = "lang.__internal" 18 | version = "0.0.0" 19 | dependencies = [ 20 | {org = "ballerina", name = "jballerina.java"}, 21 | {org = "ballerina", name = "lang.object"} 22 | ] 23 | 24 | [[package]] 25 | org = "ballerina" 26 | name = "lang.int" 27 | version = "0.0.0" 28 | dependencies = [ 29 | {org = "ballerina", name = "jballerina.java"}, 30 | {org = "ballerina", name = "lang.__internal"}, 31 | {org = "ballerina", name = "lang.object"} 32 | ] 33 | modules = [ 34 | {org = "ballerina", packageName = "lang.int", moduleName = "lang.int"} 35 | ] 36 | 37 | [[package]] 38 | org = "ballerina" 39 | name = "lang.object" 40 | version = "0.0.0" 41 | 42 | [[package]] 43 | org = "ballerina" 44 | name = "testobserve" 45 | version = "0.0.0" 46 | dependencies = [ 47 | {org = "ballerina", name = "jballerina.java"} 48 | ] 49 | modules = [ 50 | {org = "ballerina", packageName = "testobserve", moduleName = "testobserve"} 51 | ] 52 | 53 | [[package]] 54 | org = "ballerina_test" 55 | name = "listener_tests" 56 | version = "0.0.1" 57 | dependencies = [ 58 | {org = "ballerina", name = "lang.int"}, 59 | {org = "ballerina", name = "testobserve"} 60 | ] 61 | modules = [ 62 | {org = "ballerina_test", packageName = "listener_tests", moduleName = "listener_tests"} 63 | ] 64 | 65 | -------------------------------------------------------------------------------- /integration-tests/src/test/java/io/ballerina/stdlib/observability/BaseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package io.ballerina.stdlib.observability; 19 | 20 | import org.ballerinalang.test.context.BalServer; 21 | import org.ballerinalang.test.context.BallerinaTestException; 22 | import org.testng.annotations.AfterSuite; 23 | import org.testng.annotations.BeforeSuite; 24 | 25 | /** 26 | * Parent test class for all integration test cases under test-integration module. This will provide basic 27 | * functionality for integration test. This will initialize a single ballerina instance which will be used 28 | * by all the test cases throughout. 29 | */ 30 | public class BaseTest { 31 | 32 | public static BalServer balServer; 33 | 34 | @BeforeSuite(alwaysRun = true) 35 | public void initialize() throws BallerinaTestException { 36 | balServer = new BalServer(); 37 | } 38 | 39 | @AfterSuite(alwaysRun = true) 40 | public void destroy() throws BallerinaTestException { 41 | balServer.cleanup(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/metrics_tests/Dependencies.toml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE. DO NOT MODIFY. 2 | 3 | # This file is auto-generated by Ballerina for managing dependency versions. 4 | # It should not be modified by hand. 5 | 6 | [ballerina] 7 | dependencies-toml-version = "2" 8 | distribution-version = "2201.7.0-20230619-175900-bb4e4544" 9 | 10 | [[package]] 11 | org = "ballerina" 12 | name = "jballerina.java" 13 | version = "0.0.0" 14 | 15 | [[package]] 16 | org = "ballerina" 17 | name = "observe" 18 | version = "1.1.0" 19 | dependencies = [ 20 | {org = "ballerina", name = "jballerina.java"} 21 | ] 22 | modules = [ 23 | {org = "ballerina", packageName = "observe", moduleName = "observe"}, 24 | {org = "ballerina", packageName = "observe", moduleName = "observe.mockextension"} 25 | ] 26 | 27 | [[package]] 28 | org = "ballerina" 29 | name = "testobserve" 30 | version = "0.0.0" 31 | dependencies = [ 32 | {org = "ballerina", name = "jballerina.java"} 33 | ] 34 | modules = [ 35 | {org = "ballerina", packageName = "testobserve", moduleName = "testobserve"} 36 | ] 37 | 38 | [[package]] 39 | org = "ballerinai" 40 | name = "observe" 41 | version = "0.0.0" 42 | dependencies = [ 43 | {org = "ballerina", name = "jballerina.java"}, 44 | {org = "ballerina", name = "observe"} 45 | ] 46 | modules = [ 47 | {org = "ballerinai", packageName = "observe", moduleName = "observe"} 48 | ] 49 | 50 | [[package]] 51 | org = "intg_tests" 52 | name = "metrics_tests" 53 | version = "0.0.1" 54 | dependencies = [ 55 | {org = "ballerina", name = "observe"}, 56 | {org = "ballerina", name = "testobserve"}, 57 | {org = "ballerinai", name = "observe"} 58 | ] 59 | modules = [ 60 | {org = "intg_tests", packageName = "metrics_tests", moduleName = "metrics_tests"} 61 | ] 62 | 63 | -------------------------------------------------------------------------------- /testobserve/native/src/main/java/org/ballerina/testobserve/NativeUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package org.ballerina.testobserve; 20 | 21 | import io.ballerina.runtime.api.Environment; 22 | import io.ballerina.runtime.api.creators.ErrorCreator; 23 | import io.ballerina.runtime.api.utils.StringUtils; 24 | 25 | import java.util.concurrent.Executors; 26 | import java.util.concurrent.ScheduledExecutorService; 27 | 28 | /** 29 | * Native functions for the utilities in testobserve module. 30 | */ 31 | public class NativeUtils { 32 | private static final int CORE_THREAD_POOL_SIZE = 1; 33 | 34 | private static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(CORE_THREAD_POOL_SIZE); 35 | 36 | public static void sleep(Environment env, long delayMillis) { 37 | env.yieldAndRun(() -> { 38 | try { 39 | Thread.sleep(delayMillis); 40 | return null; 41 | } catch (InterruptedException e) { 42 | throw ErrorCreator.createError(StringUtils.fromString("error occurred during sleep"), e); 43 | } 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /testobserve/native/spotbugs-exclude.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/Dependencies.toml: -------------------------------------------------------------------------------- 1 | # AUTO-GENERATED FILE. DO NOT MODIFY. 2 | 3 | # This file is auto-generated by Ballerina for managing dependency versions. 4 | # It should not be modified by hand. 5 | 6 | [ballerina] 7 | dependencies-toml-version = "2" 8 | distribution-version = "2201.7.0-20230619-175900-bb4e4544" 9 | 10 | [[package]] 11 | org = "ballerina" 12 | name = "jballerina.java" 13 | version = "0.0.0" 14 | 15 | [[package]] 16 | org = "ballerina" 17 | name = "observe" 18 | version = "1.1.0" 19 | dependencies = [ 20 | {org = "ballerina", name = "jballerina.java"} 21 | ] 22 | modules = [ 23 | {org = "ballerina", packageName = "observe", moduleName = "observe"}, 24 | {org = "ballerina", packageName = "observe", moduleName = "observe.mockextension"} 25 | ] 26 | 27 | [[package]] 28 | org = "ballerina" 29 | name = "testobserve" 30 | version = "0.0.0" 31 | dependencies = [ 32 | {org = "ballerina", name = "jballerina.java"} 33 | ] 34 | modules = [ 35 | {org = "ballerina", packageName = "testobserve", moduleName = "testobserve"} 36 | ] 37 | 38 | [[package]] 39 | org = "ballerinai" 40 | name = "observe" 41 | version = "0.0.0" 42 | dependencies = [ 43 | {org = "ballerina", name = "jballerina.java"}, 44 | {org = "ballerina", name = "observe"} 45 | ] 46 | modules = [ 47 | {org = "ballerinai", packageName = "observe", moduleName = "observe"} 48 | ] 49 | 50 | [[package]] 51 | org = "intg_tests" 52 | name = "tracing_tests" 53 | version = "0.0.1" 54 | dependencies = [ 55 | {org = "ballerina", name = "observe"}, 56 | {org = "ballerina", name = "testobserve"}, 57 | {org = "ballerinai", name = "observe"} 58 | ] 59 | modules = [ 60 | {org = "intg_tests", packageName = "tracing_tests", moduleName = "tracing_tests"}, 61 | {org = "intg_tests", packageName = "tracing_tests", moduleName = "tracing_tests.utils"} 62 | ] 63 | 64 | -------------------------------------------------------------------------------- /native/src/main/java/io/ballerina/stdlib/observe/observers/BallerinaTracingObserver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package io.ballerina.stdlib.observe.observers; 19 | 20 | import io.ballerina.runtime.observability.BallerinaObserver; 21 | import io.ballerina.runtime.observability.ObserverContext; 22 | import io.ballerina.runtime.observability.tracer.TracingUtils; 23 | 24 | /** 25 | * Observe the runtime and start/stop tracing. 26 | */ 27 | public class BallerinaTracingObserver implements BallerinaObserver { 28 | 29 | @Override 30 | public void startServerObservation(ObserverContext observerContext) { 31 | TracingUtils.startObservation(observerContext, false); 32 | } 33 | 34 | @Override 35 | public void startClientObservation(ObserverContext observerContext) { 36 | TracingUtils.startObservation(observerContext, true); 37 | } 38 | 39 | @Override 40 | public void stopServerObservation(ObserverContext observerContext) { 41 | TracingUtils.stopObservation(observerContext); 42 | } 43 | 44 | @Override 45 | public void stopClientObservation(ObserverContext observerContext) { 46 | TracingUtils.stopObservation(observerContext); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/commons.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/observe; 18 | import ballerina/observe.mockextension; 19 | import ballerina/testobserve; 20 | import intg_tests/tracing_tests.utils as utils; 21 | 22 | utils:MockClient testClient = new(); 23 | 24 | @display { label: "mockTracer" } 25 | service /mockTracer on new testobserve:Listener(19090) { 26 | resource function post getMockTraces(testobserve:Caller caller, string serviceName) { 27 | mockextension:Span[] spans = mockextension:getFinishedSpans(serviceName); 28 | json spansJson = checkpanic spans.cloneWithType(json); 29 | checkpanic caller->respond(spansJson.toJsonString()); 30 | } 31 | } 32 | 33 | function panicAfterCalculatingSum(int a) { 34 | var sum = 0; 35 | foreach var i in 1 ... a { 36 | sum = sum + i; 37 | } 38 | error e = error("Test Error. Sum: " + sum.toString()); 39 | panic e; 40 | } 41 | 42 | function calculateSum(int a, int b) returns int { 43 | var sum = a + b; 44 | return a + b; 45 | } 46 | 47 | @observe:Observable 48 | function calculateSumWithObservability(int a, int b) returns int { 49 | var sum = a + b; 50 | return a + b; 51 | } 52 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Ballerina Observe Internal module build 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | ubuntu-build: 7 | name: Build on Ubuntu 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout Repository 11 | uses: actions/checkout@v3 12 | - name: Set up JDK 21 13 | uses: actions/setup-java@v3 14 | with: 15 | distribution: 'adopt' 16 | java-version: 21 17 | - name: Build with Gradle 18 | env: 19 | packageUser: ${{ github.actor }} 20 | packagePAT: ${{ secrets.GITHUB_TOKEN }} 21 | run: | 22 | ./gradlew build test --stacktrace --console=plain 23 | - name: Generate Codecov Report 24 | uses: codecov/codecov-action@v2 25 | 26 | windows-build: 27 | name: Build on Windows 28 | runs-on: windows-latest 29 | steps: 30 | - name: Checkout Repository 31 | uses: actions/checkout@v3 32 | - name: Set up JDK 21 33 | uses: actions/setup-java@v3 34 | with: 35 | distribution: 'adopt' 36 | java-version: 21 37 | - name: Build with Gradle 38 | env: 39 | packageUser: ${{ github.actor }} 40 | packagePAT: ${{ secrets.GITHUB_TOKEN }} 41 | JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8 42 | run: | 43 | ./gradlew.bat build -x test --stacktrace --console=plain 44 | ./gradlew.bat test --stacktrace --console=plain 45 | 46 | ubuntu-build-without-native-tests: 47 | name: Build on Ubuntu without native tests 48 | runs-on: ubuntu-latest 49 | steps: 50 | - name: Checkout Repository 51 | uses: actions/checkout@v3 52 | - name: Set up JDK 21 53 | uses: actions/setup-java@v3 54 | with: 55 | distribution: 'adopt' 56 | java-version: 21 57 | - name: Build with Gradle 58 | env: 59 | packageUser: ${{ github.actor }} 60 | packagePAT: ${{ secrets.GITHUB_TOKEN }} 61 | run: ./gradlew build -x test --stacktrace --console=plain 62 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/04_observability_annotation.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | import intg_tests/tracing_tests.utils as utils; 19 | 20 | @display { label: "testSvcFour" } 21 | service /testServiceFour on new testobserve:Listener(19094) { 22 | # Resource function for testing function call with observable annotation 23 | resource function post resourceOne(testobserve:Caller caller) { 24 | var sum = calculateSumWithObservability(10, 51); 25 | if (sum != 61) { // Check for validating if normal execution is intact from instrumentation 26 | error err = error("failed to find the sum of 10 and 51. expected: 61 received: " + sum.toString()); 27 | panic err; 28 | } 29 | checkpanic caller->respond("Invocation Successful"); 30 | } 31 | 32 | # Resource function for testing attached function call with observable annotation 33 | resource function post resourceTwo(testobserve:Caller caller) { 34 | utils:ObservableAdderClass adder = new utils:ObservableAdder(20, 34); 35 | var sum = adder.getSum(); 36 | if (sum != 54) { // Check for validating if normal execution is intact from instrumentation 37 | error err = error("failed to find the sum of 20 and 34. expected: 54 received: " + sum.toString()); 38 | panic err; 39 | } 40 | checkpanic caller->respond("Invocation Successful"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/metrics_tests/01_main_function.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/observe; 18 | 19 | public function main() returns error? { 20 | int a = 6; 21 | int b = 13; 22 | 23 | ObservableAdderClass adder = new ObservableAdder(a, b); 24 | var sumFromAdder = adder.getSum(); 25 | 26 | var sumFromFunc = callCalculateSum(a, b); 27 | sumFromFunc = callCalculateSum(a, b); 28 | sumFromFunc = callCalculateSum(a, b); 29 | 30 | if (sumFromAdder != sumFromFunc) { // Sanity check for sum 31 | error e = error("Sum from Adder Object (" + sumFromAdder.toString() + ") and sum from Function (" 32 | + sumFromFunc.toString() + ")"); 33 | return e; 34 | } 35 | } 36 | 37 | function callCalculateSum(int a, int b) returns int { 38 | return calculateSumWithObservability(a, b); 39 | } 40 | 41 | @observe:Observable 42 | function calculateSumWithObservability(int a, int b) returns int { 43 | var sum = a + b; 44 | return a + b; 45 | } 46 | 47 | type ObservableAdderClass object { 48 | @observe:Observable 49 | function getSum() returns int; 50 | }; 51 | 52 | class ObservableAdder { 53 | private int firstNumber; 54 | private int secondNumber; 55 | 56 | function init(int a, int b) { 57 | self.firstNumber = a; 58 | self.secondNumber = b; 59 | } 60 | 61 | function getSum() returns int { 62 | return self.firstNumber + self.secondNumber; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ballerina/init.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | import ballerina/observe; 19 | 20 | function init() { 21 | if (observe:isMetricsEnabled()) { 22 | var err = externEnableMetrics(observe:getMetricsProvider()); 23 | if (err is error) { 24 | externPrintError("failed to enable metrics"); 25 | } 26 | } 27 | 28 | if (observe:isTracingEnabled()) { 29 | var err = externEnableTracing(observe:getTracingProvider()); 30 | if (err is error) { 31 | externPrintError("failed to enable tracing"); 32 | } 33 | } 34 | 35 | if (observe:isMetricsLogsEnabled()) { 36 | var err = externEnableMetricsLogging(); 37 | if (err is error) { 38 | externPrintError("failed to enable tracing"); 39 | } 40 | } 41 | } 42 | 43 | function externEnableMetrics(string provider) returns error? = @java:Method { 44 | 'class: "io.ballerina.stdlib.observe.internal.NativeFunctions", 45 | name: "enableMetrics" 46 | } external; 47 | 48 | function externEnableTracing(string provider) returns error? = @java:Method { 49 | 'class: "io.ballerina.stdlib.observe.internal.NativeFunctions", 50 | name: "enableTracing" 51 | } external; 52 | 53 | function externEnableMetricsLogging() returns error? = @java:Method { 54 | 'class: "io.ballerina.stdlib.observe.internal.NativeFunctions", 55 | name: "enableMetricsLogging" 56 | } external; 57 | 58 | function externPrintError(string message) = @java:Method { 59 | 'class: "io.ballerina.stdlib.observe.internal.NativeFunctions", 60 | name: "printError" 61 | } external; 62 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/01_main_function.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | 19 | public function main() returns error? { 20 | testClient->callAnotherRemoteFunction(); 21 | 22 | var a = 63; 23 | var b = 81; 24 | var sum = testClient->calculateSum(a, b); 25 | var expectedSum = a + b; 26 | if (sum != expectedSum) { // Check for validating if normal execution is intact from instrumentation 27 | error err = error("failed to find the sum of " + a.toString() + " and " + b.toString() 28 | + ". expected: " + expectedSum.toString() + " received: " + sum.toString()); 29 | panic err; 30 | } 31 | 32 | var ret1 = trap testClient->callWithPanic(); 33 | if (!(ret1 is error)) { 34 | error e = error("Expected error not found"); 35 | panic e; 36 | } 37 | 38 | var ret2 = testClient->callWithErrorReturn(); 39 | if (!(ret2 is error)) { 40 | error e = error("Expected error not found"); 41 | panic e; 42 | } 43 | 44 | service object {} testServiceInMain = service object { 45 | resource function post resourceOne(testobserve:Caller caller, string body) { 46 | int numberCount = checkpanic 'int:fromString(body); 47 | var total = 0; 48 | foreach var i in 1 ... numberCount { 49 | total = total + i; 50 | } 51 | checkpanic caller->respond("Sum of numbers: " + total.toString()); 52 | } 53 | }; 54 | var testObserveListener = new testobserve:Listener(19091); 55 | check testObserveListener.attach(testServiceInMain, "/testServiceOne"); 56 | check testObserveListener.start(); 57 | } 58 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user manual at https://docs.gradle.org/7.2/userguide/multi_project_builds.html 8 | */ 9 | 10 | pluginManagement { 11 | plugins { 12 | id "com.github.spotbugs" version "${spotbugsPluginVersion}" 13 | id "com.github.johnrengelman.shadow" version "${shadowJarPluginVersion}" 14 | id "de.undercouch.download" version "${downloadPluginVersion}" 15 | id "net.researchgate.release" version "${releasePluginVersion}" 16 | id "io.ballerina.plugin" version "${ballerinaGradlePluginVersion}" 17 | } 18 | 19 | repositories { 20 | gradlePluginPortal() 21 | maven { 22 | url = 'https://maven.pkg.github.com/ballerina-platform/*' 23 | credentials { 24 | username System.getenv("packageUser") 25 | password System.getenv("packagePAT") 26 | } 27 | } 28 | } 29 | } 30 | 31 | plugins { 32 | id "com.gradle.enterprise" version "3.13.2" 33 | } 34 | 35 | rootProject.name = 'module-ballerinai-observe' 36 | 37 | include ':checkstyle' 38 | include ':testobserve-native' 39 | include ':testobserve-ballerina' 40 | include ':testobserve-ballerina-tests' 41 | include ':observe-internal-native' 42 | include ':observe-ballerina' 43 | include ':observe-ballerina-integration-tests' 44 | 45 | project(':checkstyle').projectDir = file("build-config${File.separator}checkstyle") 46 | 47 | // Integration Test Utils 48 | project(':testobserve-native').projectDir = file("testobserve${File.separator}native") 49 | project(':testobserve-ballerina').projectDir = file("testobserve${File.separator}ballerina") 50 | project(':testobserve-ballerina-tests').projectDir = file("testobserve${File.separator}tests") 51 | 52 | // Observe Internal 53 | project(':observe-internal-native').projectDir = file('native') 54 | project(':observe-ballerina').projectDir = file('ballerina') 55 | 56 | // Integration tests 57 | project(':observe-ballerina-integration-tests').projectDir = file('integration-tests') 58 | 59 | gradleEnterprise { 60 | buildScan { 61 | termsOfServiceUrl = 'https://gradle.com/terms-of-service' 62 | termsOfServiceAgree = 'yes' 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /integration-tests/src/test/java/io/ballerina/stdlib/observability/tracing/SpanContextTestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.observability.tracing; 20 | 21 | import com.google.gson.Gson; 22 | import com.google.gson.reflect.TypeToken; 23 | import io.ballerina.stdlib.observe.mockextension.BMockSpan; 24 | import org.ballerinalang.test.util.HttpClientRequest; 25 | import org.testng.Assert; 26 | import org.testng.annotations.Test; 27 | 28 | import java.lang.reflect.Type; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.Optional; 32 | 33 | /** 34 | * Test cases span context. 35 | * 36 | * @since 2.0.0 37 | */ 38 | @Test(groups = "tracing-test") 39 | public class SpanContextTestCase extends TracingBaseTestCase { 40 | private static final String FILE_NAME = "07_span_context.bal"; 41 | 42 | @Test 43 | public void test() throws Exception { 44 | String requestUrl = "http://localhost:19097/testServiceSeven/resourceOne/"; 45 | String data = HttpClientRequest.doGet(requestUrl).getData(); 46 | Type type = new TypeToken>() { 47 | }.getType(); 48 | Map spanContext = new Gson().fromJson(data, type); 49 | 50 | List spanList = getFinishedSpans("testServiceSeven", 51 | "intg_tests/tracing_tests:0.0.1", "/resourceOne"); 52 | Optional span1 = spanList.stream() 53 | .filter(bMockSpan -> bMockSpan.getTags().get("src.position").equals(FILE_NAME + ":22:5")) 54 | .findFirst(); 55 | Assert.assertTrue(span1.isPresent()); 56 | Assert.assertEquals(span1.get().getTraceId(), spanContext.get("traceId")); 57 | Assert.assertEquals(span1.get().getSpanId(), spanContext.get("spanId")); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/modules/utils/mock_client_endpoint.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | public client class MockClient { 18 | remote function callWithNoReturn(int a, int b, int expectedSum) { 19 | var sum = a + b; 20 | if (sum != expectedSum) { // Check for validating if normal execution is intact from instrumentation 21 | error e = error("Sum is " + sum.toString() + ". Expected :" + expectedSum.toString()); 22 | panic e; 23 | } 24 | } 25 | 26 | remote function calculateSum(int a, int b) returns int { 27 | var sum = a + b; 28 | return a + b; 29 | } 30 | 31 | remote function callAnotherRemoteFunction() { 32 | self->callWithNoReturn(13, 9, 22); 33 | } 34 | 35 | remote function callWithReturn(int a, int b) returns int|error { 36 | return a + b; 37 | } 38 | 39 | remote function callWithErrorReturn() returns error? { 40 | var sum = 3 + 7; 41 | var expectedSum = 10; 42 | if (sum != expectedSum) { // Check for validating if normal execution is intact from instrumentation 43 | error e = error("Sum is " + sum.toString() + ". Expected " + expectedSum.toString()); 44 | panic e; 45 | } else { 46 | error e = error("Test Error"); 47 | return e; 48 | } 49 | } 50 | 51 | remote function callWithPanic() { 52 | var sum = 5 + 2; 53 | var expectedSum = 7; 54 | if (sum != expectedSum) { // Check for validating if normal execution is intact from instrumentation 55 | error e = error("Sum is " + sum.toString() + ". Expected " + expectedSum.toString()); 56 | panic e; 57 | } 58 | error e = error("Test Error"); 59 | panic e; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/03_remote_call.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | 19 | service /test/serviceThree on new testobserve:Listener(19093) { 20 | # Resource function for testing remote call which calls another remote call 21 | resource function post resourceOne(testobserve:Caller caller) { 22 | testClient->callAnotherRemoteFunction(); 23 | checkpanic caller->respond("Invocation Successful"); 24 | } 25 | 26 | # Resource function for testing check on error return 27 | resource function post resourceTwo(testobserve:Caller caller) returns error? { 28 | check testClient->callWithErrorReturn(); 29 | checkpanic caller->respond("Invocation Successful"); 30 | } 31 | 32 | # Resource function for testing checkpanic on error return 33 | resource function post resourceThree(testobserve:Caller caller) returns error? { 34 | checkpanic testClient->callWithErrorReturn(); 35 | checkpanic caller->respond("Invocation Successful"); 36 | } 37 | 38 | # Resource function for testing ignored error return 39 | resource function post resourceFour(testobserve:Caller caller) returns error? { 40 | error? ret = testClient->callWithErrorReturn(); 41 | checkpanic caller->respond("Invocation Successful"); 42 | } 43 | 44 | # Resource function for testing whether trapping a panic from a function is handled properly. 45 | resource function post resourceFive(testobserve:Caller caller) returns error? { 46 | var sum = trap testClient->callWithPanic(); 47 | if (sum is error) { 48 | checkpanic caller->respond("Successfully trapped panic: " + sum.message()); 49 | } else { 50 | checkpanic caller->respond("Sum of numbers: " + sum.toString()); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /testobserve/native/src/main/java/org/ballerina/testobserve/listenerendpoint/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package org.ballerina.testobserve.listenerendpoint; 20 | 21 | import io.ballerina.runtime.api.creators.ErrorCreator; 22 | import io.ballerina.runtime.api.utils.StringUtils; 23 | import io.ballerina.runtime.api.values.BError; 24 | 25 | import java.io.PrintStream; 26 | 27 | import static org.ballerina.testobserve.listenerendpoint.Constants.MOCK_LISTENER_ERROR_TYPE; 28 | import static org.ballerina.testobserve.listenerendpoint.Constants.TEST_OBSERVE_PACKAGE; 29 | 30 | /** 31 | * Mock listener endpoint related utilities. 32 | */ 33 | public class Utils { 34 | private static final PrintStream out = System.out; 35 | 36 | /** 37 | * Create a Ballerina error using a throwable. 38 | * 39 | * @param t Throwable from which the error should be created 40 | * @return The corresponding ballerina error object 41 | */ 42 | public static BError createError(Throwable t) { 43 | return ErrorCreator.createDistinctError(MOCK_LISTENER_ERROR_TYPE, TEST_OBSERVE_PACKAGE, 44 | StringUtils.fromString(t.getMessage())); 45 | } 46 | 47 | public static void logInfo(String format, Object ... args) { 48 | out.printf("[Mock Listener] - INFO - " + format + "\n", args); 49 | } 50 | 51 | public static void logError(String format, Object ... args) { 52 | out.printf("[Mock Listener] - ERROR - " + format + "\n", args); 53 | } 54 | 55 | public static String normalizeResourcePath(String resourcePath) { 56 | if (!resourcePath.startsWith("/")) { 57 | resourcePath = "/" + resourcePath; 58 | } 59 | if (resourcePath.endsWith("/")) { 60 | resourcePath = resourcePath.substring(0, resourcePath.length() - 1); 61 | } 62 | return resourcePath; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | > Describe the problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc. 3 | 4 | ## Goals 5 | > Describe the solutions that this feature/fix will introduce to resolve the problems described above 6 | 7 | ## Approach 8 | > Describe how you are implementing the solutions. Include an animated GIF or screenshot if the change affects the UI (email documentation@wso2.com to review all UI text). Include a link to a Markdown file or Google doc if the feature write-up is too long to paste here. 9 | 10 | ## User stories 11 | > Summary of user stories addressed by this change> 12 | 13 | ## Release note 14 | > Brief description of the new feature or bug fix as it will appear in the release notes 15 | 16 | ## Documentation 17 | > Link(s) to product documentation that addresses the changes of this PR. If no doc impact, enter “N/A” plus brief explanation of why there’s no doc impact 18 | 19 | ## Training 20 | > Link to the PR for changes to the training content in https://github.com/wso2/WSO2-Training, if applicable 21 | 22 | ## Certification 23 | > Type “Sent” when you have provided new/updated certification questions, plus four answers for each question (correct answer highlighted in bold), based on this change. Certification questions/answers should be sent to certification@wso2.com and NOT pasted in this PR. If there is no impact on certification exams, type “N/A” and explain why. 24 | 25 | ## Marketing 26 | > Link to drafts of marketing content that will describe and promote this feature, including product page changes, technical articles, blog posts, videos, etc., if applicable 27 | 28 | ## Automation tests 29 | - Unit tests 30 | > Code coverage information 31 | - Integration tests 32 | > Details about the test cases and coverage 33 | 34 | ## Security checks 35 | - Followed secure coding standards in http://wso2.com/technical-reports/wso2-secure-engineering-guidelines? yes/no 36 | - Ran FindSecurityBugs plugin and verified report? yes/no 37 | - Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes/no 38 | 39 | ## Samples 40 | > Provide high-level details about the samples related to this feature 41 | 42 | ## Related PRs 43 | > List any other related PRs 44 | 45 | ## Migrations (if applicable) 46 | > Describe migration steps and platforms on which migration has been tested 47 | 48 | ## Test environment 49 | > List all JDK versions, operating systems, databases, and browser/versions on which this feature/fix was tested 50 | 51 | ## Learning 52 | > Describe the research phase and any blog posts, patterns, libraries, or add-ons you used to solve the problem. -------------------------------------------------------------------------------- /testobserve/ballerina/listener_endpoint.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/jballerina.java; 18 | 19 | public class Listener { 20 | 21 | public isolated function init(int port) { 22 | externInitEndpoint(self, port); 23 | } 24 | 25 | public isolated function attach(service object {} s, string[]|string? name = ()) returns error? { 26 | string basePath; 27 | if (name is string[]) { 28 | basePath = "/".'join(...name); 29 | } else if (name is string) { 30 | basePath = name; 31 | } else { 32 | basePath = ""; 33 | } 34 | return externAttach(self, s, basePath); 35 | } 36 | 37 | public isolated function detach(service object {} s) returns error? { 38 | return externDetach(self, s); 39 | } 40 | 41 | public isolated function 'start() returns error? { 42 | externStart(self); 43 | } 44 | 45 | public isolated function gracefulStop() returns error? { 46 | return externShutdownGracefully(self); 47 | } 48 | 49 | public isolated function immediateStop() returns error? { 50 | return externShutdownNow(self); 51 | } 52 | } 53 | 54 | isolated function externInitEndpoint(Listener listenerObj, int port) = @java:Method { 55 | 'class: "org.ballerina.testobserve.listenerendpoint.Endpoint", 56 | name: "initEndpoint" 57 | } external; 58 | 59 | isolated function externAttach(Listener listenerObj, service object {} s, string basePath) returns error? = @java:Method { 60 | 'class: "org.ballerina.testobserve.listenerendpoint.Endpoint", 61 | name: "attachService" 62 | } external; 63 | 64 | isolated function externDetach(Listener listenerObj, service object {} s) returns error? = @java:Method { 65 | 'class: "org.ballerina.testobserve.listenerendpoint.Endpoint", 66 | name: "detachService" 67 | } external; 68 | 69 | isolated function externStart(Listener listenerObj) = @java:Method { 70 | 'class: "org.ballerina.testobserve.listenerendpoint.Endpoint", 71 | name: "start" 72 | } external; 73 | 74 | isolated function externShutdownGracefully(Listener listenerObj) returns error? = @java:Method { 75 | 'class: "org.ballerina.testobserve.listenerendpoint.Endpoint", 76 | name: "shutdownGracefully" 77 | } external; 78 | 79 | isolated function externShutdownNow(Listener listenerObj) returns error? = @java:Method { 80 | 'class: "org.ballerina.testobserve.listenerendpoint.Endpoint", 81 | name: "shutdownNow" 82 | } external; 83 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /testobserve/native/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | plugins { 19 | id 'java-library' 20 | id 'checkstyle' 21 | id 'com.github.spotbugs' 22 | id 'jacoco' 23 | id 'com.github.johnrengelman.shadow' 24 | } 25 | 26 | description = 'Ballerina - Observability integration test utils natives' 27 | def packageName = "observe" 28 | 29 | dependencies { 30 | 31 | shadow "com.google.code.gson:gson:${gsonVersion}" 32 | shadow "org.ballerinalang:ballerina-lang:${ballerinaLangVersion}" 33 | shadow "org.ballerinalang:annotations:${ballerinaLangVersion}" 34 | 35 | checkstyle project(':checkstyle') 36 | checkstyle "com.puppycrawl.tools:checkstyle:${checkstylePluginVersion}" 37 | 38 | implementation group: 'io.netty', name: 'netty-codec-http', version: "${nettyCodecVersion}" 39 | implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" 40 | implementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}" 41 | implementation group: 'org.ballerinalang', name: 'metrics-extensions', version: "${ballerinaLangVersion}" 42 | } 43 | 44 | checkstyle { 45 | toolVersion '10.12.1' 46 | configFile rootProject.file("build-config/checkstyle/build/checkstyle.xml") 47 | configProperties = ["suppressionFile": file("${rootDir}/build-config/checkstyle/build/suppressions.xml")] 48 | } 49 | 50 | def excludePattern = '**/module-info.java' 51 | tasks.withType(Checkstyle) { 52 | exclude excludePattern 53 | } 54 | 55 | checkstyleMain.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") 56 | 57 | spotbugsMain { 58 | def classLoader = plugins["com.github.spotbugs"].class.classLoader 59 | def SpotBugsConfidence = classLoader.findLoadedClass("com.github.spotbugs.snom.Confidence") 60 | def SpotBugsEffort = classLoader.findLoadedClass("com.github.spotbugs.snom.Effort") 61 | effort = SpotBugsEffort.MAX 62 | reportLevel = SpotBugsConfidence.LOW 63 | reportsDir = file("$project.buildDir/reports/spotbugs") 64 | reports { 65 | html.enabled true 66 | text.enabled = true 67 | } 68 | def excludeFile = file('spotbugs-exclude.xml') 69 | if (excludeFile.exists()) { 70 | excludeFilter = excludeFile 71 | } 72 | } 73 | 74 | spotbugsTest { 75 | enabled = true 76 | } 77 | 78 | jar { 79 | dependsOn shadowJar 80 | } 81 | 82 | shadowJar { 83 | configurations = [project.configurations.runtimeClasspath] 84 | dependencies { 85 | exclude('META-INF/*.SF') 86 | exclude('META-INF/*.DSA') 87 | exclude('META-INF/*.RSA') 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | publish-release: 8 | runs-on: ubuntu-latest 9 | if: github.repository_owner == 'ballerina-platform' 10 | 11 | steps: 12 | - name: Checkout Repository 13 | uses: actions/checkout@v3 14 | - name: Set up JDK 21 15 | uses: actions/setup-java@v3 16 | with: 17 | distribution: 'adopt' 18 | java-version: 21 19 | - name: Build with Gradle 20 | env: 21 | packageUser: ${{ github.actor }} 22 | packagePAT: ${{ secrets.GITHUB_TOKEN }} 23 | run: ./gradlew build -x check -x test --stacktrace --console=plain 24 | # Disabled since we are not using any external libraries 25 | # - name: Run Trivy vulnerability scanner 26 | # uses: aquasecurity/trivy-action@master 27 | # with: 28 | # scan-type: 'fs' 29 | # scan-ref: '/github/workspace/ballerina/lib' 30 | # format: 'table' 31 | # timeout: '6m0s' 32 | # exit-code: '1' 33 | - name: Set version env variable 34 | run: echo "VERSION=$((grep -w 'version' | cut -d= -f2) < gradle.properties | rev | cut --complement -d- -f1 | rev)" >> $GITHUB_ENV 35 | - name: Pre release depenency version update 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }} 38 | run: | 39 | echo "Version: ${VERSION}" 40 | git config user.name ${{ secrets.BALLERINA_BOT_USERNAME }} 41 | git config user.email ${{ secrets.BALLERINA_BOT_EMAIL }} 42 | git checkout -b release-${VERSION} 43 | sed -i 's/ballerinaLangVersion=\(.*\)-SNAPSHOT/ballerinaLangVersion=\1/g' gradle.properties 44 | sed -i 's/ballerinaLangVersion=\(.*\)-[0-9]\{8\}-[0-9]\{6\}-.*$/ballerinaLangVersion=\1/g' gradle.properties 45 | sed -i 's/stdlib\(.*\)=\(.*\)-SNAPSHOT/stdlib\1=\2/g' gradle.properties 46 | sed -i 's/stdlib\(.*\)=\(.*\)-[0-9]\{8\}-[0-9]\{6\}-.*$/stdlib\1=\2/g' gradle.properties 47 | sed -i 's/observe\(.*\)=\(.*\)-SNAPSHOT/observe\1=\2/g' gradle.properties 48 | sed -i 's/observe\(.*\)=\(.*\)-[0-9]\{8\}-[0-9]\{6\}-.*$/observe\1=\2/g' gradle.properties 49 | git add gradle.properties 50 | git commit -m "Move dependencies to stable version" || echo "No changes to commit" 51 | - name: Publish artifact 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }} 54 | packageUser: ${{ secrets.BALLERINA_BOT_USERNAME }} 55 | packagePAT: ${{ secrets.BALLERINA_BOT_TOKEN }} 56 | publishUser: ${{ secrets.BALLERINA_BOT_USERNAME }} 57 | publishPAT: ${{ secrets.BALLERINA_BOT_TOKEN }} 58 | run: | 59 | ./gradlew clean release -Prelease.useAutomaticVersion=true --stacktrace --console=plain 60 | ./gradlew -Pversion=${VERSION} publish -x test -PpublishToCentral=false --stacktrace --console=plain 61 | - name: GitHub Release and Release Sync PR 62 | env: 63 | GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }} 64 | run: | 65 | gh release create v$VERSION --title "module-ballerinai-observe-v$VERSION" 66 | gh pr create --title "[Automated] Sync master after $VERSION release" --body "Sync master after $VERSION release" 67 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/metrics_tests/02_resource_function.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | 19 | MockClient testClient = new(); 20 | 21 | service /testServiceOne on new testobserve:Listener(10091) { 22 | # Resource function for testing resource function and remote calls 23 | resource function post resourceOne(testobserve:Caller caller, string body) { 24 | var ret = trap testClient->callWithPanic(); 25 | if (!(ret is error)) { 26 | error e = error("Unexpected Error"); 27 | panic e; 28 | } 29 | checkpanic testClient->callWithErrorReturn(); 30 | checkpanic caller->respond("Executed Successfully"); 31 | } 32 | 33 | # Resource function for testing worker interactions 34 | resource function post resourceTwo(testobserve:Caller caller) { 35 | testWorkerInteractions(11); 36 | checkpanic caller->respond("Invocation Successful"); 37 | } 38 | } 39 | 40 | public client class MockClient { 41 | remote function callWithErrorReturn() returns error? { 42 | var sum = 3 + 7; 43 | var expectedSum = 10; 44 | if (sum != expectedSum) { // Check for validating if normal execution is intact from instrumentation 45 | error e = error("Sum is " + sum.toString() + ". Expected " + expectedSum.toString()); 46 | panic e; 47 | } else { 48 | error e = error("Test Error"); 49 | return e; 50 | } 51 | } 52 | 53 | remote function callWithPanic() { 54 | var sum = 5 + 2; 55 | var expectedSum = 7; 56 | if (sum != expectedSum) { // Check for validating if normal execution is intact from instrumentation 57 | error e = error("Sum is " + sum.toString() + ". Expected " + expectedSum.toString()); 58 | panic e; 59 | } 60 | error e = error("Test Error"); 61 | panic e; 62 | } 63 | } 64 | 65 | function testWorkerInteractions(int c) { 66 | worker w1 { 67 | int a = c + 3; 68 | testobserve:sleep(1000); // Sleep to make the workers not finish together 69 | a -> w2; 70 | } 71 | worker w2 { 72 | int b = <- w1; 73 | if (b != (c + 3)) { 74 | error err = error("worker interaction failed"); 75 | panic err; 76 | } 77 | } 78 | wait w2; 79 | wait w1; 80 | } 81 | 82 | service /this\-is\-service\-path/still\-service\-path on new testobserve:Listener(10093) { 83 | resource function post this\-is\-resource\-function/still\-resource\-function(testobserve:Caller caller) { 84 | checkpanic caller->respond("Invocation Successful"); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /integration-tests/src/test/java/io/ballerina/stdlib/observability/ObservabilityBaseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.observability; 20 | 21 | import org.ballerinalang.test.context.BServerInstance; 22 | import org.ballerinalang.test.context.BalServer; 23 | import org.ballerinalang.test.context.BallerinaTestException; 24 | import org.ballerinalang.test.context.Utils; 25 | 26 | import java.io.IOException; 27 | import java.net.InetAddress; 28 | import java.nio.file.Files; 29 | import java.nio.file.Path; 30 | import java.nio.file.Paths; 31 | import java.util.HashMap; 32 | import java.util.Map; 33 | import java.util.logging.Logger; 34 | 35 | import static io.ballerina.runtime.internal.configurable.providers.toml.TomlConstants.CONFIG_FILES_ENV_VARIABLE; 36 | 37 | /** 38 | * Base Test Case of all Observability related test cases. 39 | */ 40 | public class ObservabilityBaseTest extends BaseTest { 41 | 42 | private static BServerInstance servicesServerInstance; 43 | 44 | protected static final String SERVER_CONNECTOR_NAME = "testobserve_listener"; 45 | private static final Logger LOGGER = Logger.getLogger(ObservabilityBaseTest.class.getName()); 46 | private static BalServer balServer; 47 | 48 | protected void setupServer(String testProject, String packageName, int[] requiredPorts) throws Exception { 49 | balServer = new BalServer(); 50 | 51 | String sourcesDir = Paths.get("src", "test", "resources", "observability", testProject).toFile() 52 | .getAbsolutePath(); 53 | 54 | String configFile = Paths.get("src", "test", "resources", "observability", testProject, 55 | "Config.toml").toFile().getAbsolutePath(); 56 | Map env = new HashMap<>(); 57 | env.put(CONFIG_FILES_ENV_VARIABLE, configFile); 58 | 59 | // Don't use 9898 port here. It is used in metrics test cases. 60 | servicesServerInstance = new BServerInstance(balServer); 61 | servicesServerInstance.startServer(sourcesDir, packageName, null, null, env, requiredPorts); 62 | Utils.waitForPortsToOpen(requiredPorts, 1000 * 60, false, InetAddress.getByName("localhost")); 63 | } 64 | 65 | protected void cleanupServer() throws BallerinaTestException, IOException { 66 | Path ballerinaInternalLog = Paths.get(balServer.getServerHome(), "ballerina-internal.log"); 67 | if (Files.exists(ballerinaInternalLog)) { 68 | LOGGER.severe("=== Ballerina Internal Log Start ==="); 69 | Files.lines(ballerinaInternalLog).forEach(LOGGER::severe); 70 | LOGGER.severe("=== Ballerina Internal Log End ==="); 71 | } 72 | servicesServerInstance.removeAllLeechers(); 73 | servicesServerInstance.shutdownServer(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /native/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | plugins { 18 | id 'java' 19 | id 'checkstyle' 20 | id 'com.github.spotbugs' 21 | id 'jacoco' 22 | } 23 | 24 | description = 'Ballerina - Observability Natives' 25 | 26 | dependencies { 27 | checkstyle project(':checkstyle') 28 | checkstyle "com.puppycrawl.tools:checkstyle:${checkstylePluginVersion}" 29 | 30 | implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" 31 | implementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}" 32 | implementation group: 'org.ballerinalang', name: 'metrics-extensions', version: "${ballerinaLangVersion}" 33 | 34 | implementation group: 'io.opentelemetry', name: 'opentelemetry-api', version: "${openTelemetryVersion}" 35 | } 36 | 37 | checkstyle { 38 | toolVersion '7.8.2' 39 | configFile rootProject.file("build-config/checkstyle/build/checkstyle.xml") 40 | configProperties = ["suppressionFile" : file("${rootDir}/build-config/checkstyle/build/suppressions.xml")] 41 | } 42 | 43 | def excludePattern = '**/module-info.java' 44 | tasks.withType(Checkstyle) { 45 | exclude excludePattern 46 | } 47 | 48 | checkstyleMain.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") 49 | 50 | spotbugsMain { 51 | def classLoader = plugins["com.github.spotbugs"].class.classLoader 52 | def SpotBugsConfidence = classLoader.findLoadedClass("com.github.spotbugs.snom.Confidence") 53 | def SpotBugsEffort = classLoader.findLoadedClass("com.github.spotbugs.snom.Effort") 54 | effort = SpotBugsEffort.MAX 55 | reportLevel = SpotBugsConfidence.LOW 56 | reportsDir = file("$project.buildDir/reports/spotbugs") 57 | reports { 58 | html.enabled true 59 | text.enabled = true 60 | } 61 | def excludeFile = file('spotbugs-exclude.xml') 62 | if(excludeFile.exists()) { 63 | excludeFilter = excludeFile 64 | } 65 | } 66 | 67 | spotbugsTest { 68 | enabled = true 69 | } 70 | 71 | publishing { 72 | publications { 73 | mavenJava(MavenPublication) { 74 | groupId project.group 75 | artifactId "observe-native" 76 | version = project.version 77 | artifact jar 78 | } 79 | } 80 | 81 | repositories { 82 | maven { 83 | name = "GitHubPackages" 84 | url = uri("https://maven.pkg.github.com/ballerina-platform/module-ballerinai-observe") 85 | credentials { 86 | username = System.getenv("publishUser") 87 | password = System.getenv("publishPAT") 88 | } 89 | } 90 | } 91 | } 92 | 93 | compileJava { 94 | doFirst { 95 | options.compilerArgs = [ 96 | '--module-path', classpath.asPath, 97 | ] 98 | classpath = files() 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /testobserve/ballerina/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import org.apache.tools.ant.taskdefs.condition.Os 19 | apply plugin: 'io.ballerina.plugin' 20 | 21 | buildscript { 22 | repositories { 23 | mavenLocal() 24 | maven { 25 | url = 'https://maven.pkg.github.com/ballerina-platform/plugin-gradle' 26 | credentials { 27 | username System.getenv("packageUser") 28 | password System.getenv("packagePAT") 29 | } 30 | } 31 | } 32 | dependencies { 33 | classpath "io.ballerina:plugin-gradle:${project.ballerinaGradlePluginVersion}" 34 | } 35 | } 36 | 37 | description = 'Ballerina - Observability Internal' 38 | 39 | def packageName = "testobserve" 40 | def packageOrg = "ballerina" 41 | 42 | def tomlVersion = stripBallerinaExtensionVersion("${project.version}") 43 | def ballerinaTomlFilePlaceHolder = 44 | new File("${project.rootDir}/build-config/resources/testobserve/Ballerina.toml") 45 | def ballerinaTomlFile = new File("$project.projectDir/Ballerina.toml") 46 | 47 | def stripBallerinaExtensionVersion(String extVersion) { 48 | if (extVersion.matches(project.ext.timestampedVersionRegex)) { 49 | def splitVersion = extVersion.split('-'); 50 | if (splitVersion.length > 3) { 51 | def strippedValues = splitVersion[0..-4] 52 | return strippedValues.join('-') 53 | } else { 54 | return extVersion 55 | } 56 | } else { 57 | return extVersion.replace("${project.ext.snapshotVersion}", "") 58 | } 59 | } 60 | 61 | 62 | ballerina { 63 | packageOrganization = packageOrg 64 | module = packageName 65 | customTomlVersion ="0.0.0" 66 | langVersion = ballerinaLangVersion 67 | } 68 | 69 | task updateTomlFiles { 70 | doLast { 71 | def stdlibDependentTestngVersion = project.testngVersion 72 | def openTelemetryVersion = project.openTelemetryVersion 73 | def newBallerinaToml = ballerinaTomlFilePlaceHolder.text.replace("@project.version@", project.version) 74 | newBallerinaToml = newBallerinaToml.replace("@toml.version@", tomlVersion) 75 | newBallerinaToml = newBallerinaToml.replace("@testng.version@", stdlibDependentTestngVersion) 76 | newBallerinaToml = newBallerinaToml.replace("@opentelemetry.version@", openTelemetryVersion) 77 | ballerinaTomlFile.text = newBallerinaToml 78 | } 79 | } 80 | 81 | task commitTomlFiles { 82 | doLast { 83 | project.exec { 84 | ignoreExitValue true 85 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 86 | commandLine 'cmd', '/c', "git commit Ballerina.toml Dependencies.toml -m \"[Automated] Update the native jar versions\"" 87 | } else { 88 | commandLine 'sh', '-c', "git commit Ballerina.toml Dependencies.toml '[Automated] Update the native jar versions'" 89 | } 90 | } 91 | } 92 | } 93 | 94 | updateTomlFiles.dependsOn copyStdlibs 95 | 96 | build.dependsOn(":testobserve-native:build") 97 | test.dependsOn(":testobserve-native:build") 98 | -------------------------------------------------------------------------------- /ballerina/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import org.apache.tools.ant.taskdefs.condition.Os 19 | 20 | plugins { 21 | id 'io.ballerina.plugin' 22 | } 23 | 24 | description = 'Ballerina - Observability Internal' 25 | 26 | def packageName = "observe" 27 | def packageOrg = "ballerinai" 28 | 29 | def tomlVersion = stripBallerinaExtensionVersion("${project.version}") 30 | def ballerinaTomlFilePlaceHolder = new File("${project.rootDir}/build-config/resources/Ballerina.toml") 31 | def ballerinaTomlFile = new File("$project.projectDir/Ballerina.toml") 32 | 33 | def stripBallerinaExtensionVersion(String extVersion) { 34 | if (extVersion.matches(project.ext.timestampedVersionRegex)) { 35 | def splitVersion = extVersion.split('-'); 36 | if (splitVersion.length > 3) { 37 | def strippedValues = splitVersion[0..-4] 38 | return strippedValues.join('-') 39 | } else { 40 | return extVersion 41 | } 42 | } else { 43 | return extVersion.replace("${project.ext.snapshotVersion}", "") 44 | } 45 | } 46 | 47 | ballerina { 48 | packageOrganization = packageOrg 49 | module = packageName 50 | langVersion = ballerinaLangVersion 51 | customTomlVersion = "0.0.0" 52 | } 53 | 54 | configurations { 55 | externalJars 56 | } 57 | 58 | task updateTomlFiles { 59 | doLast { 60 | def newBallerinaToml = ballerinaTomlFilePlaceHolder.text.replace("@project.version@", project.version) 61 | newBallerinaToml = newBallerinaToml.replace("@toml.version@", tomlVersion) 62 | ballerinaTomlFile.text = newBallerinaToml 63 | } 64 | } 65 | 66 | task commitTomlFiles { 67 | doLast { 68 | project.exec { 69 | ignoreExitValue true 70 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 71 | commandLine 'cmd', '/c', "git commit Ballerina.toml Dependencies.toml -m \"[Automated] Update the native jar versions\"" 72 | } else { 73 | commandLine 'sh', '-c', "git commit Ballerina.toml Dependencies.toml -m '[Automated] Update the native jar versions'" 74 | } 75 | } 76 | } 77 | } 78 | 79 | publishing { 80 | publications { 81 | maven(MavenPublication) { 82 | artifact source: createArtifactZip, extension: 'zip' 83 | } 84 | } 85 | 86 | repositories { 87 | maven { 88 | name = "GitHubPackages" 89 | url = uri("https://maven.pkg.github.com/ballerina-platform/module-${packageOrg}-${packageName}") 90 | credentials { 91 | username = System.getenv("publishUser") 92 | password = System.getenv("publishPAT") 93 | } 94 | } 95 | } 96 | } 97 | 98 | updateTomlFiles.dependsOn copyStdlibs 99 | 100 | build.mustRunAfter ":observe-internal-native:build" 101 | test.dependsOn ":observe-internal-native:build" 102 | 103 | publishToMavenLocal.dependsOn build 104 | publish.dependsOn build 105 | build.dependsOn(':observe-ballerina:generatePomFileForMavenPublication') 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Ballerina Observe Internal Library 3 | =================== 4 | 5 | [![Build](https://github.com/ballerina-platform/module-ballerinai-observe/actions/workflows/build-timestamped-master.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerinai-observe/actions/workflows/build-timestamped-master.yml) 6 | [![Trivy](https://github.com/ballerina-platform/module-ballerinai-observe/actions/workflows/trivy-scan.yml/badge.svg)](https://github.com/ballerina-platform/module-ballerinai-observe/actions/workflows/trivy-scan.yml) 7 | [![GitHub Last Commit](https://img.shields.io/github/last-commit/ballerina-platform/module-ballerinai-observe.svg)](https://github.com/ballerina-platform/module-ballerinai-observe/commits/master) 8 | [![codecov](https://codecov.io/gh/ballerina-platform/module-ballerinai-observe/branch/master/graph/badge.svg)](https://codecov.io/gh/ballerina-platform/module-ballerinai-observe) 9 | 10 | This module contains internal configurations and initializations for Ballerina observability. Ballerina supports observability out of the box. You can use [module-ballerina-observe](https://github.com/ballerina-platform/module-ballerina-observe) in your Ballerina project and enable the observability features. 11 | ## Build from the source 12 | 13 | ### Set Up the prerequisites 14 | 15 | 1. Download and install Java SE Development Kit (JDK) version 21 (from one of the following locations). 16 | 17 | * [Oracle](https://www.oracle.com/java/technologies/downloads/) 18 | 19 | * [OpenJDK](https://adoptopenjdk.net/) 20 | 21 | > **Note:** Set the JAVA_HOME environment variable to the path name of the directory into which you installed JDK. 22 | 23 | 2. Export your GitHub Personal access token with read package permissions as follows. 24 | 25 | export packageUser= 26 | export packagePAT= 27 | 28 | ### Build the source 29 | 30 | Execute the commands below to build from source. 31 | 32 | 1. To build the library: 33 | ``` 34 | ./gradlew clean build 35 | ``` 36 | 37 | 2. To run the integration tests: 38 | ``` 39 | ./gradlew clean test 40 | ``` 41 | 42 | 3. To run a group of tests 43 | ``` 44 | ./gradlew clean test -Pgroups= 45 | ``` 46 | 47 | 4. To build the package without the tests: 48 | ``` 49 | ./gradlew clean build -x test 50 | ``` 51 | 52 | 5. To debug the tests: 53 | ``` 54 | ./gradlew clean test -Pdebug= 55 | ``` 56 | 57 | 6. To debug with Ballerina language: 58 | ``` 59 | ./gradlew clean build -PbalJavaDebug= 60 | ``` 61 | 62 | 7. Publish the generated artifacts to the local Ballerina central repository: 63 | ``` 64 | ./gradlew clean build -PpublishToLocalCentral=true 65 | ``` 66 | 67 | 8. Publish the generated artifacts to the Ballerina central repository: 68 | ``` 69 | ./gradlew clean build -PpublishToCentral=true 70 | ``` 71 | 72 | ## Contribute to Ballerina 73 | 74 | As an open source project, Ballerina welcomes contributions from the community. 75 | 76 | For more information, go to the [contribution guidelines](https://github.com/ballerina-platform/ballerina-lang/blob/master/CONTRIBUTING.md). 77 | 78 | ## Code of conduct 79 | 80 | All contributors are encouraged to read the [Ballerina Code of Conduct](https://ballerina.io/code-of-conduct). 81 | 82 | ## Useful links 83 | 84 | * For example demonstrations of the usage, go to [Ballerina By Examples](https://ballerina.io/learn/by-example/). 85 | * Chat live with us via our [Discord server](https://discord.gg/ballerinalang). 86 | * Post all technical questions on Stack Overflow with the [#ballerina](https://stackoverflow.com/questions/tagged/ballerina) tag. 87 | * View the [Ballerina performance test results](https://github.com/ballerina-platform/ballerina-lang/blob/master/performance/benchmarks/summary.md). 88 | 89 | 90 | -------------------------------------------------------------------------------- /testobserve/tests/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | apply plugin: 'java-library' 19 | 20 | configurations { 21 | jbalTools{ 22 | transitive false 23 | } 24 | testUtils { 25 | transitive false 26 | } 27 | } 28 | 29 | dependencies { 30 | testImplementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}" 31 | testUtils testImplementation(group: 'org.ballerinalang', name: 'ballerina-test-utils', 32 | version: "${ballerinaLangVersion}") 33 | testImplementation group: 'org.ballerinalang', name: 'ballerina-metrics-extension', 34 | version: "${ballerinaLangVersion}" 35 | testImplementation group: 'org.testng', name: 'testng', version: "${testngVersion}" 36 | } 37 | 38 | def ballerinaDist = "build/target/extracted-distributions" 39 | 40 | task unpackJballerinaTools(type: Copy) { 41 | configurations.jbalTools.resolvedConfiguration.resolvedArtifacts.each { artifact -> 42 | from zipTree(artifact.getFile()) 43 | into new File(ballerinaDist, "jballerina-tools-zip") 44 | } 45 | } 46 | 47 | task copyObserveStdlib { 48 | dependsOn(":testobserve-ballerina:build") 49 | dependsOn(unpackJballerinaTools) 50 | doLast { 51 | /* Copy Observe module */ 52 | copy { 53 | from "$project.rootDir/testobserve/ballerina/build/bala_unzipped/bala" 54 | into "${ballerinaDist}/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/repo/bala" 55 | } 56 | } 57 | } 58 | 59 | task createServerZip(type: Zip) { 60 | dependsOn copyObserveStdlib 61 | 62 | archiveFileName = "jballerina-tools-${ballerinaLangVersion}.zip" 63 | destinationDirectory = file("${buildDir}/repacked-distributions") 64 | from "${project.buildDir}/target/extracted-distributions/jballerina-tools-zip/" 65 | } 66 | 67 | test { 68 | dependsOn createServerZip 69 | useTestNG() { 70 | suites 'src/test/resources/testng.xml' 71 | } 72 | systemProperty 'enableJBallerinaTests', 'true' 73 | systemProperty 'basedir', "$buildDir" 74 | systemProperty 'libdir', "$buildDir" 75 | systemProperty 'server.zip', createServerZip.outputs.files.singleFile 76 | systemProperty 'jballerina.server.zip', createServerZip.outputs.files.singleFile 77 | systemProperty 'java.util.logging.config.file', "$buildDir/resources/test/logging.properties" 78 | systemProperty 'ballerina.agent.path', configurations.testUtils.asPath 79 | systemProperty 'ballerina.agent.port.start.value', 27000 80 | 81 | testLogging.showStandardStreams = true 82 | 83 | testLogging { 84 | events "PASSED", "FAILED", "SKIPPED" 85 | afterSuite { desc, result -> 86 | if (!desc.parent) { // will match the outermost suite 87 | def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" 88 | def startItem = '| ', endItem = ' |' 89 | def repeatLength = startItem.length() + output.length() + endItem.length() 90 | println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength)) 91 | } 92 | } 93 | } 94 | } 95 | 96 | test.dependsOn ':testobserve-ballerina:build' 97 | test.dependsOn ":testobserve-native:build" 98 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/06_custom_trace_spans.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | import ballerina/observe; 19 | 20 | @display { label: "testServiceSix" } 21 | service /testServiceSix on new testobserve:Listener(19096) { 22 | resource function post resourceOne(testobserve:Caller caller) { 23 | var customSpanOneId = checkpanic observe:startSpan("customSpanOne"); 24 | _ = checkpanic observe:addTagToSpan("resource", "resourceOne", customSpanOneId); 25 | _ = checkpanic observe:addTagToSpan("custom", "true", customSpanOneId); 26 | _ = checkpanic observe:addTagToSpan("index", "1", customSpanOneId); 27 | 28 | // Adding a metric tag and this should not be included in the tracing tags 29 | checkpanic observe:addTagToMetrics("metric", "Metric Value" ); 30 | 31 | var a = 12; 32 | var b = 27; 33 | var sum = calculateSumWithObservability(a, b); 34 | var expectedSum = a + b; 35 | if (sum != expectedSum) { 36 | error err = error("failed to find the sum of " + a.toString() + " and " + b.toString() 37 | + ". expected: " + expectedSum.toString() + " received: " + sum.toString()); 38 | panic err; 39 | } 40 | checkpanic observe:finishSpan(customSpanOneId); 41 | 42 | var customSpanTwoId = checkpanic observe:startSpan("customSpanTwo"); 43 | _ = checkpanic observe:addTagToSpan("resource", "resourceOne", customSpanTwoId); 44 | _ = checkpanic observe:addTagToSpan("custom", "true", customSpanTwoId); 45 | _ = checkpanic observe:addTagToSpan("index", "2", customSpanTwoId); 46 | checkpanic caller->respond("Hello! from resource one"); 47 | checkpanic observe:finishSpan(customSpanTwoId); 48 | 49 | var err = observe:addTagToSpan("disallowed_tag", "true", customSpanTwoId); 50 | if (!(err is error)) { 51 | error panicErr = error("tag added to finished span which should not have been added"); 52 | panic panicErr; 53 | } 54 | } 55 | 56 | resource function post resourceTwo(testobserve:Caller caller) { 57 | int customSpanThreeId = observe:startRootSpan("customSpanThree"); 58 | _ = checkpanic observe:addTagToSpan("resource", "resourceTwo", customSpanThreeId); 59 | _ = checkpanic observe:addTagToSpan("custom", "true", customSpanThreeId); 60 | _ = checkpanic observe:addTagToSpan("index", "3", customSpanThreeId); 61 | testFunctionForCustomUserTrace(customSpanThreeId); 62 | checkpanic observe:finishSpan(customSpanThreeId); 63 | 64 | checkpanic caller->respond("Hello! from resource two"); 65 | } 66 | } 67 | 68 | function testFunctionForCustomUserTrace(int customSpanThreeId) { 69 | int customSpanFourId = checkpanic observe:startSpan("customSpanFour", (), customSpanThreeId); 70 | _ = checkpanic observe:addTagToSpan("resource", "resourceTwo", customSpanFourId); 71 | _ = checkpanic observe:addTagToSpan("custom", "true", customSpanFourId); 72 | _ = checkpanic observe:addTagToSpan("index", "4", customSpanFourId); 73 | var a = 12; 74 | var b = 27; 75 | var sum = calculateSumWithObservability(a, b); 76 | var expectedSum = a + b; 77 | if (sum != expectedSum) { 78 | error err = error("failed to find the sum of " + a.toString() + " and " + b.toString() 79 | + ". expected: " + expectedSum.toString() + " received: " + sum.toString()); 80 | panic err; 81 | } 82 | checkpanic observe:finishSpan(customSpanFourId); 83 | } 84 | -------------------------------------------------------------------------------- /testobserve/native/src/main/java/org/ballerina/testobserve/listenerendpoint/Endpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package org.ballerina.testobserve.listenerendpoint; 20 | 21 | import io.ballerina.runtime.api.Environment; 22 | import io.ballerina.runtime.api.values.BError; 23 | import io.ballerina.runtime.api.values.BObject; 24 | import io.ballerina.runtime.api.values.BString; 25 | import io.netty.channel.ChannelHandlerContext; 26 | import io.netty.handler.codec.http.HttpResponseStatus; 27 | 28 | import static org.ballerina.testobserve.listenerendpoint.Constants.NETTY_CONTEXT_NATIVE_DATA_KEY; 29 | import static org.ballerina.testobserve.listenerendpoint.Constants.WEB_SERVER_NATIVE_DATA_KEY; 30 | 31 | /** 32 | * Mock listener endpoint related methods which will be invoked by Ballerina Java Inter-Op calls. 33 | */ 34 | public class Endpoint { 35 | 36 | public static void initEndpoint(Environment env, BObject listenerEndpoint, int port) { 37 | WebServer webServer = new WebServer(port, env); 38 | listenerEndpoint.addNativeData(WEB_SERVER_NATIVE_DATA_KEY, webServer); 39 | Utils.logInfo("Initialized Web Server with port " + port); 40 | } 41 | 42 | public static BError attachService(BObject listenerEndpoint, BObject serviceObject, BString basePath) { 43 | try { 44 | WebServer webServer = (WebServer) listenerEndpoint.getNativeData(WEB_SERVER_NATIVE_DATA_KEY); 45 | webServer.addService(serviceObject, basePath.getValue()); 46 | return null; 47 | } catch (Throwable t) { 48 | return Utils.createError(t); 49 | } 50 | } 51 | 52 | public static BError detachService(BObject listenerEndpoint, BObject serviceObject) { 53 | try { 54 | WebServer webServer = (WebServer) listenerEndpoint.getNativeData(WEB_SERVER_NATIVE_DATA_KEY); 55 | webServer.removeService(serviceObject); 56 | return null; 57 | } catch (Throwable t) { 58 | return Utils.createError(t); 59 | } 60 | } 61 | 62 | public static void start(BObject listenerEndpoint) { 63 | WebServer webServer = (WebServer) listenerEndpoint.getNativeData(WEB_SERVER_NATIVE_DATA_KEY); 64 | new Thread(() -> { 65 | try { 66 | webServer.start(); 67 | } catch (Throwable e) { 68 | Utils.logError("Error initializing server, error - " + e.getMessage()); 69 | } 70 | }).start(); 71 | } 72 | 73 | public static BError shutdownGracefully(BObject listenerEndpoint) { 74 | try { 75 | WebServer webServer = (WebServer) listenerEndpoint.getNativeData(WEB_SERVER_NATIVE_DATA_KEY); 76 | webServer.shutdownGracefully(); 77 | return null; 78 | } catch (Throwable e) { 79 | return Utils.createError(e); 80 | } 81 | } 82 | 83 | public static BError shutdownNow(BObject listenerEndpoint) { 84 | try { 85 | WebServer webServer = (WebServer) listenerEndpoint.getNativeData(WEB_SERVER_NATIVE_DATA_KEY); 86 | webServer.shutdownNow(); 87 | return null; 88 | } catch (Throwable e) { 89 | return Utils.createError(e); 90 | } 91 | } 92 | 93 | public static BError respond(BObject caller, BString message) { 94 | try { 95 | ChannelHandlerContext ctx = (ChannelHandlerContext) caller.getNativeData(NETTY_CONTEXT_NATIVE_DATA_KEY); 96 | WebServer.writeResponse(ctx, HttpResponseStatus.OK, message.getValue()); 97 | return null; 98 | } catch (Throwable e) { 99 | return Utils.createError(e); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /integration-tests/src/test/java/io/ballerina/stdlib/observability/tracing/TracingBaseTestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.observability.tracing; 20 | 21 | import com.google.gson.Gson; 22 | import com.google.gson.reflect.TypeToken; 23 | import io.ballerina.stdlib.observability.ObservabilityBaseTest; 24 | import io.ballerina.stdlib.observe.mockextension.BMockSpan; 25 | import org.ballerinalang.test.util.HttpClientRequest; 26 | import org.testng.annotations.AfterGroups; 27 | import org.testng.annotations.BeforeGroups; 28 | import org.testng.annotations.Test; 29 | 30 | import java.io.IOException; 31 | import java.lang.reflect.Type; 32 | import java.util.Collections; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Objects; 36 | import java.util.stream.Collectors; 37 | import java.util.stream.Stream; 38 | 39 | /** 40 | * Base Test Case which is inherited by all the Tracing Test Cases. 41 | */ 42 | @Test(groups = "tracing-test") 43 | public class TracingBaseTestCase extends ObservabilityBaseTest { 44 | protected static final String TEST_SRC_PROJECT_NAME = "tracing_tests"; 45 | protected static final String TEST_SRC_ORG_NAME = "intg_tests"; 46 | protected static final String TEST_SRC_PACKAGE_NAME = "tracing_tests"; 47 | protected static final String TEST_SRC_UTILS_MODULE_NAME = TEST_SRC_PACKAGE_NAME + ".utils"; 48 | 49 | protected static final String DEFAULT_MODULE_ID = TEST_SRC_ORG_NAME + "/" + TEST_SRC_PACKAGE_NAME + ":0.0.1"; 50 | protected static final String UTILS_MODULE_ID = TEST_SRC_ORG_NAME + "/" + TEST_SRC_UTILS_MODULE_NAME + ":0.0.1"; 51 | 52 | protected static final String MOCK_CLIENT_FILE_NAME = "mock_client_endpoint.bal"; 53 | 54 | protected static final String MOCK_CLIENT_OBJECT_NAME = TEST_SRC_ORG_NAME + "/" + TEST_SRC_UTILS_MODULE_NAME 55 | + "/MockClient"; 56 | protected static final String OBSERVABLE_ADDER_OBJECT_NAME = TEST_SRC_ORG_NAME + "/" + TEST_SRC_UTILS_MODULE_NAME 57 | + "/ObservableAdder"; 58 | protected static final String ZERO_SPAN_ID = "0000000000000000"; 59 | 60 | @BeforeGroups(value = "tracing-test", alwaysRun = true) 61 | public void setup() throws Exception { 62 | super.setupServer(TEST_SRC_PROJECT_NAME, TEST_SRC_PACKAGE_NAME, 63 | new int[]{19090, 19091, 19092, 19093, 19094, 19095, 19096, 19097}); 64 | } 65 | 66 | @AfterGroups(value = "tracing-test", alwaysRun = true) 67 | public void cleanup() throws Exception { 68 | super.cleanupServer(); 69 | } 70 | 71 | protected List getFinishedSpans(String serviceName, String entrypointModule, 72 | String entrypointFunction) throws IOException { 73 | return getFinishedSpans(serviceName).stream() 74 | .filter(span -> Objects.equals(span.getTags().get("entrypoint.function.module"), entrypointModule) && 75 | Objects.equals(span.getTags().get("entrypoint.function.name"), entrypointFunction)) 76 | .collect(Collectors.toList()); 77 | } 78 | 79 | protected List getFinishedSpans(String service) throws IOException { 80 | String requestUrl = "http://localhost:19090/mockTracer/getMockTraces"; 81 | String data = HttpClientRequest.doPost(requestUrl, service, Collections.emptyMap()).getData(); 82 | Type type = new TypeToken>() { 83 | }.getType(); 84 | return new Gson().fromJson(data, type); 85 | } 86 | 87 | @SafeVarargs 88 | protected final Map toMap(Map.Entry... mapEntries) { 89 | return Stream.of(mapEntries) 90 | .filter(Objects::nonNull) 91 | .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /testobserve/native/src/main/java/org/ballerina/testobserve/listenerendpoint/Resource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.ballerina.testobserve.listenerendpoint; 19 | 20 | import io.ballerina.runtime.api.types.Parameter; 21 | import io.ballerina.runtime.api.types.ResourceMethodType; 22 | import io.ballerina.runtime.api.types.Type; 23 | import io.ballerina.runtime.api.values.BObject; 24 | 25 | import java.util.Objects; 26 | 27 | import static io.ballerina.runtime.api.constants.TypeConstants.STRING_TNAME; 28 | import static org.ballerina.testobserve.listenerendpoint.Constants.CALLER_TYPE_NAME; 29 | import static org.ballerina.testobserve.listenerendpoint.Constants.TEST_OBSERVE_PACKAGE; 30 | 31 | /** 32 | * Holds information related to a Ballerina service resource. 33 | */ 34 | public class Resource { 35 | private final BObject serviceObject; 36 | private final ResourceMethodType resourceMethodType; 37 | private final String basePath; 38 | 39 | public Resource(BObject serviceObject, ResourceMethodType resourceMethodType, String basePath) { 40 | this.serviceObject = serviceObject; 41 | this.resourceMethodType = resourceMethodType; 42 | this.basePath = basePath; 43 | 44 | // Validate resource 45 | int paramCount = this.getParamTypes().length; 46 | String resourcePath = this.getResourcePath(); 47 | if (paramCount > 2) { 48 | throw new IllegalArgumentException("Invalid number of arguments in resource function " 49 | + resourcePath + ". Expected a maximum of 2 arguments, but found " + paramCount); 50 | } 51 | if (paramCount >= 1) { 52 | Type paramOneType = this.getParamTypes()[0]; 53 | if (!Objects.equals(paramOneType.getPackage(), TEST_OBSERVE_PACKAGE) 54 | || !Objects.equals(paramOneType.getName(), CALLER_TYPE_NAME)) { 55 | throw new IllegalArgumentException("Invalid first parameter in the resource function " 56 | + resourcePath + ". Expected parameter of type " + TEST_OBSERVE_PACKAGE + "/" 57 | + CALLER_TYPE_NAME + ", but found " + paramOneType.getPackage() + "/" 58 | + paramOneType.getName()); 59 | } 60 | } 61 | if (paramCount >= 2) { 62 | Type paramTwoType = this.getParamTypes()[1]; 63 | if (!Objects.equals(paramTwoType.getPackage().toString(), "null") 64 | || !Objects.equals(paramTwoType.getName(), STRING_TNAME)) { 65 | throw new IllegalArgumentException("Invalid second parameter in the resource function " 66 | + resourcePath + ". Expected parameter of type \"" + STRING_TNAME + "\", but found \"" 67 | + paramTwoType.getName() + "\""); 68 | } 69 | } 70 | } 71 | 72 | public BObject getServiceObject() { 73 | return this.serviceObject; 74 | } 75 | 76 | public String getResourceFunctionName() { 77 | return this.resourceMethodType.getName(); 78 | } 79 | 80 | public String getAccessor() { 81 | return this.resourceMethodType.getAccessor(); 82 | } 83 | 84 | public final String getResourcePath() { 85 | return Utils.normalizeResourcePath(basePath) 86 | + Utils.normalizeResourcePath(String.join("/", this.resourceMethodType.getResourcePath())); 87 | } 88 | 89 | public final Type[] getParamTypes() { 90 | Parameter[] parameters = this.resourceMethodType.getParameters(); 91 | Type[] types = new Type[parameters.length]; 92 | for (int i = 0; i < parameters.length; i++) { 93 | types[i] = parameters[i].type; 94 | } 95 | return types; 96 | } 97 | 98 | public Type getReturnType() { 99 | return this.resourceMethodType.getReturnType(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /native/src/main/java/io/ballerina/stdlib/observe/internal/NativeFunctions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package io.ballerina.stdlib.observe.internal; 19 | 20 | import io.ballerina.runtime.api.Environment; 21 | import io.ballerina.runtime.api.values.BError; 22 | import io.ballerina.runtime.api.values.BString; 23 | import io.ballerina.runtime.observability.ObserveUtils; 24 | import io.ballerina.runtime.observability.metrics.DefaultMetricRegistry; 25 | import io.ballerina.runtime.observability.metrics.MetricRegistry; 26 | import io.ballerina.runtime.observability.metrics.noop.NoOpMetricProvider; 27 | import io.ballerina.runtime.observability.metrics.spi.MetricProvider; 28 | import io.ballerina.runtime.observability.tracer.TracersStore; 29 | import io.ballerina.runtime.observability.tracer.noop.NoOpTracerProvider; 30 | import io.ballerina.runtime.observability.tracer.spi.TracerProvider; 31 | import io.ballerina.stdlib.observe.observers.BallerinaMetricsLogsObserver; 32 | import io.ballerina.stdlib.observe.observers.BallerinaMetricsObserver; 33 | import io.ballerina.stdlib.observe.observers.BallerinaTracingObserver; 34 | 35 | import java.io.PrintStream; 36 | import java.util.ServiceLoader; 37 | 38 | /** 39 | * Java inter-op functions called by the ballerina observability internal module. 40 | */ 41 | public class NativeFunctions { 42 | private static final PrintStream errStream = System.err; 43 | 44 | public static BError enableMetrics(BString providerName) { 45 | // Loading the proper Metrics Provider 46 | MetricProvider selectedProvider = null; 47 | if (NoOpMetricProvider.NAME.equalsIgnoreCase(providerName.getValue())) { 48 | selectedProvider = new NoOpMetricProvider(); 49 | } else { 50 | for (MetricProvider providerFactory : ServiceLoader.load(MetricProvider.class)) { 51 | if (providerName.getValue().equalsIgnoreCase(providerFactory.getName())) { 52 | selectedProvider = providerFactory; 53 | break; 54 | } 55 | } 56 | if (selectedProvider == null) { 57 | errStream.println("error: metrics provider " + providerName + " not found"); 58 | selectedProvider = new NoOpMetricProvider(); 59 | } 60 | } 61 | 62 | try { 63 | selectedProvider.init(); 64 | DefaultMetricRegistry.setInstance(new MetricRegistry(selectedProvider)); 65 | ObserveUtils.addObserver(new BallerinaMetricsObserver()); 66 | return null; 67 | } catch (BError e) { 68 | return e; 69 | } 70 | } 71 | 72 | public static BError enableTracing(BString providerName) { 73 | // Loading the proper tracing Provider 74 | TracerProvider selectedProvider = null; 75 | if (NoOpTracerProvider.NAME.equalsIgnoreCase(providerName.getValue())) { 76 | selectedProvider = new NoOpTracerProvider(); 77 | } else { 78 | for (TracerProvider providerFactory : ServiceLoader.load(TracerProvider.class)) { 79 | if (providerName.getValue().equalsIgnoreCase(providerFactory.getName())) { 80 | selectedProvider = providerFactory; 81 | } 82 | } 83 | if (selectedProvider == null) { 84 | errStream.println("error: tracer provider " + providerName + " not found"); 85 | selectedProvider = new NoOpTracerProvider(); 86 | } 87 | } 88 | 89 | try { 90 | selectedProvider.init(); 91 | TracersStore.getInstance().setTracerGenerator(selectedProvider); 92 | ObserveUtils.addObserver(new BallerinaTracingObserver()); 93 | return null; 94 | } catch (BError e) { 95 | return e; 96 | } 97 | } 98 | 99 | public static BError enableMetricsLogging(Environment env) { 100 | try { 101 | ObserveUtils.addObserver(new BallerinaMetricsLogsObserver(env)); 102 | return null; 103 | } catch (BError e) { 104 | return e; 105 | } 106 | } 107 | 108 | public static void printError(BString message) { 109 | errStream.println("error: " + message.getValue()); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /testobserve/tests/src/test/java/io/ballerina/stdlib/testobserve/ListenerEndpointTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.testobserve; 20 | 21 | import org.ballerinalang.test.context.BServerInstance; 22 | import org.ballerinalang.test.context.BalServer; 23 | import org.ballerinalang.test.context.Utils; 24 | import org.ballerinalang.test.util.HttpClientRequest; 25 | import org.ballerinalang.test.util.HttpResponse; 26 | import org.testng.Assert; 27 | import org.testng.annotations.AfterClass; 28 | import org.testng.annotations.BeforeClass; 29 | import org.testng.annotations.Test; 30 | 31 | import java.io.File; 32 | import java.net.InetAddress; 33 | import java.nio.file.Files; 34 | import java.nio.file.Path; 35 | import java.nio.file.Paths; 36 | import java.util.Collections; 37 | import java.util.logging.Logger; 38 | 39 | /** 40 | * Test cases for listener written to be used in unit tests. 41 | */ 42 | public class ListenerEndpointTest { 43 | 44 | private static BalServer balServer; 45 | private static BServerInstance servicesServerInstance; 46 | 47 | private static final String SERVICE_BASE_URL = "http://localhost:29091/testServiceOne"; 48 | private static final Logger LOGGER = Logger.getLogger(ListenerEndpointTest.class.getName()); 49 | 50 | @BeforeClass 51 | private void setup() throws Exception { 52 | 53 | balServer = new BalServer(); 54 | 55 | // Don't use 9898 port here. It is used in metrics test cases. 56 | servicesServerInstance = new BServerInstance(balServer); 57 | 58 | String sourcesDir = new File("src" + File.separator + "test" + File.separator + "resources" + File.separator + 59 | "listener_tests").getAbsolutePath(); 60 | int[] requiredPorts = {29091}; 61 | servicesServerInstance.startServer(sourcesDir, "listener_tests", null, new String[0], requiredPorts); 62 | Utils.waitForPortsToOpen(requiredPorts, 1000 * 60, false, InetAddress.getByName("localhost")); 63 | } 64 | 65 | @AfterClass 66 | private void cleanup() throws Exception { 67 | 68 | Path ballerinaInternalLog = Paths.get(balServer.getServerHome(), "ballerina-internal.log"); 69 | if (Files.exists(ballerinaInternalLog)) { 70 | LOGGER.severe("=== Ballerina Internal Log Start ==="); 71 | Files.lines(ballerinaInternalLog).forEach(LOGGER::severe); 72 | LOGGER.severe("=== Ballerina Internal Log End ==="); 73 | } 74 | servicesServerInstance.removeAllLeechers(); 75 | servicesServerInstance.shutdownServer(); 76 | balServer.cleanup(); 77 | } 78 | 79 | @Test 80 | public void testHelloWorldResponse() throws Exception { 81 | 82 | HttpResponse httpResponse = HttpClientRequest.doPost(SERVICE_BASE_URL + "/resourceOne", 83 | "dummy-ignored-input-1", Collections.emptyMap()); 84 | Assert.assertEquals(httpResponse.getResponseCode(), 200); 85 | Assert.assertEquals(httpResponse.getData(), "Hello from Resource One"); 86 | } 87 | 88 | @Test 89 | public void testSuccessfulResponse() throws Exception { 90 | 91 | HttpResponse httpResponse = HttpClientRequest.doPost(SERVICE_BASE_URL + "/resourceTwo", 92 | "10", Collections.emptyMap()); 93 | Assert.assertEquals(httpResponse.getResponseCode(), 200); 94 | Assert.assertEquals(httpResponse.getData(), "Sum of numbers: 55"); 95 | } 96 | 97 | @Test 98 | public void testErrorReturnResponse() throws Exception { 99 | 100 | HttpResponse httpResponse = HttpClientRequest.doPost(SERVICE_BASE_URL + "/resourceTwo", 101 | "invalid-number", Collections.emptyMap()); 102 | Assert.assertEquals(httpResponse.getResponseCode(), 500); 103 | Assert.assertEquals(httpResponse.getData(), "{ballerina/lang.int}NumberParsingError"); 104 | } 105 | 106 | @Test 107 | public void testPanicResponse() throws Exception { 108 | 109 | HttpResponse httpResponse = HttpClientRequest.doPost(SERVICE_BASE_URL + "/resourceThree", 110 | "dummy-ignored-input-2", Collections.emptyMap()); 111 | Assert.assertEquals(httpResponse.getResponseCode(), 500); 112 | Assert.assertEquals(httpResponse.getData(), "Test Error"); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/02_resource_function.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | import intg_tests/tracing_tests.utils as utils; 19 | 20 | service /testServiceTwo on new testobserve:Listener(19092) { 21 | # Resource function for testing whether no return functions are instrumented properly. 22 | resource function post resourceOne(testobserve:Caller caller, string body) { 23 | int numberCount = checkpanic 'int:fromString(body); 24 | var sum = 0; 25 | foreach var i in 1 ... numberCount { 26 | sum = sum + i; 27 | } 28 | checkpanic caller->respond("Sum of numbers: " + sum.toString()); 29 | } 30 | 31 | # Resource function for testing whether optional error return functions are instrumented properly. 32 | resource function post resourceTwo(testobserve:Caller caller, string body) returns error? { 33 | int numberCount = checkpanic 'int:fromString(body); 34 | var sum = 0; 35 | foreach var i in 1 ... numberCount { 36 | sum = sum + i; 37 | } 38 | checkpanic caller->respond("Sum of numbers: " + sum.toString()); 39 | } 40 | 41 | # Resource function for testing whether returning errors from the resource functions are handled properly. 42 | resource function post resourceThree(testobserve:Caller caller) returns error? { 43 | var sum = 0; 44 | foreach var i in 1 ... 10 { 45 | sum = sum + i; 46 | } 47 | var expectedSum = 55; 48 | if (sum != expectedSum) { // Check for validating if normal execution is intact from instrumentation 49 | error err = error("failed to find the sum of 10 numbers. expected: " + expectedSum.toString() 50 | + ", but found: " + sum.toString()); 51 | panic err; 52 | } 53 | error e = error("Test Error 1"); 54 | return e; 55 | } 56 | 57 | # Resource function for testing whether panicking from within resource function body is handled properly. 58 | resource function post resourceFour(testobserve:Caller caller) returns error? { 59 | var sum = 0; 60 | foreach var i in 1 ... 10 { 61 | sum = sum + i; 62 | } 63 | var expectedSum = 55; 64 | if (sum != expectedSum) { // Check for validating if normal execution is intact from instrumentation 65 | error err = error("failed to find the sum of 10 numbers. expected: " + expectedSum.toString() 66 | + ", but found: " + sum.toString()); 67 | panic err; 68 | } else { 69 | error e = error("Test Error 2"); 70 | panic e; 71 | } 72 | } 73 | 74 | # Resource function for testing whether panicking from within a function call is handled properly. 75 | resource function post resourceFive(testobserve:Caller caller) returns error? { 76 | var sum = panicAfterCalculatingSum(13); 77 | checkpanic caller->respond("Sum of numbers: " + sum.toString()); 78 | } 79 | 80 | # Resource function for testing whether panicking from within a function pointer is handled properly. 81 | resource function post resourceSix(testobserve:Caller caller) returns error? { 82 | var calFunc = panicAfterCalculatingSum; 83 | var sum = calFunc(17); 84 | checkpanic caller->respond("Sum of numbers: " + sum.toString()); 85 | } 86 | 87 | # Resource function for testing calling an observable function from within resource 88 | resource function post resourceSeven(testobserve:Caller caller) { 89 | utils:MockClient testClient1 = new(); 90 | var ret = checkpanic testClient1->callWithReturn(5, 7); 91 | var expectedSum = 12; 92 | if (ret != expectedSum) { // Check for validating if normal execution is intact from instrumentation 93 | error err = error("failed to find the sum of 10 numbers. expected: " + expectedSum.toString() 94 | + ", but found: " + ret.toString()); 95 | panic err; 96 | } 97 | checkpanic caller->respond("Sum of numbers: " + ret.toString()); 98 | } 99 | 100 | # Resource function for testing early return is handled properly. 101 | resource function post resourceEight(testobserve:Caller caller) returns error? { 102 | var sum = trap testClient->callWithReturn(3, 13); 103 | if (sum is int && sum == 16) { 104 | checkpanic caller->respond("Successfully executed"); 105 | return; 106 | } else { 107 | checkpanic caller->respond("Unexpected return value"); 108 | } 109 | testClient->callWithNoReturn(5, 17, 22); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /integration-tests/src/test/resources/observability/tracing_tests/05_concurrency.bal: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 2 | // 3 | // WSO2 Inc. licenses this file to you under the Apache License, 4 | // Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, 11 | // software distributed under the License is distributed on an 12 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. See the License for the 14 | // specific language governing permissions and limitations 15 | // under the License. 16 | 17 | import ballerina/testobserve; 18 | import intg_tests/tracing_tests.utils as utils; 19 | 20 | service / on new testobserve:Listener(19095) { 21 | # Resource function for testing async remote call wait 22 | resource function post resourceOne(testobserve:Caller caller) { 23 | future futureSum = start testClient->calculateSum(6, 17); 24 | var sum = checkpanic wait futureSum; 25 | if (sum != 23) { // Check for validating if normal execution is intact from instrumentation 26 | error err = error("failed to find the sum of 6 and 17. expected: 23 received: " + sum.toString()); 27 | panic err; 28 | } 29 | checkpanic caller->respond("Invocation Successful"); 30 | } 31 | 32 | # Resource function for testing async observable call wait 33 | resource function post resourceTwo(testobserve:Caller caller) { 34 | future futureSum = start calculateSumWithObservability(18, 31); 35 | var sum = checkpanic wait futureSum; 36 | if (sum != 49) { // Check for validating if normal execution is intact from instrumentation 37 | error err = error("failed to find the sum of 18 and 31. expected: 49 received: " + sum.toString()); 38 | panic err; 39 | } 40 | checkpanic caller->respond("Invocation Successful"); 41 | } 42 | 43 | # Resource function for testing async observable call wait 44 | resource function post resourceThree(testobserve:Caller caller) { 45 | utils:ObservableAdderClass adder = new utils:ObservableAdder(61, 23); 46 | future futureSum = start adder.getSum(); 47 | var sum = checkpanic wait futureSum; 48 | if (sum != 84) { // Check for validating if normal execution is intact from instrumentation 49 | error err = error("failed to find the sum of 61 and 23. expected: 84 received: " + sum.toString()); 50 | panic err; 51 | } 52 | checkpanic caller->respond("Invocation Successful"); 53 | } 54 | 55 | # Resource function for testing worker interactions with no delays in worker completions 56 | resource function post resourceFour(testobserve:Caller caller) { 57 | testWorkerInteractions(10); // "10" adds no delays 58 | checkpanic caller->respond("Invocation Successful"); 59 | } 60 | 61 | # Resource function for testing worker interactions with delayed writing 62 | resource function post resourceFive(testobserve:Caller caller) { 63 | testWorkerInteractions(11); // "11" adds delayed writing 64 | checkpanic caller->respond("Invocation Successful"); 65 | } 66 | 67 | # Resource function for testing worker interactions with delayed reading 68 | resource function post resourceSix(testobserve:Caller caller) { 69 | testWorkerInteractions(12); // "12" adds delayed reading 70 | checkpanic caller->respond("Invocation Successful"); 71 | } 72 | 73 | # Resource function for testing forked workers 74 | resource function post resourceSeven(testobserve:Caller caller) { 75 | int n = 100; 76 | fork { 77 | worker w3 returns int { 78 | int result = 0; 79 | foreach int i in 1...10 { 80 | result = result + n; 81 | } 82 | testobserve:sleep(100); // Sleep to make the workers not finish together 83 | return result; 84 | } 85 | worker w4 returns int { 86 | int result = n * 10; 87 | return result; 88 | } 89 | } 90 | var expectedResult = 1000; 91 | record {int w3; int w4;} actualResults = wait {w3, w4}; 92 | if (actualResults.w3 != expectedResult && actualResults.w4 != expectedResult) { 93 | error err = error("failed to find the sum of first " + n.toString() + " numbers. w3 result: " 94 | + actualResults.w3.toString() + " w4 result: " + actualResults.w4.toString() + " expectedResult: " 95 | + expectedResult.toString()); 96 | panic err; 97 | } 98 | checkpanic caller->respond("Invocation Successful"); 99 | } 100 | } 101 | 102 | function testWorkerInteractions(int c) { 103 | worker w1 { 104 | int a = c + 3; 105 | if (c == 11) { 106 | testobserve:sleep(1000); // Sleep to make the workers not finish together 107 | } 108 | a -> w2; 109 | } 110 | worker w2 { 111 | if (c == 12) { 112 | testobserve:sleep(1000); // Sleep to make the workers not finish together 113 | } 114 | int b = <- w1; 115 | if (b != (c + 3)) { 116 | error err = error("worker interaction failed"); 117 | panic err; 118 | } 119 | } 120 | wait w2; 121 | wait w1; 122 | } 123 | -------------------------------------------------------------------------------- /native/src/main/java/io/ballerina/stdlib/observe/observers/BallerinaMetricsLogsObserver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package io.ballerina.stdlib.observe.observers; 19 | 20 | import io.ballerina.runtime.api.Environment; 21 | import io.ballerina.runtime.api.Module; 22 | import io.ballerina.runtime.api.creators.ValueCreator; 23 | import io.ballerina.runtime.api.utils.StringUtils; 24 | import io.ballerina.runtime.api.values.BMap; 25 | import io.ballerina.runtime.api.values.BString; 26 | import io.ballerina.runtime.observability.BallerinaObserver; 27 | import io.ballerina.runtime.observability.ObserverContext; 28 | import io.ballerina.runtime.observability.metrics.Tag; 29 | 30 | import java.io.PrintStream; 31 | import java.util.HashSet; 32 | import java.util.Map; 33 | import java.util.Optional; 34 | import java.util.Set; 35 | 36 | import static io.ballerina.runtime.observability.ObservabilityConstants.PROPERTY_KEY_HTTP_STATUS_CODE; 37 | import static io.ballerina.runtime.observability.ObservabilityConstants.STATUS_CODE_GROUP_SUFFIX; 38 | import static io.ballerina.runtime.observability.ObservabilityConstants.TAG_KEY_HTTP_STATUS_CODE_GROUP; 39 | 40 | public class BallerinaMetricsLogsObserver implements BallerinaObserver { 41 | private static final String ORG_NAME = "ballerinax"; 42 | private static final String MODULE_NAME = "metrics.logs"; 43 | private static final String METRIC_LOG_FUNCTION_NAME = "printMetricsLog"; 44 | private static final String PROPERTY_START_TIME = "_observation_start_time_"; 45 | private static final PrintStream consoleError = System.err; 46 | 47 | private static Environment environment; 48 | 49 | public BallerinaMetricsLogsObserver(Environment environment) { 50 | BallerinaMetricsLogsObserver.environment = environment; 51 | } 52 | 53 | @Override 54 | public void startServerObservation(ObserverContext observerContext) { 55 | startObservation(observerContext); 56 | } 57 | 58 | @Override 59 | public void startClientObservation(ObserverContext observerContext) { 60 | startObservation(observerContext); 61 | } 62 | 63 | @Override 64 | public void stopServerObservation(ObserverContext observerContext) { 65 | if (!observerContext.isStarted()) { 66 | // Do not collect metrics if the observation hasn't started 67 | return; 68 | } 69 | stopObservation(observerContext); 70 | } 71 | 72 | @Override 73 | public void stopClientObservation(ObserverContext observerContext) { 74 | if (!observerContext.isStarted()) { 75 | // Do not collect metrics if the observation hasn't started 76 | return; 77 | } 78 | stopObservation(observerContext); 79 | } 80 | 81 | private void startObservation(ObserverContext observerContext) { 82 | if (observerContext.getProperty(PROPERTY_START_TIME) == null) { 83 | observerContext.addProperty(PROPERTY_START_TIME, System.nanoTime()); 84 | } 85 | } 86 | 87 | private void stopObservation(ObserverContext observerContext) { 88 | Set tags = new HashSet<>(); 89 | Map customTags = observerContext.customMetricTags; 90 | if (customTags != null) { 91 | tags.addAll(customTags.values()); 92 | } 93 | tags.addAll(observerContext.getAllTags()); 94 | 95 | // Add status_code_group tag 96 | Integer statusCode = (Integer) observerContext.getProperty(PROPERTY_KEY_HTTP_STATUS_CODE); 97 | if (statusCode != null && statusCode > 0) { 98 | tags.add(Tag.of(TAG_KEY_HTTP_STATUS_CODE_GROUP, (statusCode / 100) + STATUS_CODE_GROUP_SUFFIX)); 99 | } 100 | 101 | try { 102 | Long startTime = (Long) observerContext.getProperty(PROPERTY_START_TIME); 103 | long duration = System.nanoTime() - startTime; 104 | 105 | Optional protocolValue = Optional.empty(); 106 | if (tags.stream().anyMatch(tag -> tag.getKey().equals("protocol"))) { 107 | protocolValue = tags.stream().filter(tag -> tag.getKey().equals("protocol")).map(Tag::getValue) 108 | .findFirst(); 109 | } 110 | String protocol = protocolValue.orElse("http"); 111 | 112 | BMap logAttributes = ValueCreator.createMapValue(); 113 | logAttributes.put(StringUtils.fromString("protocol"), StringUtils.fromString(protocol)); 114 | tags.stream().filter(tag -> !tag.getKey().equals("protocol")) 115 | .forEach(tag -> logAttributes.put(StringUtils.fromString(tag.getKey()), 116 | StringUtils.fromString(tag.getValue()))); 117 | logAttributes.put(StringUtils.fromString("response_time_seconds"), 118 | StringUtils.fromString(String.valueOf(duration / 1E9))); 119 | 120 | printMetricLog(logAttributes); 121 | } catch (RuntimeException e) { 122 | handleError("multiple metrics", tags, e); 123 | } 124 | } 125 | 126 | private void handleError(String metricName, Set tags, RuntimeException e) { 127 | // Metric Provider may throw exceptions if there is a mismatch in tags. 128 | consoleError.println("error: error collecting metric logs for " + metricName + " with tags " + tags + 129 | ": " + e.getMessage()); 130 | } 131 | 132 | private static void printMetricLog(BMap logAttributes) { 133 | // TODO: Remove version when the API is finalized, and add the configured org name. 134 | Module metricsLogsModule = new Module(ORG_NAME, MODULE_NAME, "1"); 135 | environment.getRuntime().callFunction(metricsLogsModule, METRIC_LOG_FUNCTION_NAME, null, logAttributes); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /native/src/main/java/io/ballerina/stdlib/observe/observers/BallerinaMetricsObserver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. 3 | * 4 | * WSO2 LLC. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package io.ballerina.stdlib.observe.observers; 19 | 20 | import io.ballerina.runtime.observability.BallerinaObserver; 21 | import io.ballerina.runtime.observability.ObserverContext; 22 | import io.ballerina.runtime.observability.metrics.DefaultMetricRegistry; 23 | import io.ballerina.runtime.observability.metrics.Gauge; 24 | import io.ballerina.runtime.observability.metrics.MetricId; 25 | import io.ballerina.runtime.observability.metrics.MetricRegistry; 26 | import io.ballerina.runtime.observability.metrics.StatisticConfig; 27 | import io.ballerina.runtime.observability.metrics.Tag; 28 | 29 | import java.io.PrintStream; 30 | import java.time.Duration; 31 | import java.util.HashSet; 32 | import java.util.Map; 33 | import java.util.Set; 34 | 35 | import static io.ballerina.runtime.observability.ObservabilityConstants.PROPERTY_KEY_HTTP_STATUS_CODE; 36 | import static io.ballerina.runtime.observability.ObservabilityConstants.STATUS_CODE_GROUP_SUFFIX; 37 | import static io.ballerina.runtime.observability.ObservabilityConstants.TAG_KEY_HTTP_STATUS_CODE_GROUP; 38 | 39 | /** 40 | * Observe the runtime and collect measurements. 41 | */ 42 | public class BallerinaMetricsObserver implements BallerinaObserver { 43 | 44 | private static final String PROPERTY_START_TIME = "_observation_start_time_"; 45 | private static final String PROPERTY_IN_PROGRESS_COUNTER = "_observation_in_progress_counter_"; 46 | 47 | private static final PrintStream consoleError = System.err; 48 | 49 | private static final MetricRegistry metricRegistry = DefaultMetricRegistry.getInstance(); 50 | 51 | private static final StatisticConfig[] responseTimeStatisticConfigs = new StatisticConfig[]{ 52 | StatisticConfig.builder() 53 | .expiry(Duration.ofSeconds(10)) 54 | .percentiles(StatisticConfig.DEFAULT.getPercentiles()) 55 | .build(), 56 | StatisticConfig.builder() 57 | .expiry(Duration.ofMinutes(1)) 58 | .percentiles(StatisticConfig.DEFAULT.getPercentiles()) 59 | .build(), 60 | StatisticConfig.builder() 61 | .expiry(Duration.ofMinutes(5)) 62 | .percentiles(StatisticConfig.DEFAULT.getPercentiles()) 63 | .build() 64 | }; 65 | 66 | @Override 67 | public void startServerObservation(ObserverContext observerContext) { 68 | startObservation(observerContext); 69 | } 70 | 71 | @Override 72 | public void startClientObservation(ObserverContext observerContext) { 73 | startObservation(observerContext); 74 | } 75 | 76 | @Override 77 | public void stopServerObservation(ObserverContext observerContext) { 78 | if (!observerContext.isStarted()) { 79 | // Do not collect metrics if the observation hasn't started 80 | return; 81 | } 82 | stopObservation(observerContext); 83 | } 84 | 85 | @Override 86 | public void stopClientObservation(ObserverContext observerContext) { 87 | if (!observerContext.isStarted()) { 88 | // Do not collect metrics if the observation hasn't started 89 | return; 90 | } 91 | stopObservation(observerContext); 92 | } 93 | 94 | private void startObservation(ObserverContext observerContext) { 95 | observerContext.addProperty(PROPERTY_START_TIME, System.nanoTime()); 96 | Set tags = observerContext.getAllTags(); 97 | try { 98 | Gauge inProgressGauge = metricRegistry.gauge(new MetricId("inprogress_requests", "In-progress requests", 99 | tags)); 100 | inProgressGauge.increment(); 101 | /* 102 | * The in progress counter is stored so that the same counter can be decremted when the observation 103 | * ends. This is needed as the the program may add tags to the context causing the tags to be 104 | * different at the end compared to the start. 105 | */ 106 | observerContext.addProperty(PROPERTY_IN_PROGRESS_COUNTER, inProgressGauge); 107 | } catch (RuntimeException e) { 108 | handleError("inprogress_requests", tags, e); 109 | } 110 | } 111 | 112 | private void stopObservation(ObserverContext observerContext) { 113 | Set tags = new HashSet<>(); 114 | Map customTags = observerContext.customMetricTags; 115 | if (customTags != null) { 116 | tags.addAll(customTags.values()); 117 | } 118 | tags.addAll(observerContext.getAllTags()); 119 | 120 | // Add status_code_group tag 121 | Integer statusCode = (Integer) observerContext.getProperty(PROPERTY_KEY_HTTP_STATUS_CODE); 122 | if (statusCode != null && statusCode > 0) { 123 | tags.add(Tag.of(TAG_KEY_HTTP_STATUS_CODE_GROUP, (statusCode / 100) + STATUS_CODE_GROUP_SUFFIX)); 124 | } 125 | 126 | try { 127 | Long startTime = (Long) observerContext.getProperty(PROPERTY_START_TIME); 128 | long duration = System.nanoTime() - startTime; 129 | ((Gauge) observerContext.getProperty(PROPERTY_IN_PROGRESS_COUNTER)).decrement(); 130 | metricRegistry.gauge(new MetricId("response_time_seconds", 131 | "Response time", tags), responseTimeStatisticConfigs).setValue(duration / 1E9); 132 | metricRegistry.counter(new MetricId("response_time_nanoseconds_total", 133 | "Total response response time for all requests", tags)).increment(duration); 134 | metricRegistry.counter(new MetricId("requests_total", 135 | "Total number of requests", tags)).increment(); 136 | if (statusCode != null && 400 <= statusCode && statusCode < 600) { 137 | metricRegistry.counter(new MetricId("response_errors_total", 138 | "Total number of response errors", tags)).increment(); 139 | } 140 | } catch (RuntimeException e) { 141 | handleError("multiple metrics", tags, e); 142 | } 143 | } 144 | 145 | private void handleError(String metricName, Set tags, RuntimeException e) { 146 | // Metric Provider may throw exceptions if there is a mismatch in tags. 147 | consoleError.println("error: error collecting metrics for " + metricName + " with tags " + tags + 148 | ": " + e.getMessage()); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /integration-tests/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | apply plugin: 'java-library' 19 | 20 | configurations { 21 | jbalTools { 22 | transitive false 23 | } 24 | testUtils { 25 | transitive false 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}" 31 | implementation group: 'io.ballerina.stdlib', name: 'observe-native', version: "${observeVersion}" 32 | testUtils implementation(group: 'org.ballerinalang', name: 'ballerina-test-utils', 33 | version: "${ballerinaLangVersion}") 34 | testImplementation group: 'org.ballerinalang', name: 'ballerina-metrics-extension', 35 | version: "${ballerinaLangVersion}" 36 | implementation group: 'org.testng', name: 'testng', version: "${testngVersion}" 37 | testImplementation "com.google.code.gson:gson:${gsonVersion}" 38 | testImplementation group: 'org.ballerinalang', name: 'identifier-util', version: "${ballerinaLangVersion}" 39 | } 40 | 41 | def ballerinaDist = "build/target/extracted-distributions" 42 | 43 | task unpackJballerinaTools(type: Copy) { 44 | configurations.jbalTools.resolvedConfiguration.resolvedArtifacts.each { artifact -> 45 | from zipTree(artifact.getFile()) 46 | into new File(ballerinaDist, "jballerina-tools-zip") 47 | } 48 | } 49 | 50 | task unpackStdLibs { 51 | doLast { 52 | project.configurations.ballerinaStdLibs.resolvedConfiguration.resolvedArtifacts.each { artifact -> 53 | project.copy { 54 | from project.zipTree(artifact.getFile()) 55 | into new File("${project.buildDir}/target/extracted-distributions", artifact.name + '-zip') 56 | } 57 | } 58 | } 59 | } 60 | 61 | task copyStdlibs { 62 | dependsOn(project.unpackJballerinaTools) 63 | dependsOn(project.unpackStdLibs) 64 | doLast { 65 | /* Standard Libraries */ 66 | project.configurations.ballerinaStdLibs.resolvedConfiguration.resolvedArtifacts.each { artifact -> 67 | def artifactExtractedPath = "${project.buildDir}/target/extracted-distributions/" + artifact.name + '-zip' 68 | project.copy { 69 | def destination = "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${project.ballerinaLangVersion}" 70 | into destination 71 | into('repo/bala') { 72 | from "${artifactExtractedPath}/bala" 73 | } 74 | into('repo/cache') { 75 | from "${artifactExtractedPath}/cache" 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | task copyObserveStdlib { 83 | dependsOn(":testobserve-ballerina:build") 84 | dependsOn(unpackJballerinaTools) 85 | dependsOn(copyStdlibs) 86 | doLast { 87 | /* Copy Observe module */ 88 | copy { 89 | from "$project.rootDir/testobserve/ballerina/build/bala_unzipped/bala" 90 | into "${ballerinaDist}/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/repo/bala" 91 | } 92 | 93 | /* Copy Observe Internal module */ 94 | copy { 95 | from "$project.rootDir/ballerina/build/bala_unzipped/bala" 96 | into "${ballerinaDist}/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/repo/bala" 97 | } 98 | } 99 | } 100 | 101 | task createServerZip(type: Zip) { 102 | dependsOn copyObserveStdlib 103 | 104 | archiveFileName = "jballerina-tools-${ballerinaLangVersion}.zip" 105 | destinationDirectory = file("${buildDir}/repacked-distributions") 106 | from "${project.buildDir}/target/extracted-distributions/jballerina-tools-zip/" 107 | } 108 | 109 | def classFilesArray = [] 110 | 111 | task copyBallerinaClassFiles { 112 | doFirst { 113 | fileTree("$project.rootDir/ballerina/build/bal_build_target/").include("**/*.jar").forEach { jar -> 114 | zipTree(jar).matching { 115 | exclude '**/tests/*' 116 | exclude '**/$_init.class' 117 | exclude '**/$value$Caller.class' 118 | exclude '**/$value$Detail.class' 119 | exclude '**/$value$DetailType.class' 120 | exclude '**/$value$EmptyIterator.class' 121 | exclude '**/$value$$anonType$_6.class' 122 | exclude '**/$value$$anonType$_*.class' 123 | exclude '**/$value$_Frame.class' 124 | include '**/*.class' 125 | }.each { file -> classFilesArray.push(file) } 126 | } 127 | } 128 | } 129 | 130 | jacocoTestReport { 131 | dependsOn test 132 | dependsOn copyBallerinaClassFiles 133 | additionalClassDirs files(classFilesArray) 134 | reports { 135 | xml.required = true 136 | html.required = true 137 | } 138 | } 139 | 140 | test { 141 | jvmArgs = ['--add-opens=java.base/java.time=ALL-UNNAMED'] 142 | dependsOn createServerZip 143 | finalizedBy jacocoTestReport 144 | useTestNG() { 145 | suites 'src/test/resources/testng.xml' 146 | } 147 | systemProperty 'enableJBallerinaTests', 'true' 148 | systemProperty 'basedir', "$buildDir" 149 | systemProperty 'libdir', "$buildDir" 150 | systemProperty 'server.zip', createServerZip.outputs.files.singleFile 151 | systemProperty 'jballerina.server.zip', createServerZip.outputs.files.singleFile 152 | systemProperty 'java.util.logging.config.file', "$buildDir/resources/test/logging.properties" 153 | systemProperty 'ballerina.agent.path', configurations.testUtils.asPath 154 | systemProperty 'ballerina.agent.port.start.value', 27000 155 | 156 | testLogging { 157 | showStackTraces true 158 | showStandardStreams true 159 | events "failed" 160 | exceptionFormat "full" 161 | events "PASSED", "FAILED", "SKIPPED" 162 | afterSuite { desc, result -> 163 | if (!desc.parent) { // will match the outermost suite 164 | def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" 165 | def startItem = '| ', endItem = ' |' 166 | def repeatLength = startItem.length() + output.length() + endItem.length() 167 | println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength)) 168 | } 169 | } 170 | } 171 | } 172 | 173 | build.dependsOn ":testobserve-ballerina-tests:build" 174 | test.dependsOn ":testobserve-ballerina-tests:build" 175 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | if ! command -v java >/dev/null 2>&1 134 | then 135 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 136 | 137 | Please set the JAVA_HOME variable in your environment to match the 138 | location of your Java installation." 139 | fi 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | 201 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 202 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 203 | 204 | # Collect all arguments for the java command; 205 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 206 | # shell script including quotes and variable substitutions, so put them in 207 | # double quotes to make sure that they get re-expanded; and 208 | # * put everything else in single quotes, so that it's not re-expanded. 209 | 210 | set -- \ 211 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 212 | -classpath "$CLASSPATH" \ 213 | org.gradle.wrapper.GradleWrapperMain \ 214 | "$@" 215 | 216 | # Stop when "xargs" is not available. 217 | if ! command -v xargs >/dev/null 2>&1 218 | then 219 | die "xargs is not available" 220 | fi 221 | 222 | # Use "xargs" to parse quoted args. 223 | # 224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 225 | # 226 | # In Bash we could simply go: 227 | # 228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 229 | # set -- "${ARGS[@]}" "$@" 230 | # 231 | # but POSIX shell has neither arrays nor command substitution, so instead we 232 | # post-process each arg (as a line of input to sed) to backslash-escape any 233 | # character that might be a shell metacharacter, then use eval to reverse 234 | # that process (while maintaining the separation between arguments), and wrap 235 | # the whole thing up as a single "set" statement. 236 | # 237 | # This will of course break if any of these variables contains a newline or 238 | # an unmatched quote. 239 | # 240 | 241 | eval "set -- $( 242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 243 | xargs -n1 | 244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 245 | tr '\n' ' ' 246 | )" '"$@"' 247 | 248 | exec "$JAVACMD" "$@" 249 | -------------------------------------------------------------------------------- /testobserve/native/src/main/resources/META-INF/native-image/org.ballerina/testobserve/reflect-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "io.netty.bootstrap.ServerBootstrap$1" 4 | }, 5 | { 6 | "name": "io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor", 7 | "methods": [ 8 | { 9 | "name": "channelRead", 10 | "parameterTypes": [ 11 | "io.netty.channel.ChannelHandlerContext", 12 | "java.lang.Object" 13 | ] 14 | }, 15 | { 16 | "name": "exceptionCaught", 17 | "parameterTypes": [ 18 | "io.netty.channel.ChannelHandlerContext", 19 | "java.lang.Throwable" 20 | ] 21 | } 22 | ] 23 | }, 24 | { 25 | "name": "io.netty.buffer.AbstractByteBufAllocator", 26 | "queryAllDeclaredMethods": true 27 | }, 28 | { 29 | "name": "io.netty.channel.ChannelInboundHandlerAdapter", 30 | "methods": [ 31 | { 32 | "name": "channelActive", 33 | "parameterTypes": [ 34 | "io.netty.channel.ChannelHandlerContext" 35 | ] 36 | }, 37 | { 38 | "name": "channelInactive", 39 | "parameterTypes": [ 40 | "io.netty.channel.ChannelHandlerContext" 41 | ] 42 | }, 43 | { 44 | "name": "channelRead", 45 | "parameterTypes": [ 46 | "io.netty.channel.ChannelHandlerContext", 47 | "java.lang.Object" 48 | ] 49 | }, 50 | { 51 | "name": "channelReadComplete", 52 | "parameterTypes": [ 53 | "io.netty.channel.ChannelHandlerContext" 54 | ] 55 | }, 56 | { 57 | "name": "channelRegistered", 58 | "parameterTypes": [ 59 | "io.netty.channel.ChannelHandlerContext" 60 | ] 61 | }, 62 | { 63 | "name": "channelUnregistered", 64 | "parameterTypes": [ 65 | "io.netty.channel.ChannelHandlerContext" 66 | ] 67 | }, 68 | { 69 | "name": "channelWritabilityChanged", 70 | "parameterTypes": [ 71 | "io.netty.channel.ChannelHandlerContext" 72 | ] 73 | }, 74 | { 75 | "name": "userEventTriggered", 76 | "parameterTypes": [ 77 | "io.netty.channel.ChannelHandlerContext", 78 | "java.lang.Object" 79 | ] 80 | } 81 | ] 82 | }, 83 | { 84 | "name": "io.netty.channel.ChannelInitializer", 85 | "methods": [ 86 | { 87 | "name": "channelRegistered", 88 | "parameterTypes": [ 89 | "io.netty.channel.ChannelHandlerContext" 90 | ] 91 | }, 92 | { 93 | "name": "exceptionCaught", 94 | "parameterTypes": [ 95 | "io.netty.channel.ChannelHandlerContext", 96 | "java.lang.Throwable" 97 | ] 98 | } 99 | ] 100 | }, 101 | { 102 | "name": "io.netty.channel.DefaultChannelPipeline$HeadContext", 103 | "methods": [ 104 | { 105 | "name": "bind", 106 | "parameterTypes": [ 107 | "io.netty.channel.ChannelHandlerContext", 108 | "java.net.SocketAddress", 109 | "io.netty.channel.ChannelPromise" 110 | ] 111 | }, 112 | { 113 | "name": "channelActive", 114 | "parameterTypes": [ 115 | "io.netty.channel.ChannelHandlerContext" 116 | ] 117 | }, 118 | { 119 | "name": "channelInactive", 120 | "parameterTypes": [ 121 | "io.netty.channel.ChannelHandlerContext" 122 | ] 123 | }, 124 | { 125 | "name": "channelRead", 126 | "parameterTypes": [ 127 | "io.netty.channel.ChannelHandlerContext", 128 | "java.lang.Object" 129 | ] 130 | }, 131 | { 132 | "name": "channelReadComplete", 133 | "parameterTypes": [ 134 | "io.netty.channel.ChannelHandlerContext" 135 | ] 136 | }, 137 | { 138 | "name": "channelRegistered", 139 | "parameterTypes": [ 140 | "io.netty.channel.ChannelHandlerContext" 141 | ] 142 | }, 143 | { 144 | "name": "channelUnregistered", 145 | "parameterTypes": [ 146 | "io.netty.channel.ChannelHandlerContext" 147 | ] 148 | }, 149 | { 150 | "name": "channelWritabilityChanged", 151 | "parameterTypes": [ 152 | "io.netty.channel.ChannelHandlerContext" 153 | ] 154 | }, 155 | { 156 | "name": "close", 157 | "parameterTypes": [ 158 | "io.netty.channel.ChannelHandlerContext", 159 | "io.netty.channel.ChannelPromise" 160 | ] 161 | }, 162 | { 163 | "name": "connect", 164 | "parameterTypes": [ 165 | "io.netty.channel.ChannelHandlerContext", 166 | "java.net.SocketAddress", 167 | "java.net.SocketAddress", 168 | "io.netty.channel.ChannelPromise" 169 | ] 170 | }, 171 | { 172 | "name": "deregister", 173 | "parameterTypes": [ 174 | "io.netty.channel.ChannelHandlerContext", 175 | "io.netty.channel.ChannelPromise" 176 | ] 177 | }, 178 | { 179 | "name": "disconnect", 180 | "parameterTypes": [ 181 | "io.netty.channel.ChannelHandlerContext", 182 | "io.netty.channel.ChannelPromise" 183 | ] 184 | }, 185 | { 186 | "name": "exceptionCaught", 187 | "parameterTypes": [ 188 | "io.netty.channel.ChannelHandlerContext", 189 | "java.lang.Throwable" 190 | ] 191 | }, 192 | { 193 | "name": "flush", 194 | "parameterTypes": [ 195 | "io.netty.channel.ChannelHandlerContext" 196 | ] 197 | }, 198 | { 199 | "name": "read", 200 | "parameterTypes": [ 201 | "io.netty.channel.ChannelHandlerContext" 202 | ] 203 | }, 204 | { 205 | "name": "userEventTriggered", 206 | "parameterTypes": [ 207 | "io.netty.channel.ChannelHandlerContext", 208 | "java.lang.Object" 209 | ] 210 | }, 211 | { 212 | "name": "write", 213 | "parameterTypes": [ 214 | "io.netty.channel.ChannelHandlerContext", 215 | "java.lang.Object", 216 | "io.netty.channel.ChannelPromise" 217 | ] 218 | } 219 | ] 220 | }, 221 | { 222 | "name": "io.netty.channel.DefaultChannelPipeline$TailContext", 223 | "methods": [ 224 | { 225 | "name": "channelActive", 226 | "parameterTypes": [ 227 | "io.netty.channel.ChannelHandlerContext" 228 | ] 229 | }, 230 | { 231 | "name": "channelInactive", 232 | "parameterTypes": [ 233 | "io.netty.channel.ChannelHandlerContext" 234 | ] 235 | }, 236 | { 237 | "name": "channelRead", 238 | "parameterTypes": [ 239 | "io.netty.channel.ChannelHandlerContext", 240 | "java.lang.Object" 241 | ] 242 | }, 243 | { 244 | "name": "channelReadComplete", 245 | "parameterTypes": [ 246 | "io.netty.channel.ChannelHandlerContext" 247 | ] 248 | }, 249 | { 250 | "name": "channelRegistered", 251 | "parameterTypes": [ 252 | "io.netty.channel.ChannelHandlerContext" 253 | ] 254 | }, 255 | { 256 | "name": "channelUnregistered", 257 | "parameterTypes": [ 258 | "io.netty.channel.ChannelHandlerContext" 259 | ] 260 | }, 261 | { 262 | "name": "channelWritabilityChanged", 263 | "parameterTypes": [ 264 | "io.netty.channel.ChannelHandlerContext" 265 | ] 266 | }, 267 | { 268 | "name": "exceptionCaught", 269 | "parameterTypes": [ 270 | "io.netty.channel.ChannelHandlerContext", 271 | "java.lang.Throwable" 272 | ] 273 | }, 274 | { 275 | "name": "userEventTriggered", 276 | "parameterTypes": [ 277 | "io.netty.channel.ChannelHandlerContext", 278 | "java.lang.Object" 279 | ] 280 | } 281 | ] 282 | }, 283 | { 284 | "name": "io.netty.channel.socket.nio.NioServerSocketChannel", 285 | "methods": [ 286 | { 287 | "name": "", 288 | "parameterTypes": [] 289 | } 290 | ] 291 | }, 292 | { 293 | "name": "io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields", 294 | "fields": [ 295 | { 296 | "name": "producerLimit" 297 | } 298 | ] 299 | }, 300 | { 301 | "name": "io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields", 302 | "fields": [ 303 | { 304 | "name": "consumerIndex" 305 | } 306 | ] 307 | }, 308 | { 309 | "name": "io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields", 310 | "fields": [ 311 | { 312 | "name": "producerIndex" 313 | } 314 | ] 315 | } 316 | ] 317 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /integration-tests/src/test/java/io/ballerina/stdlib/observability/tracing/ObservableAnnotationTestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package io.ballerina.stdlib.observability.tracing; 20 | 21 | import io.ballerina.stdlib.observability.tracing.TracingBaseTestCase; 22 | import io.ballerina.stdlib.observe.mockextension.BMockSpan; 23 | import org.ballerinalang.test.util.HttpClientRequest; 24 | import org.ballerinalang.test.util.HttpResponse; 25 | import org.testng.Assert; 26 | import org.testng.annotations.Test; 27 | 28 | import java.util.AbstractMap; 29 | import java.util.Arrays; 30 | import java.util.Collections; 31 | import java.util.HashSet; 32 | import java.util.List; 33 | import java.util.Objects; 34 | import java.util.Optional; 35 | import java.util.stream.Collectors; 36 | 37 | /** 38 | * Test cases for observe:Observable annotation. 39 | */ 40 | @Test(groups = "tracing-test") 41 | public class ObservableAnnotationTestCase extends TracingBaseTestCase { 42 | private static final String FILE_NAME = "04_observability_annotation.bal"; 43 | private static final String SERVICE_NAME = "testSvcFour"; 44 | private static final String BASE_PATH = "testServiceFour"; 45 | private static final String BASE_URL = "http://localhost:19094"; 46 | 47 | @Test 48 | public void testObservableFunction() throws Exception { 49 | final String resourceName = "resourceOne"; 50 | final String resourceFunctionPosition = FILE_NAME + ":23:5"; 51 | final String span2Position = FILE_NAME + ":24:19"; 52 | final String span3Position = FILE_NAME + ":29:20"; 53 | 54 | HttpResponse httpResponse = HttpClientRequest.doPost(BASE_URL + "/" + BASE_PATH + "/" + resourceName, 55 | "", Collections.emptyMap()); 56 | Assert.assertEquals(httpResponse.getResponseCode(), 200); 57 | Assert.assertEquals(httpResponse.getData(), "Invocation Successful"); 58 | Thread.sleep(1000); 59 | 60 | List spans = this.getFinishedSpans(SERVICE_NAME, DEFAULT_MODULE_ID, "/" + resourceName); 61 | Assert.assertEquals(spans.stream() 62 | .map(span -> span.getTags().get("src.position")) 63 | .collect(Collectors.toSet()), 64 | new HashSet<>(Arrays.asList(resourceFunctionPosition, span2Position, span3Position))); 65 | Assert.assertEquals(spans.stream().filter(bMockSpan -> bMockSpan.getParentId().equals(ZERO_SPAN_ID)) 66 | .count(), 1); 67 | 68 | Optional span1 = spans.stream() 69 | .filter(bMockSpan -> Objects.equals(bMockSpan.getTags().get("src.position"), resourceFunctionPosition)) 70 | .findFirst(); 71 | Assert.assertTrue(span1.isPresent()); 72 | String traceId = span1.get().getTraceId(); 73 | span1.ifPresent(span -> { 74 | Assert.assertTrue(spans.stream().noneMatch(mockSpan -> mockSpan.getTraceId().equals(traceId) 75 | && mockSpan.getSpanId().equals(span.getParentId()))); 76 | Assert.assertEquals(span.getOperationName(), "post /" + resourceName); 77 | Assert.assertEquals(span.getTags(), toMap( 78 | new AbstractMap.SimpleEntry<>("span.kind", "server"), 79 | new AbstractMap.SimpleEntry<>("src.module", DEFAULT_MODULE_ID), 80 | new AbstractMap.SimpleEntry<>("src.position", resourceFunctionPosition), 81 | new AbstractMap.SimpleEntry<>("src.service.resource", "true"), 82 | new AbstractMap.SimpleEntry<>("http.url", "/" + BASE_PATH + "/" + resourceName), 83 | new AbstractMap.SimpleEntry<>("http.method", "POST"), 84 | new AbstractMap.SimpleEntry<>("protocol", "http"), 85 | new AbstractMap.SimpleEntry<>("entrypoint.function.module", DEFAULT_MODULE_ID), 86 | new AbstractMap.SimpleEntry<>("entrypoint.service.name", SERVICE_NAME), 87 | new AbstractMap.SimpleEntry<>("entrypoint.function.name", "/" + resourceName), 88 | new AbstractMap.SimpleEntry<>("entrypoint.resource.accessor", "post"), 89 | new AbstractMap.SimpleEntry<>("src.object.name", SERVICE_NAME), 90 | new AbstractMap.SimpleEntry<>("listener.name", SERVER_CONNECTOR_NAME), 91 | new AbstractMap.SimpleEntry<>("src.resource.accessor", "post"), 92 | new AbstractMap.SimpleEntry<>("src.resource.path", "/" + resourceName) 93 | )); 94 | }); 95 | 96 | Optional span2 = spans.stream() 97 | .filter(bMockSpan -> Objects.equals(bMockSpan.getTags().get("src.position"), span2Position)) 98 | .findFirst(); 99 | Assert.assertTrue(span2.isPresent()); 100 | span2.ifPresent(span -> { 101 | Assert.assertEquals(span.getTraceId(), traceId); 102 | Assert.assertEquals(span.getParentId(), span1.get().getSpanId()); 103 | Assert.assertEquals(span.getOperationName(), "calculateSumWithObservability"); 104 | Assert.assertEquals(span.getTags(), toMap( 105 | new AbstractMap.SimpleEntry<>("span.kind", "client"), 106 | new AbstractMap.SimpleEntry<>("src.module", DEFAULT_MODULE_ID), 107 | new AbstractMap.SimpleEntry<>("src.position", span2Position), 108 | new AbstractMap.SimpleEntry<>("entrypoint.function.module", DEFAULT_MODULE_ID), 109 | new AbstractMap.SimpleEntry<>("entrypoint.service.name", SERVICE_NAME), 110 | new AbstractMap.SimpleEntry<>("entrypoint.function.name", "/" + resourceName), 111 | new AbstractMap.SimpleEntry<>("entrypoint.resource.accessor", "post"), 112 | new AbstractMap.SimpleEntry<>("src.function.name", "calculateSumWithObservability") 113 | )); 114 | }); 115 | 116 | Optional span3 = spans.stream() 117 | .filter(bMockSpan -> Objects.equals(bMockSpan.getTags().get("src.position"), span3Position)) 118 | .findFirst(); 119 | Assert.assertTrue(span3.isPresent()); 120 | span3.ifPresent(span -> { 121 | Assert.assertEquals(span.getTraceId(), traceId); 122 | Assert.assertEquals(span.getParentId(), span1.get().getSpanId()); 123 | Assert.assertEquals(span.getOperationName(), "ballerina/testobserve/Caller:respond"); 124 | Assert.assertEquals(span.getTags(), toMap( 125 | new AbstractMap.SimpleEntry<>("span.kind", "client"), 126 | new AbstractMap.SimpleEntry<>("src.module", DEFAULT_MODULE_ID), 127 | new AbstractMap.SimpleEntry<>("src.position", span3Position), 128 | new AbstractMap.SimpleEntry<>("src.client.remote", "true"), 129 | new AbstractMap.SimpleEntry<>("entrypoint.function.module", DEFAULT_MODULE_ID), 130 | new AbstractMap.SimpleEntry<>("entrypoint.service.name", SERVICE_NAME), 131 | new AbstractMap.SimpleEntry<>("entrypoint.function.name", "/" + resourceName), 132 | new AbstractMap.SimpleEntry<>("entrypoint.resource.accessor", "post"), 133 | new AbstractMap.SimpleEntry<>("src.object.name", "ballerina/testobserve/Caller"), 134 | new AbstractMap.SimpleEntry<>("src.function.name", "respond") 135 | )); 136 | }); 137 | } 138 | 139 | @Test 140 | public void testObservableAttachedFunction() throws Exception { 141 | final String resourceName = "resourceTwo"; 142 | final String resourceFunctionPosition = FILE_NAME + ":33:5"; 143 | final String span2Position = FILE_NAME + ":35:19"; 144 | final String span3Position = FILE_NAME + ":40:20"; 145 | 146 | HttpResponse httpResponse = HttpClientRequest.doPost(BASE_URL + "/" + BASE_PATH + "/" + resourceName, 147 | "", Collections.emptyMap()); 148 | Assert.assertEquals(httpResponse.getResponseCode(), 200); 149 | Assert.assertEquals(httpResponse.getData(), "Invocation Successful"); 150 | Thread.sleep(1000); 151 | 152 | List spans = this.getFinishedSpans(SERVICE_NAME, DEFAULT_MODULE_ID, "/" + resourceName); 153 | Assert.assertEquals(spans.stream() 154 | .map(span -> span.getTags().get("src.position")) 155 | .collect(Collectors.toSet()), 156 | new HashSet<>(Arrays.asList(resourceFunctionPosition, span2Position, span3Position))); 157 | Assert.assertEquals(spans.stream().filter(bMockSpan -> bMockSpan.getParentId().equals(ZERO_SPAN_ID)) 158 | .count(), 1); 159 | 160 | Optional span1 = spans.stream() 161 | .filter(bMockSpan -> Objects.equals(bMockSpan.getTags().get("src.position"), resourceFunctionPosition)) 162 | .findFirst(); 163 | Assert.assertTrue(span1.isPresent()); 164 | String traceId = span1.get().getTraceId(); 165 | span1.ifPresent(span -> { 166 | Assert.assertTrue(spans.stream().noneMatch(mockSpan -> mockSpan.getTraceId().equals(traceId) 167 | && mockSpan.getSpanId().equals(span.getParentId()))); 168 | Assert.assertEquals(span.getOperationName(), "post /" + resourceName); 169 | Assert.assertEquals(span.getTags(), toMap( 170 | new AbstractMap.SimpleEntry<>("span.kind", "server"), 171 | new AbstractMap.SimpleEntry<>("src.module", DEFAULT_MODULE_ID), 172 | new AbstractMap.SimpleEntry<>("src.position", resourceFunctionPosition), 173 | new AbstractMap.SimpleEntry<>("src.service.resource", "true"), 174 | new AbstractMap.SimpleEntry<>("http.url", "/" + BASE_PATH + "/" + resourceName), 175 | new AbstractMap.SimpleEntry<>("http.method", "POST"), 176 | new AbstractMap.SimpleEntry<>("protocol", "http"), 177 | new AbstractMap.SimpleEntry<>("entrypoint.function.module", DEFAULT_MODULE_ID), 178 | new AbstractMap.SimpleEntry<>("entrypoint.service.name", SERVICE_NAME), 179 | new AbstractMap.SimpleEntry<>("entrypoint.function.name", "/" + resourceName), 180 | new AbstractMap.SimpleEntry<>("entrypoint.resource.accessor", "post"), 181 | new AbstractMap.SimpleEntry<>("src.object.name", SERVICE_NAME), 182 | new AbstractMap.SimpleEntry<>("listener.name", SERVER_CONNECTOR_NAME), 183 | new AbstractMap.SimpleEntry<>("src.resource.accessor", "post"), 184 | new AbstractMap.SimpleEntry<>("src.resource.path", "/" + resourceName) 185 | )); 186 | }); 187 | 188 | Optional span2 = spans.stream() 189 | .filter(bMockSpan -> Objects.equals(bMockSpan.getTags().get("src.position"), span2Position)) 190 | .findFirst(); 191 | Assert.assertTrue(span2.isPresent()); 192 | span2.ifPresent(span -> { 193 | Assert.assertEquals(span.getTraceId(), traceId); 194 | Assert.assertEquals(span.getParentId(), span1.get().getSpanId()); 195 | Assert.assertEquals(span.getOperationName(), OBSERVABLE_ADDER_OBJECT_NAME + ":getSum"); 196 | Assert.assertEquals(span.getTags(), toMap( 197 | new AbstractMap.SimpleEntry<>("span.kind", "client"), 198 | new AbstractMap.SimpleEntry<>("src.module", DEFAULT_MODULE_ID), 199 | new AbstractMap.SimpleEntry<>("src.position", span2Position), 200 | new AbstractMap.SimpleEntry<>("entrypoint.function.module", DEFAULT_MODULE_ID), 201 | new AbstractMap.SimpleEntry<>("entrypoint.service.name", SERVICE_NAME), 202 | new AbstractMap.SimpleEntry<>("entrypoint.function.name", "/" + resourceName), 203 | new AbstractMap.SimpleEntry<>("entrypoint.resource.accessor", "post"), 204 | new AbstractMap.SimpleEntry<>("src.object.name", OBSERVABLE_ADDER_OBJECT_NAME), 205 | new AbstractMap.SimpleEntry<>("src.function.name", "getSum") 206 | )); 207 | }); 208 | 209 | Optional span3 = spans.stream() 210 | .filter(bMockSpan -> Objects.equals(bMockSpan.getTags().get("src.position"), span3Position)) 211 | .findFirst(); 212 | Assert.assertTrue(span3.isPresent()); 213 | span3.ifPresent(span -> { 214 | Assert.assertEquals(span.getTraceId(), traceId); 215 | Assert.assertEquals(span.getParentId(), span1.get().getSpanId()); 216 | Assert.assertEquals(span.getOperationName(), "ballerina/testobserve/Caller:respond"); 217 | Assert.assertEquals(span.getTags(), toMap( 218 | new AbstractMap.SimpleEntry<>("span.kind", "client"), 219 | new AbstractMap.SimpleEntry<>("src.module", DEFAULT_MODULE_ID), 220 | new AbstractMap.SimpleEntry<>("src.position", span3Position), 221 | new AbstractMap.SimpleEntry<>("src.client.remote", "true"), 222 | new AbstractMap.SimpleEntry<>("entrypoint.function.module", DEFAULT_MODULE_ID), 223 | new AbstractMap.SimpleEntry<>("entrypoint.service.name", SERVICE_NAME), 224 | new AbstractMap.SimpleEntry<>("entrypoint.function.name", "/" + resourceName), 225 | new AbstractMap.SimpleEntry<>("entrypoint.resource.accessor", "post"), 226 | new AbstractMap.SimpleEntry<>("src.object.name", "ballerina/testobserve/Caller"), 227 | new AbstractMap.SimpleEntry<>("src.function.name", "respond") 228 | )); 229 | }); 230 | } 231 | } 232 | --------------------------------------------------------------------------------