Skip to content

Commit

Permalink
Implement LogEntry methods and tests (#1110)
Browse files Browse the repository at this point in the history
  • Loading branch information
mziccard authored Jul 14, 2016
1 parent 23ad3e1 commit e3321a7
Show file tree
Hide file tree
Showing 3 changed files with 576 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
package com.google.cloud.logging;

import com.google.cloud.AsyncPage;
import com.google.cloud.MonitoredResource;
import com.google.cloud.MonitoredResourceDescriptor;
import com.google.cloud.Page;
import com.google.cloud.Service;
import com.google.common.collect.ImmutableMap;

import java.util.Map;
import java.util.concurrent.Future;
Expand Down Expand Up @@ -62,6 +64,134 @@ public static ListOption pageToken(String pageToken) {
}
}

/**
* Class for specifying options for writing log entries.
*/
final class WriteOption extends Option {

private static final long serialVersionUID = 715900132268584612L;

enum OptionType implements Option.OptionType {
LOG_NAME, RESOURCE, LABELS;

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

private WriteOption(OptionType option, Object value) {
super(option, value);
}

/**
* Returns an option to specify a default log name (see {@link LogEntry#logName()}) for those
* log entries that do not specify their own log name. Example: {@code syslog}.
*/
public static WriteOption logName(String logName) {
return new WriteOption(OptionType.LOG_NAME, logName);
}

/**
* Returns an option to specify a default monitored resource (see {@link LogEntry#resource()})
* for those log entries that do not specify their own resource.
*/
public static WriteOption resource(MonitoredResource resource) {
return new WriteOption(OptionType.RESOURCE, resource);
}

/**
* Sets an option to specify (key, value) pairs that are added to the {@link LogEntry#labels()}
* of each log entry written, except when a log entry already has a value associated to the
* same key.
*/
public static WriteOption labels(Map<String, String> labels) {
return new WriteOption(OptionType.LABELS, ImmutableMap.copyOf(labels));
}
}

/**
* Fields according to which log entries can be sorted.
*/
enum SortingField {
TIMESTAMP;

String selector() {
return name().toLowerCase();
}
}

/**
* Sorting orders available when listing log entries.
*/
enum SortingOrder {
DESCENDING("desc"),
ASCENDING("asc");

private final String selector;

SortingOrder(String selector) {
this.selector = selector;
}

String selector() {
return selector;
}
}

/**
* Class for specifying options for listing log entries.
*/
final class EntryListOption extends Option {

private static final long serialVersionUID = -1561159676386917050L;

enum OptionType implements Option.OptionType {
PAGE_SIZE, PAGE_TOKEN, ORDER_BY, FILTER;

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

private EntryListOption(OptionType option, Object value) {
super(option, value);
}

/**
* Returns an option to specify the maximum number of log entries returned per page.
*/
public static EntryListOption pageSize(int pageSize) {
return new EntryListOption(OptionType.PAGE_SIZE, pageSize);
}

/**
* Returns an option to specify the page token from which to start listing log entries.
*/
public static EntryListOption pageToken(String pageToken) {
return new EntryListOption(OptionType.PAGE_TOKEN, pageToken);
}

/**
* Returns an option to sort log entries. If not specified, log entries are sorted in ascending
* (most-recent last) order with respect to the {@link LogEntry#timestamp()} value.
*/
public static EntryListOption sortOrder(SortingField field, SortingOrder order) {
return new EntryListOption(OptionType.ORDER_BY, field.selector() + ' ' + order.selector());
}

/**
* Returns an option to specify a filter to the log entries to be listed.
*
* @see <a href="https://cloud.google.com/logging/docs/view/advanced_filters">Advanced Logs
* Filters</a>
*/
public static EntryListOption filter(String filter) {
return new EntryListOption(OptionType.FILTER, filter);
}
}

/**
* Creates a new sink.
*
Expand Down Expand Up @@ -244,4 +374,49 @@ Future<AsyncPage<MonitoredResourceDescriptor>> listMonitoredResourceDescriptorsA
* if it was not found.
*/
Future<Boolean> deleteMetricAsync(String metric);

/**
* Writes log entries to Cloud Logging. Use {@link WriteOption#logName(String)} to provide a log
* name for those entries that do not specify one. Use
* {@link WriteOption#resource(MonitoredResource)} to provide a monitored resource for those
* entries that do not specify one. Use {@link WriteOption#labels(Map)} to provide some labels
* to be added to every entry in {@code logEntries}.
*/
void write(Iterable<LogEntry> logEntries, WriteOption... options);

/**
* Sends a request to log entries to Cloud Logging. Use {@link WriteOption#logName(String)} to
* provide a log name for those entries that do not specify one. Use
* {@link WriteOption#resource(MonitoredResource)} to provide a monitored resource for those
* entries that do not specify one. Use {@link WriteOption#labels(Map)} to provide some labels
* to be added to every entry in {@code logEntries}. The method returns a {@code Future} object
* that can be used to wait for the write operation to be completed.
*/
Future<Void> writeAsync(Iterable<LogEntry> logEntries, WriteOption... options);

/**
* Lists log entries. This method returns a {@link Page} object that can be used to consume
* paginated results. Use {@link EntryListOption#pageSize(int)} to specify the page size. Use
* {@link EntryListOption#pageToken(String)} to specify the page token from which to start listing
* entries. Use {@link EntryListOption#sortOrder(SortingField, SortingOrder)} to sort log entries
* according to your preferred order (default is most-recent last). Use
* {@link EntryListOption#filter(String)} to filter listed log entries.
*
* @throws LoggingException upon failure
*/
Page<LogEntry> listLogEntries(EntryListOption... options);

/**
* Sends a request for listing log entries. This method returns a {@code Future} object to consume
* the result. {@link Future#get()} returns an {@link AsyncPage} object that can be used to
* asynchronously handle paginated results. Use {@link EntryListOption#pageSize(int)} to specify
* the page size. Use {@link EntryListOption#pageToken(String)} to specify the page token from
* which to start listing entries. Use
* {@link EntryListOption#sortOrder(SortingField, SortingOrder)} to sort log entries according to
* your preferred order (default is most-recent last). Use {@link EntryListOption#filter(String)}
* to filter listed log entries.
*
* @throws LoggingException upon failure
*/
Future<AsyncPage<LogEntry>> listLogEntriesAsync(EntryListOption... options);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@
package com.google.cloud.logging;

import static com.google.api.client.util.Preconditions.checkArgument;
import static com.google.cloud.logging.Logging.EntryListOption.OptionType.FILTER;
import static com.google.cloud.logging.Logging.EntryListOption.OptionType.ORDER_BY;
import static com.google.cloud.logging.Logging.ListOption.OptionType.PAGE_SIZE;
import static com.google.cloud.logging.Logging.ListOption.OptionType.PAGE_TOKEN;
import static com.google.cloud.logging.Logging.WriteOption.OptionType.LABELS;
import static com.google.cloud.logging.Logging.WriteOption.OptionType.LOG_NAME;
import static com.google.cloud.logging.Logging.WriteOption.OptionType.RESOURCE;
import static com.google.common.util.concurrent.Futures.lazyTransform;

import com.google.cloud.AsyncPage;
import com.google.cloud.AsyncPageImpl;
import com.google.cloud.BaseService;
import com.google.cloud.MonitoredResource;
import com.google.cloud.MonitoredResourceDescriptor;
import com.google.cloud.Page;
import com.google.cloud.PageImpl;
Expand All @@ -34,6 +40,7 @@
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Uninterruptibles;
Expand All @@ -44,6 +51,8 @@
import com.google.logging.v2.DeleteSinkRequest;
import com.google.logging.v2.GetLogMetricRequest;
import com.google.logging.v2.GetSinkRequest;
import com.google.logging.v2.ListLogEntriesRequest;
import com.google.logging.v2.ListLogEntriesResponse;
import com.google.logging.v2.ListLogMetricsRequest;
import com.google.logging.v2.ListLogMetricsResponse;
import com.google.logging.v2.ListMonitoredResourceDescriptorsRequest;
Expand All @@ -52,6 +61,8 @@
import com.google.logging.v2.ListSinksResponse;
import com.google.logging.v2.UpdateLogMetricRequest;
import com.google.logging.v2.UpdateSinkRequest;
import com.google.logging.v2.WriteLogEntriesRequest;
import com.google.logging.v2.WriteLogEntriesResponse;
import com.google.protobuf.Empty;

import java.util.List;
Expand All @@ -71,6 +82,13 @@ public Boolean apply(Empty input) {
return input != null;
}
};
private static final Function<WriteLogEntriesResponse, Void> WRITE_RESPONSE_TO_VOID_FUNCTION =
new Function<WriteLogEntriesResponse, Void>() {
@Override
public Void apply(WriteLogEntriesResponse input) {
return null;
}
};

LoggingImpl(LoggingOptions options) {
super(options);
Expand Down Expand Up @@ -154,6 +172,21 @@ public Future<AsyncPage<Metric>> nextPage() {
}
}

private static class LogEntryPageFetcher extends BasePageFetcher<LogEntry> {

private static final long serialVersionUID = 4001239712280747734L;

LogEntryPageFetcher(LoggingOptions serviceOptions, String cursor,
Map<Option.OptionType, ?> requestOptions) {
super(serviceOptions, cursor, requestOptions);
}

@Override
public Future<AsyncPage<LogEntry>> nextPage() {
return listLogEntriesAsync(serviceOptions(), requestOptions());
}
}

@Override
public Sink create(SinkInfo sink) {
return get(createAsync(sink));
Expand Down Expand Up @@ -408,6 +441,87 @@ public Future<Boolean> deleteMetricAsync(String metric) {
return lazyTransform(rpc.delete(request), EMPTY_TO_BOOLEAN_FUNCTION);
}

private static WriteLogEntriesRequest writeLogEntriesRequest(LoggingOptions serviceOptions,
Iterable<LogEntry> logEntries, Map<Option.OptionType, ?> options) {
String projectId = serviceOptions.projectId();
WriteLogEntriesRequest.Builder builder = WriteLogEntriesRequest.newBuilder();
String logName = LOG_NAME.get(options);
if (logName != null) {
builder.setLogName(LoggingServiceV2Api.formatLogName(projectId, logName));
}
MonitoredResource resource = RESOURCE.get(options);
if (resource != null) {
builder.setResource(resource.toPb());
}
Map<String, String> labels = LABELS.get(options);
if (labels != null) {
builder.putAllLabels(labels);
}
builder.addAllEntries(Iterables.transform(logEntries, LogEntry.toPbFunction(projectId)));
return builder.build();
}

public void write(Iterable<LogEntry> logEntries, WriteOption... options) {
get(writeAsync(logEntries, options));
}

public Future<Void> writeAsync(Iterable<LogEntry> logEntries, WriteOption... options) {
return lazyTransform(
rpc.write(writeLogEntriesRequest(options(), logEntries, optionMap(options))),
WRITE_RESPONSE_TO_VOID_FUNCTION);
}

private static ListLogEntriesRequest listLogEntriesRequest(LoggingOptions serviceOptions,
Map<Option.OptionType, ?> options) {
ListLogEntriesRequest.Builder builder = ListLogEntriesRequest.newBuilder();
builder.addProjectIds(serviceOptions.projectId());
Integer pageSize = PAGE_SIZE.get(options);
if (pageSize != null) {
builder.setPageSize(pageSize);
}
String pageToken = PAGE_TOKEN.get(options);
if (pageToken != null) {
builder.setPageToken(pageToken);
}
String orderBy = ORDER_BY.get(options);
if (orderBy != null) {
builder.setOrderBy(orderBy);
}
String filter = FILTER.get(options);
if (filter != null) {
builder.setFilter(filter);
}
return builder.build();
}

private static Future<AsyncPage<LogEntry>> listLogEntriesAsync(
final LoggingOptions serviceOptions, final Map<Option.OptionType, ?> options) {
final ListLogEntriesRequest request = listLogEntriesRequest(serviceOptions, options);
Future<ListLogEntriesResponse> list = serviceOptions.rpc().list(request);
return lazyTransform(list, new Function<ListLogEntriesResponse, AsyncPage<LogEntry>>() {
@Override
public AsyncPage<LogEntry> apply(ListLogEntriesResponse listLogEntrysResponse) {
List<LogEntry> entries = listLogEntrysResponse.getEntriesList() == null
? ImmutableList.<LogEntry>of() : Lists.transform(listLogEntrysResponse.getEntriesList(),
LogEntry.FROM_PB_FUNCTION);
String cursor = listLogEntrysResponse.getNextPageToken().equals("") ? null
: listLogEntrysResponse.getNextPageToken();
return new AsyncPageImpl<>(new LogEntryPageFetcher(serviceOptions, cursor, options), cursor,
entries);
}
});
}

@Override
public Page<LogEntry> listLogEntries(EntryListOption... options) {
return get(listLogEntriesAsync(options));
}

@Override
public Future<AsyncPage<LogEntry>> listLogEntriesAsync(EntryListOption... options) {
return listLogEntriesAsync(options(), optionMap(options));
}

@Override
public void close() throws Exception {
if (closed) {
Expand Down
Loading

0 comments on commit e3321a7

Please sign in to comment.