SharePoint 2010 : Bonnes pratiques de gestion des logs et des exceptions

Je vous rassure, je ne vais pas vous refaire le couplet bien connu “Les exceptions c’est mal, il faut faire des TryCatch, etc…”, je pars du point de vue que vous le savez déjà et que vous essayez tant bien que mal de faire au mieux.

Ce ne sera une nouveauté pour personne aucun SharePointeur, vous avez la possibilité d’enregistrer le comportement de votre application dans les logs SharePoint (14/Logs) ou dans l’Event Viewer. Mais souvent, on se demande comment le faire, comment choisir entre l’un ou l’autre, etc. Au final, peu de développeurs respectent ces pratiques et on se retrouve souvent avec des développements bancals.

Voyons donc une manière simple et rapide de mettre en place la gestion des logs et exception dans SharePoint via un cas concret :

Je souhaite développer une webpart qui affiche tout les listes présentes sur le site courant. Et je souhaite que cette webpart dans les cas où elle viendrait à “planter”, affiche un message d’erreur “propre” aux utilisateurs et des informations détaillés aux Développeurs/Administrateurs. Ces informations seront disponibles sur les logs ou/et dans l’event viewer.

On va créer cette webpart, j’ai pour habitude de partir sur un développement basé sur le pattern MVP mais ce que je vous propose marchera aussi si vous avez envie de la faire à l’ancienne (ie. ASP.Net spaghetti).

On part sur une vue et un modèle très simple

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace DemoExceptionHandlingWebPart.ExceptionView
{
    interface IExceptionView
    {
        List<string> SetSiteData { set; }
    }
}
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
 
namespace DemoExceptionHandlingWebPart.ExceptionView
{
    interface IExceptionViewModel
    {
        List<string> GetSiteData();
    }
}

La webpart en elle-même est très simple aussi. la seule particularité est l’utilisation d’un “ErrorVisualizer” dont je vous parlerais tout à l’heure.

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using DemoExceptionHandlingWebPart.ExceptionHandling;
using System.Collections.Generic;
 
namespace DemoExceptionHandlingWebPart.ExceptionView
{
    [ToolboxItemAttribute(false)]
    public class ExceptionView : WebPart, IExceptionView
    {
        private ExceptionViewPresenter presenter;
        private GridView gvList = new GridView();
 
        public ExceptionView()
        {
        }
 
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
 
            IErrorVisualizer errorVisualizer = new ErrorVisualizer(this);
            presenter = new ExceptionViewPresenter(this, new ExceptionViewModel());
            this.presenter.ErrorVisualizer = errorVisualizer;
            presenter.SetSiteData();
 
            Controls.Add(gvList);
        }
 
        public List<string> SetSiteData
        {
            set
            {
                gvList.ID = "gridViewList" + this.ID;
                gvList.DataSource = value;
                gvList.DataBind();
            }
        }
    }
}

On a donc ici, un minimum d’intelligence dans la vue, on fait appel au présenter et on instancie les contrôles dont on a besoin.

Vous vous en doutez, c’est dans le presenter que tout (ou presque) se passe.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DemoExceptionHandlingWebPart.ExceptionHandling;
using Microsoft.SharePoint;
 
namespace DemoExceptionHandlingWebPart.ExceptionView
{
    class ExceptionViewPresenter
    {
        private IExceptionView view;
        private IExceptionViewModel model;
        private Random rand;
 
        public ExceptionViewPresenter(IExceptionView view, IExceptionViewModel model)
        {
            this.view = view;
            this.model = model;
            this.rand = new Random(DateTime.Now.Millisecond);
        }
 
        public IErrorVisualizer ErrorVisualizer { get; set; }
 
        public void SetSiteData()
        {
            int eventId = rand.Next(1000);
            try
            {
                if (eventId > 500)
                {
                    throw new SPException("The current user does not have the permissions to access this content");
                }
                else
                {
                    this.view.SetSiteData = this.model.GetSiteData();
                }
            }
            catch (Exception ex)
            {
                // If an unhandled exception occurs in the view, then instruct the ErrorVisualizer to replace
                // the view with an errormessage. 
                ViewExceptionHandler viewExceptionHandler = new ViewExceptionHandler();
                viewExceptionHandler.HandleViewException(ex, this.ErrorVisualizer, eventId);
            }
        }
 
    }
}

Comme je souhaite pouvoir tester mon application (ie. je veux la voir “crasher” souvent), j’ai décidé de faire une simple condition sur la valeur d’un rand.next. Si cette valeur est > 500 alors je lance une exception sinon je récupère les données de mon model. Le tout englobé dans un TryCatch dans le but de récupérer l’erreur et de la traiter dans le ViewExceptionHandler.

Voyons maintenant le reste de la webpart :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
 
namespace DemoExceptionHandlingWebPart.ExceptionView
{
    class ExceptionViewModel : IExceptionViewModel
    {
        public List<string> GetSiteData()
        {
            SPWeb web = SPContext.Current.Web;
            List<string> result = new List<string>(web.Lists.Count);
 
            foreach (SPList list in web.Lists)
            {
                if (!list.Hidden)
                    result.Add(list.Title);
            }
 
            return result;
        }
    }
}

