v0.1.4版本 #2

Merged
PeterZhong merged 70 commits from host into master 2025-06-15 14:42:58 +00:00
24 changed files with 693 additions and 71 deletions
Showing only changes of commit 5107fc2f1d - Show all commits

View File

@ -104,6 +104,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Esri.ArcGISPro.Extensions30" Version="3.4.1.55405" /> <PackageReference Include="Esri.ArcGISPro.Extensions30" Version="3.4.1.55405" />
<PackageReference Include="log4net" Version="3.1.0" />
<PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.13" />
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.1.0-preview.13" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<Import Project="C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets" Condition="Exists('C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets') AND !Exists('Esri.ArcGISPro.Extensions.targets')" /> <Import Project="C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets" Condition="Exists('C:\Program Files\ArcGIS\Pro\bin\Esri.ProApp.SDK.Desktop.targets') AND !Exists('Esri.ArcGISPro.Extensions.targets')" />
</Project> </Project>

18
client/CallArcGISPro.cs Normal file
View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using LinkToolAddin.server;
using Newtonsoft.Json;
namespace LinkToolAddin.client;
public class CallArcGISPro
{
public async static Task<string> CallArcGISProTool(Dictionary<string,string> parameters)
{
// Call the ArcGIS Pro method and get the result
var result = await server.CallArcGISPro.CallArcGISProTool(parameters["toolName"], JsonConvert.DeserializeObject<List<string>>(parameters["toolParams"]));
// Serialize the result back to a JSON string
return JsonConvert.SerializeObject(result);
}
}

View File

@ -0,0 +1,6 @@
namespace LinkToolAddin.client;
public class PythonMcpClient
{
}

56
client/SseMcpClient.cs Normal file
View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;
using Newtonsoft.Json;
namespace LinkToolAddin.client;
public class SseMcpClient
{
public static async Task<string> testGaodeMcp()
{
Console.WriteLine("Connecting to 高德 MCP Server via SSE...");
// 创建 MCP Server 配置
SseClientTransportOptions options = new SseClientTransportOptions
{
Endpoint = new Uri("https://mcp.amap.com/sse?key=ed418512c94ade8f83d42c37b77d2bb2"),
};
IClientTransport transport = new SseClientTransport(options);;
// 创建 MCP Client
var client = await McpClientFactory.CreateAsync(transport);
Console.WriteLine("Connected to 高德 MCP Server");
try
{
// 获取可用工具列表
var tools = await client.ListToolsAsync();
Console.WriteLine("\nAvailable Tools:");
foreach (var tool in tools)
{
Console.WriteLine($"- {tool.Name}: {tool.Description}");
}
// 示例调用:获取当前定位
var result = await client.CallToolAsync("amap.maps_weather", new Dictionary<string, object>{{"city","北京"}});
Console.WriteLine("\n[amap.get_location] Result:");
Console.WriteLine(result);
return JsonConvert.SerializeObject(result);
}
catch (Exception ex)
{
Console.WriteLine($"Error occurred: {ex.Message}");
}
finally
{
await client.DisposeAsync();
}
Console.WriteLine("Client closed.");
return "failed";
}
}

21
common/HttpRequest.cs Normal file
View File

