diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/Logging.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/Logging.java new file mode 100644 index 000000000000..a177a3fd0f8c --- /dev/null +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/Logging.java @@ -0,0 +1,22 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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.google.cloud.logging; + +import com.google.cloud.Service; + +public interface Logging extends AutoCloseable, Service { +} diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingException.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingException.java new file mode 100644 index 000000000000..947a8741ab1f --- /dev/null +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingException.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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.google.cloud.logging; + +import com.google.api.gax.grpc.ApiException; +import com.google.cloud.BaseServiceException; + +import java.io.IOException; +import java.util.Set; + +/** + * Logging service exception. + */ +public final class LoggingException extends BaseServiceException { + + private static final long serialVersionUID = 449689219311927047L; + + public LoggingException(IOException ex, boolean idempotent) { + super(ex, idempotent); + } + + public LoggingException(ApiException apiException, boolean idempotent) { + super(apiException, idempotent); + } + + @Override + protected Set retryableErrors() { + return null; + } +} diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingFactory.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingFactory.java new file mode 100644 index 000000000000..919fc913ed8c --- /dev/null +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingFactory.java @@ -0,0 +1,25 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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.google.cloud.logging; + +import com.google.cloud.ServiceFactory; + +/** + * An interface for Logging factories. + */ +public interface LoggingFactory extends ServiceFactory { +} diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingOptions.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingOptions.java new file mode 100644 index 000000000000..5cb32b9dd1cc --- /dev/null +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingOptions.java @@ -0,0 +1,129 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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.google.cloud.logging; + +import com.google.cloud.GrpcServiceOptions; +import com.google.cloud.logging.spi.DefaultLoggingRpc; +import com.google.cloud.logging.spi.LoggingRpc; +import com.google.cloud.logging.spi.LoggingRpcFactory; +import com.google.common.collect.ImmutableSet; + +import java.io.IOException; +import java.util.Set; + +public class LoggingOptions extends GrpcServiceOptions { + + private static final long serialVersionUID = -2996451684945061075L; + private static final String LOGGING_SCOPE = "https://www.googleapis.com/auth/logging.admin"; + private static final Set SCOPES = ImmutableSet.of(LOGGING_SCOPE); + private static final String DEFAULT_HOST = "https://logging.googleapis.com"; + + public static class DefaultLoggingFactory implements LoggingFactory { + private static final LoggingFactory INSTANCE = new DefaultLoggingFactory(); + + @Override + public Logging create(LoggingOptions options) { + // todo(mziccard) uncomment once LoggingImpl is implemented + // return new LoggingImpl(options); + return null; + } + } + + /** + * Returns a default {@code LoggingOptions} instance. + */ + public static LoggingOptions defaultInstance() { + return builder().build(); + } + + public static class DefaultLoggingRpcFactory implements LoggingRpcFactory { + private static final LoggingRpcFactory INSTANCE = new DefaultLoggingRpcFactory(); + + @Override + public LoggingRpc create(LoggingOptions options) { + try { + return new DefaultLoggingRpc(options); + } catch (IOException e) { + throw new LoggingException(e, true); + } + } + } + + @Override + protected String defaultHost() { + return DEFAULT_HOST; + } + + public static class Builder extends + GrpcServiceOptions.Builder { + + private Builder() {} + + private Builder(LoggingOptions options) { + super(options); + } + + @Override + public LoggingOptions build() { + return new LoggingOptions(this); + } + } + + protected LoggingOptions(Builder builder) { + super(LoggingFactory.class, LoggingRpcFactory.class, builder); + } + + @Override + protected ExecutorFactory executorFactory() { + return super.executorFactory(); + } + + @Override + protected LoggingFactory defaultServiceFactory() { + return DefaultLoggingFactory.INSTANCE; + } + + @Override + protected LoggingRpcFactory defaultRpcFactory() { + return DefaultLoggingRpcFactory.INSTANCE; + } + + @Override + protected Set scopes() { + return SCOPES; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LoggingOptions && baseEquals((LoggingOptions) obj); + } + + @Override + public int hashCode() { + return baseHashCode(); + } + + @SuppressWarnings("unchecked") + @Override + public Builder toBuilder() { + return new Builder(this); + } + + public static Builder builder() { + return new Builder(); + } +} diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/spi/DefaultLoggingRpc.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/spi/DefaultLoggingRpc.java new file mode 100644 index 000000000000..dee1dbbc755b --- /dev/null +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/spi/DefaultLoggingRpc.java @@ -0,0 +1,255 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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.google.cloud.logging.spi; + +import com.google.api.gax.core.RetrySettings; +import com.google.api.gax.grpc.ApiCallSettings; +import com.google.api.gax.grpc.ApiException; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.cloud.AuthCredentials; +import com.google.cloud.GrpcServiceOptions.ExecutorFactory; +import com.google.cloud.RetryParams; +import com.google.cloud.logging.LoggingException; +import com.google.cloud.logging.LoggingOptions; +import com.google.cloud.logging.spi.v2.ConfigServiceV2Api; +import com.google.cloud.logging.spi.v2.ConfigServiceV2Settings; +import com.google.cloud.logging.spi.v2.LoggingServiceV2Api; +import com.google.cloud.logging.spi.v2.LoggingServiceV2Settings; +import com.google.cloud.logging.spi.v2.MetricsServiceV2Api; +import com.google.cloud.logging.spi.v2.MetricsServiceV2Settings; +import com.google.common.base.Function; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.logging.v2.CreateLogMetricRequest; +import com.google.logging.v2.CreateSinkRequest; +import com.google.logging.v2.DeleteLogMetricRequest; +import com.google.logging.v2.DeleteLogRequest; +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; +import com.google.logging.v2.ListMonitoredResourceDescriptorsResponse; +import com.google.logging.v2.ListSinksRequest; +import com.google.logging.v2.ListSinksResponse; +import com.google.logging.v2.LogMetric; +import com.google.logging.v2.LogSink; +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 io.grpc.ManagedChannel; +import io.grpc.Status.Code; +import io.grpc.netty.NegotiationType; +import io.grpc.netty.NettyChannelBuilder; + +import org.joda.time.Duration; + +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; + +public class DefaultLoggingRpc implements LoggingRpc { + + private final ConfigServiceV2Api configApi; + private final LoggingServiceV2Api loggingApi; + private final MetricsServiceV2Api metricsApi; + private final ScheduledExecutorService executor; + private final ExecutorFactory executorFactory; + + private boolean closed; + + private static final class InternalLoggingOptions extends LoggingOptions { + + private static final long serialVersionUID = -2107638980310672033L; + + private InternalLoggingOptions(LoggingOptions options) { + super(options.toBuilder()); + } + + @Override + protected ExecutorFactory executorFactory() { + return super.executorFactory(); + } + } + + public DefaultLoggingRpc(LoggingOptions options) throws IOException { + executorFactory = new InternalLoggingOptions(options).executorFactory(); + executor = executorFactory.get(); + try { + ConfigServiceV2Settings.Builder confBuilder = + ConfigServiceV2Settings.defaultBuilder().provideExecutorWith(executor, false); + LoggingServiceV2Settings.Builder logBuilder = + LoggingServiceV2Settings.defaultBuilder().provideExecutorWith(executor, false); + MetricsServiceV2Settings.Builder metricsBuilder = + MetricsServiceV2Settings.defaultBuilder().provideExecutorWith(executor, false); + // todo(mziccard): PublisherSettings should support null/absent credentials for testing + if (options.host().contains("localhost") + || options.authCredentials().equals(AuthCredentials.noAuth())) { + ManagedChannel channel = NettyChannelBuilder.forTarget(options.host()) + .negotiationType(NegotiationType.PLAINTEXT) + .build(); + confBuilder.provideChannelWith(channel, true); + logBuilder.provideChannelWith(channel, true); + metricsBuilder.provideChannelWith(channel, true); + } else { + GoogleCredentials credentials = options.authCredentials().credentials(); + confBuilder.provideChannelWith( + credentials.createScoped(ConfigServiceV2Settings.DEFAULT_SERVICE_SCOPES)); + logBuilder.provideChannelWith( + credentials.createScoped(LoggingServiceV2Settings.DEFAULT_SERVICE_SCOPES)); + metricsBuilder.provideChannelWith( + credentials.createScoped(MetricsServiceV2Settings.DEFAULT_SERVICE_SCOPES)); + } + ApiCallSettings.Builder callBuilder = apiCallSettings(options); + confBuilder.applyToAllApiMethods(callBuilder); + logBuilder.applyToAllApiMethods(callBuilder); + metricsBuilder.applyToAllApiMethods(callBuilder); + configApi = ConfigServiceV2Api.create(confBuilder.build()); + loggingApi = LoggingServiceV2Api.create(logBuilder.build()); + metricsApi = MetricsServiceV2Api.create(metricsBuilder.build()); + } catch (Exception ex) { + throw new IOException(ex); + } + } + + private static ApiCallSettings.Builder apiCallSettings(LoggingOptions options) { + // todo(mziccard): specify timeout these settings: + // retryParams.retryMaxAttempts(), retryParams.retryMinAttempts() + RetryParams retryParams = options.retryParams(); + final RetrySettings.Builder builder = RetrySettings.newBuilder() + .setTotalTimeout(Duration.millis(retryParams.totalRetryPeriodMillis())) + .setInitialRpcTimeout(Duration.millis(options.initialTimeout())) + .setRpcTimeoutMultiplier(options.timeoutMultiplier()) + .setMaxRpcTimeout(Duration.millis(options.maxTimeout())) + .setInitialRetryDelay(Duration.millis(retryParams.initialRetryDelayMillis())) + .setRetryDelayMultiplier(retryParams.retryDelayBackoffFactor()) + .setMaxRetryDelay(Duration.millis(retryParams.maxRetryDelayMillis())); + return ApiCallSettings.newBuilder().setRetrySettingsBuilder(builder); + } + + private static Future translate(ListenableFuture from, final boolean idempotent, + int... returnNullOn) { + final Set returnNullOnSet = Sets.newHashSetWithExpectedSize(returnNullOn.length); + for (int value : returnNullOn) { + returnNullOnSet.add(value); + } + return Futures.catching(from, ApiException.class, new Function() { + @Override + public V apply(ApiException exception) { + if (returnNullOnSet.contains(exception.getStatusCode().value())) { + return null; + } + throw new LoggingException(exception, idempotent); + } + }); + } + + @Override + public Future create(CreateSinkRequest request) { + return translate(configApi.createSinkCallable().futureCall(request), true); + } + + @Override + public Future update(UpdateSinkRequest request) { + return translate(configApi.updateSinkCallable().futureCall(request), true); + } + + @Override + public Future get(GetSinkRequest request) { + return translate(configApi.getSinkCallable().futureCall(request), true, Code.NOT_FOUND.value()); + } + + @Override + public Future list(ListSinksRequest request) { + return translate(configApi.listSinksCallable().futureCall(request), true); + } + + @Override + public Future delete(DeleteSinkRequest request) { + return translate(configApi.deleteSinkCallable().futureCall(request), true); + } + + @Override + public Future delete(DeleteLogRequest request) { + return translate(loggingApi.deleteLogCallable().futureCall(request), true, + Code.NOT_FOUND.value()); + } + + @Override + public Future write(WriteLogEntriesRequest request) { + return translate(loggingApi.writeLogEntriesCallable().futureCall(request), false); + } + + @Override + public Future list(ListLogEntriesRequest request) { + return translate(loggingApi.listLogEntriesCallable().futureCall(request), true); + } + + @Override + public Future list( + ListMonitoredResourceDescriptorsRequest request) { + return translate(loggingApi.listMonitoredResourceDescriptorsCallable().futureCall(request), + true); + } + + @Override + public Future create(CreateLogMetricRequest request) { + return translate(metricsApi.createLogMetricCallable().futureCall(request), true); + } + + @Override + public Future update(UpdateLogMetricRequest request) { + return translate(metricsApi.updateLogMetricCallable().futureCall(request), true); + } + + @Override + public Future get(GetLogMetricRequest request) { + return translate(metricsApi.getLogMetricCallable().futureCall(request), true, + Code.NOT_FOUND.value()); + } + + @Override + public Future list(ListLogMetricsRequest request) { + return translate(metricsApi.listLogMetricsCallable().futureCall(request), true); + } + + @Override + public Future delete(DeleteLogMetricRequest request) { + return translate(metricsApi.deleteLogMetricCallable().futureCall(request), true, + Code.NOT_FOUND.value()); + } + + @Override + public void close() throws Exception { + if (closed) { + return; + } + closed = true; + configApi.close(); + loggingApi.close(); + metricsApi.close(); + executorFactory.release(executor); + } +} diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/spi/LoggingRpc.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/spi/LoggingRpc.java new file mode 100644 index 000000000000..d708d502c20a --- /dev/null +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/spi/LoggingRpc.java @@ -0,0 +1,168 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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.google.cloud.logging.spi; + +import com.google.logging.v2.CreateLogMetricRequest; +import com.google.logging.v2.CreateSinkRequest; +import com.google.logging.v2.DeleteLogMetricRequest; +import com.google.logging.v2.DeleteLogRequest; +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; +import com.google.logging.v2.ListMonitoredResourceDescriptorsResponse; +import com.google.logging.v2.ListSinksRequest; +import com.google.logging.v2.ListSinksResponse; +import com.google.logging.v2.LogMetric; +import com.google.logging.v2.LogSink; +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.concurrent.Future; + +public interface LoggingRpc extends AutoCloseable { + + /** + * Sends a request to create a sink. This method returns a {@code Future} object to consume the + * result. {@link Future#get()} returns the created sink. + * + * @param request the request object containing all of the parameters for the API call + */ + Future create(CreateSinkRequest request); + + /** + * Sends a request to update a sink. If the sink does not exist, it is created. This method + * returns a {@code Future} object to consume the result. {@link Future#get()} returns the updated + * or created sink. + * + * @param request the request object containing all of the parameters for the API call + */ + Future update(UpdateSinkRequest request); + + /** + * Sends a request to get a sink. This method returns a {@code Future} object to consume the + * result. {@link Future#get()} returns the requested sink or {@code null} if not found. + * + * @param request the request object containing all of the parameters for the API call + */ + Future get(GetSinkRequest request); + + /** + * Sends a request to list the sinks in a project. This method returns a {@code Future} object to + * consume the result. {@link Future#get()} returns a response object containing the listing + * result. + * + * @param request the request object containing all of the parameters for the API call + */ + Future list(ListSinksRequest request); + + /** + * Sends a request to delete a sink. This method returns a {@code Future} object to consume the + * result. {@link Future#get()} returns {@link Empty#getDefaultInstance()} or {@code null} if the + * sink was not found. + * + * @param request the request object containing all of the parameters for the API call + */ + Future delete(DeleteSinkRequest request); + + /** + * Sends a request to deletes a log. This method returns a {@code Future} object to consume the + * result. {@link Future#get()} returns {@link Empty#getDefaultInstance()} or {@code null} if the + * log was not found. The deleted log will reappear if it receives new log entries. + * + * @param request the request object containing all of the parameters for the API call + */ + Future delete(DeleteLogRequest request); + + /** + * Sends a request to write log entries to Cloud Logging. This method returns a {@code Future} + * object to consume the result. {@link Future#get()} returns a response object for the write + * operation. + * + * @param request the request object containing all of the parameters for the API call + */ + Future write(WriteLogEntriesRequest request); + + /** + * Sends a request to list log entries. Use this method to retrieve log entries from Cloud + * Logging. This method returns a {@code Future} object to consume the result. + * {@link Future#get()} returns a response object containing the listing result. + * + * @param request the request object containing all of the parameters for the API call + */ + Future list(ListLogEntriesRequest request); + + /** + * Sends a request to list monitored resource descriptors. This method returns a {@code Future} + * object to consume the result. {@link Future#get()} returns a response object containing the + * listing result. + * + * @param request the request object containing all of the parameters for the API call + */ + Future list( + ListMonitoredResourceDescriptorsRequest request); + + /** + * Sends a request to create a log metric. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns the created metric. + * + * @param request the request object containing all of the parameters for the API call + */ + Future create(CreateLogMetricRequest request); + + /** + * Sends a request to update a log metric. If the log metric does not exist, it is created. This + * method returns a {@code Future} object to consume the result. {@link Future#get()} returns the + * updated or created log metric. + * + * @param request the request object containing all of the parameters for the API call + */ + Future update(UpdateLogMetricRequest request); + + /** + * Sends a request to get a log metric. This method returns a {@code Future} object to consume the + * result. {@link Future#get()} returns the requested log metric or {@code null} if not found. + * + * @param request the request object containing all of the parameters for the API call + */ + Future get(GetLogMetricRequest request); + + /** + * Sends a request to list the log metrics in a project. This method returns a {@code Future} + * object to consume the result. {@link Future#get()} returns a response object containing the + * listing result. + * + * @param request the request object containing all of the parameters for the API call + */ + Future list(ListLogMetricsRequest request); + + /** + * Sends a request to delete a log metric. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns {@link Empty#getDefaultInstance()} or {@code null} if + * the log was not found. + * + * @param request the request object containing all of the parameters for the API call + */ + Future delete(DeleteLogMetricRequest request); +} diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/spi/LoggingRpcFactory.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/spi/LoggingRpcFactory.java new file mode 100644 index 000000000000..7891751bde19 --- /dev/null +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/spi/LoggingRpcFactory.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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.google.cloud.logging.spi; + +import com.google.cloud.logging.LoggingOptions; +import com.google.cloud.spi.ServiceRpcFactory; + +/** + * An interface for Logging RPC factory. + * Implementation will be loaded via {@link java.util.ServiceLoader}. + */ +public interface LoggingRpcFactory extends ServiceRpcFactory { +}