internal ExceptionTargetSite(StackTrace trace) { Debug.Assert(trace != null); Debug.Assert(trace.FrameCount > 0); var frame = trace.GetFrame(0); Debug.Assert(frame != null); var method = frame.GetMethod(); Debug.Assert(method != null); AssemblyName = method.DeclaringType.Assembly.FullName; TypeName = method.DeclaringType.FullName; MethodName = method.Name; ILOffset = frame.GetILOffset(); FileName = frame.GetFileName(); FileLineNumber = frame.GetFileLineNumber(); FileColumnNumber = frame.GetFileColumnNumber(); TargetSiteID = Hash.SHA1(GetHashableData()); }
/// <summary> /// Logs all name/value pairs as a single collection using concurrent MERGE statements. /// </summary> /// <param name="conn"></param> /// <param name="exCollectionID"></param> /// <param name="coll"></param> /// <returns></returns> async Task LogCollection(SqlConnectionContext conn, SHA1Hash exCollectionID, IDictionary<string, string> coll) { // The exCollectionID should be pre-calculated by `CalcCollectionID`. // Check if the exCollectionID exists already: int? collectionCount = await conn.ExecReader( @"SELECT COUNT(exCollectionID) FROM [dbo].[exCollectionKeyValue] WHERE [exCollectionID] = @exCollectionID", prms => prms.AddInParamSHA1("@exCollectionID", exCollectionID), async dr => await dr.ReadAsync() ? dr.GetInt32(0) : (int?)null ); // Don't bother logging name-value pairs if the collection already exists: if (!collectionCount.HasValue) return; if (collectionCount.Value == coll.Count) return; const int numTasksPerPair = 2; // Create an array of tasks to wait upon: var tasks = new Task[coll.Count * numTasksPerPair]; // Fill out the array of tasks with concurrent MERGE statements for each name/value pair: using (var en = coll.Keys.GetEnumerator()) for (int i = 0; en.MoveNext(); ++i) { string name = en.Current; string value = coll[name]; var exCollectionValueID = Hash.SHA1(value); // Merge the Value record: tasks[i * numTasksPerPair + 0] = conn.ExecNonQuery( @"MERGE [dbo].[exCollectionValue] WITH (HOLDLOCK) AS target USING (SELECT @exCollectionValueID) AS source (exCollectionValueID) ON (target.exCollectionValueID = source.exCollectionValueID) WHEN NOT MATCHED THEN INSERT ([exCollectionValueID], [Value]) VALUES (@exCollectionValueID, @Value );", prms => prms.AddInParamSHA1("@exCollectionValueID", exCollectionValueID) .AddInParamSize("@Value", SqlDbType.VarChar, -1, value) ); // Merge the Name-Value record: tasks[i * numTasksPerPair + 1] = conn.ExecNonQuery( @"MERGE [dbo].[exCollectionKeyValue] WITH (HOLDLOCK) AS target USING (SELECT @exCollectionID, @Name, @exCollectionValueID) AS source (exCollectionID, Name, exCollectionValueID) ON (target.exCollectionID = source.exCollectionID AND target.Name = source.Name AND target.exCollectionValueID = source.exCollectionValueID) WHEN NOT MATCHED THEN INSERT ([exCollectionID], [Name], [exCollectionValueID]) VALUES (@exCollectionID, @Name, @exCollectionValueID );", prms => prms.AddInParamSHA1("@exCollectionID", exCollectionID) .AddInParamSize("@Name", SqlDbType.VarChar, 96, name) .AddInParamSHA1("@exCollectionValueID", exCollectionValueID) ); } // Our final task's completion depends on all the tasks created thus far: await Task.WhenAll(tasks); }
/// <summary> /// Represents a thrown exception and some context. /// </summary> /// <param name="ex">The exception that was thrown.</param> /// <param name="isHandled">Whether the exception is explicitly handled or not.</param> /// <param name="correlationID">A unique identifier to tie two or more exception reports together.</param> /// <param name="webHostingContext">The current web application hosting context, if applicable.</param> /// <param name="capturedHttpContext">Captured values from the current HTTP context being handled, if applicable.</param> public ExceptionWithCapturedContext( Exception ex, bool isHandled = false, Guid? correlationID = null, WebHostingContext webHostingContext = null, CapturedHttpContext capturedHttpContext = null ) { // Check for a WrappedException to pull out custom Exception info: WrappedException wex = ex as WrappedException; Exception thrownEx = ex; if (wex != null) { UserState = wex.UserState; ex = wex.Wrapped; } Exception = ex; IsHandled = isHandled; CorrelationID = correlationID; WebHostingContext = webHostingContext; CapturedHttpContext = capturedHttpContext; RealStackTrace = new StackTrace(thrownEx, true); LoggedTimeUTC = DateTime.UtcNow; ManagedThreadID = Thread.CurrentThread.ManagedThreadId; SequenceNumber = Interlocked.Increment(ref runningSequenceNumber); // Capture some details about the exception: var exType = ex.GetType(); AssemblyName = exType.Assembly.FullName; TypeName = exType.FullName; // TODO(jsd): Sanitize embedded file paths in stack trace StackTrace = thrownEx.StackTrace; // SHA1 hash the `assemblyName:typeName:stackTrace[:targetSite]`: string hashableData = String.Concat(AssemblyName, ":", TypeName, ":", StackTrace); if (RealStackTrace != null && RealStackTrace.FrameCount > 0) { // Add in TargetSite data: TargetSite = new ExceptionTargetSite(RealStackTrace); hashableData += ":" + TargetSite.GetHashableData(); } ExceptionID = Hash.SHA1(hashableData); if (ex.InnerException != null) InnerException = new ExceptionWithCapturedContext(ex.InnerException, isHandled, correlationID, webHostingContext, capturedHttpContext); }
public HashedLogIdentifier(SHA1Hash sha1hash, int instanceID) { _sha1hash = sha1hash; _instanceID = instanceID; }