// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using JetBrains.Annotations; using Microsoft.AspNet.Diagnostics.Entity.Utilities; using Microsoft.Data.Entity.Storage; using Microsoft.Extensions.Logging; using System; #if DNXCORE50 using System.Threading; #else using System.Runtime.Remoting.Messaging; #endif namespace Microsoft.AspNet.Diagnostics.Entity { public class DataStoreErrorLogger : ILogger { #if DNXCORE50 private readonly AsyncLocal _log = new AsyncLocal(); #else private const string ContextName = "__DataStoreErrorLog"; #endif public virtual DataStoreErrorLog LastError { get { #if DNXCORE50 return _log.Value; #else return (DataStoreErrorLog)CallContext.LogicalGetData(ContextName); #endif } } public virtual void StartLoggingForCurrentCallContext() { // Because CallContext is cloned at each async operation we cannot // lazily create the error object when an error is encountered, otherwise // it will not be available to code outside of the current async context. // We create it ahead of time so that any cloning just clones the reference // to the object that will hold any errors. #if DNXCORE50 _log.Value = new DataStoreErrorLog(); #else CallContext.LogicalSetData(ContextName, new DataStoreErrorLog()); #endif } public virtual void Log(LogLevel logLevel, int eventId, [CanBeNull] object state, [CanBeNull] Exception exception, [CanBeNull] Func formatter) { var errorState = state as DatabaseErrorLogState; if (errorState != null && exception != null && LastError != null) { LastError.SetError(errorState.ContextType, exception); } } public virtual bool IsEnabled(LogLevel logLevel) { return true; } public virtual IDisposable BeginScopeImpl(object state) { return NullScope.Instance; } private class NullScope : IDisposable { public static NullScope Instance = new NullScope(); public void Dispose() { } } public class DataStoreErrorLog { private Type _contextType; private Exception _exception; public virtual void SetError([NotNull] Type contextType, [NotNull] Exception exception) { Check.NotNull(contextType, nameof(contextType)); Check.NotNull(exception, nameof(exception)); _contextType = contextType; _exception = exception; } public virtual bool IsErrorLogged { get { return _exception != null; } } public virtual Type ContextType { get { return _contextType; } } public virtual Exception Exception { get { return _exception; } } } } }