Question

J'ai besoin quelque chose de similaire à String.format (...) méthode , mais avec évaluation paresseuse.

Cette méthode lazyFormat doit retourner un objet dont la méthode toString () serait alors évaluer le modèle de format.

Je soupçonne que quelqu'un a déjà fait. Est-ce disponible dans tout libararies?

Je veux remplacer (enregistreur est par exemple log4j):

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

avec ceci:

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

J'ai besoin lazyFormat au format chaîne uniquement si l'enregistrement de débogage est activé.

Était-ce utile?

La solution

si vous êtes à la recherche d'une solution "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);
            }
        };
    }
}

sorties:

  

quelques textes chaîne Looong avec   modèles une autre chaîne looooooongues

vous pouvez bien sûr ajouter une déclaration de isDebugEnabled() à l'intérieur lazyFormat si vous voulez.

Autres conseils

Il peut être fait en utilisant la substitution de paramètres dans toute nouvelle version 2.X log4j http://logging.apache.org/log4j/2.x/log4j-users-guide.pdf :

  

4.1.1.2 Paramètre Remplacement

     

Foire aux fins de l'exploitation forestière est de fournir des informations sur ce qui se passe dans le système,   exige notamment des informations sur les objets manipulés. Dans   1.x log4j cela pourrait se faire en faisant:

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

Faire cela a à plusieurs reprises pour effet de rendre la   le code se sentir comme il est plus sur la connexion que la tâche réelle à portée de main.   De plus, elle se traduit par le niveau d'enregistrement en cours de vérification deux fois; une fois que   sur l'appel à isDebugEnabled et une fois sur la méthode de débogage. Un meilleur   alternative serait:

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

Avec le code au-dessus du niveau de journalisation   ne sera vérifiée une fois la construction de chaîne ne se produira   lorsque l'enregistrement de débogage est activé.

si vous êtes à la recherche de la concaténation paresseux à cause de l'exploitation forestière efficace, jetez un oeil à SLF4J Cela vous permet d'écrire:

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

la concaténation de chaîne aura lieu uniquement si le niveau de débogage est défini.

NOTE IMPORTANTE: Il est fortement recommandé tout le code se connecter déplacer à utiliser SLF4J (en particulier 1.x log4j). Il vous protège d'être coincé avec toute sorte de problèmes idiosyncrasiques (à savoir des bugs) avec les implémentations de journalisation spécifiques. Non seulement il ne « fixe » pour bien connaître les problèmes de mise en œuvre de back-end, il fonctionne également avec des implémentations plus rapides nouvelles qui ont émergé au fil des ans.


En réponse à votre question, ici ce qu'il resemble en utilisant SLF4J :

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

Le bit le plus important de ce que vous avez fourni est le fait que vous passez deux instances de l'objet. Le object1.toString() et les méthodes de object2.toString() ne sont pas immédiatement évalués. Plus important encore, les méthodes de toString() ne sont évaluées que si les données qu'ils le retour va effectivement être utilisé; dire le vrai sens de l'évaluation paresseuse.

J'ai essayé de penser à un modèle plus général que je pouvais utiliser ce qui n'a pas besoin d'avoir à mon remplacer toString() en tonnes de classes (et il y a des classes où je n'ai pas accès à faire la dérogation). Je suis venu avec une solution simple goutte en place. Encore une fois, en utilisant SLF4J , je compose la chaîne seulement si / lors de la connexion au niveau est activé. Voici mon code:

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

Et pour simplifier dans le one-liner , vous pouvez raccourcir la ligne de LOGGER dans someCodeSomewhereInTheClass () être:

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

Je suis maintenant refondus tout mon code d'enregistrement à suivre ce modèle simple. Il a mis de l'ordre des choses considérablement. Et maintenant, quand je vois un code d'enregistrement qui n'utilise pas, je Refactor le code d'enregistrement à utiliser ce nouveau modèle, même si elle est encore nécessaire. De cette façon, si / quand un changement est fait plus tard besoin d'ajouter une opération « coûteux », le passe-partout de l'infrastructure est déjà il simplifie la tâche à ajouter simplement l'opération.

réponse Andreas de, je peux penser à un deux approches de la question de la mise en forme que l'exécution si les déclarations de Logger.isDebugEnabled true:

Option 1: Passer dans un drapeau "ne formatage"

L'une des options est d'avoir un argument de méthode qui indique si oui ou non d'effectuer effectivement la mise en forme. Un cas d'utilisation pourrait être:

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

Si la sortie serait:

Hello, Bob.
null

Le code pour lazyFormat est:

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

Dans ce cas, la String.format est exécutée uniquement lorsque l'indicateur format est réglé sur true, et si elle est réglée sur false il retournera un null. Cela empêcherait la mise en forme du message d'enregistrement de se produire et il suffit d'envoyer quelques informations « factice ».

Ainsi, un cas d'utilisation avec l'enregistreur pourrait être:

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

Cette méthode ne correspond pas exactement à la mise en forme qui est demandé dans la question.

Option 2: Vérifiez le Logger

Une autre approche consiste à demander à l'enregistreur directement si elle isDebugEnabled:

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

Dans cette approche, il est prévu que logger sera visible dans la méthode lazyFormat. Et l'avantage de cette approche est que l'appelant ne sera pas nécessaire de vérifier la méthode isDebugEnabled lorsque lazyFormat est appelé, donc l'utilisation typique peut être:

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

Vous pouvez envelopper l'instance de l'enregistreur Log4J dans votre propre Java5 compatible / String.format classe compatible. Quelque chose comme:

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

Pour utiliser l'emballage, changer votre déclaration de champ enregistreur à:

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

La classe wrapper aurait besoin de quelques méthodes supplémentaires, par exemple, il ne gère pas actuellement des exceptions d'exploitation forestière (c.-à-logger.debug (message, exception)), mais cela ne devrait pas être difficile à ajouter.

Utilisation de la classe serait presque identique à log4j, à l'exception des chaînes sont formatées:

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

Introduit dans Log4j 1.2.16 sont deux classes qui le fera pour vous.

org.apache.log4j.LogMF qui utilise un java.text.MessageFormat pour le format des messages et vous org.apache.log4j.LogSF qui utilise le « modèle SLF4J syntaxe » et est dit être plus rapide.

Voici des exemples:

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

et

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

Ou vous pouvez l'écrire comme

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

avec

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 vous aimez la syntaxe String.format mieux que {0} Syntaxe et peut utiliser Java 8/8 JDK vous pouvez utiliser lambdas / Fournisseurs:

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

()->... agit comme fournisseur ici et sera évalué paresseusement.

Vous pouvez définir une enveloppe pour appeler la String.format() que si nécessaire.

Voir cette question un exemple de code détaillé.

La même question a également exemple fonction variadique , comme l'a suggéré dans la réponse de Andreas.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top