committed by
GitHub
17 changed files with 730 additions and 3 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,22 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net9.0</TargetFramework> |
||||
|
<AssemblyName>LINGYUN.Abp.SystemInfo.HttpApi</AssemblyName> |
||||
|
<PackageId>LINGYUN.Abp.SystemInfo.HttpApi</PackageId> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" /> |
||||
|
<PackageReference Include="DotNetCore.CAP" /> |
||||
|
<PackageReference Include="StackExchange.Redis" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,17 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.SystemInfo; |
||||
|
|
||||
|
[DependsOn(typeof(AbpAspNetCoreMvcModule))] |
||||
|
public class AbpSystemInfoHttpApiModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
PreConfigure<IMvcBuilder>(mvcBuilder => |
||||
|
{ |
||||
|
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpSystemInfoHttpApiModule).Assembly); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.SystemInfo.Models; |
||||
|
/// <summary>
|
||||
|
/// 组件
|
||||
|
/// </summary>
|
||||
|
public class ComponentInfoModel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 组件名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 组件名称集合
|
||||
|
/// </summary>
|
||||
|
public ComponentKeyModel[] Keys { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 组件状态集合
|
||||
|
/// </summary>
|
||||
|
public Dictionary<string, object> Details { get; set; } |
||||
|
public ComponentInfoModel(string name, ComponentKeyModel[] keys, Dictionary<string, object> details) |
||||
|
{ |
||||
|
Name = name; |
||||
|
Keys = keys; |
||||
|
Details = details; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
namespace LINGYUN.Abp.SystemInfo.Models; |
||||
|
/// <summary>
|
||||
|
/// 组件名称
|
||||
|
/// </summary>
|
||||
|
public class ComponentKeyModel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 组件名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 组件显示名称
|
||||
|
/// </summary>
|
||||
|
public string DisplayName { get; set; } |
||||
|
public ComponentKeyModel(string name, string displayName) |
||||
|
{ |
||||
|
Name = name; |
||||
|
DisplayName = displayName; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
namespace LINGYUN.Abp.SystemInfo.Models; |
||||
|
|
||||
|
public class SystemInfoModel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 组件状态集合
|
||||
|
/// </summary>
|
||||
|
public ComponentInfoModel[] Components { get; set; } |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.SystemInfo.Permissions; |
||||
|
|
||||
|
public class SystemInfoPermissionDefinitionProvider : PermissionDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IPermissionDefinitionContext context) |
||||
|
{ |
||||
|
var systemInfoGroup = context.AddGroup( |
||||
|
SystemInfoPermissions.GroupName, |
||||
|
new FixedLocalizableString("系统详情")); |
||||
|
|
||||
|
systemInfoGroup.AddPermission( |
||||
|
SystemInfoPermissions.Default, |
||||
|
new FixedLocalizableString("获取系统详情"), |
||||
|
Volo.Abp.MultiTenancy.MultiTenancySides.Host) |
||||
|
.WithProviders(ClientPermissionValueProvider.ProviderName); // 使用客户端授权
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace LINGYUN.Abp.SystemInfo.Permissions; |
||||
|
|
||||
|
public static class SystemInfoPermissions |
||||
|
{ |
||||
|
public const string GroupName = "SystemInfo"; |
||||
|
|
||||
|
public const string Default = GroupName + ".Get"; |
||||
|
} |
||||
@ -0,0 +1,361 @@ |
|||||
|
using DotNetCore.CAP; |
||||
|
using LINGYUN.Abp.SystemInfo.Models; |
||||
|
using LINGYUN.Abp.SystemInfo.Permissions; |
||||
|
using LINGYUN.Abp.SystemInfo.Utils; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using StackExchange.Redis; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace LINGYUN.Abp.SystemInfo; |
||||
|
|
||||
|
[Controller] |
||||
|
[Route("api/system-info")] |
||||
|
[Authorize(SystemInfoPermissions.Default)] |
||||
|
public class SystemInfoController : AbpControllerBase |
||||
|
{ |
||||
|
[HttpGet] |
||||
|
public async virtual Task<SystemInfoModel> GetSystemInfoAsync() |
||||
|
{ |
||||
|
return new SystemInfoModel |
||||
|
{ |
||||
|
Components = new ComponentInfoModel[] |
||||
|
{ |
||||
|
GetSystemInfo(), |
||||
|
GetPerformanceInfo(), |
||||
|
GetRedisInfo(), |
||||
|
GetCapInfo(), |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private ComponentInfoModel GetSystemInfo() |
||||
|
{ |
||||
|
var env = HttpContext.RequestServices.GetService<IHostEnvironment>(); |
||||
|
using var process = Process.GetCurrentProcess(); |
||||
|
|
||||
|
var systemKeys = new List<ComponentKeyModel> |
||||
|
{ |
||||
|
new ComponentKeyModel("sys_machine_name", "机器名称"), |
||||
|
new ComponentKeyModel("sys_environment", "运行环境"), |
||||
|
new ComponentKeyModel("sys_framework_version", ".NET版本"), |
||||
|
new ComponentKeyModel("sys_os_version", "操作系统版本"), |
||||
|
new ComponentKeyModel("sys_app_db_provider", "数据库"), |
||||
|
new ComponentKeyModel("sys_app_version", "应用版本"), |
||||
|
new ComponentKeyModel("sys_app_build_time", "编译时间"), |
||||
|
new ComponentKeyModel("sys_app_start_time", "启动时间"), |
||||
|
}; |
||||
|
|
||||
|
var systemDetails = new Dictionary<string, object> |
||||
|
{ |
||||
|
{ "sys_machine_name", Environment.MachineName }, |
||||
|
{ "sys_environment", env?.EnvironmentName ?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") }, |
||||
|
{ "sys_framework_version", RuntimeInformation.FrameworkDescription }, |
||||
|
{ "sys_os_version", RuntimeInformation.OSDescription }, |
||||
|
{ "sys_app_db_provider", Environment.GetEnvironmentVariable("APPLICATION_DATABASE_PROVIDER") }, |
||||
|
{ "sys_app_start_time", process.StartTime.ToString("yyyy-MM-dd HH:mm:ss") }, |
||||
|
}; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
var assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly(); |
||||
|
var fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location); |
||||
|
var assemblyName = assembly.GetName(); |
||||
|
var buildTime = GetBuildTime(assembly); |
||||
|
|
||||
|
systemDetails.Add("sys_app_version", fileVersionInfo.FileVersion ?? assemblyName.Version?.ToString() ?? "N/A"); |
||||
|
systemDetails.Add("sys_app_build_time", buildTime.HasValue ? buildTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : "N/A"); |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
systemDetails.Add("sys_app_version", "N/A"); |
||||
|
systemDetails.Add("sys_app_build_time", "N/A"); |
||||
|
} |
||||
|
|
||||
|
return new ComponentInfoModel("系统", systemKeys.ToArray(), systemDetails); |
||||
|
} |
||||
|
|
||||
|
private ComponentInfoModel GetPerformanceInfo() |
||||
|
{ |
||||
|
using var process = Process.GetCurrentProcess(); |
||||
|
|
||||
|
var memoryMetrics = new MemoryMonitor().GetMemoryMetrics(); |
||||
|
var threadMetrics = new ThreadMonitor().GetThreadMetrics(); |
||||
|
var gcMetrics = new GCMonitor().GetGCMetrics(); |
||||
|
|
||||
|
return new ComponentInfoModel( |
||||
|
"性能", |
||||
|
new ComponentKeyModel[] |
||||
|
{ |
||||
|
new ComponentKeyModel("perf_total_memory", "总内存(MB)"), |
||||
|
new ComponentKeyModel("perf_working_set", "工作集内存(MB)"), |
||||
|
new ComponentKeyModel("perf_private_memory", "私有内存(MB)"), |
||||
|
new ComponentKeyModel("perf_gc_heap_size", "GC堆大小(MB)"), |
||||
|
new ComponentKeyModel("perf_gc0", "Gen0回收次数"), |
||||
|
new ComponentKeyModel("perf_gc1", "Gen1回收次数"), |
||||
|
new ComponentKeyModel("perf_gc2", "Gen2回收次数"), |
||||
|
new ComponentKeyModel("perf_active_total_memory", "总可用内存"), |
||||
|
new ComponentKeyModel("perf_memory_load", "内存负载"), |
||||
|
new ComponentKeyModel("perf_fragmented_bytes", "内存碎片大小"), |
||||
|
new ComponentKeyModel("perf_available_worker_threads", "可用工作线程数"), |
||||
|
new ComponentKeyModel("perf_available_completion_port_threads", "可用I/O线程数"), |
||||
|
new ComponentKeyModel("perf_max_worker_threads", "最大工作线程数"), |
||||
|
new ComponentKeyModel("perf_max_completion_port_threads", "最大I/O线程数"), |
||||
|
new ComponentKeyModel("perf_active_threads", "进程总线程数"), |
||||
|
new ComponentKeyModel("perf_thread_pool_thread_count", "线程池活动线程数"), |
||||
|
}, |
||||
|
new Dictionary<string, object> |
||||
|
{ |
||||
|
{ "perf_total_memory", memoryMetrics.TotalMemory }, |
||||
|
{ "perf_working_set", memoryMetrics.WorkingSet }, |
||||
|
{ "perf_private_memory", memoryMetrics.PrivateMemory }, |
||||
|
{ "perf_gc_heap_size", memoryMetrics.GCHeapSize }, |
||||
|
{ "perf_gc0", gcMetrics.Gen0Collections }, |
||||
|
{ "perf_gc1", gcMetrics.Gen1Collections }, |
||||
|
{ "perf_gc2", gcMetrics.Gen2Collections }, |
||||
|
{ "perf_active_total_memory", gcMetrics.TotalMemory }, |
||||
|
{ "perf_memory_load", gcMetrics.MemoryLoad }, |
||||
|
{ "perf_fragmented_bytes", gcMetrics.FragmentedBytes }, |
||||
|
{ "perf_available_worker_threads", threadMetrics.AvailableWorkerThreads }, |
||||
|
{ "perf_available_completion_port_threads", threadMetrics.AvailableCompletionPortThreads }, |
||||
|
{ "perf_max_worker_threads", threadMetrics.MaxWorkerThreads }, |
||||
|
{ "perf_max_completion_port_threads", threadMetrics.MaxCompletionPortThreads }, |
||||
|
{ "perf_active_threads", threadMetrics.ActiveThreads }, |
||||
|
{ "perf_thread_pool_thread_count", threadMetrics.ThreadPoolThreadCount }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private ComponentInfoModel GetCapInfo() |
||||
|
{ |
||||
|
var cap = HttpContext.RequestServices.GetService<CapMarkerService>(); |
||||
|
var storage = HttpContext.RequestServices.GetService<CapStorageMarkerService>(); |
||||
|
var broker = HttpContext.RequestServices.GetService<CapMessageQueueMakerService>(); |
||||
|
|
||||
|
var capKeys = new List<ComponentKeyModel> |
||||
|
{ |
||||
|
new ComponentKeyModel("cap_status", "状态"), |
||||
|
new ComponentKeyModel("cap_version", "版本"), |
||||
|
new ComponentKeyModel("cap_storage", "持久化"), |
||||
|
new ComponentKeyModel("cap_transport", "传输"), |
||||
|
}; |
||||
|
var capDetails = new Dictionary<string, object> |
||||
|
{ |
||||
|
{ "cap_status", cap != null ? "正常" : "未启用" }, |
||||
|
{ "cap_version", cap != null ? cap.Version.Substring(0, 5) : "N/A" }, |
||||
|
{ "cap_storage", storage != null ? storage.Name : "N/A" }, |
||||
|
{ "cap_transport", broker != null ? broker.Name : "N/A" } |
||||
|
}; |
||||
|
|
||||
|
return new ComponentInfoModel("CAP组件", capKeys.ToArray(), capDetails); |
||||
|
} |
||||
|
|
||||
|
private ComponentInfoModel GetRedisInfo() |
||||
|
{ |
||||
|
var redis = HttpContext.RequestServices.GetService<IConnectionMultiplexer>(); |
||||
|
if (redis == null) |
||||
|
{ |
||||
|
return new ComponentInfoModel( |
||||
|
"Redis组件", |
||||
|
new ComponentKeyModel[] |
||||
|
{ |
||||
|
new ComponentKeyModel("redis_status", "状态") |
||||
|
}, |
||||
|
new Dictionary<string, object> |
||||
|
{ |
||||
|
{ "redis_status", "未注册" }, |
||||
|
}); |
||||
|
} |
||||
|
var redisInfo = new Dictionary<string, string>(); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
var server = redis.GetServer(redis.GetEndPoints().First()); |
||||
|
var serverInfo = server.Info(); |
||||
|
foreach (var section in serverInfo) |
||||
|
{ |
||||
|
foreach (var item in section) |
||||
|
{ |
||||
|
redisInfo[item.Key] = item.Value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch(Exception ex) |
||||
|
{ |
||||
|
Logger.LogWarning("There is an exception in connecting to the redis server: {message}", ex.Message); |
||||
|
|
||||
|
return new ComponentInfoModel( |
||||
|
"Redis组件", |
||||
|
new ComponentKeyModel[] |
||||
|
{ |
||||
|
new ComponentKeyModel("redis_status", "状态") |
||||
|
}, |
||||
|
new Dictionary<string, object> |
||||
|
{ |
||||
|
{ "redis_status", "连接异常" }, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
var uptimeSeconds = redisInfo.ContainsKey("uptime_in_seconds") ? long.Parse(redisInfo["uptime_in_seconds"]) : 0; |
||||
|
var days = Math.Floor((double)uptimeSeconds / 86400); |
||||
|
var hours = Math.Floor((double)uptimeSeconds % 86400 / 3600); |
||||
|
var minutes = Math.Floor((double)uptimeSeconds % 3600 / 60); |
||||
|
|
||||
|
var usedMemory = redisInfo.ContainsKey("used_memory") ? long.Parse(redisInfo["used_memory"]) : 0; |
||||
|
var maxMemory = redisInfo.ContainsKey("maxmemory") ? long.Parse(redisInfo["maxmemory"]) : 0; |
||||
|
var totalKeys = CalculateTotalKeys(redisInfo); |
||||
|
var expiresKeys = CalculateExpires(redisInfo); |
||||
|
var avgTtl = CalculateAvgTtl(redisInfo); |
||||
|
|
||||
|
var redisKeys = new List<ComponentKeyModel>() |
||||
|
{ |
||||
|
new ComponentKeyModel("redis_version", "版本"), |
||||
|
new ComponentKeyModel("redis_mode", "运行模式"), |
||||
|
new ComponentKeyModel("redis_os", "操作系统"), |
||||
|
new ComponentKeyModel("redis_uptime", "运行时间"), |
||||
|
new ComponentKeyModel("redis_used_cpu_sys", "系统CPU使用时间"), |
||||
|
new ComponentKeyModel("redis_used_cpu_user", "用户CPU使用时间"), |
||||
|
new ComponentKeyModel("redis_used_memory_mb", "使用内存(MB)"), |
||||
|
new ComponentKeyModel("redis_used_memory_rss_mb", "物理内存(MB)"), |
||||
|
new ComponentKeyModel("redis_used_memory_peak_mb", "内存使用峰值(MB)"), |
||||
|
new ComponentKeyModel("redis_maxmemory_mb", "最大内存限制(MB)"), |
||||
|
new ComponentKeyModel("redis_memory_usage_percent", "内存使用率(%)"), |
||||
|
new ComponentKeyModel("redis_mem_fragmentation_ratio", "内存碎片率"), |
||||
|
new ComponentKeyModel("redis_connected_clients", "已连接客户端数"), |
||||
|
new ComponentKeyModel("redis_blocked_clients", "阻塞客户端数"), |
||||
|
new ComponentKeyModel("redis_client_longest_output_list", "客户端最长输出列表"), |
||||
|
new ComponentKeyModel("redis_client_biggest_input_buf", "客户端最大输入缓冲区"), |
||||
|
new ComponentKeyModel("redis_maxclients", "最大客户端数"), |
||||
|
new ComponentKeyModel("redis_total_keys", "总键数量"), |
||||
|
new ComponentKeyModel("redis_expires_keys", "设置过期键数量"), |
||||
|
new ComponentKeyModel("redis_expired_keys", "已过期键数量"), |
||||
|
new ComponentKeyModel("redis_evicted_keys", "被驱逐键数量"), |
||||
|
new ComponentKeyModel("redis_avg_ttl_seconds", "平均TTL(秒)"), |
||||
|
}; |
||||
|
var redisDetails = new Dictionary<string, object>() |
||||
|
{ |
||||
|
{ "redis_version", redisInfo.GetValueOrDefault("redis_version", "unknown") }, |
||||
|
{ "redis_mode", redisInfo.GetValueOrDefault("redis_mode", "unknown") }, |
||||
|
{ "redis_os", redisInfo.GetValueOrDefault("os", "unknown") }, |
||||
|
{ "redis_uptime", $"{days}天{hours}时{minutes}分" }, |
||||
|
{ "redis_used_cpu_sys", redisInfo.GetValueOrDefault("used_cpu_sys", "0") }, |
||||
|
{ "redis_used_cpu_user", redisInfo.GetValueOrDefault("used_cpu_user", "0") }, |
||||
|
{ "redis_used_memory_mb", Math.Round((double)usedMemory / 1024 / 1024, 2) }, |
||||
|
{ "redis_used_memory_rss_mb", redisInfo.ContainsKey("used_memory_rss") ? |
||||
|
Math.Round(double.Parse(redisInfo["used_memory_rss"]) / 1024 / 1024, 2) : 0 }, |
||||
|
{ "redis_used_memory_peak_mb", redisInfo.ContainsKey("used_memory_peak") ? |
||||
|
Math.Round(double.Parse(redisInfo["used_memory_peak"]) / 1024 / 1024, 2) : 0 }, |
||||
|
{ "redis_maxmemory_mb", maxMemory > 0 ? Math.Round((double)maxMemory / 1024 / 1024, 2) : 0 }, |
||||
|
{ "redis_memory_usage_percent", maxMemory > 0 ? Math.Round((double)usedMemory / maxMemory * 100, 2) : 0 }, |
||||
|
{ "redis_mem_fragmentation_ratio", redisInfo.GetValueOrDefault("mem_fragmentation_ratio", "0") }, |
||||
|
{ "redis_connected_clients", redisInfo.GetValueOrDefault("connected_clients", "0") }, |
||||
|
{ "redis_blocked_clients", redisInfo.GetValueOrDefault("blocked_clients", "0") }, |
||||
|
{ "redis_client_longest_output_list", redisInfo.GetValueOrDefault("client_longest_output_list", "0") }, |
||||
|
{ "redis_client_biggest_input_buf", redisInfo.GetValueOrDefault("client_biggest_input_buf", "0") }, |
||||
|
{ "redis_maxclients", redisInfo.GetValueOrDefault("maxclients", "unlimited") }, |
||||
|
{ "redis_total_keys", totalKeys }, |
||||
|
{ "redis_expires_keys", expiresKeys }, |
||||
|
{ "redis_expired_keys", redisInfo.GetValueOrDefault("expired_keys", "0") }, |
||||
|
{ "redis_evicted_keys", redisInfo.GetValueOrDefault("evicted_keys", "0") }, |
||||
|
{ "redis_avg_ttl_seconds", avgTtl }, |
||||
|
}; |
||||
|
|
||||
|
return new ComponentInfoModel("Redis组件", redisKeys.ToArray(), redisDetails); |
||||
|
} |
||||
|
|
||||
|
private long CalculateTotalKeys(Dictionary<string, string> redisInfo) |
||||
|
{ |
||||
|
long total = 0; |
||||
|
|
||||
|
foreach (var item in redisInfo) |
||||
|
{ |
||||
|
if (item.Key.StartsWith("db")) |
||||
|
{ |
||||
|
var keysPart = item.Value.Split(',').FirstOrDefault(x => x.StartsWith("keys=")); |
||||
|
if (keysPart != null && long.TryParse(keysPart.Split('=')[1], out var count)) |
||||
|
{ |
||||
|
total += count; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return total; |
||||
|
} |
||||
|
|
||||
|
private long CalculateExpires(Dictionary<string, string> redisInfo) |
||||
|
{ |
||||
|
long expires = 0; |
||||
|
|
||||
|
foreach (var item in redisInfo) |
||||
|
{ |
||||
|
if (item.Key.StartsWith("db")) |
||||
|
{ |
||||
|
var expiresPart = item.Value.Split(',').FirstOrDefault(x => x.StartsWith("expires=")); |
||||
|
if (expiresPart != null && long.TryParse(expiresPart.Split('=')[1], out var count)) |
||||
|
{ |
||||
|
expires += count; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return expires; |
||||
|
} |
||||
|
|
||||
|
private long CalculateAvgTtl(Dictionary<string, string> redisInfo) |
||||
|
{ |
||||
|
long totalTtl = 0; |
||||
|
long dbCount = 0; |
||||
|
|
||||
|
foreach (var item in redisInfo) |
||||
|
{ |
||||
|
if (item.Key.StartsWith("db")) |
||||
|
{ |
||||
|
var ttlPart = item.Value.Split(',').FirstOrDefault(x => x.StartsWith("avg_ttl=")); |
||||
|
if (ttlPart != null && long.TryParse(ttlPart.Split('=')[1], out var ttl) && ttl > 0) |
||||
|
{ |
||||
|
totalTtl += ttl; |
||||
|
dbCount++; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return dbCount > 0 ? totalTtl / dbCount : -1; |
||||
|
} |
||||
|
|
||||
|
private DateTime? GetBuildTime(Assembly assembly) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var attribute = assembly.GetCustomAttribute<AssemblyMetadataAttribute>(); |
||||
|
if (attribute != null && attribute.Key == "BuildTimestamp") |
||||
|
{ |
||||
|
if (DateTime.TryParse(attribute.Value, out var buildTime)) |
||||
|
{ |
||||
|
return buildTime; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var fileInfo = new FileInfo(assembly.Location); |
||||
|
if (fileInfo.Exists) |
||||
|
{ |
||||
|
return fileInfo.LastWriteTime; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.SystemInfo.Utils; |
||||
|
|
||||
|
public class GCMetrics |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gen0回收次数
|
||||
|
/// </summary>
|
||||
|
public int Gen0Collections { get; set; } |
||||
|
/// <summary>
|
||||
|
/// Gen1回收次数
|
||||
|
/// </summary>
|
||||
|
public int Gen1Collections { get; set; } |
||||
|
/// <summary>
|
||||
|
/// Gen2回收次数
|
||||
|
/// </summary>
|
||||
|
public int Gen2Collections { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 总可用内存
|
||||
|
/// </summary>
|
||||
|
public long TotalMemory { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 内存负载
|
||||
|
/// </summary>
|
||||
|
public long MemoryLoad { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 内存碎片大小
|
||||
|
/// </summary>
|
||||
|
public long FragmentedBytes { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class GCMonitor |
||||
|
{ |
||||
|
private int _lastGen0; |
||||
|
private int _lastGen1; |
||||
|
private int _lastGen2; |
||||
|
|
||||
|
public GCMetrics GetGCMetrics() |
||||
|
{ |
||||
|
var currentGen0 = GC.CollectionCount(0); |
||||
|
var currentGen1 = GC.CollectionCount(1); |
||||
|
var currentGen2 = GC.CollectionCount(2); |
||||
|
|
||||
|
var metrics = new GCMetrics |
||||
|
{ |
||||
|
Gen0Collections = currentGen0 - _lastGen0, |
||||
|
Gen1Collections = currentGen1 - _lastGen1, |
||||
|
Gen2Collections = currentGen2 - _lastGen2 |
||||
|
}; |
||||
|
|
||||
|
var gcInfo = GC.GetGCMemoryInfo(); |
||||
|
metrics.TotalMemory = gcInfo.TotalAvailableMemoryBytes; |
||||
|
metrics.MemoryLoad = gcInfo.MemoryLoadBytes; |
||||
|
metrics.FragmentedBytes = gcInfo.FragmentedBytes; |
||||
|
|
||||
|
_lastGen0 = currentGen0; |
||||
|
_lastGen1 = currentGen1; |
||||
|
_lastGen2 = currentGen2; |
||||
|
|
||||
|
return metrics; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
using System; |
||||
|
using System.Diagnostics; |
||||
|
|
||||
|
namespace LINGYUN.Abp.SystemInfo.Utils; |
||||
|
|
||||
|
public class MemoryMetrics |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 总物理内存
|
||||
|
/// </summary>
|
||||
|
public long TotalMemory { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 工作集内存
|
||||
|
/// </summary>
|
||||
|
public long WorkingSet { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 私有内存
|
||||
|
/// </summary>
|
||||
|
public long PrivateMemory { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 虚拟内存
|
||||
|
/// </summary>
|
||||
|
public long VirtualMemory { get; set; } |
||||
|
/// <summary>
|
||||
|
/// GC堆大小
|
||||
|
/// </summary>
|
||||
|
public long GCHeapSize { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class MemoryMonitor |
||||
|
{ |
||||
|
// 转换为MB
|
||||
|
const long MB = 1024 * 1024; |
||||
|
|
||||
|
public MemoryMetrics GetMemoryMetrics() |
||||
|
{ |
||||
|
var process = Process.GetCurrentProcess(); |
||||
|
|
||||
|
return new MemoryMetrics |
||||
|
{ |
||||
|
TotalMemory = GC.GetTotalMemory(false) / MB, |
||||
|
WorkingSet = process.WorkingSet64 / MB, |
||||
|
PrivateMemory = process.PrivateMemorySize64 / MB, |
||||
|
VirtualMemory = process.VirtualMemorySize64 / MB, |
||||
|
GCHeapSize = GetGCHeapSize() / MB |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private long GetGCHeapSize() |
||||
|
{ |
||||
|
var gcInfo = GC.GetGCMemoryInfo(); |
||||
|
return (long)gcInfo.HeapSizeBytes; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
using System.Diagnostics; |
||||
|
using System.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.SystemInfo.Utils; |
||||
|
|
||||
|
public class ThreadMetrics |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 可用工作线程数
|
||||
|
/// </summary>
|
||||
|
public int AvailableWorkerThreads { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 可用I/O线程数
|
||||
|
/// </summary>
|
||||
|
public int AvailableCompletionPortThreads { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 最大工作线程数
|
||||
|
/// </summary>
|
||||
|
public int MaxWorkerThreads { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 最大I/O线程数
|
||||
|
/// </summary>
|
||||
|
public int MaxCompletionPortThreads { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 进程总线程数
|
||||
|
/// </summary>
|
||||
|
public int ActiveThreads { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 线程池活动线程数
|
||||
|
/// </summary>
|
||||
|
public int ThreadPoolThreadCount { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class ThreadMonitor |
||||
|
{ |
||||
|
public ThreadMetrics GetThreadMetrics() |
||||
|
{ |
||||
|
ThreadPool.GetAvailableThreads( |
||||
|
out var availableWorkerThreads, |
||||
|
out var availableCompletionPortThreads); |
||||
|
ThreadPool.GetMaxThreads( |
||||
|
out var maxWorkerThreads, |
||||
|
out var maxCompletionPortThreads); |
||||
|
|
||||
|
// 获取进程线程数(近似活动线程数)
|
||||
|
var process = Process.GetCurrentProcess(); |
||||
|
var threads = process.Threads; |
||||
|
|
||||
|
return new ThreadMetrics |
||||
|
{ |
||||
|
AvailableWorkerThreads = availableWorkerThreads, |
||||
|
AvailableCompletionPortThreads = availableCompletionPortThreads, |
||||
|
MaxWorkerThreads = maxWorkerThreads, |
||||
|
MaxCompletionPortThreads = maxCompletionPortThreads, |
||||
|
ActiveThreads = threads.Count, |
||||
|
ThreadPoolThreadCount = ThreadPool.ThreadCount |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue