From 81158885911e6c150fa04053fc41f95640329548 Mon Sep 17 00:00:00 2001 From: minherz Date: Fri, 17 Sep 2021 21:39:11 +0300 Subject: [PATCH] feat: Add a sample snippet for use of the tailLogEntries API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add sample in Java for use of tailLogEntries API * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot --- README.md | 3 +- .../cloud/logging/JavaTimeConversions.java | 7 ++- .../cloud/logging/LogEntryIterator.java | 7 +++ .../cloud/logging/LogEntryServerStream.java | 20 +++++++ .../com/google/cloud/logging/LoggingImpl.java | 4 +- .../cloud/logging/TailLogEntriesTest.java | 4 +- .../cloud/logging/it/ITTailLogsTest.java | 6 +- .../com/example/logging/TailLogEntries.java | 55 +++++++++++++++++++ .../java/com/example/logging/LoggingIT.java | 42 ++++++++++++-- 9 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 samples/snippets/src/main/java/com/example/logging/TailLogEntries.java diff --git a/README.md b/README.md index ae8ecd0ca..8a2501a85 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ If you are using Maven without BOM, add this to your dependencies: com.google.cloud google-cloud-logging - 3.1.1 + 3.1.2 ``` @@ -235,6 +235,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-logging/tree/ | List Logs | [source code](https://github.com/googleapis/java-logging/blob/master/samples/snippets/src/main/java/com/example/logging/ListLogs.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-logging&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/logging/ListLogs.java) | | Log Entry Write Http Request | [source code](https://github.com/googleapis/java-logging/blob/master/samples/snippets/src/main/java/com/example/logging/LogEntryWriteHttpRequest.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-logging&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/logging/LogEntryWriteHttpRequest.java) | | Quickstart Sample | [source code](https://github.com/googleapis/java-logging/blob/master/samples/snippets/src/main/java/com/example/logging/QuickstartSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-logging&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/logging/QuickstartSample.java) | +| Tail Log Entries | [source code](https://github.com/googleapis/java-logging/blob/master/samples/snippets/src/main/java/com/example/logging/TailLogEntries.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-logging&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/logging/TailLogEntries.java) | | Quickstart | [source code](https://github.com/googleapis/java-logging/blob/master/samples/snippets/src/main/java/com/example/logging/jul/Quickstart.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-logging&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/logging/jul/Quickstart.java) | | Example Enhancer | [source code](https://github.com/googleapis/java-logging/blob/master/samples/snippets/src/main/java/com/example/logging/jul/enhancers/ExampleEnhancer.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-logging&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/logging/jul/enhancers/ExampleEnhancer.java) | diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/JavaTimeConversions.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/JavaTimeConversions.java index 26d42791c..0a64dc2da 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/JavaTimeConversions.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/JavaTimeConversions.java @@ -16,6 +16,7 @@ package com.google.cloud.logging; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.math.LongMath.checkedAdd; import static com.google.common.math.LongMath.checkedSubtract; @@ -33,8 +34,8 @@ * The class complements convertion methods that are currently not supported in the published * protobuf-java-util. After migrating protobuf-java-util to Java 8 this class can be removed. * - * @see protobuf-java-util + * @see protobuf-java-util */ class JavaTimeConversions { static final long NANOS_PER_SECOND = 1000000000; @@ -48,6 +49,7 @@ private JavaTimeConversions() {} * Timestamps#isValid}. */ public static Instant toJavaInstant(Timestamp timestamp) { + checkNotNull(timestamp); timestamp = normalizedTimestamp(timestamp.getSeconds(), timestamp.getNanos()); return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()); } @@ -59,6 +61,7 @@ public static Instant toJavaInstant(Timestamp timestamp) { * a {@link Timestamp}. See {@link Timestamps#isValid}. */ public static Timestamp toProtoTimestamp(Instant instant) { + checkNotNull(instant); return normalizedTimestamp(instant.getEpochSecond(), instant.getNano()); } diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/LogEntryIterator.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/LogEntryIterator.java index ca29bfe39..34887e49b 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/LogEntryIterator.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/LogEntryIterator.java @@ -21,7 +21,14 @@ import java.util.Iterator; import java.util.LinkedList; +/** + * The class implements {@see Iterator} interface over {@see LogEntry} by iterating through {@see + * TailLogEntriesResponse} streamed by {@code BidiStream}. This class is instantiated by {@see + * LogEntryServerStream} and is not intended to be used explicitly. + */ public class LogEntryIterator implements Iterator { + // TODO: consider converting this to use generics instead of + // fixed TailLogEntriesResponse private final Iterator streamIterator; private final LinkedList buffer = new LinkedList<>(); diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/LogEntryServerStream.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/LogEntryServerStream.java index 2d4b5ec12..1bbf038f3 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/LogEntryServerStream.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/LogEntryServerStream.java @@ -22,6 +22,26 @@ import com.google.logging.v2.TailLogEntriesResponse; import java.util.Iterator; +/** + * The class implements {@Iterable} interface over {@see LogEntry}. It wraps around {@BidiStream} + * bi-directional gRPC stream to support iterating through ingested responses. The class uses {@see + * LogEntryIterator} to iterate through the processed responses. The stream should be explicitly + * canceled by calling {@see LogEntryServerStream#cancel()} method. The class does not provide + * recovery or resuming functionality over the stream. + * + *

To iterate run: + * + *

{@code
+ * LogEntryServerStream stream;
+ * // code to initialize stream
+ * for (LogEntry log : stream) {
+ *   // do something with logs
+ * }
+ * stream.cancel();
+ * }
+ * + *

The iteration can be blocked on waiting for another response sent in the stream. + */ public class LogEntryServerStream implements Iterable { private final BidiStream serverStream; 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 41e08d41f..ce8e60ff7 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 @@ -924,7 +924,7 @@ public ApiFuture> listLogEntriesAsync(EntryListOption... opt return listLogEntriesAsync(getOptions(), optionMap(options)); } - static TailLogEntriesRequest tailLogEntriesRequest( + static TailLogEntriesRequest buildTailLogEntriesRequest( Map options, String defaultProjectId) { TailLogEntriesRequest.Builder builder = TailLogEntriesRequest.newBuilder(); @@ -967,7 +967,7 @@ public LogEntryServerStream tailLogEntries(TailOption... options) { BidiStream bidiStream = serviceOptions.getLoggingRpcV2().getTailLogEntriesStream(); final TailLogEntriesRequest request = - tailLogEntriesRequest(optionMap(options), serviceOptions.getProjectId()); + buildTailLogEntriesRequest(optionMap(options), serviceOptions.getProjectId()); bidiStream.send(request); return new LogEntryServerStream(bidiStream); } diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/TailLogEntriesTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/TailLogEntriesTest.java index d79a4bc5e..ee3971d6e 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/TailLogEntriesTest.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/TailLogEntriesTest.java @@ -43,7 +43,7 @@ public class TailLogEntriesTest { @Test public void testTailOptions() { TailLogEntriesRequest request = - LoggingImpl.tailLogEntriesRequest( + LoggingImpl.buildTailLogEntriesRequest( LoggingImpl.optionMap( TailOption.filter(FILTER), TailOption.bufferWindow(WINDOW), @@ -66,7 +66,7 @@ public void testTailOptions() { @Test public void testEmptyTailOptions() { TailLogEntriesRequest request = - LoggingImpl.tailLogEntriesRequest(LoggingImpl.optionMap(), DEFAULT_PROJECT_ID); + LoggingImpl.buildTailLogEntriesRequest(LoggingImpl.optionMap(), DEFAULT_PROJECT_ID); assertThat(request.getFilter()).isEqualTo(""); assertThat(request.getBufferWindow()).isEqualTo(Duration.newBuilder().build()); assertThat(request.getResourceNamesList()).containsExactly("projects/" + DEFAULT_PROJECT_ID); diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/it/ITTailLogsTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/it/ITTailLogsTest.java index 2882595c0..48e86fa44 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/it/ITTailLogsTest.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/it/ITTailLogsTest.java @@ -34,10 +34,6 @@ public class ITTailLogsTest extends BaseSystemTest { private static final MonitoredResource GLOBAL_RESOURCE = MonitoredResource.newBuilder("global").build(); - // @BeforeClass - // public static void configureWriteLogs() { - // } - @AfterClass public static void cleanUpLogs() throws InterruptedException { assertTrue(cleanupLog(LOG_ID)); @@ -83,7 +79,9 @@ public void testTailLogEntries() throws InterruptedException { assertEquals(testLogEntry.getLogName(), resultEntry.getLogName()); assertEquals( testLogEntry.getHttpRequest().getStatus(), resultEntry.getHttpRequest().getStatus()); + // assert equals for Payload objects assertTrue(testLogEntry.getPayload().equals(resultEntry.getPayload())); + // assert equals for Map objects assertTrue(testLogEntry.getLabels().equals(resultEntry.getLabels())); } } diff --git a/samples/snippets/src/main/java/com/example/logging/TailLogEntries.java b/samples/snippets/src/main/java/com/example/logging/TailLogEntries.java new file mode 100644 index 000000000..2580802a1 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/logging/TailLogEntries.java @@ -0,0 +1,55 @@ +/* + * Copyright 2021 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.example.logging; + +// [START logging_write_log_entry] +import com.google.cloud.logging.LogEntry; +import com.google.cloud.logging.LogEntryServerStream; +import com.google.cloud.logging.Logging; +import com.google.cloud.logging.Logging.TailOption; +import com.google.cloud.logging.LoggingOptions; + +public class TailLogEntries { + + public static void main(String[] args) throws Exception { + // TODO(developer): Optionally provide the logname as an argument. + String logName = args.length > 0 ? args[0] : ""; + + LoggingOptions options = LoggingOptions.getDefaultInstance(); + try (Logging logging = options.getService()) { + + // Optionally compose a filter to tail log entries only from specific log + LogEntryServerStream stream; + + if (logName != "") { + stream = + logging.tailLogEntries( + TailOption.filter( + "logName=projects/" + options.getProjectId() + "/logs/" + logName)); + } else { + stream = logging.tailLogEntries(); + } + System.out.println("start streaming.."); + for (LogEntry log : stream) { + System.out.println(log); + // cancel infinite streaming after receiving first entry + stream.cancel(); + } + } + } +} +// [END logging_write_log_entry] diff --git a/samples/snippets/src/test/java/com/example/logging/LoggingIT.java b/samples/snippets/src/test/java/com/example/logging/LoggingIT.java index 619595962..3087f97a7 100644 --- a/samples/snippets/src/test/java/com/example/logging/LoggingIT.java +++ b/samples/snippets/src/test/java/com/example/logging/LoggingIT.java @@ -64,19 +64,18 @@ public void setUp() { public void tearDown() { // Clean up created logs deleteLog(TEST_LOG); - System.setOut(null); } @Test - public void testQuickstart() throws Exception { + public void testQuickstartSample() throws Exception { QuickstartSample.main(TEST_LOG); String got = bout.toString(); assertThat(got).contains(String.format("Logged: %s", STRING_PAYLOAD)); } @Test(timeout = 60000) - public void testListLogEntries() throws Exception { + public void testListLogEntriesSample() throws Exception { // write a log entry LogEntry entry = LogEntry.newBuilder(StringPayload.of(STRING_PAYLOAD2)) @@ -99,7 +98,7 @@ public void testListLogEntries() throws Exception { } @Test(timeout = 60000) - public void testWriteLogHttpRequest() throws Exception { + public void testWriteLogHttpRequestSample() throws Exception { HttpRequest request = HttpRequest.newBuilder() .setRequestUrl("www.example.com") @@ -126,7 +125,7 @@ public void testWriteLogHttpRequest() throws Exception { } @Test(timeout = 60000) - public void testListLogNames_shouldPass() throws Exception { + public void testListLogNamesSample() throws Exception { ListLogs.main(); // Check for mocked STDOUT having data while (bout.toString().isEmpty()) { @@ -135,4 +134,37 @@ public void testListLogNames_shouldPass() throws Exception { assertThat(bout.toString().contains(GOOGLEAPIS_AUDIT_LOGNAME)).isTrue(); } + + @Test(timeout = 60000) + public void testTailLogEntriesSample() throws Exception { + Runnable task = + () -> { + // wait 10 seconds to allow establishing tail stream in the sample + try { + Thread.sleep(10_000); + try (Logging logging = LoggingOptions.getDefaultInstance().getService()) { + // create an instance of LogEntry with HTTP request + LogEntry logEntry = + LogEntry.newBuilder(StringPayload.of(STRING_PAYLOAD)) + .setLogName(TEST_LOG) + .setResource(MonitoredResource.newBuilder("global").build()) + .build(); + // Writes the log entry asynchronously + logging.write(Collections.singleton(logEntry)); + } + } catch (Exception t) { + System.out.println("Failed to write log entry:\n" + t) + } + }; + Thread thread = new Thread(task); + thread.start(); + + TailLogEntries.main(new String[] {TEST_LOG}); + + // Check for mocked STDOUT having data + while (bout.toString().isEmpty()) { + Thread.sleep(1000); + } + assertThat(bout.toString().contains(STRING_PAYLOAD)).isTrue(); + } }