web-dev-qa-db-fra.com

Meilleures pratiques pour la journalisation Java à partir de plusieurs threads?

Je souhaite disposer d'un journal de diagnostic généré par plusieurs tâches de gestion des données. Ces tâches peuvent être dans plusieurs threads. Chaque tâche doit écrire un élément (éventuellement avec des sous-éléments) dans le journal. entrer et sortir rapidement. S'il s'agissait d'une situation à tâche unique, j'utiliserais XMLStreamWriter , car cela semble être la meilleure correspondance pour la simplicité/la fonctionnalité sans avoir à conserver un document XML en ballon en mémoire. 

Mais ce n’est pas une situation de tâche unique, et je ne suis pas sûr de la meilleure façon de s’assurer que c’est «threadsafe», où «threadsafe» dans cette application signifie que chaque élément du journal doit être écrit dans le journal correctement et en série (un après l’autre). l’autre et non entrelacé de quelque manière que ce soit).

Aucune suggestion? J'ai une vague idée que la solution consiste à utiliser une file d'attente d'éléments de journal (chacun pouvant être généré rapidement: mon application est occupée à effectuer un travail réel et sensible aux performances), et à disposer d'un thread distinct qui gère le journal. éléments et les envoie dans un fichier pour que la journalisation n'interrompe pas les producteurs.

La journalisation ne doit pas nécessairement être en XML, mais je souhaite qu'elle soit structurée et lisible par machine.

edit: je mets "threadsafe" entre guillemets. Log4j semble être le choix évident (nouveau pour moi mais ancien pour la communauté), pourquoi réinventer la roue ...

25
Jason S

Utilisez un cadre de journalisation, tel que Log4j .

21
Avi

Je pense que vous êtes sur le mauvais chemin. Vous dites «threadsafe» mais vous voulez dire «sérialisé». Threadsafe signifie qu'un thread n'interférera pas avec les données d'un autre thread. La plupart du temps, les problèmes de thread sont résolus à l'avance et vous ne devez pas vous en préoccuper simplement pour vous connecter. Par exemple, si vous écrivez:

myVariableSum = 0 + myVariable;
//here comes other thread - Not very likely!
logger.info("Log some INFO; myVariable has value" + myVariable.toString());

Vous devez vous assurer que maVariable n'a pas été modifiée par un autre thread à partir du moment où le calcul (première ligne) a été effectué mais avant l'appel de la méthode de journalisation. Si cela se produit, vous enregistrez une valeur incorrecte qui n'a pas été utilisée pour effectuer l'opération, mais une valeur affectée par un autre thread. Ceci est généralement pris en charge; par exemple, la variable locale (niveau de la méthode) ne peut pas être modifiée par un autre thread. Quoi qu'il en soit, si vous devez vous inquiéter à ce sujet lors de la journalisation, plus de 99% des problèmes de threads graves de votre programme sont déjà enregistrés ..__
Tous les principaux environnements de journalisation sont eux-mêmes «threadsafe», ce qui signifie qu'ils peuvent être déployés dans des environnements multithread et ne présenteront pas de problèmes similaires à ceux décrits ci-dessus en interne.
Faire en sorte que les traces apparaissent dans le journal afin qu’elles se produisent est généralement appelé «sérialisation» des appels. La sérialisation des écritures dans les journaux constituera un goulot d'étranglement majeur en termes de performances pour toute application multithread. Si vous utilisez la structure de journalisation, comme log4j, les traces de tous les threads apparaîtront plus ou moins au même endroit afin qu'elles se produisent. Cependant, une colonne est généralement le nom du thread, vous pouvez donc facilement filtrer vos données de journal par thread; chaque thread enregistrera ses données dans un ordre chronologique. Consultez ce lien: http://logging.Apache.org/log4j/1.2/faq.html#1.7
Enfin, si vous avez vraiment besoin de sérialiser les écritures de journal, vous pouvez utiliser une sorte de structure, telle que Java.util.concurrent.BlockingQueue pour acheminer vos messages.

22
Dan

Utilisez logback-classic. C'est une nouvelle et meilleure implémentation de log4j. 

9
Ceki

J'ai tendance à utiliser SLF4J au-dessus de Log4J. La fonctionnalité de journalisation paramétrée est particulièrement intéressante si vous avez de nombreuses instructions de journalisation susceptibles de s’éteindre dans un environnement de production.