Et j’ai terminé mon travail.

Comment ça terminé mon travail ? Et la gestion des exceptions me direz vous ? Et bien tout est géré dans le ViewExceptionHandler et sa méthode HandleViewException

        /// <summary>
        /// Handle an exception in a view. This method will log the error using the ILogger that's registered in 
        /// the <see cref="SharePointServiceLocator"/> and will show the error in the <paramref name="errorVisualizer"/>
        /// </summary>
        /// <param name="exception">The exception to handle.</param>
        /// <param name="errorVisualizer">The error visualizer that will show the errormessage.</param>
        /// <param name="eventId">The EventId to log the error under.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Visualizer"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "In this case, we are building an unhandled exception handler. The ThrowExceptionHandlingException will throw the exception for us")]
        [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public virtual void HandleViewException(Exception exception, IErrorVisualizer errorVisualizer, int eventId)
        {
            try
            {
                ILogger logger = GetLogger(exception);
                logger.LogToOperations(exception, eventId, EventSeverity.Error, null);
 
                EnsureErrorVisualizer(errorVisualizer, exception);
                errorVisualizer.ShowDefaultErrorMessage();
            }
            catch (ExceptionHandlingException)
            {
                throw;
            }
            catch (Exception handlingException)
            {
                this.ThrowExceptionHandlingException(handlingException, exception);
            }
        }

Bonne nouvelle c’est ici que votre travail s’arrête, tout le reste (comprenez toute la plomberie nécessaire à ce que le code ci-dessus fonctionne a déjà été codée par l’équipe de Patterns & Practices qui ont fait tout le travail pour vous (et moi).

En effet, ils ont développé une manière uniforme et très pratique de gérer une bonne fois pour toutes les erreurs dans SharePoint.

L’exemple que vous voyez ci-dessus est appliqué à une utilisation dans des Webpart avec un contrôle “ErrorVisualiser” qui va nettoyer la vue et n’afficher que le message d’erreur “propre” destiné à l’utilisateur (ici ShowDefaultErrorMessage). Le logToOperations s’occupera des informations techniques et les placera dans les logs ou dans l’event viewer en fonction de l’EventSeverity.

Si par contre vous souhaitez utiliser le travail de P&P dans un event receiver ou autre, vous pourriez faire :

// ... 
try 
{ 
  // ... a SharePoint operation ... 
} 
catch (SPException ex) 
{ 
  //Get an instance of ILogger 
  ILogger logger = SharePointServiceLocator.Current.GetInstance<ILogger>();       
  //Define your exception properties 
  string msg = "An error occurred while trying to retrieve the customer list";
  string category = @"SalesTool/Data"; 
  int eventID = 0; 
  EventLogEntryType severity = EventLogEntryType.Error; 
  //Log the exception 
  logger.LogToOperations(ex, msg, eventID, severity, category); 
}

Vous pourrez retrouver toute cette gestion d’erreur dans le dernier drop à cet url : http://codeplex.com/spg

PS : Je vous parlerai de ce SharePointServiceLocator dans les posts à venir.

<Philippe/>

Publié mardi 23 février 2010 09:00 par phil
Classé sous , , , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

About phil

Philippe Sentenac est Consultant SharePoint à Wygwam en région Parisienne. Il intervient essentiellement sur des missions liées à SharePoint (2007 et 2010 ) mais aussi autour du Web 2.0. Plus généralement, il s'intéresse à l'ASP.Net (MVC) , à Silverlight, et à tout ce qui est orienté Web en rapport avec les nouvelles technologies, qu'il pratique depuis 2006. Féru de développement, il est passionné par les problématiques de méthodologies et d'industrialisation du développement.

Les 10 derniers blogs postés

- Pour rappel ! Les spécifications des protocoles Office et SharePoint sont disponibles sur MSDN par Julien Chable le il y a 13 heures et 41 minutes

- Joyeux anniversaire Nix par The diary of EBArtSoft le 05-29-2012, 01:00

- Imagine Cup 2012, Make a Sign en finale par Le Blog (Vert) d'Arnaud JUND le 05-23-2012, 12:57

- Kinect 1.5 is out ! par Cyril Sansus le 05-21-2012, 17:47

- Les actualités de la semaine sur c2i.fr (14 mai - 20 mai) par Richard Clark le 05-21-2012, 08:50

- Reactive Extensions : Consommer des services avec Rx Partie 3, les pièges à éviter par Léonard Labat le 05-20-2012, 23:45

- SharePoint Blog Site, problème d’archives par Le Blog (Vert) d'Arnaud JUND le 05-20-2012, 13:09

- Soirée ALT.NET Mai - 3 présentations par #Rui le 05-18-2012, 11:59

- [ #SharePoint 2010][ #SQLServer 2012] AlwaysOn pour SharePoint (2/4) : Configuration (2e partie)… par Le blog de Patrick [MVP SharePoint] le 05-18-2012, 11:31

- Team Foundation Server 11: tous les trésors cachés du site d’équipe par Philess le 05-16-2012, 19:01