Commit c66528d3 authored by Maodashu's avatar Maodashu

update

parent 8e98bf3e
[*.cs]
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = silent
......@@ -3,7 +3,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31911.196
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixOne.SlackApi.WebApi", "MatrixOne.SlackApi.WebApi\MatrixOne.SlackApi.WebApi.csproj", "{9D287115-3EAB-4945-A576-684551B487A4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MatrixOne.SlackApi.WebApi", "MatrixOne.SlackApi.WebApi\MatrixOne.SlackApi.WebApi.csproj", "{9D287115-3EAB-4945-A576-684551B487A4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E3B746BD-92EE-43AE-8002-C158A1F7A038}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
......
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace MatrixOne.SlackApi.WebApi.Controllers
{
/// <summary>
///
/// </summary>
public class BaseController : Controller
{
protected readonly ILogger _logger;
protected readonly static Dictionary<string, long> requestSum = new Dictionary<string, long>();
public override void OnActionExecuting(ActionExecutingContext context)
{
lock (requestSum)
{
string url = context.HttpContext.Request.Path.Value;
string[] urls = url.Split('/');
int i = 0;
foreach (string seg in urls)
{
url += $"{seg}/";
i++;
if (i > 4) break;
}
if (requestSum.ContainsKey(url))
{
requestSum[url]++;
}
else
{
requestSum.Add(url, 1);
}
}
base.OnActionExecuting(context);
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="logger"></param>
public BaseController(ILogger logger)
{
_logger = logger;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
protected string GetLanguage()
{
if (HttpContext.Request.Headers.TryGetValue("Accept-Language", out Microsoft.Extensions.Primitives.StringValues language))
{
if (language.ToString().IndexOf("zh-CN") < 0)
language = "en-US";
else
language = "zh-CN";
}
return language;
}
/// <summary>
/// 临时用的控制台密码
/// </summary>
protected readonly Dictionary<string, string> _accountList = new Dictionary<string, string> {
{"634983d81a62d80dc7b4e7b61d371096","nixiaode"}
};
protected string MakeToken(string psw)
{
byte[] bytes = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(psw + HttpContext.Connection.RemoteIpAddress.ToString()));
string token = "";
foreach (byte b in bytes)
{
token += b.ToString("x2");
}
return token;
}
protected bool ValidToken(string token)
{
//_logger.LogError($"token 验证 {token}|{MakeToken("nixiaode")}");
return token.Equals(MakeToken("nixiaode"));
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using Swashbuckle.AspNetCore.Annotations;
namespace MatrixOne.SlackApi.WebApi.Controllers
{
/// <summary>
///
/// </summary>
[Produces("application/json")]
[Route("api/v2/[controller]")]
public class ConsoleController : BaseController
{
/// <summary>
///
/// </summary>
public ConsoleController(
IConfiguration cfg,
ILogger<ConsoleController> logger
) : base(logger)
{
}
/// <summary>
/// 用户登录
/// </summary>
/// <returns></returns>
[SwaggerOperation(Tags = new[] { "日志管理接口" })]
[ProducesResponseType(typeof(bool), 200)]
[HttpPost("Login")]
public IActionResult Login(string code = "")
{
var ret = new
{
result = "fail",
token = ""
};
if (!string.IsNullOrEmpty(code))
{
//TODO通过MD5编码的code获取密码
if (_accountList.ContainsKey(code))
{
string psw = _accountList[code];//临时的
ret = new
{
result = "ok",
token = MakeToken(psw)
};
}
}
return Ok(ret);
}
}
}
\ No newline at end of file

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using Swashbuckle.AspNetCore.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MatrixOne.SlackApi.WebApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class DebugController : BaseController
{
public DebugController(
ILogger<DebugController> logger
) : base(logger)
{
}
/// <summary>
/// 设置调试日志开关
/// </summary>
/// <param name="on">是否打开</param>
/// <returns></returns>
[SwaggerOperation(Tags = new[] { "交易中心 - 调试接口" })]
[ProducesResponseType(typeof(string), 200)]
[ProducesResponseType(typeof(string), 401)]
[ProducesResponseType(typeof(string), 429)]
[HttpGet("SetDebuglogging")]
public IActionResult SetDebuglogging(bool on)
{
string result = null;
try
{
string a = on ? "ON" : "OFF";
Environment.SetEnvironmentVariable("debuglogging", a);
result = Environment.GetEnvironmentVariable("debuglogging");
}
catch (Exception e)
{
_logger.LogError($"[设置调试日志开关] SetDebuglogging 异常: {e.Message}\n{e.StackTrace}");
}
return Ok(result);
}
/// <summary>
/// 查看调试日志开关
/// </summary>
/// <returns></returns>
[SwaggerOperation(Tags = new[] { "交易中心 - 调试接口" })]
[ProducesResponseType(typeof(string), 200)]
[ProducesResponseType(typeof(string), 401)]
[ProducesResponseType(typeof(string), 429)]
[HttpGet("GetDebuglogging")]
public IActionResult GetDebuglogging()
{
string result = null;
try
{
result= Environment.GetEnvironmentVariable("debuglogging");
}
catch (Exception e)
{
_logger.LogError($"[查看调试日志开关] GetDebuglogging 异常: {e.Message}\n{e.StackTrace}");
}
return Ok(result);
}
/// <summary>
/// 查看接口调用统计
/// </summary>
/// <returns></returns>
[SwaggerOperation(Tags = new[] { "交易中心 - 调试接口" })]
[ProducesResponseType(typeof(string), 200)]
[ProducesResponseType(typeof(string), 401)]
[ProducesResponseType(typeof(string), 429)]
[HttpGet("GetRequestSum")]
public JObject GetRequestSum()
{
JObject result = new JObject();
try
{
lock (requestSum)
{
result = JObject.FromObject(requestSum);
}
}
catch (Exception e)
{
_logger.LogError($"[查看调试日志开关] GetDebuglogging 异常: {e.Message}\n{e.StackTrace}");
}
return result;
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using Swashbuckle.AspNetCore.Annotations;
namespace MatrixOne.SlackApi.WebApi.Controllers
{
/// <summary>
///
/// </summary>
[Produces("application/json")]
[Route("api/v2/[controller]")]
public class LogsController : BaseController
{
/// <summary>
///
/// </summary>
public LogsController(
IConfiguration cfg,
ILogger<LogsController> logger
) : base(logger)
{
}
/// <summary>
/// 清除日志
/// </summary>
/// <returns></returns>
[SwaggerOperation(Tags = new[] { "日志管理接口" })]
[ProducesResponseType(typeof(bool), 200)]
[HttpGet("ClearLog")]
public IActionResult ClearLog(string afterfile = "")
{
if (!Request.Cookies.ContainsKey("MOToken") || !ValidToken(Request.Cookies["MOToken"].ToString()))
{
return Unauthorized();
}
int count = 0;
string logPath = Environment.CurrentDirectory + $"/logs";
List<string> filenames = Directory.GetFiles(logPath).ToList();
if (filenames.Count > 0)
{
List<FileInfo> fileInfos = new List<FileInfo>();
filenames.ForEach(x =>
{
FileInfo info = new FileInfo(x);
fileInfos.Add(info);
});
fileInfos.Sort((x, y) => x.LastWriteTime < y.LastWriteTime ? 1 : -1);
if (!string.IsNullOrEmpty(afterfile))
{
int idx = fileInfos.FindIndex(x => x.Name.Equals(afterfile + ".log"));
if (idx != -1)
{
fileInfos.RemoveRange(0, idx + 1);
}
else
{
//错误的afterfile将导致删除失败
return Ok("Failed:file name error");
}
}
foreach (var info in fileInfos)
{
try
{
info.Delete();
count++;
}
catch { }
}
}
return Ok($"Success:{count}");
}
/// <summary>
/// 日志查看
/// </summary>
/// <returns></returns>
[SwaggerOperation(Tags = new[] { "日志管理接口" })]
[ProducesResponseType(typeof(bool), 200)]
[HttpGet("GetLog")]
public IActionResult GetLog(string basefile = "", int type = 0)
{
if (!Request.Cookies.ContainsKey("MOToken") || !ValidToken(Request.Cookies["MOToken"].ToString()))
{
return Unauthorized();
}
JObject ret = new JObject
{
{ "file", "" },
{ "logs", new JArray() }
};
FileInfo fileInfo = null;
string logPath = Environment.CurrentDirectory + $"/logs";
List<string> filenames = Directory.GetFiles(logPath).Where(x => !x.Contains("skyapm")).ToList();
if (filenames.Count > 0)
{
List<FileInfo> fileInfos = new List<FileInfo>();
filenames.ForEach(x =>
{
FileInfo info = new FileInfo(x);
fileInfos.Add(info);
});
fileInfos.Sort((x, y) => x.LastWriteTime < y.LastWriteTime ? 1 : -1);
int idx = 0;
if (!string.IsNullOrEmpty(basefile))
{
idx = fileInfos.FindIndex(x => x.Name.Equals(basefile + ".log"));
}
if (idx != -1)
{
fileInfo = null;
if (type == -1 && idx != 0)
{
fileInfo = fileInfos[idx - 1];
}
if (type == 1 && idx != fileInfos.Count - 1)
{
fileInfo = fileInfos[idx + 1];
}
if (type == 0)
{
fileInfo = fileInfos[idx];
}
}
}
if (fileInfo != null)
{
TextReader reader = null;
//while (reader == null)
//{
try
{
reader = fileInfo.OpenText();
}
catch (Exception e)
{
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss:ffff}][LOG] 日志文件读取异常 file:{fileInfo.Name}:{e.Message}\n{e.StackTrace}");
}
//}
ret["file"] = fileInfo.Name.Replace(".log", "");
string line;
while ((line = reader.ReadLine()) != null)
{
try
{
((JArray)ret["logs"]).Add(JObject.Parse(line));
}
catch (Exception e)
{
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss:ffff}][LOG] 日志文件读取异常 file:{fileInfo.Name}:{e.Message}\n{e.StackTrace}");
}
}
reader.Close();
}
return Ok(ret);
}
[SwaggerOperation(Tags = new[] { "日志管理接口" })]
[ProducesResponseType(typeof(bool), 200)]
[HttpGet("GetLogBlackLlist")]
public IActionResult GetLogBlackLlist()
{
string jsonStr = "[]";
try
{
jsonStr = System.IO.File.ReadAllText("blackList_Jobs.json");
}
catch (Exception e)
{
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss:ffff}][LOG] 本地日志过滤信息读取异常,将重新建立:{e.Message}\n{e.StackTrace}");
System.IO.File.WriteAllText("blackList_Jobs.json", jsonStr);
}
JArray blacklist = JArray.Parse(jsonStr);
return Ok(blacklist);
}
[SwaggerOperation(Tags = new[] { "日志管理接口" })]
[ProducesResponseType(typeof(bool), 200)]
[HttpPost("SetLogBlackLlist")]
public IActionResult SetLogBlackLlist([FromForm] string bl)
{
System.IO.File.WriteAllText("blackList_Jobs.json", bl);
return Ok("ok");
}
}
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>MatrixOne.SlackApi.WebApi.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NLog" Version="4.7.12" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.14.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.2.3" />
</ItemGroup>
......
<?xml version="1.0"?>
<doc>
<assembly>
<name>MatrixOne.SlackApi.WebApi</name>
</assembly>
<members>
<member name="T:MatrixOne.SlackApi.WebApi.Controllers.BaseController">
<summary>
</summary>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.BaseController.#ctor(Microsoft.Extensions.Logging.ILogger)">
<summary>
构造函数
</summary>
<param name="logger"></param>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.BaseController.GetLanguage">
<summary>
</summary>
<returns></returns>
</member>
<member name="F:MatrixOne.SlackApi.WebApi.Controllers.BaseController._accountList">
<summary>
临时用的控制台密码
</summary>
</member>
<member name="T:MatrixOne.SlackApi.WebApi.Controllers.ConsoleController">
<summary>
</summary>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.ConsoleController.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{MatrixOne.SlackApi.WebApi.Controllers.ConsoleController})">
<summary>
</summary>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.ConsoleController.Login(System.String)">
<summary>
用户登录
</summary>
<returns></returns>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.DebugController.SetDebuglogging(System.Boolean)">
<summary>
设置调试日志开关
</summary>
<param name="on">是否打开</param>
<returns></returns>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.DebugController.GetDebuglogging">
<summary>
查看调试日志开关
</summary>
<returns></returns>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.DebugController.GetRequestSum">
<summary>
查看接口调用统计
</summary>
<returns></returns>
</member>
<member name="T:MatrixOne.SlackApi.WebApi.Controllers.LogsController">
<summary>
</summary>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.LogsController.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{MatrixOne.SlackApi.WebApi.Controllers.LogsController})">
<summary>
</summary>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.LogsController.ClearLog(System.String)">
<summary>
清除日志
</summary>
<returns></returns>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Controllers.LogsController.GetLog(System.String,System.Int32)">
<summary>
日志查看
</summary>
<returns></returns>
</member>
<member name="T:MatrixOne.SlackApi.WebApi.Services.EnumDocumentFilter">
<summary>
向Swagger添加枚举值说明
</summary>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Services.EnumDocumentFilter.DescribeEnum(System.Type)">
<summary>
描述枚举
</summary>
<param name="enums"></param>
<returns></returns>
</member>
<member name="M:MatrixOne.SlackApi.WebApi.Services.EnumDocumentFilter.GetDescription(System.Type,System.Object)">
<summary>
获取描述
</summary>
<param name="t"></param>
<param name="value"></param>
<returns></returns>
</member>
<member name="F:MatrixOne.SlackApi.WebApi.Services.MOLogTarget.file_interval_minutes">
<summary>
日志文件间隔,分钟
</summary>
</member>
<member name="F:MatrixOne.SlackApi.WebApi.Services.MOLogTarget.clean_expire_hours">
<summary>
日志文件过期时间,小时
</summary>
</member>
<member name="F:MatrixOne.SlackApi.WebApi.Services.MOLogTarget.clean_interval_minutes">
<summary>
日志文件清理周期,分钟
</summary>
</member>
</members>
</doc>
using MatrixOne.SlackApi.WebApi.Services;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using NLog.Targets;
using NLog.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
......@@ -12,16 +20,38 @@ namespace MatrixOne.SlackApi.WebApi
{
public class Program
{
private static IConfiguration configuration;
public static void Main(string[] args)
{
Target.Register<MOLogTarget>("MOLog");
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
public static IHostBuilder CreateHostBuilder(string[] args)
{
Console.WriteLine("获取系统环境变量");
var env = Environment.GetEnvironmentVariable("MATRIXONE_ENVIRONMENT");
if (string.IsNullOrEmpty(env))
env = "local";
Console.WriteLine($"运行环境 (MATRIXONE_ENVIRONMENT):{env}");
NLogBuilder.ConfigureNLog($"nlog.{env}.config");
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.SetBasePath(Directory.GetCurrentDirectory());
configuration = configurationBuilder.AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: true)
.AddCommandLine(args)
.Build();
IHostBuilder builder = Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseUrls("http://*:6001");
webBuilder.UseNLog();
webBuilder.UseConfiguration(configuration);
webBuilder.UseStartup<Startup>();
});
return builder;
}
}
}
{
"$schema": "http://json.schemastore.org/launchsettings.json",
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
......@@ -8,6 +7,7 @@
"sslPort": 0
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
......@@ -20,11 +20,11 @@
"MatrixOne.SlackApi.WebApi": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "http://localhost:5000",
"launchUrl": "index.html",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"applicationUrl": "http://localhost:6001"
}
}
}
\ No newline at end of file

