[[geode-logging]]
== Logging
:geode-name: {apache-geode-name}


{geode-name} `1.9.2` was modularized to separate its use of the Apache Log4j API to log output in {geode-name} 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 (`log4j-api`) and the Apache Log4j service provider (`log4j-core`)
were automatically pulled in by {geode-name} core (`org.apache.geode:geode-core`), thereby making it problematic
to change logging providers when using {geode-name} in Spring Boot applications.

However, now, in order to get any log output from {geode-name} whatsoever, {geode-name} requires a logging provider
declared on your Spring Boot application classpath. Consequently, this also means the old {geode-name} `Properties`
(such as `log-level`) no longer have any effect, regardless of whether the property is specified in `gemfire.properties`,
in Spring Boot `application.properties`, or even as a JVM System Property (`-Dgemfire.log-level`).

TIP: See {geode-name}'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 {geode-name}
{spring-data-geode-javadoc}/org/springframework/data/gemfire/config/annotation/EnableLogging.html[`@EnableLogging`]
annotation no longer has any effect on {geode-name} logging either. Consequently, 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 set the corresponding {geode-name}
properties, which, again, are useless from {geode-name} `1.9.2` onward.

By way of example, and to make this concrete, *none* of the following approaches have any effect on {geode-name} logging:

.Command-line configuration
====
[source,txt]
----
$ java -classpath ...:/path/to/MySpringBootApacheGeodeClientCacheApplication.jar -Dgemfire.log-level=DEBUG
    example.app.MySpringBootApacheGeodeClientCacheApplication
----
====

.Externalized configuration using {geode-name} `gemfire.properties`
====
[source,properties]
----
# {geode-name} only/specific properties
log-level=INFO
----
====

.Externalized configuration using Spring Boot `application.properties`
====
[source,properties]
----
spring.data.gemfire.cache.log-level=DEBUG
spring.data.gemfire.logging.level=DEBUG
----
====

.Java configuration using SDG's `@EnableLogging` annotation
====
[source,java]
----
@SpringBootApplication
@EnableLogging(logLevel = "DEBUG")
class MySpringBootApacheGeodeClientApplication {

}
----
====

None of the preceding approaches have any effect without the *new* SBDG logging starter.

[[geode-logging-configuration]]
=== Configure {geode-name} Logging

So, how do you configure logging for {geode-name}?

Three things are required to get {geode-name} to log output:

