Statistiques JCMS 8 - Analyse des usages

1. Introduction

L'analyse de l'usage d'un site est un point important pour savoir s'il répond aux attentes de ses utilisateurs. Depuis de nombreuses versions, JCMS propose un outil d’analyse statistique des accès. Cependant cet outil n’était plus en adéquation avec les besoins actuels.

A partir de JCMS 8 un nouveau moteur de statisques viens remplacer le précédent, on passe d'un outil d'analyse des accès à un outil d'analyse des usages.

1.1 De nouvelles métriques

Ce nouvel outil d’analyse des usages produit des métriques sur les différents pans fonctionnels de JCMS :

  1. Consultation : visite, téléchargement, contenu les plus consultés, …

  2. Recherche : taux de recherche, mots les plus recherchés, …

  3. Contribution : évolution du nombre de contenu, de document, types les plus utilisés, …

  4. Utilisateurs : évolution du nombre d’utilisateurs, répartition selon le type (membre, contact, invités, …)

  5. Exploitation : évolution de l’espace disque, des espaces de travail, …

  6. Technique : taux de requête, temps moyen de réponse, navigateurs et système d’exploitation utilisés, …

L’analyse des usages est extensible. A l’instar des modules Espaces Collaboratifs et ESN, des modules peuvent ajouter leur propres métriques :

  1. Espaces collaboratifs : répartition par typologie, politique d’accès, membre, taux de contribution, …

  2. Réseau social (ESN) : évolution de l’activité, répartition par type d’activité, taux de relation, de complétude des profils, de microblogging, …

1.2 Une nouvelle interface

L’interface de l’analyse des usages est accessible depuis l’espace d’administration, depuis l’administration d’un espace de travail et directement en front-office pour les espaces collaboratifs. L’interface permet de choisir la période analysée et, pour l’analyse générale, l’espace à analyser. Les métriques sont organisées en onglet (Consultation, Recherche, Contribution, ...) Les graphiques sont désormais produits en HTML 5 et sont interactifs (choix des séries affichées, mise en avant d’une série, affichage de la valeur d’un point).


Analyse des usage.

Une interface de gestion permet de relancer une analyse sur une période donnée (ceci est particulièrement utile lors de la mise en place de nouvelles métriques).

1.3 Une nouvelle architecture

Le système de calcul a été entièrement repensé pour être plus performant et plus simple à mettre en place. Toutes les requêtes sont historisées quotidiennement dans un fichier JSON.
Lorsque le site est composé de plusieurs réplicas, chaque réplica historise ses propres requêtes. Ces fichiers sont assez proches des anciens fichiers statistiques en XML. Un convertisseur est disponible pour transformer les anciens fichiers XML en JSON.
Tous les jours, à une heure régulière (par défaut 00:30) une consolidation est lancée. Chaque réplica reprend la collecte des requêtes de la veille (dans le cas nominal), calcul et enregistre pour chaque métrique un rapport quotidien dans la base de données. En plus de l’analyse de la collecte certaines métriques sont calculées à partir des clichés instantanés des données (p. ex. répartition des espaces collaboratifs, le taux de complétude de profil ESN).
La base de données contient ainsi la consolidation quotidienne de tous les réplicas. Lorsqu’un utilisateur consulte l’analyse des usages, JCMS Analytics recherche et agrège les rapports quotidiens de la période demandée et produit les rendus graphiques pour chaque métrique.

Lorsque le processus d'analyse se déclenche, il regarde la date du dernier rapport en base et effectue les analyses sur les fichiers JSON manquants. Cela permet si une analyse ne se fait un soir pour quelques raisons, de rattraper l'analyse la nuit suivante. Attention dans le cas de migration de données, la première analyse s'effectuera donc sur tout l'étendue des fichiers JSON existants. 

2. Principes de fonctionnement

2.1 Cinématique générale

La génération des statistiques via l'analyse des usages JCMS fonctionne sur le principe suivant :

  1. Collecte d'information unitaire dans des fichiers journalier

  2. Analyse des ces informations unitaires via des agents (lancés via une alarme journalière ou manuellement). Le résultat de l'analyse, un rapport, est stocké en base.

  3. Affichage des données calculés dans des rapports (soit graphique, soit en tableau) via des JSP.

cinématique stats

Note : ce mécanisme peut être désactivé via la propriété jcms.analytics.disable pour le stockage et l'analyse, et via les ACL pour l'affichage.

2.2 Les composants

Voici les composants associés aux 3 blocs fonctionnels (Collecte, analyse, consultation) : 

  • La collecte

La collecte s'effectue à chaque requête, création/modification/suppression d'objet ou à la demande.
Elle génère une instance de la classe EventData qui sera sauvegardée au format JSON dans un fichier journalier.
Cette classe EventData comporte une structure commune et peut facilement être étendue pour y rajouter ces données personnelles. Une même requête HTTP peut générer plusieurs instances de la classe EventData (ex. une instance pour la requête, plusieurs instances indiquant des mises à jour de données JCMS, une alerte de sécurité, ...)

  • L'analyse

L'analyse des données collectées est effectuée par un gestionnaire à partir d'une alarme, ou d'un déclenchement manuel. 
Ce gestionnaire lit les fichiers JSON des jours correspondant à la période demandée. Il contacte les différents agents d'analyse pour effectuer l'analyse de leur contenu. Chaque agent a pour mission de construire un rapport (dérivant de la classe AbstractReportMetric), qui sera alors sauvegardé en base (JcmsDB) par le gestionnaire. 