@ -0,0 +1,21 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using LinkToolAddin.host.llm.entity;
using Newtonsoft.Json;
namespace LinkToolAddin.common;
public class HttpRequest
{
public static async Task<string> SendPostRequestAsync(string url, string jsonContent, string apiKey)
{
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
var response = await httpClient.PostAsync(url, new StringContent(jsonContent, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}
}

28
host/CallMcp.cs Normal file
View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using LinkToolAddin.server;
using log4net;
using Newtonsoft.Json;
namespace LinkToolAddin.host
{
public class CallMcp
{
private static readonly ILog log = LogManager.GetLogger(typeof(CallMcp));
public static async Task<string> CallInnerMcpTool(string jsonRpcString)
{
log.Info("通过反射调用内部MCP工具");
var jsonRpcEntity = JsonConvert.DeserializeObject<JsonRpcEntity>(jsonRpcString);
Type type = Type.GetType("LinkToolAddin.client."+jsonRpcEntity.Method.Split('.')[0]);
MethodInfo method = type.GetMethod(jsonRpcEntity.Method.Split('.')[1],BindingFlags.Public | BindingFlags.Static);
var task = method.Invoke(null, new object[] { jsonRpcEntity.Params }) as Task<JsonRpcResultEntity>;
JsonRpcResultEntity result = await task;
return JsonConvert.SerializeObject(result);
}
}
}

45
host/llm/Bailian.cs Normal file
View File

@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using LinkToolAddin.common;
using LinkToolAddin.host.llm.entity;
using Newtonsoft.Json;
namespace LinkToolAddin.host.llm;
public class Bailian : Llm
{
public string model { get; set; } = "qwen-max";
public string temperature { get; set; }
public string top_p { get; set; }
public string max_tokens { get; set; }
public string app_id { get; set; }
public string api_key { get; set; }
public IAsyncEnumerable<string> SendChatStreamAsync(string message)
{
throw new System.NotImplementedException();
}
public IAsyncEnumerable<string> SendApplicationStreamAsync(string message)
{
throw new System.NotImplementedException();
}
public async Task<string> SendChatAsync(LlmJsonContent jsonContent)
{
string url = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions";
string responseBody = await HttpRequest.SendPostRequestAsync(url, JsonConvert.SerializeObject(jsonContent), api_key);
LlmChat result = JsonConvert.DeserializeObject<LlmChat>(responseBody);
return result.Choices[0].Message.Content;
}
public async Task<string> SendApplicationAsync(CommonInput commonInput)
{
string url = $"https://dashscope.aliyuncs.com/api/v1/apps/{app_id}/completion";
string responseBody = await HttpRequest.SendPostRequestAsync(url, JsonConvert.SerializeObject(commonInput), api_key);
ApplicationOutput result = JsonConvert.DeserializeObject<ApplicationOutput>(responseBody);
return responseBody;
}
}

View File

@ -1,4 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using LinkToolAddin.host.llm.entity;
namespace LinkToolAddin.host.llm; namespace LinkToolAddin.host.llm;
@ -11,4 +13,6 @@ public interface Llm
public IAsyncEnumerable<string> SendChatStreamAsync(string message); public IAsyncEnumerable<string> SendChatStreamAsync(string message);
public IAsyncEnumerable<string> SendApplicationStreamAsync(string message); public IAsyncEnumerable<string> SendApplicationStreamAsync(string message);
public Task<string> SendChatAsync(LlmJsonContent jsonContent);
public Task<string> SendApplicationAsync(CommonInput commonInput);
} }

View File

@ -0,0 +1,51 @@
namespace LinkToolAddin.host.llm.entity
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class ApplicationOutput
{
[JsonProperty("output")]
public Output Output { get; set; }
[JsonProperty("usage")]
public Usage Usage { get; set; }
[JsonProperty("request_id")]
public Guid RequestId { get; set; }
}
public partial class Output
{
[JsonProperty("finish_reason")]
public string FinishReason { get; set; }
[JsonProperty("session_id")]
public string SessionId { get; set; }
[JsonProperty("text")]
public string Text { get; set; }
}
public partial class Usage
{
[JsonProperty("models")]
public List<Model> Models { get; set; }
}
public partial class Model
{
[JsonProperty("output_tokens")]
public long OutputTokens { get; set; }
[JsonProperty("model_id")]
public string ModelId { get; set; }
[JsonProperty("input_tokens")]
public long InputTokens { get; set; }
}
}

View File

@ -0,0 +1,31 @@
namespace LinkToolAddin.host.llm.entity
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class CommonInput
{
[JsonProperty("input")]
public Input Input { get; set; }
[JsonProperty("parameters")]
public Debug Parameters { get; set; }
[JsonProperty("debug")]
public Debug Debug { get; set; }
}
public partial class Debug
{
}
public partial class Input
{
[JsonProperty("prompt")]
public string Prompt { get; set; }
}
}

View File

@ -0,0 +1,42 @@
namespace LinkToolAddin.host.llm.entity
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class KnowledgeResult
{
[JsonProperty("rewriteQuery")]
public string RewriteQuery { get; set; }
[JsonProperty("chunkList")]
public List<ChunkList> ChunkList { get; set; }
}
public partial class ChunkList
{
[JsonProperty("score")]
public double Score { get; set; }
[JsonProperty("imagesUrl")]
public List<object> ImagesUrl { get; set; }
[JsonProperty("documentName")]
public string DocumentName { get; set; }
[JsonProperty("titcontent", NullValueHandling = NullValueHandling.Ignore)]
public string Titcontent { get; set; }
[JsonProperty("title", NullValueHandling = NullValueHandling.Ignore)]
public string Title { get; set; }
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
public string Content { get; set; }
[JsonProperty("conten_score_with_weight", NullValueHandling = NullValueHandling.Ignore)]
public string ContenScoreWithWeight { get; set; }
}
}

