{"id":17182360,"url":"https://github.com/simoncropp/cymbal","last_synced_at":"2025-04-04T19:13:04.986Z","repository":{"id":41556114,"uuid":"510204138","full_name":"SimonCropp/Cymbal","owner":"SimonCropp","description":"An MSBuild Task to enable exception line numbers for references in a deployed app","archived":false,"fork":false,"pushed_at":"2024-10-28T09:38:30.000Z","size":725,"stargazers_count":76,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-10-29T15:59:48.891Z","etag":null,"topics":["dotnet","nuget"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SimonCropp.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"License.txt","code_of_conduct":"code_of_conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"SimonCropp"}},"created_at":"2022-07-04T03:52:48.000Z","updated_at":"2024-10-28T09:38:33.000Z","dependencies_parsed_at":"2023-02-18T05:46:09.452Z","dependency_job_id":"652a26b4-9bc6-4b47-88a3-5f5efffc78d8","html_url":"https://github.com/SimonCropp/Cymbal","commit_stats":{"total_commits":490,"total_committers":5,"mean_commits":98.0,"dds":"0.41836734693877553","last_synced_commit":"33b1cbe145124a352c0b5c02a150aebd335ee688"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FCymbal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FCymbal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FCymbal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FCymbal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SimonCropp","download_url":"https://codeload.github.com/SimonCropp/Cymbal/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247234923,"owners_count":20905854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dotnet","nuget"],"created_at":"2024-10-15T00:36:51.806Z","updated_at":"2025-04-04T19:13:04.950Z","avatar_url":"https://github.com/SimonCropp.png","language":"C#","funding_links":["https://github.com/sponsors/SimonCropp"],"categories":[],"sub_categories":[],"readme":"# \u003cimg src='/src/icon.png' height='30px'\u003e Cymbal\n\n[![Build status](https://ci.appveyor.com/api/projects/status/gd7jvcs0nv8pawc8/branch/main?svg=true)](https://ci.appveyor.com/project/SimonCropp/cymbal)\n[![NuGet Status](https://img.shields.io/nuget/v/Cymbal.svg)](https://www.nuget.org/packages/Cymbal/)\n\nCymbal is an MSBuild task that enables bundling dotnet symbols for references with a deployed app. The goal being to enable line numbers for exceptions in a production system.\n\n**See [Milestones](../../milestones?state=closed) for release notes.**\n\n\n## How symbols work in .net\n\nWhen an exception occurs, the runtime uses the symbols to correlate the each code point in the stack trace with a line number and file path that the code was built from. Without symbols no line numbers or file paths exist in the stack trace. This make working out what was the cause of the exception\n\nThere are three approaches to managing symbols in .net:\n\n\n### 1. [Embedded inside the assembly](https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview?tabs=cli#include-pdb-files-inside-the-bundle).\n\nThis works in the same way development time and in the deployed app. With the side effect of an assemblies size increases by 20-30%. It does not effect startup time of apps as the symbols are only loaded interrogated when an exception occurs.\n\n\n### 2. Shipping a pdb file\n\nThis works by having a pdb named the same as an assembly and co-located in the same directory. When an exception occurs the runtime will use that convention to look for the symbols.\n\nThere are some known problems with this approach: [1458](https://github.com/dotnet/sdk/issues/1458) and [38322](https://github.com/dotnet/sdk/issues/38322).\n\n\n### 3. [Shipping a symbols nuget package](https://learn.microsoft.com/en-us/nuget/create-packages/symbol-packages-snupkg)\n\nThis is a specialized nuget package that is shipped to a symbol server. When an exception occurs, the symbols package can download to augment the stack trace. At development time this is handled by the IDE and debugger. In a deployed app this is problematic since the app would need to download the symbols package. Instead the debug experience is usually done by a developer getting the stack trace (with no symbol information) and then, using the known assembly versions of the deployed app, augment the stack trace.\n\n\n## Cymbal performs two operations\n\nCymbal targets last two scenarios (pdb files, and symbol packages) to ensure that symbol information is available to a deployed app.\n\n\n### 1. Copies symbols from references\n\nWorks around the following bugs that cause pdb not to be copied to the output directory:\n\n * [New project system doesn't copy PDBs from packages](https://github.com/dotnet/sdk/issues/1458)\n * [CopyDebugSymbolFilesFromPackages does not copy pdbs from runtime dir](https://github.com/dotnet/sdk/issues/38322)\n\nThis is done via manipulating `ReferenceCopyLocalPaths`:\n\n\u003c!-- snippet: IncludeSymbolFromReferences --\u003e\n\u003ca id='snippet-IncludeSymbolFromReferences'\u003e\u003c/a\u003e\n```targets\n\u003cTarget Name=\"IncludeSymbolFromReferences\"\n        AfterTargets=\"ResolveAssemblyReferences\"\n        Condition=\"@(ReferenceCopyLocalPaths) != ''\"\u003e\n  \u003cItemGroup\u003e\n    \u003cPdbFilesToAdd\n            Include=\"%(ReferenceCopyLocalPaths.RelativeDir)%(ReferenceCopyLocalPaths.Filename).pdb\"\n            DestinationSubDirectory=\"%(ReferenceCopyLocalPaths.DestinationSubDirectory)\" /\u003e\n    \u003cPdbFilesToAdd Remove=\"@(PdbFilesToAdd)\"\n                   Condition=\"!Exists('%(FullPath)')\" /\u003e\n    \u003cReferenceCopyLocalPaths Include=\"@(PdbFilesToAdd)\" /\u003e\n  \u003c/ItemGroup\u003e\n\u003c/Target\u003e\n```\n\u003csup\u003e\u003ca href='/src/Cymbal/build/Cymbal.targets#L19-L32' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-IncludeSymbolFromReferences' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nThis is done at Build time.\n\n\n### 2. Runs dotnet-symbol\n\nOn a [dotnet-publish](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish) any missing symbols are attempted to be downloaded via the [dotnet-symbol tool](https://www.nuget.org/packages/dotnet-symbol) ([Source](https://github.com/dotnet/symstore)).\n\nThis is done at Publish time.\n\n\n## Usage\n\nInstall the NuGet package in the top level project. i.e. the project that 'dotnet publish' is called on\n\nhttps://nuget.org/packages/Cymbal/\n\n```\nInstall-Package Cymbal\n```\n\n\n## Outcome\n\nGiven an exe project with a single package reference to `Microsoft.Data.SqlClient`, the output on disk is 39 files:\n\n```\n│   Azure.Core.dll\n│   Azure.Identity.dll\n│   Microsoft.Bcl.AsyncInterfaces.dll\n│   Microsoft.Data.SqlClient.dll\n│   Microsoft.Identity.Client.dll\n│   Microsoft.Identity.Client.Extensions.Msal.dll\n│   Microsoft.IdentityModel.Abstractions.dll\n│   Microsoft.IdentityModel.JsonWebTokens.dll\n│   Microsoft.IdentityModel.Logging.dll\n│   Microsoft.IdentityModel.Protocols.dll\n│   Microsoft.IdentityModel.Protocols.OpenIdConnect.dll\n│   Microsoft.IdentityModel.Tokens.dll\n│   Microsoft.SqlServer.Server.dll\n│   Microsoft.Win32.SystemEvents.dll\n│   SampleApp.deps.json\n│   SampleApp.dll\n│   SampleApp.exe\n│   SampleApp.pdb\n│   SampleApp.runtimeconfig.json\n│   System.Configuration.ConfigurationManager.dll\n│   System.Drawing.Common.dll\n│   System.IdentityModel.Tokens.Jwt.dll\n│   System.Memory.Data.dll\n│   System.Runtime.Caching.dll\n│   System.Security.Cryptography.ProtectedData.dll\n│   System.Security.Permissions.dll\n│   System.Windows.Extensions.dll\n└───runtimes\n    ├───unix/lib/net6.0\n    │      Microsoft.Data.SqlClient.dll\n    │      System.Drawing.Common.dll\n    ├───win/lib/net6.0\n    │      Microsoft.Data.SqlClient.dll\n    │      Microsoft.Win32.SystemEvents.dll\n    │      System.Drawing.Common.dll\n    │      System.Runtime.Caching.dll\n    │      System.Security.Cryptography.ProtectedData.dll\n    │      System.Windows.Extensions.dll\n    ├───win-arm/native\n    │      Microsoft.Data.SqlClient.SNI.dll\n    ├───win-arm64/native\n    │      Microsoft.Data.SqlClient.SNI.dll\n    ├───win-x64/native\n    │      Microsoft.Data.SqlClient.SNI.dll\n    └───win-x86/native\n           Microsoft.Data.SqlClient.SNI.dll\n```\n\nWith the addition of Cymbal, the output on disk is 73 files with the pdb files included:\n\n```\n│   Azure.Core.dll\n│   Azure.Core.pdb\n│   Azure.Identity.dll\n│   Azure.Identity.pdb\n│   Microsoft.Bcl.AsyncInterfaces.dll\n│   Microsoft.Bcl.AsyncInterfaces.pdb\n│   Microsoft.Data.SqlClient.dll\n│   Microsoft.Data.SqlClient.pdb\n│   Microsoft.Identity.Client.dll\n│   Microsoft.Identity.Client.Extensions.Msal.dll\n│   Microsoft.Identity.Client.Extensions.Msal.pdb\n│   Microsoft.Identity.Client.pdb\n│   Microsoft.IdentityModel.Abstractions.dll\n│   Microsoft.IdentityModel.Abstractions.pdb\n│   Microsoft.IdentityModel.JsonWebTokens.dll\n│   Microsoft.IdentityModel.JsonWebTokens.pdb\n│   Microsoft.IdentityModel.Logging.dll\n│   Microsoft.IdentityModel.Logging.pdb\n│   Microsoft.IdentityModel.Protocols.dll\n│   Microsoft.IdentityModel.Protocols.OpenIdConnect.dll\n│   Microsoft.IdentityModel.Protocols.OpenIdConnect.pdb\n│   Microsoft.IdentityModel.Protocols.pdb\n│   Microsoft.IdentityModel.Tokens.dll\n│   Microsoft.IdentityModel.Tokens.pdb\n│   Microsoft.SqlServer.Server.dll\n│   Microsoft.SqlServer.Server.pdb\n│   Microsoft.Win32.SystemEvents.dll\n│   Microsoft.Win32.SystemEvents.pdb\n│   SampleApp.deps.json\n│   SampleApp.dll\n│   SampleApp.exe\n│   SampleApp.pdb\n│   SampleApp.runtimeconfig.json\n│   System.Configuration.ConfigurationManager.dll\n│   System.Configuration.ConfigurationManager.pdb\n│   System.Drawing.Common.dll\n│   System.Drawing.Common.pdb\n│   System.IdentityModel.Tokens.Jwt.dll\n│   System.IdentityModel.Tokens.Jwt.pdb\n│   System.Memory.Data.dll\n│   System.Memory.Data.pdb\n│   System.Runtime.Caching.dll\n│   System.Runtime.Caching.pdb\n│   System.Security.Cryptography.ProtectedData.dll\n│   System.Security.Cryptography.ProtectedData.pdb\n│   System.Security.Permissions.dll\n│   System.Security.Permissions.pdb\n│   System.Windows.Extensions.dll\n│   System.Windows.Extensions.pdb\n└───runtimes\n    ├───unix/lib/net6.0\n    │      Microsoft.Data.SqlClient.dll\n    │      Microsoft.Data.SqlClient.pdb\n    │      System.Drawing.Common.dll\n    │      System.Drawing.Common.pdb\n    ├───win/lib/net6.0\n    │      Microsoft.Data.SqlClient.dll\n    │      Microsoft.Data.SqlClient.pdb\n    │      Microsoft.Win32.SystemEvents.dll\n    │      Microsoft.Win32.SystemEvents.pdb\n    │      System.Drawing.Common.dll\n    │      System.Drawing.Common.pdb\n    │      System.Runtime.Caching.dll\n    │      System.Runtime.Caching.pdb\n    │      System.Security.Cryptography.ProtectedData.dll\n    │      System.Security.Cryptography.ProtectedData.pdb\n    │      System.Windows.Extensions.dll\n    │      System.Windows.Extensions.pdb\n    ├───win-arm/native\n    │      Microsoft.Data.SqlClient.SNI.dll\n    │      Microsoft.Data.SqlClient.SNI.pdb\n    ├───win-arm64/native\n    │      Microsoft.Data.SqlClient.SNI.dll\n    │      Microsoft.Data.SqlClient.SNI.pdb\n    ├───win-x64/native\n    │      Microsoft.Data.SqlClient.SNI.dll\n    │      Microsoft.Data.SqlClient.SNI.pdb\n    └───win-x86/native\n           Microsoft.Data.SqlClient.SNI.dll\n           Microsoft.Data.SqlClient.SNI.pdb\n```\n\n\n## dotnet-symbol required\n\nTo install the [dotnet-symbol tool](https://www.nuget.org/packages/dotnet-symbol), the recommended approach is to [install it as a local tool](https://docs.microsoft.com/en-us/dotnet/core/tools/local-tools-how-to-use).\n\nIn the root of a repository execute:\n\n```\ndotnet new tool-manifest\ndotnet tool install dotnet-symbol\n```\n\nThis will result in a `.config/dotnet-tools.json` file:\n\n\u003c!-- snippet: src\\.config\\dotnet-tools.json --\u003e\n\u003ca id='snippet-src\\.config\\dotnet-tools.json'\u003e\u003c/a\u003e\n```json\n{\n  \"version\": 1,\n  \"isRoot\": true,\n  \"tools\": {\n    \"dotnet-symbol\": {\n      \"version\": \"9.0.607501\",\n      \"commands\": [\n        \"dotnet-symbol\"\n      ]\n    }\n  }\n}\n```\n\u003csup\u003e\u003ca href='#snippet-src\\.config\\dotnet-tools.json' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n[dotnet tool restore](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-restore) can then be run locally or in a build environment:\n\n```\ndotnet tool restore\n```\n\nOr to point to a nested directory: \n\n```\ndotnet tool restore --tool-manifest src/.config/dotnet-tools.json\n```\n\n\n### Scanned symbol servers\n\n * https://symbols.nuget.org/download/symbols \n * https://msdl.microsoft.com/download/symbols/ \n\n### Overriding symbol servers\n\nAdd the following to the project or `Directory.Build.props`:\n\n\u003c!-- snippet: SetSymbolServers --\u003e\n\u003ca id='snippet-SetSymbolServers'\u003e\u003c/a\u003e\n```csproj\n\u003cItemGroup\u003e\n  \u003cSymbolServer Include=\"http://localhost:88/symbols\" /\u003e\n  \u003cSymbolServer Include=\"http://localhost:89/symbols\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\u003csup\u003e\u003ca href='/src/SampleWithSymbolServer/SampleWithSymbolServer.csproj#L9-L14' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-SetSymbolServers' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n## Cache Directory\n\nThe cache directory can be controlled via either:\n\n * An environment variable `CymbalCacheDirectory`. Must contain a full path. Or:\n * An MSBuild property `CymbalCacheDirectory`. This can be passed into a `dotnet publish` using `-p:CymbalCacheDirectory=FullOrRelativePath`. `Path.GetFullPath()` will be used on the value.\n\nThe resolved directory will be created if it doesn't exist.\n\nThe MSBuild property take priority over the environment variable.\n\n\n## Icon\n\n[Cymbals](https://thenounproject.com/term/cymbals/4920970/) designed by [Eucalyp](https://thenounproject.com/eucalyp) from [The Noun Project](https://thenounproject.com).\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimoncropp%2Fcymbal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimoncropp%2Fcymbal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimoncropp%2Fcymbal/lists"}