Skip to content

Commit

Permalink
Audit logging (#868)
Browse files Browse the repository at this point in the history
  • Loading branch information
artem-v authored Dec 4, 2024
1 parent 9135d84 commit a85b474
Show file tree
Hide file tree
Showing 16 changed files with 302 additions and 93 deletions.
131 changes: 95 additions & 36 deletions services-api/src/main/java/io/scalecube/services/ServiceCall.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import io.scalecube.services.routing.Router;
import io.scalecube.services.routing.Routers;
import io.scalecube.services.transport.api.ClientTransport;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
Expand All @@ -34,6 +36,8 @@ public class ServiceCall implements AutoCloseable {
private ServiceClientErrorMapper errorMapper = DefaultErrorMapper.INSTANCE;
private Map<String, String> credentials = Collections.emptyMap();
private String contentType = ServiceMessage.DEFAULT_DATA_FORMAT;
private Logger logger;
private Level level;

public ServiceCall() {}

Expand All @@ -44,6 +48,8 @@ private ServiceCall(ServiceCall other) {
this.errorMapper = other.errorMapper;
this.contentType = other.contentType;
this.credentials = Collections.unmodifiableMap(new HashMap<>(other.credentials));
this.logger = other.logger;
this.level = other.level;
}

/**
Expand Down Expand Up @@ -130,6 +136,30 @@ public ServiceCall contentType(String contentType) {
return target;
}

/**
* Setter for {@code logger}.
*
* @param name logger name.
* @param level logger level.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall logger(String name, Level level) {
ServiceCall target = new ServiceCall(this);
target.logger = System.getLogger(name);
target.level = level;
return target;
}

/**
* Setter for {@code logger}.
*
* @param name logger name.
* @return new {@link ServiceCall} instance.
*/
public ServiceCall logger(String name) {
return logger(name, Level.DEBUG);
}

/**
* Issues fire-and-forget request.
*
Expand Down Expand Up @@ -159,24 +189,41 @@ public Mono<ServiceMessage> requestOne(ServiceMessage request) {
*/
public Mono<ServiceMessage> requestOne(ServiceMessage request, Type responseType) {
return Mono.defer(
() -> {
ServiceMethodInvoker methodInvoker;
if (serviceRegistry != null
&& (methodInvoker = serviceRegistry.getInvoker(request.qualifier())) != null) {
// local service
return methodInvoker.invokeOne(request).map(this::throwIfError);
} else {
// remote service
Objects.requireNonNull(transport, "[requestOne] transport");
return Mono.fromCallable(() -> serviceLookup(request))
.flatMap(
serviceReference ->
transport
.create(serviceReference)
.requestResponse(request, responseType)
.map(this::throwIfError));
}
});
() -> {
ServiceMethodInvoker methodInvoker;
if (serviceRegistry != null
&& (methodInvoker = serviceRegistry.getInvoker(request.qualifier())) != null) {
// local service
return methodInvoker.invokeOne(request).map(this::throwIfError);
} else {
// remote service
Objects.requireNonNull(transport, "[requestOne] transport");
return Mono.fromCallable(() -> serviceLookup(request))
.flatMap(
serviceReference ->
transport
.create(serviceReference)
.requestResponse(request, responseType)
.map(this::throwIfError));
}
})
.doOnSuccess(
response -> {
if (logger != null && logger.isLoggable(level)) {
logger.log(
level,
"[{0}] request: {1}, response: {2}",
request.qualifier(),
request,
response);
}
})
.doOnError(
ex -> {
if (logger != null) {
logger.log(Level.ERROR, "[{0}] request: {1}", request.qualifier(), request, ex);
}
});
}

/**
Expand All @@ -198,24 +245,36 @@ public Flux<ServiceMessage> requestMany(ServiceMessage request) {
*/
public Flux<ServiceMessage> requestMany(ServiceMessage request, Type responseType) {
return Flux.defer(
() -> {
ServiceMethodInvoker methodInvoker;
if (serviceRegistry != null
&& (methodInvoker = serviceRegistry.getInvoker(request.qualifier())) != null) {
// local service
return methodInvoker.invokeMany(request).map(this::throwIfError);
} else {
// remote service
Objects.requireNonNull(transport, "[requestMany] transport");
return Mono.fromCallable(() -> serviceLookup(request))
.flatMapMany(
serviceReference ->
transport
.create(serviceReference)
.requestStream(request, responseType)
.map(this::throwIfError));
}
});
() -> {
ServiceMethodInvoker methodInvoker;
if (serviceRegistry != null
&& (methodInvoker = serviceRegistry.getInvoker(request.qualifier())) != null) {
// local service
return methodInvoker.invokeMany(request).map(this::throwIfError);
} else {
// remote service
Objects.requireNonNull(transport, "[requestMany] transport");
return Mono.fromCallable(() -> serviceLookup(request))
.flatMapMany(
serviceReference ->
transport
.create(serviceReference)
.requestStream(request, responseType)
.map(this::throwIfError));
}
})
.doOnSubscribe(
s -> {
if (logger != null && logger.isLoggable(level)) {
logger.log(level, "[{0}] request: {1}", request.qualifier(), request);
}
})
.doOnError(
ex -> {
if (logger != null) {
logger.log(Level.ERROR, "[{0}] request: {1}", request.qualifier(), request, ex);
}
});
}

