From eac7a99cf43d1d00415d4ae491ef7b72603ee4d7 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Wed, 4 Dec 2024 17:31:53 +0200 Subject: [PATCH 1/3] WIP on audit logs --- .../io/scalecube/services/ServiceCall.java | 131 +++++++++++++----- .../services/api/ServiceMessage.java | 4 +- .../http/HttpClientConnectionTest.java | 2 + .../services/ServiceCallLocalTest.java | 2 + .../services/ServiceCallRemoteTest.java | 2 + 5 files changed, 103 insertions(+), 38 deletions(-) diff --git a/services-api/src/main/java/io/scalecube/services/ServiceCall.java b/services-api/src/main/java/io/scalecube/services/ServiceCall.java index 90930cf10..9769d8d10 100644 --- a/services-api/src/main/java/io/scalecube/services/ServiceCall.java +++ b/services-api/src/main/java/io/scalecube/services/ServiceCall.java @@ -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; @@ -34,6 +36,8 @@ public class ServiceCall implements AutoCloseable { private ServiceClientErrorMapper errorMapper = DefaultErrorMapper.INSTANCE; private Map credentials = Collections.emptyMap(); private String contentType = ServiceMessage.DEFAULT_DATA_FORMAT; + private Logger logger; + private Level level; public ServiceCall() {} @@ -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; } /** @@ -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. * @@ -159,24 +189,41 @@ public Mono requestOne(ServiceMessage request) { */ public Mono 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); + } + }); } /** @@ -198,24 +245,36 @@ public Flux requestMany(ServiceMessage request) { */ public Flux 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); + } + }); } /** diff --git a/services-api/src/main/java/io/scalecube/services/api/ServiceMessage.java b/services-api/src/main/java/io/scalecube/services/api/ServiceMessage.java index a33e191e7..ad734326b 100644 --- a/services-api/src/main/java/io/scalecube/services/api/ServiceMessage.java +++ b/services-api/src/main/java/io/scalecube/services/api/ServiceMessage.java @@ -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(); } diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpClientConnectionTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpClientConnectionTest.java index 72129e817..45dcb4f8d 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpClientConnectionTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpClientConnectionTest.java @@ -15,6 +15,7 @@ import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; import java.io.IOException; +import java.lang.System.Logger.Level; import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.AfterEach; @@ -101,6 +102,7 @@ public void testCallRepeatedlyByInvalidAddress() { private static ServiceCall serviceCall(Address address) { return new ServiceCall() + .logger("serviceCall", Level.INFO) .transport(new HttpGatewayClientTransport.Builder().address(address).build()) .router(new StaticAddressRouter(address)); } diff --git a/services/src/test/java/io/scalecube/services/ServiceCallLocalTest.java b/services/src/test/java/io/scalecube/services/ServiceCallLocalTest.java index e836e8f07..5f231dad3 100644 --- a/services/src/test/java/io/scalecube/services/ServiceCallLocalTest.java +++ b/services/src/test/java/io/scalecube/services/ServiceCallLocalTest.java @@ -30,6 +30,7 @@ import io.scalecube.services.sut.GreetingServiceImpl; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; +import java.lang.System.Logger.Level; import java.time.Duration; import java.util.Collections; import java.util.Optional; @@ -197,6 +198,7 @@ public void test_async_greeting_return_string_service_not_found_error_case() { public void test_custom_error_mapper() { GreetingService service = new ServiceCall() + .logger("test_custom_error_mapper", Level.INFO) .errorMapper( message -> { throw new RuntimeException("custom error mapper"); diff --git a/services/src/test/java/io/scalecube/services/ServiceCallRemoteTest.java b/services/src/test/java/io/scalecube/services/ServiceCallRemoteTest.java index e168c82a4..0e71676de 100644 --- a/services/src/test/java/io/scalecube/services/ServiceCallRemoteTest.java +++ b/services/src/test/java/io/scalecube/services/ServiceCallRemoteTest.java @@ -31,6 +31,7 @@ import io.scalecube.services.sut.SimpleQuoteService; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; +import java.lang.System.Logger.Level; import java.time.Duration; import java.util.Collections; import java.util.Optional; @@ -270,6 +271,7 @@ public void test_many_stream_block_first() { public void test_custom_error_mapper() { GreetingService service = new ServiceCall() + .logger("test_custom_error_mapper", Level.INFO) .errorMapper( message -> { throw new RuntimeException("custom error mapper"); From b37b96d0cee60b8531816ca3d54e5ed0fd1a86e7 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Wed, 4 Dec 2024 19:50:10 +0200 Subject: [PATCH 2/3] Audit logger --- .../io/scalecube/services/ServiceInfo.java | 62 ++++++++++--- .../services/api/ServiceMessage.java | 2 +- .../methods/ServiceMethodInvoker.java | 90 +++++++++++++------ .../methods/ServiceMethodInvokerTest.java | 36 ++++++-- .../rsocket/ServiceMessageCodec.java | 4 +- .../io/scalecube/services/Microservices.java | 26 ++++-- .../registry/ServiceRegistryImpl.java | 4 +- 7 files changed, 169 insertions(+), 55 deletions(-) diff --git a/services-api/src/main/java/io/scalecube/services/ServiceInfo.java b/services-api/src/main/java/io/scalecube/services/ServiceInfo.java index 0d6303129..8edfe49aa 100644 --- a/services-api/src/main/java/io/scalecube/services/ServiceInfo.java +++ b/services-api/src/main/java/io/scalecube/services/ServiceInfo.java @@ -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; @@ -18,6 +20,8 @@ public class ServiceInfo { private final ServiceMessageDataDecoder dataDecoder; private final Authenticator authenticator; private final PrincipalMapper principalMapper; + private final Logger logger; + private final Level level; private ServiceInfo(Builder builder) { this.serviceInstance = builder.serviceInstance; @@ -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) { @@ -60,15 +66,25 @@ public PrincipalMapper 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(); } @@ -80,6 +96,8 @@ public static class Builder { private ServiceMessageDataDecoder dataDecoder; private Authenticator authenticator; private PrincipalMapper principalMapper; + private Logger logger; + private Level level; private Builder(ServiceInfo serviceInfo) { this.serviceInstance = serviceInfo.serviceInstance; @@ -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) { @@ -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) { @@ -112,7 +132,7 @@ 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) { @@ -120,10 +140,23 @@ public Builder errorMapper(ServiceProviderErrorMapper 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 dataDecoder}. Overrides default {@code Microservices.dataDecoder}. * - * @param dataDecoder data decoder; not null + * @param dataDecoder data decoder * @return this builder */ public Builder dataDecoder(ServiceMessageDataDecoder dataDecoder) { @@ -134,7 +167,7 @@ public Builder dataDecoder(ServiceMessageDataDecoder dataDecoder) { /** * Setter for {@code authenticator}. Overrides default {@code Microservices.authenticator}. * - * @param authenticator authenticator; optional + * @param authenticator authenticator (optional) * @param type of auth data returned by authenticator * @return this builder */ @@ -147,7 +180,7 @@ public Builder authenticator(Authenticator authenticator) { /** * Setter for {@code principalMapper}. Overrides default {@code Microservices.principalMapper}. * - * @param principalMapper principalMapper; optional + * @param principalMapper principalMapper (optional) * @param auth data type * @param principal type * @return this builder @@ -160,28 +193,35 @@ public Builder principalMapper(PrincipalMapper 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 authenticator) { if (this.authenticator == null) { - this.authenticator = authenticator; + return authenticator(authenticator); } return this; } Builder principalMapperIfAbsent(PrincipalMapper 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; } diff --git a/services-api/src/main/java/io/scalecube/services/api/ServiceMessage.java b/services-api/src/main/java/io/scalecube/services/api/ServiceMessage.java index ad734326b..c73847824 100644 --- a/services-api/src/main/java/io/scalecube/services/api/ServiceMessage.java +++ b/services-api/src/main/java/io/scalecube/services/api/ServiceMessage.java @@ -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) { diff --git a/services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java b/services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java index f16527160..2f60386d9 100644 --- a/services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java +++ b/services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java @@ -11,6 +11,8 @@ import io.scalecube.services.exceptions.ServiceProviderErrorMapper; import io.scalecube.services.exceptions.UnauthorizedException; import io.scalecube.services.transport.api.ServiceMessageDataDecoder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Objects; @@ -30,18 +32,9 @@ public final class ServiceMethodInvoker { private final ServiceMessageDataDecoder dataDecoder; private final Authenticator authenticator; private final PrincipalMapper principalMapper; + private final Logger logger; + private final Level level; - /** - * Constructs a service method invoker out of real service object instance and method info. - * - * @param method service method (required) - * @param service service instance (required) - * @param methodInfo method information (required) - * @param errorMapper error mapper (required) - * @param dataDecoder data decoder (required) - * @param authenticator authenticator (optional) - * @param principalMapper principal mapper (optional) - */ public ServiceMethodInvoker( Method method, Object service, @@ -49,7 +42,9 @@ public ServiceMethodInvoker( ServiceProviderErrorMapper errorMapper, ServiceMessageDataDecoder dataDecoder, Authenticator authenticator, - PrincipalMapper principalMapper) { + PrincipalMapper principalMapper, + Logger logger, + Level level) { this.method = Objects.requireNonNull(method, "method"); this.service = Objects.requireNonNull(service, "service"); this.methodInfo = Objects.requireNonNull(methodInfo, "methodInfo"); @@ -57,6 +52,8 @@ public ServiceMethodInvoker( this.dataDecoder = Objects.requireNonNull(dataDecoder, "dataDecoder"); this.authenticator = authenticator; this.principalMapper = principalMapper; + this.logger = logger; + this.level = level; } /** @@ -67,13 +64,40 @@ public ServiceMethodInvoker( */ public Mono invokeOne(ServiceMessage message) { return Mono.deferContextual(context -> authenticate(message, (Context) context)) - .flatMap(authData -> deferWithContextOne(message, authData)) + .flatMap(authData -> invokeOne(message, authData)) .map(response -> toResponse(response, message.qualifier(), message.dataFormat())) .onErrorResume( throwable -> Mono.just(errorMapper.toMessage(message.qualifier(), throwable))) .subscribeOn(methodInfo.scheduler()); } + private Mono invokeOne(ServiceMessage message, Object authData) { + return Mono.deferContextual( + context -> { + final var request = toRequest(message); + final var qualifier = message.qualifier(); + return Mono.from(invoke(request)) + .doOnSuccess( + response -> { + if (logger != null && logger.isLoggable(level)) { + logger.log( + level, + "[{0}] request: {1}, response: {2}", + qualifier, + request, + response); + } + }) + .doOnError( + ex -> { + if (logger != null) { + logger.log(Level.ERROR, "[{0}] request: {1}", qualifier, request, ex); + } + }); + }) + .contextWrite(context -> enhanceContext(authData, context)); + } + /** * Invokes service method with message stream response. * @@ -82,13 +106,35 @@ public Mono invokeOne(ServiceMessage message) { */ public Flux invokeMany(ServiceMessage message) { return Mono.deferContextual(context -> authenticate(message, (Context) context)) - .flatMapMany(authData -> deferWithContextMany(message, authData)) + .flatMapMany(authData -> invokeMany(message, authData)) .map(response -> toResponse(response, message.qualifier(), message.dataFormat())) .onErrorResume( throwable -> Flux.just(errorMapper.toMessage(message.qualifier(), throwable))) .subscribeOn(methodInfo.scheduler()); } + private Flux invokeMany(ServiceMessage message, Object authData) { + return Flux.deferContextual( + context -> { + final var request = toRequest(message); + final var qualifier = message.qualifier(); + return Flux.from(invoke(request)) + .doOnSubscribe( + s -> { + if (logger != null && logger.isLoggable(level)) { + logger.log(level, "[{0}] request: {1}", qualifier, request); + } + }) + .doOnError( + ex -> { + if (logger != null) { + logger.log(Level.ERROR, "[{0}] request: {1}", qualifier, request, ex); + } + }); + }) + .contextWrite(context -> enhanceContext(authData, context)); + } + /** * Invokes service method with bidirectional communication. * @@ -100,7 +146,7 @@ public Flux invokeBidirectional(Publisher publis .switchOnFirst( (first, messages) -> Mono.deferContextual(context -> authenticate(first.get(), (Context) context)) - .flatMapMany(authData -> deferWithContextBidirectional(messages, authData)) + .flatMapMany(authData -> invokeBidirectional(messages, authData)) .map( response -> toResponse(response, first.get().qualifier(), first.get().dataFormat())) @@ -110,17 +156,7 @@ public Flux invokeBidirectional(Publisher publis .subscribeOn(methodInfo.scheduler())); } - private Mono deferWithContextOne(ServiceMessage message, Object authData) { - return Mono.deferContextual(context -> Mono.from(invoke(toRequest(message)))) - .contextWrite(context -> enhanceContext(authData, context)); - } - - private Flux deferWithContextMany(ServiceMessage message, Object authData) { - return Flux.deferContextual(context -> Flux.from(invoke(toRequest(message)))) - .contextWrite(context -> enhanceContext(authData, context)); - } - - private Flux deferWithContextBidirectional(Flux messages, Object authData) { + private Flux invokeBidirectional(Flux messages, Object authData) { return Flux.deferContextual(context -> messages.map(this::toRequest).transform(this::invoke)) .contextWrite(context -> enhanceContext(authData, context)); } @@ -243,6 +279,8 @@ public String toString() { .add("dataDecoder=" + dataDecoder) .add("authenticator=" + authenticator) .add("principalMapper=" + principalMapper) + .add("logger=" + logger) + .add("level=" + level) .toString(); } } diff --git a/services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java b/services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java index bae265a22..b089d4ad9 100644 --- a/services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java +++ b/services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java @@ -74,7 +74,9 @@ void testInvokeOneWhenReturnNull() throws Exception { DefaultErrorMapper.INSTANCE, dataDecoder, nullAuthenticator, - nullPrincipalMapper); + nullPrincipalMapper, + null, + null); ServiceMessage message = ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); @@ -113,7 +115,9 @@ void testInvokeManyWhenReturnNull() throws Exception { DefaultErrorMapper.INSTANCE, dataDecoder, nullAuthenticator, - nullPrincipalMapper); + nullPrincipalMapper, + null, + null); ServiceMessage message = ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); @@ -152,7 +156,9 @@ void testInvokeBidirectionalWhenReturnNull() throws Exception { DefaultErrorMapper.INSTANCE, dataDecoder, nullAuthenticator, - nullPrincipalMapper); + nullPrincipalMapper, + null, + null); ServiceMessage message = ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); @@ -192,7 +198,9 @@ void testInvokeOneWhenThrowException() throws Exception { DefaultErrorMapper.INSTANCE, dataDecoder, nullAuthenticator, - nullPrincipalMapper); + nullPrincipalMapper, + null, + null); ServiceMessage message = ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); @@ -235,7 +243,9 @@ void testInvokeManyWhenThrowException() throws Exception { DefaultErrorMapper.INSTANCE, dataDecoder, nullAuthenticator, - nullPrincipalMapper); + nullPrincipalMapper, + null, + null); ServiceMessage message = ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); @@ -277,7 +287,9 @@ void testInvokeBidirectionalWhenThrowException() throws Exception { DefaultErrorMapper.INSTANCE, dataDecoder, nullAuthenticator, - nullPrincipalMapper); + nullPrincipalMapper, + null, + null); ServiceMessage message = ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); @@ -323,7 +335,9 @@ void testAuthMethodWhenNoContextAndNoAuthenticator() throws Exception { DefaultErrorMapper.INSTANCE, dataDecoder, nullAuthenticator, - nullPrincipalMapper); + nullPrincipalMapper, + null, + null); ServiceMessage message = ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); @@ -366,7 +380,9 @@ void testAuthMethodWhenThereIsContextAndNoAuthenticator() throws Exception { DefaultErrorMapper.INSTANCE, dataDecoder, nullAuthenticator, - principalMapper); + principalMapper, + null, + null); ServiceMessage message = ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); @@ -412,7 +428,9 @@ void testAuthMethodWhenNoContextButThereIsAuthenticator() throws Exception { DefaultErrorMapper.INSTANCE, dataDecoder, authenticator, - principalMapper); + principalMapper, + null, + null); ServiceMessage message = ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); diff --git a/services-transport-parent/services-transport-rsocket/src/main/java/io/scalecube/services/transport/rsocket/ServiceMessageCodec.java b/services-transport-parent/services-transport-rsocket/src/main/java/io/scalecube/services/transport/rsocket/ServiceMessageCodec.java index d10075d0f..dd1985381 100644 --- a/services-transport-parent/services-transport-rsocket/src/main/java/io/scalecube/services/transport/rsocket/ServiceMessageCodec.java +++ b/services-transport-parent/services-transport-rsocket/src/main/java/io/scalecube/services/transport/rsocket/ServiceMessageCodec.java @@ -50,9 +50,9 @@ public ServiceMessageCodec() { * here: {@link DataCodec#contentType()}), then the last one specified will be used. Client's * collection of data codes override data codecs from SPI. * - * @param headersCodec codec for service message headers; optional, if not set then {@link + * @param headersCodec codec for service message headers (optional), if not set then {@link * JdkCodec} will be used. - * @param dataCodecs codecs for service message data; optional, if not set then {@link + * @param dataCodecs codecs for service message data (optional), if not set then {@link * DataCodec#INSTANCES} will be used. */ public ServiceMessageCodec(HeadersCodec headersCodec, Collection dataCodecs) { diff --git a/services/src/main/java/io/scalecube/services/Microservices.java b/services/src/main/java/io/scalecube/services/Microservices.java index d5805446e..8459b1538 100644 --- a/services/src/main/java/io/scalecube/services/Microservices.java +++ b/services/src/main/java/io/scalecube/services/Microservices.java @@ -236,6 +236,7 @@ private void registerService(ServiceInfo serviceInfo) { .dataDecoderIfAbsent(context.defaultDataDecoder) .authenticatorIfAbsent(context.defaultAuthenticator) .principalMapperIfAbsent(context.defaultPrincipalMapper) + .loggerIfAbsent(context.defaultLoggerName, context.defaultLoggerLevel) .build()); } @@ -523,6 +524,8 @@ public static final class Context { private PrincipalMapper defaultPrincipalMapper; private ServiceProviderErrorMapper defaultErrorMapper; private ServiceMessageDataDecoder defaultDataDecoder; + private String defaultLoggerName; + private Level defaultLoggerLevel; private String externalHost; private Integer externalPort; private ServiceDiscoveryFactory discoveryFactory; @@ -647,7 +650,7 @@ public Context gateway(Supplier gatewaySupplier) { * Setter for default {@code errorMapper}. By default, default {@code errorMapper} is set to * {@link DefaultErrorMapper#INSTANCE}. * - * @param errorMapper error mapper; not null + * @param errorMapper error mapper * @return this builder with applied parameter */ public Context defaultErrorMapper(ServiceProviderErrorMapper errorMapper) { @@ -660,7 +663,7 @@ public Context defaultErrorMapper(ServiceProviderErrorMapper errorMapper) { * {@link ServiceMessageDataDecoder#INSTANCE} if it exists, otherswise to a function {@code * (message, dataType) -> message} * - * @param dataDecoder data decoder; not null + * @param dataDecoder data decoder * @return this builder with applied parameter */ public Context defaultDataDecoder(ServiceMessageDataDecoder dataDecoder) { @@ -671,7 +674,7 @@ public Context defaultDataDecoder(ServiceMessageDataDecoder dataDecoder) { /** * Setter for default {@code authenticator}. By default, default {@code authenticator} is null. * - * @param authenticator authenticator; optional + * @param authenticator authenticator (optional) * @return this builder with applied parameter */ public Context defaultAuthenticator(Authenticator authenticator) { @@ -684,7 +687,7 @@ public Context defaultAuthenticator(Authenticator authenticator * Setter for default {@code principalMapper}. By default, default {@code principalMapper} is * null. * - * @param principalMapper principalMapper; optional + * @param principalMapper principalMapper (optional) * @param auth data type * @param principal type * @return this builder with applied parameter @@ -696,12 +699,25 @@ public Context defaultPrincipalMapper( return this; } + /** + * Setter for default {@code logger}. By default, default {@code logger} is null. + * + * @param name logger name (optional) + * @param level logger level (optional) + * @return this builder with applied parameter + */ + public Context defaultLogger(String name, Level level) { + this.defaultLoggerName = name; + this.defaultLoggerLevel = level; + return this; + } + /** * Adds {@link Scheduler} supplier to the list of scheduler suppliers. * * @param name scheduler name * @param schedulerSupplier {@link Scheduler} supplier - * @return this + * @return this builder with applied parameter */ public Context scheduler(String name, Supplier schedulerSupplier) { schedulerSuppliers.put(name, schedulerSupplier); diff --git a/services/src/main/java/io/scalecube/services/registry/ServiceRegistryImpl.java b/services/src/main/java/io/scalecube/services/registry/ServiceRegistryImpl.java index e82c4f01c..aa8115216 100644 --- a/services/src/main/java/io/scalecube/services/registry/ServiceRegistryImpl.java +++ b/services/src/main/java/io/scalecube/services/registry/ServiceRegistryImpl.java @@ -153,7 +153,9 @@ public void registerService(ServiceInfo serviceInfo) { serviceInfo.errorMapper(), serviceInfo.dataDecoder(), serviceInfo.authenticator(), - serviceInfo.principalMapper()); + serviceInfo.principalMapper(), + serviceInfo.logger(), + serviceInfo.level()); methodInvokers.put(methodInfo.qualifier(), methodInvoker); methodInvokers.put(methodInfo.oldQualifier(), methodInvoker); From fca79bf23441a73814b1a86dc45c391246b16b96 Mon Sep 17 00:00:00 2001 From: Artem Vysochyn Date: Wed, 4 Dec 2024 20:10:47 +0200 Subject: [PATCH 3/3] Audit logger --- .../main/java/io/scalecube/services/ServiceInfo.java | 10 ++++++++++ .../services/gateway/http/HttpGatewayTest.java | 2 ++ .../services/gateway/http/HttpLocalGatewayTest.java | 2 ++ .../gateway/websocket/WebsocketGatewayAuthTest.java | 2 ++ .../gateway/websocket/WebsocketGatewayTest.java | 2 ++ .../gateway/websocket/WebsocketLocalGatewayTest.java | 2 ++ .../main/java/io/scalecube/services/Microservices.java | 10 ++++++++++ 7 files changed, 30 insertions(+) diff --git a/services-api/src/main/java/io/scalecube/services/ServiceInfo.java b/services-api/src/main/java/io/scalecube/services/ServiceInfo.java index 8edfe49aa..1dba4108b 100644 --- a/services-api/src/main/java/io/scalecube/services/ServiceInfo.java +++ b/services-api/src/main/java/io/scalecube/services/ServiceInfo.java @@ -153,6 +153,16 @@ public Builder logger(String name, 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}. * diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpGatewayTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpGatewayTest.java index c4d8b0730..855547f68 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpGatewayTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpGatewayTest.java @@ -25,6 +25,7 @@ import io.scalecube.services.gateway.client.http.HttpGatewayClientTransport; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; +import java.lang.System.Logger.Level; import java.time.Duration; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -75,6 +76,7 @@ static void beforeAll() { .membership( opts -> opts.seedMembers(gateway.discoveryAddress().toString()))) .transport(RSocketServiceTransport::new) + .defaultLogger("microservices", Level.INFO) .services(new GreetingServiceImpl()) .services( ServiceInfo.fromServiceInstance(new ErrorServiceImpl()) diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpLocalGatewayTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpLocalGatewayTest.java index 146445853..7b0871642 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpLocalGatewayTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/http/HttpLocalGatewayTest.java @@ -22,6 +22,7 @@ import io.scalecube.services.gateway.SomeException; import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.http.HttpGatewayClientTransport; +import java.lang.System.Logger.Level; import java.time.Duration; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -49,6 +50,7 @@ static void beforeAll() { Microservices.start( new Context() .gateway(() -> new HttpGateway.Builder().id("HTTP").build()) + .defaultLogger("gateway", Level.INFO) .services(new GreetingServiceImpl()) .services( ServiceInfo.fromServiceInstance(new ErrorServiceImpl()) diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayAuthTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayAuthTest.java index ca6dbc487..0734d43f3 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayAuthTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayAuthTest.java @@ -17,6 +17,7 @@ import io.scalecube.services.gateway.SecuredServiceImpl; import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; +import java.lang.System.Logger.Level; import java.time.Duration; import java.util.Collections; import java.util.HashSet; @@ -53,6 +54,7 @@ static void beforeAll() { .id("WS") .gatewayHandler(new GatewaySessionHandlerImpl(AUTH_REGISTRY)) .build()) + .defaultLogger("gateway", Level.INFO) .services(new SecuredServiceImpl(AUTH_REGISTRY))); } diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayTest.java index c1d094de8..ea990fc9c 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketGatewayTest.java @@ -25,6 +25,7 @@ import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; import io.scalecube.services.transport.rsocket.RSocketServiceTransport; import io.scalecube.transport.netty.websocket.WebsocketTransportFactory; +import java.lang.System.Logger.Level; import java.time.Duration; import java.util.List; import java.util.stream.Collectors; @@ -78,6 +79,7 @@ static void beforeAll() { .membership( opts -> opts.seedMembers(gateway.discoveryAddress().toString()))) .transport(RSocketServiceTransport::new) + .defaultLogger("microservices", Level.INFO) .services(new GreetingServiceImpl()) .services( ServiceInfo.fromServiceInstance(new ErrorServiceImpl()) diff --git a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketLocalGatewayTest.java b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketLocalGatewayTest.java index 9139da008..44e60d807 100644 --- a/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketLocalGatewayTest.java +++ b/services-gateway/src/test/java/io/scalecube/services/gateway/websocket/WebsocketLocalGatewayTest.java @@ -22,6 +22,7 @@ import io.scalecube.services.gateway.SomeException; import io.scalecube.services.gateway.client.StaticAddressRouter; import io.scalecube.services.gateway.client.websocket.WebsocketGatewayClientTransport; +import java.lang.System.Logger.Level; import java.time.Duration; import java.util.List; import java.util.stream.Collectors; @@ -57,6 +58,7 @@ static void beforeAll() { .serviceCall(call -> call.errorMapper(ERROR_MAPPER)) .errorMapper(ERROR_MAPPER) .build()) + .defaultLogger("gateway", Level.INFO) .services(new GreetingServiceImpl()) .services( ServiceInfo.fromServiceInstance(new ErrorServiceImpl()) diff --git a/services/src/main/java/io/scalecube/services/Microservices.java b/services/src/main/java/io/scalecube/services/Microservices.java index 8459b1538..e024764d1 100644 --- a/services/src/main/java/io/scalecube/services/Microservices.java +++ b/services/src/main/java/io/scalecube/services/Microservices.java @@ -712,6 +712,16 @@ public Context defaultLogger(String name, Level level) { return this; } + /** + * Setter for default {@code logger}. By default, default {@code logger} is null. + * + * @param name logger name (optional) + * @return this builder with applied parameter + */ + public Context defaultLogger(String name) { + return defaultLogger(name, Level.DEBUG); + } + /** * Adds {@link Scheduler} supplier to the list of scheduler suppliers. *