View File

@ -0,0 +1,69 @@
namespace LinkToolAddin.host.llm.entity
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class LlmChat
{
[JsonProperty("choices")]
public List<Choice> Choices { get; set; }
[JsonProperty("object")]
public string Object { get; set; }
[JsonProperty("usage")]
public Usage Usage { get; set; }
[JsonProperty("created")]
public long Created { get; set; }
[JsonProperty("system_fingerprint")]
public object SystemFingerprint { get; set; }
[JsonProperty("model")]
public string Model { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
}
public partial class Choice
{
[JsonProperty("message")]
public Message Message { get; set; }
[JsonProperty("finish_reason")]
public string FinishReason { get; set; }
[JsonProperty("index")]
public long Index { get; set; }
[JsonProperty("logprobs")]
public object Logprobs { get; set; }
}
public partial class Usage
{
[JsonProperty("prompt_tokens")]
public long PromptTokens { get; set; }
[JsonProperty("completion_tokens")]
public long CompletionTokens { get; set; }
[JsonProperty("total_tokens")]
public long TotalTokens { get; set; }
[JsonProperty("prompt_tokens_details")]
public PromptTokensDetails PromptTokensDetails { get; set; }
}
public partial class PromptTokensDetails
{
[JsonProperty("cached_tokens")]
public long CachedTokens { get; set; }
}
}

View File

@ -0,0 +1,38 @@
namespace LinkToolAddin.host.llm.entity
{
using System.Collections.Generic;
using Newtonsoft.Json;
public partial class LlmJsonContent
{
[JsonProperty("model")]
public string Model { get; set; }
[JsonProperty("messages")]
public List<Message> Messages { get; set; }
[JsonProperty("stream")]
public bool Stream { get; set; } = false;
[JsonProperty("temperature")]
public double Temperature { get; set; } = 0.7;
[JsonProperty("top_p")]
public double TopP { get; set; } = 1.0;
[JsonProperty("max_tokens")]
public int MaxTokens { get; set; } = 2048;
[JsonProperty("top_k")]
public int TopK { get; set; } = 40;
}
public partial class Message
{
[JsonProperty("role")]
public string Role { get; set; }
[JsonProperty("content")]
public string Content { get; set; }
}
}

54
resource/DocDb.cs Normal file
View File

@ -0,0 +1,54 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using LinkToolAddin.host.llm;
using LinkToolAddin.host.llm.entity;
using Newtonsoft.Json;
namespace LinkToolAddin.resource;
public class DocDb
{
public string appId {get;set;}
public string apiKey {get;set;}
public DocDb(string apiKey,KnowledgeBase knowledgeBaseEnum)
{
this.apiKey = apiKey;
appId = knowledgeBase[knowledgeBaseEnum];
}
public enum KnowledgeBase
{
ArcGISProHelpDoc,
ArcGISProToolDoc,
TaskPlanningDoc,
ArcGISProApplicantExample
}
public Dictionary<KnowledgeBase, string> knowledgeBase = new Dictionary<KnowledgeBase, string>
{
{KnowledgeBase.ArcGISProHelpDoc,"6a77c5a68de64f469b79fcdcde9d5001"}
};
public async Task<KnowledgeResult> Retrieve(string query)
{
Llm bailian = new Bailian
{
api_key = apiKey,
app_id = appId,
};
var commonInput = new CommonInput
{
Input = new Input
{
Prompt = query
},
Parameters = new Debug(),
Debug = new Debug()
};
string responseBody = await bailian.SendApplicationAsync(commonInput);
ApplicationOutput result = JsonConvert.DeserializeObject<ApplicationOutput>(responseBody);
KnowledgeResult knowledgeResult = JsonConvert.DeserializeObject<KnowledgeResult>(result.Output.Text);
return knowledgeResult;
}
}

9
resource/GpToolDb.cs Normal file
View File

@ -0,0 +1,9 @@
namespace LinkToolAddin.resource;
public class GpToolDb
{
public string toolName { get; set; }
public string toolDescription { get; set; }
public string exeName { get; set; }
public string toolParma { get; set; }
}

8
resource/PromptDb.cs Normal file
View File

