Add documentation in the reference guide on the new spring-geode-starter-logging module.

Resolves gh-71.
This commit is contained in:
John Blum
2020-02-19 22:54:20 -08:00
parent ce295d235f
commit d5ac5f0c1f
3 changed files with 373 additions and 1 deletions

View File

@@ -46,7 +46,10 @@ asciidoctor {
'docs-src-dir' : rootProject.projectDir.path + '/spring-geode-docs/src/main/java',
'docs-resources-dir' : rootProject.projectDir.path + '/spring-geode-docs/src/main/resources',
'examples-dir' : rootProject.projectDir.path + '/spring-geode-examples',
'samples-dir' : rootProject.projectDir.path + '/spring-geode-samples'
'samples-dir' : rootProject.projectDir.path + '/spring-geode-samples',
'starter-logging-dir' : rootProject.projectDir.path + '/spring-geode-starter-logging',
'starter-logging-src-dir' : rootProject.projectDir.path + '/spring-geode-starter-logging/src/main/src',
'starter-logging-resources-dir' : rootProject.projectDir.path + '/spring-geode-starter-logging/src/main/resources'
}
javadoc {

View File

@@ -235,6 +235,7 @@ include::functions.adoc[]
include::continuous-query.adoc[]
include::data-serialization.adoc[]
include::security.adoc[]
include::logging.adoc[]
include::actuator.adoc[]
include::session.adoc[]
include::cloudfoundry.adoc[]

View File

@@ -0,0 +1,368 @@
[[geode-logging]]
== Logging
Apache Geode `1.9.2` was modularized to separate its use of the Apache Log4j API to log output in Geode code from
the underlying implementation of logging, which uses Apache Log4j as the logging provider by default.
Prior to `1.9.2`, the Apache Log4j API (i.e. `log4j-api`) along with the Apache Log4j provider (i.e. `log4j-core`)
were automatically pulled in by Apache Geode core (i.e. `org.apache.geode:geode-core`) thereby making it problematic
to change logging providers when using Apache Geode in Spring Boot applications.
However, now, in order to get any log output from Apache Geode whatsoever, Apache Geode requires a logging provider on
your Spring Boot application classpath. Consequently, this also means the old Apache Geode `Properties`,
e.g. `log-level` no longer have any effect, regardless of whether the property (e.g. `log-level`) is specified in
`gemfire.properties`, in Spring Boot `application.properties` or even as a JVM System Property, `-Dgemfire.log-level`.
TIP: Refer to Apache Geode's {apache-geode-docs}/reference/topics/gemfire_properties.html[Documentation]
for a complete list of valid `Properties`, including the `Properties` used to configure logging.
Unfortunately, this also means the _Spring Data for Apache Geode & Pivotal GemFire_ (SDG)
{spring-data-geode-javadoc}/org/springframework/data/gemfire/config/annotation/EnableLogging.html[`@EnableLogging`]
annotation no longer has any effect on Apache Geode or Pivotal GemFire logging either and is the reason it has been
https://jira.spring.io/browse/DATAGEODE-299[deprecated]. The reason `@EnableLogging` no longer has any effect on
logging is because this annotation's attributes and associated SDG properties indirectly sets the corresponding
Apache Geode or Pivotal GemFire properties, which again, are useless from Apache Geode `1.9.2` onward.
By way of example, and to make this concrete, **none** of the following approaches have any effect on Apache Geode
or Pivotal GemFire logging:
.Command-line configuration
[source,txt]
----
$ java -classpath ...:/path/to/MySpringBootApacheGeodeClientCacheApplication.jar -Dgemfire.log-level=DEBUG
example.app.MySpringBootApacheGeodeClientCacheApplication
----
.Externalized configuration using Apache Geode `gemfire.properties`
[source,properties]
----
# Apache Geode/Pivotal GemFire only/specific properties
log-level=INFO
----
.Externalized configuration using Spring Boot `application.properties`
[source,properties]
----
spring.data.gemfire.cache.log-level=DEBUG
----
Or:
[source,properties]
----
spring.data.gemfire.logging.level=DEBUG
----
.Java configuration using SDG's `@EnableLogging` annotation
[source,java]
----
@SpringBootApplication
@EnableLogging(logLevel = "DEBUG")
class MySpringBootApacheGeodeClientApplication {
// ...
}
----
That is to say, none of the approaches above have any effect without the **new** SBDG logging starter.
[[geode-logging-configuration]]
=== Configure Apache Geode & Pivotal GemFire Logging
So, how do you configure logging for Apache Geode and Pivotal GemFire?
Effectively, 3 things are required to get Apache Geode or Pivotal GemFire to log output:
1) First, you must declare a logging provider on your Spring Boot application classpath (e.g. _Logback_).
2) (optional) Next, you must declare an adapter, or bridge JAR, between Log4j and your logging provider if your declared
logging provider is not Apache Log4j.
For example, if you use the SLF4J API to log output from your Spring Boot application along with _Logback_ as your
logging provider/implementation, then you must include the `org.apache.logging.log4j.log4j-to-slf4j` adapter/bridge JAR
dependency as well.
Internally, Apache Geode uses the Apache Log4j API to log output from Geode components. Therefore, you must bridge Log4j
to any other logging provider (e.g. _Logback_) that is not Log4j (i.e. `log4j-core`). If you are using Log4j as your
logging provider then you do not need to declare an adapter/bridge JAR on your Spring Boot application classpath.
3) Finally, you must supply logging provider configuration to configure Loggers, Appenders, log levels, etc.
For example, when using _Logback_, you must provide a `logback.xml` configuration file on your Spring Boot application
classpath, or in the filesystem. Alternatively, you can use other means to configure your logging provider and get
Apache Geode to log output.
NOTE: Apache Geode's `geode-log4j` module covers the required configuration for steps 1-3 above and uses Apache Log4j
(i.e. `org.apache.logging.log4j:log4j-core`) as the logging provider. The `geode-log4j` module even provides a default,
`log4j2.xml` configuration file to configure Loggers, Appenders and log levels for Apache Geode.
If you declare Spring Boot's own `org.springframework.boot:spring-boot-starter-logging` on your application classpath
then this will cover Steps 1 and 2 above.
The `spring-boot-starter-logging` dependency declares _Logback_ as the logging provider and automatically adapts,
or bridges `java.util.logging` (JUL) and Apache Log4j to SLF4J. However, you still need to supply logging provider
configuration, such as a `logback.xml` file for _Logback_, to configure logging not only for your Spring Boot
application, but also for Apache Geode as well.
SBDG has simplified the setup of Apache Geode and Pivotal GemFire logging. Simply declare the
`org.springframework.geode:spring-geode-starter-logging` dependency on your Spring Boot application classpath!
Unlike Apache Geode's default Log4j XML configuration file (i.e. `log4j2.xml`), SBDG's provided `logback.xml`
configuration file is properly parameterized enabling you to adjust log levels as well as add Appenders.
In addition, SBDG's provided _Logback_ configuration uses templates so you can compose your own logging configuration
while still "including" snippets from SBDG's provided logging configuration metadata, such as Loggers and Appenders.
[[geode-logging-configuration-log-levels]]
==== Configuring Log Levels
One of the most common logging tasks is to adjust the log-level of one or more Loggers, or the ROOT Logger. However,
a user may only want to adjust the log-level for specific components of his/her Spring Boot application, such as for
Apache Geode, by setting the log-level for only the Logger that logs Apache Geode events.
SBDG's _Logback_ configuration defines 3 Loggers to control the log output from Apache Geode:
.Apache Geode Loggers by name
[source,xml]
----
<logger name="com.gemstone.gemfire" level="${spring.boot.data.gemfire.log.level:-INFO}"/>
<logger name="org.apache.geode" level="${spring.boot.data.gemfire.log.level:-INFO}"/>
<logger name="org.jgroups" level="${spring.boot.data.gemfire.jgroups.log.level:-ERROR}"/>
----
The `com.gemstone.gemfire` Logger is a legacy Logger covering old Pivotal GemFire bits still present in Apache Geode
for backwards compatibility reasons. This Logger's use should be largely unnecessary.
The `org.apache.geode` Logger is the primary Logger used to control log output from all Apache Geode components
during the runtime operation of Apache Geode. Both this Logger and the legacy `com.gemstone.gemfire` Logger default
log output to `INFO`.
The `org.jgroups` Logger is used to log output from Apache Geode's message distribution and membership system.
Apache Geode uses JGroups for membership and message distribution between peer members (nodes) in the cluster
(distributed system). By default, JGroups log messages are logged at `ERROR`.
The log-level for the `com.gemstone.gemfire` and `org.apache.geode` Loggers are configured with the
`spring.boot.data.gemfire.log.level` property. The `org.jgroups` Logger is independently configured with the
`spring.boot.data.gemfire.jgroups.log.level` property.
The SBDG logging properties can be set on the command-line as JVM System Properties when running
your Spring Boot application:
.Setting the log-level from the command-line
[source,text]
----
$ java -classpath ...:/path/to/MySpringBootApplication.jar -Dspring.boot.data.gemfire.log.level=DEBUG
package.to.MySpringBootApplicationClass
----
NOTE: Setting JVM System Properties using `$ java -jar MySpringBootApplication.jar -Dspring.boot.data.gemfire.log.level=DEBUG`
is not supported by the Java Runtime Environment (JRE).
Alternatively, you can configure and control Apache Geode logging in Spring Boot `application.properties`:
.Setting the log-level in `application.properties`
[source,properties]
----
spring.boot.data.gemfire.log.level=DEBUG
----
For backwards compatibility, SBDG additionally supports the old _Spring Data for Apache Geode_ (SDG) logging properties
as well, using either:
`spring.data.gemfire.cache.log-level=DEBUG`
Or:
`spring.data.gemfire.logging.level=DEBUG`
If you previously used either of these SDG based logging properties, they will continue to work as designed in
SBDG `1.3` or later.
[[geode-logging-configuration-composition]]
==== Composing Logging Configuration
As mentioned earlier, SBDG allows you to compose your own logging configuration from SBDG's default, provided _Logback_
configuration metadata.
SBDG conveniently bundles the Loggers and Appenders from SBDG's logging starter into a template file that you can
include into your own, custom _Logback_ XML configuration file.
The _Logback_ template file appears as follows:
.logback-include.xml
[source,xml]
----
include::{starter-logging-resources-dir}/logback-include.xml[]
----
Then, this Logback configuration snippet can be included in an application-specific, _Logback_ XML configuration file
as follows:
.logback.xml
[source,xml]
----
include::{starter-logging-resources-dir}/logback.xml[]
----
[[geode-logging-slf4j-logback-api-support]]
=== SLF4J & Logback API Support
SBDG provides additional support when working with the SLF4J and _Logback_ APIs. This support is available when you
declare the `org.springframework.geode:spring-geode-starter-logging` dependency on your Spring Boot application
classpath.
One of the main supporting classes from the `spring-geode-starter-logger`
is the `org.springframework.geode.logging.slf4j.logback.LogbackSupport` class. This class provides methods to:
* Resolve a reference to the _Logback_ `LoggingContext`
* Resolve the SLF4J ROOT `Logger` as a _Logback_ `Logger`
* Lookup `Appenders` by name and required type
* Add/Remove `Appenders` to `Loggers`
* And even reset the state of the _Logback_ logging system, which can prove to be most useful during testing
`LogbackSupport` can even suppress the auto-configuration of _Logback_ performed by Spring Boot on startup,
another useful utility during automated testing.
In addition to the `LogbackSupport` class, SBDG also provides some custom _Logback_ `Appenders`.
[[geode-logging-slf4j-logback-api-support-appender-composite]]
==== CompositeAppender
The `org.springframework.geode.logging.slf4j.logback.CompositeAppender` class is an implementation of _Logback_
`Appender` and the https://en.wikipedia.org/wiki/Composite_pattern[Composite Software Design Pattern].
`CompositeAppender` enables developers to compose multiple `Appenders` and use them as if they were a single `Appender`.
For example, you could compose both the _Logback_ `ConsoleAppender` and `FileAppender` into one using:
.Composing multiple `Appenders`
[source,java]
----
ConsoleAppender<ILoggingEvent> consoleAppender = ...;
FileAppender<ILoggingEvent> fileAppender = ...;
Appender<ILoggingEvent> compositeAppender = CompositeAppender.compose(consoleAppender, fileAppender);
// do something with the compositeAppender
----
You could then add the `CompositeAppender` to a "named" `Logger` by doing:
.Register `CompositeAppender` on "named" `Logger`
[source,java]
----
Logger namedLogger = LoggerFactory.getLogger("loggerName");
LogbackSupport.toLogbackLogger(namedLogger)
.ifPresent(it -> LogbackSupport.addAppender(it, compositeAppender));
----
In this case, the "named" `Logger` will log events (or log messages) to both the _Console_ and _File_ `Appenders`.
It is simple to compose an array or `Iterable` of `Appenders` by using either the
`CompositeAppender.compose(:Appender<T>[])` method or the `CompositeAppender.compose(:Iterable<Appender<T>>)` method.
[[geode-logging-slf4j-logback-api-support-appender-delegate]]
==== DelegatingAppender
The `org.springframework.geode.logging.slf4j.logback.DelegatingAppender` is a pass-through _Logback_ `Appender`
implementation wrapping another _Logback_ `Appender`, or collection of `Appenders` doing actual work, like the
`ConsoleAppender`, a `FileAppender` or a `SocketAppender`, etc. By default, the `DelegatingAppender` delegates
to the `NOPAppender` thereby doing no actual work.
By default, SBDG registers the `org.springframework.geode.logging.slfj4.logback.DelegatingAppender` with
the ROOT `Logger`, which can be useful for testing purposes.
With a reference to a `DelegatingAppender`, you can add any `Appender` as the delegate, even a `CompositeAppender`:
.Add `ConsoleAppender` as the "delegate" for the `DelegatingAppender`
[source,java]
----
ConsoleAppender consoleAppender = new ConsoleAppender();
LogbackSupport.resolveLoggerContext().ifPresent(consoleAppender::setContext);
consoleAppender.setImmediateFlush(true);
consoleAppender.start();
LogbackSupport.resolveRootLogger()
.flatMap(LogbackSupport::toLogbackLogger)
.flatMap(rootLogger -> LogbackSupport.resolveAppender(rootLogger,
LogbackSupport.DELEGATE_APPENDER_NAME, DelegatingAppender.class))
.ifPresent(delegateAppender -> delegateAppender.setAppender(consoleAppender));
----
[[geode-logging-slf4j-logback-api-support-appender-string]]
==== StringAppender
The `org.springframework.geode.logging.slf4j.logback.StringAppender` stores log message in-memory, appended to
a `String`.
The `StringAppender` is very useful for testing purposes. For instance, you can use the `StringAppender` to assert that
a `Logger` used by certain application components logged messages at the appropriately configured log level while other
log messages were not logged.
For example:
.`StringAppender` in Action
[source,java]
----
class ApplicationComponent {
private final Logger logger = LoggerFactory.getLogger(getClass());
public void someMethod() {
logger.debug("Some debug message");
// ...
}
public void someOtherMethod() {
logger.info("Some info message");
}
}
// Assuming the ApplicationComponent Logger was configured with log-level 'INFO', then...
class ApplicationComponentUnitTests {
private final ApplicationComponent applicationComponent = new ApplicationComponent();
private final Logger logger = LoggerFactory.getLogger(ApplicationComponent.class);
private StringAppender stringAppender;
@Before
public void setup() {
LogbackSupport.toLogbackLogger(logger)
.map(Logger::getLevel)
.ifPresent(level -> assertThat(level).isEqualTo(Level.INFO));
stringAppender = new StringAppender.Builder()
.applyTo(logger)
.build();
}
@Test
public void someMethodDoesNotLogDebugMessage() {
applicationComponent.someMethod();
assertThat(stringAppender.getLogOutput).doesNotContain("Some debug message");
}
@Test
public void someOtherMethodLogsInfoMessage() {
applicationComponent.someOtherMethod();
assertThat(stringAppender.getLogOutput()).contains("Some info message");
}
}
----
There are many other uses for the `StringAppender` and it can be used safely in a multi-Threaded context by calling
`StringAppender.Builder.useSynchronization()`.
When combined with other SBDG provided `Appenders` in conjunction with the `LogbackSupport` class, you have a lot of
power both in application code as well as your tests.