/**
Expand Down
72 changes: 61 additions & 11 deletions services-api/src/main/java/io/scalecube/services/ServiceInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import io.scalecube.services.auth.PrincipalMapper;
import io.scalecube.services.exceptions.ServiceProviderErrorMapper;
import io.scalecube.services.transport.api.ServiceMessageDataDecoder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -18,6 +20,8 @@ public class ServiceInfo {
private final ServiceMessageDataDecoder dataDecoder;
private final Authenticator<Object> authenticator;
private final PrincipalMapper<Object, Object> principalMapper;
private final Logger logger;
private final Level level;

private ServiceInfo(Builder builder) {
this.serviceInstance = builder.serviceInstance;
Expand All @@ -26,6 +30,8 @@ private ServiceInfo(Builder builder) {
this.dataDecoder = builder.dataDecoder;
this.authenticator = builder.authenticator;
this.principalMapper = builder.principalMapper;
this.logger = builder.logger;
this.level = builder.level;
}

public static Builder from(ServiceInfo serviceInfo) {
Expand Down Expand Up @@ -60,15 +66,25 @@ public PrincipalMapper<Object, Object> principalMapper() {
return principalMapper;
}

public Logger logger() {
return logger;
}

public Level level() {
return level;
}

@Override
public String toString() {
return new StringJoiner(", ", ServiceInfo.class.getSimpleName() + "[", "]")
.add("serviceInstance=" + serviceInstance)
.add("tags(" + tags.size() + ")")
.add("tags=" + tags)
.add("errorMapper=" + errorMapper)
.add("dataDecoder=" + dataDecoder)
.add("authenticator=" + authenticator)
.add("principalMapper=" + principalMapper)
.add("logger=" + logger)
.add("level=" + level)
.toString();
}

Expand All @@ -80,6 +96,8 @@ public static class Builder {
private ServiceMessageDataDecoder dataDecoder;
private Authenticator<Object> authenticator;
private PrincipalMapper<Object, Object> principalMapper;
private Logger logger;
private Level level;

private Builder(ServiceInfo serviceInfo) {
this.serviceInstance = serviceInfo.serviceInstance;
Expand All @@ -88,6 +106,8 @@ private Builder(ServiceInfo serviceInfo) {
this.dataDecoder = serviceInfo.dataDecoder;
this.authenticator = serviceInfo.authenticator;
this.principalMapper = serviceInfo.principalMapper;
this.logger = serviceInfo.logger;
this.level = serviceInfo.level;
}

private Builder(Object serviceInstance) {
Expand All @@ -98,8 +118,8 @@ private Builder(Object serviceInstance) {
* Setter for {@code tags}. Merges this {@code tags} with {@code Microservices.tags}. If keys
* are clashing this {@code tags} shall override {@code Microservices.tags}.
*
* @param key tag key; not null
* @param value tag value; not null
* @param key tag key
* @param value tag value
* @return this builder
*/
public Builder tag(String key, String value) {
Expand All @@ -112,18 +132,41 @@ public Builder tag(String key, String value) {
/**
* Setter for {@code errorMapper}. Overrides default {@code Microservices.errorMapper}.
*
* @param errorMapper error mapper; not null
* @param errorMapper error mapper
* @return this buidler
*/
public Builder errorMapper(ServiceProviderErrorMapper errorMapper) {
this.errorMapper = Objects.requireNonNull(errorMapper, "errorMapper");
return this;
}

/**
* Setter for {@code logger}. Overrides default {@code Microservices.logger}.
*
* @param name logger name (optional)
* @param level logger level (optional)
* @return this buidler
*/
public Builder logger(String name, Level level) {
this.logger = name != null ? System.getLogger(name) : null;
this.level = level;
return this;
}

/**
* Setter for {@code logger}. Overrides default {@code Microservices.logger}.
*
* @param name logger name (optional)
* @return this buidler
*/
public Builder logger(String name) {
return logger(name, Level.DEBUG);
}

/**
* Setter for {@code dataDecoder}. Overrides default {@code Microservices.dataDecoder}.
*
* @param dataDecoder data decoder; not null
* @param dataDecoder data decoder
* @return this builder
*/
public Builder dataDecoder(ServiceMessageDataDecoder dataDecoder) {
Expand All @@ -134,7 +177,7 @@ public Builder dataDecoder(ServiceMessageDataDecoder dataDecoder) {
/**
* Setter for {@code authenticator}. Overrides default {@code Microservices.authenticator}.
*
* @param authenticator authenticator; optional
* @param authenticator authenticator (optional)
* @param <T> type of auth data returned by authenticator
* @return this builder
*/
Expand All @@ -147,7 +190,7 @@ public <T> Builder authenticator(Authenticator<? extends T> authenticator) {
/**
* Setter for {@code principalMapper}. Overrides default {@code Microservices.principalMapper}.
*
* @param principalMapper principalMapper; optional
* @param principalMapper principalMapper (optional)
* @param <T> auth data type
* @param <R> principal type
* @return this builder
Expand All @@ -160,28 +203,35 @@ public <T, R> Builder principalMapper(PrincipalMapper<? super T, ? extends R> pr

Builder errorMapperIfAbsent(ServiceProviderErrorMapper errorMapper) {
if (this.errorMapper == null) {
this.errorMapper = errorMapper;
return errorMapper(errorMapper);
}
return this;
}

Builder dataDecoderIfAbsent(ServiceMessageDataDecoder dataDecoder) {
if (this.dataDecoder == null) {
this.dataDecoder = dataDecoder;
return dataDecoder(dataDecoder);
}
return this;
}

Builder authenticatorIfAbsent(Authenticator<Object> authenticator) {
if (this.authenticator == null) {
this.authenticator = authenticator;
return authenticator(authenticator);
}
return this;
}

Builder principalMapperIfAbsent(PrincipalMapper<Object, Object> principalMapper) {
if (this.principalMapper == null) {
this.principalMapper = principalMapper;
return principalMapper(principalMapper);
}
return this;
}

Builder loggerIfAbsent(String name, Level level) {
if (this.logger == null) {
return logger(name, level);
}
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,9 @@ public int errorType() {

@Override
public String toString() {
return new StringJoiner(", ", ServiceMessage.class.getSimpleName() + "[", "]")
return new StringJoiner(", ", "ServiceMessage" + "[", "]")
.add("headers(" + headers.size() + ")")
.add("data=" + (data != null ? data.getClass().getName() : null))
.add("data=" + data)
.toString();
}

Expand All @@ -205,7 +205,7 @@ private Builder() {}
/**
* Setter for {@code data}.
*
* @param data data; optional
* @param data data (optional)
* @return this builder
*/
public Builder data(Object data) {
Expand Down
Loading

0 comments on commit a85b474

Please sign in to comment.