在开发环境中,即使代码没有抛出任何异常,同样的代码在生产环境中可能会莫名其妙地失败。这就是为什么日志记录对于解决方案和代码至关重要。日志记录是开发者中非常流行的话题。一些人喜欢“自己的自定义日志系统”,其他人使用已经存在的框架,最后剩下的人可能会使用操作系统提供的服务,例如Windows API。如果在编写SharePoint的自定义代码,这里有一些建议可以帮助改进解决方案的日志记录部分。
建议 #0 - 不要使用第三方日志框架
即使坚持使用log4net、NLog、EventLog或System.Diagnostic.Trace,或者尝试发明自己的轮子,那么不要再使用这些了。SharePoint有自己的日志系统,称为“统一日志系统(ULS)”。如果还在想为什么会使用ULS而不是可爱的日志框架,那么考虑以下事实:
建议 #1 - 对于最简单的情况不要做任何事情
默认情况下,SharePoint会捕获未处理的异常,并将它们报告给ULS日志。如果有未处理的异常,可以很容易地在ULS日志中找到错误及其堆栈跟踪。如果打开“VerboseEx”日志级别,那么甚至会看到对数据库的SQL查询。
也可以使用ULSViewer获得更方便的日志查看体验。与此同时,有一个例外是关于WebParts的。如果自定义WebPart出现异常,那么最终用户只会看到默认的错误页面。可能需要花费更多的时间来处理一个配置错误的个人WebPart属性的错误 - 除非以特定用户身份登录,否则将无法追踪和调试这个错误。如果正在开发一个自定义WebPart,那么更好的方法可能不仅仅是显示错误,还要提供一些可能的修复方法。
建议 #2 - 使用SPMonitoredScope类
SPMonitoredScope可以像这样简单使用:
using (
new SPMonitoredScope(
"My Monitored Scope")
)
{
// Do Stuff...
}
将简单地获得进入/离开日志记录,以及在范围调用上花费的时间。默认情况下,SPMonitoredScope具有“Verbose”日志级别。
SPMonitoredScope还可以帮助记录性能和资源使用/分配:
using (
new SPMonitoredScope(
"My Scope Name",
1000,
new SPRequestUsageCounter(3),
new SPSqlQueryCounter(10)))
{
// Do Stuff...
}
如果达到了执行时间(1000毫秒),或者SPSite/SPWeb分配的总数超过3,或者SQL查询的总数超过10,那么日志级别将被提升到“High”。还将获得这些参数的实际值以及实际位置(通过堆栈跟踪),SPSite/SPWeb对象被创建的地方,以及SQL查询被执行的地方。
关于SPMonitoredScope最有趣的事实是,跟踪被推送到Developer Dashboard。
建议 #3 - 使用SPDiagnosticsService写日志
可以通过SPDiagnosticsService类写入ULS。
SPDiagnosticsService diagSvc = SPDiagnosticsService.Local;
diagSvc.WriteTrace(
0,
new SPDiagnosticsCategory(
"My Category",
TraceSeverity.Monitorable,
EventSeverity.Error),
TraceSeverity.Monitorable,
"An exception occurred: {0}",
new object[] {ex});
对于每个日志记录,ULS有“Area”和“Category”。默认情况下,“Area”获得“Unknown”值。
也可以直接写入事件日志:
SPDiagnosticsService diagSvc = SPDiagnosticsService.Local;
diagSvc.WriteEvent(
0,
new SPDiagnosticsCategory(
"My Category",
TraceSeverity.Monitorable,
EventSeverity.Warning),
EventSeverity.Error,
"Exception occured {0}",
new object[] {ex});
建议 #4 - 继承SPDiagnosticsServiceBase以获得对日志记录过程的更多控制
如果正在开发一个大型组件或产品,那么创建自己的“Diagnostics Service”是合理的 - 技术上只是一个从SPDiagnosticsServiceBase继承的新类。可以定义自己的“Area”和“Category”,以及更好的日志记录方法。中央管理和PowerShell也可以为每个“Category”或“Area”设置不同的日志级别。
public class LoggingService : SPDiagnosticsServiceBase
{
public static string DiagnosticAreaName = "My";
private static LoggingService _Current;
public static LoggingService Current
{
get
{
if (_Current == null)
{
_Current = new LoggingService();
}
return _Current;
}
}
private LoggingService() : base("My Logging Service", SPFarm.Local)
{
}
protected override IEnumerable<SPDiagnosticsArea> ProvideAreas()
{
List<SPDiagnosticsArea> areas = new List<SPDiagnosticsArea>()
{
new SPDiagnosticsArea(DiagnosticAreaName,
new List<SPDiagnosticsCategory>()
{
new SPDiagnosticsCategory("WebParts", TraceSeverity.Unexpected, EventSeverity.Error)
})
};
return areas;
}
public static void LogError(string categoryName, string errorMessage)
{
SPDiagnosticsCategory category = LoggingService.Current.Areas[DiagnosticAreaName].Categories[categoryName];
LoggingService.Current.WriteTrace(0, category, TraceSeverity.Unexpected, errorMessage);
}
}
实际调用可能是这样的:
LoggingService.LogError("WebParts", ex.ToString());
建议 #5 - 利用diagnostics.asmx web服务进行客户端应用程序的日志记录
事实证明,SharePoint有一个/_vti_bin/diagnostics.asmx web服务。它只有一个方法,执行日志记录到ULS,带有“SharePoint Foundation -> Unified Logging Service”类别和“Verbose”级别。
如果是在制作Silverlight或JavaScript应用程序,那么这个web服务可能是唯一的日志记录方式,可以被开发者或管理员访问。其他选项很难实现。
SharePoint有很多Java Script库,它也有像ULSOnError(msg, url, line)和ULSSendException(ex)这样的函数。
不需要简单地调用这些函数,所以只需要包含额外的调用ULS.enable与“true”值。否则,将什么也得不到。
JavaScript
ULS.enable = true;
ULSOnError("Hello trace", document.location.href, 0);