From ecd8537e4bbb601b19b72f96cfe1a0cfbcf729e4 Mon Sep 17 00:00:00 2001 From: walterlv Date: Sat, 27 Apr 2024 08:04:03 +0800 Subject: [PATCH 01/49] Upgrade repo to latest --- .gitattributes | 120 +++++++++++++++++++--------------------- .gitignore | 87 +++++++++++++++++++++++++---- CHANGELOG.md | 1 - Directory.Build.props | 25 ++++++--- dotnetCampus.Logger.sln | 5 +- 5 files changed, 151 insertions(+), 87 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/.gitattributes b/.gitattributes index 1ff0c42..256d68e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,63 +1,57 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain +# Auto detect text files and perform LF normalization +* text=false + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=text +*.csproj merge=text +*.vbproj merge=text +*.fsproj merge=text +*.dbproj merge=text + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain + +# ignore file + +*.mp3 -diff +*.wav -diff +*.enas -diff +*.png -diff +*.jpg -diff +*.psd -diff +*.gif -diff + +# binary +*.avi binary +*.bmp binary +*.exr binary +*.ico binary +*.jpeg binary +*.jpg binary +*.png binary + +*.a binary +*.so binary +*.dll binary +*.jar binary + +*.pdf binary +*.pbxproj binary +*.vec binary +*.doc binary +*.dia binary + +# CR/LF + +*.bat text eol=crlf +*.sh text eol=lf diff --git a/.gitignore b/.gitignore index 6989800..1cd7ff5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ -## Ignore Visual Studio temporary files, build results, and +## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser @@ -13,6 +13,9 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -20,12 +23,14 @@ [Rr]eleases/ x64/ x86/ +[Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ @@ -39,9 +44,10 @@ Generated\ Files/ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ @@ -56,6 +62,9 @@ project.lock.json project.fragment.lock.json artifacts/ +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + # StyleCop StyleCopReport.xml @@ -81,6 +90,7 @@ StyleCopReport.xml *.tmp_proj *_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -122,9 +132,6 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding add-in -.JustCode - # TeamCity is a build add-in _TeamCity* @@ -135,6 +142,11 @@ _TeamCity* .axoCover/* !.axoCover/settings.json +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + # Visual Studio code coverage results *.coverage *.coveragexml @@ -182,6 +194,12 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files @@ -202,6 +220,8 @@ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored @@ -251,7 +271,9 @@ ServiceFabricBackup/ *.bim.layout *.bim_*.settings *.rptproj.rsuser -*- Backup*.rdl +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -272,6 +294,17 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -287,10 +320,6 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml - # CodeRush personal settings .cr/personal @@ -332,5 +361,39 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +# Visual Studio History (VSHistory) files +.vshistory/ + # BeatPulse healthcheck temp database -healthchecksdb \ No newline at end of file +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +.idea/ +*.sln.iml diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a87a9b1..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ -# dotnetCampus.Logger \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 91c5887..d592f2b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,18 +1,25 @@ + + + latest enable - $(MSBuildThisFileDirectory)bin\$(Configuration) - dotnet campus(.NET 职业技术学院) - - + true + $(MSBuildThisFileDirectory)artifacts + $(MSBuildThisFileDirectory) + + + + + 提供统一的日志记录方法。使用源生成器允许库的作者在不依赖本日志库的情况下完成日志的记录,并且还能对接到产品中完成日志的统一输出。 dotnet-campus + dotnet campus(.NET 职业技术学院) + Copyright © 2020-2024 dotnet campus, All Rights Reserved. + git https://github.com/dotnet-campus/dotnetCampus.Logger https://github.com/dotnet-campus/dotnetCampus.Logger - 提供统一的日志记录方法,并附带各种各样的实现。 - - git - Copyright © 2020 dotnet campus, All Rights Reserved. - \ No newline at end of file + + diff --git a/dotnetCampus.Logger.sln b/dotnetCampus.Logger.sln index 1628268..35df2de 100644 --- a/dotnetCampus.Logger.sln +++ b/dotnetCampus.Logger.sln @@ -7,12 +7,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5D196596-756 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.Logger", "src\dotnetCampus.Logger\dotnetCampus.Logger.csproj", "{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AFB0DF31-474C-4ACB-88C6-DD00552D5B5A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{AFB0DF31-474C-4ACB-88C6-DD00552D5B5A}" ProjectSection(SolutionItems) = preProject - CHANGELOG.md = CHANGELOG.md Directory.Build.props = Directory.Build.props README.md = README.md build\Version.props = build\Version.props + .gitattributes = .gitattributes + .gitignore = .gitignore EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.FileLogger", "src\dotnetCampus.FileLogger\dotnetCampus.FileLogger\dotnetCampus.FileLogger.csproj", "{C9399790-B935-4CE3-A75B-4D99133E1DB1}" From 58ef20debb87466276407be92657781b2471094c Mon Sep 17 00:00:00 2001 From: walterlv Date: Sat, 27 Apr 2024 08:07:41 +0800 Subject: [PATCH 02/49] Remove legacy design of Logger projects. --- dotnetCampus.Logger.sln | 30 ------ .../dotnetCampus.FileLogger.sln | 25 ----- .../dotnetCampus.FileLogger/FileLogger.cs | 98 ------------------- .../FileLoggerConfiguration.cs | 39 -------- .../FileLoggerWriteFailedArgs.cs | 22 ----- .../dotnetCampus.FileLogger.csproj | 9 -- .../dotnetCampus.Logger.Abstractions.csproj | 14 --- src/dotnetCampus.Logger/Class1.cs | 9 -- .../EventId.cs | 0 .../ILogger.cs | 0 .../LogLevel.cs | 0 11 files changed, 246 deletions(-) delete mode 100644 src/dotnetCampus.FileLogger/dotnetCampus.FileLogger.sln delete mode 100644 src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLogger.cs delete mode 100644 src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLoggerConfiguration.cs delete mode 100644 src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLoggerWriteFailedArgs.cs delete mode 100644 src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/dotnetCampus.FileLogger.csproj delete mode 100644 src/dotnetCampus.Logger.Abstractions/dotnetCampus.Logger.Abstractions.csproj delete mode 100644 src/dotnetCampus.Logger/Class1.cs rename src/{dotnetCampus.Logger.Abstractions => dotnetCampus.Logger}/EventId.cs (100%) rename src/{dotnetCampus.Logger.Abstractions => dotnetCampus.Logger}/ILogger.cs (100%) rename src/{dotnetCampus.Logger.Abstractions => dotnetCampus.Logger}/LogLevel.cs (100%) diff --git a/dotnetCampus.Logger.sln b/dotnetCampus.Logger.sln index 35df2de..bdf1348 100644 --- a/dotnetCampus.Logger.sln +++ b/dotnetCampus.Logger.sln @@ -16,10 +16,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{AFB0DF31 .gitignore = .gitignore EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.FileLogger", "src\dotnetCampus.FileLogger\dotnetCampus.FileLogger\dotnetCampus.FileLogger.csproj", "{C9399790-B935-4CE3-A75B-4D99133E1DB1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.Logger.Abstractions", "src\dotnetCampus.Logger.Abstractions\dotnetCampus.Logger.Abstractions.csproj", "{73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,38 +38,12 @@ Global {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x64.Build.0 = Release|Any CPU {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x86.ActiveCfg = Release|Any CPU {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x86.Build.0 = Release|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Debug|x64.ActiveCfg = Debug|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Debug|x64.Build.0 = Debug|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Debug|x86.ActiveCfg = Debug|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Debug|x86.Build.0 = Debug|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Release|Any CPU.Build.0 = Release|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Release|x64.ActiveCfg = Release|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Release|x64.Build.0 = Release|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Release|x86.ActiveCfg = Release|Any CPU - {C9399790-B935-4CE3-A75B-4D99133E1DB1}.Release|x86.Build.0 = Release|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Debug|x64.ActiveCfg = Debug|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Debug|x64.Build.0 = Debug|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Debug|x86.ActiveCfg = Debug|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Debug|x86.Build.0 = Debug|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Release|Any CPU.Build.0 = Release|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Release|x64.ActiveCfg = Release|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Release|x64.Build.0 = Release|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Release|x86.ActiveCfg = Release|Any CPU - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272} = {5D196596-756D-45C2-8A05-C8E4AB8A36E6} - {C9399790-B935-4CE3-A75B-4D99133E1DB1} = {5D196596-756D-45C2-8A05-C8E4AB8A36E6} - {73E5A5FB-39A6-4417-9E66-1B9F6E925CFE} = {5D196596-756D-45C2-8A05-C8E4AB8A36E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D7D96521-5EB7-45B4-B70D-2B3FB69A3082} diff --git a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger.sln b/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger.sln deleted file mode 100644 index 6a7978d..0000000 --- a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30523.141 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.FileLogger", "dotnetCampus.FileLogger\dotnetCampus.FileLogger.csproj", "{B4F4EAE9-6C2E-4E54-BFE4-EE8BEF991DAC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B4F4EAE9-6C2E-4E54-BFE4-EE8BEF991DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B4F4EAE9-6C2E-4E54-BFE4-EE8BEF991DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B4F4EAE9-6C2E-4E54-BFE4-EE8BEF991DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B4F4EAE9-6C2E-4E54-BFE4-EE8BEF991DAC}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {043DBF55-1A43-4E6D-A528-8EE89439DD8B} - EndGlobalSection -EndGlobal diff --git a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLogger.cs b/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLogger.cs deleted file mode 100644 index d82c2b4..0000000 --- a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLogger.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; -using dotnetCampus.Threading; - -namespace dotnetCampus.FileLogger -{ - public class FileLogger : IAsyncDisposable - { - public FileLogger() - { - DoubleBufferTask = new DoubleBufferLazyInitializeTask(WriteFile); - } - - public FileLogger(FileLoggerConfiguration configuration) : this() - { - SetConfiguration(configuration); - } - - public void SetConfiguration(FileLoggerConfiguration configuration) - { - if (FileLoggerConfiguration != null) - { - throw new InvalidOperationException($"重复多次设置日志文件"); - } - - FileLoggerConfiguration = configuration.Clone(); - - DoubleBufferTask.OnInitialized(); - - IsInitialized = true; - } - - private FileLoggerConfiguration FileLoggerConfiguration { set; get; } = null!; - - public bool IsInitialized { private set; get; } = false; - - public FileInfo LogFile => FileLoggerConfiguration.LogFile; - - private DoubleBufferLazyInitializeTask DoubleBufferTask { get; } - - public async ValueTask DisposeAsync() - { - DoubleBufferTask.Finish(); - await DoubleBufferTask.WaitAllTaskFinish(); - } - - public void WriteLog(string logMessage) - { - DoubleBufferTask.AddTask(logMessage); - } - - private uint CurrentWriteTextCount { set; get; } = 0; - - private async Task WriteFile(List logList) - { - // 最多尝试写10次日志 - var maxWriteLogFileRetryCount = FileLoggerConfiguration.MaxWriteLogFileRetryCount; - for (var i = 0; i < maxWriteLogFileRetryCount; i++) - { - try - { - await File.AppendAllLinesAsync(LogFile.FullName, logList); - - // 当前写入的数据量 - foreach (var logText in logList) - { - CurrentWriteTextCount += (uint)logText.Length; - } - - if (CurrentWriteTextCount > FileLoggerConfiguration.NotifyMinWriteTextCount) - { - foreach (var limitTextCountFilter in FileLoggerConfiguration.LimitTextCountFilterList) - { - await limitTextCountFilter.FilterLogFile(LogFile); - } - } - - return; - } - catch (Exception e) - { - Debug.WriteLine("[FileLogger] {0}", e); - } - - Debug.WriteLine("[FileLogger] Retry count {0}", i); - await Task.Delay(FileLoggerConfiguration.RetryDelayTime); - } - - FileLoggerWriteFailed?.Invoke(this, new FileLoggerWriteFailedArgs(this, FileLoggerConfiguration, logList)); - // 如果超过次数依然写入失败,那就忽略失败了 - } - - public event EventHandler? FileLoggerWriteFailed; - } -} \ No newline at end of file diff --git a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLoggerConfiguration.cs b/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLoggerConfiguration.cs deleted file mode 100644 index 1c903cc..0000000 --- a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLoggerConfiguration.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace dotnetCampus.FileLogger -{ - public class FileLoggerConfiguration - { - public uint MaxWriteLogFileRetryCount { set; get; } = DefaultMaxWriteLogFileRetryCount; - - public const uint DefaultMaxWriteLogFileRetryCount = 10; - - public TimeSpan RetryDelayTime { set; get; } = DefaultRetryDelayTime; - - public static readonly TimeSpan DefaultRetryDelayTime = TimeSpan.FromMilliseconds(100); - - public FileInfo LogFile { set; get; } = null!; - - public uint NotifyMinWriteTextCount { set; get; } = DefaultNotifyMinWriteTextCount; - - public const uint DefaultNotifyMinWriteTextCount = 5 * 1024 * 1024;// 大约是 5-10M 左右 - - public FileLoggerConfiguration Clone() - { - var fileLoggerConfiguration = (FileLoggerConfiguration)MemberwiseClone(); - fileLoggerConfiguration.LimitTextCountFilterList = LimitTextCountFilterList.ToList(); - return fileLoggerConfiguration; - } - - public List LimitTextCountFilterList { private set;get; } = new List(); - } - - public interface ILimitTextCountFilter - { - Task FilterLogFile(FileInfo logFile); - } -} \ No newline at end of file diff --git a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLoggerWriteFailedArgs.cs b/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLoggerWriteFailedArgs.cs deleted file mode 100644 index 63cd7ad..0000000 --- a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/FileLoggerWriteFailedArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace dotnetCampus.FileLogger -{ - public class FileLoggerWriteFailedArgs : EventArgs - { - public FileLoggerWriteFailedArgs(FileLogger fileLogger, FileLoggerConfiguration fileLoggerConfiguration, - IReadOnlyList logList) - { - FileLoggerConfiguration = fileLoggerConfiguration; - FileLogger = fileLogger; - LogList = logList; - } - - public FileLoggerConfiguration FileLoggerConfiguration { get; } - - public FileLogger FileLogger { get; } - - public IReadOnlyList LogList { get; } - } -} \ No newline at end of file diff --git a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/dotnetCampus.FileLogger.csproj b/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/dotnetCampus.FileLogger.csproj deleted file mode 100644 index 3e1575e..0000000 --- a/src/dotnetCampus.FileLogger/dotnetCampus.FileLogger/dotnetCampus.FileLogger.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net5.0 - - - - - diff --git a/src/dotnetCampus.Logger.Abstractions/dotnetCampus.Logger.Abstractions.csproj b/src/dotnetCampus.Logger.Abstractions/dotnetCampus.Logger.Abstractions.csproj deleted file mode 100644 index 9bc4a26..0000000 --- a/src/dotnetCampus.Logger.Abstractions/dotnetCampus.Logger.Abstractions.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - netcoreapp3.1;net45;net5.0 - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - diff --git a/src/dotnetCampus.Logger/Class1.cs b/src/dotnetCampus.Logger/Class1.cs deleted file mode 100644 index 62d3abd..0000000 --- a/src/dotnetCampus.Logger/Class1.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace dotnetCampus.Logger -{ - class Class1 - { - - } -} diff --git a/src/dotnetCampus.Logger.Abstractions/EventId.cs b/src/dotnetCampus.Logger/EventId.cs similarity index 100% rename from src/dotnetCampus.Logger.Abstractions/EventId.cs rename to src/dotnetCampus.Logger/EventId.cs diff --git a/src/dotnetCampus.Logger.Abstractions/ILogger.cs b/src/dotnetCampus.Logger/ILogger.cs similarity index 100% rename from src/dotnetCampus.Logger.Abstractions/ILogger.cs rename to src/dotnetCampus.Logger/ILogger.cs diff --git a/src/dotnetCampus.Logger.Abstractions/LogLevel.cs b/src/dotnetCampus.Logger/LogLevel.cs similarity index 100% rename from src/dotnetCampus.Logger.Abstractions/LogLevel.cs rename to src/dotnetCampus.Logger/LogLevel.cs From bf4ac68173cb7760162496245baad94c8ebd4330 Mon Sep 17 00:00:00 2001 From: walterlv Date: Sat, 27 Apr 2024 08:25:39 +0800 Subject: [PATCH 03/49] Use microsoft extensions basic logger files. --- src/dotnetCampus.Logger/EventId.cs | 161 +++++++++--------- src/dotnetCampus.Logger/ILogger.cs | 52 +++--- src/dotnetCampus.Logger/LogLevel.cs | 83 ++++----- .../dotnetCampus.Logger.csproj | 61 ++++--- 4 files changed, 169 insertions(+), 188 deletions(-) diff --git a/src/dotnetCampus.Logger/EventId.cs b/src/dotnetCampus.Logger/EventId.cs index aecafef..664eb8b 100644 --- a/src/dotnetCampus.Logger/EventId.cs +++ b/src/dotnetCampus.Logger/EventId.cs @@ -1,103 +1,94 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Diagnostics.CodeAnalysis; -namespace dotnetCampus.Logger.Abstractions +namespace dotnetCampus.Logger; + +/// +/// Identifies a logging event. The primary identifier is the "Id" property, with the "Name" property providing a short description of this type of event. +/// +public readonly struct EventId { /// - /// Identifies a logging event. The primary identifier is the "Id" property, with the "Name" property providing a short description of this type of event. + /// Implicitly creates an EventId from the given . /// - /// Copy from dotnet runtime -#if LogPublicAsInternal - internal -#else - public -#endif - readonly struct EventId + /// The to convert to an EventId. + public static implicit operator EventId(int i) { - /// - /// Implicitly creates an EventId from the given . - /// - /// The to convert to an EventId. - public static implicit operator EventId(int i) - { - return new EventId(i); - } + return new EventId(i); + } - /// - /// Checks if two specified instances have the same value. They are equal if they have the same Id. - /// - /// The first . - /// The second . - /// if the objects are equal. - public static bool operator ==(EventId left, EventId right) - { - return left.Equals(right); - } + /// + /// Checks if two specified instances have the same value. They are equal if they have the same Id. + /// + /// The first . + /// The second . + /// if the objects are equal. + public static bool operator ==(EventId left, EventId right) + { + return left.Equals(right); + } - /// - /// Checks if two specified instances have different values. - /// - /// The first . - /// The second . - /// if the objects are not equal. - public static bool operator !=(EventId left, EventId right) - { - return !left.Equals(right); - } + /// + /// Checks if two specified instances have different values. + /// + /// The first . + /// The second . + /// if the objects are not equal. + public static bool operator !=(EventId left, EventId right) + { + return !left.Equals(right); + } - /// - /// Initializes an instance of the struct. - /// - /// The numeric identifier for this event. - /// The name of this event. - public EventId(int id, string? name = null) - { - Id = id; - Name = name; - } + /// + /// Initializes an instance of the struct. + /// + /// The numeric identifier for this event. + /// The name of this event. + public EventId(int id, string? name = null) + { + Id = id; + Name = name; + } - /// - /// Gets the numeric identifier for this event. - /// - public int Id { get; } + /// + /// Gets the numeric identifier for this event. + /// + public int Id { get; } - /// - /// Gets the name of this event. - /// - public string? Name { get; } + /// + /// Gets the name of this event. + /// + public string? Name { get; } - /// - public override string ToString() - { - return Name ?? Id.ToString(); - } + /// + public override string ToString() + { + return Name ?? Id.ToString(); + } - /// - /// Indicates whether the current object is equal to another object of the same type. Two events are equal if they have the same id. - /// - /// An object to compare with this object. - /// if the current object is equal to the other parameter; otherwise, . - public bool Equals(EventId other) - { - return Id == other.Id; - } + /// + /// Indicates whether the current object is equal to another object of the same type. Two events are equal if they have the same id. + /// + /// An object to compare with this object. + /// if the current object is equal to the other parameter; otherwise, . + public bool Equals(EventId other) + { + return Id == other.Id; + } - /// - public override bool Equals(object? obj) + /// + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is null) { - if (obj is null) - { - return false; - } - - return obj is EventId eventId && Equals(eventId); + return false; } - /// - public override int GetHashCode() - { - return Id; - } + return obj is EventId eventId && Equals(eventId); + } + + /// + public override int GetHashCode() + { + return Id; } } diff --git a/src/dotnetCampus.Logger/ILogger.cs b/src/dotnetCampus.Logger/ILogger.cs index 173963f..8f127e6 100644 --- a/src/dotnetCampus.Logger/ILogger.cs +++ b/src/dotnetCampus.Logger/ILogger.cs @@ -1,28 +1,32 @@ using System; -namespace dotnetCampus.Logger.Abstractions +namespace dotnetCampus.Logger; + +/// Represents a type used to perform logging. +/// Aggregates most logging patterns to a single method. +public interface ILogger +{ + /// Writes a log entry. + /// Entry will be written on this level. + /// Id of the event. + /// The entry to be written. Can be also an object. + /// The exception related to this entry. + /// Function to create a message of the and . + /// The type of the object to be written. + void Log( + LogLevel logLevel, + EventId eventId, + TState state, + Exception? exception, + Func formatter); +} + +/// +/// A generic interface for logging where the category name is derived from the specified +/// type name. +/// Generally used to enable activation of a named from dependency injection. +/// +/// The type whose name is used for the logger category name. +public interface ILogger : ILogger { - /// - /// Represents a type used to perform logging. - /// - /// Aggregates most logging patterns to a single method. - /// Copy from dotnet runtime -#if LogPublicAsInternal - internal -#else - public -#endif - interface ILogger - { - /// - /// Writes a log entry. - /// - /// Entry will be written on this level. - /// Id of the event. - /// The entry to be written. Can be also an object. - /// The exception related to this entry. - /// Function to create a message of the and . - /// The type of the object to be written. - void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter); - } } diff --git a/src/dotnetCampus.Logger/LogLevel.cs b/src/dotnetCampus.Logger/LogLevel.cs index 59ab39e..015baac 100644 --- a/src/dotnetCampus.Logger/LogLevel.cs +++ b/src/dotnetCampus.Logger/LogLevel.cs @@ -1,58 +1,47 @@ -using System; -using System.Collections.Generic; -using System.Text; +namespace dotnetCampus.Logger; -namespace dotnetCampus.Logger.Abstractions +/// +/// Defines logging severity levels. +/// +public enum LogLevel { /// - /// Defines logging severity levels. + /// Logs that contain the most detailed messages. These messages may contain sensitive application data. + /// These messages are disabled by default and should never be enabled in a production environment. /// - /// Copy from dotnet runtime -#if LogPublicAsInternal - internal -#else - public -#endif - enum LogLevel - { - /// - /// Logs that contain the most detailed messages. These messages may contain sensitive application data. - /// These messages are disabled by default and should never be enabled in a production environment. - /// - Trace = 0, + Trace = 0, - /// - /// Logs that are used for interactive investigation during development. These logs should primarily contain - /// information useful for debugging and have no long-term value. - /// - Debug = 1, + /// + /// Logs that are used for interactive investigation during development. These logs should primarily contain + /// information useful for debugging and have no long-term value. + /// + Debug = 1, - /// - /// Logs that track the general flow of the application. These logs should have long-term value. - /// - Information = 2, + /// + /// Logs that track the general flow of the application. These logs should have long-term value. + /// + Information = 2, - /// - /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the - /// application execution to stop. - /// - Warning = 3, + /// + /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the + /// application execution to stop. + /// + Warning = 3, - /// - /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a - /// failure in the current activity, not an application-wide failure. - /// - Error = 4, + /// + /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a + /// failure in the current activity, not an application-wide failure. + /// + Error = 4, - /// - /// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires - /// immediate attention. - /// - Critical = 5, + /// + /// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires + /// immediate attention. + /// + Critical = 5, - /// - /// Not used for writing log messages. Specifies that a logging category should not write any messages. - /// - None = 6, - } + /// + /// Not used for writing log messages. Specifies that a logging category should not write any messages. + /// + None = 6, } diff --git a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj index bc4c624..0654bbc 100644 --- a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj +++ b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj @@ -1,36 +1,33 @@ - - netcoreapp3.1;netstandard2.0;net45 - true - - true - - - - - - - true - - - true - - - - - - true - - - - true - snupkg - - - - - - + + + net8.0;net6.0;netstandard2.0;net45 + true + true + + + + + + + + + + true + true + true + true + snupkg + From 8a743f304306aec9c8b21790d58fb2d085d64c34 Mon Sep 17 00:00:00 2001 From: walterlv Date: Sat, 27 Apr 2024 08:34:16 +0800 Subject: [PATCH 04/49] Add sample projects. --- dotnetCampus.Logger.sln | 47 +++++++++++++++++++ .../Class1.cs | 5 ++ ...LoggerSample.LoggerDependentLibrary.csproj | 9 ++++ .../LoggerSample.MainApp.Avalonia/App.axaml | 10 ++++ .../App.axaml.cs | 23 +++++++++ .../LoggerSample.MainApp.Avalonia.csproj | 26 ++++++++++ .../MainWindow.axaml | 9 ++++ .../MainWindow.axaml.cs | 11 +++++ .../LoggerSample.MainApp.Avalonia/Program.cs | 21 +++++++++ .../Properties/App.manifest | 18 +++++++ 10 files changed, 179 insertions(+) create mode 100644 samples/LoggerSample.LoggerDependentLibrary/Class1.cs create mode 100644 samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj create mode 100644 samples/LoggerSample.MainApp.Avalonia/App.axaml create mode 100644 samples/LoggerSample.MainApp.Avalonia/App.axaml.cs create mode 100644 samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj create mode 100644 samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml create mode 100644 samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs create mode 100644 samples/LoggerSample.MainApp.Avalonia/Program.cs create mode 100644 samples/LoggerSample.MainApp.Avalonia/Properties/App.manifest diff --git a/dotnetCampus.Logger.sln b/dotnetCampus.Logger.sln index bdf1348..03c4666 100644 --- a/dotnetCampus.Logger.sln +++ b/dotnetCampus.Logger.sln @@ -16,6 +16,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{AFB0DF31 .gitignore = .gitignore EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggerSample.MainApp.Avalonia", "samples\LoggerSample.MainApp.Avalonia\LoggerSample.MainApp.Avalonia.csproj", "{C282F00B-0C42-491F-AC0D-967407E1C418}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{34E3F27E-C7E2-45D7-8035-53C304F77E2A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggerSample.LoggerDependentLibrary", "samples\LoggerSample.LoggerDependentLibrary\LoggerSample.LoggerDependentLibrary.csproj", "{0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggerSample.LoggerIndependentLibrary", "samples\LoggerSample.LoggerIndependentLibrary\LoggerSample.LoggerIndependentLibrary.csproj", "{36367E21-BABC-4CC4-891E-CEAF56D66B68}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,12 +46,51 @@ Global {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x64.Build.0 = Release|Any CPU {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x86.ActiveCfg = Release|Any CPU {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x86.Build.0 = Release|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Debug|x64.ActiveCfg = Debug|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Debug|x64.Build.0 = Debug|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Debug|x86.ActiveCfg = Debug|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Debug|x86.Build.0 = Debug|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Release|Any CPU.Build.0 = Release|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Release|x64.ActiveCfg = Release|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Release|x64.Build.0 = Release|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Release|x86.ActiveCfg = Release|Any CPU + {C282F00B-0C42-491F-AC0D-967407E1C418}.Release|x86.Build.0 = Release|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Debug|x64.ActiveCfg = Debug|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Debug|x64.Build.0 = Debug|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Debug|x86.ActiveCfg = Debug|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Debug|x86.Build.0 = Debug|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Release|Any CPU.Build.0 = Release|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Release|x64.ActiveCfg = Release|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Release|x64.Build.0 = Release|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Release|x86.ActiveCfg = Release|Any CPU + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899}.Release|x86.Build.0 = Release|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Debug|x64.ActiveCfg = Debug|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Debug|x64.Build.0 = Debug|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Debug|x86.ActiveCfg = Debug|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Debug|x86.Build.0 = Debug|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Release|Any CPU.Build.0 = Release|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Release|x64.ActiveCfg = Release|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Release|x64.Build.0 = Release|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Release|x86.ActiveCfg = Release|Any CPU + {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272} = {5D196596-756D-45C2-8A05-C8E4AB8A36E6} + {C282F00B-0C42-491F-AC0D-967407E1C418} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} + {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} + {36367E21-BABC-4CC4-891E-CEAF56D66B68} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D7D96521-5EB7-45B4-B70D-2B3FB69A3082} diff --git a/samples/LoggerSample.LoggerDependentLibrary/Class1.cs b/samples/LoggerSample.LoggerDependentLibrary/Class1.cs new file mode 100644 index 0000000..7c67d96 --- /dev/null +++ b/samples/LoggerSample.LoggerDependentLibrary/Class1.cs @@ -0,0 +1,5 @@ +namespace LoggerSample.LoggerDependentLibrary; + +public class Class1 +{ +} diff --git a/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj new file mode 100644 index 0000000..f34859b --- /dev/null +++ b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + dotnetCampus.LoggerSample + + + diff --git a/samples/LoggerSample.MainApp.Avalonia/App.axaml b/samples/LoggerSample.MainApp.Avalonia/App.axaml new file mode 100644 index 0000000..10086d8 --- /dev/null +++ b/samples/LoggerSample.MainApp.Avalonia/App.axaml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs b/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs new file mode 100644 index 0000000..addf22e --- /dev/null +++ b/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace dotnetCampus.LoggerSample.Avalonia; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } +} diff --git a/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj b/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj new file mode 100644 index 0000000..6cb9224 --- /dev/null +++ b/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj @@ -0,0 +1,26 @@ + + + + WinExe + net8.0 + dotnetCampus.LoggerSample + true + Properties\App.manifest + true + + + + + + + + + + + + + + + + + diff --git a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml new file mode 100644 index 0000000..375a54d --- /dev/null +++ b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml @@ -0,0 +1,9 @@ + + Welcome to Avalonia! + diff --git a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs new file mode 100644 index 0000000..4fd3557 --- /dev/null +++ b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace dotnetCampus.LoggerSample.Avalonia; + +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); + } +} diff --git a/samples/LoggerSample.MainApp.Avalonia/Program.cs b/samples/LoggerSample.MainApp.Avalonia/Program.cs new file mode 100644 index 0000000..98ecccb --- /dev/null +++ b/samples/LoggerSample.MainApp.Avalonia/Program.cs @@ -0,0 +1,21 @@ +using Avalonia; +using System; + +namespace dotnetCampus.LoggerSample.Avalonia; + +class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace(); +} diff --git a/samples/LoggerSample.MainApp.Avalonia/Properties/App.manifest b/samples/LoggerSample.MainApp.Avalonia/Properties/App.manifest new file mode 100644 index 0000000..5044dfa --- /dev/null +++ b/samples/LoggerSample.MainApp.Avalonia/Properties/App.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + From 15f5b53e2dd2f6cd8c5bb9f85b3c308baa29b57d Mon Sep 17 00:00:00 2001 From: walterlv Date: Sat, 27 Apr 2024 08:36:51 +0800 Subject: [PATCH 05/49] Add unit test project. --- dotnetCampus.Logger.sln | 17 ++++++++++++++ .../Class1.cs | 5 ++++ ...ggerSample.LoggerIndependentLibrary.csproj | 9 ++++++++ tests/dotnetCampus.Logger.Tests/UnitTest1.cs | 10 ++++++++ .../dotnetCampus.Logger.Tests.csproj | 23 +++++++++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 samples/LoggerSample.LoggerIndependentLibrary/Class1.cs create mode 100644 samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj create mode 100644 tests/dotnetCampus.Logger.Tests/UnitTest1.cs create mode 100644 tests/dotnetCampus.Logger.Tests/dotnetCampus.Logger.Tests.csproj diff --git a/dotnetCampus.Logger.sln b/dotnetCampus.Logger.sln index 03c4666..004359c 100644 --- a/dotnetCampus.Logger.sln +++ b/dotnetCampus.Logger.sln @@ -24,6 +24,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggerSample.LoggerDependen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggerSample.LoggerIndependentLibrary", "samples\LoggerSample.LoggerIndependentLibrary\LoggerSample.LoggerIndependentLibrary.csproj", "{36367E21-BABC-4CC4-891E-CEAF56D66B68}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{36852775-3A76-49CF-98CC-3067CE54A5AD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.Logger.Tests", "tests\dotnetCampus.Logger.Tests\dotnetCampus.Logger.Tests.csproj", "{D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -82,6 +86,18 @@ Global {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Release|x64.Build.0 = Release|Any CPU {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Release|x86.ActiveCfg = Release|Any CPU {36367E21-BABC-4CC4-891E-CEAF56D66B68}.Release|x86.Build.0 = Release|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Debug|x64.ActiveCfg = Debug|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Debug|x64.Build.0 = Debug|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Debug|x86.ActiveCfg = Debug|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Debug|x86.Build.0 = Debug|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Release|Any CPU.Build.0 = Release|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Release|x64.ActiveCfg = Release|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Release|x64.Build.0 = Release|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Release|x86.ActiveCfg = Release|Any CPU + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -91,6 +107,7 @@ Global {C282F00B-0C42-491F-AC0D-967407E1C418} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} {36367E21-BABC-4CC4-891E-CEAF56D66B68} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} + {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB} = {36852775-3A76-49CF-98CC-3067CE54A5AD} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D7D96521-5EB7-45B4-B70D-2B3FB69A3082} diff --git a/samples/LoggerSample.LoggerIndependentLibrary/Class1.cs b/samples/LoggerSample.LoggerIndependentLibrary/Class1.cs new file mode 100644 index 0000000..1a9561f --- /dev/null +++ b/samples/LoggerSample.LoggerIndependentLibrary/Class1.cs @@ -0,0 +1,5 @@ +namespace LoggerSample.LoggerIndependentLibrary; + +public class Class1 +{ +} diff --git a/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj new file mode 100644 index 0000000..f34859b --- /dev/null +++ b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + dotnetCampus.LoggerSample + + + diff --git a/tests/dotnetCampus.Logger.Tests/UnitTest1.cs b/tests/dotnetCampus.Logger.Tests/UnitTest1.cs new file mode 100644 index 0000000..2b72684 --- /dev/null +++ b/tests/dotnetCampus.Logger.Tests/UnitTest1.cs @@ -0,0 +1,10 @@ +namespace dotnetCampus.Logger.Tests; + +[TestClass] +public class UnitTest1 +{ + [TestMethod("Add test description here")] + public void TestMethod1() + { + } +} diff --git a/tests/dotnetCampus.Logger.Tests/dotnetCampus.Logger.Tests.csproj b/tests/dotnetCampus.Logger.Tests/dotnetCampus.Logger.Tests.csproj new file mode 100644 index 0000000..517a899 --- /dev/null +++ b/tests/dotnetCampus.Logger.Tests/dotnetCampus.Logger.Tests.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + From 661cef601b14280a4a3c9542b85e584d44c23925 Mon Sep 17 00:00:00 2001 From: walterlv Date: Sat, 27 Apr 2024 08:39:54 +0800 Subject: [PATCH 06/49] =?UTF-8?q?=E8=80=83=E8=99=91=E5=88=B0=20Logger=20?= =?UTF-8?q?=E4=BC=9A=E8=B7=9F=E7=B1=BB=E5=90=8D=E5=86=B2=E7=AA=81=EF=BC=8C?= =?UTF-8?q?=E6=88=91=E4=BB=AC=E5=B0=86=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E4=BD=BF=E7=94=A8=20Logging=EF=BC=88?= =?UTF-8?q?=E4=BF=9D=E6=8C=81=E4=B8=8E=20Microsoft=20=E4=B8=80=E8=87=B4?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dotnetCampus.Logger/EventId.cs | 2 +- src/dotnetCampus.Logger/ILogger.cs | 2 +- src/dotnetCampus.Logger/LogLevel.cs | 2 +- src/dotnetCampus.Logger/dotnetCampus.Logger.csproj | 1 + .../dotnetCampus.Logger.Tests/dotnetCampus.Logger.Tests.csproj | 2 -- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/dotnetCampus.Logger/EventId.cs b/src/dotnetCampus.Logger/EventId.cs index 664eb8b..1ecbd77 100644 --- a/src/dotnetCampus.Logger/EventId.cs +++ b/src/dotnetCampus.Logger/EventId.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -namespace dotnetCampus.Logger; +namespace dotnetCampus.Logging; /// /// Identifies a logging event. The primary identifier is the "Id" property, with the "Name" property providing a short description of this type of event. diff --git a/src/dotnetCampus.Logger/ILogger.cs b/src/dotnetCampus.Logger/ILogger.cs index 8f127e6..b25fc66 100644 --- a/src/dotnetCampus.Logger/ILogger.cs +++ b/src/dotnetCampus.Logger/ILogger.cs @@ -1,6 +1,6 @@ using System; -namespace dotnetCampus.Logger; +namespace dotnetCampus.Logging; /// Represents a type used to perform logging. /// Aggregates most logging patterns to a single method. diff --git a/src/dotnetCampus.Logger/LogLevel.cs b/src/dotnetCampus.Logger/LogLevel.cs index 015baac..3203bd3 100644 --- a/src/dotnetCampus.Logger/LogLevel.cs +++ b/src/dotnetCampus.Logger/LogLevel.cs @@ -1,4 +1,4 @@ -namespace dotnetCampus.Logger; +namespace dotnetCampus.Logging; /// /// Defines logging severity levels. diff --git a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj index 0654bbc..cff92ef 100644 --- a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj +++ b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj @@ -12,6 +12,7 @@ - net45: Support legacy .NET framework --> net8.0;net6.0;netstandard2.0;net45 + dotnetCampus.Logging true true diff --git a/tests/dotnetCampus.Logger.Tests/dotnetCampus.Logger.Tests.csproj b/tests/dotnetCampus.Logger.Tests/dotnetCampus.Logger.Tests.csproj index 517a899..902f82c 100644 --- a/tests/dotnetCampus.Logger.Tests/dotnetCampus.Logger.Tests.csproj +++ b/tests/dotnetCampus.Logger.Tests/dotnetCampus.Logger.Tests.csproj @@ -3,8 +3,6 @@ net8.0 enable - enable - false true From 50519c252783d320314d577e5daa37955febd8aa Mon Sep 17 00:00:00 2001 From: walterlv Date: Sun, 28 Apr 2024 09:55:46 +0800 Subject: [PATCH 07/49] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9F=BA=E6=9C=AC?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97=E7=B3=BB=E7=BB=9F=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Directory.Build.props | 1 - dotnetCampus.Logger.sln | 3 - .../LoggerSample.MainApp.Avalonia.csproj | 1 + .../LoggerSample.MainApp.Avalonia/Program.cs | 13 +- .../Building/CompositeLogger.cs | 6 + src/dotnetCampus.Logger/CompositeLogger.cs | 17 + .../Configurations/InheritedConfiguration.cs | 12 + .../Configurations/LogOptions.cs | 6 + src/dotnetCampus.Logger/Log.cs | 6 + src/dotnetCampus.Logger/LogBuilder.cs | 14 + src/dotnetCampus.Logger/LogExtensions.cs | 390 ++++++++++++++++++ src/dotnetCampus.Logger/Output/ILogWriter.cs | 6 + .../SourceGenerators/BridgeLogger.cs | 15 + .../SourceGenerators/ILoggerBridge.cs | 25 ++ .../SourceGenerators/LogFactory.cs | 16 + .../SourceGenerators/README.md | 5 + .../Writers/ConsoleColors.cs | 69 ++++ .../Writers/ConsoleLogger.cs | 19 + .../Writers/MemoryCacheLogger.cs | 11 + .../dotnetCampus.Logger.csproj | 25 +- 20 files changed, 646 insertions(+), 14 deletions(-) create mode 100644 src/dotnetCampus.Logger/Building/CompositeLogger.cs create mode 100644 src/dotnetCampus.Logger/CompositeLogger.cs create mode 100644 src/dotnetCampus.Logger/Configurations/InheritedConfiguration.cs create mode 100644 src/dotnetCampus.Logger/Configurations/LogOptions.cs create mode 100644 src/dotnetCampus.Logger/Log.cs create mode 100644 src/dotnetCampus.Logger/LogBuilder.cs create mode 100644 src/dotnetCampus.Logger/LogExtensions.cs create mode 100644 src/dotnetCampus.Logger/Output/ILogWriter.cs create mode 100644 src/dotnetCampus.Logger/SourceGenerators/BridgeLogger.cs create mode 100644 src/dotnetCampus.Logger/SourceGenerators/ILoggerBridge.cs create mode 100644 src/dotnetCampus.Logger/SourceGenerators/LogFactory.cs create mode 100644 src/dotnetCampus.Logger/SourceGenerators/README.md create mode 100644 src/dotnetCampus.Logger/Writers/ConsoleColors.cs create mode 100644 src/dotnetCampus.Logger/Writers/ConsoleLogger.cs create mode 100644 src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs diff --git a/Directory.Build.props b/Directory.Build.props index d592f2b..c647b82 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,6 @@ latest enable - true $(MSBuildThisFileDirectory)artifacts $(MSBuildThisFileDirectory) diff --git a/dotnetCampus.Logger.sln b/dotnetCampus.Logger.sln index 004359c..d462c69 100644 --- a/dotnetCampus.Logger.sln +++ b/dotnetCampus.Logger.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30804.86 MinimumVisualStudioVersion = 15.0.26124.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5D196596-756D-45C2-8A05-C8E4AB8A36E6}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.Logger", "src\dotnetCampus.Logger\dotnetCampus.Logger.csproj", "{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{AFB0DF31-474C-4ACB-88C6-DD00552D5B5A}" @@ -103,7 +101,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272} = {5D196596-756D-45C2-8A05-C8E4AB8A36E6} {C282F00B-0C42-491F-AC0D-967407E1C418} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} {36367E21-BABC-4CC4-891E-CEAF56D66B68} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} diff --git a/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj b/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj index 6cb9224..9f99a80 100644 --- a/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj +++ b/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj @@ -19,6 +19,7 @@ + diff --git a/samples/LoggerSample.MainApp.Avalonia/Program.cs b/samples/LoggerSample.MainApp.Avalonia/Program.cs index 98ecccb..7a77fdc 100644 --- a/samples/LoggerSample.MainApp.Avalonia/Program.cs +++ b/samples/LoggerSample.MainApp.Avalonia/Program.cs @@ -1,5 +1,6 @@ using Avalonia; using System; +using dotnetCampus.Logging; namespace dotnetCampus.LoggerSample.Avalonia; @@ -9,8 +10,16 @@ class Program // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. [STAThread] - public static void Main(string[] args) => BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); + public static void Main(string[] args) + { + // var logger = new LogBuilder() + // .WithO + // .AddWriter(new ConsoleLogWriter().WithOptions) + // .Build(); + + BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + } // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() diff --git a/src/dotnetCampus.Logger/Building/CompositeLogger.cs b/src/dotnetCampus.Logger/Building/CompositeLogger.cs new file mode 100644 index 0000000..f56ebec --- /dev/null +++ b/src/dotnetCampus.Logger/Building/CompositeLogger.cs @@ -0,0 +1,6 @@ +namespace dotnetCampus.Logging.Building; + +public class CompositeLogger +{ + +} diff --git a/src/dotnetCampus.Logger/CompositeLogger.cs b/src/dotnetCampus.Logger/CompositeLogger.cs new file mode 100644 index 0000000..a6783c2 --- /dev/null +++ b/src/dotnetCampus.Logger/CompositeLogger.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using dotnetCampus.Logging.Configurations; + +namespace dotnetCampus.Logging; + +public class CompositeLogger : ILogger +{ + private InheritedConfiguration Configuration { get; } = new(); + + public List Writers { get; } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + throw new NotImplementedException(); + } +} diff --git a/src/dotnetCampus.Logger/Configurations/InheritedConfiguration.cs b/src/dotnetCampus.Logger/Configurations/InheritedConfiguration.cs new file mode 100644 index 0000000..626e58c --- /dev/null +++ b/src/dotnetCampus.Logger/Configurations/InheritedConfiguration.cs @@ -0,0 +1,12 @@ +namespace dotnetCampus.Logging.Configurations; + +internal class InheritedConfiguration where T : notnull +{ + private InheritedConfiguration? _parent; + + public void AddChild(TChild child) + where TChild : InheritedConfiguration + { + child._parent = this; + } +} diff --git a/src/dotnetCampus.Logger/Configurations/LogOptions.cs b/src/dotnetCampus.Logger/Configurations/LogOptions.cs new file mode 100644 index 0000000..7297054 --- /dev/null +++ b/src/dotnetCampus.Logger/Configurations/LogOptions.cs @@ -0,0 +1,6 @@ +namespace dotnetCampus.Logging.Configurations; + +public class LogOptions +{ + +} diff --git a/src/dotnetCampus.Logger/Log.cs b/src/dotnetCampus.Logger/Log.cs new file mode 100644 index 0000000..8dd14ce --- /dev/null +++ b/src/dotnetCampus.Logger/Log.cs @@ -0,0 +1,6 @@ +namespace dotnetCampus.Logging; + +public static class Log +{ + +} diff --git a/src/dotnetCampus.Logger/LogBuilder.cs b/src/dotnetCampus.Logger/LogBuilder.cs new file mode 100644 index 0000000..4aca81d --- /dev/null +++ b/src/dotnetCampus.Logger/LogBuilder.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace dotnetCampus.Logging; + +public class LogBuilder +{ + private List _writers = []; + + public CompositeLogger Build() + { + var logger = new CompositeLogger(); + return logger; + } +} diff --git a/src/dotnetCampus.Logger/LogExtensions.cs b/src/dotnetCampus.Logger/LogExtensions.cs new file mode 100644 index 0000000..27b51bc --- /dev/null +++ b/src/dotnetCampus.Logger/LogExtensions.cs @@ -0,0 +1,390 @@ +using System; + +namespace dotnetCampus.Logging; + +/// +/// ILogger extension methods for common scenarios. +/// +public static class LoggerExtensions +{ + //------------------------------------------DEBUG------------------------------------------// + + /// + /// Formats and writes a debug log message. + /// + /// The to write to. + /// The event id associated with the log. + /// The event id associated with the log. + /// The exception to log. + /// An object array that contains zero or more objects to format. + /// logger.LogDebug(0, exception, "Error while processing request from {Address}", address) + public static void LogDebug(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Debug, eventId, exception, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// The to write to. + /// The event id associated with the log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogDebug(0, "Processing request from {Address}", address) + public static void LogDebug(this ILogger logger, EventId eventId, string? message, params object?[] args) + { + logger.Log(LogLevel.Debug, eventId, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// The to write to. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogDebug(exception, "Error while processing request from {Address}", address) + public static void LogDebug(this ILogger logger, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Debug, exception, message, args); + } + + /// + /// Formats and writes a debug log message. + /// + /// The to write to. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogDebug("Processing request from {Address}", address) + public static void LogDebug(this ILogger logger, string? message, params object?[] args) + { + logger.Log(LogLevel.Debug, message, args); + } + + //------------------------------------------TRACE------------------------------------------// + + /// + /// Formats and writes a trace log message. + /// + /// The to write to. + /// The event id associated with the log. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogTrace(0, exception, "Error while processing request from {Address}", address) + public static void LogTrace(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Trace, eventId, exception, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// The to write to. + /// The event id associated with the log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogTrace(0, "Processing request from {Address}", address) + public static void LogTrace(this ILogger logger, EventId eventId, string? message, params object?[] args) + { + logger.Log(LogLevel.Trace, eventId, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// The to write to. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogTrace(exception, "Error while processing request from {Address}", address) + public static void LogTrace(this ILogger logger, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Trace, exception, message, args); + } + + /// + /// Formats and writes a trace log message. + /// + /// The to write to. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogTrace("Processing request from {Address}", address) + public static void LogTrace(this ILogger logger, string? message, params object?[] args) + { + logger.Log(LogLevel.Trace, message, args); + } + + //------------------------------------------INFORMATION------------------------------------------// + + /// + /// Formats and writes an informational log message. + /// + /// The to write to. + /// The event id associated with the log. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogInformation(0, exception, "Error while processing request from {Address}", address) + public static void LogInformation(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Information, eventId, exception, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// The to write to. + /// The event id associated with the log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogInformation(0, "Processing request from {Address}", address) + public static void LogInformation(this ILogger logger, EventId eventId, string? message, params object?[] args) + { + logger.Log(LogLevel.Information, eventId, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// The to write to. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogInformation(exception, "Error while processing request from {Address}", address) + public static void LogInformation(this ILogger logger, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Information, exception, message, args); + } + + /// + /// Formats and writes an informational log message. + /// + /// The to write to. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogInformation("Processing request from {Address}", address) + public static void LogInformation(this ILogger logger, string? message, params object?[] args) + { + logger.Log(LogLevel.Information, message, args); + } + + //------------------------------------------WARNING------------------------------------------// + + /// + /// Formats and writes a warning log message. + /// + /// The to write to. + /// The event id associated with the log. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogWarning(0, exception, "Error while processing request from {Address}", address) + public static void LogWarning(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Warning, eventId, exception, message, args); + } + + /// + /// Formats and writes a warning log message. + /// + /// The to write to. + /// The event id associated with the log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogWarning(0, "Processing request from {Address}", address) + public static void LogWarning(this ILogger logger, EventId eventId, string? message, params object?[] args) + { + logger.Log(LogLevel.Warning, eventId, message, args); + } + + /// + /// Formats and writes a warning log message. + /// + /// The to write to. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogWarning(exception, "Error while processing request from {Address}", address) + public static void LogWarning(this ILogger logger, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Warning, exception, message, args); + } + + /// + /// Formats and writes a warning log message. + /// + /// The to write to. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogWarning("Processing request from {Address}", address) + public static void LogWarning(this ILogger logger, string? message, params object?[] args) + { + logger.Log(LogLevel.Warning, message, args); + } + + //------------------------------------------ERROR------------------------------------------// + + /// + /// Formats and writes an error log message. + /// + /// The to write to. + /// The event id associated with the log. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogError(0, exception, "Error while processing request from {Address}", address) + public static void LogError(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Error, eventId, exception, message, args); + } + + /// + /// Formats and writes an error log message. + /// + /// The to write to. + /// The event id associated with the log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogError(0, "Processing request from {Address}", address) + public static void LogError(this ILogger logger, EventId eventId, string? message, params object?[] args) + { + logger.Log(LogLevel.Error, eventId, message, args); + } + + /// + /// Formats and writes an error log message. + /// + /// The to write to. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogError(exception, "Error while processing request from {Address}", address) + public static void LogError(this ILogger logger, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Error, exception, message, args); + } + + /// + /// Formats and writes an error log message. + /// + /// The to write to. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogError("Processing request from {Address}", address) + public static void LogError(this ILogger logger, string? message, params object?[] args) + { + logger.Log(LogLevel.Error, message, args); + } + + //------------------------------------------CRITICAL------------------------------------------// + + /// + /// Formats and writes a critical log message. + /// + /// The to write to. + /// The event id associated with the log. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogCritical(0, exception, "Error while processing request from {Address}", address) + public static void LogCritical(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Critical, eventId, exception, message, args); + } + + /// + /// Formats and writes a critical log message. + /// + /// The to write to. + /// The event id associated with the log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogCritical(0, "Processing request from {Address}", address) + public static void LogCritical(this ILogger logger, EventId eventId, string? message, params object?[] args) + { + logger.Log(LogLevel.Critical, eventId, message, args); + } + + /// + /// Formats and writes a critical log message. + /// + /// The to write to. + /// The exception to log. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogCritical(exception, "Error while processing request from {Address}", address) + public static void LogCritical(this ILogger logger, Exception? exception, string? message, params object?[] args) + { + logger.Log(LogLevel.Critical, exception, message, args); + } + + /// + /// Formats and writes a critical log message. + /// + /// The to write to. + /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" + /// An object array that contains zero or more objects to format. + /// logger.LogCritical("Processing request from {Address}", address) + public static void LogCritical(this ILogger logger, string? message, params object?[] args) + { + logger.Log(LogLevel.Critical, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// The to write to. + /// Entry will be written on this level. + /// Format string of the log message. + /// An object array that contains zero or more objects to format. + public static void Log(this ILogger logger, LogLevel logLevel, string? message, params object?[] args) + { + logger.Log(logLevel, 0, null, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// The to write to. + /// Entry will be written on this level. + /// The event id associated with the log. + /// Format string of the log message. + /// An object array that contains zero or more objects to format. + public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, string? message, params object?[] args) + { + logger.Log(logLevel, eventId, null, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// The to write to. + /// Entry will be written on this level. + /// The exception to log. + /// Format string of the log message. + /// An object array that contains zero or more objects to format. + public static void Log(this ILogger logger, LogLevel logLevel, Exception? exception, string? message, params object?[] args) + { + logger.Log(logLevel, 0, exception, message, args); + } + + /// + /// Formats and writes a log message at the specified log level. + /// + /// The to write to. + /// Entry will be written on this level. + /// The event id associated with the log. + /// The exception to log. + /// Format string of the log message. + /// An object array that contains zero or more objects to format. + public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, Exception? exception, string? message, params object?[] args) + { + if (logger == null) + { + throw new ArgumentNullException(nameof(logger)); + } + + // logger.Log(logLevel, eventId, new FormattedLogValues(message, args), exception, _messageFormatter); + } +} diff --git a/src/dotnetCampus.Logger/Output/ILogWriter.cs b/src/dotnetCampus.Logger/Output/ILogWriter.cs new file mode 100644 index 0000000..7ea42b2 --- /dev/null +++ b/src/dotnetCampus.Logger/Output/ILogWriter.cs @@ -0,0 +1,6 @@ +namespace dotnetCampus.Logging.Output; + +public class ILogWriter +{ + +} diff --git a/src/dotnetCampus.Logger/SourceGenerators/BridgeLogger.cs b/src/dotnetCampus.Logger/SourceGenerators/BridgeLogger.cs new file mode 100644 index 0000000..2ff4f34 --- /dev/null +++ b/src/dotnetCampus.Logger/SourceGenerators/BridgeLogger.cs @@ -0,0 +1,15 @@ +using System; + +namespace dotnetCampus.Logging.SourceGenerators; + +internal class BridgeLogger : ILogger +{ + private ILoggerBridge? _bridge; + + private ILoggerBridge? Bridge => _bridge ??= LogFactory.TryGetBridge(); + + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + _bridge?.Log((int)logLevel, eventId.Id, eventId.Name, state, exception, formatter); + } +} diff --git a/src/dotnetCampus.Logger/SourceGenerators/ILoggerBridge.cs b/src/dotnetCampus.Logger/SourceGenerators/ILoggerBridge.cs new file mode 100644 index 0000000..835c4f9 --- /dev/null +++ b/src/dotnetCampus.Logger/SourceGenerators/ILoggerBridge.cs @@ -0,0 +1,25 @@ +using System; + +namespace dotnetCampus.Logging.SourceGenerators; + +/// +/// 仅由基本类型构成的日志源。用于源生成器将无依赖库中的日志重定向到应用程序聚合日志系统中。 +/// +public interface ILoggerBridge +{ + /// Writes a log entry. + /// Entry will be written on this level. + /// Id of the event. + /// Name of the event. + /// The entry to be written. Can be also an object. + /// The exception related to this entry. + /// Function to create a message of the and . + /// The type of the object to be written. + void Log( + int logLevel, + int? eventId, + string? eventName, + TState state, + Exception? exception, + Func formatter); +} diff --git a/src/dotnetCampus.Logger/SourceGenerators/LogFactory.cs b/src/dotnetCampus.Logger/SourceGenerators/LogFactory.cs new file mode 100644 index 0000000..16779bb --- /dev/null +++ b/src/dotnetCampus.Logger/SourceGenerators/LogFactory.cs @@ -0,0 +1,16 @@ +namespace dotnetCampus.Logging.SourceGenerators; + +public static class LogFactory +{ + private static ILoggerBridge? _bridge; + + internal static ILoggerBridge? TryGetBridge() + { + return _bridge; + } + + public static void Bridge(ILoggerBridge bridge) + { + _bridge = bridge; + } +} diff --git a/src/dotnetCampus.Logger/SourceGenerators/README.md b/src/dotnetCampus.Logger/SourceGenerators/README.md new file mode 100644 index 0000000..c30c977 --- /dev/null +++ b/src/dotnetCampus.Logger/SourceGenerators/README.md @@ -0,0 +1,5 @@ +# 源生成器 + +此文件夹中的所有类型将借助源生成器添加到目标项目中。 + +命名空间将全部被替换为 `$(RootNamespace).Logging`。 diff --git a/src/dotnetCampus.Logger/Writers/ConsoleColors.cs b/src/dotnetCampus.Logger/Writers/ConsoleColors.cs new file mode 100644 index 0000000..c8312ac --- /dev/null +++ b/src/dotnetCampus.Logger/Writers/ConsoleColors.cs @@ -0,0 +1,69 @@ +namespace dotnetCampus.Logging.Writers; + +/// +/// 包含控制台输出颜色的字符串常量。 +/// +internal static class ConsoleColors +{ + public const string Reset = "\u001b[0m"; + + public static class Foreground + { + #region 4-bit colors + + public const string Black = "\u001b[30m"; + public const string Red = "\u001b[31m"; + public const string Green = "\u001b[32m"; + public const string Yellow = "\u001b[33m"; + public const string Blue = "\u001b[34m"; + public const string Magenta = "\u001b[35m"; + public const string Cyan = "\u001b[36m"; + public const string White = "\u001b[37m"; + public const string BrightBlack = "\u001b[90m"; + public const string BrightRed = "\u001b[91m"; + public const string BrightGreen = "\u001b[92m"; + public const string BrightYellow = "\u001b[93m"; + public const string BrightBlue = "\u001b[94m"; + public const string BrightMagenta = "\u001b[95m"; + public const string BrightCyan = "\u001b[96m"; + public const string BrightWhite = "\u001b[97m"; + + #endregion + } + + public static class Background + { + #region 4-bit colors + + public const string Black = "\u001b[40m"; + public const string Red = "\u001b[41m"; + public const string Green = "\u001b[42m"; + public const string Yellow = "\u001b[43m"; + public const string Blue = "\u001b[44m"; + public const string Magenta = "\u001b[45m"; + public const string Cyan = "\u001b[46m"; + public const string White = "\u001b[47m"; + public const string BrightBlack = "\u001b[100m"; + public const string BrightRed = "\u001b[101m"; + public const string BrightGreen = "\u001b[102m"; + public const string BrightYellow = "\u001b[103m"; + public const string BrightBlue = "\u001b[104m"; + public const string BrightMagenta = "\u001b[105m"; + public const string BrightCyan = "\u001b[106m"; + public const string BrightWhite = "\u001b[107m"; + + #endregion + } + + public static class Decoration + { + public const string Bold = "\u001b[1m"; + public const string Dim = "\u001b[2m"; + public const string Italic = "\u001b[3m"; + public const string Underline = "\u001b[4m"; + public const string Blink = "\u001b[5m"; + public const string Reverse = "\u001b[7m"; + public const string Hidden = "\u001b[8m"; + public const string Strikethrough = "\u001b[9m"; + } +} diff --git a/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs b/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs new file mode 100644 index 0000000..bf47ad9 --- /dev/null +++ b/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs @@ -0,0 +1,19 @@ +using System; +using dotnetCampus.Logging.Configurations; + +namespace dotnetCampus.Logging.Writers; + +public class ConsoleLogger : ILogger +{ + internal InheritedConfiguration Configuration { get; } = new(); + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + + } + +} + +public class ConsoleLogOptions : LogOptions +{ +} diff --git a/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs b/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs new file mode 100644 index 0000000..decbeef --- /dev/null +++ b/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs @@ -0,0 +1,11 @@ +using System; + +namespace dotnetCampus.Logging.Writers; + +public class MemoryCacheLogger : ILogger +{ + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + throw new NotImplementedException(); + } +} diff --git a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj index cff92ef..c46c5bf 100644 --- a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj +++ b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj @@ -1,27 +1,36 @@ + - net8.0;net6.0;netstandard2.0;net45 + true + + + + dotnetCampus.Logging + true true true + + + CS1591 + + + - + - + true From 1bc17c3fc45b50b31b3fcc28c0ea987f34e74226 Mon Sep 17 00:00:00 2001 From: walterlv Date: Tue, 7 May 2024 16:20:57 +0800 Subject: [PATCH 08/49] =?UTF-8?q?=E5=B0=BD=E9=87=8F=E9=81=BF=E5=85=8D=20ne?= =?UTF-8?q?t8=20=E7=9A=84=E5=86=99=E6=B3=95=E5=9C=A8=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E6=A1=86=E6=9E=B6=E5=86=99=E4=B8=8D=E5=87=BA=E6=9D=A5=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Properties/Compatibility.cs | 38 +++++++++++++++++++ .../Properties/GlobalUsings.cs | 18 +++++++++ .../dotnetCampus.Logger.csproj | 5 +++ 3 files changed, 61 insertions(+) create mode 100644 src/dotnetCampus.Logger/Properties/Compatibility.cs create mode 100644 src/dotnetCampus.Logger/Properties/GlobalUsings.cs diff --git a/src/dotnetCampus.Logger/Properties/Compatibility.cs b/src/dotnetCampus.Logger/Properties/Compatibility.cs new file mode 100644 index 0000000..a6e4d9c --- /dev/null +++ b/src/dotnetCampus.Logger/Properties/Compatibility.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using SMath = System.Math; + +namespace dotnetCampus.Logging.Properties; + +internal static class Compatibility +{ +#if NET8_0_OR_GREATER +#else + internal static ImmutableHashSetString ToImmutableHashSet(this IEnumerable source) + { + return [..source]; + } +#endif + +#if NET6_0_OR_GREATER +#else + internal static string AsSpan(this string text) + { + return text; + } + + internal static bool Contains(this string text, char value) + { + return text.Contains(value.ToString()); + } + + internal static string Slice(this string text, int start, int length) + { + return text.Substring(start, length); + } + + public static int Clamp(int value, int min, int max) + { + return SMath.Max(min, SMath.Min(max, value)); + } +#endif +} diff --git a/src/dotnetCampus.Logger/Properties/GlobalUsings.cs b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs new file mode 100644 index 0000000..88df72d --- /dev/null +++ b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs @@ -0,0 +1,18 @@ +global using dotnetCampus.Logging.Properties; + +// .NET 8.0 or later +#if NET8_0_OR_GREATER +global using System.Collections.Immutable; +global using ImmutableArrayILogger = System.Collections.Immutable.ImmutableArray; +global using ImmutableHashSetString = System.Collections.Immutable.ImmutableHashSet; +#else +global using ImmutableArrayILogger = System.Collections.Generic.List; +global using ImmutableHashSetString = System.Collections.Generic.HashSet; +#endif + +// .NET 6.0 or later +#if NET6_0_OR_GREATER +global using Math = System.Math; +#else +global using Math = dotnetCampus.Logging.Properties.Compatibility; +#endif diff --git a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj index c46c5bf..fc1f1cf 100644 --- a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj +++ b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj @@ -24,6 +24,11 @@ CS1591 + + + + 0 + From 8d23d2560f5d974cd853c057dd4d107f12530555 Mon Sep 17 00:00:00 2001 From: walterlv Date: Tue, 7 May 2024 16:21:38 +0800 Subject: [PATCH 09/49] =?UTF-8?q?=E5=90=88=E5=85=A5=E4=B8=80=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=97=A5=E5=BF=97=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dotnetCampus.Logger/CompositeLogger.cs | 35 +++- .../Configurations/InheritedConfiguration.cs | 30 ++- .../Configurations/LogOptions.cs | 10 +- src/dotnetCampus.Logger/LogBuilder.cs | 54 +++++- .../Writers/ConsoleLogger.cs | 174 +++++++++++++++++- .../ConsoleColors.cs | 2 +- .../RepeatLoggerDetector.cs | 29 +++ 7 files changed, 318 insertions(+), 16 deletions(-) rename src/dotnetCampus.Logger/Writers/{ => ConsoleLoggerHelpers}/ConsoleColors.cs (97%) create mode 100644 src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/RepeatLoggerDetector.cs diff --git a/src/dotnetCampus.Logger/CompositeLogger.cs b/src/dotnetCampus.Logger/CompositeLogger.cs index a6783c2..5b497b7 100644 --- a/src/dotnetCampus.Logger/CompositeLogger.cs +++ b/src/dotnetCampus.Logger/CompositeLogger.cs @@ -1,17 +1,44 @@ using System; -using System.Collections.Generic; using dotnetCampus.Logging.Configurations; namespace dotnetCampus.Logging; +/// +/// 一个聚合多个日志记录器的综合记录器,通常作为应用程序的主要日志记录器。 +/// public class CompositeLogger : ILogger { - private InheritedConfiguration Configuration { get; } = new(); + internal CompositeLogger(LogOptions options) + { + Configuration = new InheritedConfiguration(options); + } - public List Writers { get; } + private InheritedConfiguration Configuration { get; } + + /// + /// 高于或等于此级别的日志才会被记录。 + /// + public LogLevel Level + { + get => Configuration.GetValue(o => o.LogLevel); + set => Configuration.SetValue(o => o.LogLevel = value); + } + + /// + /// 当前所有的日志记录器。 + /// + public required ImmutableArrayILogger Writers { get; init; } public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - throw new NotImplementedException(); + if (logLevel < Level) + { + return; + } + + foreach (var writer in Writers) + { + writer.Log(logLevel, eventId, state, exception, formatter); + } } } diff --git a/src/dotnetCampus.Logger/Configurations/InheritedConfiguration.cs b/src/dotnetCampus.Logger/Configurations/InheritedConfiguration.cs index 626e58c..b8b72ce 100644 --- a/src/dotnetCampus.Logger/Configurations/InheritedConfiguration.cs +++ b/src/dotnetCampus.Logger/Configurations/InheritedConfiguration.cs @@ -1,12 +1,36 @@ -namespace dotnetCampus.Logging.Configurations; +using System; -internal class InheritedConfiguration where T : notnull +namespace dotnetCampus.Logging.Configurations; + +internal class InheritedConfiguration(T options) + where T : notnull { private InheritedConfiguration? _parent; - public void AddChild(TChild child) + internal void AddChild(TChild child) where TChild : InheritedConfiguration { child._parent = this; } + + internal TValue GetValue(Func getter, TValue defaultValue = default!) + { + var value = getter(options); + if (value is not null) + { + return value; + } + + if (_parent is not null) + { + return _parent.GetValue(getter, defaultValue); + } + + return defaultValue; + } + + internal void SetValue(Func setter) + { + setter(options); + } } diff --git a/src/dotnetCampus.Logger/Configurations/LogOptions.cs b/src/dotnetCampus.Logger/Configurations/LogOptions.cs index 7297054..477a50d 100644 --- a/src/dotnetCampus.Logger/Configurations/LogOptions.cs +++ b/src/dotnetCampus.Logger/Configurations/LogOptions.cs @@ -1,6 +1,12 @@ namespace dotnetCampus.Logging.Configurations; -public class LogOptions +/// +/// 用于配置日志记录器的选项。 +/// +public record LogOptions { - + /// + /// 高于或等于此级别的日志才会被记录。 + /// + public LogLevel LogLevel { get; set; } } diff --git a/src/dotnetCampus.Logger/LogBuilder.cs b/src/dotnetCampus.Logger/LogBuilder.cs index 4aca81d..6583503 100644 --- a/src/dotnetCampus.Logger/LogBuilder.cs +++ b/src/dotnetCampus.Logger/LogBuilder.cs @@ -1,14 +1,60 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using dotnetCampus.Logging.Configurations; namespace dotnetCampus.Logging; -public class LogBuilder +public class LoggerBuilder { - private List _writers = []; + private LogOptions? _options; + private readonly List _writers = []; + private Action? _flusher; + + /// + /// 调用此方法以便在日志模块初始化完成前先对所有记录的日志进行缓存,以便在日志模块初始化完成后再将缓存的日志写入到日志文件中。 + /// + /// + /// 在日志模块初始化完成后,将缓存的日志写入到日志文件中。 + /// 如果在 Program.cs 类中,请直接传入源生成器生成的 Log 属性作为此参数。 + /// + /// + /// 此方法不会在运行时起任何作用,仅决定编译时在 Program.cs 类中所生成的日志记录器。生成后,你可以在 Program.cs 类中使用 Log.Info 等方法进行日志记录。 + /// + public LoggerBuilder UseMemoryCache(Action flusher) + { + _flusher = flusher; + return this; + } + + public LoggerBuilder UseLevel(LogLevel level) + { + _options ??= new LogOptions(); + _options.LogLevel = level; + return this; + } + + public LoggerBuilder WithOptions(LogOptions options) + { + _options = options; + return this; + } + + public LoggerBuilder AddWriter(ILogger writer) + { + _writers.Add(writer); + return this; + } public CompositeLogger Build() { - var logger = new CompositeLogger(); + var logger = new CompositeLogger(_options ?? new LogOptions()) + { + Writers = [.._writers], + }; + if (_flusher is { } flusher) + { + flusher(logger); + } return logger; } } diff --git a/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs b/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs index bf47ad9..f0ea90e 100644 --- a/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs +++ b/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs @@ -1,19 +1,189 @@ using System; +using System.IO; using dotnetCampus.Logging.Configurations; +using dotnetCampus.Logging.Writers.ConsoleLoggerHelpers; +using C = dotnetCampus.Logging.Writers.ConsoleLoggerHelpers.ConsoleColors; +using B = dotnetCampus.Logging.Writers.ConsoleLoggerHelpers.ConsoleColors.Background; +using D = dotnetCampus.Logging.Writers.ConsoleLoggerHelpers.ConsoleColors.Decoration; +using F = dotnetCampus.Logging.Writers.ConsoleLoggerHelpers.ConsoleColors.Foreground; namespace dotnetCampus.Logging.Writers; public class ConsoleLogger : ILogger { - internal InheritedConfiguration Configuration { get; } = new(); + private readonly RepeatLoggerDetector _repeat = new(ClearAndMoveToLastLine); + + /// + /// 高于或等于此级别的日志才会被记录。 + /// + public LogLevel Level { get; set; } public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { + if (logLevel < Level) + { + return; + } + + var message = formatter(state, exception); + if (!IsTagEnabled(message)) + { + return; + } + + LogCore(logLevel, exception, message, m => logLevel switch + { + LogLevel.Trace => $"{TraceTag} {TraceText}{m}{Reset}", + LogLevel.Debug => $"{DebugTag} {DebugText}{m}{Reset}", + LogLevel.Information => $"{InformationTag} {InformationText}{m}{Reset}", + LogLevel.Warning => $"{WarningTag} {WarningText}{m}{Reset}", + LogLevel.Error => $"{ErrorTag} {ErrorText}{m}{Reset}", + LogLevel.Critical => $"{CriticalTag} {CriticalText}{m}{Reset}", + _ => null, + }); + } + + private void LogCore(LogLevel logLevel, Exception? exception, string message, Func formatter) + { + if (_repeat.RepeatOrResetLastLog(logLevel, message, exception) is var count and > 1) + { + ConsoleMultilineMessage($"上述日志已重复 {count} 次", formatter, true); + } + else if (exception is null) + { + ConsoleMultilineMessage(message, formatter); + } + else + { + var tag = logLevel switch + { + LogLevel.Warning => WarningExceptionTag, + LogLevel.Error => ErrorExceptionTag, + LogLevel.Critical => CriticalExceptionTag, + _ => "", + }; + ConsoleMultilineMessage($""" + {message} + {tag}{exception.GetType().Name}: {exception.Message} + """, formatter); + } + } + + private static void ConsoleMultilineMessage(string message, Func formatter, bool forceSingleLine = false) + { + if (forceSingleLine || !message.Contains('\n')) + { + Console.WriteLine(formatter(message)); + } + else + { + using var reader = new StringReader(message); + while (reader.ReadLine() is { } line) + { + Console.WriteLine(formatter(line)); + } + } + } + + /// + /// 当前已设置的过滤标签。 + /// + private static ImmutableHashSetString ConsoleFilterTags { get; set; } = []; + + /// + /// 高于或等于此级别的日志才会被记录。 + /// + public ConsoleLogger UseLevel(LogLevel level) + { + Level = level; + return this; + } + + /// + /// 从命令行参数中提取过滤标签。 + /// + /// 命令行参数。 + public ConsoleLogger FilterConsoleTagsFromCommandLineArgs(string[] args) + { + for (var i = 0; i < args.Length; i++) + { + if (args[i] == "--log-console-tags" && i + 1 < args.Length) + { + ConsoleFilterTags = args[i + 1].Split([',', ';', ' ']).ToImmutableHashSet(); + break; + } + } + return this; + } + + /// + /// 判断某个日志是否满足当前标签过滤条件。 + /// + /// 要判断的日志原文。 + /// 是否满足过滤条件。 + private static bool IsTagEnabled(string text) + { + if (ConsoleFilterTags.Count is 0) + { + return true; + } + + var start = text.IndexOf('['); + if (start == -1) + { + return true; + } + var end = text.IndexOf(']', start); + if (end == -1) + { + return true; + } + var tag = text.AsSpan().Slice(start + 1, end - start - 1); + return ConsoleFilterTags.Contains(tag.ToString()); } + private static void ClearAndMoveToLastLine(int repeatCount) + { + if (repeatCount > 2) + { + try + { + var desiredY = Console.CursorTop - 1; + var y = Math.Clamp(desiredY, 0, Console.WindowHeight - 1); + Console.SetCursorPosition(0, y); + Console.Write(new string(' ', Console.WindowWidth)); + Console.SetCursorPosition(0, y); + } + catch (IOException) + { + // 日志记录时,如果无法移动光标,就放弃移动。 + // 通常是因为当前输出位置不在缓冲区内。 + } + } + } + + private const string Reset = C.Reset; + private const string DebugText = F.White; + private const string TraceText = F.BrightBlack; + private const string InformationText = F.Green + D.Bold; + private const string WarningText = F.Yellow; + private const string ErrorText = F.BrightRed; + private const string CriticalText = F.Red; + + private static string TraceTag => $"{B.Black}{F.BrightBlack}[{DateTime.Now:HH:mm:ss.fff}]{Reset}"; + private static string DebugTag => $"{B.BrightBlack}{F.White}[{DateTime.Now:HH:mm:ss.fff}]{Reset}"; + private static string InformationTag => $"{B.Green}{F.Black}[{DateTime.Now:HH:mm:ss.fff}]{Reset}"; + private static string WarningTag => $"{B.Yellow}{F.Black}[{DateTime.Now:HH:mm:ss.fff}]{Reset}"; + private static string ErrorTag => $"{B.BrightRed}{F.Black}[{DateTime.Now:HH:mm:ss.fff}]{Reset}"; + private static string CriticalTag => $"{B.Red}{F.Black}[{DateTime.Now:HH:mm:ss.fff}]{Reset}"; + + private static string WarningExceptionTag => $"{B.Yellow}{F.Black} ! {Reset}{WarningText} "; + private static string ErrorExceptionTag => $"{B.BrightRed}{F.Black} X {Reset}{ErrorText} "; + private static string CriticalExceptionTag => $"{B.Red}{F.Black} 💥 {Reset}{CriticalText} "; } -public class ConsoleLogOptions : LogOptions + +public record ConsoleLogOptions : LogOptions { } diff --git a/src/dotnetCampus.Logger/Writers/ConsoleColors.cs b/src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/ConsoleColors.cs similarity index 97% rename from src/dotnetCampus.Logger/Writers/ConsoleColors.cs rename to src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/ConsoleColors.cs index c8312ac..be71db8 100644 --- a/src/dotnetCampus.Logger/Writers/ConsoleColors.cs +++ b/src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/ConsoleColors.cs @@ -1,4 +1,4 @@ -namespace dotnetCampus.Logging.Writers; +namespace dotnetCampus.Logging.Writers.ConsoleLoggerHelpers; /// /// 包含控制台输出颜色的字符串常量。 diff --git a/src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/RepeatLoggerDetector.cs b/src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/RepeatLoggerDetector.cs new file mode 100644 index 0000000..820ec0a --- /dev/null +++ b/src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/RepeatLoggerDetector.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading; + +namespace dotnetCampus.Logging.Writers.ConsoleLoggerHelpers; + +internal class RepeatLoggerDetector(Action whenRepeated) +{ + private static volatile int _lastSameItemCount; + private static LogItem? _lastItem; + + internal int RepeatOrResetLastLog(LogLevel level, string message, Exception? exception) + { + var (lastLevel, lastMessage, lastException) = _lastItem ?? new(default(LogLevel), null!, null); + if (level == lastLevel && message == lastMessage && exception == lastException) + { + // 相同日志,标记重复。 + var count = Interlocked.Increment(ref _lastSameItemCount); + whenRepeated(count); + return count; + } + + // 不同日志,设置新重复状态。 + _lastSameItemCount = 1; + _lastItem = new(level, message, exception); + return 1; + } + + private readonly record struct LogItem(LogLevel Level, string Message, Exception? Exception); +} From f925a910d0aa4605a6b48c9270ce71763c2acf99 Mon Sep 17 00:00:00 2001 From: walterlv Date: Tue, 7 May 2024 16:27:42 +0800 Subject: [PATCH 10/49] =?UTF-8?q?=E5=90=88=E5=85=A5=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dotnetCampus.Logger/LogExtensions.cs | 387 ++---------------- .../Properties/GlobalUsings.cs | 2 +- .../Writers/ConsoleLogger.cs | 29 +- .../RepeatLoggerDetector.cs | 2 +- .../Writers/MemoryCacheLogger.cs | 11 - src/dotnetCampus.Logger/Writers/NullLogger.cs | 10 + 6 files changed, 62 insertions(+), 379 deletions(-) rename src/dotnetCampus.Logger/Writers/{ConsoleLoggerHelpers => Helpers}/RepeatLoggerDetector.cs (93%) delete mode 100644 src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs create mode 100644 src/dotnetCampus.Logger/Writers/NullLogger.cs diff --git a/src/dotnetCampus.Logger/LogExtensions.cs b/src/dotnetCampus.Logger/LogExtensions.cs index 27b51bc..eb4695b 100644 --- a/src/dotnetCampus.Logger/LogExtensions.cs +++ b/src/dotnetCampus.Logger/LogExtensions.cs @@ -2,389 +2,68 @@ namespace dotnetCampus.Logging; -/// -/// ILogger extension methods for common scenarios. -/// public static class LoggerExtensions { - //------------------------------------------DEBUG------------------------------------------// - - /// - /// Formats and writes a debug log message. - /// - /// The to write to. - /// The event id associated with the log. - /// The event id associated with the log. - /// The exception to log. - /// An object array that contains zero or more objects to format. - /// logger.LogDebug(0, exception, "Error while processing request from {Address}", address) - public static void LogDebug(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) - { - logger.Log(LogLevel.Debug, eventId, exception, message, args); - } - - /// - /// Formats and writes a debug log message. - /// - /// The to write to. - /// The event id associated with the log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogDebug(0, "Processing request from {Address}", address) - public static void LogDebug(this ILogger logger, EventId eventId, string? message, params object?[] args) - { - logger.Log(LogLevel.Debug, eventId, message, args); - } - - /// - /// Formats and writes a debug log message. - /// - /// The to write to. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogDebug(exception, "Error while processing request from {Address}", address) - public static void LogDebug(this ILogger logger, Exception? exception, string? message, params object?[] args) - { - logger.Log(LogLevel.Debug, exception, message, args); - } - - /// - /// Formats and writes a debug log message. - /// - /// The to write to. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogDebug("Processing request from {Address}", address) - public static void LogDebug(this ILogger logger, string? message, params object?[] args) - { - logger.Log(LogLevel.Debug, message, args); - } - - //------------------------------------------TRACE------------------------------------------// - /// - /// Formats and writes a trace log message. + /// 在开启了追踪的情况下输出日志。(默认开启) /// - /// The to write to. - /// The event id associated with the log. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogTrace(0, exception, "Error while processing request from {Address}", address) - public static void LogTrace(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) + /// 记录日志所使用的记录器。 + /// 要记录的消息,形如 [tag] message + public static void Trace(this ILogger logger, string message) { - logger.Log(LogLevel.Trace, eventId, exception, message, args); + logger.Log(LogLevel.Trace, default, message, null, (s, ex) => message); } /// - /// Formats and writes a trace log message. + /// 记录 debug 级别的日志。 /// - /// The to write to. - /// The event id associated with the log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogTrace(0, "Processing request from {Address}", address) - public static void LogTrace(this ILogger logger, EventId eventId, string? message, params object?[] args) + /// 记录日志所使用的记录器。 + /// 要记录的消息。 + public static void Debug(this ILogger logger, string message) { - logger.Log(LogLevel.Trace, eventId, message, args); + logger.Log(LogLevel.Debug, default, message, null, (s, ex) => message); } /// - /// Formats and writes a trace log message. + /// 正常记录日志。 /// - /// The to write to. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogTrace(exception, "Error while processing request from {Address}", address) - public static void LogTrace(this ILogger logger, Exception? exception, string? message, params object?[] args) + /// 记录日志所使用的记录器。 + /// 要记录的消息。 + public static void Info(this ILogger logger, string message) { - logger.Log(LogLevel.Trace, exception, message, args); + logger.Log(LogLevel.Information, default, message, null, (s, ex) => message); } /// - /// Formats and writes a trace log message. + /// 记录警告日志。 /// - /// The to write to. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogTrace("Processing request from {Address}", address) - public static void LogTrace(this ILogger logger, string? message, params object?[] args) + /// 记录日志所使用的记录器。 + /// 要记录的消息,形如 [tag] message + /// 相关的异常信息。 + public static void Warn(this ILogger logger, string message, Exception? exception = null) { - logger.Log(LogLevel.Trace, message, args); + logger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); } - //------------------------------------------INFORMATION------------------------------------------// - /// - /// Formats and writes an informational log message. + /// 记录错误日志。 /// - /// The to write to. - /// The event id associated with the log. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogInformation(0, exception, "Error while processing request from {Address}", address) - public static void LogInformation(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) + /// 记录日志所使用的记录器。 + /// 要记录的消息,形如 [tag] message + /// 相关的异常信息。 + public static void Error(this ILogger logger, string message, Exception? exception = null) { - logger.Log(LogLevel.Information, eventId, exception, message, args); + logger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); } /// - /// Formats and writes an informational log message. + /// 记录崩溃日志。 /// - /// The to write to. - /// The event id associated with the log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogInformation(0, "Processing request from {Address}", address) - public static void LogInformation(this ILogger logger, EventId eventId, string? message, params object?[] args) + /// 记录日志所使用的记录器。 + /// 要记录的消息,形如 [tag] message + /// 相关的异常信息。 + public static void Fatal(this ILogger logger, string message, Exception? exception = null) { - logger.Log(LogLevel.Information, eventId, message, args); - } - - /// - /// Formats and writes an informational log message. - /// - /// The to write to. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogInformation(exception, "Error while processing request from {Address}", address) - public static void LogInformation(this ILogger logger, Exception? exception, string? message, params object?[] args) - { - logger.Log(LogLevel.Information, exception, message, args); - } - - /// - /// Formats and writes an informational log message. - /// - /// The to write to. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogInformation("Processing request from {Address}", address) - public static void LogInformation(this ILogger logger, string? message, params object?[] args) - { - logger.Log(LogLevel.Information, message, args); - } - - //------------------------------------------WARNING------------------------------------------// - - /// - /// Formats and writes a warning log message. - /// - /// The to write to. - /// The event id associated with the log. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogWarning(0, exception, "Error while processing request from {Address}", address) - public static void LogWarning(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) - { - logger.Log(LogLevel.Warning, eventId, exception, message, args); - } - - /// - /// Formats and writes a warning log message. - /// - /// The to write to. - /// The event id associated with the log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogWarning(0, "Processing request from {Address}", address) - public static void LogWarning(this ILogger logger, EventId eventId, string? message, params object?[] args) - { - logger.Log(LogLevel.Warning, eventId, message, args); - } - - /// - /// Formats and writes a warning log message. - /// - /// The to write to. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogWarning(exception, "Error while processing request from {Address}", address) - public static void LogWarning(this ILogger logger, Exception? exception, string? message, params object?[] args) - { - logger.Log(LogLevel.Warning, exception, message, args); - } - - /// - /// Formats and writes a warning log message. - /// - /// The to write to. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogWarning("Processing request from {Address}", address) - public static void LogWarning(this ILogger logger, string? message, params object?[] args) - { - logger.Log(LogLevel.Warning, message, args); - } - - //------------------------------------------ERROR------------------------------------------// - - /// - /// Formats and writes an error log message. - /// - /// The to write to. - /// The event id associated with the log. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogError(0, exception, "Error while processing request from {Address}", address) - public static void LogError(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) - { - logger.Log(LogLevel.Error, eventId, exception, message, args); - } - - /// - /// Formats and writes an error log message. - /// - /// The to write to. - /// The event id associated with the log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogError(0, "Processing request from {Address}", address) - public static void LogError(this ILogger logger, EventId eventId, string? message, params object?[] args) - { - logger.Log(LogLevel.Error, eventId, message, args); - } - - /// - /// Formats and writes an error log message. - /// - /// The to write to. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogError(exception, "Error while processing request from {Address}", address) - public static void LogError(this ILogger logger, Exception? exception, string? message, params object?[] args) - { - logger.Log(LogLevel.Error, exception, message, args); - } - - /// - /// Formats and writes an error log message. - /// - /// The to write to. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogError("Processing request from {Address}", address) - public static void LogError(this ILogger logger, string? message, params object?[] args) - { - logger.Log(LogLevel.Error, message, args); - } - - //------------------------------------------CRITICAL------------------------------------------// - - /// - /// Formats and writes a critical log message. - /// - /// The to write to. - /// The event id associated with the log. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogCritical(0, exception, "Error while processing request from {Address}", address) - public static void LogCritical(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args) - { - logger.Log(LogLevel.Critical, eventId, exception, message, args); - } - - /// - /// Formats and writes a critical log message. - /// - /// The to write to. - /// The event id associated with the log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogCritical(0, "Processing request from {Address}", address) - public static void LogCritical(this ILogger logger, EventId eventId, string? message, params object?[] args) - { - logger.Log(LogLevel.Critical, eventId, message, args); - } - - /// - /// Formats and writes a critical log message. - /// - /// The to write to. - /// The exception to log. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogCritical(exception, "Error while processing request from {Address}", address) - public static void LogCritical(this ILogger logger, Exception? exception, string? message, params object?[] args) - { - logger.Log(LogLevel.Critical, exception, message, args); - } - - /// - /// Formats and writes a critical log message. - /// - /// The to write to. - /// Format string of the log message in message template format. Example: "User {User} logged in from {Address}" - /// An object array that contains zero or more objects to format. - /// logger.LogCritical("Processing request from {Address}", address) - public static void LogCritical(this ILogger logger, string? message, params object?[] args) - { - logger.Log(LogLevel.Critical, message, args); - } - - /// - /// Formats and writes a log message at the specified log level. - /// - /// The to write to. - /// Entry will be written on this level. - /// Format string of the log message. - /// An object array that contains zero or more objects to format. - public static void Log(this ILogger logger, LogLevel logLevel, string? message, params object?[] args) - { - logger.Log(logLevel, 0, null, message, args); - } - - /// - /// Formats and writes a log message at the specified log level. - /// - /// The to write to. - /// Entry will be written on this level. - /// The event id associated with the log. - /// Format string of the log message. - /// An object array that contains zero or more objects to format. - public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, string? message, params object?[] args) - { - logger.Log(logLevel, eventId, null, message, args); - } - - /// - /// Formats and writes a log message at the specified log level. - /// - /// The to write to. - /// Entry will be written on this level. - /// The exception to log. - /// Format string of the log message. - /// An object array that contains zero or more objects to format. - public static void Log(this ILogger logger, LogLevel logLevel, Exception? exception, string? message, params object?[] args) - { - logger.Log(logLevel, 0, exception, message, args); - } - - /// - /// Formats and writes a log message at the specified log level. - /// - /// The to write to. - /// Entry will be written on this level. - /// The event id associated with the log. - /// The exception to log. - /// Format string of the log message. - /// An object array that contains zero or more objects to format. - public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, Exception? exception, string? message, params object?[] args) - { - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - - // logger.Log(logLevel, eventId, new FormattedLogValues(message, args), exception, _messageFormatter); + logger.Log(LogLevel.Critical, default, message, null, (s, ex) => message); } } diff --git a/src/dotnetCampus.Logger/Properties/GlobalUsings.cs b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs index 88df72d..231d9ae 100644 --- a/src/dotnetCampus.Logger/Properties/GlobalUsings.cs +++ b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs @@ -2,7 +2,6 @@ // .NET 8.0 or later #if NET8_0_OR_GREATER -global using System.Collections.Immutable; global using ImmutableArrayILogger = System.Collections.Immutable.ImmutableArray; global using ImmutableHashSetString = System.Collections.Immutable.ImmutableHashSet; #else @@ -12,6 +11,7 @@ // .NET 6.0 or later #if NET6_0_OR_GREATER +global using System.Collections.Immutable; global using Math = System.Math; #else global using Math = dotnetCampus.Logging.Properties.Compatibility; diff --git a/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs b/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs index f0ea90e..d068249 100644 --- a/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs +++ b/src/dotnetCampus.Logger/Writers/ConsoleLogger.cs @@ -1,7 +1,6 @@ using System; using System.IO; -using dotnetCampus.Logging.Configurations; -using dotnetCampus.Logging.Writers.ConsoleLoggerHelpers; +using dotnetCampus.Logging.Writers.Helpers; using C = dotnetCampus.Logging.Writers.ConsoleLoggerHelpers.ConsoleColors; using B = dotnetCampus.Logging.Writers.ConsoleLoggerHelpers.ConsoleColors.Background; using D = dotnetCampus.Logging.Writers.ConsoleLoggerHelpers.ConsoleColors.Decoration; @@ -11,13 +10,23 @@ namespace dotnetCampus.Logging.Writers; public class ConsoleLogger : ILogger { - private readonly RepeatLoggerDetector _repeat = new(ClearAndMoveToLastLine); + /// + /// 控制台光标控制是否启用。目前可容纳的错误次数为 3 次,当降低到 0 次时,将不再尝试移动光标。 + /// + private int _isCursorMovementEnabled = 3; + + private readonly RepeatLoggerDetector _repeat; /// /// 高于或等于此级别的日志才会被记录。 /// public LogLevel Level { get; set; } + public ConsoleLogger() + { + _repeat = new(ClearAndMoveToLastLine); + } + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { if (logLevel < Level) @@ -143,9 +152,9 @@ private static bool IsTagEnabled(string text) return ConsoleFilterTags.Contains(tag.ToString()); } - private static void ClearAndMoveToLastLine(int repeatCount) + private void ClearAndMoveToLastLine(int repeatCount) { - if (repeatCount > 2) + if (_isCursorMovementEnabled > 0 && repeatCount > 2) { try { @@ -157,8 +166,9 @@ private static void ClearAndMoveToLastLine(int repeatCount) } catch (IOException) { - // 日志记录时,如果无法移动光标,就放弃移动。 - // 通常是因为当前输出位置不在缓冲区内。 + // 日志记录时,如果无法移动光标,说明可能当前输出位置不在缓冲区内。 + // 如果多次尝试失败,则认为当前控制台缓冲区不支持光标移动,遂放弃。 + _isCursorMovementEnabled--; } } } @@ -182,8 +192,3 @@ private static void ClearAndMoveToLastLine(int repeatCount) private static string ErrorExceptionTag => $"{B.BrightRed}{F.Black} X {Reset}{ErrorText} "; private static string CriticalExceptionTag => $"{B.Red}{F.Black} 💥 {Reset}{CriticalText} "; } - - -public record ConsoleLogOptions : LogOptions -{ -} diff --git a/src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/RepeatLoggerDetector.cs b/src/dotnetCampus.Logger/Writers/Helpers/RepeatLoggerDetector.cs similarity index 93% rename from src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/RepeatLoggerDetector.cs rename to src/dotnetCampus.Logger/Writers/Helpers/RepeatLoggerDetector.cs index 820ec0a..c87c391 100644 --- a/src/dotnetCampus.Logger/Writers/ConsoleLoggerHelpers/RepeatLoggerDetector.cs +++ b/src/dotnetCampus.Logger/Writers/Helpers/RepeatLoggerDetector.cs @@ -1,7 +1,7 @@ using System; using System.Threading; -namespace dotnetCampus.Logging.Writers.ConsoleLoggerHelpers; +namespace dotnetCampus.Logging.Writers.Helpers; internal class RepeatLoggerDetector(Action whenRepeated) { diff --git a/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs b/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs deleted file mode 100644 index decbeef..0000000 --- a/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace dotnetCampus.Logging.Writers; - -public class MemoryCacheLogger : ILogger -{ - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) - { - throw new NotImplementedException(); - } -} diff --git a/src/dotnetCampus.Logger/Writers/NullLogger.cs b/src/dotnetCampus.Logger/Writers/NullLogger.cs new file mode 100644 index 0000000..868aa77 --- /dev/null +++ b/src/dotnetCampus.Logger/Writers/NullLogger.cs @@ -0,0 +1,10 @@ +using System; + +namespace dotnetCampus.Logging.Writers; + +internal class NullLogger : ILogger +{ + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + } +} From 6c7bf09948f6deaccb83ce2193af7c3936f489c2 Mon Sep 17 00:00:00 2001 From: walterlv Date: Tue, 7 May 2024 16:48:08 +0800 Subject: [PATCH 11/49] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=BA=90=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotnetCampus.Logger.sln | 14 ++ .../LoggerSample.MainApp.Avalonia.csproj | 1 + .../Properties/launchSettings.json | 9 ++ src/dotnetCampus.Logger.Analyzer/Readme.md | 29 ++++ .../SampleIncrementalSourceGenerator.cs | 130 ++++++++++++++++++ .../SampleSourceGenerator.cs | 56 ++++++++ .../dotnetCampus.Logger.Analyzer.csproj | 26 ++++ 7 files changed, 265 insertions(+) create mode 100644 src/dotnetCampus.Logger.Analyzer/Properties/launchSettings.json create mode 100644 src/dotnetCampus.Logger.Analyzer/Readme.md create mode 100644 src/dotnetCampus.Logger.Analyzer/SampleIncrementalSourceGenerator.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/SampleSourceGenerator.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj diff --git a/dotnetCampus.Logger.sln b/dotnetCampus.Logger.sln index d462c69..70b8762 100644 --- a/dotnetCampus.Logger.sln +++ b/dotnetCampus.Logger.sln @@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{36852775 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.Logger.Tests", "tests\dotnetCampus.Logger.Tests\dotnetCampus.Logger.Tests.csproj", "{D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.Logger.Analyzer", "src\dotnetCampus.Logger.Analyzer\dotnetCampus.Logger.Analyzer.csproj", "{77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -96,6 +98,18 @@ Global {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Release|x64.Build.0 = Release|Any CPU {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Release|x86.ActiveCfg = Release|Any CPU {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB}.Release|x86.Build.0 = Release|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Debug|x64.ActiveCfg = Debug|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Debug|x64.Build.0 = Debug|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Debug|x86.ActiveCfg = Debug|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Debug|x86.Build.0 = Debug|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Release|Any CPU.Build.0 = Release|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Release|x64.ActiveCfg = Release|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Release|x64.Build.0 = Release|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Release|x86.ActiveCfg = Release|Any CPU + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj b/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj index 9f99a80..7ea671d 100644 --- a/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj +++ b/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj @@ -19,6 +19,7 @@ + diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/launchSettings.json b/src/dotnetCampus.Logger.Analyzer/Properties/launchSettings.json new file mode 100644 index 0000000..f3e8f20 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "DebugRoslynSourceGenerator": { + "commandName": "DebugRoslynComponent", + "targetProject": "../dotnetCampus.Logger.Analyzer.Sample/dotnetCampus.Logger.Analyzer.Sample.csproj" + } + } +} \ No newline at end of file diff --git a/src/dotnetCampus.Logger.Analyzer/Readme.md b/src/dotnetCampus.Logger.Analyzer/Readme.md new file mode 100644 index 0000000..9f3424f --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Readme.md @@ -0,0 +1,29 @@ +# Roslyn Source Generators Sample + +A set of three projects that illustrates Roslyn source generators. Enjoy this template to learn from and modify source generators for your own needs. + +## Content +### dotnetCampus.Logger.Analyzer +A .NET Standard project with implementations of sample source generators. +**You must build this project to see the result (generated code) in the IDE.** + +- [SampleSourceGenerator.cs](SampleSourceGenerator.cs): A source generator that creates C# classes based on a text file (in this case, Domain Driven Design ubiquitous language registry). +- [SampleIncrementalSourceGenerator.cs](SampleIncrementalSourceGenerator.cs): A source generator that creates a custom report based on class properties. The target class should be annotated with the `Generators.ReportAttribute` attribute. + +### dotnetCampus.Logger.Analyzer.Sample +A project that references source generators. Note the parameters of `ProjectReference` in [dotnetCampus.Logger.Analyzer.Sample.csproj](../dotnetCampus.Logger.Analyzer.Sample/dotnetCampus.Logger.Analyzer.Sample.csproj), they make sure that the project is referenced as a set of source generators. + +### dotnetCampus.Logger.Analyzer.Tests +Unit tests for source generators. The easiest way to develop language-related features is to start with unit tests. + +## How To? +### How to debug? +- Use the [launchSettings.json](Properties/launchSettings.json) profile. +- Debug tests. + +### How can I determine which syntax nodes I should expect? +Consider installing the Roslyn syntax tree viewer plugin [Rossynt](https://plugins.jetbrains.com/plugin/16902-rossynt/). + +### How to learn more about wiring source generators? +Watch the walkthrough video: [Let’s Build an Incremental Source Generator With Roslyn, by Stefan Pölz](https://youtu.be/azJm_Y2nbAI) +The complete set of information is available in [Source Generators Cookbook](https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md). \ No newline at end of file diff --git a/src/dotnetCampus.Logger.Analyzer/SampleIncrementalSourceGenerator.cs b/src/dotnetCampus.Logger.Analyzer/SampleIncrementalSourceGenerator.cs new file mode 100644 index 0000000..ced0607 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/SampleIncrementalSourceGenerator.cs @@ -0,0 +1,130 @@ +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + + +namespace dotnetCampus.Logger.Analyzer; + +/// +/// A sample source generator that creates a custom report based on class properties. The target class should be annotated with the 'Generators.ReportAttribute' attribute. +/// When using the source code as a baseline, an incremental source generator is preferable because it reduces the performance overhead. +/// +[Generator] +public class SampleIncrementalSourceGenerator : IIncrementalGenerator +{ + private const string Namespace = "Generators"; + private const string AttributeName = "ReportAttribute"; + + private const string AttributeSourceCode = $@"// + +namespace {Namespace} +{{ + [System.AttributeUsage(System.AttributeTargets.Class)] + public class {AttributeName} : System.Attribute + {{ + }} +}}"; + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Add the marker attribute to the compilation. + context.RegisterPostInitializationOutput(ctx => ctx.AddSource( + "ReportAttribute.g.cs", + SourceText.From(AttributeSourceCode, Encoding.UTF8))); + + // Filter classes annotated with the [Report] attribute. Only filtered Syntax Nodes can trigger code generation. + var provider = context.SyntaxProvider + .CreateSyntaxProvider( + (s, _) => s is ClassDeclarationSyntax, + (ctx, _) => GetClassDeclarationForSourceGen(ctx)) + .Where(t => t.reportAttributeFound) + .Select((t, _) => t.Item1); + + // Generate the source code. + context.RegisterSourceOutput(context.CompilationProvider.Combine(provider.Collect()), + ((ctx, t) => GenerateCode(ctx, t.Left, t.Right))); + } + + /// + /// Checks whether the Node is annotated with the [Report] attribute and maps syntax context to the specific node type (ClassDeclarationSyntax). + /// + /// Syntax context, based on CreateSyntaxProvider predicate + /// The specific cast and whether the attribute was found. + private static (ClassDeclarationSyntax, bool reportAttributeFound) GetClassDeclarationForSourceGen( + GeneratorSyntaxContext context) + { + var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node; + + // Go through all attributes of the class. + foreach (AttributeListSyntax attributeListSyntax in classDeclarationSyntax.AttributeLists) + foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes) + { + if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) + continue; // if we can't get the symbol, ignore it + + string attributeName = attributeSymbol.ContainingType.ToDisplayString(); + + // Check the full name of the [Report] attribute. + if (attributeName == $"{Namespace}.{AttributeName}") + return (classDeclarationSyntax, true); + } + + return (classDeclarationSyntax, false); + } + + /// + /// Generate code action. + /// It will be executed on specific nodes (ClassDeclarationSyntax annotated with the [Report] attribute) changed by the user. + /// + /// Source generation context used to add source files. + /// Compilation used to provide access to the Semantic Model. + /// Nodes annotated with the [Report] attribute that trigger the generate action. + private void GenerateCode(SourceProductionContext context, Compilation compilation, + ImmutableArray classDeclarations) + { + // Go through all filtered class declarations. + foreach (var classDeclarationSyntax in classDeclarations) + { + // We need to get semantic model of the class to retrieve metadata. + var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree); + + // Symbols allow us to get the compile-time information. + if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol) + continue; + + var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); + + // 'Identifier' means the token of the node. Get class name from the syntax node. + var className = classDeclarationSyntax.Identifier.Text; + + // Go through all class members with a particular type (property) to generate method lines. + var methodBody = classSymbol.GetMembers() + .OfType() + .Select(p => + $@" yield return $""{p.Name}:{{this.{p.Name}}}"";"); // e.g. yield return $"Id:{this.Id}"; + + // Build up the source code + var code = $@"// + +using System; +using System.Collections.Generic; + +namespace {namespaceName}; + +partial class {className} +{{ + public IEnumerable Report() + {{ +{string.Join("\n", methodBody)} + }} +}} +"; + + // Add the source code to the compilation. + context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8)); + } + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/SampleSourceGenerator.cs b/src/dotnetCampus.Logger.Analyzer/SampleSourceGenerator.cs new file mode 100644 index 0000000..d7b20de --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/SampleSourceGenerator.cs @@ -0,0 +1,56 @@ +using System.IO; +using Microsoft.CodeAnalysis; + +namespace dotnetCampus.Logger.Analyzer; + +/// +/// A sample source generator that creates C# classes based on the text file (in this case, Domain Driven Design ubiquitous language registry). +/// When using a simple text file as a baseline, we can create a non-incremental source generator. +/// +[Generator] +public class SampleSourceGenerator : ISourceGenerator +{ + public void Initialize(GeneratorInitializationContext context) + { + // No initialization required for this generator. + } + + public void Execute(GeneratorExecutionContext context) + { + // If you would like to put some data to non-compilable file (e.g. a .txt file), mark it as an Additional File. + + // Go through all files marked as an Additional File in file properties. + foreach (var additionalFile in context.AdditionalFiles) + { + if (additionalFile == null) + continue; + + // Check if the file name is the specific file that we expect. + if (Path.GetFileName(additionalFile.Path) != "DDD.UbiquitousLanguageRegistry.txt") + continue; + + var text = additionalFile.GetText(); + if (text == null) + continue; + + foreach (var line in text.Lines) + { + var className = line.ToString().Trim(); + + // Build up the source code. + string source = $@"// + +namespace Entities +{{ + public partial class {className} + {{ + }} +}} +"; + + // Add the source code to the compilation. + context.AddSource($"{className}.g.cs", source); + } + } + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj new file mode 100644 index 0000000..45abd5d --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -0,0 +1,26 @@ + + + + netstandard2.0 + false + enable + latest + + true + true + + dotnetCampus.Logger.Analyzer + dotnetCampus.Logger.Analyzer + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + From be58f06046d603f786265bf9a521f57a2c0dacae Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 10:15:03 +0800 Subject: [PATCH 12/49] =?UTF-8?q?=E5=B0=86=E6=BA=90=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=99=A8=E5=8A=A0=E5=85=A5=E5=88=B0=E5=8C=85=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotnetCampus.Logger.sln | 3 ++ .../Utils/EmbeddedSourceFile.cs | 35 +++++++++++++++++++ .../Utils/EmbeddedSourceFiles.cs | 33 +++++++++++++++++ .../dotnetCampus.Logger.Analyzer.csproj | 12 ++++--- .../Properties/Package/build/Package.props | 7 ++++ .../Properties/Package/build/Package.targets | 7 ++++ .../dotnetCampus.Logger.csproj | 29 ++++++++------- 7 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs create mode 100644 src/dotnetCampus.Logger/Properties/Package/build/Package.props create mode 100644 src/dotnetCampus.Logger/Properties/Package/build/Package.targets diff --git a/dotnetCampus.Logger.sln b/dotnetCampus.Logger.sln index 70b8762..324c1ed 100644 --- a/dotnetCampus.Logger.sln +++ b/dotnetCampus.Logger.sln @@ -4,6 +4,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 16.0.30804.86 MinimumVisualStudioVersion = 15.0.26124.0 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.Logger", "src\dotnetCampus.Logger\dotnetCampus.Logger.csproj", "{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}" + ProjectSection(ProjectDependencies) = postProject + {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36} = {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36} + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{AFB0DF31-474C-4ACB-88C6-DD00552D5B5A}" ProjectSection(SolutionItems) = preProject diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs new file mode 100644 index 0000000..7f7acd9 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs @@ -0,0 +1,35 @@ +using System; + +namespace dotnetCampus.Logger.Analyzer.Utils; + +/// +/// 嵌入的文本资源的数据。 +/// +/// 文件在嵌入的资源中的名称。 +/// 文件的文本内容。 +internal readonly record struct EmbeddedSourceFile(string EmbeddedName, string Content) +{ + /// + /// 根据资源名称猜测文件的无扩展名的名称。 + /// + /// 无扩展名的文件名。 + public string GuessFileNameWithoutExtension() + { + var span = EmbeddedName.AsSpan(); + var secondLastDotIndex = 0; + var lastDotIndex = 0; + for (var i = 0; i < span.Length; i++) + { + var c = span[i]; + if (c is '.') + { + secondLastDotIndex = lastDotIndex; + lastDotIndex = i; + } + } + var guessedName = lastDotIndex is 0 + ? span + : span.Slice(secondLastDotIndex + 1, lastDotIndex - secondLastDotIndex - 1); + return guessedName.ToString(); + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs new file mode 100644 index 0000000..1ab297c --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace dotnetCampus.Logger.Analyzer.Utils; + +/// +/// 从嵌入的资源中寻找源代码。 +/// +internal static class EmbeddedSourceFiles +{ + /// + /// 寻找 文件夹下的源代码名称和内容。 + /// + /// 资源文件夹名称。 + /// + internal static IEnumerable Enumerate(string folderName) + { + // 资源字符串格式为:"{Namespace}.{Folder}.{filename}.{Extension}" + var desiredFolder = $"{typeof(EmbeddedSourceFiles).Namespace}.{folderName}"; + var assembly = Assembly.GetExecutingAssembly(); + foreach (var resourceName in assembly.GetManifestResourceNames()) + { + if (resourceName.StartsWith(desiredFolder, StringComparison.OrdinalIgnoreCase)) + { + using var stream = assembly.GetManifestResourceStream(resourceName)!; + using var reader = new StreamReader(stream); + yield return new(resourceName, reader.ReadToEnd()); + } + } + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 45abd5d..98f2e54 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -3,17 +3,13 @@ netstandard2.0 false - enable - latest - true true - dotnetCampus.Logger.Analyzer - dotnetCampus.Logger.Analyzer + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -21,6 +17,12 @@ + + + + + + diff --git a/src/dotnetCampus.Logger/Properties/Package/build/Package.props b/src/dotnetCampus.Logger/Properties/Package/build/Package.props new file mode 100644 index 0000000..44d225a --- /dev/null +++ b/src/dotnetCampus.Logger/Properties/Package/build/Package.props @@ -0,0 +1,7 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + diff --git a/src/dotnetCampus.Logger/Properties/Package/build/Package.targets b/src/dotnetCampus.Logger/Properties/Package/build/Package.targets new file mode 100644 index 0000000..44d225a --- /dev/null +++ b/src/dotnetCampus.Logger/Properties/Package/build/Package.targets @@ -0,0 +1,7 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + diff --git a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj index fc1f1cf..ba8cb85 100644 --- a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj +++ b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj @@ -9,7 +9,6 @@ - net47/net461/net45: Support legacy .NET framework --> net8.0;net6.0;netstandard2.0;net45 - true @@ -21,20 +20,11 @@ + + 0 CS1591 - - - - 0 - - - - - - - @@ -45,4 +35,19 @@ snupkg + + + + + + + + + + + + + + + From 8ef5c981ceec1ed722d71dd878f10982065f8010 Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 11:24:28 +0800 Subject: [PATCH 13/49] =?UTF-8?q?=E5=8A=A0=E5=85=A5=20Program=20=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97=E5=88=86=E9=83=A8=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoggerSample.MainApp.Avalonia/Program.cs | 2 +- .../AssemblyInfo.cs | 6 + .../ProgramMainLogGenerator.cs | 127 ++++++++++++++++++ .../Templates/Program.logger.g.cs | 80 +++++++++++ .../Utils/EmbeddedSourceFile.cs | 39 ++---- .../Utils/EmbeddedSourceFiles.cs | 15 ++- .../dotnetCampus.Logger.Analyzer.csproj | 3 + 7 files changed, 238 insertions(+), 34 deletions(-) create mode 100644 src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/Templates/Program.logger.g.cs diff --git a/samples/LoggerSample.MainApp.Avalonia/Program.cs b/samples/LoggerSample.MainApp.Avalonia/Program.cs index 7a77fdc..c185048 100644 --- a/samples/LoggerSample.MainApp.Avalonia/Program.cs +++ b/samples/LoggerSample.MainApp.Avalonia/Program.cs @@ -4,7 +4,7 @@ namespace dotnetCampus.LoggerSample.Avalonia; -class Program +partial class Program { // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized diff --git a/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs b/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs new file mode 100644 index 0000000..c105a9d --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs @@ -0,0 +1,6 @@ +namespace dotnetCampus.Logger.Analyzer; + +internal class AssemblyInfo +{ + public static readonly string RootNamespace = typeof(AssemblyInfo).Namespace!; +} diff --git a/src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs new file mode 100644 index 0000000..bfa3b06 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs @@ -0,0 +1,127 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Text; +using dotnetCampus.Logger.Analyzer.Templates; +using dotnetCampus.Logger.Analyzer.Utils; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace dotnetCampus.Logger.Analyzer; + +/// +/// 生成 Program.g.cs,为 Main 方法第一行日志生成支持代码。 +/// +[Generator] +public class ProgramMainLogGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var provider = context.SyntaxProvider.CreateSyntaxProvider((node, ct) => + { + if (node is not MethodDeclarationSyntax mds) + { + // 必须是方法声明。 + return false; + } + + if (!CheckCanBeProgramMain(mds)) + { + // 必须符合 Main 方法的要求。 + return false; + } + + if (mds.Parent is not ClassDeclarationSyntax cds) + { + return false; + } + + return true; + }, (c, ct) => + { + var mainMethodNode = (MethodDeclarationSyntax)c.Node; + var programClassNode = (ClassDeclarationSyntax)mainMethodNode.Parent!; + var programTypeSymbol = c.SemanticModel.GetDeclaredSymbol(programClassNode)!; + return programTypeSymbol; + }); + + context.RegisterSourceOutput(provider, Execute); + } + + private void Execute(SourceProductionContext context, INamedTypeSymbol programTypeSymbol) + { + var templateProgramNamespace = typeof(Program).Namespace!; + var generatedProgramNamespace = programTypeSymbol.ContainingNamespace.ToDisplayString(); + + var templatesFolder = templateProgramNamespace.AsSpan().Slice(AssemblyInfo.RootNamespace.Length + 1).ToString(); + foreach (var template in EmbeddedSourceFiles + .Enumerate(templatesFolder) + .Where(x => x.FileName.StartsWith("Program."))) + { + var generatedText = template.Content + .Replace(templateProgramNamespace, generatedProgramNamespace) + .Replace("Program", programTypeSymbol.Name); + + context.AddSource(template.FileName, SourceText.From(generatedText, Encoding.UTF8)); + } + } + + /// + /// 从语法上判断一个方法声明是否符合成为 Main 方法的要求。 + /// + /// 语法树中的方法声明。 + /// 是否符合成为 Main 方法的要求。 + private static bool CheckCanBeProgramMain(MethodDeclarationSyntax methodNode) + { + var methodName = methodNode.Identifier.Text; + if (methodName != "Main") + { + // 名称必须是 Main。 + return false; + } + + if (methodNode.Modifiers.Any(SyntaxKind.StaticKeyword) == false) + { + // 必须是静态方法。 + return false; + } + + if (methodNode.ParameterList.Parameters.Count > 1) + { + // 最多只能有一个参数。 + return false; + } + + if (methodNode.ParameterList.Parameters.Count == 1) + { + var parameter = methodNode.ParameterList.Parameters[0]; + if (parameter.Type is not ArrayTypeSyntax { ElementType: PredefinedTypeSyntax spts }) + { + // 参数必须是预定义类型。 + return false; + } + + if (!spts.Keyword.IsKind(SyntaxKind.StringKeyword)) + { + // 参数类型必须是 string[] 或 System.String[]。 + return false; + } + } + + if (methodNode.ReturnType is not PredefinedTypeSyntax ipts) + { + // 返回值必须是预定义类型。 + return false; + } + + if (!ipts.Keyword.IsKind(SyntaxKind.VoidKeyword) && !ipts.Keyword.IsKind(SyntaxKind.IntKeyword)) + { + // 返回值必须是 void 或 int。 + return false; + } + + return true; + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/Templates/Program.logger.g.cs b/src/dotnetCampus.Logger.Analyzer/Templates/Program.logger.g.cs new file mode 100644 index 0000000..cac3707 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Templates/Program.logger.g.cs @@ -0,0 +1,80 @@ +#nullable enable + +using EditorBrowsable = global::System.ComponentModel.EditorBrowsableAttribute; +using EditorBrowsableState = global::System.ComponentModel.EditorBrowsableState; +using EventId = global::dotnetCampus.Logging.EventId; +using Exception = global::System.Exception; +using ILogger = global::dotnetCampus.Logging.ILogger; +using LazyThreadSafetyMode = global::System.Threading.LazyThreadSafetyMode; +using LogLevel = global::dotnetCampus.Logging.LogLevel; + +namespace dotnetCampus.Logger.Analyzer.Templates; + +partial class Program +{ + /// + /// 用于在 类的内部记录日志。 + /// + /// + /// 由于此代码是源生成器生成的代码,所以可以在日志模块初始化之前记录日志且提前生效。
+ /// 🤩 你甚至能在 Main 方法的第一行就使用它记录日志! + ///
+ private static GeneratedMemoryCacheLogger Log => GeneratedMemoryCacheLogger.Instance.Value; + + [EditorBrowsable(EditorBrowsableState.Never)] + private sealed class GeneratedMemoryCacheLogger : ILogger + { + [EditorBrowsable(EditorBrowsableState.Never)] + internal static readonly global::System.Lazy Instance = new( + () => new GeneratedMemoryCacheLogger(), + LazyThreadSafetyMode.None); + + private ILogger? _realLogger; + + private readonly global::System.Collections.Concurrent.ConcurrentQueue _queue = []; + + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, global::System.Func formatter) + { + if (_realLogger is { } logger) + { + logger.Log(logLevel, eventId, state, exception, (s, e) => formatter((TState)s, e)); + } + _queue.Enqueue(new(logLevel, eventId, state!, exception, (s, e) => formatter((TState)s, e))); + } + + /// + /// 将 隐式转换为可传递到 LogBuilder.UseMemoryCache 方法的委托。 + /// + /// + /// + public static implicit operator global::System.Action(GeneratedMemoryCacheLogger logger) + { + return logger.Flush; + } + + private void Flush(ILogger logger) + { + _realLogger = logger; + while (_queue.TryDequeue(out var context)) + { + logger.Log(context.logLevel, context.eventId, context.state, context.exception, context.formatter); + } + } + + /// + /// 辅助缓存日志条目。 + /// + /// 日志级别。 + /// 事件 Id。 + /// 日志内容。 + /// 异常信息。 + /// 格式化器。 + private readonly record struct CachedLogItem( + LogLevel logLevel, + EventId eventId, + object state, + Exception? exception, + global::System.Func formatter + ); + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs index 7f7acd9..b7c0e6e 100644 --- a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs +++ b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs @@ -1,35 +1,16 @@ -using System; - -namespace dotnetCampus.Logger.Analyzer.Utils; +namespace dotnetCampus.Logger.Analyzer.Utils; /// /// 嵌入的文本资源的数据。 /// +/// 文件的命名空间。 +/// 文件相对于项目根目录所在的文件夹的路径。 +/// 文件的名称(含扩展名)。 /// 文件在嵌入的资源中的名称。 /// 文件的文本内容。 -internal readonly record struct EmbeddedSourceFile(string EmbeddedName, string Content) -{ - /// - /// 根据资源名称猜测文件的无扩展名的名称。 - /// - /// 无扩展名的文件名。 - public string GuessFileNameWithoutExtension() - { - var span = EmbeddedName.AsSpan(); - var secondLastDotIndex = 0; - var lastDotIndex = 0; - for (var i = 0; i < span.Length; i++) - { - var c = span[i]; - if (c is '.') - { - secondLastDotIndex = lastDotIndex; - lastDotIndex = i; - } - } - var guessedName = lastDotIndex is 0 - ? span - : span.Slice(secondLastDotIndex + 1, lastDotIndex - secondLastDotIndex - 1); - return guessedName.ToString(); - } -} +internal readonly record struct EmbeddedSourceFile( + string Namespace, + string RelativeDirectoryPath, + string FileName, + string EmbeddedName, + string Content); diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs index 1ab297c..6bfe787 100644 --- a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs +++ b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs @@ -13,20 +13,27 @@ internal static class EmbeddedSourceFiles /// /// 寻找 文件夹下的源代码名称和内容。 /// - /// 资源文件夹名称。 + /// 资源文件夹名称。请以“/”或“\”分隔文件夹。 /// internal static IEnumerable Enumerate(string folderName) { // 资源字符串格式为:"{Namespace}.{Folder}.{filename}.{Extension}" - var desiredFolder = $"{typeof(EmbeddedSourceFiles).Namespace}.{folderName}"; + var desiredFolder = $"{AssemblyInfo.RootNamespace}.{folderName}"; var assembly = Assembly.GetExecutingAssembly(); foreach (var resourceName in assembly.GetManifestResourceNames()) { - if (resourceName.StartsWith(desiredFolder, StringComparison.OrdinalIgnoreCase)) + var prefix = desiredFolder.Replace('/', '.').Replace('\\', '.') + "."; + if (resourceName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { + var fileName = resourceName.AsSpan().Slice(prefix.Length).ToString(); using var stream = assembly.GetManifestResourceStream(resourceName)!; using var reader = new StreamReader(stream); - yield return new(resourceName, reader.ReadToEnd()); + yield return new EmbeddedSourceFile( + desiredFolder, + folderName, + fileName, + resourceName, + reader.ReadToEnd()); } } } diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 98f2e54..7f84089 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -24,5 +24,8 @@
+ + + From 86a1f3d881ebc60acd9fedfadee94e4664691871 Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 11:24:38 +0800 Subject: [PATCH 14/49] =?UTF-8?q?=E5=88=A0=E9=99=A4=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E6=BA=90=E7=94=9F=E6=88=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProgramMainLogGenerator.cs | 1 - src/dotnetCampus.Logger.Analyzer/Readme.md | 29 ---- .../SampleIncrementalSourceGenerator.cs | 130 ------------------ .../SampleSourceGenerator.cs | 56 -------- 4 files changed, 216 deletions(-) delete mode 100644 src/dotnetCampus.Logger.Analyzer/Readme.md delete mode 100644 src/dotnetCampus.Logger.Analyzer/SampleIncrementalSourceGenerator.cs delete mode 100644 src/dotnetCampus.Logger.Analyzer/SampleSourceGenerator.cs diff --git a/src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs index bfa3b06..abb57c5 100644 --- a/src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Linq; using System.Text; using dotnetCampus.Logger.Analyzer.Templates; diff --git a/src/dotnetCampus.Logger.Analyzer/Readme.md b/src/dotnetCampus.Logger.Analyzer/Readme.md deleted file mode 100644 index 9f3424f..0000000 --- a/src/dotnetCampus.Logger.Analyzer/Readme.md +++ /dev/null @@ -1,29 +0,0 @@ -# Roslyn Source Generators Sample - -A set of three projects that illustrates Roslyn source generators. Enjoy this template to learn from and modify source generators for your own needs. - -## Content -### dotnetCampus.Logger.Analyzer -A .NET Standard project with implementations of sample source generators. -**You must build this project to see the result (generated code) in the IDE.** - -- [SampleSourceGenerator.cs](SampleSourceGenerator.cs): A source generator that creates C# classes based on a text file (in this case, Domain Driven Design ubiquitous language registry). -- [SampleIncrementalSourceGenerator.cs](SampleIncrementalSourceGenerator.cs): A source generator that creates a custom report based on class properties. The target class should be annotated with the `Generators.ReportAttribute` attribute. - -### dotnetCampus.Logger.Analyzer.Sample -A project that references source generators. Note the parameters of `ProjectReference` in [dotnetCampus.Logger.Analyzer.Sample.csproj](../dotnetCampus.Logger.Analyzer.Sample/dotnetCampus.Logger.Analyzer.Sample.csproj), they make sure that the project is referenced as a set of source generators. - -### dotnetCampus.Logger.Analyzer.Tests -Unit tests for source generators. The easiest way to develop language-related features is to start with unit tests. - -## How To? -### How to debug? -- Use the [launchSettings.json](Properties/launchSettings.json) profile. -- Debug tests. - -### How can I determine which syntax nodes I should expect? -Consider installing the Roslyn syntax tree viewer plugin [Rossynt](https://plugins.jetbrains.com/plugin/16902-rossynt/). - -### How to learn more about wiring source generators? -Watch the walkthrough video: [Let’s Build an Incremental Source Generator With Roslyn, by Stefan Pölz](https://youtu.be/azJm_Y2nbAI) -The complete set of information is available in [Source Generators Cookbook](https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md). \ No newline at end of file diff --git a/src/dotnetCampus.Logger.Analyzer/SampleIncrementalSourceGenerator.cs b/src/dotnetCampus.Logger.Analyzer/SampleIncrementalSourceGenerator.cs deleted file mode 100644 index ced0607..0000000 --- a/src/dotnetCampus.Logger.Analyzer/SampleIncrementalSourceGenerator.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; - - -namespace dotnetCampus.Logger.Analyzer; - -/// -/// A sample source generator that creates a custom report based on class properties. The target class should be annotated with the 'Generators.ReportAttribute' attribute. -/// When using the source code as a baseline, an incremental source generator is preferable because it reduces the performance overhead. -/// -[Generator] -public class SampleIncrementalSourceGenerator : IIncrementalGenerator -{ - private const string Namespace = "Generators"; - private const string AttributeName = "ReportAttribute"; - - private const string AttributeSourceCode = $@"// - -namespace {Namespace} -{{ - [System.AttributeUsage(System.AttributeTargets.Class)] - public class {AttributeName} : System.Attribute - {{ - }} -}}"; - - public void Initialize(IncrementalGeneratorInitializationContext context) - { - // Add the marker attribute to the compilation. - context.RegisterPostInitializationOutput(ctx => ctx.AddSource( - "ReportAttribute.g.cs", - SourceText.From(AttributeSourceCode, Encoding.UTF8))); - - // Filter classes annotated with the [Report] attribute. Only filtered Syntax Nodes can trigger code generation. - var provider = context.SyntaxProvider - .CreateSyntaxProvider( - (s, _) => s is ClassDeclarationSyntax, - (ctx, _) => GetClassDeclarationForSourceGen(ctx)) - .Where(t => t.reportAttributeFound) - .Select((t, _) => t.Item1); - - // Generate the source code. - context.RegisterSourceOutput(context.CompilationProvider.Combine(provider.Collect()), - ((ctx, t) => GenerateCode(ctx, t.Left, t.Right))); - } - - /// - /// Checks whether the Node is annotated with the [Report] attribute and maps syntax context to the specific node type (ClassDeclarationSyntax). - /// - /// Syntax context, based on CreateSyntaxProvider predicate - /// The specific cast and whether the attribute was found. - private static (ClassDeclarationSyntax, bool reportAttributeFound) GetClassDeclarationForSourceGen( - GeneratorSyntaxContext context) - { - var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node; - - // Go through all attributes of the class. - foreach (AttributeListSyntax attributeListSyntax in classDeclarationSyntax.AttributeLists) - foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes) - { - if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) - continue; // if we can't get the symbol, ignore it - - string attributeName = attributeSymbol.ContainingType.ToDisplayString(); - - // Check the full name of the [Report] attribute. - if (attributeName == $"{Namespace}.{AttributeName}") - return (classDeclarationSyntax, true); - } - - return (classDeclarationSyntax, false); - } - - /// - /// Generate code action. - /// It will be executed on specific nodes (ClassDeclarationSyntax annotated with the [Report] attribute) changed by the user. - /// - /// Source generation context used to add source files. - /// Compilation used to provide access to the Semantic Model. - /// Nodes annotated with the [Report] attribute that trigger the generate action. - private void GenerateCode(SourceProductionContext context, Compilation compilation, - ImmutableArray classDeclarations) - { - // Go through all filtered class declarations. - foreach (var classDeclarationSyntax in classDeclarations) - { - // We need to get semantic model of the class to retrieve metadata. - var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree); - - // Symbols allow us to get the compile-time information. - if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol) - continue; - - var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); - - // 'Identifier' means the token of the node. Get class name from the syntax node. - var className = classDeclarationSyntax.Identifier.Text; - - // Go through all class members with a particular type (property) to generate method lines. - var methodBody = classSymbol.GetMembers() - .OfType() - .Select(p => - $@" yield return $""{p.Name}:{{this.{p.Name}}}"";"); // e.g. yield return $"Id:{this.Id}"; - - // Build up the source code - var code = $@"// - -using System; -using System.Collections.Generic; - -namespace {namespaceName}; - -partial class {className} -{{ - public IEnumerable Report() - {{ -{string.Join("\n", methodBody)} - }} -}} -"; - - // Add the source code to the compilation. - context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8)); - } - } -} diff --git a/src/dotnetCampus.Logger.Analyzer/SampleSourceGenerator.cs b/src/dotnetCampus.Logger.Analyzer/SampleSourceGenerator.cs deleted file mode 100644 index d7b20de..0000000 --- a/src/dotnetCampus.Logger.Analyzer/SampleSourceGenerator.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.IO; -using Microsoft.CodeAnalysis; - -namespace dotnetCampus.Logger.Analyzer; - -/// -/// A sample source generator that creates C# classes based on the text file (in this case, Domain Driven Design ubiquitous language registry). -/// When using a simple text file as a baseline, we can create a non-incremental source generator. -/// -[Generator] -public class SampleSourceGenerator : ISourceGenerator -{ - public void Initialize(GeneratorInitializationContext context) - { - // No initialization required for this generator. - } - - public void Execute(GeneratorExecutionContext context) - { - // If you would like to put some data to non-compilable file (e.g. a .txt file), mark it as an Additional File. - - // Go through all files marked as an Additional File in file properties. - foreach (var additionalFile in context.AdditionalFiles) - { - if (additionalFile == null) - continue; - - // Check if the file name is the specific file that we expect. - if (Path.GetFileName(additionalFile.Path) != "DDD.UbiquitousLanguageRegistry.txt") - continue; - - var text = additionalFile.GetText(); - if (text == null) - continue; - - foreach (var line in text.Lines) - { - var className = line.ToString().Trim(); - - // Build up the source code. - string source = $@"// - -namespace Entities -{{ - public partial class {className} - {{ - }} -}} -"; - - // Add the source code to the compilation. - context.AddSource($"{className}.g.cs", source); - } - } - } -} From 21a5bc40d062567455a59926edba86621090a7f7 Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 11:42:03 +0800 Subject: [PATCH 15/49] =?UTF-8?q?=E6=95=B4=E7=90=86=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AssemblyInfo.cs | 2 +- .../Templates/Program.logger.g.cs | 7 ++++--- .../PartialProgramCodeFixProvider.cs | 20 +++++++++++++++++++ .../PartialProgramAnalyzer.cs | 17 ++++++++++++++++ .../ProgramMainLogGenerator.cs | 6 +++--- .../Utils/EmbeddedSourceFile.cs | 2 +- .../Utils/EmbeddedSourceFiles.cs | 2 +- .../dotnetCampus.Logger.Analyzer.csproj | 10 +++++----- 8 files changed, 52 insertions(+), 14 deletions(-) rename src/dotnetCampus.Logger.Analyzer/{ => Assets}/Templates/Program.logger.g.cs (95%) create mode 100644 src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs rename src/dotnetCampus.Logger.Analyzer/{ => Generators}/ProgramMainLogGenerator.cs (96%) diff --git a/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs b/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs index c105a9d..ada8129 100644 --- a/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs +++ b/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs @@ -1,4 +1,4 @@ -namespace dotnetCampus.Logger.Analyzer; +namespace dotnetCampus.Logger; internal class AssemblyInfo { diff --git a/src/dotnetCampus.Logger.Analyzer/Templates/Program.logger.g.cs b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs similarity index 95% rename from src/dotnetCampus.Logger.Analyzer/Templates/Program.logger.g.cs rename to src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs index cac3707..7900e08 100644 --- a/src/dotnetCampus.Logger.Analyzer/Templates/Program.logger.g.cs +++ b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs @@ -8,7 +8,7 @@ using LazyThreadSafetyMode = global::System.Threading.LazyThreadSafetyMode; using LogLevel = global::dotnetCampus.Logging.LogLevel; -namespace dotnetCampus.Logger.Analyzer.Templates; +namespace dotnetCampus.Logger.Templates; partial class Program { @@ -33,7 +33,8 @@ private sealed class GeneratedMemoryCacheLogger : ILogger private readonly global::System.Collections.Concurrent.ConcurrentQueue _queue = []; - void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, global::System.Func formatter) + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, + global::System.Func formatter) { if (_realLogger is { } logger) { @@ -75,6 +76,6 @@ private readonly record struct CachedLogItem( object state, Exception? exception, global::System.Func formatter - ); + ); } } diff --git a/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs new file mode 100644 index 0000000..eb8a223 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs @@ -0,0 +1,20 @@ +using System.Collections.Immutable; +using System.Composition; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; + +namespace dotnetCampus.Logger.CodeFixeProviders; + +[ExportCodeFixProvider(LanguageNames.CSharp), Shared] +public class PartialProgramCodeFixProvider : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds { get; } = new() { }; + + public override FixAllProvider? GetFixAllProvider() => null; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + return Task.CompletedTask; + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs new file mode 100644 index 0000000..1f26042 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs @@ -0,0 +1,17 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace dotnetCampus.Logger.DiagnosticAnalyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class PartialProgramAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics { get; } = new() { }; + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs similarity index 96% rename from src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs rename to src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs index abb57c5..aebdfc1 100644 --- a/src/dotnetCampus.Logger.Analyzer/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs @@ -1,14 +1,14 @@ using System; using System.Linq; using System.Text; -using dotnetCampus.Logger.Analyzer.Templates; -using dotnetCampus.Logger.Analyzer.Utils; +using dotnetCampus.Logger.Templates; +using dotnetCampus.Logger.Utils; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; -namespace dotnetCampus.Logger.Analyzer; +namespace dotnetCampus.Logger.Generators; /// /// 生成 Program.g.cs,为 Main 方法第一行日志生成支持代码。 diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs index b7c0e6e..d23bd7b 100644 --- a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs +++ b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs @@ -1,4 +1,4 @@ -namespace dotnetCampus.Logger.Analyzer.Utils; +namespace dotnetCampus.Logger.Utils; /// /// 嵌入的文本资源的数据。 diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs index 6bfe787..6568fe0 100644 --- a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs +++ b/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs @@ -3,7 +3,7 @@ using System.IO; using System.Reflection; -namespace dotnetCampus.Logger.Analyzer.Utils; +namespace dotnetCampus.Logger.Utils; /// /// 从嵌入的资源中寻找源代码。 diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 7f84089..3bedc93 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -5,7 +5,7 @@ false true true - dotnetCampus.Logger.Analyzer + dotnetCampus.Logger @@ -17,11 +17,11 @@ - + - - - + + + From 159f2a65551696cdae61df8ed1b4b52c117fdc75 Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 11:44:03 +0800 Subject: [PATCH 16/49] =?UTF-8?q?=E8=A6=81=E6=B1=82=E5=BF=85=E9=A1=BB?= =?UTF-8?q?=E6=98=AF=20partial=20=E7=B1=BB=E6=89=8D=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=BE=85=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Generators/ProgramMainLogGenerator.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs index aebdfc1..2d52ab9 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs @@ -34,6 +34,13 @@ public void Initialize(IncrementalGeneratorInitializationContext context) if (mds.Parent is not ClassDeclarationSyntax cds) { + // 必须在类中。 + return false; + } + + if (!cds.Modifiers.Any(SyntaxKind.PartialKeyword)) + { + // 必须是 partial 类。 return false; } From be159feca00442e0ea367ec57411ce4a2d8079ea Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 13:09:57 +0800 Subject: [PATCH 17/49] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=88=86=E6=9E=90?= =?UTF-8?q?=E5=99=A8=E5=A4=9A=E8=AF=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Diagnostics.cs | 61 ++++++++++++++ .../Properties/Localizations.Designer.cs | 80 +++++++++++++++++++ .../Properties/Localizations.resx | 32 ++++++++ .../Properties/Localizations.zh-hans.resx | 24 ++++++ .../Properties/Localizations.zh-hant.resx | 24 ++++++ .../Properties/copilot.md | 14 ++++ .../dotnetCampus.Logger.Analyzer.csproj | 14 ++++ 7 files changed, 249 insertions(+) create mode 100644 src/dotnetCampus.Logger.Analyzer/Diagnostics.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx create mode 100644 src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx create mode 100644 src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx create mode 100644 src/dotnetCampus.Logger.Analyzer/Properties/copilot.md diff --git a/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs b/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs new file mode 100644 index 0000000..ce3cc06 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs @@ -0,0 +1,61 @@ +using dotnetCampus.Logger.Properties; +using Microsoft.CodeAnalysis; +using static dotnetCampus.Logger.Properties.Localizations; + +// ReSharper disable InconsistentNaming + +namespace dotnetCampus.Logger; + +/// +/// 包含日志库中的所有诊断。 +/// +public class Diagnostics +{ + public static DiagnosticDescriptor DL0000_UnknownError { get; } = new( + nameof(DL0000), + Localize(nameof(DL0000)), + Localize(nameof(DL0000_Message)), + Categories.Useless, + DiagnosticSeverity.Error, + true); + + private static class Categories + { + /// + /// 可能产生 bug,则报告此诊断。 + /// + public const string AvoidBugs = "dotnetCampus.AvoidBugs"; + + /// + /// 为了提供代码生成能力,则报告此诊断。 + /// + public const string CodeFixOnly = "dotnetCampus.CodeFixOnly"; + + /// + /// 因编译要求而必须满足的条件没有满足,则报告此诊断。 + /// + public const string Compiler = "dotnetCampus.Compiler"; + + /// + /// 因库内的机制限制,必须满足此要求后库才可正常工作,则报告此诊断。 + /// + public const string Mechanism = "dotnetCampus.Mechanism"; + + /// + /// 为了代码可读性,使之更易于理解、方便调试,则报告此诊断。 + /// + public const string Readable = "dotnetCampus.Readable"; + + /// + /// 能写得出来正常编译,但会引发运行时异常,则报告此诊断。 + /// + public const string RuntimeException = "dotnetCampus.RuntimeException"; + + /// + /// 编写了无法生效的代码,则报告此诊断。 + /// + public const string Useless = "dotnetCampus.Useless"; + } + + private static LocalizableString Localize(string key) => new LocalizableResourceString(key, ResourceManager, typeof(Localizations)); +} diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs new file mode 100644 index 0000000..6c032e0 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace dotnetCampus.Logger.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Localizations { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Localizations() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("dotnetCampus.Logger.Properties.Localizations", typeof(Localizations).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Unknown error.. + /// + internal static string DL0000 { + get { + return ResourceManager.GetString("DL0000", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An unknown error occurred.. + /// + internal static string DL0000_Message { + get { + return ResourceManager.GetString("DL0000_Message", resourceCulture); + } + } + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx new file mode 100644 index 0000000..d52e304 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx @@ -0,0 +1,32 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + Unknown error. + + + An unknown error occurred. + + diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx new file mode 100644 index 0000000..8b2bfe1 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx @@ -0,0 +1,24 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + 未知错误。 + + + 发生了未知错误。 + + diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx new file mode 100644 index 0000000..9812224 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx @@ -0,0 +1,24 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + 未知錯誤。 + + + 發生了未知錯誤。 + + diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md b/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md new file mode 100644 index 0000000..2c86ecd --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md @@ -0,0 +1,14 @@ +# 多语言翻译工具 + +在有自动化翻译工具之前,我们可以先使用 Copilot 进行手工翻译。借助 Copilot 的智能提示,可以快速生成翻译文本。 + +--- + +请将以下 C# 分析器的 Id 进行翻译。 + +| Id | zh-hans | zh-hant | en | +|----------------|----------|----------|----------------------------| +| DL0000 | 未知错误。 | 未知錯誤。 | Unknown error. | +| DL0000_Message | 发生了未知错误。 | 發生了未知錯誤。 | An unknown error occurred. | + + diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 3bedc93..2475354 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -22,10 +22,24 @@ + + True + True + Localizations.zh-CN.resx + + + True + True + Localications.zh-CN.resx + + + ResXFileCodeGenerator + Localizations.Designer.cs + From c5a691cce30ada48a076b42b02a8c2f726d8aecb Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 13:46:23 +0800 Subject: [PATCH 18/49] =?UTF-8?q?=E5=88=86=E6=9E=90=20Program=20=E5=88=86?= =?UTF-8?q?=E9=83=A8=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoggerSample.MainApp.Avalonia/Program.cs | 2 +- .../PartialProgramCodeFixProvider.cs | 4 +- .../PartialProgramAnalyzer.cs | 31 ++++++++- .../Diagnostics.cs | 9 +++ .../Generators/ProgramMainLogGenerator.cs | 60 +---------------- .../Properties/Localizations.Designer.cs | 29 ++++++++- .../Properties/Localizations.resx | 13 +++- .../Properties/Localizations.zh-hans.resx | 13 +++- .../Properties/Localizations.zh-hant.resx | 13 +++- .../Properties/copilot.md | 12 ++-- .../CodeAnalysis/ProgramMainExtensions.cs | 65 +++++++++++++++++++ .../Utils/{ => IO}/EmbeddedSourceFile.cs | 2 +- .../Utils/{ => IO}/EmbeddedSourceFiles.cs | 2 +- 13 files changed, 178 insertions(+), 77 deletions(-) create mode 100644 src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/ProgramMainExtensions.cs rename src/dotnetCampus.Logger.Analyzer/Utils/{ => IO}/EmbeddedSourceFile.cs (93%) rename src/dotnetCampus.Logger.Analyzer/Utils/{ => IO}/EmbeddedSourceFiles.cs (97%) diff --git a/samples/LoggerSample.MainApp.Avalonia/Program.cs b/samples/LoggerSample.MainApp.Avalonia/Program.cs index c185048..cbf1966 100644 --- a/samples/LoggerSample.MainApp.Avalonia/Program.cs +++ b/samples/LoggerSample.MainApp.Avalonia/Program.cs @@ -4,7 +4,7 @@ namespace dotnetCampus.LoggerSample.Avalonia; -partial class Program +internal class Program { // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized diff --git a/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs index eb8a223..d764124 100644 --- a/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs +++ b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs @@ -9,7 +9,9 @@ namespace dotnetCampus.Logger.CodeFixeProviders; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class PartialProgramCodeFixProvider : CodeFixProvider { - public override ImmutableArray FixableDiagnosticIds { get; } = new() { }; + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( + Diagnostics.DL0101_ProgramIsRecommendedToBePartial.Id + ); public override FixAllProvider? GetFixAllProvider() => null; diff --git a/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs index 1f26042..26ded7d 100644 --- a/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs +++ b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs @@ -1,17 +1,44 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; namespace dotnetCampus.Logger.DiagnosticAnalyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] public class PartialProgramAnalyzer : DiagnosticAnalyzer { - public override ImmutableArray SupportedDiagnostics { get; } = new() { }; + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + Diagnostics.DL0101_ProgramIsRecommendedToBePartial + ); public override void Initialize(AnalysisContext context) { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterSyntaxNodeAction(AnalyzeProgram, SyntaxKind.MethodDeclaration); + } + + private void AnalyzeProgram(SyntaxNodeAnalysisContext context) + { + if (context.Node is MemberDeclarationSyntax + { + Parent: ClassDeclarationSyntax cds, + } && !cds.Modifiers.Any(SyntaxKind.PartialKeyword)) + { + var spanStart = cds.Modifiers.Count is 0 + ? cds.Keyword.SpanStart + : cds.Modifiers.Span.Start; + context.ReportDiagnostic(Diagnostic.Create( + Diagnostics.DL0101_ProgramIsRecommendedToBePartial, + Location.Create(context.Node.SyntaxTree, new TextSpan( + spanStart, + cds.Identifier.Span.End - spanStart + )), + cds.Identifier.Text)); + } } } diff --git a/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs b/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs index ce3cc06..f138c6c 100644 --- a/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs +++ b/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs @@ -19,6 +19,15 @@ public class Diagnostics DiagnosticSeverity.Error, true); + public static DiagnosticDescriptor DL0101_ProgramIsRecommendedToBePartial { get; } = new( + nameof(DL1001), + Localize(nameof(DL1001)), + Localize(nameof(DL1001_Message)), + Categories.Mechanism, + DiagnosticSeverity.Warning, + true, + description: Localize(DL1001_Description)); + private static class Categories { /// diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs index 2d52ab9..ae105e9 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs @@ -2,11 +2,12 @@ using System.Linq; using System.Text; using dotnetCampus.Logger.Templates; -using dotnetCampus.Logger.Utils; +using dotnetCampus.Logger.Utils.IO; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; +using static dotnetCampus.Logger.Utils.CodeAnalysis.ProgramMainExtensions; namespace dotnetCampus.Logger.Generators; @@ -73,61 +74,4 @@ private void Execute(SourceProductionContext context, INamedTypeSymbol programTy context.AddSource(template.FileName, SourceText.From(generatedText, Encoding.UTF8)); } } - - /// - /// 从语法上判断一个方法声明是否符合成为 Main 方法的要求。 - /// - /// 语法树中的方法声明。 - /// 是否符合成为 Main 方法的要求。 - private static bool CheckCanBeProgramMain(MethodDeclarationSyntax methodNode) - { - var methodName = methodNode.Identifier.Text; - if (methodName != "Main") - { - // 名称必须是 Main。 - return false; - } - - if (methodNode.Modifiers.Any(SyntaxKind.StaticKeyword) == false) - { - // 必须是静态方法。 - return false; - } - - if (methodNode.ParameterList.Parameters.Count > 1) - { - // 最多只能有一个参数。 - return false; - } - - if (methodNode.ParameterList.Parameters.Count == 1) - { - var parameter = methodNode.ParameterList.Parameters[0]; - if (parameter.Type is not ArrayTypeSyntax { ElementType: PredefinedTypeSyntax spts }) - { - // 参数必须是预定义类型。 - return false; - } - - if (!spts.Keyword.IsKind(SyntaxKind.StringKeyword)) - { - // 参数类型必须是 string[] 或 System.String[]。 - return false; - } - } - - if (methodNode.ReturnType is not PredefinedTypeSyntax ipts) - { - // 返回值必须是预定义类型。 - return false; - } - - if (!ipts.Keyword.IsKind(SyntaxKind.VoidKeyword) && !ipts.Keyword.IsKind(SyntaxKind.IntKeyword)) - { - // 返回值必须是 void 或 int。 - return false; - } - - return true; - } } diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs index 6c032e0..ba7b1fd 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs @@ -60,7 +60,7 @@ internal Localizations() { } /// - /// Looks up a localized string similar to Unknown error.. + /// Looks up a localized string similar to Unknown Error. /// internal static string DL0000 { get { @@ -76,5 +76,32 @@ internal static string DL0000_Message { return ResourceManager.GetString("DL0000_Message", resourceCulture); } } + + /// + /// Looks up a localized string similar to Change the entry class to a partial class. + /// + internal static string DL1001 { + get { + return ResourceManager.GetString("DL1001", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to After changing the entry class to a partial class, the source generator can generate auxiliary log code according to the subsequent log initialization requirements; this way, you can even start using logs in the first sentence of the Main method without worrying about initialization issues.. + /// + internal static string DL1001_Description { + get { + return ResourceManager.GetString("DL1001_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change {0} to a partial class to allow it to use the logging system before the logging module is initialized.. + /// + internal static string DL1001_Message { + get { + return ResourceManager.GetString("DL1001_Message", resourceCulture); + } + } } } diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx index d52e304..62133fc 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx @@ -24,9 +24,18 @@ - Unknown error. - + Unknown Error + An unknown error occurred. + + Change the entry class to a partial class + + + Change {0} to a partial class to allow it to use the logging system before the logging module is initialized. + + + After changing the entry class to a partial class, the source generator can generate auxiliary log code according to the subsequent log initialization requirements; this way, you can even start using logs in the first sentence of the Main method without worrying about initialization issues. + diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx index 8b2bfe1..c3c9a7a 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx @@ -16,9 +16,18 @@ - 未知错误。 - + 未知错误 + 发生了未知错误。 + + 将 {0} 改为分部类,以允许其在日志模块初始化之前使用日志系统。 + + + 修改入口类为分部类 + + + 将入口类改为分部类后,源生成器可以根据后续日志初始化的要求生成辅助日志代码;这样,你甚至可以在 Main 方法第一句化就开始使用日志而无需担心初始化问题。 + diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx index 9812224..f80d441 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx @@ -16,9 +16,18 @@ - 未知錯誤。 - + 未知錯誤 + 發生了未知錯誤。 + + 將 {0} 改為分部類,以允許其在日誌模塊初始化之前使用日誌系統。 + + + 修改入口類為分部類 + + + 將入口類改為分部類後,源生成器可以根據後續日誌初始化的要求生成輔助日誌代碼;這樣,你甚至可以在 Main 方法第一句化就開始使用日誌而無需擔心初始化問題。 + diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md b/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md index 2c86ecd..a3e18b2 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md +++ b/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md @@ -6,9 +6,9 @@ 请将以下 C# 分析器的 Id 进行翻译。 -| Id | zh-hans | zh-hant | en | -|----------------|----------|----------|----------------------------| -| DL0000 | 未知错误。 | 未知錯誤。 | Unknown error. | -| DL0000_Message | 发生了未知错误。 | 發生了未知錯誤。 | An unknown error occurred. | - - +| Id | zh-hans | zh-hant | en | +|----------------|-----------------------------------|-----------------------------------|---------------------------------------------------------------------------------------------------------------| +| DL0000 | 未知错误 | 未知錯誤 | Unknown Error | +| DL0000_Message | 发生了未知错误。 | 發生了未知錯誤。 | An unknown error occurred. | +| DL1001 | 修改入口类为分部类 | 修改入口類為分部類 | Change the entry class to a partial class | +| DL1001_Message | 将 {0} 改为分部类,以允许其在日志模块初始化之前使用日志系统。 | 將 {0} 改為分部類,以允許其在日誌模塊初始化之前使用日誌系統。 | Change {0} to a partial class to allow it to use the logging system before the logging module is initialized. | diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/ProgramMainExtensions.cs b/src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/ProgramMainExtensions.cs new file mode 100644 index 0000000..be9742c --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/ProgramMainExtensions.cs @@ -0,0 +1,65 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace dotnetCampus.Logger.Utils.CodeAnalysis; + +public static class ProgramMainExtensions +{ + /// + /// 从语法上判断一个方法声明是否符合成为 Main 方法的要求。 + /// + /// 语法树中的方法声明。 + /// 是否符合成为 Main 方法的要求。 + public static bool CheckCanBeProgramMain(MethodDeclarationSyntax methodNode) + { + var methodName = methodNode.Identifier.Text; + if (methodName != "Main") + { + // 名称必须是 Main。 + return false; + } + + if (methodNode.Modifiers.Any(SyntaxKind.StaticKeyword) == false) + { + // 必须是静态方法。 + return false; + } + + if (methodNode.ParameterList.Parameters.Count > 1) + { + // 最多只能有一个参数。 + return false; + } + + if (methodNode.ParameterList.Parameters.Count == 1) + { + var parameter = methodNode.ParameterList.Parameters[0]; + if (parameter.Type is not ArrayTypeSyntax { ElementType: PredefinedTypeSyntax spts }) + { + // 参数必须是预定义类型。 + return false; + } + + if (!spts.Keyword.IsKind(SyntaxKind.StringKeyword)) + { + // 参数类型必须是 string[] 或 System.String[]。 + return false; + } + } + + if (methodNode.ReturnType is not PredefinedTypeSyntax ipts) + { + // 返回值必须是预定义类型。 + return false; + } + + if (!ipts.Keyword.IsKind(SyntaxKind.VoidKeyword) && !ipts.Keyword.IsKind(SyntaxKind.IntKeyword)) + { + // 返回值必须是 void 或 int。 + return false; + } + + return true; + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFile.cs similarity index 93% rename from src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs rename to src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFile.cs index d23bd7b..df4c2b6 100644 --- a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFile.cs +++ b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFile.cs @@ -1,4 +1,4 @@ -namespace dotnetCampus.Logger.Utils; +namespace dotnetCampus.Logger.Utils.IO; /// /// 嵌入的文本资源的数据。 diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs similarity index 97% rename from src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs rename to src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs index 6568fe0..bf3766d 100644 --- a/src/dotnetCampus.Logger.Analyzer/Utils/EmbeddedSourceFiles.cs +++ b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs @@ -3,7 +3,7 @@ using System.IO; using System.Reflection; -namespace dotnetCampus.Logger.Utils; +namespace dotnetCampus.Logger.Utils.IO; /// /// 从嵌入的资源中寻找源代码。 From 033a67906d4d4bf5c004f5c157bad567386f9d7a Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 14:53:43 +0800 Subject: [PATCH 19/49] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=B0=86=20Program=20?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=88=86=E9=83=A8=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoggerSample.MainApp.Avalonia/Program.cs | 3 +- .../Assets/Templates/Program.logger.g.cs | 2 +- .../PartialProgramCodeFixProvider.cs | 43 ++++++++++++++++++- .../Diagnostics.cs | 1 - .../Generators/ProgramMainLogGenerator.cs | 2 +- .../Properties/GlobalUsings.cs | 1 + .../Properties/Localizations.Designer.cs | 11 ++++- .../Properties/Localizations.resx | 5 ++- .../Properties/Localizations.zh-hans.resx | 5 ++- .../Properties/Localizations.zh-hant.resx | 5 ++- .../Utils/IO/Log.cs | 16 +++++++ .../dotnetCampus.Logger.Analyzer.csproj | 20 ++------- 12 files changed, 86 insertions(+), 28 deletions(-) create mode 100644 src/dotnetCampus.Logger.Analyzer/Properties/GlobalUsings.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/Utils/IO/Log.cs diff --git a/samples/LoggerSample.MainApp.Avalonia/Program.cs b/samples/LoggerSample.MainApp.Avalonia/Program.cs index cbf1966..296c83c 100644 --- a/samples/LoggerSample.MainApp.Avalonia/Program.cs +++ b/samples/LoggerSample.MainApp.Avalonia/Program.cs @@ -1,10 +1,9 @@ using Avalonia; using System; -using dotnetCampus.Logging; namespace dotnetCampus.LoggerSample.Avalonia; -internal class Program +internal partial class Program { // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized diff --git a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs index 7900e08..a5f145f 100644 --- a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs +++ b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs @@ -8,7 +8,7 @@ using LazyThreadSafetyMode = global::System.Threading.LazyThreadSafetyMode; using LogLevel = global::dotnetCampus.Logging.LogLevel; -namespace dotnetCampus.Logger.Templates; +namespace dotnetCampus.Logger.Assets.Templates; partial class Program { diff --git a/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs index d764124..ccf5a24 100644 --- a/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs +++ b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs @@ -1,8 +1,12 @@ using System.Collections.Immutable; using System.Composition; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace dotnetCampus.Logger.CodeFixeProviders; @@ -15,8 +19,43 @@ public class PartialProgramCodeFixProvider : CodeFixProvider public override FixAllProvider? GetFixAllProvider() => null; - public override Task RegisterCodeFixesAsync(CodeFixContext context) + public override async Task RegisterCodeFixesAsync(CodeFixContext context) { - return Task.CompletedTask; + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken); + if (root is null) + { + return; + } + + var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken); + if (semanticModel is null) + { + return; + } + + foreach (var diagnostic in context.Diagnostics) + { + var diagnosticSpan = diagnostic.Location.SourceSpan; + var cds = (ClassDeclarationSyntax)root.FindNode(diagnosticSpan); + context.RegisterCodeFix( + CodeAction.Create( + title: string.Format(DL1001_Fix, cds.Identifier.Text), + createChangedDocument: c => AddPartialModifierAsync(context.Document, cds, c), + equivalenceKey: nameof(DL1001_Fix)), + diagnostic); + } + } + + private async Task AddPartialModifierAsync(Document document, ClassDeclarationSyntax classDeclarationNode, CancellationToken cancellationToken) + { + var newClassDeclarationNode = classDeclarationNode.AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword)); + var root = await document.GetSyntaxRootAsync(cancellationToken); + if (root is null) + { + return document; + } + + var newRoot = root.ReplaceNode(classDeclarationNode, newClassDeclarationNode); + return document.WithSyntaxRoot(newRoot); } } diff --git a/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs b/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs index f138c6c..d006614 100644 --- a/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs +++ b/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs @@ -1,6 +1,5 @@ using dotnetCampus.Logger.Properties; using Microsoft.CodeAnalysis; -using static dotnetCampus.Logger.Properties.Localizations; // ReSharper disable InconsistentNaming diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs index ae105e9..db2e10e 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Text; -using dotnetCampus.Logger.Templates; +using dotnetCampus.Logger.Assets.Templates; using dotnetCampus.Logger.Utils.IO; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/GlobalUsings.cs b/src/dotnetCampus.Logger.Analyzer/Properties/GlobalUsings.cs new file mode 100644 index 0000000..cf21062 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Properties/GlobalUsings.cs @@ -0,0 +1 @@ +global using static dotnetCampus.Logger.Properties.Localizations; diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs index ba7b1fd..785b88a 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.Designer.cs @@ -96,7 +96,16 @@ internal static string DL1001_Description { } /// - /// Looks up a localized string similar to Change {0} to a partial class to allow it to use the logging system before the logging module is initialized.. + /// Looks up a localized string similar to Change '{0}' to a partial class. + /// + internal static string DL1001_Fix { + get { + return ResourceManager.GetString("DL1001_Fix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change '{0}' to a partial class to allow it to use the logging system before the logging module is initialized.. /// internal static string DL1001_Message { get { diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx index 62133fc..1b3b12d 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx @@ -33,9 +33,12 @@ Change the entry class to a partial class - Change {0} to a partial class to allow it to use the logging system before the logging module is initialized. + Change '{0}' to a partial class to allow it to use the logging system before the logging module is initialized. After changing the entry class to a partial class, the source generator can generate auxiliary log code according to the subsequent log initialization requirements; this way, you can even start using logs in the first sentence of the Main method without worrying about initialization issues. + + Change '{0}' to a partial class + diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx index c3c9a7a..b06dc7a 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx @@ -22,7 +22,7 @@ 发生了未知错误。 - 将 {0} 改为分部类,以允许其在日志模块初始化之前使用日志系统。 + 将 '{0}' 改为分部类,以允许其在日志模块初始化之前使用日志系统。 修改入口类为分部类 @@ -30,4 +30,7 @@ 将入口类改为分部类后,源生成器可以根据后续日志初始化的要求生成辅助日志代码;这样,你甚至可以在 Main 方法第一句化就开始使用日志而无需担心初始化问题。 + + 将 '{0}' 设为 partial + diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx index f80d441..d811e51 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx @@ -22,7 +22,7 @@ 發生了未知錯誤。 - 將 {0} 改為分部類,以允許其在日誌模塊初始化之前使用日誌系統。 + 將 '{0}' 改為分部類,以允許其在日誌模塊初始化之前使用日誌系統。 修改入口類為分部類 @@ -30,4 +30,7 @@ 將入口類改為分部類後,源生成器可以根據後續日誌初始化的要求生成輔助日誌代碼;這樣,你甚至可以在 Main 方法第一句化就開始使用日誌而無需擔心初始化問題。 + + 將 '{0}' 設為 partial + diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/IO/Log.cs b/src/dotnetCampus.Logger.Analyzer/Utils/IO/Log.cs new file mode 100644 index 0000000..5138017 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Utils/IO/Log.cs @@ -0,0 +1,16 @@ +using System; +using System.Diagnostics; +using System.IO; + +// ReSharper disable CheckNamespace + +namespace dotnetCampus.Logger; + +internal static class Log +{ + [Conditional("DEBUG")] + internal static void Debug(string message) + { + Debugger.Launch(); + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 2475354..0af25c5 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -14,32 +14,18 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - True - True - Localizations.zh-CN.resx - - - True - True - Localications.zh-CN.resx - - - - ResXFileCodeGenerator - Localizations.Designer.cs - + From 9f200308064da572dabecf3bf9ccf9be65b7e970 Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 16:24:17 +0800 Subject: [PATCH 20/49] =?UTF-8?q?=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoggerSample.MainApp.Avalonia/App.axaml | 4 +-- .../App.axaml.cs | 2 +- .../MainWindow.axaml | 4 +-- .../MainWindow.axaml.cs | 2 +- .../LoggerSample.MainApp.Avalonia/Program.cs | 15 +++++------ .../Startup/LogStartup.cs | 15 +++++++++++ .../PartialProgramAnalyzer.cs | 6 +++-- .../Generators/ProgramMainLogGenerator.cs | 26 +++++++++++-------- .../{LogExtensions.cs => LoggerExtensions.cs} | 4 ++- 9 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs rename src/dotnetCampus.Logger/{LogExtensions.cs => LoggerExtensions.cs} (98%) diff --git a/samples/LoggerSample.MainApp.Avalonia/App.axaml b/samples/LoggerSample.MainApp.Avalonia/App.axaml index 10086d8..484dbc9 100644 --- a/samples/LoggerSample.MainApp.Avalonia/App.axaml +++ b/samples/LoggerSample.MainApp.Avalonia/App.axaml @@ -1,8 +1,8 @@ - + diff --git a/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs b/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs index addf22e..934c1b0 100644 --- a/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs +++ b/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs @@ -2,7 +2,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -namespace dotnetCampus.LoggerSample.Avalonia; +namespace dotnetCampus.LoggerSample; public partial class App : Application { diff --git a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml index 375a54d..218f057 100644 --- a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml +++ b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml @@ -3,7 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="dotnetCampus.LoggerSample.Avalonia.MainWindow" + x:Class="dotnetCampus.LoggerSample.MainWindow" Title="LoggerSample.MainApp.Avalonia"> Welcome to Avalonia! - + \ No newline at end of file diff --git a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs index 4fd3557..1cd2c09 100644 --- a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs +++ b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs @@ -1,6 +1,6 @@ using Avalonia.Controls; -namespace dotnetCampus.LoggerSample.Avalonia; +namespace dotnetCampus.LoggerSample; public partial class MainWindow : Window { diff --git a/samples/LoggerSample.MainApp.Avalonia/Program.cs b/samples/LoggerSample.MainApp.Avalonia/Program.cs index 296c83c..575144f 100644 --- a/samples/LoggerSample.MainApp.Avalonia/Program.cs +++ b/samples/LoggerSample.MainApp.Avalonia/Program.cs @@ -1,7 +1,8 @@ -using Avalonia; -using System; +using System; +using Avalonia; +using dotnetCampus.LoggerSample.Startup; -namespace dotnetCampus.LoggerSample.Avalonia; +namespace dotnetCampus.LoggerSample; internal partial class Program { @@ -11,11 +12,6 @@ internal partial class Program [STAThread] public static void Main(string[] args) { - // var logger = new LogBuilder() - // .WithO - // .AddWriter(new ConsoleLogWriter().WithOptions) - // .Build(); - BuildAvaloniaApp() .StartWithClassicDesktopLifetime(args); } @@ -25,5 +21,6 @@ public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() .WithInterFont() - .LogToTrace(); + .UseLogger(b => b + .UseMemoryCache(Log)); } diff --git a/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs b/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs new file mode 100644 index 0000000..1109278 --- /dev/null +++ b/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs @@ -0,0 +1,15 @@ +using System; +using Avalonia; +using dotnetCampus.Logging; + +namespace dotnetCampus.LoggerSample.Startup; + +public static class LogStartup +{ + public static AppBuilder UseLogger(this AppBuilder appBuilder, Action builder) + { + var b = new LoggerBuilder(); + builder(b); + return appBuilder.LogToTrace(); + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs index 26ded7d..17b3638 100644 --- a/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs +++ b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs @@ -24,10 +24,12 @@ public override void Initialize(AnalysisContext context) private void AnalyzeProgram(SyntaxNodeAnalysisContext context) { - if (context.Node is MemberDeclarationSyntax + if (context.Node is MethodDeclarationSyntax { Parent: ClassDeclarationSyntax cds, - } && !cds.Modifiers.Any(SyntaxKind.PartialKeyword)) + } mds + && Utils.CodeAnalysis.ProgramMainExtensions.CheckCanBeProgramMain(mds) + && !cds.Modifiers.Any(SyntaxKind.PartialKeyword)) { var spanStart = cds.Modifiers.Count is 0 ? cds.Keyword.SpanStart diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs index db2e10e..e740ab8 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Immutable; using System.Linq; using System.Text; using dotnetCampus.Logger.Assets.Templates; @@ -60,18 +61,21 @@ public void Initialize(IncrementalGeneratorInitializationContext context) private void Execute(SourceProductionContext context, INamedTypeSymbol programTypeSymbol) { var templateProgramNamespace = typeof(Program).Namespace!; - var generatedProgramNamespace = programTypeSymbol.ContainingNamespace.ToDisplayString(); - var templatesFolder = templateProgramNamespace.AsSpan().Slice(AssemblyInfo.RootNamespace.Length + 1).ToString(); - foreach (var template in EmbeddedSourceFiles - .Enumerate(templatesFolder) - .Where(x => x.FileName.StartsWith("Program."))) - { - var generatedText = template.Content - .Replace(templateProgramNamespace, generatedProgramNamespace) - .Replace("Program", programTypeSymbol.Name); + var embeddedFiles = EmbeddedSourceFiles.Enumerate(templatesFolder).ToImmutableArray(); + + // 生成 Program.Logger.g.cs + var partialLoggerFile = embeddedFiles.First(x => x.FileName.StartsWith("Program.", StringComparison.Ordinal)); + var generatedLoggerText = ConvertPartialProgramLogger(partialLoggerFile.Content, programTypeSymbol); + context.AddSource($"{programTypeSymbol.Name}.Logger.g.cs", SourceText.From(generatedLoggerText, Encoding.UTF8)); + } - context.AddSource(template.FileName, SourceText.From(generatedText, Encoding.UTF8)); - } + private string ConvertPartialProgramLogger(string sourceText, INamedTypeSymbol programTypeSymbol) + { + var templateProgramNamespace = typeof(Program).Namespace!; + var generatedProgramNamespace = programTypeSymbol.ContainingNamespace.ToDisplayString(); + return sourceText + .Replace(templateProgramNamespace, generatedProgramNamespace) + .Replace("Program", programTypeSymbol.Name); } } diff --git a/src/dotnetCampus.Logger/LogExtensions.cs b/src/dotnetCampus.Logger/LoggerExtensions.cs similarity index 98% rename from src/dotnetCampus.Logger/LogExtensions.cs rename to src/dotnetCampus.Logger/LoggerExtensions.cs index eb4695b..96e0410 100644 --- a/src/dotnetCampus.Logger/LogExtensions.cs +++ b/src/dotnetCampus.Logger/LoggerExtensions.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; namespace dotnetCampus.Logging; From 391afa42c32323a0921444c2ee34e384e09352f7 Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 17:46:13 +0800 Subject: [PATCH 21/49] =?UTF-8?q?=E5=9C=A8=E7=9B=AE=E6=A0=87=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=94=9F=E6=88=90=E5=86=85=E9=83=A8=E7=9A=84=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoggerSample.MainApp.Avalonia/Program.cs | 3 +- .../Generators/LoggerGenerator.cs | 67 +++++++++++++++++++ .../Generators/ProgramMainLogGenerator.cs | 2 + .../dotnetCampus.Logger.Analyzer.csproj | 13 ++-- src/dotnetCampus.Logger/EventId.cs | 4 +- src/dotnetCampus.Logger/ILogger.cs | 14 +--- src/dotnetCampus.Logger/LogLevel.cs | 4 +- .../Properties/Package/build/Package.props | 4 ++ 8 files changed, 90 insertions(+), 21 deletions(-) create mode 100644 src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs diff --git a/samples/LoggerSample.MainApp.Avalonia/Program.cs b/samples/LoggerSample.MainApp.Avalonia/Program.cs index 575144f..6784d8a 100644 --- a/samples/LoggerSample.MainApp.Avalonia/Program.cs +++ b/samples/LoggerSample.MainApp.Avalonia/Program.cs @@ -21,6 +21,5 @@ public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() .WithInterFont() - .UseLogger(b => b - .UseMemoryCache(Log)); + .UseLogger(b => { }); } diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs new file mode 100644 index 0000000..407ed3f --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using dotnetCampus.Logger.Utils.IO; +using dotnetCampus.Logging; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace dotnetCampus.Logger.Generators; + +/// +/// 生成一组用于记录日志的代码。 +/// +[Generator] +public class LoggerGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var provider = context.AnalyzerConfigOptionsProvider; + context.RegisterSourceOutput(provider, Execute); + } + + private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvider provider) + { + if (!provider.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace)) + { + return; + } + + foreach (var file in EmbeddedSourceFiles.Enumerate("Assets/Sources")) + { + var code = GenerateSource(rootNamespace, file.Content); + context.AddSource(file.FileName, SourceText.From(code, Encoding.UTF8)); + } + } + + private string GenerateSource(string rootNamespace, string sourceText) + { + var sourceSpan = sourceText.AsSpan(); + + var namespaceKeywordIndex = sourceText.IndexOf("namespace", StringComparison.Ordinal); + var namespaceStartIndex = namespaceKeywordIndex + "namespace".Length + 1; + var namespaceEndIndex = sourceText.IndexOf(";", namespaceStartIndex, StringComparison.Ordinal); + + var @namespace = sourceSpan.Slice(namespaceStartIndex, namespaceEndIndex - namespaceStartIndex).Trim(); + + // 搜索 class/record/struct/enum/interface 等关键字 + var classKeywordIndex = GetTypeRegex().Match(sourceText).Index; + var publicKeywordIndex = sourceText.IndexOf("public", namespaceEndIndex, classKeywordIndex - namespaceEndIndex, StringComparison.Ordinal); + + return string.Concat( + sourceSpan.Slice(0, namespaceStartIndex).ToString(), + rootNamespace, + sourceSpan.Slice(namespaceEndIndex, publicKeywordIndex - namespaceEndIndex).ToString(), + "internal", + sourceSpan.Slice(publicKeywordIndex + "public".Length, sourceSpan.Length - publicKeywordIndex - "public".Length).ToString() + ); + } + + private static Regex? _typeRegex; + + private static Regex GetTypeRegex() => _typeRegex ??= new Regex(@"\b(?:class|record|struct|enum|interface)\b", RegexOptions.Compiled); +} diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs index e740ab8..891416f 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs @@ -20,6 +20,8 @@ public class ProgramMainLogGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + return; + var provider = context.SyntaxProvider.CreateSyntaxProvider((node, ct) => { if (node is not MethodDeclarationSyntax mds) diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 0af25c5..177369d 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -19,12 +19,13 @@ - - - - - - + <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\EventId.cs" /> + <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\ILogger.cs" /> + <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\Log.cs" /> + <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\LoggerExtensions.cs" /> + <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\LogLevel.cs" /> + + diff --git a/src/dotnetCampus.Logger/EventId.cs b/src/dotnetCampus.Logger/EventId.cs index 1ecbd77..29c80a9 100644 --- a/src/dotnetCampus.Logger/EventId.cs +++ b/src/dotnetCampus.Logger/EventId.cs @@ -1,4 +1,6 @@ -using System.Diagnostics.CodeAnalysis; +#nullable enable + +using System.Diagnostics.CodeAnalysis; namespace dotnetCampus.Logging; diff --git a/src/dotnetCampus.Logger/ILogger.cs b/src/dotnetCampus.Logger/ILogger.cs index b25fc66..3261b2c 100644 --- a/src/dotnetCampus.Logger/ILogger.cs +++ b/src/dotnetCampus.Logger/ILogger.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; namespace dotnetCampus.Logging; @@ -20,13 +22,3 @@ void Log( Exception? exception, Func formatter); } - -/// -/// A generic interface for logging where the category name is derived from the specified -/// type name. -/// Generally used to enable activation of a named from dependency injection. -/// -/// The type whose name is used for the logger category name. -public interface ILogger : ILogger -{ -} diff --git a/src/dotnetCampus.Logger/LogLevel.cs b/src/dotnetCampus.Logger/LogLevel.cs index 3203bd3..6e1fa45 100644 --- a/src/dotnetCampus.Logger/LogLevel.cs +++ b/src/dotnetCampus.Logger/LogLevel.cs @@ -1,4 +1,6 @@ -namespace dotnetCampus.Logging; +#nullable enable + +namespace dotnetCampus.Logging; /// /// Defines logging severity levels. diff --git a/src/dotnetCampus.Logger/Properties/Package/build/Package.props b/src/dotnetCampus.Logger/Properties/Package/build/Package.props index 44d225a..68352a5 100644 --- a/src/dotnetCampus.Logger/Properties/Package/build/Package.props +++ b/src/dotnetCampus.Logger/Properties/Package/build/Package.props @@ -4,4 +4,8 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + From 8689a11b5ff55156e0af9fd619c3b08cc1f10310 Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 17:47:38 +0800 Subject: [PATCH 22/49] =?UTF-8?q?=E6=95=B4=E7=90=86=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoggerSample.LoggerDependentLibrary.csproj | 4 ++++ .../LoggerSample.LoggerIndependentLibrary.csproj | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj index f34859b..16d03b1 100644 --- a/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj +++ b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj @@ -6,4 +6,8 @@ dotnetCampus.LoggerSample + + + + diff --git a/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj index f34859b..bd5a17f 100644 --- a/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj +++ b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj @@ -6,4 +6,8 @@ dotnetCampus.LoggerSample + + + + From d8aa6acd898a4e22b34fb2fad9d365902d98b0ac Mon Sep 17 00:00:00 2001 From: walterlv Date: Wed, 8 May 2024 17:56:52 +0800 Subject: [PATCH 23/49] =?UTF-8?q?=E7=94=9F=E6=88=90=20GlobalUsings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Generators/LoggerGenerator.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs index 407ed3f..8a3ff01 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs @@ -1,12 +1,10 @@ using System; +using System.Collections.Immutable; using System.Linq; using System.Text; using System.Text.RegularExpressions; using dotnetCampus.Logger.Utils.IO; -using dotnetCampus.Logging; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; @@ -31,11 +29,18 @@ private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvi return; } - foreach (var file in EmbeddedSourceFiles.Enumerate("Assets/Sources")) + var sourceFiles = EmbeddedSourceFiles.Enumerate("Assets/Sources").ToImmutableArray(); + + foreach (var file in sourceFiles) { var code = GenerateSource(rootNamespace, file.Content); context.AddSource(file.FileName, SourceText.From(code, Encoding.UTF8)); } + + var globalUsingsCode = GenerateGlobalUsings( + rootNamespace, + [..sourceFiles.Select(x => x.FileName.Substring(0, x.FileName.IndexOf('.')))]); + context.AddSource("GlobalUsings.g.cs", SourceText.From(globalUsingsCode, Encoding.UTF8)); } private string GenerateSource(string rootNamespace, string sourceText) @@ -54,13 +59,23 @@ private string GenerateSource(string rootNamespace, string sourceText) return string.Concat( sourceSpan.Slice(0, namespaceStartIndex).ToString(), - rootNamespace, + $"{rootNamespace}.Logging", sourceSpan.Slice(namespaceEndIndex, publicKeywordIndex - namespaceEndIndex).ToString(), "internal", sourceSpan.Slice(publicKeywordIndex + "public".Length, sourceSpan.Length - publicKeywordIndex - "public".Length).ToString() ); } + private string GenerateGlobalUsings(string rootNamespace, ImmutableArray typeNames) + { + return $""" +global using {rootNamespace}.Logging; + +{string.Join("\n", typeNames.Select(x => $"global using {x} = {rootNamespace}.Logging.{x};"))} + +"""; + } + private static Regex? _typeRegex; private static Regex GetTypeRegex() => _typeRegex ??= new Regex(@"\b(?:class|record|struct|enum|interface)\b", RegexOptions.Compiled); From 846bd9155779cfacd003417c47419cd9cd5e9ea7 Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 14:36:52 +0800 Subject: [PATCH 24/49] =?UTF-8?q?=E5=86=8D=E7=94=9F=E6=88=90=E5=87=A0?= =?UTF-8?q?=E4=B8=AA=20Writers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Class1.cs | 7 +- ...LoggerSample.LoggerDependentLibrary.csproj | 1 - ...ggerSample.LoggerIndependentLibrary.csproj | 1 - .../LoggerSample.MainApp.Avalonia/App.axaml | 2 +- .../App.axaml.cs | 2 +- .../LoggerSample.MainApp.Avalonia.csproj | 1 - .../MainWindow.axaml | 2 +- .../MainWindow.axaml.cs | 2 +- .../LoggerSample.MainApp.Avalonia/Program.cs | 4 +- .../Startup/LogStartup.cs | 2 +- .../Generators/LoggerGenerator.cs | 44 +++++++----- .../dotnetCampus.Logger.Analyzer.csproj | 4 ++ src/dotnetCampus.Logger/Log.cs | 70 ++++++++++++++++++- .../Properties/GenerationUsings.cs | 3 + .../Properties/GlobalUsings.cs | 2 + .../Writers/DebugLogger.cs | 57 +++++++++++++++ src/dotnetCampus.Logger/Writers/NullLogger.cs | 4 +- .../Writers/TraceLogger.cs | 57 +++++++++++++++ 18 files changed, 235 insertions(+), 30 deletions(-) create mode 100644 src/dotnetCampus.Logger/Properties/GenerationUsings.cs create mode 100644 src/dotnetCampus.Logger/Writers/DebugLogger.cs create mode 100644 src/dotnetCampus.Logger/Writers/TraceLogger.cs diff --git a/samples/LoggerSample.LoggerDependentLibrary/Class1.cs b/samples/LoggerSample.LoggerDependentLibrary/Class1.cs index 7c67d96..46d43c0 100644 --- a/samples/LoggerSample.LoggerDependentLibrary/Class1.cs +++ b/samples/LoggerSample.LoggerDependentLibrary/Class1.cs @@ -1,5 +1,10 @@ -namespace LoggerSample.LoggerDependentLibrary; +using dotnetCampus.Logging; + +namespace LoggerSample.LoggerDependentLibrary; public class Class1 { + public void CollectLogs() + { + } } diff --git a/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj index 16d03b1..4924947 100644 --- a/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj +++ b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj @@ -3,7 +3,6 @@ net8.0 enable - dotnetCampus.LoggerSample diff --git a/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj index bd5a17f..d2fce35 100644 --- a/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj +++ b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj @@ -3,7 +3,6 @@ net8.0 enable - dotnetCampus.LoggerSample diff --git a/samples/LoggerSample.MainApp.Avalonia/App.axaml b/samples/LoggerSample.MainApp.Avalonia/App.axaml index 484dbc9..c40f1e0 100644 --- a/samples/LoggerSample.MainApp.Avalonia/App.axaml +++ b/samples/LoggerSample.MainApp.Avalonia/App.axaml @@ -1,6 +1,6 @@ diff --git a/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs b/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs index 934c1b0..bc1d194 100644 --- a/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs +++ b/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs @@ -2,7 +2,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -namespace dotnetCampus.LoggerSample; +namespace LoggerSample.MainApp.Avalonia; public partial class App : Application { diff --git a/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj b/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj index 7ea671d..cb32cbe 100644 --- a/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj +++ b/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj @@ -3,7 +3,6 @@ WinExe net8.0 - dotnetCampus.LoggerSample true Properties\App.manifest true diff --git a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml index 218f057..c274d16 100644 --- a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml +++ b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml @@ -3,7 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="dotnetCampus.LoggerSample.MainWindow" + x:Class="LoggerSample.MainApp.Avalonia.MainWindow" Title="LoggerSample.MainApp.Avalonia"> Welcome to Avalonia! \ No newline at end of file diff --git a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs index 1cd2c09..c3341c7 100644 --- a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs +++ b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs @@ -1,6 +1,6 @@ using Avalonia.Controls; -namespace dotnetCampus.LoggerSample; +namespace LoggerSample.MainApp.Avalonia; public partial class MainWindow : Window { diff --git a/samples/LoggerSample.MainApp.Avalonia/Program.cs b/samples/LoggerSample.MainApp.Avalonia/Program.cs index 6784d8a..0ca71d3 100644 --- a/samples/LoggerSample.MainApp.Avalonia/Program.cs +++ b/samples/LoggerSample.MainApp.Avalonia/Program.cs @@ -1,8 +1,8 @@ using System; using Avalonia; -using dotnetCampus.LoggerSample.Startup; +using LoggerSample.MainApp.Avalonia.Startup; -namespace dotnetCampus.LoggerSample; +namespace LoggerSample.MainApp.Avalonia; internal partial class Program { diff --git a/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs b/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs index 1109278..3ae0796 100644 --- a/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs +++ b/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs @@ -2,7 +2,7 @@ using Avalonia; using dotnetCampus.Logging; -namespace dotnetCampus.LoggerSample.Startup; +namespace LoggerSample.MainApp.Avalonia.Startup; public static class LogStartup { diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs index 8a3ff01..04440ae 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs @@ -31,16 +31,16 @@ private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvi var sourceFiles = EmbeddedSourceFiles.Enumerate("Assets/Sources").ToImmutableArray(); + var globalUsingsCode = GenerateGlobalUsings( + rootNamespace, + [..sourceFiles.Select(x => x.FileName.Substring(0, x.FileName.IndexOf('.')))]); + context.AddSource("GlobalUsings.g.cs", SourceText.From(globalUsingsCode, Encoding.UTF8)); + foreach (var file in sourceFiles) { var code = GenerateSource(rootNamespace, file.Content); context.AddSource(file.FileName, SourceText.From(code, Encoding.UTF8)); } - - var globalUsingsCode = GenerateGlobalUsings( - rootNamespace, - [..sourceFiles.Select(x => x.FileName.Substring(0, x.FileName.IndexOf('.')))]); - context.AddSource("GlobalUsings.g.cs", SourceText.From(globalUsingsCode, Encoding.UTF8)); } private string GenerateSource(string rootNamespace, string sourceText) @@ -51,27 +51,37 @@ private string GenerateSource(string rootNamespace, string sourceText) var namespaceStartIndex = namespaceKeywordIndex + "namespace".Length + 1; var namespaceEndIndex = sourceText.IndexOf(";", namespaceStartIndex, StringComparison.Ordinal); - var @namespace = sourceSpan.Slice(namespaceStartIndex, namespaceEndIndex - namespaceStartIndex).Trim(); - - // 搜索 class/record/struct/enum/interface 等关键字 var classKeywordIndex = GetTypeRegex().Match(sourceText).Index; var publicKeywordIndex = sourceText.IndexOf("public", namespaceEndIndex, classKeywordIndex - namespaceEndIndex, StringComparison.Ordinal); - return string.Concat( - sourceSpan.Slice(0, namespaceStartIndex).ToString(), - $"{rootNamespace}.Logging", - sourceSpan.Slice(namespaceEndIndex, publicKeywordIndex - namespaceEndIndex).ToString(), - "internal", - sourceSpan.Slice(publicKeywordIndex + "public".Length, sourceSpan.Length - publicKeywordIndex - "public".Length).ToString() - ); + if (publicKeywordIndex < 0) + { + // 此类型不是 public 的,无需修改为 internal;仅修改命名空间即可。 + return string.Concat( + sourceSpan.Slice(0, namespaceStartIndex).ToString(), + $"{rootNamespace}.Logging", + sourceSpan.Slice(namespaceEndIndex, sourceSpan.Length - namespaceEndIndex).ToString() + ); + } + else + { + // 此类型是 public 的,需要修改为 internal。 + return string.Concat( + sourceSpan.Slice(0, namespaceStartIndex).ToString(), + $"{rootNamespace}.Logging", + sourceSpan.Slice(namespaceEndIndex, publicKeywordIndex - namespaceEndIndex).ToString(), + "internal", + sourceSpan.Slice(publicKeywordIndex + "public".Length, sourceSpan.Length - publicKeywordIndex - "public".Length).ToString() + ); + } } private string GenerateGlobalUsings(string rootNamespace, ImmutableArray typeNames) { return $""" -global using {rootNamespace}.Logging; +global using global::{rootNamespace}.Logging; -{string.Join("\n", typeNames.Select(x => $"global using {x} = {rootNamespace}.Logging.{x};"))} +{string.Join("\n", typeNames.Select(x => $"global using {x} = global::{rootNamespace}.Logging.{x};"))} """; } diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 177369d..dd64f8e 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -19,11 +19,15 @@ + <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\Writers\DebugLogger.cs" /> + <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\Writers\NullLogger.cs" /> + <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\Writers\TraceLogger.cs" /> <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\EventId.cs" /> <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\ILogger.cs" /> <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\Log.cs" /> <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\LoggerExtensions.cs" /> <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\LogLevel.cs" /> + diff --git a/src/dotnetCampus.Logger/Log.cs b/src/dotnetCampus.Logger/Log.cs index 8dd14ce..12cc59f 100644 --- a/src/dotnetCampus.Logger/Log.cs +++ b/src/dotnetCampus.Logger/Log.cs @@ -1,6 +1,74 @@ -namespace dotnetCampus.Logging; +#nullable enable + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace dotnetCampus.Logging; public static class Log { + private static ILogger _current; + + static Log() + { + Current = new NullLogger(); + } + + + public static ILogger Current + { + get => _current; + [MemberNotNull(nameof(_current), nameof(Debug), nameof(Trace))] + private set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (Equals(_current, value)) + { + Debug ??= new DebugLogger(value); + Trace ??= new TraceLogger(value); + return; + } + + _current = value; + Debug = new DebugLogger(value); + Trace = new TraceLogger(value); + } + } + + public static DebugLogger Debug { get; private set; } + + public static TraceLogger Trace { get; private set; } + + /// + /// 正常记录日志。 + /// + /// 要记录的消息。 + public static void Info(string message) + { + Current.Log(LogLevel.Information, default, message, null, (s, ex) => message); + } + + /// + /// 记录警告日志。 + /// + /// 要记录的消息,形如 [tag] message + /// 相关的异常信息。 + public static void Warn(string message, Exception? exception = null) + { + Current.Log(LogLevel.Warning, default, message, exception, (s, ex) => message); + } + /// + /// 记录错误日志。 + /// + /// 要记录的消息,形如 [tag] message + /// 相关的异常信息。 + public static void Error(string message, Exception? exception = null) + { + Current.Log(LogLevel.Error, default, message, exception, (s, ex) => message); + } } diff --git a/src/dotnetCampus.Logger/Properties/GenerationUsings.cs b/src/dotnetCampus.Logger/Properties/GenerationUsings.cs new file mode 100644 index 0000000..4904880 --- /dev/null +++ b/src/dotnetCampus.Logger/Properties/GenerationUsings.cs @@ -0,0 +1,3 @@ +global using DebugLogger = dotnetCampus.Logging.Writers.DebugLogger; +global using NullLogger = dotnetCampus.Logging.Writers.NullLogger; +global using TraceLogger = dotnetCampus.Logging.Writers.TraceLogger; diff --git a/src/dotnetCampus.Logger/Properties/GlobalUsings.cs b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs index 231d9ae..528e55f 100644 --- a/src/dotnetCampus.Logger/Properties/GlobalUsings.cs +++ b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs @@ -1,6 +1,7 @@ global using dotnetCampus.Logging.Properties; // .NET 8.0 or later + #if NET8_0_OR_GREATER global using ImmutableArrayILogger = System.Collections.Immutable.ImmutableArray; global using ImmutableHashSetString = System.Collections.Immutable.ImmutableHashSet; @@ -13,6 +14,7 @@ #if NET6_0_OR_GREATER global using System.Collections.Immutable; global using Math = System.Math; + #else global using Math = dotnetCampus.Logging.Properties.Compatibility; #endif diff --git a/src/dotnetCampus.Logger/Writers/DebugLogger.cs b/src/dotnetCampus.Logger/Writers/DebugLogger.cs new file mode 100644 index 0000000..765e8cc --- /dev/null +++ b/src/dotnetCampus.Logger/Writers/DebugLogger.cs @@ -0,0 +1,57 @@ +#nullable enable + +using System; +using System.Diagnostics; + +namespace dotnetCampus.Logging.Writers; + +/// +/// 提供仅在 debug 下才会记录的日志。 +/// +public class DebugLogger(ILogger realLogger) : ILogger +{ + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + realLogger.Log(logLevel, eventId, state, exception, formatter); + } + + /// + /// 在开启了追踪的情况下输出日志。(默认开启) + /// + /// 要记录的消息,形如 [tag] message + [Conditional("DEBUG")] + public void Trace(string message) + { + realLogger.Log(LogLevel.Trace, default, message, null, (s, ex) => message); + } + + /// + /// 记录 debug 级别的日志。 + /// + /// 要记录的消息。 + [Conditional("DEBUG")] + public void Debug(string message) + { + realLogger.Log(LogLevel.Debug, default, message, null, (s, ex) => message); + } + + /// + /// 正常记录日志。 + /// + /// 要记录的消息。 + [Conditional("DEBUG")] + public void Info(string message) + { + realLogger.Log(LogLevel.Information, default, message, null, (s, ex) => message); + } + + /// + /// 记录警告日志。 + /// + /// 要记录的消息,形如 [tag] message + [Conditional("DEBUG")] + public void Warn(string message) + { + realLogger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); + } +} diff --git a/src/dotnetCampus.Logger/Writers/NullLogger.cs b/src/dotnetCampus.Logger/Writers/NullLogger.cs index 868aa77..c0e50f0 100644 --- a/src/dotnetCampus.Logger/Writers/NullLogger.cs +++ b/src/dotnetCampus.Logger/Writers/NullLogger.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; namespace dotnetCampus.Logging.Writers; diff --git a/src/dotnetCampus.Logger/Writers/TraceLogger.cs b/src/dotnetCampus.Logger/Writers/TraceLogger.cs new file mode 100644 index 0000000..daa4067 --- /dev/null +++ b/src/dotnetCampus.Logger/Writers/TraceLogger.cs @@ -0,0 +1,57 @@ +#nullable enable + +using System; +using System.Diagnostics; + +namespace dotnetCampus.Logging.Writers; + +/// +/// 提供仅在 debug 下才会记录的日志。 +/// +public class TraceLogger(ILogger realLogger) : ILogger +{ + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + realLogger.Log(logLevel, eventId, state, exception, formatter); + } + + /// + /// 在开启了追踪的情况下输出日志。(默认开启) + /// + /// 要记录的消息,形如 [tag] message + [Conditional("TRACE")] + public void Trace(string message) + { + realLogger.Log(LogLevel.Trace, default, message, null, (s, ex) => message); + } + + /// + /// 记录 debug 级别的日志。 + /// + /// 要记录的消息。 + [Conditional("TRACE")] + public void Debug(string message) + { + realLogger.Log(LogLevel.Debug, default, message, null, (s, ex) => message); + } + + /// + /// 正常记录日志。 + /// + /// 要记录的消息。 + [Conditional("TRACE")] + public void Info(string message) + { + realLogger.Log(LogLevel.Information, default, message, null, (s, ex) => message); + } + + /// + /// 记录警告日志。 + /// + /// 要记录的消息,形如 [tag] message + [Conditional("TRACE")] + public void Warn(string message) + { + realLogger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); + } +} From 473c15a16cd2cf03b8471df73d5d1f582a5b0c78 Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 14:43:38 +0800 Subject: [PATCH 25/49] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20.g.cs=20=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E5=B0=86=E6=9D=A5=E9=9C=80=E8=A6=81=E8=A2=AB=E7=94=9F?= =?UTF-8?q?=E6=88=90=E7=9A=84=E7=B1=BB=EF=BC=8C=E8=BF=99=E6=A0=B7=E5=88=86?= =?UTF-8?q?=E6=9E=90=E5=99=A8=E5=8F=AF=E4=BB=A5=E5=91=8A=E8=AF=89=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E8=80=85=E9=92=88=E5=AF=B9=E7=94=9F=E6=88=90=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=9A=84=E8=BD=AC=E6=9C=89=E5=88=86=E6=9E=90=EF=BC=9A?= =?UTF-8?q?=201.=20=E6=8F=90=E9=86=92=E5=BF=85=E9=A1=BB=E5=86=99=20#nullab?= =?UTF-8?q?le=20enable=202.=20=E4=B8=8D=E4=BC=9A=E8=A6=81=E6=B1=82?= =?UTF-8?q?=E5=88=A0=E9=99=A4=20global::?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dotnetCampus.Logger.Analyzer.csproj | 14 +++-------- .../{EventId.cs => EventId.g.cs} | 2 +- .../{ILogger.cs => ILogger.g.cs} | 2 +- src/dotnetCampus.Logger/{Log.cs => Log.g.cs} | 4 ++-- .../{LogLevel.cs => LogLevel.g.cs} | 0 ...gerExtensions.cs => LoggerExtensions.g.cs} | 2 +- .../Properties/CompatibilityGlobalUsings.cs | 20 ++++++++++++++++ .../Properties/GenerationUsings.cs | 3 --- .../Properties/GlobalUsings.cs | 23 +++---------------- .../{DebugLogger.cs => DebugLogger.g.cs} | 4 ++-- .../{NullLogger.cs => NullLogger.g.cs} | 2 +- .../{TraceLogger.cs => TraceLogger.g.cs} | 4 ++-- 12 files changed, 36 insertions(+), 44 deletions(-) rename src/dotnetCampus.Logger/{EventId.cs => EventId.g.cs} (98%) rename src/dotnetCampus.Logger/{ILogger.cs => ILogger.g.cs} (97%) rename src/dotnetCampus.Logger/{Log.cs => Log.g.cs} (96%) rename src/dotnetCampus.Logger/{LogLevel.cs => LogLevel.g.cs} (100%) rename src/dotnetCampus.Logger/{LoggerExtensions.cs => LoggerExtensions.g.cs} (99%) create mode 100644 src/dotnetCampus.Logger/Properties/CompatibilityGlobalUsings.cs delete mode 100644 src/dotnetCampus.Logger/Properties/GenerationUsings.cs rename src/dotnetCampus.Logger/Writers/{DebugLogger.cs => DebugLogger.g.cs} (96%) rename src/dotnetCampus.Logger/Writers/{NullLogger.cs => NullLogger.g.cs} (92%) rename src/dotnetCampus.Logger/Writers/{TraceLogger.cs => TraceLogger.g.cs} (96%) diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index dd64f8e..26b0c86 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -19,17 +19,9 @@ - <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\Writers\DebugLogger.cs" /> - <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\Writers\NullLogger.cs" /> - <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\Writers\TraceLogger.cs" /> - <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\EventId.cs" /> - <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\ILogger.cs" /> - <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\Log.cs" /> - <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\LoggerExtensions.cs" /> - <_GeneratedLoggerFile Include="..\dotnetCampus.Logger\LogLevel.cs" /> - - - + + + diff --git a/src/dotnetCampus.Logger/EventId.cs b/src/dotnetCampus.Logger/EventId.g.cs similarity index 98% rename from src/dotnetCampus.Logger/EventId.cs rename to src/dotnetCampus.Logger/EventId.g.cs index 29c80a9..2bec934 100644 --- a/src/dotnetCampus.Logger/EventId.cs +++ b/src/dotnetCampus.Logger/EventId.g.cs @@ -1,6 +1,6 @@ #nullable enable -using System.Diagnostics.CodeAnalysis; +using global::System.Diagnostics.CodeAnalysis; namespace dotnetCampus.Logging; diff --git a/src/dotnetCampus.Logger/ILogger.cs b/src/dotnetCampus.Logger/ILogger.g.cs similarity index 97% rename from src/dotnetCampus.Logger/ILogger.cs rename to src/dotnetCampus.Logger/ILogger.g.cs index 3261b2c..738aff6 100644 --- a/src/dotnetCampus.Logger/ILogger.cs +++ b/src/dotnetCampus.Logger/ILogger.g.cs @@ -1,6 +1,6 @@ #nullable enable -using System; +using global::System; namespace dotnetCampus.Logging; diff --git a/src/dotnetCampus.Logger/Log.cs b/src/dotnetCampus.Logger/Log.g.cs similarity index 96% rename from src/dotnetCampus.Logger/Log.cs rename to src/dotnetCampus.Logger/Log.g.cs index 12cc59f..05d42f0 100644 --- a/src/dotnetCampus.Logger/Log.cs +++ b/src/dotnetCampus.Logger/Log.g.cs @@ -1,7 +1,7 @@ #nullable enable -using System; -using System.Diagnostics.CodeAnalysis; +using global::System; +using global::System.Diagnostics.CodeAnalysis; namespace dotnetCampus.Logging; diff --git a/src/dotnetCampus.Logger/LogLevel.cs b/src/dotnetCampus.Logger/LogLevel.g.cs similarity index 100% rename from src/dotnetCampus.Logger/LogLevel.cs rename to src/dotnetCampus.Logger/LogLevel.g.cs diff --git a/src/dotnetCampus.Logger/LoggerExtensions.cs b/src/dotnetCampus.Logger/LoggerExtensions.g.cs similarity index 99% rename from src/dotnetCampus.Logger/LoggerExtensions.cs rename to src/dotnetCampus.Logger/LoggerExtensions.g.cs index 96e0410..5e57964 100644 --- a/src/dotnetCampus.Logger/LoggerExtensions.cs +++ b/src/dotnetCampus.Logger/LoggerExtensions.g.cs @@ -1,6 +1,6 @@ #nullable enable -using System; +using global::System; namespace dotnetCampus.Logging; diff --git a/src/dotnetCampus.Logger/Properties/CompatibilityGlobalUsings.cs b/src/dotnetCampus.Logger/Properties/CompatibilityGlobalUsings.cs new file mode 100644 index 0000000..528e55f --- /dev/null +++ b/src/dotnetCampus.Logger/Properties/CompatibilityGlobalUsings.cs @@ -0,0 +1,20 @@ +global using dotnetCampus.Logging.Properties; + +// .NET 8.0 or later + +#if NET8_0_OR_GREATER +global using ImmutableArrayILogger = System.Collections.Immutable.ImmutableArray; +global using ImmutableHashSetString = System.Collections.Immutable.ImmutableHashSet; +#else +global using ImmutableArrayILogger = System.Collections.Generic.List; +global using ImmutableHashSetString = System.Collections.Generic.HashSet; +#endif + +// .NET 6.0 or later +#if NET6_0_OR_GREATER +global using System.Collections.Immutable; +global using Math = System.Math; + +#else +global using Math = dotnetCampus.Logging.Properties.Compatibility; +#endif diff --git a/src/dotnetCampus.Logger/Properties/GenerationUsings.cs b/src/dotnetCampus.Logger/Properties/GenerationUsings.cs deleted file mode 100644 index 4904880..0000000 --- a/src/dotnetCampus.Logger/Properties/GenerationUsings.cs +++ /dev/null @@ -1,3 +0,0 @@ -global using DebugLogger = dotnetCampus.Logging.Writers.DebugLogger; -global using NullLogger = dotnetCampus.Logging.Writers.NullLogger; -global using TraceLogger = dotnetCampus.Logging.Writers.TraceLogger; diff --git a/src/dotnetCampus.Logger/Properties/GlobalUsings.cs b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs index 528e55f..4904880 100644 --- a/src/dotnetCampus.Logger/Properties/GlobalUsings.cs +++ b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs @@ -1,20 +1,3 @@ -global using dotnetCampus.Logging.Properties; - -// .NET 8.0 or later - -#if NET8_0_OR_GREATER -global using ImmutableArrayILogger = System.Collections.Immutable.ImmutableArray; -global using ImmutableHashSetString = System.Collections.Immutable.ImmutableHashSet; -#else -global using ImmutableArrayILogger = System.Collections.Generic.List; -global using ImmutableHashSetString = System.Collections.Generic.HashSet; -#endif - -// .NET 6.0 or later -#if NET6_0_OR_GREATER -global using System.Collections.Immutable; -global using Math = System.Math; - -#else -global using Math = dotnetCampus.Logging.Properties.Compatibility; -#endif +global using DebugLogger = dotnetCampus.Logging.Writers.DebugLogger; +global using NullLogger = dotnetCampus.Logging.Writers.NullLogger; +global using TraceLogger = dotnetCampus.Logging.Writers.TraceLogger; diff --git a/src/dotnetCampus.Logger/Writers/DebugLogger.cs b/src/dotnetCampus.Logger/Writers/DebugLogger.g.cs similarity index 96% rename from src/dotnetCampus.Logger/Writers/DebugLogger.cs rename to src/dotnetCampus.Logger/Writers/DebugLogger.g.cs index 765e8cc..f62914c 100644 --- a/src/dotnetCampus.Logger/Writers/DebugLogger.cs +++ b/src/dotnetCampus.Logger/Writers/DebugLogger.g.cs @@ -1,7 +1,7 @@ #nullable enable -using System; -using System.Diagnostics; +using global::System; +using global::System.Diagnostics; namespace dotnetCampus.Logging.Writers; diff --git a/src/dotnetCampus.Logger/Writers/NullLogger.cs b/src/dotnetCampus.Logger/Writers/NullLogger.g.cs similarity index 92% rename from src/dotnetCampus.Logger/Writers/NullLogger.cs rename to src/dotnetCampus.Logger/Writers/NullLogger.g.cs index c0e50f0..6626619 100644 --- a/src/dotnetCampus.Logger/Writers/NullLogger.cs +++ b/src/dotnetCampus.Logger/Writers/NullLogger.g.cs @@ -1,6 +1,6 @@ #nullable enable -using System; +using global::System; namespace dotnetCampus.Logging.Writers; diff --git a/src/dotnetCampus.Logger/Writers/TraceLogger.cs b/src/dotnetCampus.Logger/Writers/TraceLogger.g.cs similarity index 96% rename from src/dotnetCampus.Logger/Writers/TraceLogger.cs rename to src/dotnetCampus.Logger/Writers/TraceLogger.g.cs index daa4067..07ddbf2 100644 --- a/src/dotnetCampus.Logger/Writers/TraceLogger.cs +++ b/src/dotnetCampus.Logger/Writers/TraceLogger.g.cs @@ -1,7 +1,7 @@ #nullable enable -using System; -using System.Diagnostics; +using global::System; +using global::System.Diagnostics; namespace dotnetCampus.Logging.Writers; From 09cb780cd5868b0d229a299e4f89f077c00613fe Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 15:10:39 +0800 Subject: [PATCH 26/49] =?UTF-8?q?=E4=B8=BA=E6=89=80=E6=9C=89=E7=94=9F?= =?UTF-8?q?=E6=88=90=E7=9A=84=E6=96=87=E4=BB=B6=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{Class1.cs => DllReferenceTarget.cs} | 2 +- .../Class1.cs | 5 --- .../DllReferenceTarget.cs | 8 ++++ src/dotnetCampus.Logger/EventId.g.cs | 38 +++++++++--------- src/dotnetCampus.Logger/ILogger.g.cs | 24 ++++++----- src/dotnetCampus.Logger/Log.g.cs | 40 ++++++++++++++++--- src/dotnetCampus.Logger/LogLevel.g.cs | 21 ++++------ src/dotnetCampus.Logger/LoggerExtensions.g.cs | 31 +++++++++----- .../Writers/DebugLogger.g.cs | 31 ++++++++++++-- .../Writers/NullLogger.g.cs | 4 ++ .../Writers/TraceLogger.g.cs | 33 ++++++++++++--- 11 files changed, 165 insertions(+), 72 deletions(-) rename samples/LoggerSample.LoggerDependentLibrary/{Class1.cs => DllReferenceTarget.cs} (79%) delete mode 100644 samples/LoggerSample.LoggerIndependentLibrary/Class1.cs create mode 100644 samples/LoggerSample.LoggerIndependentLibrary/DllReferenceTarget.cs diff --git a/samples/LoggerSample.LoggerDependentLibrary/Class1.cs b/samples/LoggerSample.LoggerDependentLibrary/DllReferenceTarget.cs similarity index 79% rename from samples/LoggerSample.LoggerDependentLibrary/Class1.cs rename to samples/LoggerSample.LoggerDependentLibrary/DllReferenceTarget.cs index 46d43c0..a6d13df 100644 --- a/samples/LoggerSample.LoggerDependentLibrary/Class1.cs +++ b/samples/LoggerSample.LoggerDependentLibrary/DllReferenceTarget.cs @@ -2,7 +2,7 @@ namespace LoggerSample.LoggerDependentLibrary; -public class Class1 +public class DllReferenceTarget { public void CollectLogs() { diff --git a/samples/LoggerSample.LoggerIndependentLibrary/Class1.cs b/samples/LoggerSample.LoggerIndependentLibrary/Class1.cs deleted file mode 100644 index 1a9561f..0000000 --- a/samples/LoggerSample.LoggerIndependentLibrary/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace LoggerSample.LoggerIndependentLibrary; - -public class Class1 -{ -} diff --git a/samples/LoggerSample.LoggerIndependentLibrary/DllReferenceTarget.cs b/samples/LoggerSample.LoggerIndependentLibrary/DllReferenceTarget.cs new file mode 100644 index 0000000..07b9826 --- /dev/null +++ b/samples/LoggerSample.LoggerIndependentLibrary/DllReferenceTarget.cs @@ -0,0 +1,8 @@ +namespace LoggerSample.LoggerIndependentLibrary; + +public static class DllReferenceTarget +{ + public static void CollectLogs() + { + } +} diff --git a/src/dotnetCampus.Logger/EventId.g.cs b/src/dotnetCampus.Logger/EventId.g.cs index 2bec934..7393f09 100644 --- a/src/dotnetCampus.Logger/EventId.g.cs +++ b/src/dotnetCampus.Logger/EventId.g.cs @@ -5,46 +5,46 @@ namespace dotnetCampus.Logging; /// -/// Identifies a logging event. The primary identifier is the "Id" property, with the "Name" property providing a short description of this type of event. +/// 标识一个日志事件。主要标识是 "Id" 属性,而 "Name" 属性提供了此类型事件的简短描述。 /// public readonly struct EventId { /// - /// Implicitly creates an EventId from the given . + /// 从给定的 隐式创建一个 EventId。 /// - /// The to convert to an EventId. + /// 要转换为 EventId 的 。 public static implicit operator EventId(int i) { return new EventId(i); } /// - /// Checks if two specified instances have the same value. They are equal if they have the same Id. + /// 检查两个指定的 实例是否具有相同的值。如果它们具有相同的 Id,则它们是相等的。 /// - /// The first . - /// The second . - /// if the objects are equal. + /// 第一个 。 + /// 第二个 。 + /// 如果对象相等,则为 public static bool operator ==(EventId left, EventId right) { return left.Equals(right); } /// - /// Checks if two specified instances have different values. + /// 检查两个指定的 实例是否具有不同的值。 /// - /// The first . - /// The second . - /// if the objects are not equal. + /// 第一个 。 + /// 第二个 。 + /// 如果对象不相等,则为 public static bool operator !=(EventId left, EventId right) { return !left.Equals(right); } /// - /// Initializes an instance of the struct. + /// 初始化 结构的新实例。 /// - /// The numeric identifier for this event. - /// The name of this event. + /// 此事件的数字标识符。 + /// 此事件的名称。 public EventId(int id, string? name = null) { Id = id; @@ -52,12 +52,12 @@ public EventId(int id, string? name = null) } /// - /// Gets the numeric identifier for this event. + /// 获取此事件的数字标识符。 /// public int Id { get; } /// - /// Gets the name of this event. + /// 获取此事件的名称。 /// public string? Name { get; } @@ -68,10 +68,10 @@ public override string ToString() } /// - /// Indicates whether the current object is equal to another object of the same type. Two events are equal if they have the same id. + /// 指示当前对象是否等于另一个相同类型的对象。如果两个事件具有相同的 id,则它们是相等的。 /// - /// An object to compare with this object. - /// if the current object is equal to the other parameter; otherwise, . + /// 要与此对象进行比较的对象。 + /// 如果两个对象相等,则为 ;否则为 public bool Equals(EventId other) { return Id == other.Id; diff --git a/src/dotnetCampus.Logger/ILogger.g.cs b/src/dotnetCampus.Logger/ILogger.g.cs index 738aff6..8ff001d 100644 --- a/src/dotnetCampus.Logger/ILogger.g.cs +++ b/src/dotnetCampus.Logger/ILogger.g.cs @@ -4,17 +4,23 @@ namespace dotnetCampus.Logging; -/// Represents a type used to perform logging. -/// Aggregates most logging patterns to a single method. +/// +/// 表示用于执行日志记录的类型。 +/// +/// +/// 将大多数日志模式聚合到一个方法中。 +/// public interface ILogger { - /// Writes a log entry. - /// Entry will be written on this level. - /// Id of the event. - /// The entry to be written. Can be also an object. - /// The exception related to this entry. - /// Function to create a message of the and . - /// The type of the object to be written. + /// + /// 写入日志条目。 + /// + /// 将在此级别上写入条目。 + /// 事件的 Id。 + /// 要写入的条目。也可以是一个对象。 + /// 与此条目相关的异常。 + /// 创建一条字符串消息以记录 。 + /// 要写入的对象的类型。 void Log( LogLevel logLevel, EventId eventId, diff --git a/src/dotnetCampus.Logger/Log.g.cs b/src/dotnetCampus.Logger/Log.g.cs index 05d42f0..ee3604e 100644 --- a/src/dotnetCampus.Logger/Log.g.cs +++ b/src/dotnetCampus.Logger/Log.g.cs @@ -5,6 +5,9 @@ namespace dotnetCampus.Logging; +/// +/// 提供静态的日志记录方法。 +/// public static class Log { private static ILogger _current; @@ -14,7 +17,9 @@ static Log() Current = new NullLogger(); } - + /// + /// 获取此静态日志记录器当前所用的日志记录器实例。 + /// public static ILogger Current { get => _current; @@ -39,12 +44,25 @@ private set } } + /// + /// 获取仅在 debug 配置下才会记录的日志记录器。 + /// + /// + /// 请注意,所有通过此记录器记录的日志仅在以 debug 配置编译时才会被记录,并且在非 debug 编译后对此记录器的调用都会被编译器优化掉。 + /// public static DebugLogger Debug { get; private set; } + /// + /// 获取仅在 TRACE 条件编译符被定义时才会记录的日志记录器。 + /// 通常情况下,debug 和 release 配置下都会定义 TRACE 条件编译符。 + /// + /// + /// 请注意,所有通过此记录器记录的日志仅在定义了 TRACE 条件编译符时才会被记录,并且在未定义 TRACE 条件编译符后对此记录器的调用都会被编译器优化掉。 + /// public static TraceLogger Trace { get; private set; } /// - /// 正常记录日志。 + /// 记录信息日志。 /// /// 要记录的消息。 public static void Info(string message) @@ -55,8 +73,8 @@ public static void Info(string message) /// /// 记录警告日志。 /// - /// 要记录的消息,形如 [tag] message - /// 相关的异常信息。 + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 public static void Warn(string message, Exception? exception = null) { Current.Log(LogLevel.Warning, default, message, exception, (s, ex) => message); @@ -65,10 +83,20 @@ public static void Warn(string message, Exception? exception = null) /// /// 记录错误日志。 /// - /// 要记录的消息,形如 [tag] message - /// 相关的异常信息。 + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 public static void Error(string message, Exception? exception = null) { Current.Log(LogLevel.Error, default, message, exception, (s, ex) => message); } + + /// + /// 记录崩溃日志。 + /// + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 + public static void Fatal(string message, Exception? exception = null) + { + Current.Log(LogLevel.Critical, default, message, null, (s, ex) => message); + } } diff --git a/src/dotnetCampus.Logger/LogLevel.g.cs b/src/dotnetCampus.Logger/LogLevel.g.cs index 6e1fa45..9a46a7a 100644 --- a/src/dotnetCampus.Logger/LogLevel.g.cs +++ b/src/dotnetCampus.Logger/LogLevel.g.cs @@ -3,47 +3,42 @@ namespace dotnetCampus.Logging; /// -/// Defines logging severity levels. +/// 定义日志的严重程度。 /// public enum LogLevel { /// - /// Logs that contain the most detailed messages. These messages may contain sensitive application data. - /// These messages are disabled by default and should never be enabled in a production environment. + /// 包含最详细消息的日志。这些消息可能包含敏感的应用程序数据。默认情况下禁用这些消息,不应在生产环境中启用。 /// Trace = 0, /// - /// Logs that are used for interactive investigation during development. These logs should primarily contain - /// information useful for debugging and have no long-term value. + /// 用于开发过程中的交互式调查的日志。这些日志应主要包含有用于调试的信息,没有长期价值。 /// Debug = 1, /// - /// Logs that track the general flow of the application. These logs should have long-term value. + /// 跟踪应用程序的一般流程的日志。这些日志应具有长期价值。 /// Information = 2, /// - /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the - /// application execution to stop. + /// 强调应用程序流程中的异常或意外事件的日志,但不会导致应用程序执行停止。 /// Warning = 3, /// - /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a - /// failure in the current activity, not an application-wide failure. + /// 强调当前执行流程由于失败而停止的日志。这些日志应指示当前活动的失败,而不是应用程序范围的失败。 /// Error = 4, /// - /// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires - /// immediate attention. + /// 描述不可恢复的应用程序或系统崩溃,或需要立即处理的灾难性失败的日志。 /// Critical = 5, /// - /// Not used for writing log messages. Specifies that a logging category should not write any messages. + /// 此严重程度不用于写入日志消息,配置成此级别仅表示不会写入任何日志。 /// None = 6, } diff --git a/src/dotnetCampus.Logger/LoggerExtensions.g.cs b/src/dotnetCampus.Logger/LoggerExtensions.g.cs index 5e57964..57fb42a 100644 --- a/src/dotnetCampus.Logger/LoggerExtensions.g.cs +++ b/src/dotnetCampus.Logger/LoggerExtensions.g.cs @@ -4,30 +4,41 @@ namespace dotnetCampus.Logging; +/// +/// 的常见场景的扩展方法。 +/// public static class LoggerExtensions { /// - /// 在开启了追踪的情况下输出日志。(默认开启) + /// 记录追踪级别的日志。 /// /// 记录日志所使用的记录器。 - /// 要记录的消息,形如 [tag] message + /// 要记录的消息。 + /// + /// 请注意,这里的 仅代表追踪级别;如果配置输出 trace 级别的日志,即便编译时未定义 TRACE 条件编译符也会输出。
+ /// 如果希望仅在定义了 TRACE 条件编译符时输出日志,请使用 。 + ///
public static void Trace(this ILogger logger, string message) { logger.Log(LogLevel.Trace, default, message, null, (s, ex) => message); } /// - /// 记录 debug 级别的日志。 + /// 记录调试级别的日志。 /// /// 记录日志所使用的记录器。 /// 要记录的消息。 + /// + /// 请注意,这里的 仅代表调试级别;如果配置了输出 debug 级别的日志,即便是 release 编译也会输出。
+ /// 如果希望仅在 debug 配置下输出日志,请使用 。 + ///
public static void Debug(this ILogger logger, string message) { logger.Log(LogLevel.Debug, default, message, null, (s, ex) => message); } /// - /// 正常记录日志。 + /// 记录信息日志。 /// /// 记录日志所使用的记录器。 /// 要记录的消息。 @@ -40,8 +51,8 @@ public static void Info(this ILogger logger, string message) /// 记录警告日志。 ///
/// 记录日志所使用的记录器。 - /// 要记录的消息,形如 [tag] message - /// 相关的异常信息。 + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 public static void Warn(this ILogger logger, string message, Exception? exception = null) { logger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); @@ -51,8 +62,8 @@ public static void Warn(this ILogger logger, string message, Exception? exceptio /// 记录错误日志。 ///
/// 记录日志所使用的记录器。 - /// 要记录的消息,形如 [tag] message - /// 相关的异常信息。 + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 public static void Error(this ILogger logger, string message, Exception? exception = null) { logger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); @@ -62,8 +73,8 @@ public static void Error(this ILogger logger, string message, Exception? excepti /// 记录崩溃日志。 ///
/// 记录日志所使用的记录器。 - /// 要记录的消息,形如 [tag] message - /// 相关的异常信息。 + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 public static void Fatal(this ILogger logger, string message, Exception? exception = null) { logger.Log(LogLevel.Critical, default, message, null, (s, ex) => message); diff --git a/src/dotnetCampus.Logger/Writers/DebugLogger.g.cs b/src/dotnetCampus.Logger/Writers/DebugLogger.g.cs index f62914c..842ebb0 100644 --- a/src/dotnetCampus.Logger/Writers/DebugLogger.g.cs +++ b/src/dotnetCampus.Logger/Writers/DebugLogger.g.cs @@ -10,15 +10,16 @@ namespace dotnetCampus.Logging.Writers; ///
public class DebugLogger(ILogger realLogger) : ILogger { + /// void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { realLogger.Log(logLevel, eventId, state, exception, formatter); } /// - /// 在开启了追踪的情况下输出日志。(默认开启) + /// 记录追踪级别的日志。 /// - /// 要记录的消息,形如 [tag] message + /// 要记录的消息。 [Conditional("DEBUG")] public void Trace(string message) { @@ -26,7 +27,7 @@ public void Trace(string message) } /// - /// 记录 debug 级别的日志。 + /// 记录调试级别的日志。 /// /// 要记录的消息。 [Conditional("DEBUG")] @@ -36,7 +37,7 @@ public void Debug(string message) } /// - /// 正常记录日志。 + /// 记录信息日志。 /// /// 要记录的消息。 [Conditional("DEBUG")] @@ -54,4 +55,26 @@ public void Warn(string message) { realLogger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); } + + /// + /// 记录错误日志。 + /// + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 + [Conditional("DEBUG")] + public void Error(string message, Exception? exception = null) + { + realLogger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); + } + + /// + /// 记录崩溃日志。 + /// + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 + [Conditional("DEBUG")] + public void Fatal(string message, Exception? exception = null) + { + realLogger.Log(LogLevel.Critical, default, message, null, (s, ex) => message); + } } diff --git a/src/dotnetCampus.Logger/Writers/NullLogger.g.cs b/src/dotnetCampus.Logger/Writers/NullLogger.g.cs index 6626619..c387c1c 100644 --- a/src/dotnetCampus.Logger/Writers/NullLogger.g.cs +++ b/src/dotnetCampus.Logger/Writers/NullLogger.g.cs @@ -4,8 +4,12 @@ namespace dotnetCampus.Logging.Writers; +/// +/// 不记录任何日志的日志记录器。 +/// internal class NullLogger : ILogger { + /// void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { } diff --git a/src/dotnetCampus.Logger/Writers/TraceLogger.g.cs b/src/dotnetCampus.Logger/Writers/TraceLogger.g.cs index 07ddbf2..443291a 100644 --- a/src/dotnetCampus.Logger/Writers/TraceLogger.g.cs +++ b/src/dotnetCampus.Logger/Writers/TraceLogger.g.cs @@ -6,19 +6,20 @@ namespace dotnetCampus.Logging.Writers; /// -/// 提供仅在 debug 下才会记录的日志。 +/// 提供仅在开启了 TRACE 条件编译符下才会记录的日志。 /// public class TraceLogger(ILogger realLogger) : ILogger { + /// void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { realLogger.Log(logLevel, eventId, state, exception, formatter); } /// - /// 在开启了追踪的情况下输出日志。(默认开启) + /// 记录追踪级别的日志。 /// - /// 要记录的消息,形如 [tag] message + /// 要记录的消息。 [Conditional("TRACE")] public void Trace(string message) { @@ -26,7 +27,7 @@ public void Trace(string message) } /// - /// 记录 debug 级别的日志。 + /// 记录调试级别的日志。 /// /// 要记录的消息。 [Conditional("TRACE")] @@ -36,7 +37,7 @@ public void Debug(string message) } /// - /// 正常记录日志。 + /// 记录信息日志。 /// /// 要记录的消息。 [Conditional("TRACE")] @@ -54,4 +55,26 @@ public void Warn(string message) { realLogger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); } + + /// + /// 记录错误日志。 + /// + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 + [Conditional("TRACE")] + public void Error(string message, Exception? exception = null) + { + realLogger.Log(LogLevel.Warning, default, message, null, (s, ex) => message); + } + + /// + /// 记录崩溃日志。 + /// + /// 要记录的消息。 + /// 如果有异常信息,可以传入此参数。 + [Conditional("TRACE")] + public void Fatal(string message, Exception? exception = null) + { + realLogger.Log(LogLevel.Critical, default, message, null, (s, ex) => message); + } } From 2fac6aea90af1cc70c3ae220c3d7e9a6ba917aad Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 15:27:05 +0800 Subject: [PATCH 27/49] =?UTF-8?q?=E5=9C=A8=E5=BA=93=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E4=B8=AD=E4=BD=BF=E7=94=A8=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DllReferenceTarget.cs | 27 ++++++++++++++- .../DllReferenceTarget.cs | 8 ----- .../SourceReferenceTarget.cs | 33 +++++++++++++++++++ src/dotnetCampus.Logger/LoggerExtensions.g.cs | 4 +-- 4 files changed, 61 insertions(+), 11 deletions(-) delete mode 100644 samples/LoggerSample.LoggerIndependentLibrary/DllReferenceTarget.cs create mode 100644 samples/LoggerSample.LoggerIndependentLibrary/SourceReferenceTarget.cs diff --git a/samples/LoggerSample.LoggerDependentLibrary/DllReferenceTarget.cs b/samples/LoggerSample.LoggerDependentLibrary/DllReferenceTarget.cs index a6d13df..e3d7f24 100644 --- a/samples/LoggerSample.LoggerDependentLibrary/DllReferenceTarget.cs +++ b/samples/LoggerSample.LoggerDependentLibrary/DllReferenceTarget.cs @@ -4,7 +4,32 @@ namespace LoggerSample.LoggerDependentLibrary; public class DllReferenceTarget { - public void CollectLogs() + public static void CollectLogs() { + Log.Trace.Trace("[DllReference] Log.Trace.Trace"); + Log.Trace.Debug("[DllReference] Log.Trace.Debug"); + Log.Trace.Info("[DllReference] Log.Trace.Info"); + Log.Trace.Warn("[DllReference] Log.Trace.Warn"); + Log.Trace.Error("[DllReference] Log.Trace.Error"); + Log.Trace.Fatal("[DllReference] Log.Trace.Fatal"); + + Log.Debug.Trace("[DllReference] Log.Debug.Trace"); + Log.Debug.Debug("[DllReference] Log.Debug.Debug"); + Log.Debug.Info("[DllReference] Log.Debug.Info"); + Log.Debug.Warn("[DllReference] Log.Debug.Warn"); + Log.Debug.Error("[DllReference] Log.Debug.Error"); + Log.Debug.Fatal("[DllReference] Log.Debug.Fatal"); + + Log.Info("[DllReference] Log.Info"); + Log.Warn("[DllReference] Log.Warn"); + Log.Error("[DllReference] Log.Error"); + Log.Fatal("[DllReference] Log.Fatal"); + + Log.Current.Trace("[DllReference] Log.Current.Trace"); + Log.Current.Debug("[DllReference] Log.Current.Debug"); + Log.Current.Info("[DllReference] Log.Current.Info"); + Log.Current.Warn("[DllReference] Log.Current.Warn"); + Log.Current.Error("[DllReference] Log.Current.Error"); + Log.Current.Fatal("[DllReference] Log.Current.Fatal"); } } diff --git a/samples/LoggerSample.LoggerIndependentLibrary/DllReferenceTarget.cs b/samples/LoggerSample.LoggerIndependentLibrary/DllReferenceTarget.cs deleted file mode 100644 index 07b9826..0000000 --- a/samples/LoggerSample.LoggerIndependentLibrary/DllReferenceTarget.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace LoggerSample.LoggerIndependentLibrary; - -public static class DllReferenceTarget -{ - public static void CollectLogs() - { - } -} diff --git a/samples/LoggerSample.LoggerIndependentLibrary/SourceReferenceTarget.cs b/samples/LoggerSample.LoggerIndependentLibrary/SourceReferenceTarget.cs new file mode 100644 index 0000000..7e8e077 --- /dev/null +++ b/samples/LoggerSample.LoggerIndependentLibrary/SourceReferenceTarget.cs @@ -0,0 +1,33 @@ +namespace LoggerSample.LoggerIndependentLibrary; + +public static class SourceReferenceTarget +{ + public static void CollectLogs() + { + Log.Trace.Trace("[SourceReference] Log.Trace.Trace"); + Log.Trace.Debug("[SourceReference] Log.Trace.Debug"); + Log.Trace.Info("[SourceReference] Log.Trace.Info"); + Log.Trace.Warn("[SourceReference] Log.Trace.Warn"); + Log.Trace.Error("[SourceReference] Log.Trace.Error"); + Log.Trace.Fatal("[SourceReference] Log.Trace.Fatal"); + + Log.Debug.Trace("[SourceReference] Log.Debug.Trace"); + Log.Debug.Debug("[SourceReference] Log.Debug.Debug"); + Log.Debug.Info("[SourceReference] Log.Debug.Info"); + Log.Debug.Warn("[SourceReference] Log.Debug.Warn"); + Log.Debug.Error("[SourceReference] Log.Debug.Error"); + Log.Debug.Fatal("[SourceReference] Log.Debug.Fatal"); + + Log.Info("[SourceReference] Log.Info"); + Log.Warn("[SourceReference] Log.Warn"); + Log.Error("[SourceReference] Log.Error"); + Log.Fatal("[SourceReference] Log.Fatal"); + + Log.Current.Trace("[SourceReference] Log.Current.Trace"); + Log.Current.Debug("[SourceReference] Log.Current.Debug"); + Log.Current.Info("[SourceReference] Log.Current.Info"); + Log.Current.Warn("[SourceReference] Log.Current.Warn"); + Log.Current.Error("[SourceReference] Log.Current.Error"); + Log.Current.Fatal("[SourceReference] Log.Current.Fatal"); + } +} diff --git a/src/dotnetCampus.Logger/LoggerExtensions.g.cs b/src/dotnetCampus.Logger/LoggerExtensions.g.cs index 57fb42a..3d77c48 100644 --- a/src/dotnetCampus.Logger/LoggerExtensions.g.cs +++ b/src/dotnetCampus.Logger/LoggerExtensions.g.cs @@ -16,7 +16,7 @@ public static class LoggerExtensions /// 要记录的消息。 /// /// 请注意,这里的 仅代表追踪级别;如果配置输出 trace 级别的日志,即便编译时未定义 TRACE 条件编译符也会输出。
- /// 如果希望仅在定义了 TRACE 条件编译符时输出日志,请使用 。 + /// 如果希望仅在定义了 TRACE 条件编译符时输出日志,请使用 .。 ///
public static void Trace(this ILogger logger, string message) { @@ -30,7 +30,7 @@ public static void Trace(this ILogger logger, string message) /// 要记录的消息。 /// /// 请注意,这里的 仅代表调试级别;如果配置了输出 debug 级别的日志,即便是 release 编译也会输出。
- /// 如果希望仅在 debug 配置下输出日志,请使用 。 + /// 如果希望仅在 debug 配置下输出日志,请使用 .。 ///
public static void Debug(this ILogger logger, string message) { From 5dbf04ddb6d366b2f48ab1a3709adb48794a048d Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 16:39:02 +0800 Subject: [PATCH 28/49] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=A1=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotnetCampus.Logger.sln | 2 +- .../LoggerSample.MainApp.Avalonia/App.axaml | 10 --- .../App.axaml.cs | 23 ------ .../LoggerSample.MainApp.Avalonia.csproj | 27 ------- .../MainWindow.axaml | 9 --- .../MainWindow.axaml.cs | 11 --- .../LoggerSample.MainApp.Avalonia/Program.cs | 25 ------- .../Properties/App.manifest | 18 ----- .../Startup/LogStartup.cs | 15 ---- .../LoggerSample.MainApp.csproj | 15 ++++ samples/LoggerSample.MainApp/Program.cs | 13 ++++ .../Templates/AggregateLoggerBridge.g.cs | 74 +++++++++++++++++++ .../Generators/LoggerGenerator.cs | 15 +++- .../Attributes/ImportLoggerBridgeAttribute.cs | 27 +++++++ .../Bridges/BridgeLogger.g.cs | 23 ++++++ .../Bridges/ILoggerBridge.g.cs | 56 ++++++++++++++ src/dotnetCampus.Logger/Log.g.cs | 2 + .../SourceGenerators/BridgeLogger.cs | 15 ---- .../SourceGenerators/ILoggerBridge.cs | 25 ------- .../SourceGenerators/LogFactory.cs | 16 ---- .../SourceGenerators/README.md | 5 -- 21 files changed, 222 insertions(+), 204 deletions(-) delete mode 100644 samples/LoggerSample.MainApp.Avalonia/App.axaml delete mode 100644 samples/LoggerSample.MainApp.Avalonia/App.axaml.cs delete mode 100644 samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj delete mode 100644 samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml delete mode 100644 samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs delete mode 100644 samples/LoggerSample.MainApp.Avalonia/Program.cs delete mode 100644 samples/LoggerSample.MainApp.Avalonia/Properties/App.manifest delete mode 100644 samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs create mode 100644 samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj create mode 100644 samples/LoggerSample.MainApp/Program.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs create mode 100644 src/dotnetCampus.Logger/Attributes/ImportLoggerBridgeAttribute.cs create mode 100644 src/dotnetCampus.Logger/Bridges/BridgeLogger.g.cs create mode 100644 src/dotnetCampus.Logger/Bridges/ILoggerBridge.g.cs delete mode 100644 src/dotnetCampus.Logger/SourceGenerators/BridgeLogger.cs delete mode 100644 src/dotnetCampus.Logger/SourceGenerators/ILoggerBridge.cs delete mode 100644 src/dotnetCampus.Logger/SourceGenerators/LogFactory.cs delete mode 100644 src/dotnetCampus.Logger/SourceGenerators/README.md diff --git a/dotnetCampus.Logger.sln b/dotnetCampus.Logger.sln index 324c1ed..8a6fa8c 100644 --- a/dotnetCampus.Logger.sln +++ b/dotnetCampus.Logger.sln @@ -17,7 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{AFB0DF31 .gitignore = .gitignore EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggerSample.MainApp.Avalonia", "samples\LoggerSample.MainApp.Avalonia\LoggerSample.MainApp.Avalonia.csproj", "{C282F00B-0C42-491F-AC0D-967407E1C418}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggerSample.MainApp", "samples\LoggerSample.MainApp\LoggerSample.MainApp.csproj", "{C282F00B-0C42-491F-AC0D-967407E1C418}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{34E3F27E-C7E2-45D7-8035-53C304F77E2A}" EndProject diff --git a/samples/LoggerSample.MainApp.Avalonia/App.axaml b/samples/LoggerSample.MainApp.Avalonia/App.axaml deleted file mode 100644 index c40f1e0..0000000 --- a/samples/LoggerSample.MainApp.Avalonia/App.axaml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs b/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs deleted file mode 100644 index bc1d194..0000000 --- a/samples/LoggerSample.MainApp.Avalonia/App.axaml.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; - -namespace LoggerSample.MainApp.Avalonia; - -public partial class App : Application -{ - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new MainWindow(); - } - - base.OnFrameworkInitializationCompleted(); - } -} diff --git a/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj b/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj deleted file mode 100644 index cb32cbe..0000000 --- a/samples/LoggerSample.MainApp.Avalonia/LoggerSample.MainApp.Avalonia.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - WinExe - net8.0 - true - Properties\App.manifest - true - - - - - - - - - - - - - - - - - - - diff --git a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml deleted file mode 100644 index c274d16..0000000 --- a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml +++ /dev/null @@ -1,9 +0,0 @@ - - Welcome to Avalonia! - \ No newline at end of file diff --git a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs b/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs deleted file mode 100644 index c3341c7..0000000 --- a/samples/LoggerSample.MainApp.Avalonia/MainWindow.axaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Controls; - -namespace LoggerSample.MainApp.Avalonia; - -public partial class MainWindow : Window -{ - public MainWindow() - { - InitializeComponent(); - } -} diff --git a/samples/LoggerSample.MainApp.Avalonia/Program.cs b/samples/LoggerSample.MainApp.Avalonia/Program.cs deleted file mode 100644 index 0ca71d3..0000000 --- a/samples/LoggerSample.MainApp.Avalonia/Program.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using Avalonia; -using LoggerSample.MainApp.Avalonia.Startup; - -namespace LoggerSample.MainApp.Avalonia; - -internal partial class Program -{ - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - [STAThread] - public static void Main(string[] args) - { - BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); - } - - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .UsePlatformDetect() - .WithInterFont() - .UseLogger(b => { }); -} diff --git a/samples/LoggerSample.MainApp.Avalonia/Properties/App.manifest b/samples/LoggerSample.MainApp.Avalonia/Properties/App.manifest deleted file mode 100644 index 5044dfa..0000000 --- a/samples/LoggerSample.MainApp.Avalonia/Properties/App.manifest +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - diff --git a/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs b/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs deleted file mode 100644 index 3ae0796..0000000 --- a/samples/LoggerSample.MainApp.Avalonia/Startup/LogStartup.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using Avalonia; -using dotnetCampus.Logging; - -namespace LoggerSample.MainApp.Avalonia.Startup; - -public static class LogStartup -{ - public static AppBuilder UseLogger(this AppBuilder appBuilder, Action builder) - { - var b = new LoggerBuilder(); - builder(b); - return appBuilder.LogToTrace(); - } -} diff --git a/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj b/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj new file mode 100644 index 0000000..ba96328 --- /dev/null +++ b/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj @@ -0,0 +1,15 @@ + + + + WinExe + net8.0 + + + + + + + + + + diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs new file mode 100644 index 0000000..dd5225b --- /dev/null +++ b/samples/LoggerSample.MainApp/Program.cs @@ -0,0 +1,13 @@ +using dotnetCampus.Logging.Attributes; + +namespace LoggerSample.MainApp; + +internal partial class Program +{ + public static void Main(string[] args) + { + } +} + +[ImportLoggerBridge] +internal partial class LoggerBridge; diff --git a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs new file mode 100644 index 0000000..f024c2a --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs @@ -0,0 +1,74 @@ +#nullable enable + +using GEventId = global::dotnetCampus.Logging.EventId; +using GException = global::System.Exception; +using GILogger = global::dotnetCampus.Logging.ILogger; +using GLog = global::dotnetCampus.Logging.Log; +using GLogLevel = global::dotnetCampus.Logging.LogLevel; + +namespace LoggerSample.MainApp; + +/// +/// 聚合各个来源的日志桥。调用其 方法可以将 +/// +partial class AggregateLoggerBridge +{ + private GILogger? _logger; + + /// + /// 将所有已指派给此聚合日志桥的所有日志桥对接到 日志记录器上。 + /// + /// 要对接的日志记录器。如果不指定,则默认使用全局的 .。 + /// + /// 如果已经对接过日志记录器,则会抛出此异常。 + /// 如果希望针对不同的库对接不同的日志记录器,请编写多个聚合日志桥并分别导入各自的日志桥。 + /// + public void Link(GILogger? logger = null) + { + if (_logger != null) + { + throw new global::System.InvalidOperationException("The logger has been linked. If you want to link different logger instance, please create a new AggregateLoggerBridge instance."); + } + + _logger = logger; + + // + // + // 源生成器请在此处添加代码... + // + // 对于 .NET 运行时: + // global::Xxx.Logging.ILoggerBridge.Link(this); + // + // 对于 .NET Framework 运行时: + // global::Xxx.Logging.LoggerBridgeLinker.Link(this); + // + // + } + + /// + /// 写入日志条目。 + /// + /// 将在此级别上写入条目。 + /// 事件的 Id。 + /// 事件的名称。 + /// 要写入的条目。也可以是一个对象。 + /// 与此条目相关的异常。 + /// 创建一条字符串消息以记录 。 + /// 要写入的对象的类型。 + public void Log( + int logLevel, + int eventId, + string? eventName, + TState state, + GException? exception, + global::System.Func formatter) + { + var logger = _logger ?? GLog.Current; + logger.Log( + (GLogLevel)logLevel, + new GEventId(eventId, eventName), + state, + exception, + formatter); + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs index 04440ae..a859e9d 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs @@ -38,13 +38,19 @@ private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvi foreach (var file in sourceFiles) { - var code = GenerateSource(rootNamespace, file.Content); + var code = GenerateSource(file.FileName, rootNamespace, file.Content); context.AddSource(file.FileName, SourceText.From(code, Encoding.UTF8)); } } - private string GenerateSource(string rootNamespace, string sourceText) + private string GenerateSource(string fileName, string rootNamespace, string sourceText) { + if (fileName == "Log.g.cs") + { + // 源生成器为单独库生成的代码中,默认日志记录器是 BridgeLogger。 + sourceText = sourceText.Replace("new NullLogger();", "new BridgeLogger();"); + } + var sourceSpan = sourceText.AsSpan(); var namespaceKeywordIndex = sourceText.IndexOf("namespace", StringComparison.Ordinal); @@ -54,9 +60,10 @@ private string GenerateSource(string rootNamespace, string sourceText) var classKeywordIndex = GetTypeRegex().Match(sourceText).Index; var publicKeywordIndex = sourceText.IndexOf("public", namespaceEndIndex, classKeywordIndex - namespaceEndIndex, StringComparison.Ordinal); - if (publicKeywordIndex < 0) + if (publicKeywordIndex < 0 || fileName.Contains("Bridge")) { - // 此类型不是 public 的,无需修改为 internal;仅修改命名空间即可。 + // 此类型不是 public 的,无需修改为 internal。 + // 此类型是 BridgeLogger,应该保持 public。 return string.Concat( sourceSpan.Slice(0, namespaceStartIndex).ToString(), $"{rootNamespace}.Logging", diff --git a/src/dotnetCampus.Logger/Attributes/ImportLoggerBridgeAttribute.cs b/src/dotnetCampus.Logger/Attributes/ImportLoggerBridgeAttribute.cs new file mode 100644 index 0000000..9c562f9 --- /dev/null +++ b/src/dotnetCampus.Logger/Attributes/ImportLoggerBridgeAttribute.cs @@ -0,0 +1,27 @@ +using System; + +namespace dotnetCampus.Logging.Attributes; + +/// +/// 指示源生成器应该生成对接指定的日志桥接器的代码。 +/// +/// +/// 桥接器的类型。通常名为 global::Xxx.Logging.ILoggerBridge,其中 Xxx 为被桥接的库的根命名空间。 +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] +public class ImportLoggerBridgeAttribute(Type loggerBridgeInterfaceType) : Attribute +{ + /// + /// 获取桥接器的类型。 + /// + public Type LoggerBridgeInterfaceType { get; } = loggerBridgeInterfaceType; +} + +/// +/// 指示源生成器应该生成对接指定的日志桥接器的代码。 +/// +/// +/// 桥接器的类型。通常名为 global::Xxx.Logging.BridgeLogger,其中 Xxx 为被桥接的库的根命名空间。 +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] +public class ImportLoggerBridgeAttribute() : ImportLoggerBridgeAttribute(typeof(T)); diff --git a/src/dotnetCampus.Logger/Bridges/BridgeLogger.g.cs b/src/dotnetCampus.Logger/Bridges/BridgeLogger.g.cs new file mode 100644 index 0000000..4bd78c0 --- /dev/null +++ b/src/dotnetCampus.Logger/Bridges/BridgeLogger.g.cs @@ -0,0 +1,23 @@ +#nullable enable + +using System; +using System.ComponentModel; + +namespace dotnetCampus.Logging.Bridges; + +/// +/// dotnetCampus.Logging.Bridges 的桥接日志记录器。 +/// +internal class BridgeLogger : ILogger +{ + /// + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { +#if NETCOREAPP3_0_OR_GREATER + ILoggerBridge +#else + LoggerBridgeLinker +#endif + .Bridge?.Log((int)logLevel, eventId.Id, eventId.Name, state, exception, formatter); + } +} diff --git a/src/dotnetCampus.Logger/Bridges/ILoggerBridge.g.cs b/src/dotnetCampus.Logger/Bridges/ILoggerBridge.g.cs new file mode 100644 index 0000000..b783bb9 --- /dev/null +++ b/src/dotnetCampus.Logger/Bridges/ILoggerBridge.g.cs @@ -0,0 +1,56 @@ +#nullable enable + +using System; +using System.ComponentModel; + +namespace dotnetCampus.Logging.Bridges; + +/// +/// 仅由 .NET 库类型构成的日志桥。用于源生成器将无依赖库中的日志重定向到应用程序聚合日志系统中。 +/// +public interface ILoggerBridge +{ + /// + /// 写入日志条目。 + /// + /// 将在此级别上写入条目。 + /// 事件的 Id。 + /// 事件的名称。 + /// 要写入的条目。也可以是一个对象。 + /// 与此条目相关的异常。 + /// 创建一条字符串消息以记录 。 + /// 要写入的对象的类型。 + void Log( + int logLevel, + int eventId, + string? eventName, + TState state, + Exception? exception, + Func formatter); + +#if !NETCOREAPP3_0_OR_GREATER +} + +/// +/// 提供一个静态类,用于连接日志桥到当前的桥接日志记录器中。 +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class LoggerBridgeLinker +{ +#endif + + /// + /// 获取当前的日志桥。 + /// + internal static ILoggerBridge? Bridge; + + /// + /// 连接一个日志桥到当前的桥接日志记录器中。 + /// 这样,所有使用此桥接日志记录器记录的日志会全部被重定向到日志桥中。 + /// + /// 要连接此日志记录器的日志桥。 + public static void Link(ILoggerBridge bridge) + { + Bridge = bridge; + } +} diff --git a/src/dotnetCampus.Logger/Log.g.cs b/src/dotnetCampus.Logger/Log.g.cs index ee3604e..5513cb2 100644 --- a/src/dotnetCampus.Logger/Log.g.cs +++ b/src/dotnetCampus.Logger/Log.g.cs @@ -14,6 +14,8 @@ public static class Log static Log() { + // 在全局日志中,默认日志记录器是 NullLogger。 + // 然而在源生成器为单独库生成的代码中,默认日志记录器是 BridgeLogger。 Current = new NullLogger(); } diff --git a/src/dotnetCampus.Logger/SourceGenerators/BridgeLogger.cs b/src/dotnetCampus.Logger/SourceGenerators/BridgeLogger.cs deleted file mode 100644 index 2ff4f34..0000000 --- a/src/dotnetCampus.Logger/SourceGenerators/BridgeLogger.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace dotnetCampus.Logging.SourceGenerators; - -internal class BridgeLogger : ILogger -{ - private ILoggerBridge? _bridge; - - private ILoggerBridge? Bridge => _bridge ??= LogFactory.TryGetBridge(); - - void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) - { - _bridge?.Log((int)logLevel, eventId.Id, eventId.Name, state, exception, formatter); - } -} diff --git a/src/dotnetCampus.Logger/SourceGenerators/ILoggerBridge.cs b/src/dotnetCampus.Logger/SourceGenerators/ILoggerBridge.cs deleted file mode 100644 index 835c4f9..0000000 --- a/src/dotnetCampus.Logger/SourceGenerators/ILoggerBridge.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace dotnetCampus.Logging.SourceGenerators; - -/// -/// 仅由基本类型构成的日志源。用于源生成器将无依赖库中的日志重定向到应用程序聚合日志系统中。 -/// -public interface ILoggerBridge -{ - /// Writes a log entry. - /// Entry will be written on this level. - /// Id of the event. - /// Name of the event. - /// The entry to be written. Can be also an object. - /// The exception related to this entry. - /// Function to create a message of the and . - /// The type of the object to be written. - void Log( - int logLevel, - int? eventId, - string? eventName, - TState state, - Exception? exception, - Func formatter); -} diff --git a/src/dotnetCampus.Logger/SourceGenerators/LogFactory.cs b/src/dotnetCampus.Logger/SourceGenerators/LogFactory.cs deleted file mode 100644 index 16779bb..0000000 --- a/src/dotnetCampus.Logger/SourceGenerators/LogFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace dotnetCampus.Logging.SourceGenerators; - -public static class LogFactory -{ - private static ILoggerBridge? _bridge; - - internal static ILoggerBridge? TryGetBridge() - { - return _bridge; - } - - public static void Bridge(ILoggerBridge bridge) - { - _bridge = bridge; - } -} diff --git a/src/dotnetCampus.Logger/SourceGenerators/README.md b/src/dotnetCampus.Logger/SourceGenerators/README.md deleted file mode 100644 index c30c977..0000000 --- a/src/dotnetCampus.Logger/SourceGenerators/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# 源生成器 - -此文件夹中的所有类型将借助源生成器添加到目标项目中。 - -命名空间将全部被替换为 `$(RootNamespace).Logging`。 From 37599d38e7937d15ab32994727e2684146bc41ac Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 16:44:43 +0800 Subject: [PATCH 29/49] =?UTF-8?q?=E4=B8=B4=E6=97=B6=E4=B8=8D=E7=94=9F?= =?UTF-8?q?=E6=88=90=20Program=20=E5=88=86=E9=83=A8=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- samples/LoggerSample.MainApp/Program.cs | 2 +- .../CodeFixeProviders/PartialProgramCodeFixProvider.cs | 2 +- .../DiagnosticAnalyzers/PartialProgramAnalyzer.cs | 2 +- .../Generators/ProgramMainLogGenerator.cs | 4 +--- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs index dd5225b..64af6ff 100644 --- a/samples/LoggerSample.MainApp/Program.cs +++ b/samples/LoggerSample.MainApp/Program.cs @@ -2,7 +2,7 @@ namespace LoggerSample.MainApp; -internal partial class Program +internal class Program { public static void Main(string[] args) { diff --git a/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs index ccf5a24..31b1261 100644 --- a/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs +++ b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs @@ -10,7 +10,7 @@ namespace dotnetCampus.Logger.CodeFixeProviders; -[ExportCodeFixProvider(LanguageNames.CSharp), Shared] +// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class PartialProgramCodeFixProvider : CodeFixProvider { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( diff --git a/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs index 17b3638..f60b6ab 100644 --- a/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs +++ b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs @@ -19,7 +19,7 @@ public override void Initialize(AnalysisContext context) context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.RegisterSyntaxNodeAction(AnalyzeProgram, SyntaxKind.MethodDeclaration); + // context.RegisterSyntaxNodeAction(AnalyzeProgram, SyntaxKind.MethodDeclaration); } private void AnalyzeProgram(SyntaxNodeAnalysisContext context) diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs index 891416f..b46b888 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs @@ -15,13 +15,11 @@ namespace dotnetCampus.Logger.Generators; /// /// 生成 Program.g.cs,为 Main 方法第一行日志生成支持代码。 /// -[Generator] +// [Generator] public class ProgramMainLogGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { - return; - var provider = context.SyntaxProvider.CreateSyntaxProvider((node, ct) => { if (node is not MethodDeclarationSyntax mds) From cb197179691a43263b8e1ede883b408ce3669c39 Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 20:15:12 +0800 Subject: [PATCH 30/49] =?UTF-8?q?=E7=94=9F=E6=88=90=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=A1=A5=E7=9A=84=E8=BF=9E=E6=8E=A5=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AssemblyInfo.cs | 6 - .../Templates/AggregateLoggerBridge.g.cs | 3 +- .../GeneratorInfo.cs | 20 +++ .../Generators/LoggerBridgeGenerator.cs | 140 ++++++++++++++++++ .../Generators/ProgramMainLogGenerator.cs | 6 +- .../Utils/IO/EmbeddedSourceFiles.cs | 2 +- .../dotnetCampus.Logger.Analyzer.csproj | 1 + 7 files changed, 165 insertions(+), 13 deletions(-) delete mode 100644 src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/GeneratorInfo.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/Generators/LoggerBridgeGenerator.cs diff --git a/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs b/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs deleted file mode 100644 index ada8129..0000000 --- a/src/dotnetCampus.Logger.Analyzer/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace dotnetCampus.Logger; - -internal class AssemblyInfo -{ - public static readonly string RootNamespace = typeof(AssemblyInfo).Namespace!; -} diff --git a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs index f024c2a..9f1a8c8 100644 --- a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs +++ b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs @@ -6,7 +6,7 @@ using GLog = global::dotnetCampus.Logging.Log; using GLogLevel = global::dotnetCampus.Logging.LogLevel; -namespace LoggerSample.MainApp; +namespace dotnetCampus.Logger.Assets.Templates; /// /// 聚合各个来源的日志桥。调用其 方法可以将 @@ -32,6 +32,7 @@ public void Link(GILogger? logger = null) _logger = logger; + // 链接来自各个源的日志桥: // // // 源生成器请在此处添加代码... diff --git a/src/dotnetCampus.Logger.Analyzer/GeneratorInfo.cs b/src/dotnetCampus.Logger.Analyzer/GeneratorInfo.cs new file mode 100644 index 0000000..cd1426f --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/GeneratorInfo.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; +using dotnetCampus.Logger.Utils.IO; + +namespace dotnetCampus.Logger; + +internal static class GeneratorInfo +{ + public static readonly string RootNamespace = typeof(GeneratorInfo).Namespace!; + + public static EmbeddedSourceFile GetEmbeddedTemplateFile() + { + var typeName = typeof(TReferenceType).Name; + var templateNamespace = typeof(TReferenceType).Namespace!; + var templatesFolder = templateNamespace.AsSpan().Slice(GeneratorInfo.RootNamespace.Length + 1).ToString(); + var embeddedFile = EmbeddedSourceFiles.Enumerate(templatesFolder) + .Single(x => x.FileName.StartsWith(typeName) && x.FileName.EndsWith(".g.cs")); + return embeddedFile; + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerBridgeGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerBridgeGenerator.cs new file mode 100644 index 0000000..c162097 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerBridgeGenerator.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using dotnetCampus.Logger.Assets.Templates; +using dotnetCampus.Logging.Attributes; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace dotnetCampus.Logger.Generators; + +/// +/// 生成聚合日志桥,为来自各个库的日志桥对接日志记录器。 +/// +[Generator] +public class LoggerBridgeGenerator : IIncrementalGenerator +{ + private static readonly string ImportLoggerBridgeUsageName = nameof(ImportLoggerBridgeAttribute).Replace("Attribute", ""); + private static readonly string ImportLoggerBridgeAttributeName = typeof(ImportLoggerBridgeAttribute).FullName!; + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var provider = context.SyntaxProvider.CreateSyntaxProvider((node, ct) => + { + if (node is not ClassDeclarationSyntax cds) + { + // 必须是类声明。 + return false; + } + + var attributes = cds.AttributeLists + .SelectMany(x => x.Attributes) + .Where(x => x.Name.ToString().StartsWith(ImportLoggerBridgeUsageName)); + if (!attributes.Any()) + { + // 必须有 ImportLoggerBridge 特性。 + return false; + } + + return true; + }, (c, ct) => + { + var aggregateBridgeType = c.SemanticModel.GetDeclaredSymbol((ClassDeclarationSyntax)c.Node); + if (aggregateBridgeType is null) + { + return null; + } + + var collectedBridgeTypes = aggregateBridgeType.GetAttributes() + .Where(x => x.AttributeClass switch + { + { IsGenericType: true } ga => ga.OriginalDefinition.ToDisplayString() == $"{ImportLoggerBridgeAttributeName}", + _ => x.AttributeClass?.ToDisplayString() == ImportLoggerBridgeAttributeName, + }) + .Select(x => x.AttributeClass switch + { + { IsGenericType: true } ga => ga.TypeArguments.FirstOrDefault() as INamedTypeSymbol, + var ba => x.ConstructorArguments.FirstOrDefault().Value as INamedTypeSymbol, + }) + .OfType() + .ToImmutableArray(); + if (collectedBridgeTypes.Length is 0) + { + return null; + } + + return new LoggerBridgeItem(aggregateBridgeType, collectedBridgeTypes); + }) + .Where(x => x is not null) + .Select((x, ct) => x!); + + context.RegisterSourceOutput(provider, Execute); + } + + private void Execute(SourceProductionContext context, LoggerBridgeItem bridgeItem) + { + var bridgeNamespace = bridgeItem.Aggregate.ContainingNamespace.ToDisplayString(); + var bridgeName = bridgeItem.Aggregate.Name; + + var loggerBridgeFile = GeneratorInfo.GetEmbeddedTemplateFile(); + var intermediateCode = loggerBridgeFile.Content + .Replace(typeof(AggregateLoggerBridge).Namespace!, bridgeNamespace) + .Replace(nameof(AggregateLoggerBridge), bridgeName) + .Replace($"partial class {bridgeName}", $""" +partial class {bridgeName} : +{string.Join("\n", bridgeItem.Collected.Select(x => $" global::{x.ToDisplayString()}"))} +"""); + + var generatedCode = InsertLinks(bridgeItem, intermediateCode); + + context.AddSource($"{nameof(AggregateLoggerBridge)}.{bridgeName}.g.cs", SourceText.From(generatedCode, Encoding.UTF8)); + } + + private string InsertLinks(LoggerBridgeItem bridgeItem, string sourceCode) + { + var sourceSpan = sourceCode.AsSpan(); + + var regex = GetFlagRegex(); + var match = regex.Match(sourceCode); + if (!match.Success) + { + return sourceCode; + } + + var insertStartIndex = match.Index; + var insertEndIndex = match.Index + match.Length; + var links = GenerateLinks(bridgeItem.Collected); + + return string.Concat( + sourceSpan.Slice(0, insertStartIndex).ToString(), + links, + sourceSpan.Slice(insertEndIndex, sourceSpan.Length - insertEndIndex).ToString() + ); + } + + private string GenerateLinks(IEnumerable collected) + { + return $""" + +{string.Join("\n", collected.Select(x => $" {GenerateLink(x)}"))} +"""; + } + + private string GenerateLink(INamedTypeSymbol collectedBridgeType) + { + // global::Xxx.Logging.ILoggerBridge.Link(this); + return $"global::{collectedBridgeType.ToDisplayString()}.Link(this);"; + } + + private static Regex? _flagRegex; + + private static Regex GetFlagRegex() => _flagRegex ??= new Regex(@"\s+// .+?", RegexOptions.Compiled | RegexOptions.Singleline); + + private record LoggerBridgeItem(INamedTypeSymbol Aggregate, ImmutableArray Collected); +} diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs index b46b888..c5ef00c 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs @@ -60,12 +60,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) private void Execute(SourceProductionContext context, INamedTypeSymbol programTypeSymbol) { - var templateProgramNamespace = typeof(Program).Namespace!; - var templatesFolder = templateProgramNamespace.AsSpan().Slice(AssemblyInfo.RootNamespace.Length + 1).ToString(); - var embeddedFiles = EmbeddedSourceFiles.Enumerate(templatesFolder).ToImmutableArray(); - // 生成 Program.Logger.g.cs - var partialLoggerFile = embeddedFiles.First(x => x.FileName.StartsWith("Program.", StringComparison.Ordinal)); + var partialLoggerFile = GeneratorInfo.GetEmbeddedTemplateFile(); var generatedLoggerText = ConvertPartialProgramLogger(partialLoggerFile.Content, programTypeSymbol); context.AddSource($"{programTypeSymbol.Name}.Logger.g.cs", SourceText.From(generatedLoggerText, Encoding.UTF8)); } diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs index bf3766d..70428ac 100644 --- a/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs +++ b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs @@ -18,7 +18,7 @@ internal static class EmbeddedSourceFiles internal static IEnumerable Enumerate(string folderName) { // 资源字符串格式为:"{Namespace}.{Folder}.{filename}.{Extension}" - var desiredFolder = $"{AssemblyInfo.RootNamespace}.{folderName}"; + var desiredFolder = $"{GeneratorInfo.RootNamespace}.{folderName}"; var assembly = Assembly.GetExecutingAssembly(); foreach (var resourceName in assembly.GetManifestResourceNames()) { diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 26b0c86..a2d2078 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -20,6 +20,7 @@ + From 8cd9abdedd6c8391660b830374907e469328f431 Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 21:10:00 +0800 Subject: [PATCH 31/49] =?UTF-8?q?=E7=94=9F=E6=88=90=20globalusings=20?= =?UTF-8?q?=E6=97=B6=E5=8C=BA=E5=88=86=E5=BA=93=E5=92=8C=E5=BA=94=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...LoggerSample.LoggerDependentLibrary.csproj | 5 ++ ...ggerSample.LoggerIndependentLibrary.csproj | 5 ++ .../LoggerSample.MainApp.csproj | 5 ++ .../Generators/GlobalUsingsGenerator.cs | 77 +++++++++++++++++++ .../Generators/LoggerGenerator.cs | 16 ---- .../Properties/Localizations.resx | 2 +- .../Properties/Localizations.zh-hans.resx | 2 +- .../Properties/Localizations.zh-hant.resx | 2 +- .../CodeAnalysis/DiagnosticExtensions.cs | 14 ++++ .../dotnetCampus.Logger.Analyzer.csproj | 4 +- .../Properties/Package/build/Package.props | 2 + .../Properties/Package/build/Package.targets | 5 ++ 12 files changed, 118 insertions(+), 21 deletions(-) create mode 100644 src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs create mode 100644 src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/DiagnosticExtensions.cs diff --git a/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj index 4924947..2db8c72 100644 --- a/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj +++ b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj @@ -1,12 +1,17 @@  + + net8.0 enable + <_DLMainlyUseGeneratedLogger>false + + diff --git a/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj index d2fce35..dc27bd5 100644 --- a/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj +++ b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj @@ -1,12 +1,17 @@  + + net8.0 enable + <_DLMainlyUseGeneratedLogger>true + + diff --git a/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj b/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj index ba96328..8a24e56 100644 --- a/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj +++ b/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj @@ -1,8 +1,11 @@  + + WinExe net8.0 + <_DLMainlyUseGeneratedLogger>false @@ -12,4 +15,6 @@ + + diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs new file mode 100644 index 0000000..62e8f04 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using dotnetCampus.Logger.Utils.CodeAnalysis; +using dotnetCampus.Logger.Utils.IO; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace dotnetCampus.Logger.Generators; + +/// +/// 生成一组用于记录日志的代码。 +/// +[Generator] +public class GlobalUsingsGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + context.RegisterSourceOutput(context.AnalyzerConfigOptionsProvider, Execute); + } + + private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvider provider) + { + provider.GlobalOptions.TryGetValue("build_property.OutputType", out var outputType); + provider.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace); + provider.GlobalOptions.TryGetValue("build_property._DLMainlyUseGeneratedLogger", out var mainlyUseGeneratedLogger); + if (outputType is null || rootNamespace is null || mainlyUseGeneratedLogger is null) + { + context.ReportUnknownError("NuGet 包中应包含 OutputType、RootNamespace 和 _DLMainlyUseGeneratedLogger 属性。"); + return; + } + + var useGeneratedLogger = CheckIsUsingGeneratedLogger(mainlyUseGeneratedLogger); + var generatedCode = useGeneratedLogger + ? GenerateGlobalUsings(rootNamespace) + : GenerateGlobalUsings("dotnetCampus"); + + context.AddSource("GlobalUsings.g.cs", SourceText.From(generatedCode, Encoding.UTF8)); + } + + private static bool CheckIsUsingGeneratedLogger(string mainlyUseGeneratedLogger) + { + return mainlyUseGeneratedLogger.Equals("true", StringComparison.OrdinalIgnoreCase); + } + + private string GenerateGlobalUsings(string rootNamespace) + { + var sourceFiles = EmbeddedSourceFiles.Enumerate("Assets/Sources").ToImmutableArray(); + + var globalUsingsCode = GenerateGlobalUsingsForTypes( + rootNamespace, + [..sourceFiles.Select(x => x.FileName.Replace(".g.cs", ""))]); + return globalUsingsCode; + } + + private string GenerateGlobalUsingsForTypes(string rootNamespace, ImmutableArray relativeTypeNames) + { + return $""" +global using global::{rootNamespace}.Logging; + +{string.Join("\n", relativeTypeNames.Select(GenerateTypeUsing).OfType())} + +"""; + + string? GenerateTypeUsing(string relativeTypeName) + { + if (relativeTypeName.Contains('.')) + { + return null; + } + + return $"global using {relativeTypeName} = global::{rootNamespace}.Logging.{relativeTypeName};"; + } + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs index a859e9d..a35a77a 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Immutable; -using System.Linq; using System.Text; using System.Text.RegularExpressions; using dotnetCampus.Logger.Utils.IO; @@ -31,11 +30,6 @@ private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvi var sourceFiles = EmbeddedSourceFiles.Enumerate("Assets/Sources").ToImmutableArray(); - var globalUsingsCode = GenerateGlobalUsings( - rootNamespace, - [..sourceFiles.Select(x => x.FileName.Substring(0, x.FileName.IndexOf('.')))]); - context.AddSource("GlobalUsings.g.cs", SourceText.From(globalUsingsCode, Encoding.UTF8)); - foreach (var file in sourceFiles) { var code = GenerateSource(file.FileName, rootNamespace, file.Content); @@ -83,16 +77,6 @@ private string GenerateSource(string fileName, string rootNamespace, string sour } } - private string GenerateGlobalUsings(string rootNamespace, ImmutableArray typeNames) - { - return $""" -global using global::{rootNamespace}.Logging; - -{string.Join("\n", typeNames.Select(x => $"global using {x} = global::{rootNamespace}.Logging.{x};"))} - -"""; - } - private static Regex? _typeRegex; private static Regex GetTypeRegex() => _typeRegex ??= new Regex(@"\b(?:class|record|struct|enum|interface)\b", RegexOptions.Compiled); diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx index 1b3b12d..9efb6fd 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx @@ -27,7 +27,7 @@ Unknown Error - An unknown error occurred. + An unknown error occurred. {0} Change the entry class to a partial class diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx index b06dc7a..5b15a53 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx @@ -19,7 +19,7 @@ 未知错误 - 发生了未知错误。 + 发生了未知错误。{0} 将 '{0}' 改为分部类,以允许其在日志模块初始化之前使用日志系统。 diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx index d811e51..62fa0fa 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx @@ -19,7 +19,7 @@ 未知錯誤 - 發生了未知錯誤。 + 發生了未知錯誤。{0} 將 '{0}' 改為分部類,以允許其在日誌模塊初始化之前使用日誌系統。 diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/DiagnosticExtensions.cs b/src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/DiagnosticExtensions.cs new file mode 100644 index 0000000..fa1a8b6 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/DiagnosticExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.CodeAnalysis; + +namespace dotnetCampus.Logger.Utils.CodeAnalysis; + +public static class DiagnosticExtensions +{ + public static void ReportUnknownError(this SourceProductionContext context, string message) + { + context.ReportDiagnostic(Diagnostic.Create( + Diagnostics.DL0000_UnknownError, + null, + message)); + } +} diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index a2d2078..fcaf2fb 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -21,8 +21,8 @@ - - + + diff --git a/src/dotnetCampus.Logger/Properties/Package/build/Package.props b/src/dotnetCampus.Logger/Properties/Package/build/Package.props index 68352a5..3e9cfa9 100644 --- a/src/dotnetCampus.Logger/Properties/Package/build/Package.props +++ b/src/dotnetCampus.Logger/Properties/Package/build/Package.props @@ -6,6 +6,8 @@ + + diff --git a/src/dotnetCampus.Logger/Properties/Package/build/Package.targets b/src/dotnetCampus.Logger/Properties/Package/build/Package.targets index 44d225a..2a50c46 100644 --- a/src/dotnetCampus.Logger/Properties/Package/build/Package.targets +++ b/src/dotnetCampus.Logger/Properties/Package/build/Package.targets @@ -4,4 +4,9 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + <_DLMainlyUseGeneratedLogger Condition="$(OutputType) != 'Exe' and $(OutputType) != 'WinExe'">true + + From 2cf52589770df32e5c9fa8b701313a8d4394298c Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 21:10:23 +0800 Subject: [PATCH 32/49] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dotnetCampus.Logger.Analyzer/Utils/IO/Log.cs | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 src/dotnetCampus.Logger.Analyzer/Utils/IO/Log.cs diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/IO/Log.cs b/src/dotnetCampus.Logger.Analyzer/Utils/IO/Log.cs deleted file mode 100644 index 5138017..0000000 --- a/src/dotnetCampus.Logger.Analyzer/Utils/IO/Log.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; - -// ReSharper disable CheckNamespace - -namespace dotnetCampus.Logger; - -internal static class Log -{ - [Conditional("DEBUG")] - internal static void Debug(string message) - { - Debugger.Launch(); - } -} From cbba2e3e0435cceee33f4c81713d0d4635a4e645 Mon Sep 17 00:00:00 2001 From: walterlv Date: Thu, 9 May 2024 21:36:56 +0800 Subject: [PATCH 33/49] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20globalUsings=20?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GeneratorInfo.cs | 2 +- .../Generators/GlobalUsingsGenerator.cs | 34 ++++++++++--------- .../Generators/LoggerGenerator.cs | 10 +++--- .../Utils/IO/EmbeddedSourceFile.cs | 10 +++--- .../Utils/IO/EmbeddedSourceFiles.cs | 31 +++++++++++++---- 5 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/dotnetCampus.Logger.Analyzer/GeneratorInfo.cs b/src/dotnetCampus.Logger.Analyzer/GeneratorInfo.cs index cd1426f..fd517aa 100644 --- a/src/dotnetCampus.Logger.Analyzer/GeneratorInfo.cs +++ b/src/dotnetCampus.Logger.Analyzer/GeneratorInfo.cs @@ -14,7 +14,7 @@ public static EmbeddedSourceFile GetEmbeddedTemplateFile() var templateNamespace = typeof(TReferenceType).Namespace!; var templatesFolder = templateNamespace.AsSpan().Slice(GeneratorInfo.RootNamespace.Length + 1).ToString(); var embeddedFile = EmbeddedSourceFiles.Enumerate(templatesFolder) - .Single(x => x.FileName.StartsWith(typeName) && x.FileName.EndsWith(".g.cs")); + .Single(x => x.TypeName == typeName); return embeddedFile; } } diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs index 62e8f04..75b274e 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Text; using dotnetCampus.Logger.Utils.CodeAnalysis; @@ -32,46 +33,47 @@ private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvi return; } - var useGeneratedLogger = CheckIsUsingGeneratedLogger(mainlyUseGeneratedLogger); + var useGeneratedLogger = mainlyUseGeneratedLogger.Equals("true", StringComparison.OrdinalIgnoreCase); var generatedCode = useGeneratedLogger - ? GenerateGlobalUsings(rootNamespace) - : GenerateGlobalUsings("dotnetCampus"); + ? GenerateGlobalUsings(rootNamespace, useGeneratedLogger) + : GenerateGlobalUsings("dotnetCampus", useGeneratedLogger); context.AddSource("GlobalUsings.g.cs", SourceText.From(generatedCode, Encoding.UTF8)); } - private static bool CheckIsUsingGeneratedLogger(string mainlyUseGeneratedLogger) - { - return mainlyUseGeneratedLogger.Equals("true", StringComparison.OrdinalIgnoreCase); - } - - private string GenerateGlobalUsings(string rootNamespace) + private string GenerateGlobalUsings(string rootNamespace, bool useGeneratedLogger) { var sourceFiles = EmbeddedSourceFiles.Enumerate("Assets/Sources").ToImmutableArray(); var globalUsingsCode = GenerateGlobalUsingsForTypes( rootNamespace, - [..sourceFiles.Select(x => x.FileName.Replace(".g.cs", ""))]); + [..sourceFiles], + useGeneratedLogger); return globalUsingsCode; } - private string GenerateGlobalUsingsForTypes(string rootNamespace, ImmutableArray relativeTypeNames) + private string GenerateGlobalUsingsForTypes(string rootNamespace, ImmutableArray sourceFiles, bool useGeneratedLogger) { return $""" global using global::{rootNamespace}.Logging; -{string.Join("\n", relativeTypeNames.Select(GenerateTypeUsing).OfType())} +{string.Join("\n", sourceFiles.Select(GenerateTypeUsing).OfType())} """; - string? GenerateTypeUsing(string relativeTypeName) + string? GenerateTypeUsing(EmbeddedSourceFile sourceFile) { - if (relativeTypeName.Contains('.')) + if ( + // 如果使用源生成器的日志系统,则所有类型均要导出全局引用。 + useGeneratedLogger + // 如果使用源生成器的日志系统,则所有类型均要导出全局引用。 + || sourceFile.Namespace.EndsWith("Sources") + ) { - return null; + return $"global using {sourceFile.TypeName} = global::{rootNamespace}.Logging.{sourceFile.TypeName};"; } - return $"global using {relativeTypeName} = global::{rootNamespace}.Logging.{relativeTypeName};"; + return null; } } } diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs index a35a77a..f31b73f 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs @@ -32,14 +32,14 @@ private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvi foreach (var file in sourceFiles) { - var code = GenerateSource(file.FileName, rootNamespace, file.Content); - context.AddSource(file.FileName, SourceText.From(code, Encoding.UTF8)); + var code = GenerateSource(file.TypeName, rootNamespace, file.Content); + context.AddSource($"{file.TypeName}.g.cs", SourceText.From(code, Encoding.UTF8)); } } - private string GenerateSource(string fileName, string rootNamespace, string sourceText) + private string GenerateSource(string typeName, string rootNamespace, string sourceText) { - if (fileName == "Log.g.cs") + if (typeName == "Log") { // 源生成器为单独库生成的代码中,默认日志记录器是 BridgeLogger。 sourceText = sourceText.Replace("new NullLogger();", "new BridgeLogger();"); @@ -54,7 +54,7 @@ private string GenerateSource(string fileName, string rootNamespace, string sour var classKeywordIndex = GetTypeRegex().Match(sourceText).Index; var publicKeywordIndex = sourceText.IndexOf("public", namespaceEndIndex, classKeywordIndex - namespaceEndIndex, StringComparison.Ordinal); - if (publicKeywordIndex < 0 || fileName.Contains("Bridge")) + if (publicKeywordIndex < 0 || typeName.Contains("Bridge")) { // 此类型不是 public 的,无需修改为 internal。 // 此类型是 BridgeLogger,应该保持 public。 diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFile.cs b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFile.cs index df4c2b6..0020cf1 100644 --- a/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFile.cs +++ b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFile.cs @@ -3,14 +3,12 @@ /// /// 嵌入的文本资源的数据。 /// -/// 文件的命名空间。 -/// 文件相对于项目根目录所在的文件夹的路径。 /// 文件的名称(含扩展名)。 -/// 文件在嵌入的资源中的名称。 +/// 文件的名称(不含扩展名),或者也很可能是类型名称。 +/// 文件的命名空间。 /// 文件的文本内容。 internal readonly record struct EmbeddedSourceFile( - string Namespace, - string RelativeDirectoryPath, string FileName, - string EmbeddedName, + string TypeName, + string Namespace, string Content); diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs index 70428ac..bf8a78b 100644 --- a/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs +++ b/src/dotnetCampus.Logger.Analyzer/Utils/IO/EmbeddedSourceFiles.cs @@ -25,15 +25,32 @@ internal static IEnumerable Enumerate(string folderName) var prefix = desiredFolder.Replace('/', '.').Replace('\\', '.') + "."; if (resourceName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { - var fileName = resourceName.AsSpan().Slice(prefix.Length).ToString(); using var stream = assembly.GetManifestResourceStream(resourceName)!; using var reader = new StreamReader(stream); - yield return new EmbeddedSourceFile( - desiredFolder, - folderName, - fileName, - resourceName, - reader.ReadToEnd()); + var contentText = reader.ReadToEnd(); + + var fileName = resourceName.AsSpan().Slice(prefix.Length).ToString(); + var fileNameWithoutExtension = fileName.Replace(".g.cs", "").Replace(".cs", ""); + var fileNameIndex = fileNameWithoutExtension.LastIndexOf('.'); + if (fileNameIndex < 0) + { + yield return new EmbeddedSourceFile( + fileName, + fileNameWithoutExtension, + desiredFolder, + contentText); + } + else + { + var typeName = fileNameWithoutExtension.Substring(fileNameIndex + 1); + var @namespace = $"{desiredFolder}.{fileNameWithoutExtension.Substring(0, fileNameIndex)}"; + + yield return new EmbeddedSourceFile( + fileName, + typeName, + @namespace, + contentText); + } } } } From 79277137cac037e13fca40483753d3e25d587215 Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 09:29:18 +0800 Subject: [PATCH 34/49] =?UTF-8?q?=E6=9E=84=E5=BB=BA=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E5=8F=AF=E4=BD=BF=E7=94=A8=E6=A1=A5=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- samples/LoggerSample.MainApp/Program.cs | 9 +++ ....g.cs => AggregateLoggerBridgeLinker.g.cs} | 12 +++- .../Assets/Templates/Program.logger.g.cs | 60 +------------------ .../Generators/LoggerBridgeGenerator.cs | 15 +++-- .../dotnetCampus.Logger.Analyzer.csproj | 8 +++ .../Bridges/ILoggerBridgeLinker.cs | 17 ++++++ src/dotnetCampus.Logger/Log.g.cs | 2 +- src/dotnetCampus.Logger/LogBuilder.cs | 35 +++++++++-- .../Writers/MemoryCacheLogger.cs | 51 ++++++++++++++++ 9 files changed, 134 insertions(+), 75 deletions(-) rename src/dotnetCampus.Logger.Analyzer/Assets/Templates/{AggregateLoggerBridge.g.cs => AggregateLoggerBridgeLinker.g.cs} (86%) create mode 100644 src/dotnetCampus.Logger/Bridges/ILoggerBridgeLinker.cs create mode 100644 src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs index 64af6ff..0dec36e 100644 --- a/samples/LoggerSample.MainApp/Program.cs +++ b/samples/LoggerSample.MainApp/Program.cs @@ -1,4 +1,5 @@ using dotnetCampus.Logging.Attributes; +using dotnetCampus.Logging.Writers; namespace LoggerSample.MainApp; @@ -6,6 +7,14 @@ internal class Program { public static void Main(string[] args) { + // 这里是 Main 方法入口。 + + // 以下初始化代码可能会较晚执行。 + new LoggerBuilder() + .UseLevel(LogLevel.Information) + .AddWriter(new ConsoleLogger()) + .AddBridge(LoggerBridge.Default) + .BuildIntoStaticLog(); } } diff --git a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridgeLinker.g.cs similarity index 86% rename from src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs rename to src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridgeLinker.g.cs index 9f1a8c8..a5d6d43 100644 --- a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridge.g.cs +++ b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/AggregateLoggerBridgeLinker.g.cs @@ -3,16 +3,22 @@ using GEventId = global::dotnetCampus.Logging.EventId; using GException = global::System.Exception; using GILogger = global::dotnetCampus.Logging.ILogger; +using GILoggerBridgeLinker = global::dotnetCampus.Logging.Bridges.ILoggerBridgeLinker; using GLog = global::dotnetCampus.Logging.Log; using GLogLevel = global::dotnetCampus.Logging.LogLevel; namespace dotnetCampus.Logger.Assets.Templates; /// -/// 聚合各个来源的日志桥。调用其 方法可以将 +/// 聚合各个来源的日志桥。调用其 方法可以将这些来源的日志桥对接到指定的日志记录器上。 /// -partial class AggregateLoggerBridge +partial class AggregateLoggerBridgeLinker : GILoggerBridgeLinker { + /// + /// 获取用于对接到日志记录系统的聚合日志桥。 + /// + public static AggregateLoggerBridgeLinker Default { get; } = new(); + private GILogger? _logger; /// @@ -23,7 +29,7 @@ partial class AggregateLoggerBridge /// 如果已经对接过日志记录器,则会抛出此异常。 /// 如果希望针对不同的库对接不同的日志记录器,请编写多个聚合日志桥并分别导入各自的日志桥。 /// - public void Link(GILogger? logger = null) + public void Link(GILogger logger) { if (_logger != null) { diff --git a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs index a5f145f..99485e4 100644 --- a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs +++ b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs @@ -19,63 +19,5 @@ partial class Program /// 由于此代码是源生成器生成的代码,所以可以在日志模块初始化之前记录日志且提前生效。
/// 🤩 你甚至能在 Main 方法的第一行就使用它记录日志! /// - private static GeneratedMemoryCacheLogger Log => GeneratedMemoryCacheLogger.Instance.Value; - - [EditorBrowsable(EditorBrowsableState.Never)] - private sealed class GeneratedMemoryCacheLogger : ILogger - { - [EditorBrowsable(EditorBrowsableState.Never)] - internal static readonly global::System.Lazy Instance = new( - () => new GeneratedMemoryCacheLogger(), - LazyThreadSafetyMode.None); - - private ILogger? _realLogger; - - private readonly global::System.Collections.Concurrent.ConcurrentQueue _queue = []; - - void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, - global::System.Func formatter) - { - if (_realLogger is { } logger) - { - logger.Log(logLevel, eventId, state, exception, (s, e) => formatter((TState)s, e)); - } - _queue.Enqueue(new(logLevel, eventId, state!, exception, (s, e) => formatter((TState)s, e))); - } - - /// - /// 将 隐式转换为可传递到 LogBuilder.UseMemoryCache 方法的委托。 - /// - /// - /// - public static implicit operator global::System.Action(GeneratedMemoryCacheLogger logger) - { - return logger.Flush; - } - - private void Flush(ILogger logger) - { - _realLogger = logger; - while (_queue.TryDequeue(out var context)) - { - logger.Log(context.logLevel, context.eventId, context.state, context.exception, context.formatter); - } - } - - /// - /// 辅助缓存日志条目。 - /// - /// 日志级别。 - /// 事件 Id。 - /// 日志内容。 - /// 异常信息。 - /// 格式化器。 - private readonly record struct CachedLogItem( - LogLevel logLevel, - EventId eventId, - object state, - Exception? exception, - global::System.Func formatter - ); - } + private static ILogger Log { get; } } diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerBridgeGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerBridgeGenerator.cs index c162097..2f4251b 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerBridgeGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerBridgeGenerator.cs @@ -82,18 +82,17 @@ private void Execute(SourceProductionContext context, LoggerBridgeItem bridgeIte var bridgeNamespace = bridgeItem.Aggregate.ContainingNamespace.ToDisplayString(); var bridgeName = bridgeItem.Aggregate.Name; - var loggerBridgeFile = GeneratorInfo.GetEmbeddedTemplateFile(); + var loggerBridgeFile = GeneratorInfo.GetEmbeddedTemplateFile(); var intermediateCode = loggerBridgeFile.Content - .Replace(typeof(AggregateLoggerBridge).Namespace!, bridgeNamespace) - .Replace(nameof(AggregateLoggerBridge), bridgeName) - .Replace($"partial class {bridgeName}", $""" -partial class {bridgeName} : -{string.Join("\n", bridgeItem.Collected.Select(x => $" global::{x.ToDisplayString()}"))} -"""); + .Replace(typeof(AggregateLoggerBridgeLinker).Namespace!, bridgeNamespace) + .Replace(nameof(AggregateLoggerBridgeLinker), bridgeName) + .Replace( + $"partial class {bridgeName} : GILoggerBridgeLinker", + $"partial class {bridgeName} : GILoggerBridgeLinker{string.Concat(bridgeItem.Collected.Select(x => $",\n global::{x.ToDisplayString()}"))}"); var generatedCode = InsertLinks(bridgeItem, intermediateCode); - context.AddSource($"{nameof(AggregateLoggerBridge)}.{bridgeName}.g.cs", SourceText.From(generatedCode, Encoding.UTF8)); + context.AddSource($"{nameof(AggregateLoggerBridgeLinker)}.{bridgeName}.g.cs", SourceText.From(generatedCode, Encoding.UTF8)); } private string InsertLinks(LoggerBridgeItem bridgeItem, string sourceCode) diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index fcaf2fb..7dc5864 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -19,11 +19,19 @@ + + + + + + + + diff --git a/src/dotnetCampus.Logger/Bridges/ILoggerBridgeLinker.cs b/src/dotnetCampus.Logger/Bridges/ILoggerBridgeLinker.cs new file mode 100644 index 0000000..3b04333 --- /dev/null +++ b/src/dotnetCampus.Logger/Bridges/ILoggerBridgeLinker.cs @@ -0,0 +1,17 @@ +namespace dotnetCampus.Logging.Bridges; + +/// +/// 表示一个日志桥对接器。 +/// +public interface ILoggerBridgeLinker +{ + /// + /// 将已指派给此日志桥连接器的所有日志桥对接到 日志记录器上。 + /// + /// 要对接的日志记录器。 + /// + /// 如果已经对接过日志记录器,则会抛出此异常。 + /// 如果希望针对不同的库对接不同的日志记录器,请编写多个聚合日志桥并分别导入各自的日志桥。 + /// + void Link(ILogger logger); +} diff --git a/src/dotnetCampus.Logger/Log.g.cs b/src/dotnetCampus.Logger/Log.g.cs index 5513cb2..3fae1a3 100644 --- a/src/dotnetCampus.Logger/Log.g.cs +++ b/src/dotnetCampus.Logger/Log.g.cs @@ -8,7 +8,7 @@ namespace dotnetCampus.Logging; /// /// 提供静态的日志记录方法。 /// -public static class Log +public static partial class Log { private static ILogger _current; diff --git a/src/dotnetCampus.Logger/LogBuilder.cs b/src/dotnetCampus.Logger/LogBuilder.cs index 6583503..ccc8d4f 100644 --- a/src/dotnetCampus.Logger/LogBuilder.cs +++ b/src/dotnetCampus.Logger/LogBuilder.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using dotnetCampus.Logging.Bridges; using dotnetCampus.Logging.Configurations; +using dotnetCampus.Logging.Writers; namespace dotnetCampus.Logging; @@ -8,7 +10,7 @@ public class LoggerBuilder { private LogOptions? _options; private readonly List _writers = []; - private Action? _flusher; + private readonly List _linkers = []; /// /// 调用此方法以便在日志模块初始化完成前先对所有记录的日志进行缓存,以便在日志模块初始化完成后再将缓存的日志写入到日志文件中。 @@ -22,7 +24,6 @@ public class LoggerBuilder /// public LoggerBuilder UseMemoryCache(Action flusher) { - _flusher = flusher; return this; } @@ -45,16 +46,42 @@ public LoggerBuilder AddWriter(ILogger writer) return this; } + public LoggerBuilder AddBridge(ILoggerBridgeLinker linker) + { + _linkers.Add(linker); + return this; + } + public CompositeLogger Build() { var logger = new CompositeLogger(_options ?? new LogOptions()) { Writers = [.._writers], }; - if (_flusher is { } flusher) + foreach (var linker in _linkers) { - flusher(logger); + linker.Link(logger); } return logger; } + + public CompositeLogger BuildIntoStaticLog() + { + var logger = Build(); + Log.SetLogger(logger); + return logger; + } +} + +partial class Log +{ + internal static void SetLogger(ILogger logger) + { + var oldLogger = Current; + if (oldLogger is MemoryCacheLogger mcl) + { + mcl.Flush(logger); + } + Current = logger; + } } diff --git a/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs b/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs new file mode 100644 index 0000000..9968de9 --- /dev/null +++ b/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs @@ -0,0 +1,51 @@ +using System; + +namespace dotnetCampus.Logging.Writers; + +/// +/// 用于在日志模块初始化完成前先对所有记录的日志进行缓存,以便在日志模块初始化完成后再将缓存的日志写入到日志文件中。 +/// +internal class MemoryCacheLogger : ILogger +{ + private ILogger? _realLogger; + + private readonly System.Collections.Concurrent.ConcurrentQueue _queue = []; + + void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (_realLogger is { } logger) + { + logger.Log(logLevel, eventId, state, exception, (s, e) => formatter((TState)s, e)); + } + _queue.Enqueue(new(logLevel, eventId, state!, exception, (s, e) => formatter((TState)s, e))); + } + + /// + /// 将所有缓存的日志写入到真正的日志记录器中。 + /// + /// 真正的日志记录器。 + internal void Flush(ILogger logger) + { + _realLogger = logger; + while (_queue.TryDequeue(out var context)) + { + logger.Log(context.LogLevel, context.EventId, context.State, context.Exception, context.Formatter); + } + } + + /// + /// 辅助缓存日志条目。 + /// + /// 日志级别。 + /// 事件 Id。 + /// 日志内容。 + /// 异常信息。 + /// 格式化器。 + private readonly record struct CachedLogItem( + LogLevel LogLevel, + EventId EventId, + object State, + Exception? Exception, + Func Formatter + ); +} From f9f8dd3d56634e0c74c6c1f8e8268b8f647c2970 Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 09:31:20 +0800 Subject: [PATCH 35/49] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E6=97=B6=E7=9A=84=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- samples/LoggerSample.MainApp/Program.cs | 15 +++++++++++++-- src/dotnetCampus.Logger/LogBuilder.cs | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs index 0dec36e..bc1c98b 100644 --- a/samples/LoggerSample.MainApp/Program.cs +++ b/samples/LoggerSample.MainApp/Program.cs @@ -1,4 +1,5 @@ using dotnetCampus.Logging.Attributes; +using dotnetCampus.Logging.Configurations; using dotnetCampus.Logging.Writers; namespace LoggerSample.MainApp; @@ -11,8 +12,18 @@ public static void Main(string[] args) // 以下初始化代码可能会较晚执行。 new LoggerBuilder() - .UseLevel(LogLevel.Information) - .AddWriter(new ConsoleLogger()) + .WithLevel(LogLevel.Information) + .WithOptions(new LogOptions + { + LogLevel = LogLevel.Debug, + }) + .AddWriter(new ConsoleLogger + { + // Options = new ConsoleLoggerOptions + // { + // IncludeScopes = true, + // }, + }) .AddBridge(LoggerBridge.Default) .BuildIntoStaticLog(); } diff --git a/src/dotnetCampus.Logger/LogBuilder.cs b/src/dotnetCampus.Logger/LogBuilder.cs index ccc8d4f..44976eb 100644 --- a/src/dotnetCampus.Logger/LogBuilder.cs +++ b/src/dotnetCampus.Logger/LogBuilder.cs @@ -27,7 +27,7 @@ public LoggerBuilder UseMemoryCache(Action flusher) return this; } - public LoggerBuilder UseLevel(LogLevel level) + public LoggerBuilder WithLevel(LogLevel level) { _options ??= new LogOptions(); _options.LogLevel = level; From 3865e99d83c0417aa5cae5367490125d1af4246f Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 09:41:38 +0800 Subject: [PATCH 36/49] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E6=88=90=E6=9C=80=E7=BB=88=E7=9B=AE=E6=A0=87=E7=9A=84=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- samples/LoggerSample.MainApp/Program.cs | 3 +- .../{LogBuilder.cs => LoggerBuilder.cs} | 38 +++++++++++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) rename src/dotnetCampus.Logger/{LogBuilder.cs => LoggerBuilder.cs} (61%) diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs index bc1c98b..094491e 100644 --- a/samples/LoggerSample.MainApp/Program.cs +++ b/samples/LoggerSample.MainApp/Program.cs @@ -25,7 +25,8 @@ public static void Main(string[] args) // }, }) .AddBridge(LoggerBridge.Default) - .BuildIntoStaticLog(); + .Build() + .IntoGlobalStaticLog(); } } diff --git a/src/dotnetCampus.Logger/LogBuilder.cs b/src/dotnetCampus.Logger/LoggerBuilder.cs similarity index 61% rename from src/dotnetCampus.Logger/LogBuilder.cs rename to src/dotnetCampus.Logger/LoggerBuilder.cs index 44976eb..19bafcd 100644 --- a/src/dotnetCampus.Logger/LogBuilder.cs +++ b/src/dotnetCampus.Logger/LoggerBuilder.cs @@ -6,6 +6,9 @@ namespace dotnetCampus.Logging; +/// +/// 辅助创建日志记录器的构建器。 +/// public class LoggerBuilder { private LogOptions? _options; @@ -52,7 +55,7 @@ public LoggerBuilder AddBridge(ILoggerBridgeLinker linker) return this; } - public CompositeLogger Build() + public LoggerBuilder Build() { var logger = new CompositeLogger(_options ?? new LogOptions()) { @@ -62,19 +65,46 @@ public CompositeLogger Build() { linker.Link(logger); } - return logger; + return new LoggerBuilder(logger); } +} - public CompositeLogger BuildIntoStaticLog() +/// +/// 包含已经创建完成的日志记录器,日志记录器初始状态已不可再更改,但可以继续构建以设置日志记录器的其他行为。 +/// +/// 已经创建完成的日志记录器。 +/// 日志记录器的类型。 +public sealed class LoggerBuilder(T logger) where T : ILogger +{ + /// + /// 获取创建好的日志记录器。 + /// + public T Logger => logger; + + /// + /// 将创建好的日志记录器设置为全局日志记录器。 + /// + /// + public T IntoGlobalStaticLog() { - var logger = Build(); Log.SetLogger(logger); return logger; } + + /// + /// 隐式将创建好的日志记录器转换为日志记录器实例。 + /// + /// 要转换的日志记录器构建器。 + /// 已经创建好的日志记录器。 + public static implicit operator T(LoggerBuilder builder) => builder.Logger; } partial class Log { + /// + /// 仅供内部使用,将指定的日志记录器设置为全局日志记录器。 + /// + /// 要设置为全局的日志记录器。 internal static void SetLogger(ILogger logger) { var oldLogger = Current; From 71432bc3c103011c8bf92435774f08a6cfec88f7 Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 09:45:49 +0800 Subject: [PATCH 37/49] =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E5=AF=B9=E6=8E=A5?= =?UTF-8?q?=E5=A4=9A=E4=B8=AA=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotnetCampus.Logger.sln | 15 +++++++++ ...ggerSample.LoggerIndependentProject.csproj | 17 ++++++++++ .../SourceReferenceTarget.cs | 33 +++++++++++++++++++ .../LoggerSample.MainApp.csproj | 1 + samples/LoggerSample.MainApp/Program.cs | 5 +-- 5 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 samples/LoggerSample.LoggerIndependentProject/LoggerSample.LoggerIndependentProject.csproj create mode 100644 samples/LoggerSample.LoggerIndependentProject/SourceReferenceTarget.cs diff --git a/dotnetCampus.Logger.sln b/dotnetCampus.Logger.sln index 8a6fa8c..b9f1a1e 100644 --- a/dotnetCampus.Logger.sln +++ b/dotnetCampus.Logger.sln @@ -31,6 +31,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.Logger.Tests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.Logger.Analyzer", "src\dotnetCampus.Logger.Analyzer\dotnetCampus.Logger.Analyzer.csproj", "{77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoggerSample.LoggerIndependentProject", "samples\LoggerSample.LoggerIndependentProject\LoggerSample.LoggerIndependentProject.csproj", "{E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -113,6 +115,18 @@ Global {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Release|x64.Build.0 = Release|Any CPU {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Release|x86.ActiveCfg = Release|Any CPU {77F0A6B5-6C8B-4815-8C1E-4F97C24BFB36}.Release|x86.Build.0 = Release|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Debug|x64.ActiveCfg = Debug|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Debug|x64.Build.0 = Debug|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Debug|x86.ActiveCfg = Debug|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Debug|x86.Build.0 = Debug|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Release|Any CPU.Build.0 = Release|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Release|x64.ActiveCfg = Release|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Release|x64.Build.0 = Release|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Release|x86.ActiveCfg = Release|Any CPU + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -122,6 +136,7 @@ Global {0A1ACF06-FC64-4B5C-97E9-AA2CC307C899} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} {36367E21-BABC-4CC4-891E-CEAF56D66B68} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} {D0ACB879-D49B-4ACD-9852-23D0E6D15DDB} = {36852775-3A76-49CF-98CC-3067CE54A5AD} + {E0DE93BF-AE07-4F92-A3FD-F4D661339BE0} = {34E3F27E-C7E2-45D7-8035-53C304F77E2A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D7D96521-5EB7-45B4-B70D-2B3FB69A3082} diff --git a/samples/LoggerSample.LoggerIndependentProject/LoggerSample.LoggerIndependentProject.csproj b/samples/LoggerSample.LoggerIndependentProject/LoggerSample.LoggerIndependentProject.csproj new file mode 100644 index 0000000..dc27bd5 --- /dev/null +++ b/samples/LoggerSample.LoggerIndependentProject/LoggerSample.LoggerIndependentProject.csproj @@ -0,0 +1,17 @@ + + + + + + net8.0 + enable + <_DLMainlyUseGeneratedLogger>true + + + + + + + + + diff --git a/samples/LoggerSample.LoggerIndependentProject/SourceReferenceTarget.cs b/samples/LoggerSample.LoggerIndependentProject/SourceReferenceTarget.cs new file mode 100644 index 0000000..4b2f358 --- /dev/null +++ b/samples/LoggerSample.LoggerIndependentProject/SourceReferenceTarget.cs @@ -0,0 +1,33 @@ +namespace LoggerSample.LoggerIndependentProject; + +public class SourceReferenceTarget +{ + public static void CollectLogs() + { + Log.Trace.Trace("[SourceReference] Log.Trace.Trace"); + Log.Trace.Debug("[SourceReference] Log.Trace.Debug"); + Log.Trace.Info("[SourceReference] Log.Trace.Info"); + Log.Trace.Warn("[SourceReference] Log.Trace.Warn"); + Log.Trace.Error("[SourceReference] Log.Trace.Error"); + Log.Trace.Fatal("[SourceReference] Log.Trace.Fatal"); + + Log.Debug.Trace("[SourceReference] Log.Debug.Trace"); + Log.Debug.Debug("[SourceReference] Log.Debug.Debug"); + Log.Debug.Info("[SourceReference] Log.Debug.Info"); + Log.Debug.Warn("[SourceReference] Log.Debug.Warn"); + Log.Debug.Error("[SourceReference] Log.Debug.Error"); + Log.Debug.Fatal("[SourceReference] Log.Debug.Fatal"); + + Log.Info("[SourceReference] Log.Info"); + Log.Warn("[SourceReference] Log.Warn"); + Log.Error("[SourceReference] Log.Error"); + Log.Fatal("[SourceReference] Log.Fatal"); + + Log.Current.Trace("[SourceReference] Log.Current.Trace"); + Log.Current.Debug("[SourceReference] Log.Current.Debug"); + Log.Current.Info("[SourceReference] Log.Current.Info"); + Log.Current.Warn("[SourceReference] Log.Current.Warn"); + Log.Current.Error("[SourceReference] Log.Current.Error"); + Log.Current.Fatal("[SourceReference] Log.Current.Fatal"); + } +} diff --git a/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj b/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj index 8a24e56..b18f10b 100644 --- a/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj +++ b/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj @@ -13,6 +13,7 @@ + diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs index 094491e..ad23759 100644 --- a/samples/LoggerSample.MainApp/Program.cs +++ b/samples/LoggerSample.MainApp/Program.cs @@ -24,11 +24,12 @@ public static void Main(string[] args) // IncludeScopes = true, // }, }) - .AddBridge(LoggerBridge.Default) + .AddBridge(LoggerBridgeLinker.Default) .Build() .IntoGlobalStaticLog(); } } [ImportLoggerBridge] -internal partial class LoggerBridge; +[ImportLoggerBridge] +internal partial class LoggerBridgeLinker; From 9ebdd81583445c9b9615a9bc3303fa3a0fcae7b6 Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 09:46:47 +0800 Subject: [PATCH 38/49] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=AF=E5=8A=A0?= =?UTF-8?q?=E7=9A=84=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dotnetCampus.Logger.Analyzer.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 7dc5864..c8021a0 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -30,8 +30,4 @@ - - - - From a2713a11faa0a5faf2c808c4206328f4e6b321dc Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 10:00:43 +0800 Subject: [PATCH 39/49] =?UTF-8?q?=E4=B8=BA=20Program=20=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=BE=85=E5=8A=A9=E6=96=B9=E6=B3=95=EF=BC=88?= =?UTF-8?q?=E6=97=A0=E9=9C=80=E5=BC=95=E7=94=A8=E5=BA=93=E7=9A=84=E9=82=A3?= =?UTF-8?q?=E7=A7=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- samples/LoggerSample.MainApp/Program.cs | 5 +++- .../Assets/Templates/Program.g.cs | 18 +++++++++++++++ .../Assets/Templates/Program.logger.g.cs | 23 ------------------- .../Generators/ProgramMainLogGenerator.cs | 13 ++++------- 4 files changed, 27 insertions(+), 32 deletions(-) create mode 100644 src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.g.cs delete mode 100644 src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs index ad23759..2740a83 100644 --- a/samples/LoggerSample.MainApp/Program.cs +++ b/samples/LoggerSample.MainApp/Program.cs @@ -1,14 +1,16 @@ using dotnetCampus.Logging.Attributes; using dotnetCampus.Logging.Configurations; using dotnetCampus.Logging.Writers; +using LoggerSample.MainApp.Logging; namespace LoggerSample.MainApp; -internal class Program +internal partial class Program { public static void Main(string[] args) { // 这里是 Main 方法入口。 + Log.Info($"[App] App started with args: {string.Join(", ", args)}"); // 以下初始化代码可能会较晚执行。 new LoggerBuilder() @@ -32,4 +34,5 @@ public static void Main(string[] args) [ImportLoggerBridge] [ImportLoggerBridge] +[ImportLoggerBridge] internal partial class LoggerBridgeLinker; diff --git a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.g.cs b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.g.cs new file mode 100644 index 0000000..1d9d9e1 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.g.cs @@ -0,0 +1,18 @@ +#nullable enable + +using LILogger = global::dotnetCampus.Logging.ILogger; +using LLog = global::dotnetCampus.Logging.Log; + +namespace dotnetCampus.Logger.Assets.Templates; + +partial class Program +{ + /// + /// 用于在 类的内部记录日志。 + /// + /// + /// 由于此代码是源生成器生成的代码,所以可以在日志模块初始化之前记录日志且提前生效。
+ /// 🤩 你甚至能在 Main 方法的第一行就使用它记录日志! + ///
+ private static LILogger Log => LLog.Current; +} diff --git a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs b/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs deleted file mode 100644 index 99485e4..0000000 --- a/src/dotnetCampus.Logger.Analyzer/Assets/Templates/Program.logger.g.cs +++ /dev/null @@ -1,23 +0,0 @@ -#nullable enable - -using EditorBrowsable = global::System.ComponentModel.EditorBrowsableAttribute; -using EditorBrowsableState = global::System.ComponentModel.EditorBrowsableState; -using EventId = global::dotnetCampus.Logging.EventId; -using Exception = global::System.Exception; -using ILogger = global::dotnetCampus.Logging.ILogger; -using LazyThreadSafetyMode = global::System.Threading.LazyThreadSafetyMode; -using LogLevel = global::dotnetCampus.Logging.LogLevel; - -namespace dotnetCampus.Logger.Assets.Templates; - -partial class Program -{ - /// - /// 用于在 类的内部记录日志。 - /// - /// - /// 由于此代码是源生成器生成的代码,所以可以在日志模块初始化之前记录日志且提前生效。
- /// 🤩 你甚至能在 Main 方法的第一行就使用它记录日志! - ///
- private static ILogger Log { get; } -} diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs index c5ef00c..ed51799 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/ProgramMainLogGenerator.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Text; +using System.Text; using dotnetCampus.Logger.Assets.Templates; -using dotnetCampus.Logger.Utils.IO; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -15,7 +11,7 @@ namespace dotnetCampus.Logger.Generators; /// /// 生成 Program.g.cs,为 Main 方法第一行日志生成支持代码。 /// -// [Generator] +[Generator] public class ProgramMainLogGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) @@ -71,7 +67,8 @@ private string ConvertPartialProgramLogger(string sourceText, INamedTypeSymbol p var templateProgramNamespace = typeof(Program).Namespace!; var generatedProgramNamespace = programTypeSymbol.ContainingNamespace.ToDisplayString(); return sourceText - .Replace(templateProgramNamespace, generatedProgramNamespace) - .Replace("Program", programTypeSymbol.Name); + .Replace("global::dotnetCampus.Logging.", $"global::{generatedProgramNamespace}.Logging.") + .Replace($"namespace {templateProgramNamespace};", $"namespace {generatedProgramNamespace};") + .Replace("partial class Program", $"partial class {programTypeSymbol.Name}"); } } From ac90f165ae2d2b382f4d71e83a0eb3cf8d06d6a3 Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 13:05:29 +0800 Subject: [PATCH 40/49] =?UTF-8?q?=E9=87=8D=E6=96=B0=E5=BC=80=E6=94=BE=20Pr?= =?UTF-8?q?ogram=20=E7=B1=BB=E7=9A=84=20partial=20=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- samples/LoggerSample.MainApp/Program.cs | 2 +- .../PartialProgramCodeFixProvider.cs | 2 +- .../DiagnosticAnalyzers/PartialProgramAnalyzer.cs | 2 +- src/dotnetCampus.Logger.Analyzer/Diagnostics.cs | 9 +++++++-- .../Properties/Localizations.resx | 2 +- .../Properties/Localizations.zh-hans.resx | 2 +- .../Properties/Localizations.zh-hant.resx | 2 +- .../Properties/copilot.md | 11 +++++------ 8 files changed, 18 insertions(+), 14 deletions(-) diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs index 2740a83..d7820ed 100644 --- a/samples/LoggerSample.MainApp/Program.cs +++ b/samples/LoggerSample.MainApp/Program.cs @@ -5,7 +5,7 @@ namespace LoggerSample.MainApp; -internal partial class Program +internal class Program { public static void Main(string[] args) { diff --git a/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs index 31b1261..ccf5a24 100644 --- a/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs +++ b/src/dotnetCampus.Logger.Analyzer/CodeFixeProviders/PartialProgramCodeFixProvider.cs @@ -10,7 +10,7 @@ namespace dotnetCampus.Logger.CodeFixeProviders; -// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] +[ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class PartialProgramCodeFixProvider : CodeFixProvider { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( diff --git a/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs index f60b6ab..17b3638 100644 --- a/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs +++ b/src/dotnetCampus.Logger.Analyzer/DiagnosticAnalyzers/PartialProgramAnalyzer.cs @@ -19,7 +19,7 @@ public override void Initialize(AnalysisContext context) context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - // context.RegisterSyntaxNodeAction(AnalyzeProgram, SyntaxKind.MethodDeclaration); + context.RegisterSyntaxNodeAction(AnalyzeProgram, SyntaxKind.MethodDeclaration); } private void AnalyzeProgram(SyntaxNodeAnalysisContext context) diff --git a/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs b/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs index d006614..fa9d259 100644 --- a/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs +++ b/src/dotnetCampus.Logger.Analyzer/Diagnostics.cs @@ -22,8 +22,8 @@ public class Diagnostics nameof(DL1001), Localize(nameof(DL1001)), Localize(nameof(DL1001_Message)), - Categories.Mechanism, - DiagnosticSeverity.Warning, + Categories.Performance, + DiagnosticSeverity.Info, true, description: Localize(DL1001_Description)); @@ -54,6 +54,11 @@ private static class Categories ///
public const string Readable = "dotnetCampus.Readable"; + /// + /// 为了提升性能,或避免性能问题,则报告此诊断。 + /// + public const string Performance = "dotnetCampus.Performance"; + /// /// 能写得出来正常编译,但会引发运行时异常,则报告此诊断。 /// diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx index 9efb6fd..593461c 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.resx @@ -39,6 +39,6 @@ After changing the entry class to a partial class, the source generator can generate auxiliary log code according to the subsequent log initialization requirements; this way, you can even start using logs in the first sentence of the Main method without worrying about initialization issues. - Change '{0}' to a partial class + [Logger/Performance] Set '{0}' to partial diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx index 5b15a53..7fdf608 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hans.resx @@ -31,6 +31,6 @@ 将入口类改为分部类后,源生成器可以根据后续日志初始化的要求生成辅助日志代码;这样,你甚至可以在 Main 方法第一句化就开始使用日志而无需担心初始化问题。 - 将 '{0}' 设为 partial + [日志/性能] 将 '{0}' 设为 partial diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx index 62fa0fa..feefd4a 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx +++ b/src/dotnetCampus.Logger.Analyzer/Properties/Localizations.zh-hant.resx @@ -31,6 +31,6 @@ 將入口類改為分部類後,源生成器可以根據後續日誌初始化的要求生成輔助日誌代碼;這樣,你甚至可以在 Main 方法第一句化就開始使用日誌而無需擔心初始化問題。 - 將 '{0}' 設為 partial + [日誌/性能] 將 '{0}' 設為 partial diff --git a/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md b/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md index a3e18b2..e0eb565 100644 --- a/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md +++ b/src/dotnetCampus.Logger.Analyzer/Properties/copilot.md @@ -6,9 +6,8 @@ 请将以下 C# 分析器的 Id 进行翻译。 -| Id | zh-hans | zh-hant | en | -|----------------|-----------------------------------|-----------------------------------|---------------------------------------------------------------------------------------------------------------| -| DL0000 | 未知错误 | 未知錯誤 | Unknown Error | -| DL0000_Message | 发生了未知错误。 | 發生了未知錯誤。 | An unknown error occurred. | -| DL1001 | 修改入口类为分部类 | 修改入口類為分部類 | Change the entry class to a partial class | -| DL1001_Message | 将 {0} 改为分部类,以允许其在日志模块初始化之前使用日志系统。 | 將 {0} 改為分部類,以允許其在日誌模塊初始化之前使用日誌系統。 | Change {0} to a partial class to allow it to use the logging system before the logging module is initialized. | +| Id | zh-hans | zh-hant | en | +|----------------|---------------------------|---------------------------|----------------------------------------| +| DL0000 | 未知错误 | 未知錯誤 | Unknown Error | +| DL0000_Message | 发生了未知错误。 | 發生了未知錯誤。 | An unknown error occurred. | +| DL1001 | [日志性能] 将 '{0}' 设为 partial | [日誌性能] 將 '{0}' 設為 partial | [Log Performance] Set '{0}' to partial | From e00d912474fb659b3013a4acb9b39859db9aa191 Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 13:13:24 +0800 Subject: [PATCH 41/49] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Generators/LoggerGenerator.cs | 2 +- .../dotnetCampus.Logger.Analyzer.csproj | 1 + src/dotnetCampus.Logger/Log.g.cs | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs index f31b73f..d4615f5 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs @@ -42,7 +42,7 @@ private string GenerateSource(string typeName, string rootNamespace, string sour if (typeName == "Log") { // 源生成器为单独库生成的代码中,默认日志记录器是 BridgeLogger。 - sourceText = sourceText.Replace("new NullLogger();", "new BridgeLogger();"); + sourceText = sourceText.Replace("new MemoryCacheLogger();", "new BridgeLogger();"); } var sourceSpan = sourceText.AsSpan(); diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index c8021a0..40a24c1 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -23,6 +23,7 @@ + diff --git a/src/dotnetCampus.Logger/Log.g.cs b/src/dotnetCampus.Logger/Log.g.cs index 3fae1a3..475faeb 100644 --- a/src/dotnetCampus.Logger/Log.g.cs +++ b/src/dotnetCampus.Logger/Log.g.cs @@ -1,5 +1,6 @@ #nullable enable +using global::dotnetCampus.Logging.Writers; using global::System; using global::System.Diagnostics.CodeAnalysis; @@ -14,9 +15,9 @@ public static partial class Log static Log() { - // 在全局日志中,默认日志记录器是 NullLogger。 + // 在全局日志中,默认日志记录器是 MemoryCacheLogger。 // 然而在源生成器为单独库生成的代码中,默认日志记录器是 BridgeLogger。 - Current = new NullLogger(); + Current = new MemoryCacheLogger(); } /// From 085777535d0c49100f044b8dd1b365f22966634e Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 13:32:35 +0800 Subject: [PATCH 42/49] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=E4=B8=A4=E4=B8=AA=E6=97=A9=E6=9C=9F?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dotnetCampus.Logger/Building/CompositeLogger.cs | 6 ------ src/dotnetCampus.Logger/Output/ILogWriter.cs | 6 ------ 2 files changed, 12 deletions(-) delete mode 100644 src/dotnetCampus.Logger/Building/CompositeLogger.cs delete mode 100644 src/dotnetCampus.Logger/Output/ILogWriter.cs diff --git a/src/dotnetCampus.Logger/Building/CompositeLogger.cs b/src/dotnetCampus.Logger/Building/CompositeLogger.cs deleted file mode 100644 index f56ebec..0000000 --- a/src/dotnetCampus.Logger/Building/CompositeLogger.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace dotnetCampus.Logging.Building; - -public class CompositeLogger -{ - -} diff --git a/src/dotnetCampus.Logger/Output/ILogWriter.cs b/src/dotnetCampus.Logger/Output/ILogWriter.cs deleted file mode 100644 index 7ea42b2..0000000 --- a/src/dotnetCampus.Logger/Output/ILogWriter.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace dotnetCampus.Logging.Output; - -public class ILogWriter -{ - -} From 3f956ee4cf30a3a34e32a9a5bad00fc5dd9c2607 Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 13:32:52 +0800 Subject: [PATCH 43/49] =?UTF-8?q?=E5=8A=A0=E4=B8=8A=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- samples/LoggerSample.MainApp/Program.cs | 3 +++ .../Configurations/MemoryCacheOptions.cs | 17 +++++++++++++++++ src/dotnetCampus.Logger/LoggerBuilder.cs | 12 ++++++------ 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 src/dotnetCampus.Logger/Configurations/MemoryCacheOptions.cs diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs index d7820ed..a7f15a3 100644 --- a/samples/LoggerSample.MainApp/Program.cs +++ b/samples/LoggerSample.MainApp/Program.cs @@ -14,6 +14,9 @@ public static void Main(string[] args) // 以下初始化代码可能会较晚执行。 new LoggerBuilder() + .WithMemoryCache(new MemoryCacheOptions + { + }) .WithLevel(LogLevel.Information) .WithOptions(new LogOptions { diff --git a/src/dotnetCampus.Logger/Configurations/MemoryCacheOptions.cs b/src/dotnetCampus.Logger/Configurations/MemoryCacheOptions.cs new file mode 100644 index 0000000..203877e --- /dev/null +++ b/src/dotnetCampus.Logger/Configurations/MemoryCacheOptions.cs @@ -0,0 +1,17 @@ +namespace dotnetCampus.Logging.Configurations; + +/// +/// 当日志系统尚未初始化,但有模块已经开始记录日志时,此选项指定这些日志应如何缓存。 +/// +public record MemoryCacheOptions +{ + /// + /// 缓存的日志数量上限。超过此数量的日志将被丢弃。 + /// + public int MaxCachedLogCount { get; init; } = 1024; + + /// + /// 在日志系统初始化完成前,如果应用程序崩溃退出,则会将崩溃日志写入到此文件中。 + /// + public string? EmergencyCrashLoggerFile { get; init; } +} diff --git a/src/dotnetCampus.Logger/LoggerBuilder.cs b/src/dotnetCampus.Logger/LoggerBuilder.cs index 19bafcd..45ac2ab 100644 --- a/src/dotnetCampus.Logger/LoggerBuilder.cs +++ b/src/dotnetCampus.Logger/LoggerBuilder.cs @@ -16,16 +16,16 @@ public class LoggerBuilder private readonly List _linkers = []; /// - /// 调用此方法以便在日志模块初始化完成前先对所有记录的日志进行缓存,以便在日志模块初始化完成后再将缓存的日志写入到日志文件中。 + /// 指定在日志模块完成初始化之前直接或间接调用全局 所记录的日志应被缓存到内存中。 /// - /// - /// 在日志模块初始化完成后,将缓存的日志写入到日志文件中。 - /// 如果在 Program.cs 类中,请直接传入源生成器生成的 Log 属性作为此参数。 + /// + /// 指定缓存日志的选项。 /// /// - /// 此方法不会在运行时起任何作用,仅决定编译时在 Program.cs 类中所生成的日志记录器。生成后,你可以在 Program.cs 类中使用 Log.Info 等方法进行日志记录。 + /// 此方法不会在运行时起任何作用!!!
+ /// 此方法仅在编译期决定全局日志 的行为,并且一旦编译完成,此行为将不可更改。
///
- public LoggerBuilder UseMemoryCache(Action flusher) + public LoggerBuilder WithMemoryCache(MemoryCacheOptions? options = null) { return this; } From d34c5f34d78062d0c525194e11251a040ea7e3a5 Mon Sep 17 00:00:00 2001 From: walterlv Date: Fri, 10 May 2024 13:41:55 +0800 Subject: [PATCH 44/49] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=EF=BC=8C=E5=87=8F=E5=B0=91=E7=BA=BF=E7=A8=8B=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dotnetCampus.Logger/LoggerBuilder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dotnetCampus.Logger/LoggerBuilder.cs b/src/dotnetCampus.Logger/LoggerBuilder.cs index 45ac2ab..3fcc2cb 100644 --- a/src/dotnetCampus.Logger/LoggerBuilder.cs +++ b/src/dotnetCampus.Logger/LoggerBuilder.cs @@ -84,7 +84,7 @@ public sealed class LoggerBuilder(T logger) where T : ILogger /// /// 将创建好的日志记录器设置为全局日志记录器。 /// - /// + /// 已经创建完成的日志记录器。 public T IntoGlobalStaticLog() { Log.SetLogger(logger); @@ -95,7 +95,7 @@ public T IntoGlobalStaticLog() /// 隐式将创建好的日志记录器转换为日志记录器实例。 ///
/// 要转换的日志记录器构建器。 - /// 已经创建好的日志记录器。 + /// 已经创建完成的日志记录器。 public static implicit operator T(LoggerBuilder builder) => builder.Logger; } @@ -108,10 +108,10 @@ partial class Log internal static void SetLogger(ILogger logger) { var oldLogger = Current; + Current = logger; if (oldLogger is MemoryCacheLogger mcl) { mcl.Flush(logger); } - Current = logger; } } From c67b5c8461999718c57438f25a2ca2851d7eaad6 Mon Sep 17 00:00:00 2001 From: walterlv Date: Mon, 13 May 2024 11:36:31 +0800 Subject: [PATCH 45/49] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E6=97=A5=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index c647b82..7a5f547 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -15,7 +15,7 @@ 提供统一的日志记录方法。使用源生成器允许库的作者在不依赖本日志库的情况下完成日志的记录,并且还能对接到产品中完成日志的统一输出。 dotnet-campus dotnet campus(.NET 职业技术学院) - Copyright © 2020-2024 dotnet campus, All Rights Reserved. + Copyright 2020-$([System.DateTime]::Now.ToString(`yyyy`)) © dotnet campus, All Rights Reserved. git https://github.com/dotnet-campus/dotnetCampus.Logger https://github.com/dotnet-campus/dotnetCampus.Logger From 3273299bf80cc374737b0edc43be734fbdc4b72e Mon Sep 17 00:00:00 2001 From: walterlv Date: Mon, 20 May 2024 11:11:34 +0800 Subject: [PATCH 46/49] =?UTF-8?q?=E7=A1=AE=E4=BF=9D=20MemoryCacheLogger=20?= =?UTF-8?q?=E5=8F=AF=E8=A2=AB=E7=BC=96=E8=AF=91=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/Version.props | 4 ++-- .../dotnetCampus.Logger.Analyzer.csproj | 1 - src/dotnetCampus.Logger/Log.g.cs | 1 - src/dotnetCampus.Logger/Properties/GlobalUsings.cs | 1 + .../Writers/{MemoryCacheLogger.cs => MemoryCacheLogger.g.cs} | 4 +++- 5 files changed, 6 insertions(+), 5 deletions(-) rename src/dotnetCampus.Logger/Writers/{MemoryCacheLogger.cs => MemoryCacheLogger.g.cs} (98%) diff --git a/build/Version.props b/build/Version.props index 4cbacab..f1d2d98 100644 --- a/build/Version.props +++ b/build/Version.props @@ -1,5 +1,5 @@ - 0.1.0-alpha + 0.1.0-alpha01 - \ No newline at end of file + diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index 40a24c1..c8021a0 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -23,7 +23,6 @@ - diff --git a/src/dotnetCampus.Logger/Log.g.cs b/src/dotnetCampus.Logger/Log.g.cs index 475faeb..4432412 100644 --- a/src/dotnetCampus.Logger/Log.g.cs +++ b/src/dotnetCampus.Logger/Log.g.cs @@ -1,6 +1,5 @@ #nullable enable -using global::dotnetCampus.Logging.Writers; using global::System; using global::System.Diagnostics.CodeAnalysis; diff --git a/src/dotnetCampus.Logger/Properties/GlobalUsings.cs b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs index 4904880..ee32b80 100644 --- a/src/dotnetCampus.Logger/Properties/GlobalUsings.cs +++ b/src/dotnetCampus.Logger/Properties/GlobalUsings.cs @@ -1,3 +1,4 @@ global using DebugLogger = dotnetCampus.Logging.Writers.DebugLogger; +global using MemoryCacheLogger = dotnetCampus.Logging.Writers.MemoryCacheLogger; global using NullLogger = dotnetCampus.Logging.Writers.NullLogger; global using TraceLogger = dotnetCampus.Logging.Writers.TraceLogger; diff --git a/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs b/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.g.cs similarity index 98% rename from src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs rename to src/dotnetCampus.Logger/Writers/MemoryCacheLogger.g.cs index 9968de9..59d5205 100644 --- a/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.cs +++ b/src/dotnetCampus.Logger/Writers/MemoryCacheLogger.g.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; namespace dotnetCampus.Logging.Writers; From d23b6505892efe8e56aa267a2a2c698fb0920c67 Mon Sep 17 00:00:00 2001 From: walterlv Date: Mon, 20 May 2024 17:17:32 +0800 Subject: [PATCH 47/49] =?UTF-8?q?=E5=8A=A0=E5=85=A5=20MSBuild=20API=20?= =?UTF-8?q?=E4=BB=A5=E5=88=86=E5=88=AB=E4=BD=BF=E7=94=A8=E6=BA=90=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E5=92=8C=E5=BA=93=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...LoggerSample.LoggerDependentLibrary.csproj | 1 - ...ggerSample.LoggerIndependentLibrary.csproj | 2 +- .../SourceReferenceTarget.cs | 4 +- ...ggerSample.LoggerIndependentProject.csproj | 2 +- .../SourceReferenceTarget.cs | 4 +- .../LoggerSample.MainApp.csproj | 4 +- samples/LoggerSample.MainApp/Program.cs | 2 - .../Generators/GlobalUsingsGenerator.cs | 30 ++++--- .../Generators/LoggerGenerator.cs | 13 ++- .../AnalyzerConfigOptionsExtensions.cs | 87 +++++++++++++++++++ .../Properties/Package/build/Package.props | 21 +++-- .../Properties/Package/build/Package.targets | 51 ++++++++++- 12 files changed, 190 insertions(+), 31 deletions(-) create mode 100644 src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/AnalyzerConfigOptionsExtensions.cs diff --git a/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj index 2db8c72..8b1387f 100644 --- a/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj +++ b/samples/LoggerSample.LoggerDependentLibrary/LoggerSample.LoggerDependentLibrary.csproj @@ -5,7 +5,6 @@ net8.0 enable - <_DLMainlyUseGeneratedLogger>false diff --git a/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj index dc27bd5..2410b13 100644 --- a/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj +++ b/samples/LoggerSample.LoggerIndependentLibrary/LoggerSample.LoggerIndependentLibrary.csproj @@ -5,7 +5,7 @@ net8.0 enable - <_DLMainlyUseGeneratedLogger>true + true diff --git a/samples/LoggerSample.LoggerIndependentLibrary/SourceReferenceTarget.cs b/samples/LoggerSample.LoggerIndependentLibrary/SourceReferenceTarget.cs index 7e8e077..72923f2 100644 --- a/samples/LoggerSample.LoggerIndependentLibrary/SourceReferenceTarget.cs +++ b/samples/LoggerSample.LoggerIndependentLibrary/SourceReferenceTarget.cs @@ -1,4 +1,6 @@ -namespace LoggerSample.LoggerIndependentLibrary; +using LoggerSample.LoggerIndependentLibrary.Logging; + +namespace LoggerSample.LoggerIndependentLibrary; public static class SourceReferenceTarget { diff --git a/samples/LoggerSample.LoggerIndependentProject/LoggerSample.LoggerIndependentProject.csproj b/samples/LoggerSample.LoggerIndependentProject/LoggerSample.LoggerIndependentProject.csproj index dc27bd5..2410b13 100644 --- a/samples/LoggerSample.LoggerIndependentProject/LoggerSample.LoggerIndependentProject.csproj +++ b/samples/LoggerSample.LoggerIndependentProject/LoggerSample.LoggerIndependentProject.csproj @@ -5,7 +5,7 @@ net8.0 enable - <_DLMainlyUseGeneratedLogger>true + true diff --git a/samples/LoggerSample.LoggerIndependentProject/SourceReferenceTarget.cs b/samples/LoggerSample.LoggerIndependentProject/SourceReferenceTarget.cs index 4b2f358..4536fa0 100644 --- a/samples/LoggerSample.LoggerIndependentProject/SourceReferenceTarget.cs +++ b/samples/LoggerSample.LoggerIndependentProject/SourceReferenceTarget.cs @@ -1,4 +1,6 @@ -namespace LoggerSample.LoggerIndependentProject; +using LoggerSample.LoggerIndependentProject.Logging; + +namespace LoggerSample.LoggerIndependentProject; public class SourceReferenceTarget { diff --git a/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj b/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj index b18f10b..c34714d 100644 --- a/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj +++ b/samples/LoggerSample.MainApp/LoggerSample.MainApp.csproj @@ -5,12 +5,12 @@ WinExe net8.0 - <_DLMainlyUseGeneratedLogger>false + preferReference - + diff --git a/samples/LoggerSample.MainApp/Program.cs b/samples/LoggerSample.MainApp/Program.cs index a7f15a3..d3684d8 100644 --- a/samples/LoggerSample.MainApp/Program.cs +++ b/samples/LoggerSample.MainApp/Program.cs @@ -1,7 +1,6 @@ using dotnetCampus.Logging.Attributes; using dotnetCampus.Logging.Configurations; using dotnetCampus.Logging.Writers; -using LoggerSample.MainApp.Logging; namespace LoggerSample.MainApp; @@ -37,5 +36,4 @@ public static void Main(string[] args) [ImportLoggerBridge] [ImportLoggerBridge] -[ImportLoggerBridge] internal partial class LoggerBridgeLinker; diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs index 75b274e..e2b512e 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/GlobalUsingsGenerator.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Immutable; -using System.Diagnostics; +using System.Collections.Immutable; using System.Linq; using System.Text; using dotnetCampus.Logger.Utils.CodeAnalysis; @@ -24,19 +22,26 @@ public void Initialize(IncrementalGeneratorInitializationContext context) private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvider provider) { - provider.GlobalOptions.TryGetValue("build_property.OutputType", out var outputType); - provider.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace); - provider.GlobalOptions.TryGetValue("build_property._DLMainlyUseGeneratedLogger", out var mainlyUseGeneratedLogger); - if (outputType is null || rootNamespace is null || mainlyUseGeneratedLogger is null) + if (provider.GlobalOptions + .TryGetValue("_DLRootNamespace", out var rootNamespace) + .TryGetValue("_DLGenerateSource", out var generateSource) + .TryGetValue("_DLGenerateGlobalUsings", out var generateGlobalUsings) + .TryGetValue("_DLPreferGeneratedSource", out var preferGeneratedSource) + is var result + && !result) { - context.ReportUnknownError("NuGet 包中应包含 OutputType、RootNamespace 和 _DLMainlyUseGeneratedLogger 属性。"); + // 此项目是通过依赖间接引用的,没有 build 因此无法在源生成器中使用编译属性,所以只能选择引用。 return; } - var useGeneratedLogger = mainlyUseGeneratedLogger.Equals("true", StringComparison.OrdinalIgnoreCase); - var generatedCode = useGeneratedLogger - ? GenerateGlobalUsings(rootNamespace, useGeneratedLogger) - : GenerateGlobalUsings("dotnetCampus", useGeneratedLogger); + if (!generateSource || !generateGlobalUsings) + { + return; + } + + var generatedCode = preferGeneratedSource + ? GenerateGlobalUsings(rootNamespace, preferGeneratedSource) + : GenerateGlobalUsings("dotnetCampus", preferGeneratedSource); context.AddSource("GlobalUsings.g.cs", SourceText.From(generatedCode, Encoding.UTF8)); } @@ -66,7 +71,6 @@ private string GenerateGlobalUsingsForTypes(string rootNamespace, ImmutableArray if ( // 如果使用源生成器的日志系统,则所有类型均要导出全局引用。 useGeneratedLogger - // 如果使用源生成器的日志系统,则所有类型均要导出全局引用。 || sourceFile.Namespace.EndsWith("Sources") ) { diff --git a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs index d4615f5..36460f0 100644 --- a/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs +++ b/src/dotnetCampus.Logger.Analyzer/Generators/LoggerGenerator.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using System.Text; using System.Text.RegularExpressions; +using dotnetCampus.Logger.Utils.CodeAnalysis; using dotnetCampus.Logger.Utils.IO; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -23,7 +24,17 @@ public void Initialize(IncrementalGeneratorInitializationContext context) private void Execute(SourceProductionContext context, AnalyzerConfigOptionsProvider provider) { - if (!provider.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace)) + if (provider.GlobalOptions + .TryGetValue("_DLRootNamespace", out var rootNamespace) + .TryGetValue("_DLGenerateSource", out var isGenerateSource) + is var result + && !result) + { + // 此项目是通过依赖间接引用的,没有 build 因此无法在源生成器中使用编译属性,所以只能选择引用。 + return; + } + + if (!isGenerateSource) { return; } diff --git a/src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/AnalyzerConfigOptionsExtensions.cs b/src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/AnalyzerConfigOptionsExtensions.cs new file mode 100644 index 0000000..1ec7b40 --- /dev/null +++ b/src/dotnetCampus.Logger.Analyzer/Utils/CodeAnalysis/AnalyzerConfigOptionsExtensions.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace dotnetCampus.Logger.Utils.CodeAnalysis; + +internal static class AnalyzerConfigOptionsExtensions +{ + public static AnalyzerConfigOptionResult TryGetValue( + this AnalyzerConfigOptions options, + string key, + out T value) + where T : notnull + { + if (options.TryGetValue($"build_property.{key}", out var stringValue)) + { + value = ConvertFromString(stringValue); + return new AnalyzerConfigOptionResult(options, true) + { + UnsetPropertyNames = [], + }; + } + + value = default!; + return new AnalyzerConfigOptionResult(options, false) + { + UnsetPropertyNames = [key], + }; + } + + public static AnalyzerConfigOptionResult TryGetValue( + this AnalyzerConfigOptionResult builder, + string key, + out T value) + where T : notnull + { + var options = builder.Options; + + if (options.TryGetValue($"build_property.{key}", out var stringValue)) + { + value = ConvertFromString(stringValue); + return builder.Link(true, key); + } + + value = default!; + return builder.Link(false, key); + } + + private static T ConvertFromString(string value) + { + if (typeof(T) == typeof(string)) + { + return (T)(object)value; + } + if (typeof(T) == typeof(bool)) + { + return (T)(object)value.Equals("true", StringComparison.OrdinalIgnoreCase); + } + return default!; + } +} + +public readonly record struct AnalyzerConfigOptionResult(AnalyzerConfigOptions Options, bool GotValue) +{ + public required ImmutableList UnsetPropertyNames { get; init; } + + public AnalyzerConfigOptionResult Link(bool result, string propertyName) + { + if (result) + { + return this; + } + + if (propertyName is null) + { + throw new ArgumentNullException(nameof(propertyName), @"The property name must be specified if the result is false."); + } + + return this with + { + GotValue = false, + UnsetPropertyNames = UnsetPropertyNames.Add(propertyName), + }; + } + + public static implicit operator bool(AnalyzerConfigOptionResult result) => result.GotValue; +} diff --git a/src/dotnetCampus.Logger/Properties/Package/build/Package.props b/src/dotnetCampus.Logger/Properties/Package/build/Package.props index 3e9cfa9..33e458d 100644 --- a/src/dotnetCampus.Logger/Properties/Package/build/Package.props +++ b/src/dotnetCampus.Logger/Properties/Package/build/Package.props @@ -4,10 +4,21 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - + + + + + diff --git a/src/dotnetCampus.Logger/Properties/Package/build/Package.targets b/src/dotnetCampus.Logger/Properties/Package/build/Package.targets index 2a50c46..e830c51 100644 --- a/src/dotnetCampus.Logger/Properties/Package/build/Package.targets +++ b/src/dotnetCampus.Logger/Properties/Package/build/Package.targets @@ -4,9 +4,54 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - <_DLMainlyUseGeneratedLogger Condition="$(OutputType) != 'Exe' and $(OutputType) != 'WinExe'">true + + + onlyReference + + + <_DLGenerateSource>true + <_DLGenerateGlobalUsings>false + <_DLPreferGeneratedSource>true + + + + + + + + + + <_DLGenerateSource>true + <_DLGenerateGlobalUsings>true + <_DLPreferGeneratedSource>true + + + + + <_DLGenerateSource>true + <_DLGenerateGlobalUsings>true + <_DLPreferGeneratedSource>false + + + + + <_DLGenerateSource Condition=" '$(_DLGenerateSource)' == '' ">false + <_DLGenerateGlobalUsings Condition=" '$(_DLGenerateGlobalUsings)' == '' ">false + <_DLPreferGeneratedSource Condition=" '$(_DLPreferGeneratedSource)' == '' ">false + + + + <_DLRootNamespace>$(RootNamespace) + <_DLRootNamespace Condition=" '$(_DLRootNamespace)' == '' ">$(MSBuildProjectName.Replace(" ", "_")) + + + + + + + + + From d5f0cfe29dfdf38eb276c28f0095a2dc6aa80667 Mon Sep 17 00:00:00 2001 From: walterlv Date: Tue, 21 May 2024 10:41:39 +0800 Subject: [PATCH 48/49] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20dotnet=20=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/nuget-tag-publish.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nuget-tag-publish.yml b/.github/workflows/nuget-tag-publish.yml index b4d2b1f..ec29c6c 100644 --- a/.github/workflows/nuget-tag-publish.yml +++ b/.github/workflows/nuget-tag-publish.yml @@ -1,6 +1,6 @@ name: publish nuget -on: +on: push: tags: - '*' @@ -12,16 +12,16 @@ jobs: steps: - uses: actions/checkout@v1 - + - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.1.300 + dotnet-version: 8.0.300 - name: Install dotnet tool run: dotnet tool install -g dotnetCampus.TagToVersion - - name: Set tag to version + - name: Set tag to version run: dotnet TagToVersion -t ${{ github.ref }} - name: Build with dotnet From 9df7d9b5c4e8a2ec8ffb97a1918b47263947fa45 Mon Sep 17 00:00:00 2001 From: walterlv Date: Tue, 21 May 2024 10:46:32 +0800 Subject: [PATCH 49/49] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E7=89=B9=E6=80=A7=E5=BA=93=E4=BB=A5=E8=A7=A3=E5=86=B3=E9=83=A8?= =?UTF-8?q?=E5=88=86=E7=B1=BB=E4=B8=8D=E7=94=9F=E6=88=90=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dotnetCampus.Logger.Analyzer.csproj | 2 +- src/dotnetCampus.Logger/dotnetCampus.Logger.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj index c8021a0..93f27ca 100644 --- a/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj +++ b/src/dotnetCampus.Logger.Analyzer/dotnetCampus.Logger.Analyzer.csproj @@ -9,7 +9,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj index ba8cb85..312f5e4 100644 --- a/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj +++ b/src/dotnetCampus.Logger/dotnetCampus.Logger.csproj @@ -37,7 +37,7 @@ - +