2009-06-03 42 views
21

我需要类似String.format(...)的方法,但需要延迟评估。String.format with lazy evaluation

这个lazyFormat方法应该返回一些对象的toString()方法然后将评估格式模式。

我怀疑有人已经这样做了。这是否可用于任何libararies?

我要取代这个(记录器log4j的实例):

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

与此:

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

我需要lazyFormat仅当调试启用日志记录格式化字符串。

回答

22

,如果你正在寻找一个 “简单” 的解决方案:

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); 
      } 
     }; 
    } 
} 

输出:

一些文本looong串 图案的另一过长...串

,你当然可以如果愿意,可以在lazyFormat中添加任何isDebugEnabled()声明。

+0

Log4J的较新版本允许参数替换请参阅 http://stackoverflow.com/a/14078904/620113 – computermacgyver 2013-01-24 04:26:13

13

,如果你正在寻找懒拼接为有效的记录的缘故,看看Slf4J 这允许你写:

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

字符串连接才会发生,如果调试级别设置。

+2

懒惰评估的问题正是为什么slf4j的{}语法被引入 – 2009-06-03 06:45:28

+0

否则好,但我坚持log4j。我有一种复杂的日志记录设置,所以我不能仅仅删除slf4j。 – 2009-06-03 06:50:41

+2

SLF4J对log4j有一个绑定。所以无论你的“复杂日志记录设置”是什么,SLF4J都能很好地处理它。只有在需要懒惰字符串评估时,才能继续直接使用log4j和SLF4J。 – Ceki 2009-06-03 14:50:54

0

你可以为了调用仅当需要String.format()定义的包装。

有关详细代码示例,请参阅this question

同样的问题也有一variadic function example,如安德烈亚斯的答案建议。

3

Andreas' answer大厦,我能想到的几个方法来只进行格式化的问题,如果Logger.isDebugEnabled回报true

方案1:通过在“不格式化”标志

一个选项是有一个方法参数,用于指示是否实际执行格式化。一个用例可能是:

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

当输出为:

Hello, Bob. 
null 

lazyFormat的代码是:

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

在这种情况下,只执行String.formatformat标志设置为true,并且如果它设置为false它将返回一个null。这将停止记录消息的格式化并仅发送一些“虚拟”信息。

因此,一个用例的记录器可能是:

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

这种方法并不完全适合被要求在这个问题的格式。

选择2:检查记录器

另一种方法是要求记录器直接,如果它isDebugEnabled

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

在这种方法中,预计logger将在lazyFormat可见方法。而这种做法的好处是,主叫方将不需要检查isDebugEnabled方法时lazyFormat被调用,所以典型用途可以是:

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

你可以换你自己的Java5-内的Log4J记录器实例兼容/ String.format兼容类。喜欢的东西:

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));  
    }  
} 

要使用包装,更改记录字段声明:

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

包装类将需要一些额外的方法,例如,它目前不处理日志记录的异常(即logger.debug(message,exception)),但这不应该很难添加。

使用类将是几乎相同的log4j,除了字符串格式化:

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

或者你可以将其与

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

它可以写成

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

通过在最新的log4j中使用参数替换完成2.X版本http://logging.apache.org/log4j/2.x/log4j-users-guide.pdf

4.1.1。2参数替换

通常记录的目的是提供有关系统中正在发生的事情的信息,其中包含有关正在操作的对象的信息。在 Log4j的1.x中,这可以通过这样做来完成:

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

这样做反复具有使 代码的效果觉得它更多的是记录比手头的实际任务。 此外,它会导致记录级别被检查两次;一旦 就调用isDebugEnabled并一次调试方法。一个更好的替代 是:

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

随着日志记录级别 上面的代码将只检查一次,并启用调试日志记录时,字符串结构才会发生 。

5

重要提示:强烈建议所有日志代码移到使用SLF4J(尤其是log4j的1.x中)。它可以保护您免受特定日志实施的任何特殊问题(即错误)的困扰。它不仅为已知的后端实现问题“修复”,而且还与多年来出现的更新更快的实现协同工作。


在你的问题直接回应,在这里它看起来像使用SLF4J

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

您提供什么样的最重要的一点是要传递两个对象实例的事实。 object1.toString()object2.toString()方法不会立即评估。更重要的是,toString()方法仅在它们返回的数据实际将要被使用时才被评估;即懒惰评估的真正意义。

我试图想到一个更通用的模式,我可以使用它并不需要我必须在吨类中重写toString()(并且有些类我没有权限进行重写)。我想出了一个简单的就地解决方案。再次,使用SLF4J,我只在/为日志级别启用时才组成字符串。这里是我的代码:

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 
     } 
    } 

,并简化成的一行,可以缩短在someCodeSomewhereInTheClass()记录器行是:

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

我现在已经重构我的所有日​​志记录代码遵循这个简单的模型。它大大地整理了一些东西。现在,当我看到任何不使用此代码的日志代码时,我会重构日志代码以使用这种新模式,即使它仍然需要。这样,如果/稍后在需要添加一些“昂贵”的操作时进行更改,则基础架构样板已经在那里简化了添加操作的任务。

2

在Log4j 1.2.16中引入了两个类,它们将为您执行此操作。

org.apache.log4j.LogMF它使用java.text.MessageFormat表示消息的格式,而org.apache.log4j.LogSF使用“SLF4J模式语法”并且被认为更快。

下面举例说明:

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

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

如果你喜欢的String.format语法比{0}语法更好,可以使用的Java 8/JDK 8可以使用lambda /供应商:

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

()->...作为供应商在这里,将被评估懒惰。