I want to start logging some custom messages into the ULS from my custom SharePoint code. My code is running inside list item receivers attached to some lists. I'd like to configure this logging mechanism within the application start event handler in global.asax. What's the best-practices way to deploy a SharePoint solution package that modifies global.asax?
R – SharePoint and deployment of global.asax code
global-asaxloggingsharepoint
Related Solutions
Yes this is possible, see this MSDN article: http://msdn2.microsoft.com/hi-in/library/aa979595(en-us).aspx
And here is some sample code in C#:
using System;
using System.Runtime.InteropServices;
using Microsoft.SharePoint.Administration;
namespace ManagedTraceProvider
{
class Program
{
static void Main(string[] args)
{
TraceProvider.RegisterTraceProvider();
TraceProvider.WriteTrace(0, TraceProvider.TraceSeverity.High, Guid.Empty, "MyExeName", "Product Name", "Category Name", "Sample Message");
TraceProvider.WriteTrace(TraceProvider.TagFromString("abcd"), TraceProvider.TraceSeverity.Monitorable, Guid.NewGuid(), "MyExeName", "Product Name", "Category Name", "Sample Message");
TraceProvider.UnregisterTraceProvider();
}
}
static class TraceProvider
{
static UInt64 hTraceLog;
static UInt64 hTraceReg;
static class NativeMethods
{
internal const int TRACE_VERSION_CURRENT = 1;
internal const int ERROR_SUCCESS = 0;
internal const int ERROR_INVALID_PARAMETER = 87;
internal const int WNODE_FLAG_TRACED_GUID = 0x00020000;
internal enum TraceFlags
{
TRACE_FLAG_START = 1,
TRACE_FLAG_END = 2,
TRACE_FLAG_MIDDLE = 3,
TRACE_FLAG_ID_AS_ASCII = 4
}
// Copied from Win32 APIs
[StructLayout(LayoutKind.Sequential)]
internal struct EVENT_TRACE_HEADER_CLASS
{
internal byte Type;
internal byte Level;
internal ushort Version;
}
// Copied from Win32 APIs
[StructLayout(LayoutKind.Sequential)]
internal struct EVENT_TRACE_HEADER
{
internal ushort Size;
internal ushort FieldTypeFlags;
internal EVENT_TRACE_HEADER_CLASS Class;
internal uint ThreadId;
internal uint ProcessId;
internal Int64 TimeStamp;
internal Guid Guid;
internal uint ClientContext;
internal uint Flags;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct ULSTraceHeader
{
internal ushort Size;
internal uint dwVersion;
internal uint Id;
internal Guid correlationID;
internal TraceFlags dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
internal string wzExeName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
internal string wzProduct;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
internal string wzCategory;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 800)]
internal string wzMessage;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ULSTrace
{
internal EVENT_TRACE_HEADER Header;
internal ULSTraceHeader ULSHeader;
}
// Copied from Win32 APIs
internal enum WMIDPREQUESTCODE
{
WMI_GET_ALL_DATA = 0,
WMI_GET_SINGLE_INSTANCE = 1,
WMI_SET_SINGLE_INSTANCE = 2,
WMI_SET_SINGLE_ITEM = 3,
WMI_ENABLE_EVENTS = 4,
WMI_DISABLE_EVENTS = 5,
WMI_ENABLE_COLLECTION = 6,
WMI_DISABLE_COLLECTION = 7,
WMI_REGINFO = 8,
WMI_EXECUTE_METHOD = 9
}
// Copied from Win32 APIs
internal unsafe delegate uint EtwProc(NativeMethods.WMIDPREQUESTCODE requestCode, IntPtr requestContext, uint* bufferSize, IntPtr buffer);
// Copied from Win32 APIs
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
internal static extern unsafe uint RegisterTraceGuids([In] EtwProc cbFunc, [In] void* context, [In] ref Guid controlGuid, [In] uint guidCount, IntPtr guidReg, [In] string mofImagePath, [In] string mofResourceName, out ulong regHandle);
// Copied from Win32 APIs
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
internal static extern uint UnregisterTraceGuids([In]ulong regHandle);
// Copied from Win32 APIs
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
internal static extern UInt64 GetTraceLoggerHandle([In]IntPtr Buffer);
// Copied from Win32 APIs
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern uint TraceEvent([In]UInt64 traceHandle, [In]ref ULSTrace evnt);
}
public enum TraceSeverity
{
Unassigned = 0,
CriticalEvent = 1,
WarningEvent = 2,
InformationEvent = 3,
Exception = 4,
Assert = 7,
Unexpected = 10,
Monitorable = 15,
High = 20,
Medium = 50,
Verbose = 100,
}
public static void WriteTrace(uint tag, TraceSeverity level, Guid correlationGuid, string exeName, string productName, string categoryName, string message)
{
const ushort sizeOfWCHAR = 2;
NativeMethods.ULSTrace ulsTrace = new NativeMethods.ULSTrace();
// Pretty standard code needed to make things work
ulsTrace.Header.Size = (ushort)Marshal.SizeOf(typeof(NativeMethods.ULSTrace));
ulsTrace.Header.Flags = NativeMethods.WNODE_FLAG_TRACED_GUID;
ulsTrace.ULSHeader.dwVersion = NativeMethods.TRACE_VERSION_CURRENT;
ulsTrace.ULSHeader.dwFlags = NativeMethods.TraceFlags.TRACE_FLAG_ID_AS_ASCII;
ulsTrace.ULSHeader.Size = (ushort)Marshal.SizeOf(typeof(NativeMethods.ULSTraceHeader));
// Variables communicated to SPTrace
ulsTrace.ULSHeader.Id = tag;
ulsTrace.Header.Class.Level = (byte)level;
ulsTrace.ULSHeader.wzExeName = exeName;
ulsTrace.ULSHeader.wzProduct = productName;
ulsTrace.ULSHeader.wzCategory = categoryName;
ulsTrace.ULSHeader.wzMessage = message;
ulsTrace.ULSHeader.correlationID = correlationGuid;
// Pptionally, to improve performance by reducing the amount of data copied around,
// the Size parameters can be reduced by the amount of unused buffer in the Message
if (message.Length < 800)
{
ushort unusedBuffer = (ushort) ((800 - (message.Length + 1)) * sizeOfWCHAR);
ulsTrace.Header.Size -= unusedBuffer;
ulsTrace.ULSHeader.Size -= unusedBuffer;
}
if (hTraceLog != 0)
NativeMethods.TraceEvent(hTraceLog, ref ulsTrace);
}
public static unsafe void RegisterTraceProvider()
{
SPFarm farm = SPFarm.Local;
Guid traceGuid = farm.TraceSessionGuid;
uint result = NativeMethods.RegisterTraceGuids(ControlCallback, null, ref traceGuid, 0, IntPtr.Zero, null, null, out hTraceReg);
System.Diagnostics.Debug.Assert(result == NativeMethods.ERROR_SUCCESS);
}
public static void UnregisterTraceProvider()
{
uint result = NativeMethods.UnregisterTraceGuids(hTraceReg);
System.Diagnostics.Debug.Assert(result == NativeMethods.ERROR_SUCCESS);
}
public static uint TagFromString(string wzTag)
{
System.Diagnostics.Debug.Assert(wzTag.Length == 4);
return (uint) (wzTag[0] << 24 | wzTag[1] << 16 | wzTag[2] << 8 | wzTag[3]);
}
static unsafe uint ControlCallback(NativeMethods.WMIDPREQUESTCODE RequestCode, IntPtr Context, uint* InOutBufferSize, IntPtr Buffer)
{
uint Status;
switch (RequestCode)
{
case NativeMethods.WMIDPREQUESTCODE.WMI_ENABLE_EVENTS:
hTraceLog = NativeMethods.GetTraceLoggerHandle(Buffer);
Status = NativeMethods.ERROR_SUCCESS;
break;
case NativeMethods.WMIDPREQUESTCODE.WMI_DISABLE_EVENTS:
hTraceLog = 0;
Status = NativeMethods.ERROR_SUCCESS;
break;
default:
Status = NativeMethods.ERROR_INVALID_PARAMETER;
break;
}
*InOutBufferSize = 0;
return Status;
}
}
}
I implemented this recently and came up with a solution that worked for me.
Deploy your log4net config file to the 12 hive and the log4net dll into the GAC using a globally scoped solution. Then in your application code explicitly initialize log4net from the location of your global file. This allows you to log feature receiver, timer jobs and web application code.
[assembly: log4net.Config.XmlConfigurator(ConfigFile =
@"C:\Program Files\Common Files\Microsoft Shared\" +
@"Web Server Extensions\12\CONFIG\log4net.config", Watch = true)]
see here http://www.codeproject.com/KB/sharepoint/SharepointLog4Net.aspx
Best Answer
I don't know about "best practice", but I would be quite keen on making the edits via code in the feature reciever.
With a line that backs up the file for later restoration.
For logging, we have used Scott hilliers code here to create a trace provider to log with.
Works a treat.
I should clarify that we use a static readonly wrapper for the trace provider
that registers itself with the code
Gah!
This then gets instanciated only once and we use the destructor to unregister it.