Documentation:Outils Log Tech

From Gameforge Official Website

Jump to: navigation, search

Contents

Introduction

Pourquoi ne pas avoir utilisé un logger deja existant ?

Cas de log4cxx

  1. Très voir trop buggé
  2. Support les applications multi-threadées mais avec les bibliothèques d'apache (ce qui nous aurait ajouté une nouvelle dépendance).

Cas de Log4cpp

  1. Quasiment mort
  2. Support les applications multi-threadées mais avec les bibliothèques d'omniworks (ce qui nous aurait ajouté une nouvelle dépendance).

Prérequis

  1. Configuration :
    • Elle doit pouvoir se faire au moyen d'un fichier xml ou part une classe contennat l'ensemble des propriété.
    • Si la configuration est basée sur un fichier et que si fichier est modifié alors la conf est actualisée
  2. Général :
    • Thread safe
    • Modulaire (cad qu'on puisse avoir des logs de niveau debug sur certaines sections pour ne pas avoir de log sur d'autrre section)
    • 5 niveaux de log (debug, info, error, fatal, warning)
    • Filtrage de log

Dépendance

  • Xerces-c pour le parsing du fichier xml de configuration.
  • ACE pour la partie martie thread-safe (a base de mutex...)

L'organisation

Des fichiers

Les fichiers :

- source  : commun/src/log
- headers : commun/include/gameforge/commun/log

Des namespaces

L'ensemble du code sera situé dans le namespace :

gameforge --> commun --> log

Donc en c++ :

namespace gameforge
{
  namespace commun
  {
    namespace log
    {
    }
  }
}

Les Principales classes et interfaces

Logger

Logger est un singleton [1].

namespace gameforge
{
  namespace commun
  {
    namespace log
    {
      /**
       */
      class Logger;
      typedef LoggerPtr boost::shared_ptr<Logger>;
 
      class Logger
      {
        public:
	  /**
	   */
	  enum TraceLevel
	  {
	    DEBUG,
	    INFO,
	    WARN,
	    ERROR,
	    FATAL
	  };
	
	  /**
           * Creation & retrieval methods:
           */
	  static LoggerPtr getRootLogger( void );
	  static LoggerPtr getLogger(const std::string& name);
	
	  /**
	   *
	   */
	  static void destroy( void );
	 
	  /**
	   * Printing method:
	   */
          void debug(const std::string& message, const char* file = 0, int line = -1);
	  void info(const std::string& message, const char* file = 0, int line = -1);
	  void warn(const std::string& message, const char* file = 0, int line = -1);
	  void error(const std::string& message, const char* file = 0, int line = -1);
	  void fatal(const std::string& message, const char* file = 0, int line = -1);
	 
	  /**
	   * Check if log type is enable
	   */
	  bool hasDebugLog(void) const;
	  bool hasInfoLog(void) const;
	  bool hasWarnLog(void) const;
	  bool hasErrorLog(void) const;
	  bool hasFatalLog(void) const;
	
	  /**
	   *
	   */
	  void updateConfiguration ( void );
		
	protected:
	   /**
	    */
	   LoggerPtr	getNode(LoggerPtr node, const std::string &name);
	
	  /**
	   */
	  void print(TraceLevel level, const std::string& message, const char* file , int line );
		
	private:
	  /**
	   * Default constrcutor (to builda a racine node)
	   */
	  Logger(void);
	
	  /**
	   * Constructor to build Node with name
	   */
	  Logger(const std::string &name);
	
	  /**
	   */
	  std::list<std::pair<TraceLevel, Output::OutputPtr> >  mOutputs;
	  ACE_Thread_Mutex                                      mOutputsMutex;
	  bool                                                  mHaveOutput;
	
	  /**
	   */
	  std::map<std::string, LoggerPtr>      mNodes;
	  ACE_Thread_Mutex                      mNodesMutex;
	
	  /**
	   */
	  std::string           mName;
	
	  /**
	   */
	  static LoggerPtr      mRoot;
      };
    }
  }
}

Configurator

Configurator est aussi un singleton.

namespace gameforge
{
  namespace commun
  {
    namespace log
    {
      /**
       */
      class Configurator;
      typedef ConfiguratorPtr boost::shared_ptr<Configurator>;
      