. You must declare a logging provider (such as Logback) on your Spring Boot application classpath.
. (optional) You can declare an adapter (a 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 and use Logback as your
logging provider or implementation, you must include the `org.apache.logging.log4j.log4j-to-slf4j` adapter or
bridge JAR as well.
+
Internally, {geode-name} uses the Apache Log4j API to log output from Geode components. Therefore, you must bridge Log4j
to any other logging provider (such as Logback) that is not Log4j (`log4j-core`). If you use Log4j as your
logging provider, you need not declare an adapter or bridge JAR on your Spring Boot application classpath.
. Finally, you must supply logging provider configuration to configure Loggers, Appenders, log levels, and other details.
+
For example, when you use 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
{geode-name} to log output.

NOTE: {geode-name}'s `geode-log4j` module covers the required configuration for steps 1-3 above and uses Apache Log4j
(`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 {geode-name}.

If you declare Spring Boot's own `org.springframework.boot:spring-boot-starter-logging` on your application classpath,
it covers steps 1 and 2 above.

The `spring-boot-starter-logging` dependency declares Logback as the logging provider and automatically adapts (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 for
{geode-name} as well.

SBDG has simplified the setup of {geode-name} logging. You need only declare the
`org.springframework.geode:spring-geode-starter-logging` dependency on your Spring Boot application classpath.

Unlike {geode-name}'s default Log4j XML configuration file (`log4j2.xml`), SBDG's provided `logback.xml` configuration
file is properly parameterized, letting you adjust log levels as well as add Appenders.

In addition, SBDG's provided Logback configuration uses templates so that 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,
you may want to only adjust the log level for specific components of your Spring Boot application, such as for
{geode-name}, by setting the log level for only the Logger that logs {geode-name} events.

SBDG's Logback configuration defines three Loggers to control the log output from {geode-name}:

.{geode-name} Loggers by Name
====
[source,xml]
----
<comfiguration>
  <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}"/>
</comfiguration>
----
====

The `com.gemstone.gemfire` Logger covers old GemFire components that are still present in {geode-name} for backwards
compatibility. By default, it logs output at `INFO`. This Logger's use should be mostly unnecessary.

The `org.apache.geode` Logger is the primary Logger used to control log output from all {geode-name} components
during the runtime operation of {geode-name}. By default, it logs output at `INFO`.

The `org.jgroups` Logger is used to log output from {geode-name}'s message distribution and membership system.
{geode-name} uses JGroups for membership and message distribution between peer members (nodes) in the cluster
(distributed system). By default, JGroups logs output at `ERROR`.

You can configure the log level for the `com.gemstone.gemfire` and `org.apache.geode` Loggers by setting
the `spring.boot.data.gemfire.log.level` property. You can independently configure the `org.jgroups` Logger by setting
the `spring.boot.data.gemfire.jgroups.log.level` property.

You can set the SBDG logging properties on the command line as JVM System properties when you run
your Spring Boot application:

.Setting the log-level from the CLI
====
[source,text]
----
$ java -classpath ...:/path/to/MySpringBootApplication.jar -Dspring.boot.data.gemfire.log.level=DEBUG
    package.to.MySpringBootApplicationClass
----
====

NOTE: Setting JVM System properties by 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 {geode-name} logging in Spring Boot `application.properties`:

.Setting the log-level in Spring Boot `application.properties`
====
[source,properties]
----
spring.boot.data.gemfire.log.level=DEBUG
----
====

For backwards compatibility, SBDG additionally supports the Spring Data for {geode-name} (SDG) logging properties
as well, by using either of the following properties:

.Setting log-level using SDG Properties
====
[source,properties]
----
spring.data.gemfire.cache.log-level=DEBUG
spring.data.gemfire.logging.level=DEBUG
----
====

If you previously used either of these SDG-based logging properties, they continue to work as designed in SBDG `1.3`
or later.

[[geode-logging-configuration-composition]]
==== Composing Logging Configuration

As mentioned earlier, SBDG lets you compose your own logging configuration from SBDG's default 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 you can include this Logback configuration snippet 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 and 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`.
* Look up `Appenders` by name and required type.
* Add or remove `Appenders` to `Loggers`.
* 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, which is
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 the Logback
`Appender` interface and the https://en.wikipedia.org/wiki/Composite_pattern[Composite software design pattern].

`CompositeAppender` lets developers 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 `Appender`:

.Composing multiple `Appenders`
====
[source,java]
----
class LoggingConfiguration {

  Appender<ILoggingEvent> compositeAppender() {

    ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<>();

    FileAppender<ILoggingEvent> fileAppender = new FileApender<>();

    Appender<ILoggingEvent> compositeAppender = CompositeAppender.compose(consoleAppender, fileAppender);

    return compositeAppender;
  }
}

// do something with the compositeAppender
----
====

You could then add the `CompositeAppender` to a named `Logger`:

.Register `CompositeAppender` on "named" `Logger`
====
[source,java]
----
class LoggerConfiguration {

  void registerAppenderOnLogger() {

    Logger namedLogger = LoggerFactory.getLogger("loggerName");

    LogbackSupport.toLogbackLogger(namedLogger)
      .ifPresent(it -> LogbackSupport.addAppender(it, compositeAppender));
  }
}
----
====

In this case, the named `Logger` logs events (or log messages) to both the console and file Appenders.

You can 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 that wraps another Logback `Appender` or collection of `Appenders`, such as the `ConsoleAppender`,
a `FileAppender`, a `SocketAppender`, or others. 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` (even a `CompositeAppender`) as the delegate:

.Add `ConsoleAppender` as the "delegate" for the `DelegatingAppender`
====
[source,java]
----
class LoggerConfiguration {

  void setupDelegation() {

    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 a log message in-memory, appended to
a `String`.

The `StringAppender` is 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.

Consider the following 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 you can use it 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 in your tests.
