Skip to content

Commit

Permalink
feat: Add destination property into LogEntry (#720)
Browse files Browse the repository at this point in the history
* Add ResourceName class which will provide destination log resource name customization and integrate it with LogEntry

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* Add testToAndFromPbWithExpectedFailure and fix copyright header

* Adress PR comments

* Fix build break which caused by missing isBlank() symbol

* Address PR comments and fix tests accordingly

* Add extra test in testToAndFromPb()

* Fix description for fromLogName()

* Address latest PR comments

* Fix fromPb() to call fromLogName() only once

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
  • Loading branch information
losalex and gcf-owl-bot[bot] committed Oct 24, 2021
1 parent 1fa3a6e commit 43ea0b4
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 14 deletions.
@@ -0,0 +1,129 @@
/*
* 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
*
* 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.logging;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.logging.v2.LogName;
import java.util.Map;

/**
* Class for specifying resource name of the log to which this log entry belongs (see 'logName'
* parameter in https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry)
*/
public final class LogDestinationName extends Option {

enum DestinationType implements Option.OptionType {
PROJECT,
FOLDER,
ORGANIZATION,
BILLINGACCOUNT;

@SuppressWarnings("unchecked")
<T> T get(Map<Option.OptionType, ?> options) {
return (T) options.get(this);
}
}

private LogDestinationName(Option.OptionType option, Object value) {
super(option, value);
checkArgument(!checkNotNull(value).toString().trim().isEmpty());
}

/**
* Returns an option which sets and validates project ID resource name for log entries.
*
* @param id corresponds to PROJECT_ID token in 'logName' field described in
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
*/
public static LogDestinationName project(String id) {
return new LogDestinationName(DestinationType.PROJECT, id);
}

/**
* Returns an option which sets and validates project ID resource name for log entries.
*
* @param id corresponds to FOLDER_ID token in 'logName' field described in
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
*/
public static LogDestinationName folder(String id) {
return new LogDestinationName(DestinationType.FOLDER, id);
}

/**
* Returns an option which sets and validates project ID resource name for log entries.
*
* @param id corresponds to ORGANIZATION_ID token in 'logName' field described in
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
*/
public static LogDestinationName organization(String id) {
return new LogDestinationName(DestinationType.ORGANIZATION, id);
}

/**
* Returns an option which sets and validates project ID resource name for log entries.
*
* @param id corresponds to BILLING_ACCOUNT_ID token in 'logName' field described in
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
*/
public static LogDestinationName billingAccount(String id) {
return new LogDestinationName(DestinationType.BILLINGACCOUNT, id);
}

/** Creates a {@code LogEntry} object for given log ID. */
public LogName toLogName(String logId) {
if (logId == null) {
return null;
}

switch ((DestinationType) getOptionType()) {
case PROJECT:
return LogName.ofProjectLogName(getValue().toString(), logId);

case FOLDER:
return LogName.ofFolderLogName(getValue().toString(), logId);

case ORGANIZATION:
return LogName.ofOrganizationLogName(getValue().toString(), logId);

case BILLINGACCOUNT:
return LogName.ofBillingAccountLogName(getValue().toString(), logId);
}

return null;
}

/** Creates a {@code LogDestinationName} object from given {@code LogName}. */
public static LogDestinationName fromLogName(LogName logName) {
if (logName == null) {
return null;
}

if (logName.getProject() != null) {
return project(logName.getProject());
} else if (logName.getBillingAccount() != null) {
return billingAccount(logName.getBillingAccount());
} else if (logName.getFolder() != null) {
return folder(logName.getFolder());
} else if (logName.getOrganization() != null) {
return organization(logName.getOrganization());
}

return null;
}
}
Expand Up @@ -66,6 +66,7 @@ public LogEntry apply(com.google.logging.v2.LogEntry pb) {
private final boolean traceSampled;
private final SourceLocation sourceLocation;
private final Payload<?> payload;
private final LogDestinationName destination;

/** A builder for {@code LogEntry} objects. */
public static class Builder {
Expand All @@ -84,6 +85,7 @@ public static class Builder {
private boolean traceSampled;
private SourceLocation sourceLocation;
private Payload<?> payload;
private LogDestinationName destination;

Builder(Payload<?> payload) {
this.payload = payload;
Expand All @@ -104,6 +106,7 @@ public static class Builder {
this.traceSampled = entry.traceSampled;
this.sourceLocation = entry.sourceLocation;
this.payload = entry.payload;
this.destination = entry.destination;
}

/**
Expand Down Expand Up @@ -282,6 +285,12 @@ public Builder setPayload(Payload<?> payload) {
return this;
}

/** Sets the log path destination name type associated with the log entry. */
public Builder setDestination(LogDestinationName destination) {
this.destination = destination;
return this;
}

/** Creates a {@code LogEntry} object for this builder. */
public LogEntry build() {
return new LogEntry(this);
Expand All @@ -303,6 +312,7 @@ public LogEntry build() {
this.traceSampled = builder.traceSampled;
this.sourceLocation = builder.sourceLocation;
this.payload = builder.payload;
this.destination = builder.destination;
}

/**
Expand Down Expand Up @@ -438,6 +448,16 @@ public <T extends Payload<?>> T getPayload() {
return (T) payload;
}

/**
* Returns the log path destination name type associated with log entry. By default, project name
* based destination is used.
*
* @see <a href="https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry">logName</a>
*/
public LogDestinationName getDestination() {
return destination;
}

@Override
public int hashCode() {
return Objects.hash(
Expand All @@ -454,7 +474,8 @@ public int hashCode() {
getSpanId(),
traceSampled,
sourceLocation,
payload);
payload,
destination);
}

@Override
Expand All @@ -479,7 +500,8 @@ public boolean equals(Object obj) {
&& Objects.equals(getSpanId(), other.getSpanId())
&& Objects.equals(traceSampled, other.traceSampled)
&& Objects.equals(sourceLocation, other.sourceLocation)
&& Objects.equals(payload, other.payload);
&& Objects.equals(payload, other.payload)
&& Objects.equals(destination, other.destination);
}

@Override
Expand All @@ -499,6 +521,7 @@ public String toString() {
.add("traceSampled", traceSampled)
.add("sourceLocation", sourceLocation)
.add("payload", payload)
.add("destination", destination)
.toString();
}

Expand All @@ -510,8 +533,13 @@ public Builder toBuilder() {
com.google.logging.v2.LogEntry toPb(String projectId) {
com.google.logging.v2.LogEntry.Builder builder = payload.toPb();
builder.putAllLabels(labels);

if (logName != null) {
builder.setLogName(LogName.ofProjectLogName(projectId, logName).toString());
if (destination == null) {
builder.setLogName(LogName.ofProjectLogName(projectId, logName).toString());
} else {
builder.setLogName(destination.toLogName(logName).toString());
}
}
if (resource != null) {
builder.setResource(resource.toPb());
Expand Down Expand Up @@ -570,7 +598,12 @@ static LogEntry fromPb(com.google.logging.v2.LogEntry entryPb) {
builder.setLabels(entryPb.getLabelsMap());
builder.setSeverity(Severity.fromPb(entryPb.getSeverity()));
if (!entryPb.getLogName().equals("")) {
builder.setLogName(LogName.parse(entryPb.getLogName()).getLog());
LogName name = LogName.parse(entryPb.getLogName());
builder.setLogName(name.getLog());
LogDestinationName resource = LogDestinationName.fromLogName(name);
if (resource != null) {
builder.setDestination(resource);
}
}
if (!entryPb.getResource().equals(com.google.api.MonitoredResource.getDefaultInstance())) {
builder.setResource(MonitoredResource.fromPb(entryPb.getResource()));
Expand Down
Expand Up @@ -34,6 +34,7 @@
public class LogEntryTest {

private static final String LOG_NAME = "syslog";
private static final String PROJECT = "project";
private static final MonitoredResource RESOURCE =
MonitoredResource.newBuilder("cloudsql_database")
.setLabels(ImmutableMap.of("datasetId", "myDataset", "zone", "myZone"))
Expand Down Expand Up @@ -74,6 +75,11 @@ public String toString() {
JsonPayload.of(ImmutableMap.<String, Object>of("key", "val"));
private static final ProtoPayload PROTO_PAYLOAD =
ProtoPayload.of(Any.pack(Empty.getDefaultInstance()));
private static final LogDestinationName BILLING_NAME =
LogDestinationName.billingAccount("000000-111111-222222");
private static final LogDestinationName PROJECT_NAME = LogDestinationName.project(PROJECT);
private static final LogDestinationName FOLDER_NAME = LogDestinationName.folder("123456789");
private static final LogDestinationName ORG_NAME = LogDestinationName.organization("1122334455");
private static final LogEntry STRING_ENTRY =
LogEntry.newBuilder(STRING_PAYLOAD)
.setLogName(LOG_NAME)
Expand Down Expand Up @@ -122,6 +128,14 @@ public String toString() {
.setTraceSampled(TRACE_SAMPLED)
.setSourceLocation(SOURCE_LOCATION)
.build();
private static final LogEntry STRING_ENTRY_BILLING =
STRING_ENTRY.toBuilder().setDestination(BILLING_NAME).build();
private static final LogEntry STRING_ENTRY_PROJECT =
STRING_ENTRY.toBuilder().setDestination(PROJECT_NAME).build();
private static final LogEntry STRING_ENTRY_FOLDER =
STRING_ENTRY.toBuilder().setDestination(FOLDER_NAME).build();
private static final LogEntry STRING_ENTRY_ORG =
STRING_ENTRY.toBuilder().setDestination(ORG_NAME).build();

@Test
public void testOf() {
Expand Down Expand Up @@ -251,7 +265,7 @@ public void testBuilder() {

@Test
public void testToBuilder() {
compareLogEntry(STRING_ENTRY, STRING_ENTRY.toBuilder().build());
compareLogEntry(STRING_ENTRY, STRING_ENTRY.toBuilder().build(), true);
HttpRequest request =
HttpRequest.newBuilder()
.setRequestMethod(HttpRequest.RequestMethod.POST)
Expand Down Expand Up @@ -309,22 +323,45 @@ public void testToBuilder() {
.setTraceSampled(TRACE_SAMPLED)
.setSourceLocation(SOURCE_LOCATION)
.build();
compareLogEntry(STRING_ENTRY, logEntry);
compareLogEntry(STRING_ENTRY, logEntry, true);
}

@Test
public void testToAndFromPb() {
compareLogEntry(STRING_ENTRY, LogEntry.fromPb(STRING_ENTRY.toPb("project")));
compareLogEntry(JSON_ENTRY, LogEntry.fromPb(JSON_ENTRY.toPb("project")));
compareLogEntry(PROTO_ENTRY, LogEntry.fromPb(PROTO_ENTRY.toPb("project")));
compareLogEntry(STRING_ENTRY, LogEntry.fromPb(STRING_ENTRY.toPb(PROJECT)), false);
compareLogEntry(JSON_ENTRY, LogEntry.fromPb(JSON_ENTRY.toPb(PROJECT)), false);
compareLogEntry(PROTO_ENTRY, LogEntry.fromPb(PROTO_ENTRY.toPb(PROJECT)), false);
compareLogEntry(
STRING_ENTRY_BILLING, LogEntry.fromPb(STRING_ENTRY_BILLING.toPb(PROJECT)), true);
compareLogEntry(STRING_ENTRY_FOLDER, LogEntry.fromPb(STRING_ENTRY_FOLDER.toPb(PROJECT)), true);
compareLogEntry(STRING_ENTRY_ORG, LogEntry.fromPb(STRING_ENTRY_ORG.toPb(PROJECT)), true);
compareLogEntry(
STRING_ENTRY_PROJECT, LogEntry.fromPb(STRING_ENTRY_PROJECT.toPb(PROJECT)), true);
LogEntry logEntry = LogEntry.of(STRING_PAYLOAD);
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb("project")));
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb(PROJECT)), true);
logEntry = LogEntry.of(LOG_NAME, RESOURCE, STRING_PAYLOAD);
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb("project")));
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb(PROJECT)), false);
logEntry =
LogEntry.newBuilder(STRING_PAYLOAD)
.setLogName(LOG_NAME)
.setResource(RESOURCE)
.setDestination(FOLDER_NAME)
.build();
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb(PROJECT)), true);
}

@Test(expected = AssertionError.class)
public void testToAndFromPbWithExpectedFailure() {
LogEntry logEntry =
LogEntry.newBuilder(STRING_PAYLOAD).setLogName(LOG_NAME).setResource(RESOURCE).build();
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb(PROJECT)), true);
}

private void compareLogEntry(LogEntry expected, LogEntry value) {
assertEquals(expected, value);
private void compareLogEntry(LogEntry expected, LogEntry value, Boolean extraValidations) {
if (extraValidations) {
assertEquals(expected.hashCode(), value.hashCode());
assertEquals(expected, value);
}
assertEquals(expected.getLogName(), value.getLogName());
assertEquals(expected.getResource(), value.getResource());
assertEquals(expected.getTimestamp(), value.getTimestamp());
Expand All @@ -341,6 +378,5 @@ private void compareLogEntry(LogEntry expected, LogEntry value) {
assertEquals(expected.getTraceSampled(), value.getTraceSampled());
assertEquals(expected.getSourceLocation(), value.getSourceLocation());
assertEquals(expected.getPayload(), value.getPayload());
assertEquals(expected.hashCode(), value.hashCode());
}
}
Expand Up @@ -118,11 +118,13 @@ public class LoggingImplTest {
private static final LogEntry LOG_ENTRY1 =
LogEntry.newBuilder(StringPayload.of("entry1"))
.setLogName(LOG_NAME)
.setDestination(LogDestinationName.project(PROJECT))
.setResource(MONITORED_RESOURCE)
.build();
private static final LogEntry LOG_ENTRY2 =
LogEntry.newBuilder(StringPayload.of("entry2"))
.setLogName(LOG_NAME)
.setDestination(LogDestinationName.project(PROJECT))
.setResource(MONITORED_RESOURCE)
.build();
private static final Function<SinkInfo, LogSink> SINK_TO_PB_FUNCTION =
Expand Down

0 comments on commit 43ea0b4

Please sign in to comment.