Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of contents

Table of Contents
excludeTable of contents

Definitions

...

1There are 6 commonly used logging levels:

...

This section contains recommendations about what must and what must not be contained in log messages

1.1 Log message must be informative.

There should be no messages like "Start processing", "Exiting". Messages must always contain parameters/indicators to link with processes or entities

Code Block
languagejava
themeEclipse
firstline1
linenumberstrue
public Future<Boolean> deleteByJobExecutionId(String jobExecutionId, String tenantId) {
	LOGGER.debug("Trying to delete row from the {} table by jobExecutionId = {}", JOURNAL_RECORDS_TABLE, jobExecutionId);

1.2 Business code logging

Controller endpoints or methods containing business must have at least two messages:

...

This should be treated carefully to avoid excessive logging: if the method is simple, or does not require logging (everything is logged in nested methods for example) skip this recommendation. Common layered architecture of a service contains controllers, services and repositories (generated and hand-written). Methods of these classes are a good place to log enter/exit.This can not be directly applied to reactive-style methods, returning Future objects. In this case, the logging should be done in the code which sets Future result.

Code Block
languagejava
themeEclipse
firstline1
linenumberstrue
  public int sumWithCondition(int a, int b, int cond) {
    LOGGER.debug("sumWithCondition:: parameters a: {}, b: {}, cond: {}", a, b, cond);
    int sumResult;
    if (a > cond) {
      LOGGER.info("sumWithCondition:: a > cond");
      sumResult = a + b;
    } else {
      LOGGER.info("sumWithCondition:: a <= cond");
      sumResult = cond + b;
    }
    LOGGER.info("sumWithCondition:: result: {}", sumResult);
    return sumResult;
  }

This can not be directly applied to reactive-style methods, returning Future objects. In this case, the logging should be done in the code which sets Future result, e.g. add logging for onSuccess and ensure there is error logging for onFailure.

1.3 Branching and conditions

Branching conditions and decisions need to be logged at INFO/DEBUG levels

Code Block
languagejava
themeEclipse
firstline1
linenumberstrue
  private CompletableFuture<Optional<MappingParameters>> loadMappingParametersSnapshot(String jobExecutionId, OkapiConnectionParams params) {
    LOGGER.debug("loadMappingParametersSnapshot:: Trying to load MappingParametersSnapshot by jobExecutionId  '{}' for cache, okapi url: {}, tenantId: {}", jobExecutionId, params.getOkapiUrl(), params.getTenantId());
    return RestUtil.doRequest(params, "/mapping-metadata/"+ jobExecutionId, HttpMethod.GET, null)
      .toCompletionStage()
      .toCompletableFuture()
      .thenCompose(httpResponse -> {
        if (httpResponse.getResponse().statusCode() == HttpStatus.SC_OK) {
          LOGGER.info("loadMappingParametersSnapshot:: MappingParametersSnapshot was loaded by jobExecutionId '{}'", jobExecutionId);
          return CompletableFuture.completedFuture(Optional.of(Json.decodeValue(httpResponse.getJson().getString("mappingParams"), MappingParameters.class)));
        } else if (httpResponse.getResponse().statusCode() == HttpStatus.SC_NOT_FOUND) {
          LOGGER.warn("loadMappingParametersSnapshot:: MappingParametersSnapshot was not found by jobExecutionId '{}'", jobExecutionId);
          return CompletableFuture.completedFuture(Optional.empty());
        } else {
          String message = String.format("loadMappingParametersSnapshot:: Error loading MappingParametersSnapshot by jobExecutionId: '%s', status code: %s, response message: %s",
            jobExecutionId, httpResponse.getResponse().statusCode(), httpResponse.getBody());
          LOGGER.warn(message);
          return CompletableFuture.failedFuture(new CacheLoadingException(message));
        }
    });
  }
}

1.4 Exception and error handling

In case of expected (handled) exception, there must be a WARN level log message.

...

If sensitive data or part of it is required to troubleshoot, it should be tagged in log with a special token. It will allow to mask/remove those messages after they are no longer required or to limit access to logs, containing those tags.

Masking example from RMB:

Code Block
languagejava
themeEclipse
titlemasking password
  /**
   * Log postgreSQLClientConfig.
   */
  @SuppressWarnings("squid:S2068")  // Suppress "Credentials should not be hard-coded"
                                    // "'password' detected in this expression".
                                    // False positive: Password is configurable, here we remove it from the log.
  private void logPostgresConfig() {
    if (! log.isInfoEnabled()) {
      return;
    }
    JsonObject passwordRedacted = postgreSQLClientConfig.copy();
    passwordRedacted.put(PASSWORD, "...");
    log.info("postgreSQLClientConfig = {}", passwordRedacted.encode());
  }


Info

Additional information in log

...

Log aggregation

See: Folio logging solution