Skip to content

Commit d7c0184

Browse files
committed
feat: add logging bucket destination for log sinks (#226)
1 parent b2cc344 commit d7c0184

File tree

2 files changed

+144
-1
lines changed

2 files changed

+144
-1
lines changed

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

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ public enum Type {
6262
DATASET,
6363

6464
/** Specifies a Google Cloud Pub/Sub topic as destination for the sink. */
65-
TOPIC;
65+
TOPIC,
66+
67+
/** Specifies a Logging bucket as destination for the sink. */
68+
LOGGING_BUCKET;
6669
}
6770

6871
/** Class for specifying a Google Cloud Storage bucket as destination for the sink. */
@@ -225,6 +228,106 @@ static DatasetDestination fromPb(String destinationPb) {
225228
}
226229
}
227230

231+
public static final class LoggingBucketDestination extends Destination {
232+
233+
private static final long serialVersionUID = 4894431968778789038L;
234+
private static final String BASE_NAME = "logging.googleapis.com/";
235+
private static final String REGEX =
236+
BASE_NAME + "projects/([^/]+)/locations/([^/]+)/buckets/([^/]+)";
237+
private static final Pattern PATTERN = Pattern.compile(REGEX);
238+
239+
private final String project;
240+
private final String location;
241+
private final String bucket;
242+
243+
LoggingBucketDestination(String project, String location, String bucket) {
244+
super(Type.LOGGING_BUCKET);
245+
this.project = project;
246+
this.location = checkNotNull(location);
247+
this.bucket = checkNotNull(bucket);
248+
}
249+
250+
/**
251+
* Returns the name of the project where the Google Cloud BigQuery dataset resides. If {@code
252+
* null}, the default project is used.
253+
*/
254+
public String getProject() {
255+
return project;
256+
}
257+
258+
/** Returns the name of the bucket location this destination represents. */
259+
public String getLocation() {
260+
return location;
261+
}
262+
263+
/** Returns the name of the logging bucket this destination represents. */
264+
public String getBucket() {
265+
return bucket;
266+
}
267+
268+
@Override
269+
public boolean equals(Object obj) {
270+
if (obj == this) {
271+
return true;
272+
}
273+
if (obj == null || !(obj instanceof LoggingBucketDestination)) {
274+
return false;
275+
}
276+
LoggingBucketDestination other = (LoggingBucketDestination) obj;
277+
return baseEquals(other)
278+
&& Objects.equals(project, other.project)
279+
&& Objects.equals(location, other.location);
280+
}
281+
282+
@Override
283+
public int hashCode() {
284+
return Objects.hash(baseHashCode(), project, location, bucket);
285+
}
286+
287+
@Override
288+
public String toString() {
289+
return MoreObjects.toStringHelper(this)
290+
.add("project", project)
291+
.add("location", location)
292+
.add("bucket", bucket)
293+
.toString();
294+
}
295+
296+
@Override
297+
String toPb(String projectId) {
298+
String project = this.project == null ? projectId : this.project;
299+
return BASE_NAME + "projects/" + project + "/locations/" + location + "/buckets/" + bucket;
300+
}
301+
302+
/**
303+
* Creates a {@code DatasetDestination} object given the name of the project and dataset to be
304+
* used as sink destination.
305+
*/
306+
public static LoggingBucketDestination of(String project, String location, String bucket) {
307+
return new LoggingBucketDestination(project, location, bucket);
308+
}
309+
310+
/**
311+
* Creates a {@code DatasetDestination} object given the name of the dataset to be used as
312+
* sink destination. Dataset is assumed to reside in the default project.
313+
*/
314+
public static LoggingBucketDestination of(String location, String bucket) {
315+
return new LoggingBucketDestination(null, location, bucket);
316+
}
317+
318+
static boolean matchesDestination(String destinationPb) {
319+
return PATTERN.matcher(destinationPb).matches();
320+
}
321+
322+
static LoggingBucketDestination fromPb(String destinationPb) {
323+
Matcher matcher = PATTERN.matcher(destinationPb);
324+
if (!matcher.matches()) {
325+
throw new IllegalArgumentException(destinationPb + " is not a valid sink destination");
326+
}
327+
return new LoggingBucketDestination(matcher.group(1), matcher.group(2), matcher.group(3));
328+
}
329+
}
330+
228331
/** Class for specifying a Google Cloud BigQuery dataset as destination for the sink. */
229332
public static final class TopicDestination extends Destination {
230333

@@ -344,6 +447,8 @@ static <T extends Destination> T fromPb(String destinationPb) {
344447
return (T) DatasetDestination.fromPb(destinationPb);
345448
} else if (TopicDestination.matchesDestination(destinationPb)) {
346449
return (T) TopicDestination.fromPb(destinationPb);
450+
} else if (LoggingBucketDestination.matchesDestination(destinationPb)) {
451+
return (T) LoggingBucketDestination.fromPb(destinationPb);
347452
}
348453
throw new IllegalArgumentException(destinationPb + " is not a valid sink destination");
349454
}

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.cloud.logging.SinkInfo.Destination;
2424
import com.google.cloud.logging.SinkInfo.Destination.BucketDestination;
2525
import com.google.cloud.logging.SinkInfo.Destination.DatasetDestination;
26+
import com.google.cloud.logging.SinkInfo.Destination.LoggingBucketDestination;
2627
import com.google.cloud.logging.SinkInfo.Destination.TopicDestination;
2728
import com.google.cloud.logging.SinkInfo.VersionFormat;
2829
import org.junit.Test;
@@ -37,6 +38,8 @@ public class SinkInfoTest {
3738
private static final DatasetDestination DATASET_DESTINATION =
3839
DatasetDestination.of("project", "dataset");
3940
private static final TopicDestination TOPIC_DESTINATION = TopicDestination.of("project", "topic");
41+
private static final LoggingBucketDestination LOGGING_BUCKET_DESTINATION =
42+
LoggingBucketDestination.of("project", "location", "bucket");
4043
private static final SinkInfo BUCKET_SINK_INFO =
4144
SinkInfo.newBuilder(NAME, BUCKET_DESTINATION)
4245
.setFilter(FILTER)
@@ -79,6 +82,19 @@ public void testOfTopicDestination() {
7982
assertEquals("topic", topicDestination.getTopic());
8083
}
8184

85+
@Test
86+
public void testOfLoggingBucketDestination() {
87+
assertEquals(Destination.Type.LOGGING_BUCKET, LOGGING_BUCKET_DESTINATION.getType());
88+
assertEquals("project", LOGGING_BUCKET_DESTINATION.getProject());
89+
assertEquals("location", LOGGING_BUCKET_DESTINATION.getLocation());
90+
assertEquals("bucket", LOGGING_BUCKET_DESTINATION.getBucket());
91+
LoggingBucketDestination loggingBucketDestination =
92+
LoggingBucketDestination.of("location", "bucket");
93+
assertNull(loggingBucketDestination.getProject());
94+
assertEquals("location", loggingBucketDestination.getLocation());
95+
assertEquals("bucket", loggingBucketDestination.getBucket());
96+
}
97+
8298
@Test
8399
public void testToAndFromPbDestination() {
84100
BucketDestination bucketDestination = Destination.fromPb(BUCKET_DESTINATION.toPb("other"));
@@ -95,6 +111,13 @@ public void testToAndFromPbDestination() {
95111
assertEquals("project", topicDestination.getProject());
96112
assertEquals("topic", topicDestination.getTopic());
97113
compareTopicDestination(TOPIC_DESTINATION, topicDestination);
114+
LoggingBucketDestination loggingBucketDestination =
115+
Destination.fromPb(LOGGING_BUCKET_DESTINATION.toPb("other"));
116+
assertEquals(Destination.Type.LOGGING_BUCKET, loggingBucketDestination.getType());
117+
assertEquals("project", loggingBucketDestination.getProject());
118+
assertEquals("location", loggingBucketDestination.getLocation());
119+
assertEquals("bucket", loggingBucketDestination.getBucket());
120+
compareLoggingBucketDestination(LOGGING_BUCKET_DESTINATION, loggingBucketDestination);
98121
try {
99122
Destination.fromPb("wrongDestination");
100123
fail();
@@ -113,6 +136,11 @@ public void testToAndFromPbDestination_NoProjectId() {
113136
TopicDestination.fromPb(TopicDestination.of("topic").toPb("project"));
114137
assertEquals("project", topicDestination.getProject());
115138
compareTopicDestination(TOPIC_DESTINATION, topicDestination);
139+
LoggingBucketDestination loggingBucketDestination =
140+
LoggingBucketDestination.fromPb(
141+
LoggingBucketDestination.of("location", "bucket").toPb("project"));
142+
assertEquals("project", loggingBucketDestination.getProject());
143+
compareLoggingBucketDestination(LOGGING_BUCKET_DESTINATION, loggingBucketDestination);
116144
}
117145

118146
@Test
@@ -209,6 +237,16 @@ private void compareTopicDestination(TopicDestination expected, TopicDestination
209237
assertEquals(expected.toString(), value.toString());
210238
}
211239

240+
private void compareLoggingBucketDestination(
241+
LoggingBucketDestination expected, LoggingBucketDestination value) {
242+
assertEquals(expected, value);
243+
assertEquals(expected.getProject(), value.getProject());
244+
assertEquals(expected.getLocation(), value.getLocation());
245+
assertEquals(expected.getBucket(), value.getBucket());
246+
assertEquals(expected.hashCode(), value.hashCode());
247+
assertEquals(expected.toString(), value.toString());
248+
}
249+
212250
private void compareSinkInfo(SinkInfo expected, SinkInfo value) {
213251
assertEquals(expected, value);
214252
assertEquals(expected.getName(), value.getName());

0 commit comments

Comments
 (0)