@ -0,0 +1,8 @@
namespace LinkToolAddin.resource;
public class PromptDb
{
public string promptName { get; set; }
public string promptDescription { get; set; }
public string promptContent { get; set; }
}

23
server/CallArcGISPro.cs Normal file
View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ArcGIS.Desktop.Core.Geoprocessing;
using ArcGIS.Desktop.Framework.Dialogs;
using ArcGIS.Desktop.Framework.Threading.Tasks;
namespace LinkToolAddin.server;
public class CallArcGISPro
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(CallArcGISPro));
public async static Task<JsonRpcResultEntity> CallArcGISProTool(string toolName, List<string> toolParams)
{
var results = await Geoprocessing.ExecuteToolAsync(toolName, toolParams);
log.Info($"CallArcGISProTool: {toolName} | {toolParams}");
return new JsonRpcSuccessEntity()
{
Id = 1,
Result = results.ToString()
};
}
}

25
server/JsonRpcEntity.cs Normal file
View File

@ -0,0 +1,25 @@
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace LinkToolAddin.server
{
using System;
using System.Collections.Generic;
using System.Globalization;
public partial class JsonRpcEntity
{
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("method")]
public string Method { get; set; }
[JsonProperty("params")]
public Dictionary<string,string> Params { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
}
}

View File