using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MatrixOne.SlackApi.WebApi.Services
{
public class ApiExplorerGroupConvention : IControllerModelConvention
{
private readonly Dictionary<string, List<string>> GroupDict = new Dictionary<string, List<string>> {
{ "debug",new List<string>{
"Debug"
} },
{ "system",new List<string>{
"System",
"Logs",
"Console"
} },
{ "other",new List<string>{
} }
};
public void Apply(ControllerModel controller)
{
string groupName = "other";
foreach (var kv in GroupDict)
{
if (GroupDict[kv.Key].Contains(controller.ControllerName))
{
groupName = kv.Key;
break;
}
}
controller.ApiExplorer.GroupName = groupName;
}
}
}

using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
namespace MatrixOne.SlackApi.WebApi.Services
{
/// <summary>
/// 向Swagger添加枚举值说明
/// </summary>
public class EnumDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
IEnumerable<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(x => x.FullName.ToLower().StartsWith("matrixone"));
// 向结果模型添加枚举描述
foreach (var schemaDictionaryItem in swaggerDoc.Components.Schemas)
{
var schema = schemaDictionaryItem.Value;
if (schema.Enum.Count > 0)
{
Type enumType = null;
foreach (Assembly assembly in assemblies)
{
enumType = assembly.GetTypes().FirstOrDefault(x => x.IsEnum && x.Name.Equals(schemaDictionaryItem.Key));
if (enumType != null && enumType.FullName != null && enumType.FullName.ToLower().StartsWith("matrixone")) break;
}
if (enumType != null && enumType.GetEnumUnderlyingType().Equals(typeof(Int32))) schema.Description += DescribeEnum(enumType);
}
}
}
/// <summary>
/// 描述枚举
/// </summary>
/// <param name="enums"></param>
/// <returns></returns>
private static string DescribeEnum(Type enumType)
{
var enumDescriptions = new List<string>();
foreach (object field in enumType.GetEnumValues())
{
int value = (int)Enum.Parse(enumType, field.ToString());
enumDescriptions.Add($"<br>[{value} = {Enum.GetName(enumType, value)}{GetDescription(enumType, value)}]");
}
return $"{Environment.NewLine}{string.Join(Environment.NewLine, enumDescriptions)}";
}
/// <summary>
/// 获取描述
/// </summary>
/// <param name="t"></param>
/// <param name="value"></param>
/// <returns></returns>
private static string GetDescription(Type t, object value)
{
foreach (MemberInfo mInfo in t.GetMembers())
{
if (mInfo.Name == t.GetEnumName(value))
{
foreach (Attribute attr in Attribute.GetCustomAttributes(mInfo))
{
if (attr.GetType() == typeof(DescriptionAttribute))
{
return ((DescriptionAttribute)attr).Description;
}
}
}
}
return string.Empty;
}
}
}

