Skip to content

Commit 43ea0b4

Browse files
feat: Add destination property into LogEntry (#720)
* 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>
1 parent 1fa3a6e commit 43ea0b4

File tree

4 files changed

+214
-14
lines changed

4 files changed

+214
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2021 Google LLC
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+
* https://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+
package com.google.cloud.logging;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.base.Preconditions.checkNotNull;
21+
22+
import com.google.logging.v2.LogName;
23+
import java.util.Map;
24+
25+
/**
26+
* Class for specifying resource name of the log to which this log entry belongs (see 'logName'
27+
* parameter in https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry)
28+
*/
29+
public final class LogDestinationName extends Option {
30+
31+
enum DestinationType implements Option.OptionType {
32+
PROJECT,
33+
FOLDER,
34+
ORGANIZATION,
35+
BILLINGACCOUNT;
36+
37+
@SuppressWarnings("unchecked")
38+
<T> T get(Map<Option.OptionType, ?> options) {
39+
return (T) options.get(this);
40+
}
41+
}
42+
43+
private LogDestinationName(Option.OptionType option, Object value) {
44+
super(option, value);
45+
checkArgument(!checkNotNull(value).toString().trim().isEmpty());
46+
}
47+
48+
/**
49+
* Returns an option which sets and validates project ID resource name for log entries.
50+
*
51+
* @param id corresponds to PROJECT_ID token in 'logName' field described in
52+
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
53+
*/
54+
public static LogDestinationName project(String id) {
55+
return new LogDestinationName(DestinationType.PROJECT, id);
56+
}
57+
58+
/**
59+
* Returns an option which sets and validates project ID resource name for log entries.
60+
*
61+
* @param id corresponds to FOLDER_ID token in 'logName' field described in
62+
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
63+
*/
64+
public static LogDestinationName folder(String id) {
65+
return new LogDestinationName(DestinationType.FOLDER, id);
66+
}
67+
68+
/**
69+
* Returns an option which sets and validates project ID resource name for log entries.
70+
*
71+
* @param id corresponds to ORGANIZATION_ID token in 'logName' field described in
72+
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
73+
*/
74+
public static LogDestinationName organization(String id) {
75+
return new LogDestinationName(DestinationType.ORGANIZATION, id);
76+
}
77+
78+
/**
79+
* Returns an option which sets and validates project ID resource name for log entries.
80+
*
81+
* @param id corresponds to BILLING_ACCOUNT_ID token in 'logName' field described in
82+
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
83+
*/
84+
public static LogDestinationName billingAccount(String id) {
85+
return new LogDestinationName(DestinationType.BILLINGACCOUNT, id);
86+
}
87+
88+
/** Creates a {@code LogEntry} object for given log ID. */
89+
public LogName toLogName(String logId) {
90+
if (logId == null) {
91+
return null;
92+
}
93+
94+
switch ((DestinationType) getOptionType()) {
95+
case PROJECT:
96+
return LogName.ofProjectLogName(getValue().toString(), logId);
97+
98+
case FOLDER:
99+
return LogName.ofFolderLogName(getValue().toString(), logId);
100+
101+
case ORGANIZATION:
102+
return LogName.ofOrganizationLogName(getValue().toString(), logId);
103+
104+
case BILLINGACCOUNT:
105+
return LogName.ofBillingAccountLogName(getValue().toString(), logId);
106+
}
107+
108+
return null;
109+
}
110+
111+
/** Creates a {@code LogDestinationName} object from given {@code LogName}. */
112+
public static LogDestinationName fromLogName(LogName logName) {
113+
if (logName == null) {
114+
return null;
115+
}
116+
117+
if (logName.getProject() != null) {
118+
return project(logName.getProject());
119+
} else if (logName.getBillingAccount() != null) {
120+
return billingAccount(logName.getBillingAccount());
121+
} else if (logName.getFolder() != null) {
122+
return folder(logName.getFolder());
123+
} else if (logName.getOrganization() != null) {
124+
return organization(logName.getOrganization());
125+
}
126+
127+
return null;
128+
}
129+
}

google-cloud-logging/src/main/java/com/google/cloud/logging/LogEntry.java

+37-4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public LogEntry apply(com.google.logging.v2.LogEntry pb) {
6666
private final boolean traceSampled;
6767
private final SourceLocation sourceLocation;
6868
private final Payload<?> payload;
69+
private final LogDestinationName destination;
6970

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

8890
Builder(Payload<?> payload) {
8991
this.payload = payload;
@@ -104,6 +106,7 @@ public static class Builder {
104106
this.traceSampled = entry.traceSampled;
105107
this.sourceLocation = entry.sourceLocation;
106108
this.payload = entry.payload;
109+
this.destination = entry.destination;
107110
}
108111

109112
/**
@@ -282,6 +285,12 @@ public Builder setPayload(Payload<?> payload) {
282285
return this;
283286
}
284287

288+
/** Sets the log path destination name type associated with the log entry. */
289+
public Builder setDestination(LogDestinationName destination) {
290+
this.destination = destination;
291+
return this;
292+
}
293+
285294
/** Creates a {@code LogEntry} object for this builder. */
286295
public LogEntry build() {
287296
return new LogEntry(this);
@@ -303,6 +312,7 @@ public LogEntry build() {
303312
this.traceSampled = builder.traceSampled;
304313
this.sourceLocation = builder.sourceLocation;
305314
this.payload = builder.payload;
315+
this.destination = builder.destination;
306316
}
307317

308318
/**
@@ -438,6 +448,16 @@ public <T extends Payload<?>> T getPayload() {
438448
return (T) payload;
439449
}
440450

451+
/**
452+
* Returns the log path destination name type associated with log entry. By default, project name
453+
* based destination is used.
454+
*
455+
* @see <a href="https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry">logName</a>
456+
*/
457+
public LogDestinationName getDestination() {
458+
return destination;
459+
}
460+
441461
@Override
442462
public int hashCode() {
443463
return Objects.hash(
@@ -454,7 +474,8 @@ public int hashCode() {
454474
getSpanId(),
455475
traceSampled,
456476
sourceLocation,
457-
payload);
477+
payload,
478+
destination);
458479
}
459480

460481
@Override
@@ -479,7 +500,8 @@ public boolean equals(Object obj) {
479500
&& Objects.equals(getSpanId(), other.getSpanId())
480501
&& Objects.equals(traceSampled, other.traceSampled)
481502
&& Objects.equals(sourceLocation, other.sourceLocation)
482-
&& Objects.equals(payload, other.payload);
503+
&& Objects.equals(payload, other.payload)
504+
&& Objects.equals(destination, other.destination);
483505
}
484506

485507
@Override
@@ -499,6 +521,7 @@ public String toString() {
499521
.add("traceSampled", traceSampled)
500522
.add("sourceLocation", sourceLocation)
501523
.add("payload", payload)
524+
.add("destination", destination)
502525
.toString();
503526
}
504527

@@ -510,8 +533,13 @@ public Builder toBuilder() {
510533
com.google.logging.v2.LogEntry toPb(String projectId) {
511534
com.google.logging.v2.LogEntry.Builder builder = payload.toPb();
512535
builder.putAllLabels(labels);
536+
513537
if (logName != null) {
514-
builder.setLogName(LogName.ofProjectLogName(projectId, logName).toString());
538+
if (destination == null) {
539+
builder.setLogName(LogName.ofProjectLogName(projectId, logName).toString());
540+
} else {
541+
builder.setLogName(destination.toLogName(logName).toString());
542+
}
515543
}
516544
if (resource != null) {
517545
builder.setResource(resource.toPb());
@@ -570,7 +598,12 @@ static LogEntry fromPb(com.google.logging.v2.LogEntry entryPb) {
570598
builder.setLabels(entryPb.getLabelsMap());
571599
builder.setSeverity(Severity.fromPb(entryPb.getSeverity()));
572600
if (!entryPb.getLogName().equals("")) {
573-
builder.setLogName(LogName.parse(entryPb.getLogName()).getLog());
601+
LogName name = LogName.parse(entryPb.getLogName());
602+
builder.setLogName(name.getLog());
603+
LogDestinationName resource = LogDestinationName.fromLogName(name);
604+
if (resource != null) {
605+
builder.setDestination(resource);
606+
}
574607
}
575608
if (!entryPb.getResource().equals(com.google.api.MonitoredResource.getDefaultInstance())) {
576609
builder.setResource(MonitoredResource.fromPb(entryPb.getResource()));

google-cloud-logging/src/test/java/com/google/cloud/logging/LogEntryTest.java

+46-10
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
public class LogEntryTest {
3535

3636
private static final String LOG_NAME = "syslog";
37+
private static final String PROJECT = "project";
3738
private static final MonitoredResource RESOURCE =
3839
MonitoredResource.newBuilder("cloudsql_database")
3940
.setLabels(ImmutableMap.of("datasetId", "myDataset", "zone", "myZone"))
@@ -74,6 +75,11 @@ public String toString() {
7475
JsonPayload.of(ImmutableMap.<String, Object>of("key", "val"));
7576
private static final ProtoPayload PROTO_PAYLOAD =
7677
ProtoPayload.of(Any.pack(Empty.getDefaultInstance()));
78+
private static final LogDestinationName BILLING_NAME =
79+
LogDestinationName.billingAccount("000000-111111-222222");
80+
private static final LogDestinationName PROJECT_NAME = LogDestinationName.project(PROJECT);
81+
private static final LogDestinationName FOLDER_NAME = LogDestinationName.folder("123456789");
82+
private static final LogDestinationName ORG_NAME = LogDestinationName.organization("1122334455");
7783
private static final LogEntry STRING_ENTRY =
7884
LogEntry.newBuilder(STRING_PAYLOAD)
7985
.setLogName(LOG_NAME)
@@ -122,6 +128,14 @@ public String toString() {
122128
.setTraceSampled(TRACE_SAMPLED)
123129
.setSourceLocation(SOURCE_LOCATION)
124130
.build();
131+
private static final LogEntry STRING_ENTRY_BILLING =
132+
STRING_ENTRY.toBuilder().setDestination(BILLING_NAME).build();
133+
private static final LogEntry STRING_ENTRY_PROJECT =
134+
STRING_ENTRY.toBuilder().setDestination(PROJECT_NAME).build();
135+
private static final LogEntry STRING_ENTRY_FOLDER =
136+
STRING_ENTRY.toBuilder().setDestination(FOLDER_NAME).build();
137+
private static final LogEntry STRING_ENTRY_ORG =
138+
STRING_ENTRY.toBuilder().setDestination(ORG_NAME).build();
125139

126140
@Test
127141
public void testOf() {
@@ -251,7 +265,7 @@ public void testBuilder() {
251265

252266
@Test
253267
public void testToBuilder() {
254-
compareLogEntry(STRING_ENTRY, STRING_ENTRY.toBuilder().build());
268+
compareLogEntry(STRING_ENTRY, STRING_ENTRY.toBuilder().build(), true);
255269
HttpRequest request =
256270
HttpRequest.newBuilder()
257271
.setRequestMethod(HttpRequest.RequestMethod.POST)
@@ -309,22 +323,45 @@ public void testToBuilder() {
309323
.setTraceSampled(TRACE_SAMPLED)
310324
.setSourceLocation(SOURCE_LOCATION)
311325
.build();
312-
compareLogEntry(STRING_ENTRY, logEntry);
326+
compareLogEntry(STRING_ENTRY, logEntry, true);
313327
}
314328

315329
@Test
316330
public void testToAndFromPb() {
317-
compareLogEntry(STRING_ENTRY, LogEntry.fromPb(STRING_ENTRY.toPb("project")));
318-
compareLogEntry(JSON_ENTRY, LogEntry.fromPb(JSON_ENTRY.toPb("project")));
319-
compareLogEntry(PROTO_ENTRY, LogEntry.fromPb(PROTO_ENTRY.toPb("project")));
331+
compareLogEntry(STRING_ENTRY, LogEntry.fromPb(STRING_ENTRY.toPb(PROJECT)), false);
332+
compareLogEntry(JSON_ENTRY, LogEntry.fromPb(JSON_ENTRY.toPb(PROJECT)), false);
333+
compareLogEntry(PROTO_ENTRY, LogEntry.fromPb(PROTO_ENTRY.toPb(PROJECT)), false);
334+
compareLogEntry(
335+
STRING_ENTRY_BILLING, LogEntry.fromPb(STRING_ENTRY_BILLING.toPb(PROJECT)), true);
336+
compareLogEntry(STRING_ENTRY_FOLDER, LogEntry.fromPb(STRING_ENTRY_FOLDER.toPb(PROJECT)), true);
337+
compareLogEntry(STRING_ENTRY_ORG, LogEntry.fromPb(STRING_ENTRY_ORG.toPb(PROJECT)), true);
338+
compareLogEntry(
339+
STRING_ENTRY_PROJECT, LogEntry.fromPb(STRING_ENTRY_PROJECT.toPb(PROJECT)), true);
320340
LogEntry logEntry = LogEntry.of(STRING_PAYLOAD);
321-
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb("project")));
341+
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb(PROJECT)), true);
322342
logEntry = LogEntry.of(LOG_NAME, RESOURCE, STRING_PAYLOAD);
323-
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb("project")));
343+
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb(PROJECT)), false);
344+
logEntry =
345+
LogEntry.newBuilder(STRING_PAYLOAD)
346+
.setLogName(LOG_NAME)
347+
.setResource(RESOURCE)
348+
.setDestination(FOLDER_NAME)
349+
.build();
350+
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb(PROJECT)), true);
351+
}
352+
353+
@Test(expected = AssertionError.class)
354+
public void testToAndFromPbWithExpectedFailure() {
355+
LogEntry logEntry =
356+
LogEntry.newBuilder(STRING_PAYLOAD).setLogName(LOG_NAME).setResource(RESOURCE).build();
357+
compareLogEntry(logEntry, LogEntry.fromPb(logEntry.toPb(PROJECT)), true);
324358
}
325359

326-
private void compareLogEntry(LogEntry expected, LogEntry value) {
327-
assertEquals(expected, value);
360+
private void compareLogEntry(LogEntry expected, LogEntry value, Boolean extraValidations) {
361+
if (extraValidations) {
362+
assertEquals(expected.hashCode(), value.hashCode());
363+
assertEquals(expected, value);
364+
}
328365
assertEquals(expected.getLogName(), value.getLogName());
329366
assertEquals(expected.getResource(), value.getResource());
330367
assertEquals(expected.getTimestamp(), value.getTimestamp());
@@ -341,6 +378,5 @@ private void compareLogEntry(LogEntry expected, LogEntry value) {
341378
assertEquals(expected.getTraceSampled(), value.getTraceSampled());
342379
assertEquals(expected.getSourceLocation(), value.getSourceLocation());
343380
assertEquals(expected.getPayload(), value.getPayload());
344-
assertEquals(expected.hashCode(), value.hashCode());
345381
}
346382
}

google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java

+2
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,13 @@ public class LoggingImplTest {
118118
private static final LogEntry LOG_ENTRY1 =
119119
LogEntry.newBuilder(StringPayload.of("entry1"))
120120
.setLogName(LOG_NAME)
121+
.setDestination(LogDestinationName.project(PROJECT))
121122
.setResource(MONITORED_RESOURCE)
122123
.build();
123124
private static final LogEntry LOG_ENTRY2 =
124125
LogEntry.newBuilder(StringPayload.of("entry2"))
125126
.setLogName(LOG_NAME)
127+
.setDestination(LogDestinationName.project(PROJECT))
126128
.setResource(MONITORED_RESOURCE)
127129
.build();
128130
private static final Function<SinkInfo, LogSink> SINK_TO_PB_FUNCTION =

0 commit comments

Comments
 (0)