Snapshots ou "instantanés"
En plus des données collectées, les agents d'analyse peuvent ajouter dans les rapports des données de type "instantanée". Ces données sont des états du système au moment de l'analyse (ex. valeur de l'espace disque disponible).Ces données ne peuvent pas être recalculées à partir des fichiers journaliers.

  • La consultation

Lors de la consultation des rapports d'analyse, la base de données est requêtée pour obtenir les rapports voulus. La page du rapport (fournies avec les agents) agrège alors les différentes métriques et les présente à l'utilisateur.

3. Ajout de nouvelles métriques

3.1 Qu'est ce que l'on peut faire ?

Le mécanisme de calcul des statistiques a été conçu pour pouvoir personnaliser les rapports proposés. Il est ainsi possible d'ajouter des données de base dans les fichiers JSON, de générer de nouveaux rapports d'analyses, de changer l'affichage des rapports existants.

3.2 Nouveau rapport d'analyse

Pour ajouter un nouveau rapport d'analyse, il faut créer 2 classes : un agent d'analyse et le rapport correspondant.

3.2.1 Le rapport

Le rapport est une classe dérivant de la classe AbstractReportMetric. Elle sert au stockage des résultats de l'analyse.
Le rapport est sauvegardé en base par la mécanique des statistiques. La classe doit donc être fournie avec le HBM correspondant.
Enfin c'est cette classe qui comporte le code permettant de fusionner les rapports journaliers des différents noeuds (si le rapport le nécessite).

Exemple de rapport :

public class MyNewReportMetric extends AbstractReportMetric {

    long mbrCount;
    int mbrCreateCount;
     ...
    
    @Override
    public void aggregateUridData(AbstractReportMetric metric) {
      if(!(metric instanceof MyPluginReportMetric)){
        return;
      }
      MyPluginReportMetric adminMetric = (MyPluginReportMetric) metric;
      this.mbrCount = adminMetric.getMbrCount();
      this.mbrCreateCount = adminMetric.getMbrCreateCount();
    }
  }

HBM associé

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "classpath://org/hibernate/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.jalios.jcmsplugin.myPlugin.analytics.MyNewReportMetric" table="P_MYPLUGIN_RPT">
...
<property name="mbrCount" /> <property name="mbrCreateCount" /> </class> </hibernate-mapping>

3.2.2 L'agent d'analyse

L'agent d'analyse est une classe dérivant de la classe AbstractStatisticAgent.
Cette classe effectue la consolidation des données présentes dans le fichier JSON et générera une instance du rapport en fin d'analyse.
C'est dans cette classe que le développeur devra implémenter les règles de comptage ainsi que la récupération des instantanées nécessaires.

Voici un exemple d'agent d'analyse (en relation avec le rapport précédent) :

package com.jalios.jcmsplugin.myplugin.analytics;
public class MyPluginAgent extends AbstractStatisticAgent {

  long mbrCount;
  int mbrCreateCount;

  @Override
  public void doProcess(EventData data, StatisticContext context) {
    if (data.isInstanceOf(Member.class)){
      if(data.isCreate()){
        mbrCreateCount++;
      }
    }
  }

  @Override
  public boolean endAnalyze(AbstractReportMetric abstractReportMetric, StatisticContext context) {
    final Workspace analyzedWS = getWorkspace();
    if(context.isScheduled()){
      // take a snapshot of some indicators only if the analysis is scheduled, not a manual one
      Channel channel = Channel.getChannel();

      Set<Member> mbrSet = channel.getDataSet(Member.class);
      if(analyzedWS != null){
        mbrSet = JcmsUtil.select(mbrSet, Member.getBelongsToWorkspaceSelector(analyzedWS), null);
      }

      // count member
      if(Util.notEmpty(mbrSet)){
        mbrCount = mbrSet.size();
      }
    }
    MyPluginReportMetric reportMetric = (MyPluginReportMetric) abstractReportMetric;

    reportMetric.setMbrCount(this.mbrCount);
    reportMetric.setMbrCreateCount(this.mbrCreateCount);
    return true;
  }
}

Note : un agent a absolument besoin d'avoir 2 constructeurs, un sans paramètre et un constructeur par copie.

Pour que le nouvel agent soit exécuté, il faut l'enregister via les propriétés :

jcms.analytics.day-agent-list.<nom de l'agent>.classname : classe à instancier pour éxécuter l'agent
jcms.analytics.day-agent-list.<nom de l'agent>.display-jsp : JSP utilisé pour l'affichage du rapport d'analyse en backoffice
jcms.analytics.day-agent-list.<nom de l'agent>.order : Ordre de l'agent dans l'interface d'affichage des rapports en backoffice

3.3 Ajout de données de base

Afin de pouvoir ajouter ses propres informations dans des fichiers JSON, plusieurs solutions sont disponibles :

  1. Ajouter des informations dans l'événement concernant la requête en cours

  2. Ajouter des informations dans un événement via le ThreadLocal. Cette méthode est à utiliser lorsque que l'on veut enrichir un événement existant mais en dehors d'un traitement de requête (exemple dans une alarme).

  3. Ajouter des événements personnels. Cette méthode est à utiliser lorsque qu'un événement doit être créé. Il est préférable d'utiliser un événement personnel si les données de requêtes sont inutiles, cela accélère le traitement.

3.3.1 Ajouter des informations sur la requête en cours

Pour ajouter des informations personnelles dans l'événement concernant la requête HTTP en cours, il suffit de remplir une Map<String,String> avec les informations désirées.
Ceci doit être effectué avant la fin du traitement de la requête (avant doEmptyFooter.jsp) et les clés ajoutées écraseront les clés existantes (mais seront écrasées par celles présentes dans la Map associée au ThreadLocal).

Exemple :

Map<String, String> extendedContextMap = AnalyticsManager.getInstance().getRequestCustomContextMap(request);
extendedContextMap.put("jcmsplugin.MyPlugin.MyKey", value);
request.setAttribute("com.jalios.jcms.analytics.ctxMap", extendedContextMap);

La valeur peut être un objet complexe serialisé en JSON (via la méthode AnalyticsManager.toJSON(Object)).
Dans ce cas, les objets dérivant de com.jalios.jcms.Data seront sérialisés en indiquant uniquement la classe et l'id, les membres, uniquement l'id. les autres champs de l'objet seront ignorés.
Ces données sont présentes dans le champ context de la classe EventData en plus des données communes déjà présentes.

3.3.2 Ajouter des informations dans un événement via le ThreadLocal

La procédure est en tout point identique au point précédente. Seul l'emplacement de la Map diffère.

Map<String, String> extendedContextMap = AnalyticsManager.getInstance().getThreadLocalCustomContextMap();
extendedContextMap.put("jcmsplugin.MyPlugin.MyKey", value);

Cette Map est stockée dans le ThreadLocal, elle permet donc de pouvoir ajouter des données dans un événement qui ne serait pas associé à une requête HTTP (déclenché à partir d'une alarme).
Il faut cependant faire attention à être dans le Thread qui va générer l'événement.

3.3.3 Ajouter des événements personnels

Il est possible d'ajouter un événement personnel en plus des événements générés par le coeur ou des modules.
Pour cela, il suffit de créer une instance d'EventData et l'enregistrer.

EventData eventData = new EventData("jcmsplugin.MyPlugin",loggedMember,"pluginOperation",currentData);
eventData.getContext().put("jcmsplugin.MyPlugin.MyKey", value);
try {
   AnalyticsManager.appendEventData(eventData, channel.getCurrentServletRequest());
} catch (Exception e) {
  logger.warn(e.getMessage(), e);
}

3.4 Affichage des rapports

La page d'affichage des rapports est composée d'une en-tête fixe, avec des critères de sélection (dates de début et de fin, période, espace de travail à afficher, ...) et d'onglets présentants les rapports.
Les critères d'affichage sont accessibles dans un handler dont la classe est com.jalios.jcms.analytics.ui.ReportHandler. Chaque agent peut être associé à une JSP effectuant l'affichage du rapport généré.
Dans le mécanisme par défaut, l'agent indique dans le contexte d'affichage courant la JSP à afficher.Pour effectuer le rendu, la JSP récupère dans un attribut de requête (nommé "ui.analytics.current-report-handler") l'instance du ReportHandler courant. Puis il effectue la récupération des données en base et leur affichage.
Plusieurs méthodes utilitaires sont disponibles pour faciliter la manipulation des données et le rendu du rapport. Elles sont décrites dans la javadoc de la classe AnalyticsManager.

Exemple d'affichage associé à l'agent précédent :

<div class="chart-result" style="overflow:hidden;"><%
...
ReportHandler formHandler = (ReportHandler) request.getAttribute("ui.analytics.current-report-handler");
Date endDate = formHandler.getAvailableEndDate();
endDate = DateUtil.getDayEndDate(endDate, userLocale);
Date beginDate = formHandler.getAvailableBeginDate();
String wkId = formHandler.getAnalyticsWS();
Workspace wk = channel.getData(Workspace.class, wkId);
Set<AbstractReportMetric> metricSet = AbstractStatisticAgent.getMetricObjectSet(wk,beginDate,endDate, null, MyPluginReportMetric.class);

...
  %>
 <div class="row-fluid">
  <% String suffix = "_mbr_myPlugin_report"; %>
  <div class="span6 report-element mbr-chart">
   <jalios:flotChart name='<%=suffix%>' externalLegend="true" selectableLegend="true" width="500px">
    <jalios:flotChartData label='<%=glp("jcmsplugin.myplugin.MypluginAgent.mbrCreateCount") %>' valuesMap='<%=mbrCreateCountDataMap %>'/>
    <jalios:flotChartData label='<%=glp("jcmsplugin.myplugin.MypluginAgentt.mbrUpdateCount") %>' valuesMap='<%=mbrUpdateCountDataMap %>'/>
    <jalios:flotChartData label='<%=glp("jcmsplugin.myplugin.MypluginAgent.mbrDeleteCount") %>' valuesMap='<%=mbrDeleteCountDataMap %>'/>
   </jalios:flotChart>
  </div>
 </div>
<%}%>
</div>

3.5 Remarques importantes sur les indicateurs

Pour une intégration réussie d'indicateurs dans JCMS il faut prendre en compte certains points :

  • Bien qualifier le besoin fonctionnel pour stocker judicieusement les informations nécessaires dans le fichier JSON. En effet il est facile de rejouer des agents dans le passé mais sur un jeu de données fixes. Si les données attendues ne sont pas présentes, l'analyse ne pourra pas être effectuée.

  • Stocker dans les rapports les résultats d"analyse mais aussi les éléments nécessaires à leur consolidation (en effectuant le plus de pré-calcul)

3.5.1 Le cas des palmarès

Dans le cas d'un palmarès sur une valeur (par exemple, les membres les plus actifs), il faut stocker plus d'information que ce que l'on va réellement afficher.

Explication sur un exemple : Calcul du top 3 des membres les plus actifs

Les données journalières brutes (nombre d'accés) :

 

Jour 1

Jour 2

Membre 1

1

11

Membre 2

10

1

Membre 3

9

7

Membre 4

20

1

Membre 5

15

8

 

L'approche naïve consiste à créer des rapports d'analyse quotidien avec le top 3 de chaque jour, ce qui donnes :

Jour 1 :

  • 1 - Membre 4 (20)

  • 2 - Membre 5 (15)

  • 3 - Membre 2 (10)   

Jour 2 :

  • 1 - Membre 1 (11)

  • 2 - Membre 5 (8)

  • 3 - Membre 3 (7)   

La consolidation naïve sur 2 jours en utilisant les calculs intermédiaires donne alors :

  • 1 - Membre 5 (23)

  • 2 - Membre 4 ( 20 )

  • 3 - Membre 1 (11 )   

Et c'est FAUX car le Membre 3 a fait plus de consultations sur la semaine que le Membre 1.

La consolidation en utilisant les données de base devrait donner :

  • 1 - Membre 5 (15 + 8 --> 23)

  • 2 - Membre 4 ( 20 + 1 --> 21 )

  • 3 - Membre 3 ( 9 + 7 --> 16 )  

Il est donc nécessaire de stocker plus de 3 résultats par rapport pour pouvoir effectuer une consolidation.
Idéalement, il faudrait tout stocker pour être juste mais ceci engendrera une grande volumétrie. Actuellement afin de préparer des palmarès à 10 entrées, l'agent stocke les 100 plus grands résultats par jour.
Le postulat est que, quelques soit la période d’agrégation, le palmarès de 10 se situe quasiment toujours dans les 100 premiers par jours et que les éventuelles erreurs de calculs ne sont pas significatives.

3.5.2 Le cas des moyennes

Dans le cas d'une moyenne, il faut stocker le résultat moyen et le nombre d'éléments utilisés pour calculer cette moyenne.

Explication sur un exemple : Quel est le taux de consultation de chaque publication ?

Les données journalières brutes (nombre de consultation) :

 

Jour 1

Jour 2

Publication 1

1

1

Publication 2

1

19


L'approche naïve consiste à créer 2 rapports d'analyse avec uniquement les taux de consultation :

Jour 1 :

  • Publication 1 : 50%

  • Publication 2 : 50%

Jour 2 :

  • Publication 1 : 5%

  • Publication 2 : 95%

La consolidation naïve des ses 2 rapports sur les 2 jours en utilisant les moyennes journalières donne :

  • Publication 1 : (50% + 5%) / 2 --> 27,5%

  • Publication 2 : 100% - 27,5% --> 72.5 %

Et c'est FAUX.

En effet en regardant les volumes, sur les 2 jours,  Publication 1 a été consulté 2 fois (1+1) et Publication 2, 20 fois (1+19) sur un total de 22 consultations.
Soit en moyenne  pour publication 1 : 9% (2/22) et 91% pour Publication 2 (20/22)

En conséquence pour effectuer la consolidation des taux de consultation, il est nécessaire de connaître le volume total par jour.

3.6. Relance des agents

En phase de développement, pour valider des évolutions sur les agent (nouveaux indicateurs, modification du calcul d'un indicateur, ...), il est pratique de pouvoir relancer les agents sur les données passées.

Ceci peut être fait via l' interface dédiée dans le back-office : Administration > Exploitation > Administration de l'analyse des usages.

Administration de l'analyse des usages

Il est possible dans cet écran de restreindre l'exécution des agents sur une période donnée et sur un espace de travail.

Dans ce cas, les agents sont exécutés en mode manuel, les snapshots des agents standards ne sont donc pas mis à jour.

4. Exemple : ajout d'un rapport sur la consultation horaire

4.1 Besoin fonctionnel 

Le besoin est le suivant : présenter dans un graphique l'utilisation du site en fonction de l'heure afin de présenter un profil type d'une journée.

Le graphique présente une métrique moyennée sur la période d'affichage et indique les valeurs maximum et minimum du nombre de requête par heure.

On ajoute quelques contraintes :

  1. La métrique doit ignorer :
    • les données des utilisateurs anonymes
    • des utilisateurs dont l'extraDbData "jcmsplugin.HourlyReport.isIgnored" vaut true
  2. Par ailleurs une propriété du module permet d'ignorer les données collectées pendant le week-end.

L'affichage de la métrique sera semblable à :
Sélection 006

4.2 Déroulement du développement

Les fichiers décrits dans ce tutoriel sont disponibles dans un module téléchargeable.

4.2.1 Création d'un module HourlyReportPlugin

On commence par créer un module JCMS pour accueillir ce développement. Pour plus d'informations, consulter les documentations suivantes :

JCMS 5.7 : Développement de modules
JCMS - Développement orienté module avec Eclipse et SVN

4.2.2 Ajout de données spécifiques ou non ?

Maintenant que le module vide a été créé, il faut se poser la question suivante  :
A-t-on besoin d'enrichir les données présentes dans le fichier journalier pour calculer cet indicateur ?

Les requêtes HTTP sont déjà présentes dans le fichier avec l'indication de l'auteur de la requête.
Les dates des requête permettent de savoir si elle a été faite pendant un week-end ou non.
Il reste le cas de l'extraDBData. Cette information n'est pas disponible nativement dans le fichier, il va donc falloir l'ajouter.

Remarque : Il aurait été plus judicieux d'utiliser un groupe pour ignorer certains utilisateurs, mais les groupes de l'utilisateur courant sont déjà dans le fichier. Donc pour les besoins de ce tutoriel, cette information est récupérée à partir d'une extraDBData.

 Pour ajouter cette information, il faut mettre à jour la Map de contexte utilisée par le coeur à la création de la ligne de données reliée à la requête. Nous allons le réaliser via une target en stockant l'information avec comme clé "jcmsplugin.HourlyReport.isIgnored".

Pas à pas : 

  • Créer un fichier JSP nommé updateEventData.jsp dans le répertoire jsp du module avec le contenu suivant :

<%@page import="com.jalios.jcms.analytics.AnalyticsManager"%>
<%@page import="com.jalios.jcms.Channel"%>
<%@ page contentType="text/html; charset=UTF-8" %><%
%><%@ include file='/jcore/doInitPage.jsp' %><%
%><%
if(loggedMember == null){
 return;
}
boolean isIgnored = Util.toBoolean( loggedMember.getExtraDBData("jcmsplugin.HourlyReport.isIgnored"), false);
if(isIgnored){
 Map<String, String> extendedContextMap = AnalyticsManager.getInstance().getRequestCustomContextMap(jcmsContext.getRequest());
 extendedContextMap.put("jcmsplugin.HourlyReport.isIgnored",Boolean.TRUE);
 request.setAttribute("com.jalios.jcms.analytics.ctxMap", extendedContextMap);
}
%>
  • Déclarer la jsp updateEventData.jsp dans le plugin.xml en utilisant la target EMPTY_FOOTER

<plugin name="HourlyReportPlugin"...> 
  ...  
 <public-files>
   <directory path="js" />
   <directory path="images" />
   <directory path="css" />
   <directory path="docs" />
   <directory path="jsp" />
   
   <file path="jsp/updateEventData.jsp" include="EMPTY_FOOTER"/>
  </public-files>
</plugin>

 Il faut maintenant générer un peu de trafic sur la plateforme pour que les fichiers journaliers contiennent l'information désirée.

4.2.3 Définition du rapport

Ecrivons maintenant le rapport que nous voulons générer. Pour cela il faut créer une classe dérivant d'AbstractReportMetric : 

public class HourlyReportMetric extends AbstractReportMetric{

...

}

On va y ajouter dedans les métriques que nous voulons sauver.
Créons donc un tableau de 24 éléments.

private long[] values = new long[24];

public long[] getValues() {
 return values;
 }
public void setValues(long[] values) {
 this.values = values;
 }

Pour simplifier la suite du développement, nous allons aussi créer quelques méthodes utilitaires dont les sources ne sont pas détaillées ci-après mais qui sont dans l'archive de ce tutoriel :

// This method call addCounter
public void incrementCounter(Date date) 

// This method computes the hour of the data parameter and it sums the value in the internal array.
public void addCounter(Date date, long value) 

// This method returns the value associated with an hour
public long getHourValue(int hour)

// This method overwrites hour value in internal array
private void setHourValue(int hour, long value)

// This method copies hour value from an HourlyReportMetric 
public void setValues(HourlyReportMetric workMetric)

 Il faut aussi créer le fichier HBM associé (dans le même répertoire que la classe HourlyReportMetric).

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "classpath://org/hibernate/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.jalios.jcmsplugin.hourlyReport.HourlyReportMetric" table="P_HOURLY_RPT">
  <cache usage="read-write" />
  <id name="rowId" column="rowId"><generator class="native" /></id>
<property name="startDate" not-null="true" index="IS_HOURLY_RPT" />
<property name="endDate" not-null="true" index="IE_HOURLY_RPT" />
<property name="reportDate" not-null="true" />
<property name="workspaceId" length="64" index="IW_HOURLY_RPT" />
<property name="urid" length="64" />
<primitive-array name="values" table="P_HOURLY_VAL"> <key column="RPT_ID" />
<index column="HOUR" />
<element column="VALUE" type="long" />
</primitive-array>
</class>
</hibernate-mapping>

Il faut enregistrer la classe et le fichier HBM dans plugin.xml

<java-classes>
<java class="com.jalios.jcmsplugin.hourlyReport.HourlyReportMetric" />
</java-classes>
<hibernate>
<mapping resource="com/jalios/jcmsplugin/hourlyReport/HourlyReportMetric.hbm.xml"/>
</hibernate>

Il faut maintenant implémenter les méthodes héritées de la classe abstraite :

  • public void checkAbsoluteMetrics(List<AbstractReportMetric> previousReportList)

Cette méthode est utilisée pour corriger d'éventuels problèmes liés au fait que les "snapshot" n'existeraient pas pour certains rapports.
Il est alors possible d’utiliser les rapports précédents pour reconstruire un snapshot cohérent si il venait à manquer. Dans notre cas, notre rapport n'a pas de snapshot, il n'est donc pas nécessaire d'implémenter cette méthode.

  • public void initializeDBDataCollections()

Cette méthode est utilisée pour initialiser les éventuelles collections du rapport, dans le but d'éviter les erreurs Hibernate due au chargement différé des collections.
Dans notre cas, le rapport ne contient pas de collections,  il n'est donc pas nécessaire d'implémenter cette méthode.

  • public void aggregateUridData(AbstractReportMetric metric)

Cette méthode est la méthode utilisé pour fusionner les données en provenance de plusieurs noeuds.
En effet chaque noeud JSync effectue son analyse et stocke un rapport dans la base. Dans notre cas, il faut pouvoir fusionner les rapports pour ne présenter qu'un rapport global à l'utilisateur.On écrit donc la fusion en sommant les valeurs du rapport courant avec celui en paramètre pour chaque membre.

 @Override
 public void aggregateUridData(AbstractReportMetric metric) {
   if(!( metric instanceof HourlyReportMetric)){
     return;
   }
   HourlyReportMetric hrMetric = (HourlyReportMetric) metric;
   for(int i = 0; i< 24; i++){
     setHourValue(i, getHourValue(i)+hrMetric.getHourValue(i));
   }
 }

4.2.4 Ecriture de l'agent

Pour continuer, il faut écrire un agent d'analyse permettant de consolider les données présentes dans les fichiers journaliers dans les rapports que nous venons de créer.
Pour cela nous allons créer une classe HourlyAgent qui étend AbstractStatisticAgent.

public class HourlyAgent extends AbstractStatisticAgent{
   ...
}

Maintenant que le squelette de l'agent est prêt, il faut l'implémenter :

  • Créer un constructeur simple et un constructeur par copie, utiliser ce constructeur par copie dans la méthode clone()

public HourlyAgent() {
 super();
 }
public HourlyAgent(HourlyAgent agent) {
 super(agent);
 }
@Override
public AbstractStatisticAgent clone() {
 return new HourlyAgent(this);
}
  • Indiquer la classe du rapport associé à cet agent

@Override
public Class<? extends AbstractReportMetric> getReportMetricClass() {
  return HourlyReportMetric.class;
}

Il faut maintenant implémenter les méthodes startAnalysis, doProcess et endAnalysis.
Pour simplifier le code de l'agent, nous allons utiliser un rapport temporaire pour pouvoir utiliser les méthodes utilitaires présentes dans la classe du rapport.
Nous allons donc créer ce rapport temporaire dans startAnalysis et le détruire dans endAnalysis.

  • Dans startAnalysis, nous allons aussi lire la propriété jcmsplugin.hourlyReport.HourlyAgent.ignoreWeekEnd qui indique si les week-ends sont ignorés ou non, soit :

@Override
public void startAnalyze(StatisticContext context) {
  workMetric = new HourlyReportMetric();
  ignoreWeekEnd= Channel.getChannel().getBooleanProperty("jcmsplugin.hourlyReport.HourlyAgent.ignoreWeekEnd", true);
}
  • Dans endAnalysis, nous allons remplir le rapport reçu avec les données du rapport prélémininaire (voir la méthode setValues dans le rapport) avant de le détruire. La sauvegarde du rapport complet est effectuée par le gestionnaire de l'analyse.

@Override
public boolean endAnalyze(AbstractReportMetric reportMetric, StatisticContext context) {
   HourlyReportMetric hrReportMetric = (HourlyReportMetric) reportMetric;
   hrReportMetric.setValues(workMetric);
   HibernateUtil.evict(workMetric);
   workMetric = null;
   return true; // true if report parameter should be saved, false to be evicted
}
  • Il reste maintenant à écrire le processus d'analyse en implémentant la méthode doProcess. Il faut noter que la mécanique effectue déjà quelques tests basiques (correspondance entre les dates, le workspace et l'URID de l'agent et les données de l'événement).

@Override
public void doProcess(EventData data, StatisticContext context) {

On ignore les données qui ne sont pas des requêtes HTTP (de type access)

if(!data.isAccess()){
 if(logger.isTraceEnabled()){
   logger.trace("data is ignored because type '"+data.getType()+"' is not a compatible type.");
 }
 return;
}

On ignore les requêtes en provenance des robots

if(Util.toBoolean(data.getContext().get(AnalyticsManager.IS_BOT), false)){ 
 if(logger.isTraceEnabled()){
   logger.trace("data is ignored because request is a bot request.");
 }
 return;
}

On trace le contenu de l'événement pour débogage

if(logger.isTraceEnabled()){
logger.trace(this.toString() + " : "+data.toString());
}

On ignore les requêtes anonymes

if(data.getActor() == null){
 return;
}

On ignore l'événement si la clé "jcmsplugin.HourlyReport.isIgnored" est positionné à true

boolean isIgnored =Util.toBoolean(data.getContext().get("jcmsplugin.HourlyReport.isIgnored"), false);
if(isIgnored){
 return;
}

On ignore la ligne si elle concerne un week-end (la méthode isWeekEnd() teste si la date est un samedi ou un dimanche, son code est disponible dans l'archive).

Date startDate = new Date(data.getStartDate()); 
if(ignoreWeekEnd && isWeekEnd(startDate)){
 return;
}
 

Enfin on incrémente le compteur associé à l'heure de l'événement

 workMetric.incrementCounter(startDate);

On déclare maintenant l'agent. Pour cela, il faut ajouter la classe dans plugin.xml et les propriétés suivantes dans plugin.prop.

Plugin.xml :

...
<java-classes>
 <java class="com.jalios.jcmsplugin.hourlyReport.HourlyAgent" />
...

Plugin.prop :

jcms.analytics.day-agent-list.HourlyAgent.classname: com.jalios.jcmsplugin.hourlyReport.HourlyAgent
jcms.analytics.day-agent-list.HourlyAgent.display-jsp: plugins/HourlyReportPlugin/jsp/doDisplayHourlyReport.jsp
jcms.analytics.day-agent-list.HourlyAgent.order:100

Voilà, l'agent d'analyse est maintenant terminé. On relance sur les données précédentes (voir section 5) pour peupler la base de données.

4.2.5 Ecriture de la JSP d'affichage

Comme indiqué dans les propriétés du module, ion crée la page d'affichage plugins/HourlyReportPlugin/jsp/doDisplayHourlyReport.jsp.

Créez le fichier avec le template suivant :

<%@page import="java.util.Map.Entry"%>
<%@page import="com.jalios.jcms.analytics.agent.*"%><%
%><%@page import="com.jalios.jcms.analytics.*"%><%
%><%@page import="com.jalios.jcms.analytics.metric.*"%><%
%><%@page import="com.jalios.jcms.analytics.ui.*"%><%
%><%@page import="com.jalios.jcmsplugin.hourlyReport.*"%><%
%><%@ include file='/jcore/doInitPage.jsp' %><%
%><div class="chart-result" style="overflow:hidden;"><%
ReportHandler formHandler = (ReportHandler) request.getAttribute("ui.analytics.current-report-handler");
Date endDate = formHandler.getAvailableEndDate();
endDate = DateUtil.getDayEndDate(endDate, userLocale);
Date beginDate = formHandler.getAvailableBeginDate();
String wkId = formHandler.getAnalyticsWS();
Workspace wk = channel.getData(Workspace.class, wkId);
Set<AbstractReportMetric> metricSet = AbstractStatisticAgent.getMetricObjectSet(wk,beginDate,endDate, null,AbstractReportMetric.class);
if(Util.isEmpty(metricSet)){
 %><%=glp("ui.analytics.report.no-result")%><%
}
else{%>
<%}%>
</div>

On modifie l'appel à AbstractStatisticAgent.getMetricObjectSet pour fournir la classe correcte du rapport 

Set<AbstractReportMetric> metricSet = AbstractStatisticAgent.getMetricObjectSet(wk,beginDate,endDate, null, HourlyReportMetric.class);

On implémente l'affichage des données.

Il a été demandé de présenter les données sous la forme d'une journée type, de moyenner les nombres de requêtes par heures et d'indiquer les minimums et maximum.
On crée donc 3 Maps (averageDataMap,maxDataMap,minDataMap) avec comme clé une Date représentant l'heure dans la journée et comme valeur un Double avec la valeur à afficher.

Map<Date, Double> averageDataMap = new HashMap<Date, Double>();
Map<Date, Double> maxDataMap = new HashMap<Date, Double>();
Map<Date, Double> minDataMap = new HashMap<Date, Double>();

Dans le cas où des analyses n'ont pas été faites dans la périodes, il faut créer des rapports factices pour éviter les trous dans le grapqhique :

AnalyticsManager.completeMetricObjectSet(metricSet, beginDate, endDate, userLocale, HourlyReportMetric.class);

Enfin dans le cas où plusieurs noeuds existeraient dans l'architecture, il faut fusionner les données ayant des urid différents:

metricSet = AnalyticsManager.aggregateAllUrid(metricSet,HourlyReportMetric.class);

Maintenant le traitement des données, nous allons itérer sur les différents rapports (jour par jour) pour en extraire les données horaires, les moyenner et insérer le résultat dans les maps créée précédemment:

long value = 0;
Double currentAverageValue;
Double currentMaxValue ;
Double currentMinValue;
 
Iterator<AbstractReportMetric> metricIt = metricSet.iterator();
int metricIndex = 1;
while(metricIt.hasNext()){
  HourlyReportMetric metric = (HourlyReportMetric) metricIt.next();
 
  for(int hour= 0;hour < 24 ; hour ++){
    value = metric.getHourValue(hour);
    Date workDate = new Date(3600000 * hour);
 
    //average value
    currentAverageValue = averageDataMap.get(workDate);
    if(currentAverageValue == null){
      currentAverageValue = Double.valueOf(0);
    }
    currentAverageValue = ((currentAverageValue * metricIndex) + value)/(metricIndex +1);
    averageDataMap.put(workDate, currentAverageValue);
 
    //max value
    currentMaxValue = maxDataMap.get(workDate);
    if(currentMaxValue == null){
      currentMaxValue = Double.valueOf(0);
    }
    if(value > currentMaxValue){
     maxDataMap.put(workDate,Double.valueOf(value));
    }
 
    //min value
    currentMinValue = maxDataMap.get(workDate);
    if(currentMinValue == null){
      currentMinValue = Double.MAX_VALUE;
    }
    if(value < currentMinValue){
      minDataMap.put(workDate, Double.valueOf(value));
    } 
 }
 metricIndex ++;
}

Maintenant que les données ont été préparé pour l'affichage, nous allons ajouter le graphique au sein d'un onglet bootstrap dans la JSP :

<div class="row-fluid">
 <% String suffix = "_hourlyReport"; %>
 <div class="span6 report-element hourlyReport-chart">
   <h3><%=glp("jcmsplugin.hourlyReport.section") %></h3>
   <span class="help-block"><%=glp("jcmsplugin.hourlyReport.section.help") %></span>
      <!-- le graphique arrivera ici -->
 </div>
</div>

Pour insérer le graphique, nous utilisons les tags jalios:flotChart et jalios:flotChartData :

<jalios:flotChart name='<%=suffix%>' externalLegend="true" selectableLegend="true" width="500px">
 <jalios:flotChartData label='<%=glp("jcmsplugin.hourlyReport.averageCount") %>' valuesMap='<%=averageDataMap %>'/>
 <jalios:flotChartData label='<%=glp("jcmsplugin.hourlyReport.maxCount") %>' valuesMap='<%=maxDataMap %>'/>
 <jalios:flotChartData label='<%=glp("jcmsplugin.hourlyReport.minCount") %>' valuesMap='<%=minDataMap %>'/>
</jalios:flotChart>

La page d'affichage du rapport est maintenant fonctionnelle mais le rendu n'est pas adapté à ce type de graphique car les valeurs par défaut des tags ne sont pas prévues pour ce type d'indicateur. Nous allons personnaliser le graphique.hourly_graphique_sans_conf

La bibliothèque utilisée pour l'affichage est une bibliothèque javascript et il est possible d'agir sur sa configuration via un autre tag jalios:flotChartOption (à mettre à l'intérieur du tag jalios:flotChart).

<jalios:flotChartOption>
 { 
    xaxis: {
       mode: "time",
       timeformat: "%H",
       tickSize: [1, "hour"],
    },
    bars: {
       show: true,
       barWidth: 60*60*1000,
       fill: true,
       lineWidth: 1,
       order: 1,
    },
    lines:{
       show: false
    },
    points: {
       show : false
    },
    series: {
       shadowSize: 1
    }
 }
</jalios:flotChartOption>

La configuration par défaut du graphique est fusionnée avec cette configuration.

On aboutit ainsi à un graphique représentant notre journée type ressemblant à :

Sélection 006

Pour plus d'informations sur la personnalisation des graphiques, veuillez vous reporter à la documentation de flotChart.

5. Migration des données précédentes (avant JCMS 8.0)

Les données utilisées pour effectuer les calculs sont une évolution des données précédemment stockés dans les statistiques des versions antérieures à JCMS 8.0. En conséquence, il est possible de migrer les données précédentes afin de pouvoir les utiliser dans les nouvelles statistiques.
Toutefois, avec ces jeux  de données, certains indicateurs ne seront pas disponibles sur les périodes migrées.
Pour effectuer la migration, un outil est livré dans JCMS 8.0. Son exécution ne nécessite pas un JCMS en fonctionnement et peut être fait avant ou aprés une mise à jour de JCMS en 8.0.

La commande à éxécuter pour effectuer la migration est  :

  • Sur Windows :

java -cp %JCMS_WEBAPP%\WEB-INF\lib\*;%TOMCAT_HOME%\lib\* com.jalios.jcms.analytics.MigrationTools  <répertoire stats XML> <date de début> <date de fin> <répertoire analytics>

  • Sur linux :

java -cp $JCMS_WEBAPP/WEB-INF/lib/*:$TOMCAT_HOME/lib/* com.jalios.jcms.analytics.MigrationTools  <répertoire stats XML> <date de début> <date de fin> <répertoire analytics>

Ex.  pour convertir les fichiers XML du répertoire stats du 01/01/2013 au 18/01/2013

java -cp $JCMS_WEBAPP/WEB-INF/lib/*:$TOMCAT_HOME/lib/* com.jalios.jcms.analytics.MigrationTools /home/ludovic/dev/tmp/stats 01012013 18012013 /home/ludovic/dev/tmp/stats/analytics/

Remarque : Les anciens fichiers XML n'ont pas besoin d'être conservés sur la prod et peuvent en être retirés.

Une fois la migration des fichiers effectuée, relancez les analyses sur les périodes migrées afin de mettre à jour la base de données (Administration > Exploitation > Administration de l'analyse des usages).

Mises en garde :

  • sur une instance de JCMS donnée, seules les statistiques concernant son propre URID sont prises en compte, si dans le cadre de votre migration vous changez d'URID la procedure d'analyse des périodes antérieures doit être réalisée avec l'URID d'origine.
  • la procédure "relancez les analyses sur les périodes migrées" est gourmande en ressource sur le serveur et peut prendre beaucoup de temps selon la quantité d'informations à traiter

Pour la mise en production des statistiques migrées dans la base de données de prod vous avez plusieurs possibilités :

  • déconseillé : réaliser en pré-prod puis faire un export des données des tables correspondant aux statistiques pour les réimporter en prod
  • conseillé : appliquer la procédure sur un noeud de prod temporaire dédié sur lequel les utilisateurs ne sont pas dirigés, ou basculer les utilisateurs sur un noeud temporaire le temps d'executer la procédure (pensez aux URID qui ne doivent pas être en conflit et dont la correspondance doit être faite sur le serveur qui exécute la procédure)
  • Appliquer la procédure durant une période d'inactivité de la plateforme de production, par exemple via des lots nocturnes.

 

6. Principales classes

6.1 AnalyticsManager

Cette classe est la classe gestionnaire des statistiques. Elle permet entre autre d'écrire les données collectées, d'exécuter les agents et obtenir les rapports pour l'affichage.Cette classe est un singleton et doit être accédée via la méthode AnalyticsManager.getInstance().

Les principales méthodes de cette classe sont :

// append an eventData in daily file
void appendEventData(EventData eventData,HttpServletRequest request)

// read a daily file to extract EventData
List<EventData> readEventFile( File file)

// trigger a manual daily analysis (with default parameter)
void runDayAgent()

// trigger a manual daily analysis on a period
void runDayAgent(Date startDate, Date endDate, Workspace workspace, String urid,boolean scheduled, bool
// trigger analysis with a specific analysis period (available in JCMS 8.0.2)
public void runPeriodAgent(Date startDate, Date endDate,  AbstractPeriod period, Set<Workspace> workspaceSet, String urid, File eventDirectory, Set<AbstractStatisticAgent> agentList, boolean scheduled, boolean globalAnalyze)

// Util method used to serialize data in EventData object
public static String toJSON(Object obj)

D'autres méthodes utilitaires concernant l'affichage des rapports sont aussi disponibles dans cette classe mais elles seront détaillées dans la javadoc.

6.2 EventData

Il s'agit de la classe représentant une donnée de collecte. Elle comporte les éléments suivants :

public class EventData implements Serializable {
    //Event Type (Core, Module, ...)
    private String type;

    // who is doing the action
    private Member actor;
   
    // Action done in this event (create , delete, update, comment, ...)
    private String action;

    // Object : target of the action (publication, group, workspace, ...)
    private String objectId;
    private String objectClass;

    // common data
    private long date;
    private long startDate;
    private long endDate;
    private String urid;
  
    // specific data stored in key/value map 
    // Plugins can add private data
    private Map<String, String> context;
}

Plusieurs possibilités pour ajouter ses propres informations sont disponibles et détaillées dans la section 3.

Le membre type est une chaîne de caractère qui identifie le type d'EventData. Ce champ est librement utilisable par des modules.

De base, JCMS génère des données avec les types suivants :

  • CORE : événement généré par le coeur de JCMS (création,modification, effacement d'élément, erreur CSRF, erreur d'authentification, ...)
  • ACCESS : événement généré à chaque requête (non ajax) sur le site
  • OPENAPI : événement généré à chaque requête OpenAPI
  • AC_SEARCH : événement généré à chaque recherche instantanée

6.3 AbstractStatisticAgent

Le principe d'analyse se base sur des agents de générations de rapport appelés par la mécanique de lecture des données.

Chaque agent est notifié :

  1. au début de l'analyse (méthode startAnalyze)

  2. à chaque élément de données à traiter : (méthode doProcess)

  3. à la fin de l'analyse (méthode endAnalyze)

Un agent est une classe dérivée de la classe com.jalios.jcms.analytics.AbstractStatisticAgent présentées ci-dessous :

package com.jalios.jcms.analytics;

public abstract class AbstractStatisticAgent {
  private Workspace workspace = null;
  private Date reportDay= null;
  private String displayJSP = null;
  
  public AbstractStatisticAgent();
  public AbstractStatisticAgent(AbstractStatisticAgent parent);
  public void init(StatisticContext context);
  public Workspace getWorkspace();
  public void setWorkspace(Workspace workspace);
  public Date getReportDay();
  public void setReportDay(Date reportDay);
  public String getDisplayJSP();
  public void setDisplayJSP(String displayJSP);

  public static Set<AccessReportMetric> getMetricObjectSet(Workspace workspace, Date startDate, Date endDate,String urid, Class<? extends AbstractReportMetric> clazz);
  public static Date getLatestAnalyzeDate();
  public boolean isReportDisplayed(Workspace displayedWS, Date beginDate, Date endDate);
 public abstract AbstractStatisticAgent clone();
  public abstract void startAnalyze(StatisticContext context);
  public abstract void endAnalyze(StatisticContext context);
  public abstract void doProcess(EventData data, StatisticContext context);
  public abstract Class<? extends AbstractReportMetric> getReportMetricClass();
}

Pour ajouter un nouvel agent, il faut ajouter 3 propriétés :

jcms.analytics.day-agent-list.<nom de l'agent>.classname : classe à instancier pour éxécuter l'agent
jcms.analytics.day-agent-list.<nom de l'agent>.display-jsp : JSP utilisé pour l'affichage du rapport d'analyse
jcms.analytics.day-agent-list.<nom de l'agent>.order : Ordre de l'agent dans l'interface d'affichage des rapports en backofficeEx.

jcms.analytics.day-agent-list.MyPluginAgent.classname: com.jalios.jcmsplugin.myplugin.analytics.MyPluginAgent
jcms.analytics.day-agent-list.MyPluginAgent.display-jsp: plugins/MyPlugin/jsp/doDisplaMyPluginReport.jsp
jcms.analytics.day-agent-list.MyPluginAgent.order:100

L'agent est appelé à chaque consolidation et le rapport d'analyse est présenté dans un onglet dans la section d'administration.

Pour la présentation, les agents sont triés par leur numéro d'ordre puis par nom de classe.

Un agent peut aussi effectuer des "snapshots", il s'agit des métriques ne provenant pas de l'analyse des fichiers de statistiques mais étant l'état d'une donnée au moment de l'éxécution de l'agent (Exemples des quotas disques, de la complétudes des profils ESN, ...).

Pour des raisons de consistance, dans les agents fournis en standard, ces  "snapshots" ne sont pas mis à jour si l'agent est démarré manuellement. 

6.4 StatisticContext

 La classe StatisticContext est une classe permettant d'accéder aux propriétés de l'analyse (mode de déclenchement, date de l'analyse, urid, espace de travail, ...).

public class StatisticContext {

  private File currentEventFile = null;
  private Date startDate = null;
  private Date endDate = null;
  private Workspace analyzedWorkspace = null;
  private Set<Workspace> allAnalyzedWorkspaceSet = null;
  private String urid = null;
  private boolean scheduled= false; // scheduled is set to true when the agent is run from an alarm, false if the agent is run from admin page
  private boolean globalAnalyze= false; // true if the analyze is concerning all the workspace or not 
}

6.5 AbstractReportMetric

La classe AbstractReportMetric est la classe abstraite des rapports d'analyse. Elle contient les données communes à tous les rapports et quelques méthodes que le créateur du rapport doit implémenter.

public abstract class AbstractReportMetric implements DBData {
 private Long rowId;
 private Date startDate = null;
 private Date endDate = null;
 private Date reportDate = null;
 private String urid = null;
 private String workspaceId;
 
 abstract public void initializeDBDataCollections();
 
 /**
 * Aggregate a metric to this one (to provide aggregated metrics like global from uri's)
 * @param metric the metric to add
 */
 public abstract void aggregateUridData(AbstractReportMetric metric);
 /**
 * Check "snapshots" indicator in this metric. If the snapshot indicator is missing, value can sometimes be computed from previous metrics.
 * @param previousReportList {@link List} of previous {@link AbstractReportMetric}
 */
 public abstract void checkAbsoluteMetrics(List<AbstractReportMetric> previousReportList);
}

7. Description des métriques coeur

7.1 Consultation

Cet onglet contient les résultats d'analyses concernant la consultation du site.

7.1.1 Visites

Nombre de visites effectuées sur le site, i.e, le nombre de fois que doEmptyFooter.jsp a été appelé (soit à chaque requête sur le portail) plus les téléchargements de fichiers qui ne sont pas des images (via RestrictedFileFilter). Les visites effectuées par des robots sont ignorées.

7.1.2 Téléchargement

Nombre de téléchargement de fichiers (via RestrictedFileFilter). Les images ne sont pas comptabilisées.

7.1.3 Impression

Nombre d'appel à un portail JCMS en mode print (gabarit doPortletPortalPrintDisplay.jsp).

7.1.4 Visiteurs

Nombre de visiteurs (anonymes ou visiteurs authentifiées). Un visiteur anonyme est un visiteur non identifié, discriminé par un couple (adresse IP, session J2EE).

7.1.5 Visiteurs authentifiés

Nombre de membres authentifiés uniques.

7.1.6 Espaces les plus visités

Nombre de visites par espaces de travail (voir la définition de visites au dessus).

7.1.7 Palmarès des membres

Membres ayant fait le plus de visites.

7.1.8 Contenus les plus visités

Contenus ayant été le plus visités (appel à display.jsp)

7.1.9 Palmarès des portails

Portails les plus utilisés (lors de la navigation)

7.2 Recherche

7.2.1 Recherche

Nombre de requête respectant les critères suivants :

  • ne provenant pas d'un robot

  • possède une queryString non vide

et

  • soit l'URI est soit "query.jsp" ou le contenu de jcms.resource.query au moment de l'analyse

  • soit la queryString contient "jsp=front%2Fquery.jsp" ou "jsp=" + le contenu de jcms.resource.query au moment de l'analyse

7.2.2 Recherche instantanée

Nombre de requête de recherche instantanée. Une requête de recherche instantanée respecte les points précédents et est en provenance de la jsp acsearch.jsp

7.2.3 Palmarès des mots clés recherchés

Mots (séparés par des espaces) le plus fréquemment présents dans le champ texte de la requête.

7.3 Contribution

7.3.1 Contenus

Nombre de Publication présents dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot").

7.3.2 Contenus créés

Nombre d'appel à performCreate sur des contenus ou contenus utilisateur.

7.3.3 Contenus mis à jour

Nombre d'appel à performUpdate sur des contenus ou contenus utilisateur.

7.3.4 Contenus supprimés

Nombre d'appel à performDelete sur des contenus ou contenus utilisateur.

7.3.5 Documents

Nombre de FileDocument présents dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot").

7.3.6 Documents créés

Nombre d'appel à performCreate sur des FileDocument.

7.3.7 Documents mis à jour

Nombre d'appel à performUpdate sur des FileDocument.

7.3.8 Document supprimés

Nombre d'appel à performDelete sur des publications.

7.3.9 Créations par espaces

Ce graphique représente le pourcentage de création (Publication, FileDocument et Categories) par espace. Cela indique que 10% de création ont été effectués dans l'espace A, 15% dans l'espace B, ...

7.3.10 Contributions par espaces

Ce graphique représente le pourcentage d'actions  (création, mis à jour, suppression de Publication, FileDocument et Categories) par espace.

7.3.11 Types de contenu les plus utilisés

C'est le palmarès des types de contenus les plus modifiés (création, mis à jour ou suppression).

7.3.12 Palmarès des contributeurs

C'est le palmarès des membres qui ont effectués le plus d'actions de contribution (création, mis à jour ou suppression).

7.4 Utilisateurs

7.4.1 Comptes utilisateurs

Nombre de membres, non contacts, présents dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot").

7.4.2 Comptes utilisateurs créés

Nombre d'appel à performCreate sur des membres non désactivés, non invités et non contacts.

7.4.3 Comptes utilisateurs mis à jour

Nombre d'appel à performUpdate sur des membres non désactivés, non invités et non contacts.

7.4.4 Comptes utilisateurs supprimés

Nombre d'appel à performDelete sur des membres non désactivés, non invités et non contacts.

7.4.5 Comptes utilisateurs désactivés

Nombre de membres désactivés présents dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot").

7.4.6 Comptes utilisateurs désactivés créés

Nombre d'appel à performCreate sur des membres désactivés.

7.4.7 Comptes utilisateurs désactivés mis à jour

Nombre d'appel à performUpdate sur des membres désactivés.

7.4.8 Comptes utilisateurs désactivés supprimés

Nombre d'appel à performDelete sur des membres désactivés.

7.4.9 Contacts

Nombre de membres contacts présents dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot").

7.4.10 Contacts créés

Nombre d'appel à performCreate sur des membres contacts.

7.4.11 Contacts mis à jour

Nombre d'appel à performUpdate sur des membres contacts.

7.4.12 Contacts supprimés

Nombre d'appel à performDelete sur des membres contacts.

7.4.13 Invités

Nombre de membres invités présents dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot").

7.4.14 Invités créés

Nombre d'appel à performCreate sur des membres invités.

7.4.15 Invités mis à jour

Nombre d'appel à performUpdate sur des membres invités.

7.4.16 Invités supprimés

Nombre d'appel à performDelete sur des membres invités. 

7.5 Exploitation

7.5.1 Membres

Le nombre entre parenthèses est le nombre de membre de JCMS suivi du nombre de membre en base. Ces nombres sont les données présentes dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot").

7.5.2 Membres créés

Nombre d'appel à performCreate sur des membres dans le store.

7.5.3 Membres mis à jour

Nombre d'appel à performUpdate sur des membres dans le store.

7.5.4 Membres supprimés

Nombre d'appel à performDelete sur des membres dans le store.

7.5.5 Membres en base créés

Nombre d'appel à performCreate sur des membres en base.

7.5.6 Membres en base mis à jour

Nombre d'appel à performUpdate sur des membres en base.

7.5.7 Membres en base supprimés

Nombre d'appel à performDelete sur des membres en base.

7.5.8 Espace disque - maximum autorisé

C'est la valeur du quota maximum autorisé. C'est la donnée présente dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot").

7.5.9 Espace disque - valeur

C'est la valeur de l'espace disque utilisé. C'est la donnée présente dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot").

7.5.10 Espaces

Le nombre entre parenthèses est le nombre d'espace de travail présents dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot"). Les espaces secrets, fermés, ... sont comptés dans cet indicateur.

7.5.11 Espaces créés

Nombre d'appel à performCreate sur des espaces de travail.

7.5.12 Espaces mis à jour

Nombre d'appel à performUpdate sur des espaces de travail.

7.5.13 Espaces supprimés

Nombre d'appel à performDelete sur des espaces de travail.

7.5.14 Espaces collaboratifs

Le nombre entre parenthèses est le nombre d'espace collaboratifs présents dans JCMS à l'exécution du rapport planifié le plus récents (un "snapshot"). Les espaces secrets, fermés, ... sont comptés dans cet indicateur.

7.5.15 Espaces collaboratifs créés

Nombre d'appel à performCreate sur des espaces collaboratifs.

7.5.16 Espaces collaboratifs mis à jour

Nombre d'appel à performUpdate sur des espaces collaboratifs.

7.5.17 Espaces collaboratifs supprimés

Nombre d'appel à performDelete sur des espaces collaboratifs.

7.6 Technique

7.6.1 Requêtes

Il s'agit du même compteur que visites dans l'onglet Consultation.

7.6.2 Robots

Il s'agit du nombre de visites effectuées par un robot. La détection d'un robot est effectuée en analysant la requête. Pour déclarer un nouveau robot, enrichissez la propriété "jcms.analytics.regexp.bot".

7.6.3 Accès RSS

Il s'agit du nombre de requête dont la zone est "feedArea", précisé dans doFeedInit.jsp

7.6.4 Accès OpenApi

Il s'agit du nombre d’accès via OpenApi

7.6.5 Authentification erronée

Nombre d'authentification erronée sur la plate-forme.

7.6.6 Alertes CSRF

Nombre d'erreur CSRF généré sur la plate-forme.

7.6.7 Temps moyen d'accès

C'est le temps moyen d’exécution des requêtes, soit la moyenne des différences entre le passage dans doInitPage.jsp et celui dans doEmptyFooter.jsp.

7.6.8 Palmarès des navigateurs

Ce graphique présente le nombres de requêtes par navigateurs. Le nom du navigateur est obtenu par la classe Browser de JCMS et en analysant le userAgent de la requête selon l'expression régulière présente dans la propriété "jcms.analytics.filter.browser".

7.6.9 Palmarès des OS

Ce graphique présente le nombres de requêtes par OS client. Le nom de l'OS client est obtenu par la classe Browser de JCMS et en analysant le userAgent de la requête selon l'expression régulière présente dans la propriété "jcms.analytics.filter.os".

7.6.10 Palmarès des zones

Ce graphique présente le nombres de requêtes par "zone" JCMS.

7.6.11 Palmarès des IP

Palmarès des IP clientes qui se sont connectées sur la plate-forme. l'adresse IP utilisé est le contenu du champ X-Forwarded-For si ce dernier est rempli, sinon c'est l'adresse d'origine de la requête HTTP.

7.6.12 Palmarès des referers

Palmarès des nombres de requêtes en provenance de referrers externes.  Un referer externe est un referrer dont l'URL ne commence pas par l'URL du Channel. Ces URL sont tronqués à 255 caractères.

7.6.13 Palmarès des requêtes lentes

Palmarès des requêtes dont le temps de traitement (voir temps moyen d'accès) dépasse une valeur définie dans la propriété "jcms.analytics.day-agent.max-request-duration" avant l’exécution de l'agent (par défaut 60000 pour 1 minute)