Exploration du code source du framework de log de façade SLF4J | Équipe technique JD Cloud

1 Présentation de SLF4J

SLF4J est Simple Logging Facade for Java, qui fournit une apparence ou une abstraction simple de tous les frameworks de journalisation en Java. Ainsi, il permet aux utilisateurs de travailler avec n'importe quel framework de journalisation tel que : Log4j, Logback et JUL (java.util.logging) avec une seule dépendance. La structure de journalisation requise peut être insérée au moment du déploiement en insérant les fichiers jar appropriés (liaisons) dans le chemin de classe. Si vous souhaitez modifier la structure de journalisation, remplacez simplement les liaisons slf4j dépendantes. Par exemple, pour remplacer java.util.logging par log4j, remplacez simplement slf4j-jdk14-1.7.28.jar par slf4j-log4j12-1.7.28.jar.

2 Analyse du code source SLF4J

Nous commençons par le code, ajoutons du code couche par couche, ressentons intuitivement le journal d'impression SLF4J et retraçons le code jusqu'à la source. Comprendre principalement comment SLF4J est découplé en tant que façade et autres frameworks de journalisation.

2.1 Le pom se réfère uniquement à la dépendance sur slf4j-api, la version est 1.7.30

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>

2.1.1 Exécuter une démo

public class HelloSlf4j {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(HelloSlf4j.class);
        logger.info("Hello World info");
    }
}

2.1.2 Informations d'invite de journal

La liaison org.slf4j.impl.StaticLoggerBinder a échoué. SLF4J utilisera par défaut une implémentation sans opération si aucune liaison n'est trouvée sur le chemin de classe

2.1.3 Suivi du code source

Cliquez sur la méthode getLogger(), et vous pouvez intuitivement voir que LoggerFactory utilise une fabrique statique pour créer un Logger. À travers les méthodes suivantes, cliquez étape par étape, et il est facile de trouver le rapport d'erreur. Vous pouvez voir les informations imprimées du journal des exceptions dans la méthode bind().

org.slf4j.LoggerFactory#getLogger(java.lang.Class<?>)
org.slf4j.LoggerFactory#getLogger(java.lang.String)
org.slf4j.LoggerFactory#getILoggerFactory
org.slf4j.LoggerFactory#performInitialization
org.slf4j.LoggerFactory #lier

private final static void bind() {
        try {
            Set<URL> staticLoggerBinderPathSet = null;
            // skip check under android, see also
            // http://jira.qos.ch/browse/SLF4J-328
            if (!isAndroid()) {
                staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
                reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            }
            // the next line does the binding
            StaticLoggerBinder.getSingleton();
            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
            reportActualBinding(staticLoggerBinderPathSet);
        } catch (NoClassDefFoundError ncde) {
            String msg = ncde.getMessage();
            if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
                INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
                Util.report("Failed to load class "org.slf4j.impl.StaticLoggerBinder".");
                Util.report("Defaulting to no-operation (NOP) logger implementation");
                Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
            } else {
                failedBinding(ncde);
                throw ncde;
            }
        } catch (java.lang.NoSuchMethodError nsme) {
            String msg = nsme.getMessage();
            if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
                INITIALIZATION_STATE = FAILED_INITIALIZATION;
                Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
                Util.report("Your binding is version 1.5.5 or earlier.");
                Util.report("Upgrade your binding to version 1.6.x.");
            }
            throw nsme;
        } catch (Exception e) {
            failedBinding(e);
            throw new IllegalStateException("Unexpected initialization failure", e);
        } finally {
            postBindCleanUp();
        }
    }

Une analyse plus approfondie de la méthode de liaison findPossibleStaticLoggerBinderPathSet(), nous pouvons constater que toutes les ressources "org/slf4j/impl/StaticLoggerBinder.class" de ce chemin ont été interrogées sous le ClassPath actuel. Il se peut qu'aucun fichier ne soit chargé ici, ou plusieurs des liaisons peuvent être possibles. Il existe des rappels amicaux pour les scènes sans liaison et les liaisons multiples. Le but du chargement des ressources via le chemin ici est principalement de provoquer divers scénarios anormaux de chargement.

Le code sous StaticLoggerBinder.getSingleton() est la liaison réelle et obtient l'instance de StaticLoggerBinder. Si vous décompilez ici, vous constaterez qu'il n'y a pas du tout une telle classe StaticLoggerBinder.

S'il n'est pas chargé dans le fichier, à la suite de l'exécution de la démonstration ci-dessus, une exception NoSuchMethodError sera déclenchée et un message d'invite indiquant qu'il n'y a pas de scène liée sera imprimé.

Le code source de la méthode findPossibleStaticLoggerBinderPathSet() est le suivant : on peut constater que le chargeur de classe obtient des ressources URL via le chemin.

            ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
            Enumeration<URL> paths;
            if (loggerFactoryClassLoader == null) {
                paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
            } else {
                paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
            }

2.2 la référence pom dépend de logback-classic

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

