You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
9.7 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using EntrustSettle.Common;
using EntrustSettle.Common.LogHelper;
using EntrustSettle.Hubs;
using Castle.DynamicProxy;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace EntrustSettle.AOP
{
/// <summary>
/// 拦截器BlogLogAOP 继承IInterceptor接口
/// </summary>
public class BlogLogAOP : IInterceptor
{
private readonly IHubContext<ChatHub> _hubContext;
private readonly IHttpContextAccessor _accessor;
public BlogLogAOP(IHubContext<ChatHub> hubContext, IHttpContextAccessor accessor)
{
_hubContext = hubContext;
_accessor = accessor;
}
/// <summary>
/// 实例化IInterceptor唯一方法
/// </summary>
/// <param name="invocation">包含被拦截方法的信息</param>
public void Intercept(IInvocation invocation)
{
string UserName = _accessor.HttpContext?.User?.Identity?.Name;
string json;
try
{
json = JsonConvert.SerializeObject(invocation.Arguments);
}
catch (Exception ex)
{
json = "无法序列化,可能是兰姆达表达式等原因造成,按照框架优化代码" + ex.ToString();
}
DateTime startTime = DateTime.Now;
AOPLogInfo apiLogAopInfo = new AOPLogInfo
{
RequestTime = startTime.ToString("yyyy-MM-dd hh:mm:ss fff"),
OpUserName = UserName,
RequestMethodName = invocation.Method.Name,
RequestParamsName = string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()),
ResponseJsonData = json
};
//测试异常记录
//Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss fff"));
//记录被拦截方法信息的日志信息
//var dataIntercept = "" +
// $"【当前操作用户】:{ UserName} \r\n" +
// $"【当前执行方法】:{ invocation.Method.Name} \r\n" +
// $"【携带的参数有】: {string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray())} \r\n";
try
{
//在被拦截的方法执行完毕后 继续执行当前方法,注意是被拦截的是异步的
invocation.Proceed();
// 异步获取异常,先执行
if (IsAsyncMethod(invocation.Method))
{
#region 方案一
//Wait task execution and modify return value
if (invocation.Method.ReturnType == typeof(Task))
{
invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally(
(Task) invocation.ReturnValue,
async () => await SuccessAction(invocation, apiLogAopInfo, startTime), /*成功时执行*/
ex =>
{
LogEx(ex, apiLogAopInfo);
});
}
//Task<TResult>
else
{
invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult(
invocation.Method.ReturnType.GenericTypeArguments[0],
invocation.ReturnValue,
//async () => await SuccessAction(invocation, dataIntercept),/*成功时执行*/
async (o) => await SuccessAction(invocation, apiLogAopInfo, startTime, o), /*成功时执行*/
ex =>
{
LogEx(ex, apiLogAopInfo);
});
}
#endregion
// 如果方案一不行,试试这个方案
//#region 方案二
//var type = invocation.Method.ReturnType;
//var resultProperty = type.GetProperty("Result");
//DateTime endTime = DateTime.Now;
//string ResponseTime = (endTime - startTime).Milliseconds.ToString();
//apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff");
//apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms";
//apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue));
////dataIntercept += ($"【响应时间】:{ResponseTime}ms\r\n");
////dataIntercept += ($"【执行完成时间】:{endTime.ToString("yyyy-MM-dd hh:mm:ss fff")}\r\n");
////dataIntercept += ($"【执行完成结果】:{JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue))}\r\n");
//Parallel.For(0, 1, e =>
//{
// //LogLock.OutLogAOP("ServiceAOPLog", new string[] { dataIntercept });
// LogLock.OutLogAOP("ServiceAOPLog", new string[] { apiLogAopInfo.GetType().ToString() + " - ResponseJsonDataType:" + type, JsonConvert.SerializeObject(apiLogAopInfo) });
//});
//#endregion
}
else
{
// 同步1
string jsonResult;
try
{
jsonResult = JsonConvert.SerializeObject(invocation.ReturnValue);
}
catch (Exception ex)
{
jsonResult = "无法序列化,可能是兰姆达表达式等原因造成,按照框架优化代码" + ex.ToString();
}
var type = invocation.Method.ReturnType;
var resultProperty = type.GetProperty("Result");
DateTime endTime = DateTime.Now;
string ResponseTime = (endTime - startTime).Milliseconds.ToString();
apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff");
apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms";
//apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue));
apiLogAopInfo.ResponseJsonData = jsonResult;
//dataIntercept += ($"【执行完成结果】:{jsonResult}");
Parallel.For(0, 1, e =>
{
LogLock.OutLogAOP("ServiceAOPLog", _accessor.HttpContext?.TraceIdentifier,
new string[] {apiLogAopInfo.GetType().ToString(), JsonConvert.SerializeObject(apiLogAopInfo)});
});
}
}
catch (Exception ex) // 同步2
{
LogEx(ex, apiLogAopInfo);
throw;
}
if (AppSettings.app(new string[] {"Middleware", "SignalRSendLog", "Enabled"}).ObjToBool())
{
_hubContext.Clients.All.SendAsync("ReceiveUpdate", "LogLock.GetLogData()").Wait();
}
}
private async Task SuccessAction(IInvocation invocation, AOPLogInfo apiLogAopInfo, DateTime startTime, object o = null)
{
//invocation.ReturnValue = o;
//var type = invocation.Method.ReturnType;
//if (typeof(Task).IsAssignableFrom(type))
//{
// //var resultProperty = type.GetProperty("Result");
// //类型错误 都可以不要invocation参数直接将o系列化保存到日记中
// dataIntercept += ($"【执行完成结果】:{JsonConvert.SerializeObject(invocation.ReturnValue)}");
//}
//else
//{
// dataIntercept += ($"【执行完成结果】:{invocation.ReturnValue}");
//}
DateTime endTime = DateTime.Now;
string ResponseTime = (endTime - startTime).Milliseconds.ToString();
apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff");
apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms";
apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(o);
await Task.Run(() =>
{
Parallel.For(0, 1, e =>
{
LogLock.OutLogAOP("ServiceAOPLog", _accessor.HttpContext?.TraceIdentifier,
new string[] {apiLogAopInfo.GetType().ToString(), JsonConvert.SerializeObject(apiLogAopInfo)});
});
});
}
private void LogEx(Exception ex, AOPLogInfo dataIntercept)
{
if (ex != null)
{
//执行的 service 中,捕获异常
//dataIntercept += ($"【执行完成结果】:方法中出现异常:{ex.Message + ex.InnerException}\r\n");
AOPLogExInfo apiLogAopExInfo = new AOPLogExInfo
{
ExMessage = ex.Message,
InnerException = "InnerException-内部异常:\r\n" + (ex.InnerException == null ? "" : ex.InnerException.InnerException.ToString()) +
("\r\nStackTrace-堆栈跟踪:\r\n") + (ex.StackTrace == null ? "" : ex.StackTrace.ToString()),
ApiLogAopInfo = dataIntercept
};
// 异常日志里有详细的堆栈信息
Parallel.For(0, 1, e =>
{
LogLock.OutLogAOP("ServiceAOPLogEx", _accessor.HttpContext?.TraceIdentifier,
new string[] {apiLogAopExInfo.GetType().ToString(), JsonConvert.SerializeObject(apiLogAopExInfo)});
});
}
}
public static bool IsAsyncMethod(MethodInfo method)
{
return (
method.ReturnType == typeof(Task) ||
(method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
);
}
}
internal static class InternalAsyncHelper
{
public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func<Task> postAction, Action<Exception> finalAction)
{
Exception exception = null;
try
{
await actualReturnValue;
await postAction();
}
catch (Exception ex)
{
exception = ex;
}
finally
{
finalAction(exception);
}
}
public static async Task<T> AwaitTaskWithPostActionAndFinallyAndGetResult<T>(Task<T> actualReturnValue, Func<object, Task> postAction,
Action<Exception> finalAction)
{
Exception exception = null;
try
{
var result = await actualReturnValue;
await postAction(result);
return result;
}
catch (Exception ex)
{
exception = ex;
throw;
}
finally
{
finalAction(exception);
}
}
public static object CallAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue,
Func<object, Task> action, Action<Exception> finalAction)
{
return typeof(InternalAsyncHelper)
.GetMethod("AwaitTaskWithPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static)
.MakeGenericMethod(taskReturnType)
.Invoke(null, new object[] {actualReturnValue, action, finalAction});
}
}
}