在 Java 中创建用于日志记录或异常的消息的最佳实践

Best Practices to create Message for Logging or Exception in Java

我在 Java 6.

中找到了这段代码
String mensajeExcluido = ArqSpringContext.getPropiedad("MENSAJE.EXCLUIDO");
LOG.warn("ERROR: Servicio: " + mensajeExcluido + ":" + someDTO.getProperty() +
        ",\tsomeValue:" + someDTO.getValue() + "'.");
throw new Exception(mensajeExcluido);

此代码

String mensajeExcluido = ArqSpringContext.getPropiedad("REGLA.MENSAJE");
String mensajeWarn = "ALERTA: Otro Servicio: " + mensajeExcluido + ":" +
        someDTO.getProperty() + ",\tsomeValue:" + someDTO.getValue() + "'.";
LOG.warn(mensajeWarn);

boolean exclusionVisible = Boolean.valueOf(ArqSpringContext.getPropiedad("EXCLUSION.VISIBLE"));
if (exclusionVisible) {
    mensajeWarn = "<br></br>" + mensajeWarn;
} else {
    mensajeWarn = "";
}
throw new Exception(mensajeExcluido + mensajeWarn);

此代码

LOG.warn("No se pudo validar Client Service. Code: " +
        someDTO.getStatusCode() + ".");
return "No se pudo validar Client Service. Code: " +
        someDTO.getStatusCode() + ".";

为了遵循最佳实践...

哪些建议适用?

他们会对代码做哪些修改?

应该如何处理文本?

首先,尽量避免在检查是否应该打印日志语句之前进行消息创建处理(即:在检查日志级别之前不要连接消息字符串。

// Better this
if (LOG.isDebugEnabled())
    LOG.debug("This is a message with " + variable + " inside");

// Than this
final String message = "This is a message with " + variable + " inside";
if (LOG.isDebugEnabled())
    LOG.debug(message);

大多数 Java 日志框架允许提前检查是否要根据给定设置打印日志语句。

如果您想省去为每个日志语句编写这些检查的负担,您可以利用 Java 8 个 lambda 并编写如下实用程序:


import java.util.function.Supplier;
import java.util.logging.Logger;

import static java.util.logging.Level.FINE;

class MyLogger {

    public static MyLogger of(final Class<?> loggerClass) {
        return new MyLogger(loggerClass);
    }

    private final Logger logger;

    private MyLogger(final Class<?> loggerClass) {
        logger = Logger.getLogger(loggerClass.getName());
    }

    // Supplier will be evaluated AFTER checking if log statement must be executed
    public void fine(final Supplier<?> message) {
        if (logger.isLoggable(FINE))
            logger.log(FINE, message.get().toString());
    }
}

static final LOG = MyLogger.of(String.class);

public void example() {
    LOG.fine(() -> "This is a message with a system property: " + System.getProperty("property"));
}

最后,您可以利用 Java 字符串格式来使用 String.format 格式化日志消息。即:

final String message = String.format("Print %s string and %d digit", "str", 42);

应用于您提供的示例的那些良好做法是:

/*
 * Using java.util.logging in JDK8+
 */

import java.util.logging.Level;
import java.util.logging.Logger;

import static java.lang.String.format;

class Dto {
    String getProperty() { return "property"; }
    String getValue() { return "property"; }
    String getStatusCode() { return "statusCode"; }
}

final Logger LOG = Logger.getGlobal();
final Dto someDTO = new Dto();

void example1() throws Exception {
    String mensajeExcluido = System.getProperty("MENSAJE.EXCLUIDO");

    // Check if log will be printed before composing the log message
    if (LOG.isLoggable(Level.WARNING)) {
        // Using String.format usually is clearer and gives you more formatting options
        final String messageFormat = "ERROR: Servicio: %s:%s,\tsomeValue:%s'.";
        LOG.warning(format(messageFormat, mensajeExcluido, someDTO.getProperty(), someDTO.getValue()));
    }

    // Or using lambdas
    LOG.warning(() -> {
        final String message = "ERROR: Servicio: %s:%s,\tsomeValue:%s'.";
        return format(message, mensajeExcluido, someDTO.getProperty(), someDTO.getValue());
    });

    throw new Exception(mensajeExcluido);
}

void example2() throws Exception {
    String mensajeExcluido = System.getProperty("REGLA.MENSAJE");
    String mensajeWarn = format(
        // The concatenated message is probably missing a single quote at 'someValue'
        "ALERTA: Otro Servicio: %s:%s,\tsomeValue:%s'.",
        mensajeExcluido,
        someDTO.getProperty(),
        someDTO.getValue()
    );

    LOG.warning(mensajeWarn);

    boolean exclusionVisible = Boolean.parseBoolean(System.getProperty("EXCLUSION.VISIBLE"));
    String exceptionMessage = exclusionVisible ?
            mensajeExcluido + "<br></br>" + mensajeWarn : mensajeExcluido;

    throw new Exception(exceptionMessage);
}

String example3() {
    // You can compose the message only once and use it for the log and the result
    String message =
        format("No se pudo validar Client Service. Code: %s.", someDTO.getStatusCode());
    LOG.warning(message);
    return message;
}