2.2.1 Exécuter la démo

Vous pouvez voir les informations normales du journal d'impression et il n'y a aucune anomalie

2.2.2 Suivi du code source

À ce stade, si vous cliquez pour entrer la méthode StaticLoggerBinder.getSingleton(), vous constaterez que la classe StaticLoggerBinder est fournie par le package logback-classic et implémente l'interface LoggerFactoryBinder dans SLF4J. La création de StaticLoggerBinder utilise un modèle singleton, et le but principal de cette classe est de retourner une usine qui crée un Logger. Ici, une instance de ch.qos.logback.classic.LoggerContext est réellement renvoyée, puis ch.qos.logback.classic.Logger est créé par cette instance.

Le diagramme de classes UML est le suivant :

2.3 Pom réintroduit log4j-slf4j-impl

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.9.1</version>
        </dependency>

2.3.1 Exécuter la démo

Le journal imprimé est le suivant, ce qui indique que deux classes StaticLoggerBinder sont liées, mais à la fin, ch.qos.logback.classic.util.ContextSelectorStaticBinder est en fait lié. Il est également vérifié ici qu'une fois qu'une classe est chargée, les classes avec le même nom qualifié global ne peuvent pas être chargées. L'ordre dans lequel les packages Jar sont chargés ici détermine directement l'ordre dans lequel les classes sont chargées.

SLF4J: Found binding in [jar:file:/D:/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.9.1/log4j-slf4j-impl-2.9.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
18:19:43.521 [main] INFO com.cj.HelloSlf4j - Hello World info

2.4 Transformation de la position d'importation de log4j-slf4j-impl et logback-classic

Si le fichier Pom importe d'abord log4j-slf4j-impl, puis importe logback-classic

2.4.1 Exécuter la démo

Selon les résultats de l'impression du journal, vous pouvez voir que la liaison réelle est org.apache.logging.slf4j.Log4jLoggerFactory ; mais le journal n'est pas imprimé normalement et la configuration du journal de log4j2 est requise. Il montre que la liaison réelle est le fichier org/slf4j/impl/StaticLoggerBinder.class dans le package og4j-slf4j-impl ; ici, il est également vérifié que si plusieurs packages de pontage sont introduits, la liaison réelle est le fichier chargé en premier ;

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.9.1/log4j-slf4j-impl-2.9.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. Set system property 'log4j2.debug' to show Log4j2 internal initialization logging.

2.5 Changements dans les méthodes de chargement des classes

2.5.1 Compétences d'empaquetage de la version slf4j-api-1.7.30

Regardez slf4j-api-1.7.30-sources.jar pour la décompilation, et trouvez qu'il n'y a pas du tout une telle classe org.slf4j.impl.StaticLoggerBinder Comment peut-il compiler avec succès ? Devinez si cette classe a été exclue lors de l'emballage ? Téléchargez le code source via git et constatez que le code source slf4j contient en fait ce fichier, org/slf4j/impl/StaticLoggerBinder.class ; une petite astuce est utilisée ici pour exclure la classe d'implémentation lors de l'empaquetage. Bien que ce ne soit pas élégant, l'idée est très intelligent.

La version 2.5.2 de slf4j-api-2.0.0 introduit SPI (Service Provider Interface)

Cette version utilise la méthode SPI pour charger la classe d'implémentation, ce qui semble beaucoup plus élégant que la méthode d'implémentation précédente. Le package de pont doit uniquement se trouver à cet emplacement : META-INF/services/, définissez un fichier org.slf4j.spi.SLF4JServiceProvider (nommé le nom de l'interface fourni par SLFJ4) et spécifiez la classe d'implémentation dans le fichier. Tant que ce package de pontage est introduit, il peut être adapté au cadre de journalisation de l'implémentation correspondante.

Voici le code source chargé par SPI

private static List<SLF4JServiceProvider> findServiceProviders() {
        ServiceLoader<SLF4JServiceProvider> serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class);
        List<SLF4JServiceProvider> providerList = new ArrayList();
        Iterator var2 = serviceLoader.iterator();

        while(var2.hasNext()) {
            SLF4JServiceProvider provider = (SLF4JServiceProvider)var2.next();
            providerList.add(provider);
        }

        return providerList;
     }

2.5.3 Comparaison des méthodes de chargement de classe

2.6 SLF4J a officiellement implémenté le framework de journal de liaison

slf4j a fourni des packages de transition pour les frameworks de journalisation couramment utilisés, ainsi que des descriptions détaillées de la documentation, qui sont très faciles à utiliser.
La figure suivante est fournie sur le site Web officiel de SLF4J, montrant la relation entre divers frameworks d'implémentation de journaux et SLF4J :

2.7 Résumé

  • L'API SLF4J est conçue pour se lier à un et un seul framework de journalisation sous-jacent à la fois. De plus, après l'introduction de SLF4J, qu'il puisse être chargé dans StaticLoggerBinder ou chargé dans plusieurs StaticLoggerBinder, des invites conviviales seront données et l'expérience utilisateur est considérée comme très réfléchie. S'il y a plusieurs liaisons sur le chemin de classe, SLF4J émettra une liste d'avertissement où se trouvent ces liaisons. Lorsque plusieurs liaisons sont disponibles sur le chemin de classe, vous devez choisir celle que vous souhaitez utiliser et supprimer les autres.
  • En regardant simplement le code source de SLF4J, en fait, la conception et la mise en œuvre globales sont très simples et claires, et le positionnement est très clair, juste pour faire bonne figure.
  • Compte tenu de la simplicité de l'interface SLF4J et de son modèle de déploiement, les développeurs de nouveaux frameworks de journalisation devraient trouver facile d'écrire des liaisons SLF4J.
  • Pour les cadres de journalisation courants actuels, ils sont compatibles et pris en charge par la mise en œuvre de l'adaptation. Tant que l'utilisateur choisit SLF4J, la liberté de modifier le cadre de journalisation à l'avenir est garantie.

3 Utilisation des modèles de conception SLF4J

Certains modèles de conception classiques sont utilisés dans slf4j, tels que le modèle de façade, le modèle singleton, le modèle d'usine statique, etc. Analysons les modèles de conception suivants.

3.1 Motif de façade

1) expliquer

