|
//------------------------------------------------------------------------------
// <copyright file="events.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Management {
using System.Configuration.Provider;
using System.Collections;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.Data;
using System.Data.SqlClient;
using System.Security.Permissions;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web.DataAccess;
using System.Web.Util;
////////////
// Events
////////////
[PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
public class SqlWebEventProvider : BufferedWebEventProvider, IInternalWebEventProvider {
const int SQL_MAX_NTEXT_SIZE = 1073741823;
const int NO_LIMIT = -1;
const string SP_LOG_EVENT = "dbo.aspnet_WebEvent_LogEvent";
string _sqlConnectionString;
int _maxEventDetailsLength = NO_LIMIT;
int _commandTimeout = -1;
int _SchemaVersionCheck;
int _connectionCount = 0;
DateTime _retryDate = DateTime.MinValue; // Won't try sending unless DateTime.UtcNow is > _retryDate
protected internal SqlWebEventProvider() { }
public override void Initialize(string name, NameValueCollection config) {
Debug.Trace("SqlWebEventProvider", "Initializing: name=" + name);
_SchemaVersionCheck = 0;
string temp = null;
ProviderUtil.GetAndRemoveStringAttribute(config, "connectionStringName", name, ref temp);
ProviderUtil.GetAndRemoveStringAttribute(config, "connectionString", name, ref _sqlConnectionString);
if (!String.IsNullOrEmpty(temp)) {
if (!String.IsNullOrEmpty(_sqlConnectionString)) {
throw new ConfigurationErrorsException(SR.GetString(SR.Only_one_connection_string_allowed));
}
_sqlConnectionString = SqlConnectionHelper.GetConnectionString(temp, true, true);
if (_sqlConnectionString == null || _sqlConnectionString.Length < 1) {
throw new ConfigurationErrorsException(SR.GetString(SR.Connection_string_not_found, temp));
}
}
else {
// If a connection string is specified explicitly, verify that its not using integrated security
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(_sqlConnectionString);
if (builder.IntegratedSecurity) {
throw new ConfigurationErrorsException(SR.GetString(SR.Cannot_use_integrated_security));
}
}
if (String.IsNullOrEmpty(_sqlConnectionString)) {
throw new ConfigurationErrorsException(SR.GetString(SR.Must_specify_connection_string_or_name, temp));
}
ProviderUtil.GetAndRemovePositiveOrInfiniteAttribute(config, "maxEventDetailsLength", name, ref _maxEventDetailsLength);
if (_maxEventDetailsLength == ProviderUtil.Infinite) {
_maxEventDetailsLength = NO_LIMIT;
}
else if (_maxEventDetailsLength > SQL_MAX_NTEXT_SIZE) {
throw new ConfigurationErrorsException(SR.GetString(SR.Invalid_max_event_details_length, name, _maxEventDetailsLength.ToString(CultureInfo.CurrentCulture)));
}
ProviderUtil.GetAndRemovePositiveAttribute(config, "commandTimeout", name, ref _commandTimeout);
base.Initialize(name, config);
}
private void CheckSchemaVersion(SqlConnection connection) {
string[] features = { "Health Monitoring" };
string version = "1";
SecUtility.CheckSchemaVersion( this, connection, features, version, ref _SchemaVersionCheck );
}
public override void ProcessEventFlush(WebEventBufferFlushInfo flushInfo) {
Debug.Trace("SqlWebEventProvider", "EventBufferFlush called: " +
"NotificationType=" + flushInfo.NotificationType +
", NotificationSequence=" + flushInfo.NotificationSequence +
", Events.Count=" + flushInfo.Events.Count);
WriteToSQL(flushInfo.Events, flushInfo.EventsDiscardedSinceLastNotification,
flushInfo.LastNotificationUtc);
}
void PrepareParams(SqlCommand sqlCommand) {
sqlCommand.Parameters.Add(new SqlParameter("@EventId", SqlDbType.Char, 32));
sqlCommand.Parameters.Add(new SqlParameter("@EventTimeUtc", SqlDbType.DateTime));
sqlCommand.Parameters.Add(new SqlParameter("@EventTime", SqlDbType.DateTime));
sqlCommand.Parameters.Add(new SqlParameter("@EventType", SqlDbType.NVarChar, 256));
sqlCommand.Parameters.Add(new SqlParameter("@EventSequence", SqlDbType.Decimal));
sqlCommand.Parameters.Add(new SqlParameter("@EventOccurrence", SqlDbType.Decimal));
sqlCommand.Parameters.Add(new SqlParameter("@EventCode", SqlDbType.Int));
sqlCommand.Parameters.Add(new SqlParameter("@EventDetailCode", SqlDbType.Int));
sqlCommand.Parameters.Add(new SqlParameter("@Message", SqlDbType.NVarChar, 1024));
sqlCommand.Parameters.Add(new SqlParameter("@ApplicationPath", SqlDbType.NVarChar, 256));
sqlCommand.Parameters.Add(new SqlParameter("@ApplicationVirtualPath", SqlDbType.NVarChar, 256));
sqlCommand.Parameters.Add(new SqlParameter("@MachineName", SqlDbType.NVarChar, 256));
sqlCommand.Parameters.Add(new SqlParameter("@RequestUrl", SqlDbType.NVarChar, 1024));
sqlCommand.Parameters.Add(new SqlParameter("@ExceptionType", SqlDbType.NVarChar, 256));
sqlCommand.Parameters.Add(new SqlParameter("@Details", SqlDbType.NText));
}
void FillParams(SqlCommand sqlCommand, WebBaseEvent eventRaised) {
Exception exception = null;
WebRequestInformation reqInfo = null;
string details = null;
WebApplicationInformation appInfo = WebBaseEvent.ApplicationInformation;
int n = 0;
sqlCommand.Parameters[n++].Value = eventRaised.EventID.ToString("N", CultureInfo.InstalledUICulture); // @EventId
sqlCommand.Parameters[n++].Value = eventRaised.EventTimeUtc; // @EventTimeUtc
sqlCommand.Parameters[n++].Value = eventRaised.EventTime; // @EventTime
sqlCommand.Parameters[n++].Value = eventRaised.GetType().ToString(); // @EventType
sqlCommand.Parameters[n++].Value = eventRaised.EventSequence; // @EventSequence
sqlCommand.Parameters[n++].Value = eventRaised.EventOccurrence; // @EventOccurrence
sqlCommand.Parameters[n++].Value = eventRaised.EventCode; // @EventCode
sqlCommand.Parameters[n++].Value = eventRaised.EventDetailCode; // @EventDetailCode
sqlCommand.Parameters[n++].Value = eventRaised.Message; // @Message
sqlCommand.Parameters[n++].Value = appInfo.ApplicationPath; // @ApplicationPath
sqlCommand.Parameters[n++].Value = appInfo.ApplicationVirtualPath; // @ApplicationVirtualPath
sqlCommand.Parameters[n++].Value = appInfo.MachineName; // @MachineName
//
// @RequestUrl
if (eventRaised is WebRequestEvent) {
reqInfo = ((WebRequestEvent)eventRaised).RequestInformation;
}
else if (eventRaised is WebRequestErrorEvent) {
reqInfo = ((WebRequestErrorEvent)eventRaised).RequestInformation;
}
else if (eventRaised is WebErrorEvent) {
reqInfo = ((WebErrorEvent)eventRaised).RequestInformation;
}
else if (eventRaised is WebAuditEvent) {
reqInfo = ((WebAuditEvent)eventRaised).RequestInformation;
}
sqlCommand.Parameters[n++].Value = (reqInfo != null) ? reqInfo.RequestUrl : Convert.DBNull;
// @ExceptionType
if (eventRaised is WebBaseErrorEvent) {
exception = ((WebBaseErrorEvent)eventRaised).ErrorException;
}
sqlCommand.Parameters[n++].Value = (exception != null) ? exception.GetType().ToString() : Convert.DBNull;
// @Details
details = eventRaised.ToString();
if (_maxEventDetailsLength != NO_LIMIT &&
details.Length > _maxEventDetailsLength) {
details = details.Substring(0, _maxEventDetailsLength);
}
sqlCommand.Parameters[n++].Value = details;
}
[PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
[SqlClientPermission(SecurityAction.Assert, Unrestricted = true)]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
void WriteToSQL(WebBaseEventCollection events, int eventsDiscardedByBuffer, DateTime lastNotificationUtc) {
// We don't want to send any more events until we've waited until the _retryDate (which defaults to minValue)
if (_retryDate > DateTime.UtcNow) {
return;
}
try {
SqlConnectionHolder sqlConnHolder = SqlConnectionHelper.GetConnection(_sqlConnectionString, true);
SqlCommand sqlCommand = new SqlCommand(SP_LOG_EVENT);
CheckSchemaVersion(sqlConnHolder.Connection);
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Connection = sqlConnHolder.Connection;
if (_commandTimeout > -1) {
sqlCommand.CommandTimeout = _commandTimeout;
}
PrepareParams(sqlCommand);
try {
sqlConnHolder.Open(null, true);
Interlocked.Increment(ref _connectionCount);
if (eventsDiscardedByBuffer != 0) {
WebBaseEvent infoEvent = new WebBaseEvent(
SR.GetString(SR.Sql_webevent_provider_events_dropped,
eventsDiscardedByBuffer.ToString(CultureInfo.InstalledUICulture),
lastNotificationUtc.ToString("r", CultureInfo.InstalledUICulture)),
null,
WebEventCodes.WebEventProviderInformation,
WebEventCodes.SqlProviderEventsDropped);
FillParams(sqlCommand, infoEvent);
sqlCommand.ExecuteNonQuery();
}
foreach (WebBaseEvent eventRaised in events) {
FillParams(sqlCommand, eventRaised);
sqlCommand.ExecuteNonQuery();
}
}
#if DBG
catch (Exception e) {
Debug.Trace("SqlWebEventProvider", "ExecuteNonQuery failed: " + e);
throw;
}
#endif
finally {
sqlConnHolder.Close();
Interlocked.Decrement(ref _connectionCount);
}
#if (!DBG)
try {
#endif
EventProcessingComplete(events);
#if (!DBG)
}
catch {
// Ignore all errors.
}
#endif
}
catch {
// For any failure, we will wait at least 30 seconds or _commandTimeout before trying again
double timeout = 30;
if (_commandTimeout > -1) {
timeout = (double)_commandTimeout;
}
_retryDate = DateTime.UtcNow.AddSeconds(timeout);
throw;
}
}
public override void ProcessEvent(WebBaseEvent eventRaised)
{
if (UseBuffering) {
base.ProcessEvent(eventRaised);
}
else {
Debug.Trace("SqlWebEventProvider", "Writing event to SQL: event=" + eventRaised.GetType().Name);
WriteToSQL(new WebBaseEventCollection(eventRaised), 0, new DateTime(0));
}
}
protected virtual void EventProcessingComplete(WebBaseEventCollection raisedEvents) {
}
public override void Shutdown() {
try {
Flush();
}
finally {
base.Shutdown();
}
// VSWhidbey 531556: Need to wait until all connections are gone before returning here
// Sleep for 2x the command timeout in 1 sec intervals then give up, default timeout is 30 sec
if (_connectionCount > 0) {
int sleepAttempts = _commandTimeout*2;
if (sleepAttempts <= 0) {
sleepAttempts = 60;
}
// Check every second
while (_connectionCount > 0 && sleepAttempts > 0) {
--sleepAttempts;
Thread.Sleep(1000);
}
}
}
}
}
|