diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index a79f06271..68f2b159d 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-java:latest - digest: sha256:2567a120ce90fadb6201999b87d649d9f67459de28815ad239bce9ebfaa18a74 -# created: 2022-05-19T15:12:45.278246753Z + digest: sha256:58ccd4737212f64a7dd4b3063d447447acf71a2b9d409eab19fc7a00b18eadc0 +# created: 2022-06-10T19:20:11.004014696Z diff --git a/CHANGELOG.md b/CHANGELOG.md index a6f324103..96fc9d96d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## [3.10.0](https://github.com/googleapis/java-logging/compare/v3.9.0...v3.10.0) (2022-06-25) + + +### Features + +* Add support for library instrumentation ([#979](https://github.com/googleapis/java-logging/issues/979)) ([2749974](https://github.com/googleapis/java-logging/commit/27499744f37a5fddcc2d6825c69481374e78829c)) + + +### Documentation + +* **sample:** update README for native image sample ([#974](https://github.com/googleapis/java-logging/issues/974)) ([1512487](https://github.com/googleapis/java-logging/commit/1512487e60141ed5c61a3a60fcca29f52f4ec141)) + + +### Dependencies + +* update dependency com.google.cloud:google-cloud-shared-dependencies to v2.13.0 ([#980](https://github.com/googleapis/java-logging/issues/980)) ([18acf1f](https://github.com/googleapis/java-logging/commit/18acf1f64d836ca3fb1b8f4b558ef21d728c391f)) +* update dependency org.graalvm.buildtools:junit-platform-native to v0.9.12 ([#976](https://github.com/googleapis/java-logging/issues/976)) ([01d3213](https://github.com/googleapis/java-logging/commit/01d3213b9e010c3ae3843e5a05bbd01b2961b454)) + ## [3.9.0](https://github.com/googleapis/java-logging/compare/v3.8.0...v3.9.0) (2022-05-19) diff --git a/README.md b/README.md index 45f7872c3..46021884c 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file com.google.cloud libraries-bom - 25.3.0 + 25.4.0 pom import @@ -43,7 +43,7 @@ If you are using Maven without BOM, add this to your dependencies: com.google.cloud google-cloud-logging - 3.7.6 + 3.8.0 ``` @@ -51,20 +51,20 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies ```Groovy -implementation platform('com.google.cloud:libraries-bom:25.3.0') +implementation platform('com.google.cloud:libraries-bom:25.4.0') implementation 'com.google.cloud:google-cloud-logging' ``` If you are using Gradle without BOM, add this to your dependencies ```Groovy -implementation 'com.google.cloud:google-cloud-logging:3.8.0' +implementation 'com.google.cloud:google-cloud-logging:3.9.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-logging" % "3.8.0" +libraryDependencies += "com.google.cloud" % "google-cloud-logging" % "3.9.0" ``` ## Authentication diff --git a/google-cloud-logging-bom/pom.xml b/google-cloud-logging-bom/pom.xml index d88d9de74..af6ba5253 100644 --- a/google-cloud-logging-bom/pom.xml +++ b/google-cloud-logging-bom/pom.xml @@ -3,12 +3,12 @@ 4.0.0 com.google.cloud google-cloud-logging-bom - 3.9.0 + 3.10.0 pom com.google.cloud google-cloud-shared-config - 1.4.0 + 1.5.0 Google Cloud logging BOM @@ -53,17 +53,17 @@ com.google.cloud google-cloud-logging - 3.9.0 + 3.10.0 com.google.api.grpc grpc-google-cloud-logging-v2 - 0.98.0 + 0.99.0 com.google.api.grpc proto-google-cloud-logging-v2 - 0.98.0 + 0.99.0 diff --git a/google-cloud-logging/pom.xml b/google-cloud-logging/pom.xml index b4fa79a91..223f5f1fd 100644 --- a/google-cloud-logging/pom.xml +++ b/google-cloud-logging/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-logging - 3.9.0 + 3.10.0 jar Google Cloud Logging https://github.com/googleapis/java-logging @@ -11,7 +11,7 @@ com.google.cloud google-cloud-logging-parent - 3.9.0 + 3.10.0 google-cloud-logging diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/Instrumentation.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/Instrumentation.java new file mode 100644 index 000000000..8471d882e --- /dev/null +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/Instrumentation.java @@ -0,0 +1,224 @@ +/* + * Copyright 2022 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.logging; + +import com.google.api.client.util.Strings; +import com.google.api.gax.core.GaxProperties; +import com.google.cloud.Tuple; +import com.google.cloud.logging.Logging.WriteOption; +import com.google.cloud.logging.Payload.JsonPayload; +import com.google.cloud.logging.Payload.Type; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.protobuf.ListValue; +import com.google.protobuf.Struct; +import com.google.protobuf.Value; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Instrumentation { + public static final String DIAGNOSTIC_INFO_KEY = "logging.googleapis.com/diagnostic"; + public static final String INSTRUMENTATION_SOURCE_KEY = "instrumentation_source"; + public static final String INSTRUMENTATION_NAME_KEY = "name"; + public static final String INSTRUMENTATION_VERSION_KEY = "version"; + public static final String JAVA_LIBRARY_NAME_PREFIX = "java"; + public static final String DEFAULT_INSTRUMENTATION_VERSION = "UNKNOWN"; + public static final String INSTRUMENTATION_LOG_NAME = "diagnostic-log"; + public static final int MAX_DIAGNOSTIC_VALUE_LENGTH = 14; + public static final int MAX_DIAGNOSTIC_ENTIES = 3; + private static boolean instrumentationAdded = false; + private static Object instrumentationLock = new Object(); + + /** + * Populates entries with instrumentation info which is added in separate log entry + * + * @param logEntries {Iterable} The list of entries to be populated + * @return {Tuple>} containg a flag if instrumentation info was added + * or not and a modified list of log entries + */ + public static Tuple> populateInstrumentationInfo( + Iterable logEntries) { + boolean isWritten = setInstrumentationStatus(true); + if (isWritten) return Tuple.of(false, logEntries); + List entries = new ArrayList<>(); + + for (LogEntry logEntry : logEntries) { + // Check if LogEntry has a proper payload and also contains a diagnostic entry + if (!isWritten + && logEntry.getPayload().getType() == Type.JSON + && logEntry + .getPayload() + .getData() + .containsFields(DIAGNOSTIC_INFO_KEY)) { + try { + ListValue infoList = + logEntry + .getPayload() + .getData() + .getFieldsOrThrow(DIAGNOSTIC_INFO_KEY) + .getStructValue() + .getFieldsOrThrow(INSTRUMENTATION_SOURCE_KEY) + .getListValue(); + entries.add(createDiagnosticEntry(null, null, infoList)); + isWritten = true; + } catch (Exception ex) { + System.err.println("ERROR: unexpected exception in populateInstrumentationInfo: " + ex); + } + } else { + entries.add(logEntry); + } + } + if (!isWritten) { + entries.add(createDiagnosticEntry(null, null, null)); + } + return Tuple.of(true, entries); + } + + /** + * Adds a partialSuccess flag option to array of WriteOption + * + * @param options {WriteOption[]} The options array to be extended + * @return The new array of oprions containing WriteOption.OptionType.PARTIAL_SUCCESS flag set to + * true + */ + public static WriteOption[] addPartialSuccessOption(WriteOption[] options) { + if (options == null) return options; + List writeOptions = new ArrayList(); + writeOptions.addAll(Arrays.asList(options)); + // Make sure we remove all partial success flags if any exist + writeOptions.removeIf( + option -> option.getOptionType() == WriteOption.OptionType.PARTIAL_SUCCESS); + writeOptions.add(WriteOption.partialSuccess(true)); + return Iterables.toArray(writeOptions, WriteOption.class); + } + + /** + * The helper method to generate a log entry with diagnostic instrumentation data. + * + * @param libraryName {string} The name of the logging library to be reported. Should be prefixed + * with 'java'. Will be truncated if longer than 14 characters. + * @param libraryVersion {string} The version of the logging library to be reported. Will be + * truncated if longer than 14 characters. + * @returns {LogEntry} The entry with diagnostic instrumentation data. + */ + public static LogEntry createDiagnosticEntry(String libraryName, String libraryVersion) { + return createDiagnosticEntry(libraryName, libraryVersion, null); + } + + private static LogEntry createDiagnosticEntry( + String libraryName, String libraryVersion, ListValue existingLibraryList) { + Struct instrumentation = + Struct.newBuilder() + .putAllFields( + ImmutableMap.of( + INSTRUMENTATION_SOURCE_KEY, + Value.newBuilder() + .setListValue( + generateLibrariesList(libraryName, libraryVersion, existingLibraryList)) + .build())) + .build(); + LogEntry entry = + LogEntry.newBuilder( + JsonPayload.of( + Struct.newBuilder() + .putAllFields( + ImmutableMap.of( + DIAGNOSTIC_INFO_KEY, + Value.newBuilder().setStructValue(instrumentation).build())) + .build())) + .setLogName(INSTRUMENTATION_LOG_NAME) + .build(); + return entry; + } + + private static ListValue generateLibrariesList( + String libraryName, String libraryVersion, ListValue existingLibraryList) { + if (Strings.isNullOrEmpty(libraryName) || !libraryName.startsWith(JAVA_LIBRARY_NAME_PREFIX)) + libraryName = JAVA_LIBRARY_NAME_PREFIX; + if (Strings.isNullOrEmpty(libraryVersion)) { + libraryVersion = getLibraryVersion(Instrumentation.class.getClass()); + } + Struct libraryInfo = createInfoStruct(libraryName, libraryVersion); + ListValue.Builder libraryList = ListValue.newBuilder(); + // Append first the library info for this library + libraryList.addValues(Value.newBuilder().setStructValue(libraryInfo).build()); + if (existingLibraryList != null) { + for (Value val : existingLibraryList.getValuesList()) { + if (val.hasStructValue()) { + try { + String name = + val.getStructValue().getFieldsOrThrow(INSTRUMENTATION_NAME_KEY).getStringValue(); + if (Strings.isNullOrEmpty(name) || !name.startsWith(JAVA_LIBRARY_NAME_PREFIX)) continue; + String version = + val.getStructValue().getFieldsOrThrow(INSTRUMENTATION_VERSION_KEY).getStringValue(); + if (Strings.isNullOrEmpty(version)) continue; + libraryList.addValues( + Value.newBuilder().setStructValue(createInfoStruct(name, version)).build()); + if (libraryList.getValuesCount() == MAX_DIAGNOSTIC_ENTIES) break; + } catch (Exception ex) { + } + } + } + } + return libraryList.build(); + } + + private static Struct createInfoStruct(String libraryName, String libraryVersion) { + return Struct.newBuilder() + .putAllFields( + ImmutableMap.of( + INSTRUMENTATION_NAME_KEY, + Value.newBuilder().setStringValue(truncateValue(libraryName)).build(), + INSTRUMENTATION_VERSION_KEY, + Value.newBuilder().setStringValue(truncateValue(libraryVersion)).build())) + .build(); + } + + /** + * The package-private helper method used to set the flag which indicates if instrumentation info + * already written or not. + * + * @returns The value of the flag before it was set. + */ + static boolean setInstrumentationStatus(boolean value) { + if (instrumentationAdded == value) return instrumentationAdded; + synchronized (instrumentationLock) { + boolean current = instrumentationAdded; + instrumentationAdded = value; + return current; + } + } + + /** + * Returns a library version associated with given class + * + * @param libraryClass {Class} The class to be used to determine a library version + * @return The version number string for given class or "UNKNOWN" if class library version cannot + * be detected + */ + public static String getLibraryVersion(Class libraryClass) { + String libraryVersion = GaxProperties.getLibraryVersion(libraryClass); + if (Strings.isNullOrEmpty(libraryVersion)) libraryVersion = DEFAULT_INSTRUMENTATION_VERSION; + return libraryVersion; + } + + private static String truncateValue(String value) { + if (Strings.isNullOrEmpty(value) || value.length() < MAX_DIAGNOSTIC_VALUE_LENGTH) return value; + return value.substring(0, MAX_DIAGNOSTIC_VALUE_LENGTH) + "*"; + } +} diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/Logging.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/Logging.java index a765c73e0..832c61137 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/Logging.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/Logging.java @@ -71,7 +71,8 @@ enum OptionType implements Option.OptionType { RESOURCE, LABELS, LOG_DESTINATION, - AUTO_POPULATE_METADATA; + AUTO_POPULATE_METADATA, + PARTIAL_SUCCESS; @SuppressWarnings("unchecked") T get(Map options) { @@ -123,6 +124,15 @@ public static WriteOption destination(LogDestinationName destination) { public static WriteOption autoPopulateMetadata(boolean autoPopulateMetadata) { return new WriteOption(OptionType.AUTO_POPULATE_METADATA, autoPopulateMetadata); } + + /** + * Returns an option to set partialSuccess flag. See {@link + * https://cloud.google.com/logging/docs/reference/v2/rest/v2/entries/write#body.request_body.FIELDS.partial_success} + * for more details. + */ + public static WriteOption partialSuccess(boolean partialSuccess) { + return new WriteOption(OptionType.PARTIAL_SUCCESS, partialSuccess); + } } /** Fields according to which log entries can be sorted. */ diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java index 92b9f8794..ffa4c6273 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingHandler.java @@ -312,7 +312,10 @@ public void publish(LogRecord record) { } if (logEntry != null) { try { - Iterable logEntries = ImmutableList.of(logEntry); + Iterable logEntries = + redirectToStdout + ? Instrumentation.populateInstrumentationInfo(ImmutableList.of(logEntry)).y() + : ImmutableList.of(logEntry); if (autoPopulateMetadata) { logEntries = logging.populateMetadata( diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java index c75658c11..a2078e079 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java @@ -23,6 +23,7 @@ import static com.google.cloud.logging.Logging.WriteOption.OptionType.LABELS; import static com.google.cloud.logging.Logging.WriteOption.OptionType.LOG_DESTINATION; import static com.google.cloud.logging.Logging.WriteOption.OptionType.LOG_NAME; +import static com.google.cloud.logging.Logging.WriteOption.OptionType.PARTIAL_SUCCESS; import static com.google.cloud.logging.Logging.WriteOption.OptionType.RESOURCE; import static com.google.common.base.Preconditions.checkNotNull; @@ -39,6 +40,7 @@ import com.google.cloud.MonitoredResource; import com.google.cloud.MonitoredResourceDescriptor; import com.google.cloud.PageImpl; +import com.google.cloud.Tuple; import com.google.cloud.logging.spi.v2.LoggingRpc; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; @@ -92,7 +94,6 @@ import java.util.concurrent.TimeoutException; class LoggingImpl extends BaseService implements Logging { - protected static final String RESOURCE_NAME_FORMAT = "projects/%s/traces/%s"; private static final int FLUSH_WAIT_TIMEOUT_SECONDS = 6; private final LoggingRpc rpc; @@ -774,6 +775,7 @@ private static WriteLogEntriesRequest writeLogEntriesRequest( builder.putAllLabels(labels); } + builder.setPartialSuccess(Boolean.TRUE.equals(PARTIAL_SUCCESS.get(options))); builder.addAllEntries(Iterables.transform(logEntries, LogEntry.toPbFunction(projectId))); return builder.build(); } @@ -851,6 +853,9 @@ public void write(Iterable logEntries, WriteOption... options) { final Boolean logingOptionsPopulateFlag = getOptions().getAutoPopulateMetadata(); final Boolean writeOptionPopulateFlga = WriteOption.OptionType.AUTO_POPULATE_METADATA.get(writeOptions); + Tuple> pair = + Instrumentation.populateInstrumentationInfo(logEntries); + logEntries = pair.y(); if (writeOptionPopulateFlga == Boolean.TRUE || (writeOptionPopulateFlga == null && logingOptionsPopulateFlag == Boolean.TRUE)) { @@ -858,8 +863,9 @@ public void write(Iterable logEntries, WriteOption... options) { logEntries = populateMetadata(logEntries, sharedResourceMetadata, this.getClass().getName()); } - - writeLogEntries(logEntries, options); + // Add partialSuccess option always for request containing instrumentation data + writeLogEntries( + logEntries, pair.x() ? Instrumentation.addPartialSuccessOption(options) : options); if (flushSeverity != null) { for (LogEntry logEntry : logEntries) { // flush pending writes if log severity at or above flush severity diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/ConfigClient.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/ConfigClient.java index dc686eafe..e038117dc 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/ConfigClient.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/ConfigClient.java @@ -18,7 +18,6 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; -import com.google.api.core.BetaApi; import com.google.api.gax.core.BackgroundResource; import com.google.api.gax.longrunning.OperationFuture; import com.google.api.gax.paging.AbstractFixedSizeCollection; @@ -181,7 +180,6 @@ public static final ConfigClient create(ConfigSettings settings) throws IOExcept * Constructs an instance of ConfigClient, using the given stub for making calls. This is for * advanced usage - prefer using create(ConfigSettings). */ - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") public static final ConfigClient create(ConfigServiceV2Stub stub) { return new ConfigClient(stub); } @@ -196,7 +194,6 @@ protected ConfigClient(ConfigSettings settings) throws IOException { this.operationsClient = OperationsClient.create(this.stub.getOperationsStub()); } - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") protected ConfigClient(ConfigServiceV2Stub stub) { this.settings = null; this.stub = stub; @@ -207,7 +204,6 @@ public final ConfigSettings getSettings() { return settings; } - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") public ConfigServiceV2Stub getStub() { return stub; } @@ -464,7 +460,7 @@ public final ListBucketsPagedResponse listBuckets(ListBucketsRequest request) { * .build(); * while (true) { * ListBucketsResponse response = configClient.listBucketsCallable().call(request); - * for (LogBucket element : response.getResponsesList()) { + * for (LogBucket element : response.getBucketsList()) { * // doThingsWith(element); * } * String nextPageToken = response.getNextPageToken(); @@ -879,7 +875,7 @@ public final UnaryCallable listViewsPa * .build(); * while (true) { * ListViewsResponse response = configClient.listViewsCallable().call(request); - * for (LogView element : response.getResponsesList()) { + * for (LogView element : response.getViewsList()) { * // doThingsWith(element); * } * String nextPageToken = response.getNextPageToken(); @@ -1336,7 +1332,7 @@ public final UnaryCallable listSinksPa * .build(); * while (true) { * ListSinksResponse response = configClient.listSinksCallable().call(request); - * for (LogSink element : response.getResponsesList()) { + * for (LogSink element : response.getSinksList()) { * // doThingsWith(element); * } * String nextPageToken = response.getNextPageToken(); @@ -2294,7 +2290,7 @@ public final ListExclusionsPagedResponse listExclusions(ListExclusionsRequest re * .build(); * while (true) { * ListExclusionsResponse response = configClient.listExclusionsCallable().call(request); - * for (LogExclusion element : response.getResponsesList()) { + * for (LogExclusion element : response.getExclusionsList()) { * // doThingsWith(element); * } * String nextPageToken = response.getNextPageToken(); diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/LoggingClient.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/LoggingClient.java index 92f83bed0..a80f2f0bd 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/LoggingClient.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/LoggingClient.java @@ -20,7 +20,6 @@ import com.google.api.MonitoredResourceDescriptor; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; -import com.google.api.core.BetaApi; import com.google.api.gax.core.BackgroundResource; import com.google.api.gax.paging.AbstractFixedSizeCollection; import com.google.api.gax.paging.AbstractPage; @@ -142,7 +141,6 @@ public static final LoggingClient create(LoggingSettings settings) throws IOExce * Constructs an instance of LoggingClient, using the given stub for making calls. This is for * advanced usage - prefer using create(LoggingSettings). */ - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") public static final LoggingClient create(LoggingServiceV2Stub stub) { return new LoggingClient(stub); } @@ -156,7 +154,6 @@ protected LoggingClient(LoggingSettings settings) throws IOException { this.stub = ((LoggingServiceV2StubSettings) settings.getStubSettings()).createStub(); } - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") protected LoggingClient(LoggingServiceV2Stub stub) { this.settings = null; this.stub = stub; @@ -166,7 +163,6 @@ public final LoggingSettings getSettings() { return settings; } - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") public LoggingServiceV2Stub getStub() { return stub; } @@ -678,7 +674,7 @@ public final ListLogEntriesPagedResponse listLogEntries(ListLogEntriesRequest re * .build(); * while (true) { * ListLogEntriesResponse response = loggingClient.listLogEntriesCallable().call(request); - * for (LogEntry element : response.getResponsesList()) { + * for (LogEntry element : response.getEntriesList()) { * // doThingsWith(element); * } * String nextPageToken = response.getNextPageToken(); @@ -774,7 +770,7 @@ public final ListMonitoredResourceDescriptorsPagedResponse listMonitoredResource * while (true) { * ListMonitoredResourceDescriptorsResponse response = * loggingClient.listMonitoredResourceDescriptorsCallable().call(request); - * for (MonitoredResourceDescriptor element : response.getResponsesList()) { + * for (MonitoredResourceDescriptor element : response.getResourceDescriptorsList()) { * // doThingsWith(element); * } * String nextPageToken = response.getNextPageToken(); @@ -1043,7 +1039,7 @@ public final UnaryCallable listLogsPaged * .build(); * while (true) { * ListLogsResponse response = loggingClient.listLogsCallable().call(request); - * for (String element : response.getResponsesList()) { + * for (String element : response.getLogNamesList()) { * // doThingsWith(element); * } * String nextPageToken = response.getNextPageToken(); diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/MetricsClient.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/MetricsClient.java index c74e0a912..3f1565fc5 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/MetricsClient.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/MetricsClient.java @@ -18,7 +18,6 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; -import com.google.api.core.BetaApi; import com.google.api.gax.core.BackgroundResource; import com.google.api.gax.paging.AbstractFixedSizeCollection; import com.google.api.gax.paging.AbstractPage; @@ -130,7 +129,6 @@ public static final MetricsClient create(MetricsSettings settings) throws IOExce * Constructs an instance of MetricsClient, using the given stub for making calls. This is for * advanced usage - prefer using create(MetricsSettings). */ - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") public static final MetricsClient create(MetricsServiceV2Stub stub) { return new MetricsClient(stub); } @@ -144,7 +142,6 @@ protected MetricsClient(MetricsSettings settings) throws IOException { this.stub = ((MetricsServiceV2StubSettings) settings.getStubSettings()).createStub(); } - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") protected MetricsClient(MetricsServiceV2Stub stub) { this.settings = null; this.stub = stub; @@ -154,7 +151,6 @@ public final MetricsSettings getSettings() { return settings; } - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") public MetricsServiceV2Stub getStub() { return stub; } @@ -290,7 +286,7 @@ public final ListLogMetricsPagedResponse listLogMetrics(ListLogMetricsRequest re * .build(); * while (true) { * ListLogMetricsResponse response = metricsClient.listLogMetricsCallable().call(request); - * for (LogMetric element : response.getResponsesList()) { + * for (LogMetric element : response.getMetricsList()) { * // doThingsWith(element); * } * String nextPageToken = response.getNextPageToken(); diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/ConfigServiceV2StubSettings.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/ConfigServiceV2StubSettings.java index 0ee548e17..55030892f 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/ConfigServiceV2StubSettings.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/ConfigServiceV2StubSettings.java @@ -533,7 +533,6 @@ public UnaryCallSettings copyLogEntriesSetting return copyLogEntriesOperationSettings; } - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") public ConfigServiceV2Stub createStub() throws IOException { if (getTransportChannelProvider() .getTransportName() diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/LoggingServiceV2StubSettings.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/LoggingServiceV2StubSettings.java index 180ef48c6..220b7f22c 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/LoggingServiceV2StubSettings.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/LoggingServiceV2StubSettings.java @@ -424,7 +424,6 @@ public UnaryCallSettings deleteLogSettings() { return tailLogEntriesSettings; } - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") public LoggingServiceV2Stub createStub() throws IOException { if (getTransportChannelProvider() .getTransportName() diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/MetricsServiceV2StubSettings.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/MetricsServiceV2StubSettings.java index 5716cdcea..871b9b1ee 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/MetricsServiceV2StubSettings.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/v2/stub/MetricsServiceV2StubSettings.java @@ -192,7 +192,6 @@ public UnaryCallSettings deleteLogMetricSettings( return deleteLogMetricSettings; } - @BetaApi("A restructuring of stub classes is planned, so this may break in the future") public MetricsServiceV2Stub createStub() throws IOException { if (getTransportChannelProvider() .getTransportName() diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/InstrumentationTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/InstrumentationTest.java new file mode 100644 index 000000000..5838fa80f --- /dev/null +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/InstrumentationTest.java @@ -0,0 +1,145 @@ +/* + * Copyright 2022 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.logging; + +import com.google.api.client.util.Lists; +import com.google.cloud.Tuple; +import com.google.cloud.logging.Payload.JsonPayload; +import com.google.cloud.logging.Payload.StringPayload; +import com.google.cloud.logging.Payload.Type; +import com.google.common.collect.ImmutableList; +import com.google.protobuf.ListValue; +import com.google.protobuf.Value; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import org.junit.Assert; +import org.junit.Test; + +public class InstrumentationTest { + private static final StringPayload STRING_PAYLOAD = StringPayload.of("payload"); + private static final LogEntry STRING_ENTRY = LogEntry.newBuilder(STRING_PAYLOAD).build(); + private static final String JAVA_OTHER_NAME = "java-other"; + private static final String JAVA_INVALID_NAME = "no-java-name"; + private static final String JAVA_OTHER_VERSION = "1.0.0"; + + @Test + public void testInstrumentationGenerated() { + Instrumentation.setInstrumentationStatus(false); + verifyEntries( + Instrumentation.populateInstrumentationInfo(ImmutableList.of(STRING_ENTRY)), + 1, + 2, + new HashSet<>(Arrays.asList(Instrumentation.JAVA_LIBRARY_NAME_PREFIX)), + new HashSet<>( + Arrays.asList(Instrumentation.getLibraryVersion(Instrumentation.class.getClass())))); + } + + @Test + public void testNoInstrumentationGenerated() { + Instrumentation.setInstrumentationStatus(true); + Tuple> pair = + Instrumentation.populateInstrumentationInfo(ImmutableList.of(STRING_ENTRY)); + ArrayList entries = Lists.newArrayList(pair.y()); + Assert.assertFalse(pair.x()); + Assert.assertEquals(entries.size(), 1); + Assert.assertTrue(entries.get(0).getPayload().getType() == Type.STRING); + } + + @Test + public void testInstrumentationUpdated() { + Instrumentation.setInstrumentationStatus(false); + LogEntry json_entry = + LogEntry.newBuilder(generateInstrumentationPayload(JAVA_OTHER_NAME, JAVA_OTHER_VERSION)) + .build(); + verifyEntries( + Instrumentation.populateInstrumentationInfo(ImmutableList.of(json_entry)), + 0, + 1, + new HashSet<>(Arrays.asList(Instrumentation.JAVA_LIBRARY_NAME_PREFIX, JAVA_OTHER_NAME)), + new HashSet<>( + Arrays.asList( + Instrumentation.getLibraryVersion(Instrumentation.class.getClass()), + JAVA_OTHER_VERSION))); + } + + @Test + public void testInvalidInstrumentationRemoved() { + Instrumentation.setInstrumentationStatus(false); + LogEntry json_entry = + LogEntry.newBuilder(generateInstrumentationPayload(JAVA_INVALID_NAME, JAVA_OTHER_VERSION)) + .build(); + verifyEntries( + Instrumentation.populateInstrumentationInfo(ImmutableList.of(json_entry)), + 0, + 1, + new HashSet<>(Arrays.asList(Instrumentation.JAVA_LIBRARY_NAME_PREFIX)), + new HashSet<>( + Arrays.asList(Instrumentation.getLibraryVersion(Instrumentation.class.getClass())))); + } + + public static JsonPayload generateInstrumentationPayload( + String libraryName, String libraryVersion) { + Map json_data = new HashMap<>(); + Map instrumentation_data = new HashMap<>(); + Map info = new HashMap<>(); + info.put(Instrumentation.INSTRUMENTATION_NAME_KEY, libraryName); + info.put(Instrumentation.INSTRUMENTATION_VERSION_KEY, libraryVersion); + List list = ImmutableList.of(info); + instrumentation_data.put(Instrumentation.INSTRUMENTATION_SOURCE_KEY, list); + json_data.put(Instrumentation.DIAGNOSTIC_INFO_KEY, instrumentation_data); + return JsonPayload.of(json_data); + } + + private static void verifyEntries( + Tuple> pair, + int index, + int expected, + HashSet names, + HashSet versions) { + ArrayList entries = Lists.newArrayList(pair.y()); + Assert.assertTrue(pair.x()); + Assert.assertEquals(entries.size(), expected); + Assert.assertTrue(entries.get(index).getPayload().getType() == Type.JSON); + ListValue infoList = + entries + .get(index) + .getPayload() + .getData() + .getFieldsOrThrow(Instrumentation.DIAGNOSTIC_INFO_KEY) + .getStructValue() + .getFieldsOrThrow(Instrumentation.INSTRUMENTATION_SOURCE_KEY) + .getListValue(); + for (Value val : infoList.getValuesList()) { + Assert.assertTrue( + names.remove( + val.getStructValue() + .getFieldsOrThrow(Instrumentation.INSTRUMENTATION_NAME_KEY) + .getStringValue())); + Assert.assertTrue( + versions.remove( + val.getStructValue() + .getFieldsOrThrow(Instrumentation.INSTRUMENTATION_VERSION_KEY) + .getStringValue())); + } + Assert.assertEquals(names.size(), 0); + Assert.assertEquals(versions.size(), 0); + } +} diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java index dbe3f7f5f..d55a3e786 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingHandlerTest.java @@ -201,6 +201,7 @@ public void enhanceLogEntry(LogEntry.Builder builder) { @Before public void setUp() { + Instrumentation.setInstrumentationStatus(true); logging = EasyMock.createMock(Logging.class); options = EasyMock.createMock(LoggingOptions.class); expect(options.getProjectId()).andStubReturn(PROJECT); diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java index 68774e720..421f057e7 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java @@ -252,6 +252,7 @@ private void configureListLogsTests( @Before public void setUp() { + Instrumentation.setInstrumentationStatus(true); rpcFactoryMock = EasyMock.createStrictMock(LoggingRpcFactory.class); loggingRpcMock = EasyMock.createStrictMock(LoggingRpc.class); EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(LoggingOptions.class))) @@ -2288,6 +2289,42 @@ public void run() { assertSame(0, exceptions.get()); } + @Test + public void testDiagnosticInfoWithNoPartialSuccess() { + testDiagnosticInfoGeneration(false); + } + + @Test + public void testDiagnosticInfoWithPartialSuccess() { + testDiagnosticInfoGeneration(true); + } + + private void testDiagnosticInfoGeneration(boolean addPartialSuccessOption) { + Instrumentation.setInstrumentationStatus(false); + LogEntry json_entry = + LogEntry.newBuilder( + InstrumentationTest.generateInstrumentationPayload( + Instrumentation.JAVA_LIBRARY_NAME_PREFIX, + Instrumentation.getLibraryVersion(Instrumentation.class.getClass()))) + .setLogName(Instrumentation.INSTRUMENTATION_LOG_NAME) + .build(); + WriteLogEntriesRequest request = + WriteLogEntriesRequest.newBuilder() + .addAllEntries( + Iterables.transform( + ImmutableList.of(LOG_ENTRY1, LOG_ENTRY2, json_entry), + LogEntry.toPbFunction(PROJECT))) + .setPartialSuccess(true) + .build(); + WriteLogEntriesResponse response = WriteLogEntriesResponse.newBuilder().build(); + EasyMock.expect(loggingRpcMock.write(request)).andReturn(ApiFutures.immediateFuture(response)); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.getService(); + logging.write( + ImmutableList.of(LOG_ENTRY1, LOG_ENTRY2), + WriteOption.partialSuccess(addPartialSuccessOption)); + } + private void testDeleteByDestination( String logId, String logName, LogDestinationName destination, boolean useAsyncDelete) throws ExecutionException, InterruptedException { diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingTest.java index fab23f972..ef87bb13d 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingTest.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingTest.java @@ -44,6 +44,7 @@ public class LoggingTest { private static final String ORGANIZATION_NAME = "organization"; private static final String BILLING_NAME = "billing"; private static final Boolean DONT_AUTO_POPULATE_METADATA = false; + private static final Boolean DO_PARTIAL_SUCCESS = false; @Test public void testListOption() { @@ -114,6 +115,10 @@ public void testWriteOption() { writeOption = WriteOption.autoPopulateMetadata(DONT_AUTO_POPULATE_METADATA); assertEquals(DONT_AUTO_POPULATE_METADATA, writeOption.getValue()); assertEquals(WriteOption.OptionType.AUTO_POPULATE_METADATA, writeOption.getOptionType()); + + writeOption = WriteOption.partialSuccess(DO_PARTIAL_SUCCESS); + assertEquals(DO_PARTIAL_SUCCESS, writeOption.getValue()); + assertEquals(WriteOption.OptionType.PARTIAL_SUCCESS, writeOption.getOptionType()); } @Test diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/it/ITJulLoggerTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/it/ITJulLoggerTest.java index 9f4853628..80f0e8a50 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/it/ITJulLoggerTest.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/it/ITJulLoggerTest.java @@ -82,7 +82,6 @@ public void testLoggingHandler() throws InterruptedException { assertThat(entry.getOperation()).isNull(); assertThat(entry.getInsertId()).isNotNull(); assertThat(entry.getTimestamp()).isNotNull(); - assertThat(iterator.hasNext()).isFalse(); logger.removeHandler(handler); } diff --git a/grpc-google-cloud-logging-v2/pom.xml b/grpc-google-cloud-logging-v2/pom.xml index 5b7270611..a7b9b6f5f 100644 --- a/grpc-google-cloud-logging-v2/pom.xml +++ b/grpc-google-cloud-logging-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-logging-v2 - 0.98.0 + 0.99.0 grpc-google-cloud-logging-v2 GRPC library for grpc-google-cloud-logging-v2 com.google.cloud google-cloud-logging-parent - 3.9.0 + 3.10.0 diff --git a/pom.xml b/pom.xml index b6b62e603..338f11e04 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-logging-parent pom - 3.9.0 + 3.10.0 Google Cloud Logging Parent https://github.com/googleapis/java-logging @@ -14,7 +14,7 @@ com.google.cloud google-cloud-shared-config - 1.4.0 + 1.5.0 @@ -61,23 +61,23 @@ com.google.api.grpc proto-google-cloud-logging-v2 - 0.98.0 + 0.99.0 com.google.api.grpc grpc-google-cloud-logging-v2 - 0.98.0 + 0.99.0 com.google.cloud google-cloud-logging - 3.9.0 + 3.10.0 com.google.cloud google-cloud-shared-dependencies - 2.12.0 + 2.13.0 pom import diff --git a/proto-google-cloud-logging-v2/pom.xml b/proto-google-cloud-logging-v2/pom.xml index 6962f1115..cba473431 100644 --- a/proto-google-cloud-logging-v2/pom.xml +++ b/proto-google-cloud-logging-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-logging-v2 - 0.98.0 + 0.99.0 proto-google-cloud-logging-v2 PROTO library for proto-google-cloud-logging-v2 com.google.cloud google-cloud-logging-parent - 3.9.0 + 3.10.0 diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index a66105dca..8e805ab6e 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -28,7 +28,7 @@ com.google.cloud google-cloud-logging - 3.7.6 + 3.8.0 diff --git a/samples/native-image-sample/README.md b/samples/native-image-sample/README.md index 398a55e83..555c42fbc 100644 --- a/samples/native-image-sample/README.md +++ b/samples/native-image-sample/README.md @@ -18,25 +18,25 @@ You will need to follow these prerequisite steps in order to run the samples: **Note:** Authenticating with Application Default Credentials is convenient to use during development, but we recommend [alternate methods of authentication](https://cloud.google.com/docs/authentication/production) during production use. -3. Install the GraalVM compiler. +3. Install the native image compiler. - You can follow the [official installation instructions](https://www.graalvm.org/docs/getting-started/#install-graalvm) from the GraalVM website. + You can follow the [official installation instructions](https://www.graalvm.org/docs/getting-started/#install-graalvm). After following the instructions, ensure that you install the native image extension installed by running: ``` gu install native-image ``` - Once you finish following the instructions, verify that the default version of Java is set to the GraalVM version by running `java -version` in a terminal. + Once you finish following the instructions, verify that the default version of Java is set to the correct version by running `java -version` in a terminal. You will see something similar to the below output: ``` $ java -version - openjdk version "11.0.7" 2020-04-14 - OpenJDK Runtime Environment GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02) - OpenJDK 64-Bit Server VM GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02, mixed mode, sharing) + openjdk version "17.0.3" 2022-04-19 + OpenJDK Runtime Environment GraalVM CE 22.1.0 (build 17.0.3+7-jvmci-22.1-b06) + OpenJDK 64-Bit Server VM GraalVM CE 22.1.0 (build 17.0.3+7-jvmci-22.1-b06, mixed mode, sharing) ``` 4. Enable the [Logging APIs](https://console.cloud.google.com/flows/enableapi?apiid=logging.googleapis.com). @@ -45,7 +45,7 @@ You will need to follow these prerequisite steps in order to run the samples: Navigate to this directory in a new terminal. -1. Compile the application using the Native Image Compiler. This step may take a few minutes. +1. Compile the application using the native image Compiler. This step may take a few minutes. ``` mvn package -P native -DskipTests @@ -68,4 +68,4 @@ In order to run the sample integration test as a native image, call the followin ``` mvn test -Pnative - ``` \ No newline at end of file + ``` diff --git a/samples/native-image-sample/pom.xml b/samples/native-image-sample/pom.xml index 7e8da6c80..550e9e8d9 100644 --- a/samples/native-image-sample/pom.xml +++ b/samples/native-image-sample/pom.xml @@ -29,7 +29,7 @@ com.google.cloud libraries-bom - 25.3.0 + 25.4.0 pom import @@ -110,7 +110,7 @@ org.graalvm.buildtools junit-platform-native - 0.9.11 + 0.9.12 test diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 11d61ac54..eb8b6b73c 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -28,7 +28,7 @@ com.google.cloud google-cloud-logging - 3.9.0 + 3.10.0 diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index f27295060..7d930315b 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -29,7 +29,7 @@ com.google.cloud libraries-bom - 25.3.0 + 25.4.0 pom import diff --git a/versions.txt b/versions.txt index 750e10942..5482afda1 100644 --- a/versions.txt +++ b/versions.txt @@ -1,6 +1,6 @@ # Format: # module:released-version:current-version -google-cloud-logging:3.9.0:3.9.0 -grpc-google-cloud-logging-v2:0.98.0:0.98.0 -proto-google-cloud-logging-v2:0.98.0:0.98.0 +google-cloud-logging:3.10.0:3.10.0 +grpc-google-cloud-logging-v2:0.99.0:0.99.0 +proto-google-cloud-logging-v2:0.99.0:0.99.0