      class Configurator
      {
        public:
          /**
           */
          static ConfiguratorPtr getInstance ( void );
        
          /**
           */
          static void destroy( void );
        
          /**
           */
          void fromFile(const std::string& file, unsigned int delay = 0);
        
          /**
           */
          void clear( void );
        
          /**
           */
          void addOutput(const std::string &name, OutputPtr output);
        
          /**
           */
          void addFilter(const std::string &loggerName, Logger::TraceLevel level, 
                          const std::string &nameOutput);
          
          /**
           */
          void setStatusLogger(const std::string &loggerName, bool status);
          
          /**
           */
          struct LoggerConfiguration
          {
            bool enable;
            std::list<std::pair<Logger::TraceLevel, Output::OutputPtr> > output;
          };
          
          typedef LoggerConfigurationPtr	boost::shared_ptr<LoggerConfiguration>;
          
          /**
           */
          const LoggerConfigurationPtr getConfiguration(const std::string & name) const;
          
        private:
          /**
           */
          ACE_Thread_Mutex                              mMutexCondition;
          ACE_Condition<ACE_Thread_Mutex>               mCondition;
        
          /**
           */
          unsigned int                                  mDelay;
          std::string                                   mFile;
        
          /**
           */
          static ConfiguratorPtr                        mInstance;
        
          /**
           */
          std::map<std::string, OutputPtr>              mOutputs;
        
          /**
           */
          std::map<std::string, LoggerConfigurationPtr>	mConfiguration;
      };
    }
  }
}

Output

L'interface

namespace gameforge
{
  namespace commun
  {
    namespace log
    {
      class Output
      {
        public:
          /**
           */
          virtual ~Output( void )
          {
          }
          
          /**
           */
          virtual void setProperties(const std::string& properties, const std::string &value) = 0;
          
          /**
           */
          virtual void print(Logger::TraceLevel level, const std::string& ns, 
                         const std::string& message, const char* file , int line ) = 0;
      };
      
      /**
       */
      typedef OutputPtr boost::shared_ptr<Output>;
    }
  }
}

La Factory

namespace gameforge
{
  namespace commun
  {
    namespace log
    {
      /**
       */
      class OutputFactory
      {
        public:
          /**
           */
          static OutputPtr create(const std::string& type);
        
        private:
          /**
           */
          OutputFactory(void)
          {
          }
 
          /**
           */
          ~OutputFactory(void)
          {
          }
      };
    }
  }
}

Les implémentations de l'interface

namespace gameforge
{
  namespace commun
  {
    namespace log
    {
      class StreamOutput
      {
        public:
          /**
           */
          StreamOutput(std::ostream &stream);
        
          /**
           */
          virtual ~StreamOutput(void);
        
          /**
           */
          virtual void setProperties(const std::string& properties, const std::string &value);
          
          /**
           */
          virtual void print(Logger::TraceLevel level, const std::string& name, 
                         const std::string& message, const char* file , int line );
      };
      
      /**
       */
      class FileOutput
      {
        public:
          /**
           */
          FileOutput(void);
        
          /**
           */
          virtual ~FileOutput(void);
        
          /**
           */
          virtual void setProperties(const std::string& properties, const std::string &value);
          
          /**
           */
          virtual void print(Logger::TraceLevel level, const std::string& name, 
                         const std::string& message, const char* file , int line );
      };
    }
  }
}

Fichier de configuration

La configuration se fera au moyen d'un fichier xml. La syntaxe de ce fichier sera vérifié par un fichier XSD.

Fichier XSD

To do...

Exemples de fichier de configuration

Exemple n°1

<?xml version="1.0"?>
<log>
 
  <!--
    Definition des flux de sortie
  -->
  <output id="f" type="file">
    <propertie name="Blablabla-%yyyy%mm%dd-%hh%mm%ss-%unique"/>
    <propertie format="%hh%mm%ss%mmmm-%t"/>
    <propertie maxsize="200k" />
  </output>
 
  <output id="c" type="stdout">
    <propertie format="%hh%mm%ss%mmmm-%t"/>
  </output>
 
  <output id="e" type="stderr">
    <propertie format="%hh%mm%ss%mmmm-%t"/>
  </output>
 
  <!--
    Definition des règle de filtrage
  -->
  <node name=".">
    <filter level="debug" output="f" />
    <filter level="error" output="e" />
  </node>
 
  <node name="gameforge.client">
    <filter level="debug" output="c" />
  </node>
 
  <node name="gameforge.server">
    <filter level="error" output="f" />
  </node>
 
  <node name="gameforge.commun.msg">
    <filter status="false" />
  </node>
 