Le motif de façade, également appelé motif d'apparence, exige que la communication entre l'extérieur d'un sous-système et son intérieur soit effectuée à travers un objet unifié. Le modèle Facade fournit une interface de haut niveau qui facilite l'utilisation du sous-système. Le motif de façade est utilisé pour faciliter les appels des clients.

Slf4j a formulé la norme d'utilisation du journal et fourni une interface de haut niveau. Notre processus de codage ne doit s'appuyer que sur l'interface Logger et la classe d'usine LoggerFactory pour réaliser l'impression du journal. Nous n'avons pas besoin de nous soucier de l'implémentation interne. détails du journal L'implémentation de log4j.

2) Diagramme

        Logger logger = LoggerFactory.getLogger(HelloSlf4j.class);
        logger.info("Hello World info");

3) Avantages

Le découplage réduit l'interdépendance des systèmes. Toutes les dépendances dépendent des objets de façade et n'ont rien à voir avec les sous-systèmes. Le développement de la couche métier n'a pas besoin de se soucier de la mise en œuvre et des détails de l'infrastructure de journalisation sous-jacente, et il n'est pas nécessaire de prendre en compte le coût de remplacement de l'infrastructure. à l'avenir lors du codage.
L'interface et l'implémentation sont séparées, protégeant les détails d'implémentation sous-jacents et la programmation orientée interface.

3.2 Modèle de singleton

1) expliquer

Le modèle singleton garantit qu'une classe n'a qu'une seule instance et lui fournit un point d'accès global.
Dans le package d'adaptation SLF4J, la classe StaticLoggerBinder doit être implémentée, et l'implémentation de la classe StaticLoggerBinder utilise le mode singleton, et c'est la méthode d'implémentation la plus simple.Dans l'initialiseur statique, directement new StaticLoggerBinder() fournit une méthode d'accès globale à obtenir l'instance.

2) Diagramme UML

3) Avantages

Dans le modèle singleton, il n'y a qu'une seule instance du singleton actif et toutes les instanciations de la classe singleton obtiennent la même instance. Cela empêche d'autres objets de s'instancier eux-mêmes et garantit que tous les objets accèdent à une instance. Le
mode singleton a une certaine évolutivité. La classe elle-même contrôle le processus d'instanciation et la classe a une évolutivité correspondante lors de la modification du processus d'instanciation.

Fournit un accès contrôlé à des instances uniques.

Il n'y a qu'une seule instance en mémoire, ce qui réduit la charge mémoire et améliore les performances du système.

4 Révélation

  • Bien que le code global de SLF4J soit court mais très raffiné, on peut voir que le mode façade est bien utilisé. Le mode façade nous fournit également une référence sur la manière de définir uniformément les interfaces et d'assurer la compatibilité pour les implémentations multi-versions.
  • La définition et la mise en œuvre de SLF4J sont très conviviales pour les utilisateurs, et en même temps, divers packages de transition sont fournis pour guider l'utilisation de documents complets. Dans l'ensemble, l'expérience utilisateur est excellente, ce qui peut être l'une des raisons les plus populaires de SLF4J.
  • Nous devons réfléchir davantage à la programmation orientée interface, réduire le couplage de code et améliorer l'évolutivité du code.
  • Utilisez la méthode SPI pour charger avec élégance l'implémentation de l'extension.
  • Un bon produit est conçu, optimisé et itéré.

5 références

Auteur : JD Logistique Cao Jun

Source : Communauté de développeurs JD Cloud

{{o.name}}
{{m.name}}

Je suppose que tu aimes

Origine my.oschina.net/u/4090830/blog/10084131
conseillé
Classement