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

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01

- SharePoint Online: Script PowerShell pour supprimer une colonne dans tous les sites d’une collection par Blog Technique de Romelard Fabrice le 11-27-2018, 18:01