Il peut également s'exécuter via Java.util.logging ou utiliser sa propre sortie simple.

5
Evan

Vous pouvez utiliser des mécanismes de synchronisation (comme un moniteur ou une sémaphore) pour vous assurer qu'une requête de journal est traitée avant d'accepter la suivante. Tout cela pourrait être caché du code appelant les routines de journalisation.

4
jpfollenius

Utilisez un cadre de journalisation, tel que Log4.

et si vous n'êtes pas satisfait du résultat, vous pouvez écrire votre propre filtre Appender, tout ce que vous souhaitez modifier. Vous pouvez donc même mettre en cache pour réorganiser les entrées, bien que je ne dis pas que c'est une bonne idée.

4
Jens Schauder

Utilisez un cadre de journalisation qui implémente une forme de le modèle NDC , comme Log4J .

4
eljenso

log4j est et a été la norme pour la journalisation Java pendant de nombreuses années. Mais si vous n’aimez pas les dépendances externes, le paquet Java.util.logging fournit une solution acceptable.

3
Gareth Davis

J'ai eu un problème similaire et des demandes d'implémentation pour des journaux spéciaux uniquement. Ma solution était:

  1. J'ai pris une blockinglinkedqueue avec une taille de *2 du trafic de l'application/min.

  2. Tous les threads mettent l'objet dans la file d'attente et finissent le travail.

  3. Séparez l'objet tête de fil Log-Writer de la file d'attente et écrivez-le dans un fichier log4j à l'aide d'un appender séparé. Cet appender n'a pas été utilisé pour les journaux système.

Cela garantit que les journaux sont écrits en série et sont toujours en ordre.

Cela n'affectera pas les performances de l'application car l'écriture du journal est un processus complètement distinct et ne créera pas de goulot d'étranglement.

Vous pouvez également utiliser aysncappender of log4j.

2
JDev

Si vous le deviez, vous pourriez lancer votre propre .. en utilisant un seul écrivain/lecteur unique FIFO ou des files d'attente.

1
JustJeff

Ceci est une vieille question mais voici ma solution en utilisant Log4J par programme. 

Classe LogFactory

import org.Apache.log4j.Logger;
import org.Apache.log4j.PropertyConfigurator;

import Java.util.Properties;

public class LogFactory {

    private final static ThreadLocal<Logger> logFactory = new ThreadLocal<>();

    public static void createNewLogger(String className) {

        Logger log = Logger.getLogger("Thread" + className);

        Properties props = new Properties();
        props.setProperty("log4j.appender.file", "org.Apache.log4j.RollingFileAppender");

        props.setProperty("log4j.appender.file.maxFileSize", "100MB");
        props.setProperty("log4j.appender.file.Append", "false");
        props.setProperty("log4j.", "100MB");
        props.setProperty("log4j.appender.file.maxBackupIndex", "100");
        props.setProperty("log4j.appender.file.File", "logs/" + className + ".log");
        props.setProperty("log4j.appender.file.threshold", "info");
        props.setProperty("log4j.appender.file.layout", "org.Apache.log4j.PatternLayout");
        props.setProperty("log4j.appender.file.layout.ConversionPattern", "%d{yyyy-MM-dd HH-mm-ss} | %-5p | %C{1}:%L | %m%n");
        props.setProperty("log4j.appender.stdout", "org.Apache.log4j.ConsoleAppender");
        props.setProperty("log4j.appender.stdout.Target", "System.out");
        props.setProperty("log4j.logger." + "Thread" + className, "INFO, file");
        PropertyConfigurator.configure(props);
        logFactory.set(log);
    }

    public static Logger getLogger() {
        return logFactory.get();
    }

}

Ensuite, pour initialiser le logger, utilisez l’approche suivante

logFactory.createNewLogger(String.valueOf(Thread.currentThread().getId()));
logFactory.getLogger().info(" TEST . Thread id is: " + id);
0
JBAutomation

Développer ceci vous-même d'une manière thread-safe n'est pas trivial, vous devriez donc utiliser un framework de journalisation existant qui est thread-safe. Le plus couramment utilisé est Log4J , qui est thread-safe (voir le FAQ ).

0
Christian Berg