Pregunta

Necesito algo similar a String.Format (...) método, pero con la evaluación perezosa.

Este método lazyFormat debe devolver un objeto cuyo método toString () sería entonces evaluar el patrón de formato.

Sospecho que alguien ya lo ha hecho. Es esta disponible en cualquier libararies?

quiero reemplazar esto (ejemplo log4j registrador es):

if(logger.isDebugEnabled() ) {
   logger.debug(String.format("some texts %s with patterns %s", object1, object2));
}

con esto:

logger.debug(lazyFormat("some texts %s with patterns %s", object1, object2));

Necesito lazyFormat de formato de cadena sólo si el registro de depuración está habilitada.

¿Fue útil?

Solución

Si usted está buscando una solución "simple":

 public class LazyFormat {

    public static void main(String[] args) {
        Object o = lazyFormat("some texts %s with patterns %s", "looong string", "another loooong string");
        System.out.println(o);
    }

    private static Object lazyFormat(final String s, final Object... o) {
        return new Object() {
            @Override
            public String toString() {
                return String.format(s,o);
            }
        };
    }
}

salidas:

  

algunos textos Looong cadena con   patrones de otra cadena muuuucho

por supuesto puede añadir cualquier declaración isDebugEnabled() dentro lazyFormat si se quiere.

Otros consejos

Se puede hacer mediante el uso de sustitución de parámetros en la más nueva versión log4j 2.X http://logging.apache.org/log4j/2.x/log4j-users-guide.pdf :

  

4.1.1.2 parámetro de sustitución

     

Con frecuencia los fines de registro es proporcionar información sobre lo que está ocurriendo en el sistema, lo cual   requiere incluyendo información sobre los objetos que están siendo manipulados. En   Log4j 1.x esto podría lograrse haciendo:

if (logger.isDebugEnabled()) {     
  logger.debug("Logging in user " + user.getName() + " with id " + user.getId()); 
} 
  

Hacer esto varias veces tiene el efecto de hacer que la   código siente como que es más acerca del registro de la tarea real a la mano.   Además, resulta en el nivel de registro que se comprueba dos veces; una vez   en la llamada a isDebugEnabled y una vez en el método de depuración. Un mejor   alternativa sería:

logger.debug("Logging in user {} with id {}", user.getName(), user.getId()); 
  

Con el código anterior el nivel de registro   sólo se deben revisar una vez y la construcción de cuerdas sólo ocurrirá   cuando el registro de depuración está habilitada.

Si usted está buscando para la concatenación perezoso por el bien de la tala eficiente, echar un vistazo a SLF4J esto le permite escribir:

LOGGER.debug("this is my long string {}", fatObject);

la concatenación de cadenas sólo se producirá si el nivel de depuración se establece.

NOTA IMPORTANTE: Se recomienda encarecidamente a todos los código de registro de ser trasladado a usar SLF4J (especialmente 1.x log4j). Te protege de ser atrapado con ningún tipo de problemas idiosincrásicos (es decir bugs) con implementaciones específicas de registro. No sólo tienen "soluciones" para así conocer las cuestiones de aplicación de back-end, que también funciona con implementaciones más rápidas más nuevos que han surgido en los últimos años.


En respuesta directa a su pregunta, aquí lo que se vería como el uso de SLF4J :

LOGGER.debug("some texts {} with patterns {}", object1, object2);

El bit más importante de lo que ha proporcionado es el hecho de que está pasando dos instancias de Object. El object1.toString() y los métodos object2.toString() no se evalúan inmediatamente. Más importante aún, los métodos toString() sólo se evalúan si los datos vuelven en realidad va a ser utilizado; es decir, el verdadero significado de la evaluación perezosa.

Traté de pensar en un patrón más general que podría utilizar que no requiere que yo tenga que anular toString() en toneladas de clases (y hay clases en las que no tienen acceso a hacer la anulación). Se me ocurrió con una simple solución de gotas en el lugar. Una vez más, el uso de SLF4J , compongo la cadena sólo si / cuando el registro para el nivel está habilitado. Aquí está mi código:

    class SimpleSfl4jLazyStringEvaluation {
      private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSfl4jLazyStringEvaluation.class);

      ...

      public void someCodeSomewhereInTheClass() {
//all the code between here
        LOGGER.debug(
            "{}"
          , new Object() {
              @Override
              public String toString() {
                return "someExpensiveInternalState=" + getSomeExpensiveInternalState();
              }
            }
//and here can be turned into a one liner
        );
      }

      private String getSomeExpensiveInternalState() {
        //do expensive string generation/concatenation here
      }
    }

Y para simplificar en el de una sola línea , se puede acortar la línea del maderero en someCodeSomewhereInTheClass () para ser:

LOGGER.debug("{}", new Object(){@Override public String toString(){return "someExpensiveInternalState=" + getSomeExpensiveInternalState();}});