using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using NLog;
using NLog.Config;
using NLog.Layouts;
using NLog.Targets;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MatrixOne.SlackApi.WebApi.Services
{
[Target("MOLog")]
public sealed class MOLogTarget : TargetWithLayout
{
private readonly Queue<string> msgpool;
private readonly Task writtingTask;
private readonly CancellationTokenSource cTokenSource;
private readonly CancellationToken cToken;
private readonly string logPath;
private DateTime last_file_clean_time;
private readonly List<string> blackList_Jobs;
/// <summary>
/// 日志文件间隔,分钟
/// </summary>
private readonly int file_interval_minutes = 10;
/// <summary>
/// 日志文件过期时间,小时
/// </summary>
private readonly int clean_expire_hours = 12;
/// <summary>
/// 日志文件清理周期,分钟
/// </summary>
private readonly int clean_interval_minutes = 20;
public MOLogTarget()
{
last_file_clean_time = DateTime.UtcNow;
Host = "localhost";
logPath = Environment.CurrentDirectory + $"/logs";
msgpool = new Queue<string>(1024);
cTokenSource = new CancellationTokenSource();
blackList_Jobs = new List<string>();
cToken = cTokenSource.Token;
writtingTask = new Task(WriteFile, cToken);
if (!Directory.Exists(logPath)) Directory.CreateDirectory(logPath);
}
[RequiredParameter]
public Layout Host { get; set; }
protected override void Write(LogEventInfo logEvent)
{
string logMessage = RenderLogEvent(Layout, logEvent);
msgpool.Enqueue($"{logMessage}");
//清理clean_expire_days日之前的日志文件
try
{
if (last_file_clean_time.AddMilliseconds(clean_interval_minutes) < DateTime.UtcNow)
{
last_file_clean_time = DateTime.UtcNow;
Task.Run(() =>
{
string[] files = Directory.GetFiles(logPath);
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss:ffff}][LOG]Logs cleaning start");
int removed_count = 0;
Array.ForEach(files, s =>
{
FileInfo fileInfo = new FileInfo(s);
if (fileInfo.LastWriteTimeUtc.AddHours(clean_expire_hours) < DateTime.UtcNow)
{
try
{
fileInfo.Delete();
removed_count++;
}
catch (Exception e)
{
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss:ffff}][LOG]IO error when remove {fileInfo.Name}:{e.Message}\n{e.StackTrace}");
}
}
});
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss:ffff}][LOG]Logs cleaning finished,{removed_count} files removed.");
});
}
}
catch (Exception e)
{
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss:ffff}][LOG]IO error:{e.Message}\n{e.StackTrace}");
}
}
private void WriteFile()
{
while (true)
{
lock (msgpool)
{
ReloadBL();
bool skip = false;
FileStream fs = null;
try
{
DateTime now = DateTime.Now;
int pf = file_interval_minutes * (now.Minute / file_interval_minutes);
string fname = $"/{DateTime.Now:MM-dd-HH}-{pf:G2}.log";
fs = File.Open(logPath + fname, FileMode.Append);
if (fs != null)
{
for (int i = 0; i < 1000; i++)
{
if (msgpool.Count == 0) break;
string str = msgpool.Dequeue();
JObject jsonData = JObject.Parse(str);
string msg = jsonData["message"].ToString();
//忽略黑名单内的业务流程标签
lock (blackList_Jobs)
{
skip = blackList_Jobs.Any(x => msg.Contains($"[{x}]"));
}
if (skip)
continue;
str += "\n";
fs.Write(Encoding.UTF8.GetBytes(str));
}
fs.Close();
}
}
catch (Exception e)
{
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss:ffff}][LOG] 日志文件写入异常:{e.Message}\n{e.StackTrace}");
}
}
Thread.Sleep(1000);
}
}
public void ReloadBL()
{
lock (blackList_Jobs)
{
try
{
string jsonStr = File.ReadAllText("blackList_Jobs.json");
JArray blacklist = JArray.Parse(jsonStr);
blackList_Jobs.Clear();
foreach (string key in blacklist)
{
blackList_Jobs.Add(key);
}
}
catch (Exception e)
{
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss:ffff}][LOG] 本地日志过滤信息读取异常,将重新建立:{e.Message}\n{e.StackTrace}");
JArray blacklist = JArray.FromObject(blackList_Jobs);
File.WriteAllText("blackList_Jobs.json", blacklist.ToString());
}
}
}
protected override void InitializeTarget()
{
base.InitializeTarget();
writtingTask.Start();
}
protected override void CloseTarget()
{
cTokenSource.Cancel();
writtingTask.Wait();
lock (blackList_Jobs)
{
JArray blacklist = JArray.FromObject(blackList_Jobs);
File.WriteAllText("blackList_Jobs.json", blacklist.ToString());
}
base.CloseTarget();
}
}
}
using MatrixOne.SlackApi.WebApi.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
......@@ -15,6 +20,7 @@ namespace MatrixOne.SlackApi.WebApi
{
public class Startup
{
private ILogger<Startup> _logger;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
......@@ -25,25 +31,135 @@ namespace MatrixOne.SlackApi.WebApi
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
if (Configuration["DebugLogging"] != null && Configuration["DebugLogging"].Equals("ON"))
{
Environment.SetEnvironmentVariable("debuglogging", "ON");
}
else
{
Environment.SetEnvironmentVariable("debuglogging", "OFF");
}
#region .Net Core 服务配置
//配置路由服务
services.AddRouting();
//配置控制器服务
services.AddControllers(options =>
{
options.Conventions.Add(new ApiExplorerGroupConvention());
})
.SetCompatibilityVersion(CompatibilityVersion.Latest); ;
//配置 Cors 服务
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
});
});
services.AddMvcCore();
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
//配置 Http客户端服务,注入HttpClientFactory
services.AddHttpClient("INTERNAL");//内网专用
#endregion
#region 其他服务配置
//swagger配置
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("debug", new OpenApiInfo
{
if (env.IsDevelopment())
Version = "v2",
Description = "交易中心 WebsocketHub Swagger接口文档 V2 基于Asp.net Core 3.1",
Title = "调试接口",
});
c.SwaggerDoc("system", new OpenApiInfo
{
app.UseDeveloperExceptionPage();
Version = "v2",
Description = "交易中心 WebsocketHub Swagger接口文档 V2 基于Asp.net Core 3.1",
Title = "系统管理接口",
});
c.SwaggerDoc("other", new OpenApiInfo
{
Version = "v2",
Description = "交易中心 WebsocketHub Swagger接口文档 V2 基于Asp.net Core 3.1",
Title = "其他接口",
});
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"MatrixOne.SlackApi.WebApi.xml"));
c.EnableAnnotations();
c.DocumentFilter<EnumDocumentFilter>();
});
#endregion
}
public void Configure(
ILogger<Startup> logger,
IApplicationBuilder app,
IWebHostEnvironment env,
IHostApplicationLifetime appLifetime)
{
_logger = logger;
appLifetime.ApplicationStarted.Register(OnStarted);
appLifetime.ApplicationStopping.Register(OnStopping);
appLifetime.ApplicationStopped.Register(OnStopped);
app.UseDefaultFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "wwwroot")),
RequestPath = ""
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
//配置swagger
var path = Configuration.GetSection("Swagger:VirtualPath").Value;
string scheme = "https";
#if DEBUG
scheme = "http";//本地测试使用http协议
#endif
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((doc, req) => doc.Servers = new List<OpenApiServer> {
new OpenApiServer { Url = $"{scheme}://{req.Host.Value}{path}" }
});
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{path}/swagger/debug/swagger.json", "调试接口");
c.SwaggerEndpoint($"{path}/swagger/system/swagger.json", "服务管理");
c.SwaggerEndpoint($"{path}/swagger/other/swagger.json", "其他接口");
});
}
private void OnStarted()
{
_logger.LogWarning($"[服务器状态]WebHost 已经启动({DateTime.Now:yyyy-MM-dd HH:mm:ss:ffff})");
}
private void OnStopping()
{
_logger.LogWarning($"[服务器状态]WebHost 开始停止({DateTime.Now:yyyy-MM-dd HH:mm:ss:ffff})");
}
private void OnStopped()
{
_logger.LogWarning($"[服务器状态]WebHost 已经关闭({DateTime.Now:yyyy-MM-dd HH:mm:ss:ffff})");
}
}
}
......@@ -5,5 +5,6 @@
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
},
"DebugLogging": "ON"
}
......@@ -5,5 +5,6 @@
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
},
"DebugLogging": "ON"
}
[
]
\ No newline at end of file
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target name="network" xsi:type="Network" address="tcp://172.10.26.144:9205" keepConnection="false" maxConnections="10">
<layout xsi:type="JsonLayout">
<attribute name="application" layout="slackapi_s" />
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
<attribute name="exception" layout="${exception:format=tostring}" />
</layout>
</target>
<target name="MOLog" xsi:type="MOLog">
<layout xsi:type="JsonLayout">
<attribute name="id" layout="${sequenceid}" />
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
<attribute name="exception" layout="${exception:format=tostring}" />
</layout>
</target>
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxLevel="Info" final="true" />
<logger name="IdentityServer4.*" maxLevel="Info" final="true" />
<logger name="SupWin.IdentityServer4.*" maxLevel="Info" final="true" />
<logger name="System.Net.Http.*" maxLevel="Info" final="true" />
<logger name="Hangfire.*" maxLevel="Info" final="true" />
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Warn" writeTo="network" />
<logger name="*" minlevel="Info" writeTo="MOLog" />
<!-- BlackHole without writeTo -->
</rules>
</nlog>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<target name="MOLog" xsi:type="MOLog">
<layout xsi:type="JsonLayout">
<attribute name="id" layout="${sequenceid}" />
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
<attribute name="exception" layout="${exception:format=tostring}" />
</layout>
</target>
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxLevel="Info" final="true" />
<logger name="IdentityServer4.*" maxLevel="Info" final="true" />
<logger name="SupWin.IdentityServer4.*" maxLevel="Info" final="true" />
<logger name="System.Net.Http.*" maxLevel="Info" final="true" />
<logger name="Hangfire.*" maxLevel="Info" final="true" />
<logger name="MORC.*" maxLevel="Info" final="true" />
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Warn" writeTo="network" />
<logger name="*" minlevel="Trace" writeTo="MOLog" />
<!-- BlackHole without writeTo -->
</rules>
</nlog>
\ No newline at end of file
body {
background-color: #1e1e1e;
margin: 0px;
padding: 5px;
font-size: 16px;
font-family: Courier New, Courier, monospace
}
.border {
border: 1px solid #686868;
display: flex;
justify-content: center;
}
.progress_frame {
visibility: hidden;
position: absolute;
top: 0;
left: 0;
background-color: RGBA(0,0,0,0.8);
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.loginscreen {
visibility: hidden;
position: absolute;
top: 0;
left: 0;
background-color: #1e1e1e;
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.errorscreen {
visibility: hidden;
position: absolute;
top: 0;
left: 0;
background-color: #1e1e1e;
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
@keyframes progress_bar_anim {
0% {
background-image: radial-gradient(#007acc,5%,#007acc,65%,#007acc00);
}
30% {
background-image: radial-gradient(#007acc,5%,#007acc,70%,#007acc00);
}
60% {
background-image: radial-gradient(#007acc,5%,#007acc,75%,#007acc00);
}
90% {
background-image: radial-gradient(#007acc,5%,#007acc,80%,#007acc00);
}
100% {
background-image: radial-gradient(#007acc,5%,#007acc,85%,#007acc00);
}
}
.progress_bar {
background-image: radial-gradient(#007acc,15%,#007acc,45%,#007acc00);
animation-name: progress_bar_anim;
animation-duration: 0.5s;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-direction: alternate;
border-radius: 32px;
margin-bottom: 16px;
height: 64px;
width: 64px;
}
.progress_text {
margin-bottom: 64px;
color: #ffffff;
font-size: 16px;
}
.frame {
margin: 5px;
padding: 5px;
}
.loginframe {
height: 80px;
margin-bottom: 120px;
padding-top: 15px;
padding-bottom: 15px;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.darkBackground {
background-color: #252526;
}
.titleText {
font-size: 18px;
color: #ffffff;
}
.whiteText {
color: #ffffff;
}
.grayText {
color: #686868;
}
.row {
display: inline-flex;
justify-content: start;
align-items: center;
flex-direction: row;
}
.filterbar {
width: 100%;
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: start;
}
.edit_psw {
min-width: 100px;
border: 1px solid #007acc;
margin: 1px;
margin-left: 5px;
margin-right: 5px;
padding: 1px;
}
.edit_in_or {
min-width: 100px;
border: 1px solid #007acc;
margin: 1px;
margin-left: 5px;
margin-right: 5px;
padding: 1px;
}
.edit_ex_or {
min-width: 100px;
border: 1px solid #007acc;
margin: 1px;
margin-left: 5px;
margin-right: 5px;
padding: 1px;
}
.loglist {
display: flex;
flex-direction: column;
justify-content: start;
min-height: 85vh;
height: 85vh;
width: 100%;
}
.scrollarea {
overflow-y: scroll;
width: 100%;
height: 100%;
}
.logrow {
width: 99%;
display: inline-flex;
flex-direction: row;
justify-content: start;
border-bottom: 1px solid #686868;
}
.logitem {
display: inline-block;
text-align: start;
font-size: 14px;
}
.button {
padding: 5px;
margin: 5px;
border: 1px solid #686868;
background-color: #1e1e1e;
display: flex;
justify-content: center;
cursor: default;
user-select: none;
}
.button:hover {
border: 1px solid #007acc;
background-color: #252526;
}
.button:active {
background-color: #007acc;
border: 1px solid #007acc;
}
.wide_button {
padding: 1px;
padding-left: 30px;
padding-right: 30px;
}
.smallbutton {
padding: 1px;
padding-left: 8px;
padding-right: 8px;
margin: 1px;
border: 1px solid #686868;
background-color: #1e1e1e;
display: flex;
color: #ffffff;
justify-content: center;
cursor: default;
user-select: none;
}
.smallbutton:hover {
border: 1px solid #007acc;
background-color: #252526;
}
.smallbutton:active {
background-color: #007acc;
border: 1px solid #007acc;
}
.disablebutton {
padding: 1px;
padding-left: 8px;
padding-right: 8px;
margin: 1px;
border: 1px solid #686868;
background-color: #1e1e1e;
color: #686868;
display: flex;
justify-content: center;
cursor: default;
user-select: none;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>SlackApi Console</title>
<script type="text/javascript" src="js/jquery/jquery.min.js"></script>
<script type="text/javascript" src="js/jquery/jquery.cookie.min.js"></script>
<script type="text/javascript" src="js/jquery/jquery.md5.js"></script>
<script type="text/javascript" src="js/vue/vue.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<link rel="stylesheet" href="css/mo.css" />
</head>
<body>
<div class="row">
<div class="frame titleText">WebsocketHub Console</div>
<div id="btn_swagger" class="button whiteText">Swagger</div>
<div id="btn_clean_all_logs" class="button whiteText">Clean all logs</div>
<div id="btn_clean_unloaded_logs" class="button whiteText">Clean unloaded logs</div>
<div id="field_file" class="edit_psw whiteText" contenteditable="true"></div>
<div id="btn_changedate" class="button whiteText">GO</div>
</div>
<div class="frame border whiteText darkBackground" style="flex-direction:column;">
<div class="filterbar">
filters:
include
<div class="edit_in_or" contenteditable="true"></div>
<div class="edit_in_or" contenteditable="true"></div>
<div class="edit_in_or" contenteditable="true"></div>
and exclude
<div class="edit_ex_or" contenteditable="true"></div>
<div class="edit_ex_or" contenteditable="true"></div>
<div class="edit_ex_or" contenteditable="true"></div>
<div id="btn_apply" class="smallbutton whiteText" style="text-align:center;">Apply</div>
<div id="btn_clean" class="smallbutton whiteText" style="text-align:center;">Clean</div>
</div>
<div id="list_logs" class="loglist">
<div class="row">
<div v-on:click="reset" class="button wide_button whiteText">Reset</div>
</div>
<div id="btn_loadnext" v-on:click="loadnext" class="button whiteText" style="text-align:center;">Load next >></div>
<div class="logrow">
<div class="logitem" style="min-width:100px;">Id</div>
<div class="logitem" style="min-width:260px;">Time</div>
<div class="logitem" style="min-width:60px;">Level</div>
<div class="logitem" style="width:calc(100% - 330px);text-align: center;">Message</div>
</div>
<div class="scrollarea">
<div class="logrow" v-for="log in logs">
<div class="logitem" style="color:#007acc;min-width:100px;">{{ log.id }}^</div>
<div class="logitem" style="color:#007acc;min-width:260px;">{{ log.time }}^</div>
<div class="logitem" style="color:#007acc;min-width:60px;">{{ log.level }}^</div>
<div class="logitem" style="width:calc(100% - 330px);word-wrap:break-word;overflow-wrap:break-word;white-space:pre-line;">{{ log.message }}</div>
</div>
</div>
<div id="btn_loadprevious" v-on:click="loadprevious" class="button whiteText" style="text-align:center;">Load previous >></div>
</div>
</div>
<div class="progress_frame">
<div class="progress_bar"></div>
<div class="progress_text">LOADING</div>
</div>
<div class="loginscreen">
<div class="frame border whiteText darkBackground loginframe">
<div>请输入密码</div>
<input id="input_psw" maxlength="18" type="password" class="edit_psw whiteText darkBackground" style="margin: 10px;" />
<div id="btn_login" class="disablebutton" style="text-align:center;margin:10px;">Login</div>
<div id="field_pswerror" style="color:red;font-size:12px;margin:5px;height:20px;"></div>
</div>
</div>
<div class="errorscreen">
<div class="frame border whiteText darkBackground loginframe">
<div style="color:red;margin-left:50px;margin-right:50px;">出错啦!</div>
<div id="field_error" style="color:red;"></div>
<div id="btn_refresh" class="smallbutton" style="text-align:center;margin:10px;">刷新</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
var BaseUrl = window.location.hostname
if (BaseUrl == "localhost")
BaseUrl = window.location.protocol + "//" + window.location.host + "/"
else
BaseUrl = window.location.protocol + "//" + window.location.host + "/tradecenterwsh/"
var MOToken = $.cookie('MOToken');
var loadedlogs = [];
var loglistVue = {};
var lastfile = "";
var firstfile = "";
$(document).ready(() => {
$("#btn_swagger").click((e) => {
window.open('swagger/index.html', '_blank');
})
$("#btn_clean_all_logs").click((e) => {
var psw = $("#input_psw").val()
var url = BaseUrl + "api/v2/Logs/ClearLog"
$(".progress_frame").css("visibility", "visible")
$.ajax({
url: url,
headers: {
"MOToken": MOToken
},
success: e => {
loadedlogs = [];
lastfile = "";
firstfile = "";
loglistVue.logs = [];
loadlogs();
},
error: (e) => {
neterror(e)
},
complete: e => {
$(".progress_frame").css("visibility", "hidden")
}
})
})
$("#btn_clean_unloaded_logs").click((e) => {
var psw = $("#input_psw").val()
var url = BaseUrl + "api/v2/Logs/ClearLog?afterfile=" + lastfile
$(".progress_frame").css("visibility", "visible")
$.ajax({
url: url,
headers: {
"MOToken": MOToken
},
success: e => {
loadedlogs = [];
lastfile = "";
firstfile = "";
loglistVue.logs = [];
loadlogs();
},
error: (e) => {
neterror(e)
},
complete: e => {
$(".progress_frame").css("visibility", "hidden")
}
})
})
$("#btn_apply").click((e) => {
updatelist()
})
$("#btn_changedate").click((e) => {
if ($("#field_file").text() != "") {
loadedlogs = [];
lastfile = "";
firstfile = "";
loglistVue.logs = [];
loadlogs($("#field_file").text(), 0);
}
})
$("#btn_clean").click((e) => {
$(".edit_in_or").text("");
$(".edit_ex_or").text("");
loglistVue.logs = loadedlogs
})
$("#btn_login").click((e) => {
if ($("#btn_login").hasClass("smallbutton")) {
login($("#input_psw").val());
}
})
$("#input_psw").on("keydown", (e) => {
if (e.keyCode == 13 && $("#input_psw").val().trim() != "") {
login($("#input_psw").val());
}
})
$("#input_psw").on("input propertychange", (e) => {
var text = $("#input_psw").val().trim()
if (text != "") {
$("#btn_login").removeClass("disablebutton")
$("#btn_login").addClass("smallbutton")
$("#field_pswerror").text("")
} else {
$("#btn_login").removeClass("smallbutton")
$("#btn_login").addClass("disablebutton")
}
})
loglistVue = new Vue({
el: '#list_logs',
data: {
logs: []
},
methods: {
reset: () => {
loadedlogs = [];
logs = [];
loadlogs();
},
loadnext: () => {
loadlogs(lastfile, -1)
},
loadprevious: () => {
loadlogs(firstfile, 1)
}
}
})
if (undefined == MOToken || MOToken == "") {
$(".loginscreen").css("visibility", "visible")
$("#input_psw").focus();
}
else {
loadlogs();
}
})
function updatelist() {
var newlogs = []
var filterIn = []
var filterEx = []
var filter = $(".edit_in_or").toArray();
for (var idx in filter) {
var kw = filter[idx].innerText.trim();
if (kw != "") {
filterIn.push(kw)
}
}
filter = $(".edit_ex_or").toArray();
for (var idx in filter) {
var kw = filter[idx].innerText.trim();
if (kw != "") {
filterEx.push(kw)
}
}
loadedlogs.forEach(x => {
var hit = false;
for (var idx in filterIn) {
if (x.message.includes(filterIn[idx])) {
hit = true;
break;
}
}
hit = hit || filterIn.length == 0;
if (hit) {
for (var idx in filterEx) {
if (x.message.includes(filterEx[idx])) {
hit = false;
break;
}
}
if (hit) {
newlogs.push(x)
}
}
})
loglistVue.logs = newlogs
}
function loadlogs(basefile, type) {
var url = BaseUrl + "api/v2/Logs/GetLog"
if (undefined == type) {
type = 0
}
if (undefined == basefile) {
basefile = ""
}
url += "?basefile=" + basefile + "&type=" + type
$(".progress_frame").css("visibility", "visible")
$.ajax({
url: url,
headers: {
"MOToken": MOToken
},
success: e => {
if (e.file != "") {
if (type < 0) {
lastfile = e.file;
loadedlogs = e.logs.reverse().concat(loadedlogs)
}
if (type > 0) {
$("#field_file").text(e.file)
firstfile = e.file;
loadedlogs = loadedlogs.concat(e.logs.reverse())
}
if (type == 0) {
console.log(e)
$("#field_file").text(e.file)
firstfile = e.file;
lastfile = e.file;
loadedlogs = loadedlogs.concat(e.logs.reverse())
}
updatelist();
}
},
error: (e) => {
neterror(e)
},
complete: e => {
$(".progress_frame").css("visibility", "hidden")
}
})
}
function neterror(e) {
switch (e.status) {
case 401:
{
$(".loginscreen").css("visibility", "visible")
}
break;
default:
{
$(".errorscreen").css("visibility", "visible")
}
break;
}
}
function login(psw) {
var md5psw = $.md5(psw)
var url = BaseUrl + "api/v2/Console/Login"
$(".progress_frame").css("visibility", "visible")
$.ajax({
url: url,
method: "POST",
data: { code: md5psw },
success: e => {
if (e.result == "ok") {
MOToken = e.token
$.cookie('MOToken', MOToken);
$(".loginscreen").css("visibility", "hidden")
loadlogs();
} else {
$("#input_psw").val("")
$("#field_pswerror").text("登录失败")
$("#btn_login").removeClass("smallbutton")
$("#btn_login").addClass("disablebutton")
}
},
error: (e) => {
neterror(e)
},
complete: e => {
$(".progress_frame").css("visibility", "hidden")
}
})
}
/*!
* jQuery Cookie Plugin v1.4.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (value !== undefined && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setTime(+t + days * 864e+5);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// Read
var result = key ? undefined : {};
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling $.cookie().
var cookies = document.cookie ? document.cookie.split('; ') : [];
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = parts.join('=');
if (key && key === name) {
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) === undefined) {
return false;
}
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));
/*! jquery.cookie v1.4.1 | MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?a(require("jquery")):a(jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=h.raw?b:e(b);return a.isFunction(c)?c(d):d}var g=/\+/g,h=a.cookie=function(e,g,i){if(void 0!==g&&!a.isFunction(g)){if(i=a.extend({},h.defaults,i),"number"==typeof i.expires){var j=i.expires,k=i.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};h.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))}});
\ No newline at end of file
/*
* jQuery MD5 Plugin 1.2.1
* https://github.com/blueimp/jQuery-MD5
*
* Copyright 2010, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://creativecommons.org/licenses/MIT/
*
* Based on
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
/*jslint bitwise: true */
/*global unescape, jQuery */
(function ($) {
'use strict';
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF),
msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt));
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn(q, a, b, x, s, t) {
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
}
function md5_ff(a, b, c, d, x, s, t) {
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t) {
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t) {
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t) {
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
/*
* Calculate the MD5 of an array of little-endian words, and a bit length.
*/
function binl_md5(x, len) {
/* append padding */
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var i, olda, oldb, oldc, oldd,
a = 1732584193,
b = -271733879,
c = -1732584194,
d = 271733878;
for (i = 0; i < x.length; i += 16) {
olda = a;
oldb = b;
oldc = c;
oldd = d;
a = md5_ff(a, b, c, d, x[i], 7, -680876936);
d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i], 20, -373897302);
a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
d = md5_hh(d, a, b, c, x[i], 11, -358537222);
c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i], 6, -198630844);
d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return [a, b, c, d];
}
/*
* Convert an array of little-endian words to a string
*/
function binl2rstr(input) {
var i,
output = '';
for (i = 0; i < input.length * 32; i += 8) {
output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
}
return output;
}
/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binl(input) {
var i,
output = [];
output[(input.length >> 2) - 1] = undefined;
for (i = 0; i < output.length; i += 1) {
output[i] = 0;
}
for (i = 0; i < input.length * 8; i += 8) {
output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
}
return output;
}
/*
* Calculate the MD5 of a raw string
*/
function rstr_md5(s) {
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}
/*
* Calculate the HMAC-MD5, of a key and some data (raw strings)
*/
function rstr_hmac_md5(key, data) {
var i,
bkey = rstr2binl(key),
ipad = [],
opad = [],
hash;
ipad[15] = opad[15] = undefined;
if (bkey.length > 16) {
bkey = binl_md5(bkey, key.length * 8);
}
for (i = 0; i < 16; i += 1) {
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}
/*
* Convert a raw string to a hex string
*/
function rstr2hex(input) {
var hex_tab = '0123456789abcdef',
output = '',
x,
i;
for (i = 0; i < input.length; i += 1) {
x = input.charCodeAt(i);
output += hex_tab.charAt((x >>> 4) & 0x0F) +
hex_tab.charAt(x & 0x0F);
}
return output;
}
/*
* Encode a string as utf-8
*/
function str2rstr_utf8(input) {
return unescape(encodeURIComponent(input));
}
/*
* Take string arguments and return either raw or hex encoded strings
*/
function raw_md5(s) {
return rstr_md5(str2rstr_utf8(s));
}
function hex_md5(s) {
return rstr2hex(raw_md5(s));
}
function raw_hmac_md5(k, d) {
return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d));
}
function hex_hmac_md5(k, d) {
return rstr2hex(raw_hmac_md5(k, d));
}
$.md5 = function (string, key, raw) {
if (!key) {
if (!raw) {
return hex_md5(string);
} else {
return raw_md5(string);
}
}
if (!raw) {
return hex_hmac_md5(key, string);
} else {
return raw_hmac_md5(key, string);
}
};
}(typeof jQuery === 'function' ? jQuery : this));
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment