Skip to content

Commit

Permalink
fix: add runtime hints for logging (#1933)
Browse files Browse the repository at this point in the history
This pr adds:
- runtime hints for springframework aot to allow Logging module work with Native build.
- a relevant unit test
- runtime hints needed for sample integration test. (this is test setup specific and is not needed to run sample application).

### CI for native is setup to run on sample integration tests that:
- has `it.xx` property added to [systemPropertyVariables](https://github.com/GoogleCloudPlatform/spring-cloud-gcp/blob/f11fde736c783c4334ae50cb31e8ccf879738daa/spring-cloud-gcp-samples/pom.xml#L133) 
- module added to [native-sample-config](https://github.com/GoogleCloudPlatform/spring-cloud-gcp/blob/f11fde736c783c4334ae50cb31e8ccf879738daa/spring-cloud-gcp-samples/pom.xml#L92C2-L92C2) profile.

Modification to `spring-cloud-gcp-samples/pom.xml` so that:
- by default, all modules are included.
- To run native test only for selected modules, use `--define notAllModules=true` in mvn cmd.

This is needed because native test would fail on no test configuration found.

### To verify locally:
- Run the sample manually:
   - Follow guide in README, instead of `mvn spring-boot:run`, package with `mvn clean package -Pnative-sample-config -Pnative`
   - Run executable `./target/spring-cloud-gcp-logging-sample `
   - Follow guide to issue a request and observe log lines from cloud console.
   - Log entry written by sample:
```
{
insertId: "1c08xcxf1v07m9"
jsonPayload: {
message: "This line was also written to the log with the same Trace ID."
}
labels: {3}
logName: "projects/[project-id]/logs/spring.log"
receiveTimestamp: "2023-06-06T14:15:08.236653291Z"
resource: {2}
severity: "INFO"
timestamp: "2023-06-06T14:15:08.122Z"
trace: "projects/[project-id]/traces/trace-id-to-show-1234"
}
``` 
- Run sample test:
    - `mvn -PnativeTest clean test -Pnative-sample-config`
    - native test are run at the end of the build.
```
com.example.LoggingSampleApplicationIntegrationTests > testLogRecordedInStackDriver() SUCCESSFUL


Test run finished after 12975 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         1 tests found           ]
[         0 tests skipped         ]
[         1 tests started         ]
[         0 tests aborted         ]
[         1 tests successful      ]
[         0 tests failed          ]

```
  • Loading branch information
zhumin8 authored Jul 6, 2023
1 parent 186a150 commit 21903af
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 35 deletions.
116 changes: 116 additions & 0 deletions .github/workflows/NativeTests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: NativeTests

on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- 'spring-cloud-generator/**'
- 'spring-cloud-previews/**'
workflow_dispatch:


jobs:
NativeTests:
if: |
github.actor != 'dependabot[bot]' && ((
github.event_name == 'pull_request' && github.repository == github.event.pull_request.head.repo.full_name
) || (github.event_name != 'pull_request'))
runs-on: ubuntu-20.04
strategy:
fail-fast: false
steps:
- name: Get current date
id: date
run: echo "date=$(date +'%Y-%m-%d' --utc)" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
java-version: 17
distribution: 'temurin'
- name: Set Up Authentication
uses: google-github-actions/auth@v1
with:
credentials_json: ${{ secrets.SPRING_CLOUD_GCP_CI_NATIVE_SA_KEY }}
- name: Setup gcloud
uses: google-github-actions/setup-gcloud@v1
with:
project_id: spring-cloud-gcp-ci-native
- name: Maven go offline
id: mvn-offline
if: steps.mvn-cache.outputs.cache-hit != 'true'
run: ./mvnw compile dependency:go-offline
- name: Mvn install # Need this when the directory/pom structure changes
id: install1
continue-on-error: true
run: |
./mvnw \
--batch-mode \
--threads 1.5C \
--define maven.test.skip=true \
--define maven.javadoc.skip=true \
install
- name: Retry install on failure
id: install2
if: steps.install1.outcome == 'failure'
run: |
./mvnw \
--batch-mode \
--threads 1.5C \
--define maven.test.skip=true \
--define maven.javadoc.skip=true \
install
- name: Wait our turn for running native tests
uses: softprops/turnstyle@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
continue-after-seconds: 3600 # 60 min
same-branch-only: false
- uses: graalvm/setup-graalvm@v1
with:
version: '22.3.0'
java-version: '17'
components: 'native-image'
github-token: ${{ secrets.GITHUB_TOKEN }}
native-image-job-reports: 'true'
- name: Native Tests in Samples
id: intTest1
continue-on-error: true
run: |
../mvnw \
--batch-mode \
--activate-profiles native-sample-config,nativeTest \
--define notAllModules=true \
--define maven.javadoc.skip=true \
test
working-directory: ./spring-cloud-gcp-samples
- name: Retry on Failure
id: intTest2
if: steps.intTest1.outcome == 'failure'
run: |
../mvnw \
--batch-mode \
--activate-profiles native-sample-config,nativeTest \
--define notAllModules=true \
--define maven.javadoc.skip=true \
test
working-directory: ./spring-cloud-gcp-samples
- name: Aggregate Report
run: |
../mvnw \
--batch-mode \
--define aggregate=true \
surefire-report:report-only
working-directory: ./spring-cloud-gcp-samples
- name: Archive logs
if: always()
continue-on-error: true
uses: actions/upload-artifact@v3
with:
name: Native Test Logs
path: |
**/target/failsafe-reports/*
**/target/site
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.spring.logging.aot;

import com.google.cloud.spring.logging.LoggingAppender;
import com.google.cloud.spring.logging.StackdriverJsonLayout;
import com.google.cloud.spring.logging.TraceIdLoggingEnhancer;
import java.util.Arrays;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;

public class LoggingRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints
.reflection()
.registerTypes(
Arrays.asList(
TypeReference.of(LoggingAppender.class),
TypeReference.of(TraceIdLoggingEnhancer.class),
TypeReference.of(StackdriverJsonLayout.class)),
hint ->
hint.withMembers(
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
com.google.cloud.spring.logging.aot.LoggingRuntimeHints
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.spring.logging.aot;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection;

import com.google.cloud.spring.logging.LoggingAppender;
import com.google.cloud.spring.logging.StackdriverJsonLayout;
import com.google.cloud.spring.logging.TraceIdLoggingEnhancer;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;

class LoggingRuntimeHintsTest {

@Test
void shouldRegisterHints() {
RuntimeHints hints = new RuntimeHints();
new LoggingRuntimeHints().registerHints(hints, getClass().getClassLoader());

assertThat(hints)
.matches(reflection().onType(LoggingAppender.class))
.matches(reflection().onType(TraceIdLoggingEnhancer.class))
.matches(reflection().onType(StackdriverJsonLayout.class));
}
}
80 changes: 45 additions & 35 deletions spring-cloud-gcp-samples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,44 +42,55 @@
<sonar.skip>true</sonar.skip>
</properties>

<modules>
<module>spring-cloud-gcp-config-sample</module>
<module>spring-cloud-gcp-trace-sample</module>
<module>spring-cloud-gcp-logging-sample</module>
<module>spring-cloud-gcp-sql-mysql-sample</module>
<module>spring-cloud-gcp-integration-pubsub-sample</module>
<module>spring-cloud-gcp-storage-resource-sample</module>
<module>spring-cloud-gcp-data-spanner-repository-sample</module>
<module>spring-cloud-gcp-data-spanner-template-sample</module>
<module>spring-cloud-gcp-data-datastore-sample</module>
<module>spring-cloud-gcp-data-datastore-basic-sample</module>
<module>spring-cloud-gcp-pubsub-sample</module>
<module>spring-cloud-gcp-data-jpa-sample</module>
<module>spring-cloud-gcp-vision-api-sample</module>
<module>spring-cloud-gcp-integration-storage-sample</module>
<module>spring-cloud-gcp-pubsub-bus-config-sample</module>
<module>spring-cloud-gcp-pubsub-stream-dead-letter-sample</module>
<module>spring-cloud-gcp-pubsub-stream-functional-sample</module>
<module>spring-cloud-gcp-pubsub-reactive-sample</module>
<module>spring-cloud-gcp-integration-pubsub-json-sample</module>
<module>spring-cloud-gcp-security-iap-sample</module>
<module>spring-cloud-gcp-sql-postgres-sample</module>
<module>spring-cloud-gcp-sql-postgres-r2dbc-sample</module>
<module>spring-cloud-gcp-vision-ocr-demo</module>
<module>spring-cloud-gcp-firestore-sample</module>
<module>spring-cloud-gcp-data-multi-sample</module>
<module>spring-cloud-gcp-data-firestore-sample</module>
<module>spring-cloud-gcp-bigquery-sample</module>
<module>spring-cloud-gcp-security-firebase-sample</module>
<module>spring-cloud-gcp-secretmanager-sample</module>
<module>spring-cloud-gcp-kotlin-samples</module>
<module>spring-cloud-gcp-metrics-sample</module>
<module>spring-cloud-gcp-kms-sample</module>
</modules>

<profiles>
<profile>
<id>default</id>
<activation>
<property>
<name>!notAllModules</name>
</property>
</activation>
<modules>
<module>spring-cloud-gcp-config-sample</module>
<module>spring-cloud-gcp-trace-sample</module>
<module>spring-cloud-gcp-logging-sample</module>
<module>spring-cloud-gcp-sql-mysql-sample</module>
<module>spring-cloud-gcp-integration-pubsub-sample</module>
<module>spring-cloud-gcp-storage-resource-sample</module>
<module>spring-cloud-gcp-data-spanner-repository-sample</module>
<module>spring-cloud-gcp-data-spanner-template-sample</module>
<module>spring-cloud-gcp-data-datastore-sample</module>
<module>spring-cloud-gcp-data-datastore-basic-sample</module>
<module>spring-cloud-gcp-pubsub-sample</module>
<module>spring-cloud-gcp-data-jpa-sample</module>
<module>spring-cloud-gcp-vision-api-sample</module>
<module>spring-cloud-gcp-integration-storage-sample</module>
<module>spring-cloud-gcp-pubsub-bus-config-sample</module>
<module>spring-cloud-gcp-pubsub-stream-dead-letter-sample</module>
<module>spring-cloud-gcp-pubsub-stream-functional-sample</module>
<module>spring-cloud-gcp-pubsub-reactive-sample</module>
<module>spring-cloud-gcp-integration-pubsub-json-sample</module>
<module>spring-cloud-gcp-security-iap-sample</module>
<module>spring-cloud-gcp-sql-postgres-sample</module>
<module>spring-cloud-gcp-sql-postgres-r2dbc-sample</module>
<module>spring-cloud-gcp-vision-ocr-demo</module>
<module>spring-cloud-gcp-firestore-sample</module>
<module>spring-cloud-gcp-data-multi-sample</module>
<module>spring-cloud-gcp-data-firestore-sample</module>
<module>spring-cloud-gcp-bigquery-sample</module>
<module>spring-cloud-gcp-security-firebase-sample</module>
<module>spring-cloud-gcp-secretmanager-sample</module>
<module>spring-cloud-gcp-kotlin-samples</module>
<module>spring-cloud-gcp-metrics-sample</module>
<module>spring-cloud-gcp-kms-sample</module>
</modules>
</profile>
<profile>
<id>native-sample-config</id>
<modules>
<module>spring-cloud-gcp-logging-sample</module>
</modules>
<build>
<plugins>
<plugin>
Expand Down Expand Up @@ -168,7 +179,6 @@
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.9.23</version>
<extensions>true</extensions>
</plugin>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
Expand All @@ -52,6 +53,7 @@
@SpringBootTest(
webEnvironment = WebEnvironment.RANDOM_PORT,
classes = {Application.class})
@ImportRuntimeHints(TestRuntimeHints.class)
class LoggingSampleApplicationIntegrationTests {

private static final String LOG_FILTER_FORMAT =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example;

import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.ConsoleAppender;
import java.util.Arrays;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;

/** Runtime Hints for GraalVM Native Images * */
public class TestRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(
org.springframework.aot.hint.RuntimeHints hints, ClassLoader classLoader) {
hints
.reflection()
.registerTypes(
Arrays.asList(
TypeReference.of(ConsoleAppender.class),
TypeReference.of(PatternLayoutEncoder.class)),
hint ->
hint.withMembers(
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS));
hints.resources().registerPattern("logback-test.xml");
hints.resources().registerPattern("com/google/cloud/spring/logging/logback-appender.xml");
}
}

0 comments on commit 21903af

Please sign in to comment.