Ahora he rediseñado toda mi código de registro para seguir este modelo simple. Se ha arreglado las cosas considerablemente. Y ahora cuando veo a ningún código de registro que no utiliza esto, refactorizar el código de registro que puede utilizar este nuevo patrón, incluso si se necesita todavía. De esa manera ya, si / cuando se realiza un cambio posterior a tener que añadir un poco de la operación "cara", el repetitivo infraestructura está ahí simplificando la tarea de limitarse a añadir la operación.

Sobre la base de Andreas' respuesta , no puedo pensar en una par de aproximaciones al tema de la única que realiza el formateo si los retornos Logger.isDebugEnabled true:

Opción 1: Pass en una bandera "do de formato"

Una opción es tener un argumento de un método que indica si realizar o no en realidad el formato. Un caso de uso podría ser:

System.out.println(lazyFormat(true, "Hello, %s.", "Bob"));
System.out.println(lazyFormat(false, "Hello, %s.", "Dave"));

Cuando la salida sería:

Hello, Bob.
null

El código para lazyFormat es:

private String lazyFormat(boolean format, final String s, final Object... o) {
  if (format) {
    return String.format(s, o);
  }
  else {
    return null;
  }
}

En este caso, la única String.format se ejecuta cuando la bandera format se establece en true, y si se establece en false devolverá un null. Esto dejaría el formato del mensaje de registro que puede ocurrir y se acaba de enviar algo de información "falsa".

Así que un caso de uso con el registrador podría ser:

logger.debug(lazyFormat(logger.isDebugEnabled(), "Message: %s", someValue));

Este método no se ajusta exactamente el formato que se pide en la pregunta.

Opción 2: Compruebe el registrador

Otro enfoque consiste en pedir al registrador directamente si isDebugEnabled:

private static String lazyFormat(final String s, final Object... o) {
  if (logger.isDebugEnabled()) {
    return String.format(s, o);
  }
  else {
    return null;
  }
}

En este enfoque, se espera que logger serán visibles en el método lazyFormat. Y el beneficio de este enfoque es que la persona que llama no necesita ser verificar la técnica isDebugEnabled cuando lazyFormat se llama, por lo que el uso típico puede ser:

logger.debug(lazyFormat("Debug message is %s", someMessage));

Se puede envolver la instancia registrador Log4J dentro de su propia clase compatible con Java5 / String.Format compatibles. Algo así como:

public class Log4jWrapper {

    private final Logger inner;

    private Log4jWrapper(Class<?> clazz) {
        inner = Logger.getLogger(clazz);
    }

    public static Log4jWrapper getLogger(Class<?> clazz) {
        return new Log4jWrapper(clazz);
    }

    public void trace(String format, Object... args) {
        if(inner.isTraceEnabled()) {
            inner.trace(String.format(format, args));    
        }
    }

    public void debug(String format, Object... args) {
        if(inner.isDebugEnabled()) {
            inner.debug(String.format(format, args));    
        }
    }

    public void warn(String format, Object... args) {
        inner.warn(String.format(format, args));    
    }

    public void error(String format, Object... args) {
        inner.error(String.format(format, args));    
    }

    public void fatal(String format, Object... args) {
        inner.fatal(String.format(format, args));    
    }    
}

Para utilizar el contenedor, cambiar su declaración de campo registrador a:

private final static Log4jWrapper logger = Log4jWrapper.getLogger(ClassUsingLogging.class);

La clase contenedora necesitaría algunos métodos adicionales, por ejemplo, que no se ocupa de la actualidad de las excepciones de registro (es decir logger.debug (mensaje, excepción)), pero esto no debería ser difícil añadir.

Uso de la clase sería casi idéntica a log4j, excepto cadenas tienen el formato:

logger.debug("User {0} is not authorized to access function {1}", user, accessFunction)

Introducido en Log4J 1.2.16 son dos clases que lo hará por usted.

org.apache.log4j.LogMF que utiliza un formato que java.text.MessageFormat de mensajes y org.apache.log4j.LogSF que utiliza el "patrón de sintaxis SLF4J" y se dice que es más rápido.

Estos son algunos ejemplos:

LogSF.debug(log, "Processing request {}", req);

y

 LogMF.debug(logger, "The {0} jumped over the moon {1} times", "cow", 5); 

O se podría escribir como

debug(logger, "some texts %s with patterns %s", object1, object2);

con

public static void debug(Logger logger, String format, Object... args) {
    if(logger.isDebugEnabled()) 
       logger.debug(String.format("some texts %s with patterns %s", args));
}

Si te gusta el String.Format Sintaxis mejor que el {0} Sintaxis y puede utilizar Java 8/8 JDK puede utilizar lambdas / Proveedores:

logger.log(Level.FINER, () -> String.format("SomeOperation %s took %04dms to complete", name, duration));

()->... actúa como un proveedor aquí y será evaluado con pereza.

Se podría definido un envoltorio con el fin de llamar la String.format() sólo si es necesario.

esta pregunta para un ejemplo de código detallada.

La misma pregunta tiene también una función de variadic ejemplo , como se sugiere en la respuesta de Andreas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top