I’m learning how I can improve my coding skills by using Patterns and practices(PP) and I learned how easy it was with PP to log within sandboxed solutions to the event viewer. unfortunately this doesn’t work for SharePoint 365 but don’t worry I also have a “little fix” for that.
I uploaded an example that you can use at our codeplex site. If you open the zip file you will see the following folder and files. It is recommended to download the example because the necessary wsp and DLL are also in the zip file :).
Diagnostics area
First thing we need to do is create a custom diagnostics area where we can log to. When your creating a diagnostic area it is also advised to create categories.
I used an xml file that will be the base of creating the areas and categories. In this example where only going to add new areas and categories how to remove areas and categories you can read it at the PP site.
XML File
<Areas>
<ADD>
<Name>VNTGName>
<Categories>
<Category>DebugCategory>
<Category>WorkflowCategory>
<Category>FeatureCategory>
<Category>WebpartCategory>
Categories>
ADD>
Areas>
The Code
You can create diagnostics areas with a SharePoint feature or you can use a Console application. In this example I’m going to use a console application.
Step 1.
Create a new Console application and name it “CreateEventSources”.
Step 2.
Put the platform target to “x64”.
Step 3.
Refenrence the following DLL files “Microsoft.Practices.ServiceLocation”,”Microsoft.Practices.SharePoint.Common”. The DLL files can be found in the zip file you downloaded from our codeplex.
Step 4.
Set the DLL files to “Copy Local” = true.
Step 5.
Reference the SharePoint DLL.
Step 6.
Use the following classes.
using Microsoft.Practices.SharePoint.Common.Logging;
using System.Diagnostics;
using Microsoft.Practices.SharePoint.Common;
using Microsoft.SharePoint;
using Microsoft.Practices.SharePoint.Common.Configuration;
using Microsoft.Practices.SharePoint.Common.ServiceLocation;
using Microsoft.Practices.ServiceLocation;
Step 7.
Copy past the code :).
class Program
{
static void Main(string[] args)
{
if (args.Length >= 2)
{
List<string> paramaters = new List<string>();
paramaters.Add("ADD");
paramaters.Add("REMOVE");
paramaters.Add("SEARCH");
string operation = args[0].Trim();
if (paramaters.Contains(operation.ToUpper()))
{
string siteURl = args[1].Trim();
IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent();
IConfigManager config = serviceLocator.GetInstance();
using (SPSite site = new SPSite(siteURl))
{
config.SetWeb(site.RootWeb);
DiagnosticsAreaCollection areaCollection = new DiagnosticsAreaCollection(config);
string areaName = string.Empty;
string categoryName = string.Empty;
if (operation.ToUpper() == "ADD" && args.Length >= 4)
{
areaName = args[2];
categoryName = args[3];
AddArea(areaCollection, areaName, categoryName);
DiagnosticsAreaEventSource.EnsureConfiguredAreasRegistered();
}
if (operation.ToUpper() == "REMOVE" && args.Length >= 3)
{
areaName = args[2];
if (args.Length >= 4)
{
categoryName = args[3];
}
if (string.IsNullOrEmpty(categoryName))
{
RemoveArea(areaCollection, areaName);
if (EventLog.SourceExists(categoryName))
{
EventLog.DeleteEventSource(categoryName);
}
}
}
Console.WriteLine("Event Log: Event Log Name = {0}; Exist = {1}",
Constants.EventLogName, EventLog.Exists(Constants.EventLogName));
foreach (DiagnosticsArea item in areaCollection)
{
Console.WriteLine("Event Log: Area Name = {0}; Exist = {1}",
item.Name, EventLog.SourceExists(item.Name));
Console.WriteLine("SP: Area Name = {0}; Category Count = {1}",
item.Name, item.DiagnosticsCategories.Count.ToString());
if (item.DiagnosticsCategories.Count > 0)
{
int categoryIndex = 0;
foreach (DiagnosticsCategory category in item.DiagnosticsCategories)
{
Console.WriteLine("\tCategory[{0}] Name = {1}", categoryIndex++, category.Name);
}
}
}
return;
}
}
}
Console.WriteLine("Parameters Error.");
}
private static void RemoveArea(DiagnosticsAreaCollection areas, string areaName)
{
if (IsExistArea(areas, areaName))
{
while (areas[areaName].DiagnosticsCategories.Count != 0)
{
areas[areaName].DiagnosticsCategories.Clear();
}
areas.RemoveAt(areas.IndexOf(areas[areaName]));
areas.SaveConfiguration();
}
}
private static void AddArea(DiagnosticsAreaCollection areaCollection, string areaName, string categoryName)
{
if (!IsExistArea(areaCollection, areaName))
{
DiagnosticsArea newArea = new DiagnosticsArea(areaName);
newArea.DiagnosticsCategories.Add(new DiagnosticsCategory(categoryName));
areaCollection.Add(newArea);
}
else
{
int index = areaCollection.IndexOf(areaCollection.First(o => o.Name.Equals(areaName)));
if (areaCollection[index].DiagnosticsCategories.Count(o => o.Name.Equals(categoryName)) == 0)
{
areaCollection[index].DiagnosticsCategories.Add(new DiagnosticsCategory(categoryName));
}
}
areaCollection.SaveConfiguration();
}
static private bool IsExistArea(DiagnosticsAreaCollection collection, string areaName)
{
foreach (DiagnosticsArea item in collection)
{
if (item.Name.Trim().ToUpper() == areaName.Trim().ToUpper())
return true;
}
return false;
}
static private void RemoveCategory(DiagnosticsAreaCollection areas, string areaName, string categoryName)
{
if (IsExistArea(areas, areaName))
{
DiagnosticsArea currentArea = areas[areaName];
if (currentArea.DiagnosticsCategories.Count != 0 &&
currentArea.DiagnosticsCategories.Count(o => o.Name.Equals(categoryName,
StringComparison.OrdinalIgnoreCase)) > 0)
{
int index = currentArea.DiagnosticsCategories.IndexOf(
currentArea.DiagnosticsCategories.First(o =>
o.Name.Equals(categoryName, StringComparison.OrdinalIgnoreCase)));
currentArea.DiagnosticsCategories.RemoveAt(index);
}
areas.SaveConfiguration();
}
}
}
Step 8.
Create a folder where you will add all the necessary files to deploy and install the diagnostics logging. You can find the wsp files in my zip file. After you build the the console application copy paste the exe file to the folder CreateEventSources.
Step 9.
Create a powershell script to deploy the necessary wsp’s and to install the diagnostics areas with the xml file and console application. Name the powershell script “EnableSandboxedLoging.ps1”.
Step 10.
The powershell code.
Add-PSSnapin Microsoft.SharePoint.Powershell -ErrorAction SilentlyContinue
#function that will check the job status of the solution.
function WaitForJobToFinish([string]$SolutionFileName)
{
$JobName = "*solution-deployment*$SolutionFileName*"
$job = Get-SPTimerJob | ?{ $_.Name -like $JobName }
if ($job -eq $null)
{
Write-Host 'Timer job not found'
}
else
{
$JobFullName = $job.Name
Write-Host -NoNewLine "Waiting to finish job $JobFullName"
while ((Get-SPTimerJob $JobFullName) -ne $null)
{
Write-Host -NoNewLine .
Start-Sleep -Seconds 2
}
Write-Host "Finished waiting for job.."
}
}
if ($args.Length -ge 1) {
#get the sharepoint url
$url = $args[0];
#create the solution path
$solutionPath = [string]::Concat($PWD.Path,"\wsp\");
#configurationproxy wsp name
$configurationproxy = "configurationproxy.wsp";
#Logerproxy wsp name
$loggerproxy = "Microsoft.Practices.SharePoint.Common.LoggerProxy.wsp";
Write-Host -ForegroundColor Green "adding and installing $configurationproxy and $loggerproxy";
Add-SPSolution "$solutionPath$configurationproxy";
Add-SPSolution "$solutionPath$loggerproxy";
Install-SPSolution $configurationproxy -GACDeployment -Force;
WaitForJobToFinish($configurationproxy)
Install-SPSolution $loggerproxy -GACDeployment -Force;
WaitForJobToFinish($loggerproxy)
#fetch the xml data
[Xml]$xmlFile = Get-Content "Areas.xml"
#Loop the ADD elements to add the diagnostics areas and categories
Foreach($add in $xmlFile.Areas.ADD){
Write-Host -ForegroundColor Green $add.Name
foreach($cat in $add.Categories.Category){
& ".\CreateEventSource\CreateEventSource.exe" "ADD" $url $add.Name $cat;
}
}
}
Step 11.
You can run the PowerShell script with the following command.
.\ EnableSandboxedLoging.ps1 “http://urlofyoursharepointsite”
Step 12.
Check if the the following features are activated. The features can be found at the Farm Features.
Tip: If you are using multiple frontend servers you have to create a console application that executes the following line of code.
DiagnosticsAreaEventSource.EnsureConfiguredAreasRegistered();
Then you are sure that on all frontend servers the the areas and categories are created. You can also create a timer job that only runs one time to execute that command.
Part 2: Example webpart sandbox logging