@ -0,0 +1,25 @@
namespace LinkToolAddin.server
{
using Newtonsoft.Json;
public partial class JsonRpcErrorEntity
{
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("error")]
public Error Error { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
}
public partial class Error
{
[JsonProperty("code")]
public long Code { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using Newtonsoft.Json;
namespace LinkToolAddin.server
{
using Newtonsoft.Json;
public partial class JsonRpcResultEntity
{
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
}
}

View File

@ -0,0 +1,16 @@
namespace LinkToolAddin.server
{
using Newtonsoft.Json;
public partial class JsonRpcSuccessEntity : JsonRpcResultEntity
{
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
}
}

View File

@ -22,14 +22,7 @@
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<DockPanel Grid.Row="0" LastChildFill="true" KeyboardNavigation.TabNavigation="Local" Height="30"> <DockPanel Grid.Row="0" LastChildFill="true" KeyboardNavigation.TabNavigation="Local" Height="30">
<TextBlock Text="{Binding Heading}" Style="{DynamicResource Esri_TextBlockDockPaneHeader}"> <Button Content="Test Server" Name="TestServer" Click="TestServer_OnClick"></Button>
<TextBlock.ToolTip>
<WrapPanel Orientation="Vertical" MaxWidth="300">
<TextBlock Text="{Binding Heading}" TextWrapping="Wrap"/>
</WrapPanel>
</TextBlock.ToolTip>
</TextBlock>
<Button Content="Test Server" Name="TestButton" Click="TestButton_OnClick" ></Button>
</DockPanel> </DockPanel>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -1,9 +1,27 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json; using System.ComponentModel;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using System.Threading;
using ArcGIS.Desktop.Core.Geoprocessing;
using LinkToolAddin.client;
using LinkToolAddin.host.llm;
using LinkToolAddin.host.llm.entity;
using LinkToolAddin.resource;
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Layout;
using ModelContextProtocol.Server;
using Newtonsoft.Json;
namespace LinkToolAddin.ui.dockpane namespace LinkToolAddin.ui.dockpane
{ {
@ -12,76 +30,94 @@ namespace LinkToolAddin.ui.dockpane
/// </summary> /// </summary>
public partial class DialogDockpaneView : UserControl public partial class DialogDockpaneView : UserControl
{ {
private static ILog log = LogManager.GetLogger(typeof(DialogDockpaneView));
public DialogDockpaneView() public DialogDockpaneView()
{ {
InitLogger();
InitializeComponent(); InitializeComponent();
} }
private void TestButton_OnClick(object sender, RoutedEventArgs e) public void CallBack(string str,object obj)
{ {
string originalJson = @"{ log.Info($"CallBack {str}");
""name"": ""Alice"",
""age"": 30,
""isStudent"": false,
""hobbies"": [
""reading"",
""swimming"",
""hiking""
],
""address"": {
""street"": ""123 Main St"",
""city"": ""Anytown"",
""postalCode"": ""12345""
} }
}";
try private async void TestServer_OnClick(object sender, RoutedEventArgs e)
{ {
// 反序列化 JSON 到对象 log.Info("TestServer Clicked");
Person? person = JsonSerializer.Deserialize<Person>(originalJson); string res = await SseMcpClient.testGaodeMcp();
log.Info(res);
}
if (person != null) private async void Retrieve_Test()
{ {
// 序列化对象回 JSON 字符串 log.Info("TestServer Clicked");
string serializedJson = JsonSerializer.Serialize(person, new JsonSerializerOptions // string jsonRpcString = @"{""jsonrpc"":""2.0"",""method"":""CallArcGISPro.CallArcGISProTool"",""params"":{""toolName"":""analysis.Buffer"",""toolParams"":""[\""D:/01_Development/02_ArcGIS_Pro_Project/20250319_GisAi/Test.gdb/河流\"",\""D:/01_Development/02_ArcGIS_Pro_Project/20250319_GisAi/Test.gdb/河流buffer\"",\""100\""]""},""id"":1}";
DocDb docDb = new DocDb("sk-db177155677e438f832860e7f4da6afc", DocDb.KnowledgeBase.ArcGISProHelpDoc);
string query = "缓冲区";
KnowledgeResult knowledgeResult = await docDb.Retrieve(query);
log.Info(JsonConvert.SerializeObject(knowledgeResult.ChunkList));
}
private async void Request_Bailian_Test()
{ {
WriteIndented = true // 格式化输出 Llm bailian = new Bailian
{
api_key = "sk-db177155677e438f832860e7f4da6afc",
app_id = "6a77c5a68de64f469b79fcdcde9d5001",
};
string reponse = await bailian.SendChatAsync(new LlmJsonContent()
{
Model = "qwen-max",
Messages = new List<Message>()
{
new Message()
{
Role = "user",
Content = "你是谁"
}
},
Temperature = 0.7,
TopP = 1,
MaxTokens = 1000,
}); });
log.Info(reponse);
Console.WriteLine("序列化后的 JSON:");
Console.WriteLine(serializedJson);
ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(serializedJson);
}
else
{
Console.WriteLine("反序列化失败,对象为 null。");
}
}
catch (JsonException ex)
{
Console.WriteLine($"JSON 处理错误: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"发生错误: {ex.Message}");
}
}
} }
// 定义数据模型类 protected void InitLogger()
public class Person
{ {
public string? Name { get; set; } // 1. 创建控制台输出器Appender
public int Age { get; set; } var consoleAppender = new ConsoleAppender
public bool IsStudent { get; set; } {
public List<string>? Hobbies { get; set; } Layout = new PatternLayout("%date [%thread] %-5level %logger - %message%newline"),
public Address? Address { get; set; } Threshold = log4net.Core.Level.Info // 仅输出 Info 及以上级别
} };
consoleAppender.ActivateOptions(); // 激活配置
public class Address // 2. 创建文件滚动输出器(按大小滚动)
var fileAppender = new RollingFileAppender
{ {
public string? Street { get; set; } File = Path.Combine("Logs", "linktool_app.log"), // 日志文件路径
public string? City { get; set; } AppendToFile = true, // 追加模式
public string? PostalCode { get; set; } RollingStyle = RollingFileAppender.RollingMode.Size, // 按文件大小滚动
MaxSizeRollBackups = 10, // 保留 10 个历史文件
MaximumFileSize = "1MB", // 单个文件最大 1MB
StaticLogFileName = true, // 固定文件名(否则自动追加序号)
Layout = new PatternLayout("%date [%thread] %-5level %logger - %message%newline"),
Threshold = log4net.Core.Level.Info // 仅输出 Info 及以上级别
};
fileAppender.ActivateOptions(); // 激活配置
// 3. 直接通过 BasicConfigurator 注册 Appender
BasicConfigurator.Configure(consoleAppender, fileAppender);
log = LogManager.GetLogger(typeof(DialogDockpaneView));
// 测试日志输出
log.Debug("Debug 日志(控制台可见)");
log.Info("Info 日志(控制台和文件可见)");
log.Error("Error 日志(严重问题)");
}
} }
} }

View File

@ -17,6 +17,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;
namespace LinkToolAddin.ui.dockpane namespace LinkToolAddin.ui.dockpane
{ {
@ -56,6 +60,7 @@ namespace LinkToolAddin.ui.dockpane
{ {
protected override void OnClick() protected override void OnClick()
{ {
DialogDockpaneViewModel.Show(); DialogDockpaneViewModel.Show();
} }
} }