</log>

Dans cette exemple il y a deux sections :

  1. Celle ou on définit les flux de sortie
  2. Les règle de filtrage
Les flux de sortie
<output id="f" type="file">
  <propertie name="Blablabla-%yyyy%mm%dd-%hh%mm%ss-%unique"/>
  <propertie format="%hh%mm%ss%mmmm-%t [%node - %type] %message"/>
  <propertie maxsize="200k" />
</output>

On définit un flux de sortie qu'on nomme f de vers un fichier.

On lui associe aussi un certain nombre de propriétés :

  1. le nom du fichier qui sera la concaténation de :
    1. d'une chaine statique Blablabla-
    2. de %yyyy%mm%dd- avec :
      •  %yyyy l'année sous forme de quatre digit (1997, 2006...)
      •  %mm le mois sous forme de deux digites (01, 07, 11...)
      •  %dd la journée sous dorme de deux digite (01, 09, 31 ...)
    3. de %hh%mm%ss- avec :
      •  %hh l'heure sous forme de deux digites.
      •  %mm le mois sous forme de deux digites.
      •  %ss le nombre de seconde sous forme de deux digites.
    4. de %unique par défaut vaut 1 mais si par exemple le fichier Blablabla-20060430-140304-1 existe deja il prendra 2 comme valeur et si Blablabla-20060430-140304-2 existe deja il prendra la valeur de 3...
  2. le format de sortie
    1.  %hh l'heure avec deux digites
    2.  %mm les minutes avec deux digites.
    3.  %ss les secondes avec deux digites.
    4.  %mmmmm les mili seconde avec quatres digites.
    5.  %t le numero du thread
    6.  %node le nom du node (".", "gameforge.commun.message", "gameforge.client"...)
    7.  %type le type de message (debug, error, info...)
    8.  %message le message
  3. la taille maximum des fichiers (ici 200 koctets)
<output id="c" type="stdout">
  <propertie format="%hh%mm%ss%mmmm-%t"/>
</output>
 
<output id="e" type="stderr">
  <propertie format="%hh%mm%ss%mmmm-%t"/>
</output>

On définit deux flux de sortie, l'une vers la sortout standard et l'autre vers la sortie d'erreur.

Les règles de filtrage
<node name=".">
  <filter level="debug" output="f" />
  <filter level="error" output="e" />
</node>

C'est la règle de filtrage par défault, toutes les autres vont "dériver" d'elles. Cette règle dit :

  1. que tous messages de niveau debug ou supérieur vont être envoyés vers le flux f.
  2. que tous messages de niveau error ou supérieur vont être envoyés vers le flux e.
<node name="gameforge.client">
  <filter level="debug" output="c" />
</node>

Cet règle de filtrage "dérive" de :

  • "gameforge" (qui n'existe pas)
  • "." qui existe.

donc par défaut elle a deux règles de filtrage si elle n'avait eu aucune règle spécifique. Mais dans ce cas là, elle a une règle que tous les messages de niveau debug ou supérièur vont être envoyés vers le flux c.

<node name="gameforge.commun.msg">
  <filter status="false" />
</node>

Cet règle de filtrage "dérive" de :

  • "gameforge.commun" (qui n'existe pas)
  • "gameforge" (qui n'existe pas)
  • "." qui existe.

donc par défaut elle a deux règles de filtrage si elle n'avait eu aucune règle spécifique. Mais dans ce cas là, il y a une règle que tous les log soient ignorés.

Liens externe

  1. Logger existant
    • Log4cxx [2] de la fondation apache pour le c++.
    • Log4cpp [3] sur le model de log4j.
    • Log4j [4] de la fondation apache pour java.
  2. Dépendance
Personal tools