├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.targets ├── nuget.exe └── packages.config ├── LICENSE ├── README.md ├── RELEASE_NOTES.md ├── Zipkin.sln ├── build.bat ├── build.fsx ├── examples └── ZipkinExample │ ├── App.config │ ├── Program.cs │ ├── Properties │ └── AssemblyInfo.cs │ └── ZipkinExample.csproj ├── src ├── CommonAssemblyInfo.cs ├── Zipkin.Collector.Kafka │ ├── KafkaCollector.cs │ ├── KafkaSettings.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Zipkin.Collector.Kafka.csproj │ ├── Zipkin.Collector.Kafka.nuspec │ └── packages.config └── Zipkin.Core │ ├── Annotation.cs │ ├── AnnotationConstants.cs │ ├── AnnotationType.cs │ ├── Annotations.cs │ ├── BinaryAnnotation.cs │ ├── DebugCollector.cs │ ├── HttpCollector.cs │ ├── ISpanCollector.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Span.cs │ ├── Thrift │ ├── Annotation.cs │ ├── BinaryAnnotation.cs │ ├── Endpoint.cs │ ├── Span.cs │ ├── ThriftExtensions.cs │ └── ThriftSpanSerializer.cs │ ├── TraceHeader.cs │ ├── Zipkin.Core.csproj │ ├── Zipkin.Core.nuspec │ ├── ZipkinCollectorException.cs │ └── packages.config ├── tests └── Zipkin.Core.Tests │ ├── AnnotationTest.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── SpanTest.cs │ ├── Zipkin.Core.Tests.csproj │ └── packages.config └── tools ├── thrift ├── scribe.thrift ├── thrift-0.9.3.exe ├── zipkinCore.thrift └── zipkinDependencies.thrift └── zipkin ├── run.bat └── zipkin-server-0.18.2-exec.jar /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # thrift-generated csharp files 5 | gen-csharp/ 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # DNX 46 | project.lock.json 47 | artifacts/ 48 | 49 | *_i.c 50 | *_p.c 51 | *_i.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.tmp_proj 66 | *.log 67 | *.vspscc 68 | *.vssscc 69 | .builds 70 | *.pidb 71 | *.svclog 72 | *.scc 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # NuGet Packages 149 | *.nupkg 150 | # The packages folder can be ignored because of Package Restore 151 | **/packages/* 152 | # except build/, which is used as an MSBuild target. 153 | !**/packages/build/ 154 | # Uncomment if necessary however generally it will be regenerated when needed 155 | #!**/packages/repositories.config 156 | # NuGet v3's project.json files produces more ignoreable files 157 | *.nuget.props 158 | *.nuget.targets 159 | 160 | # Microsoft Azure Build Output 161 | csx/ 162 | *.build.csdef 163 | 164 | # Microsoft Azure Emulator 165 | ecf/ 166 | rcf/ 167 | 168 | # Microsoft Azure ApplicationInsights config file 169 | ApplicationInsights.config 170 | 171 | # Windows Store app package directory 172 | AppPackages/ 173 | BundleArtifacts/ 174 | 175 | # Visual Studio cache files 176 | # files ending in .cache can be ignored 177 | *.[Cc]ache 178 | # but keep track of directories ending in .cache 179 | !*.[Cc]ache/ 180 | 181 | # Others 182 | ClientBin/ 183 | ~$* 184 | *~ 185 | *.dbmdl 186 | *.dbproj.schemaview 187 | *.pfx 188 | *.publishsettings 189 | node_modules/ 190 | orleans.codegen.cs 191 | 192 | # RIA/Silverlight projects 193 | Generated_Code/ 194 | 195 | # Backup & report files from converting an old project file 196 | # to a newer Visual Studio version. Backup files are not needed, 197 | # because we have git ;-) 198 | _UpgradeReport_Files/ 199 | Backup*/ 200 | UpgradeLog*.XML 201 | UpgradeLog*.htm 202 | 203 | # SQL Server files 204 | *.mdf 205 | *.ldf 206 | 207 | # Business Intelligence projects 208 | *.rdl.data 209 | *.bim.layout 210 | *.bim_*.settings 211 | 212 | # Microsoft Fakes 213 | FakesAssemblies/ 214 | 215 | # GhostDoc plugin setting file 216 | *.GhostDoc.xml 217 | 218 | # Node.js Tools for Visual Studio 219 | .ntvs_analysis.dat 220 | 221 | # Visual Studio 6 build log 222 | *.plg 223 | 224 | # Visual Studio 6 workspace options file 225 | *.opt 226 | 227 | # Visual Studio LightSwitch build output 228 | **/*.HTMLClient/GeneratedArtifacts 229 | **/*.DesktopClient/GeneratedArtifacts 230 | **/*.DesktopClient/ModelManifest.xml 231 | **/*.Server/GeneratedArtifacts 232 | **/*.Server/ModelManifest.xml 233 | _Pvt_Extensions 234 | 235 | # Paket dependency manager 236 | .paket/paket.exe 237 | 238 | # FAKE - F# Make 239 | .fake/ 240 | 241 | # test results 242 | TestResults.xml 243 | -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | 32 | 33 | 34 | 35 | $(SolutionDir).nuget 36 | 37 | 38 | 39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config 40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config 41 | 42 | 43 | 44 | $(MSBuildProjectDirectory)\packages.config 45 | $(PackagesProjectConfig) 46 | 47 | 48 | 49 | 50 | $(NuGetToolsPath)\NuGet.exe 51 | @(PackageSource) 52 | 53 | "$(NuGetExePath)" 54 | mono --runtime=v4.0.30319 "$(NuGetExePath)" 55 | 56 | $(TargetDir.Trim('\\')) 57 | 58 | -RequireConsent 59 | -NonInteractive 60 | 61 | "$(SolutionDir) " 62 | "$(SolutionDir)" 63 | 64 | 65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 67 | 68 | 69 | 70 | RestorePackages; 71 | $(BuildDependsOn); 72 | 73 | 74 | 75 | 76 | $(BuildDependsOn); 77 | BuildPackage; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /.nuget/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openzipkin-attic/zipkin-csharp/872ee914c5e3509acaecbb3578aa81ce380eee8a/.nuget/nuget.exe -------------------------------------------------------------------------------- /.nuget/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Bazinga Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zipkin.Tracer 2 | 3 | A minimalistic .NET client library for Twitter [Zipkin](http://zipkin.io/) tracing. 4 | 5 | It provides a handy set of Zipkin primitives such as spans, annotations, binary annotations to Zipkin receiver through chosen protocol (HTTP or Kafka), using Thrift encoding for efficiency. 6 | 7 | It does **NOT** keep any kind of logical trace context for you. This way it avoids any dependencies on things like HttpContext or building complex abstractions. It also does **NOT** try to introduce any retry or failure handling policies. All of this + extremely small API makes it a great choice as low level library to be used by custom higher level plugins. 8 | 9 | ## Example 10 | 11 | ```csharp 12 | var collector = new HttpCollector(new Uri("http://localhost:9411/")); 13 | 14 | // create a span 15 | var trace = new TraceHeader(traceId: (ulong)random.Next(), spanId: (ulong)random.Next()); 16 | var span = new Span(trace, new IPEndPoint(serviceIp, servicePort), "test-service"); 17 | 18 | span.Record(Annotations.ServerReceive(DateTime.UtcNow)); 19 | // ... handle a RPC request 20 | span.Record(Annotations.ServerSend(DateTime.UtcNow)); 21 | 22 | // send data to to zipkin 23 | await collector.CollectAsync(span); 24 | ``` 25 | 26 | ## API 27 | 28 | #### `ISpanCollector` 29 | 30 | An interface used to communicate with one of the Zipkin span receivers. 31 | 32 | - `Task CollectAsync(params Span[] spans)` - Asynchronously sends a series of spans. May throw `ZipkinCollectorException` when spans delivery failed. 33 | 34 | Span collector has following implementations: 35 | 36 | - `HttpCollector` - A collector sending all data to zipkin endpoint using HTTP protocol. Spans are encoded using Thrift encoding. 37 | - `DebugCollector` - Debug collector used for printing spans into provided output. 38 | - `KafkaCollector` (Zipkin.Tracer.Kafka library) - A collector sending all data to zipkin through kafka producer. 39 | 40 | #### `Span` 41 | 42 | A set of annotations and binary annotations that corresponds to a particular RPC. 43 | 44 | - `TraceHeader TraceHeader` - Trace header containing are identifiers necessary to locate current span. 45 | - `string ServiceName` - Name of the service displayed by Zipkin UI. 46 | - `ICollection Annotations` - Collection of annotations recorder withing current span time frame. 47 | - `ICollection BinaryAnnotations` - Collection of binary annotations used to attach additional metadata with the span itself. 48 | - `IPEndPoint Endpoint` - Endpoint, target span's service is listening on. 49 | - `void Record(Annotation annotation)` - Records an annotation within current span. Also sets it's endpoint if it was not set previously. 50 | - `void Record(BinaryAnnotation binaryAnnotation)` - Records a binary annotation within current span. Also sets it's endpoint if it was not set previously. 51 | 52 | #### `TraceHeader` 53 | 54 | A structure containing all of the data necessary to identify span and it's trace among others. 55 | 56 | - `ulong TraceId` - The overall ID of the trace. Every span in a trace will share this ID. 57 | - `ulong SpanId` - The ID for a particular span. This may or may not be the same as the trace id. 58 | - `ulong? ParentId` - This is an optional ID that will only be present on child spans. That is the span without a parent id is considered the root of the trace. 59 | - `bool IsDebug` - Marks current span with debug flag. 60 | - `TraceHeader Child(ulong childId)` - Creates a trace header for the new span being a child of the span identified by current trace header. 61 | 62 | #### `Annotation` 63 | 64 | An Annotation is used to record an occurance in time. 65 | 66 | - `DateTime Timestamp` - Timestamp marking the occurrence of an event. 67 | - `string Value` - Value holding an information about the annotation. 68 | - `IPEndPoint Endpoint` - Service endpoint. 69 | 70 | #### `BinaryAnnotation` 71 | 72 | Special annotation without time component. They can carry extra information i.e. when calling an HTTP service ⇒ URI of the call. 73 | 74 | - `string Key` - Key identifier of binnary annotation. 75 | - `byte[] Value` - Binary annotation's value as binary. 76 | - `AnnotationType AnnotationType` - Enum identifying type of value stored inside Value field. 77 | - `IPEndPoint Endpoint` - Service endpoint. 78 | 79 | #### `Annotations` 80 | 81 | Static class containing set of constructors for some of the annotations (also binary annotations) recognized by Zipkin itself i.e: `ServerSend`, `ClientSend`, `ServerReceive` or `ClientReceive`. 82 | 83 | #### `AnnotationConstants` 84 | 85 | Static class with set of values, that could be used as `Annotation` values of `BinaryAnnotation` keys. 86 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | #### 0.1.0-beta February 09 2017 2 | * Nuget package under openzipkin 3 | * New structure and naming -------------------------------------------------------------------------------- /Zipkin.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "thrift", "thrift", "{EB1ABC99-0CB6-4E8E-A9FF-23E8A8ECC92A}" 7 | ProjectSection(SolutionItems) = preProject 8 | tools\thrift\scribe.thrift = tools\thrift\scribe.thrift 9 | tools\thrift\zipkinCore.thrift = tools\thrift\zipkinCore.thrift 10 | tools\thrift\zipkinDependencies.thrift = tools\thrift\zipkinDependencies.thrift 11 | EndProjectSection 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8A723099-C10E-4B5B-B8D4-682CB255D8ED}" 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D0018292-24A5-4200-B74D-2652C5C4FB57}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{CE742124-A835-4B08-A7B6-7B1550C8215F}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zipkin.Core", "src\Zipkin.Core\Zipkin.Core.csproj", "{4BB262E2-3927-4E75-952A-F0CF8DDB36B9}" 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZipkinExample", "examples\ZipkinExample\ZipkinExample.csproj", "{7D03BA73-ECC3-4295-9740-7B8E00552341}" 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zipkin.Core.Tests", "tests\Zipkin.Core.Tests\Zipkin.Core.Tests.csproj", "{BAFFCFD1-84EF-4896-A28C-EB97500F5224}" 24 | EndProject 25 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{619760EE-68EE-4392-AA46-B94FEC6B79C9}" 26 | ProjectSection(SolutionItems) = preProject 27 | README.md = README.md 28 | RELEASE_NOTES.md = RELEASE_NOTES.md 29 | EndProjectSection 30 | EndProject 31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zipkin.Collector.Kafka", "src\Zipkin.Collector.Kafka\Zipkin.Collector.Kafka.csproj", "{BB1C4B54-9638-40CB-A785-6228B391E621}" 32 | EndProject 33 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{3A5EEDCE-5B00-406F-A904-10C71E8ABCBD}" 34 | ProjectSection(SolutionItems) = preProject 35 | build.bat = build.bat 36 | build.fsx = build.fsx 37 | EndProjectSection 38 | EndProject 39 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "zipkin", "zipkin", "{1050EEEC-F214-4C7B-A6F4-B4EC14F04078}" 40 | ProjectSection(SolutionItems) = preProject 41 | tools\zipkin\run.bat = tools\zipkin\run.bat 42 | tools\zipkin\zipkin-server-0.18.2-exec.jar = tools\zipkin\zipkin-server-0.18.2-exec.jar 43 | EndProjectSection 44 | EndProject 45 | Global 46 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 47 | Debug|Any CPU = Debug|Any CPU 48 | Release|Any CPU = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 51 | {4BB262E2-3927-4E75-952A-F0CF8DDB36B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {4BB262E2-3927-4E75-952A-F0CF8DDB36B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {4BB262E2-3927-4E75-952A-F0CF8DDB36B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {4BB262E2-3927-4E75-952A-F0CF8DDB36B9}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {7D03BA73-ECC3-4295-9740-7B8E00552341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {7D03BA73-ECC3-4295-9740-7B8E00552341}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {7D03BA73-ECC3-4295-9740-7B8E00552341}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {7D03BA73-ECC3-4295-9740-7B8E00552341}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {BAFFCFD1-84EF-4896-A28C-EB97500F5224}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {BAFFCFD1-84EF-4896-A28C-EB97500F5224}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {BAFFCFD1-84EF-4896-A28C-EB97500F5224}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {BAFFCFD1-84EF-4896-A28C-EB97500F5224}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {BB1C4B54-9638-40CB-A785-6228B391E621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {BB1C4B54-9638-40CB-A785-6228B391E621}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {BB1C4B54-9638-40CB-A785-6228B391E621}.Release|Any CPU.ActiveCfg = Release|Any CPU 66 | {BB1C4B54-9638-40CB-A785-6228B391E621}.Release|Any CPU.Build.0 = Release|Any CPU 67 | EndGlobalSection 68 | GlobalSection(SolutionProperties) = preSolution 69 | HideSolutionNode = FALSE 70 | EndGlobalSection 71 | GlobalSection(NestedProjects) = preSolution 72 | {EB1ABC99-0CB6-4E8E-A9FF-23E8A8ECC92A} = {3A5EEDCE-5B00-406F-A904-10C71E8ABCBD} 73 | {4BB262E2-3927-4E75-952A-F0CF8DDB36B9} = {8A723099-C10E-4B5B-B8D4-682CB255D8ED} 74 | {7D03BA73-ECC3-4295-9740-7B8E00552341} = {CE742124-A835-4B08-A7B6-7B1550C8215F} 75 | {BAFFCFD1-84EF-4896-A28C-EB97500F5224} = {D0018292-24A5-4200-B74D-2652C5C4FB57} 76 | {BB1C4B54-9638-40CB-A785-6228B391E621} = {8A723099-C10E-4B5B-B8D4-682CB255D8ED} 77 | {1050EEEC-F214-4C7B-A6F4-B4EC14F04078} = {3A5EEDCE-5B00-406F-A904-10C71E8ABCBD} 78 | EndGlobalSection 79 | EndGlobal 80 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | pushd %~dp0 4 | 5 | SETLOCAL 6 | SET CACHED_NUGET=%LocalAppData%\NuGet\NuGet.exe 7 | 8 | IF EXIST %CACHED_NUGET% goto copynuget 9 | echo Downloading latest version of NuGet.exe... 10 | IF NOT EXIST %LocalAppData%\NuGet md %LocalAppData%\NuGet 11 | @powershell -NoProfile -ExecutionPolicy unrestricted -Command "$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest 'https://www.nuget.org/nuget.exe' -OutFile '%CACHED_NUGET%'" 12 | 13 | :copynuget 14 | IF EXIST .nuget\nuget.exe goto restore 15 | md .nuget 16 | copy %CACHED_NUGET% .nuget\nuget.exe > nul 17 | 18 | :restore 19 | 20 | .nuget\NuGet.exe update -self 21 | 22 | pushd %~dp0 23 | 24 | .nuget\NuGet.exe update -self 25 | .nuget\NuGet.exe install FAKE -ConfigFile .nuget\Nuget.Config -OutputDirectory packages -ExcludeVersion -Version 4.16.1 26 | .nuget\NuGet.exe install xunit.runner.console -ConfigFile .nuget\Nuget.Config -OutputDirectory packages -ExcludeVersion -Version 2.0.0 27 | 28 | rem cls 29 | 30 | set encoding=utf-8 31 | packages\FAKE\tools\FAKE.exe build.fsx %* 32 | 33 | popd 34 | -------------------------------------------------------------------------------- /build.fsx: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------- 2 | // FAKE build script 3 | // -------------------------------------------------------------------------------------- 4 | 5 | #r @"packages/FAKE/tools/FakeLib.dll" 6 | open Fake 7 | open Fake.Git 8 | open Fake.AssemblyInfoFile 9 | open Fake.ReleaseNotesHelper 10 | open System 11 | open System.IO 12 | 13 | let project = "Zipkin" 14 | let summary = "A minimalistic .NET client library for Twitter Zipkin tracing." 15 | let solutionFile = "Zipkin.sln" 16 | let testAssemblies = "tests/**/bin/Release/*Tests*.dll" 17 | let gitOwner = "openzipkin" 18 | let gitHome = "https://github.com/" + gitOwner 19 | let gitName = "Zipkin-csharp" 20 | let gitRaw = environVarOrDefault "gitRaw" "https://raw.github.com/openzipkin" 21 | 22 | let binDir = currentDirectory @@ "bin" 23 | 24 | // Read additional information from the release notes document 25 | let release = LoadReleaseNotes "RELEASE_NOTES.md" 26 | 27 | // Helper active pattern for project types 28 | let (|Fsproj|Csproj|Vbproj|) (projFileName:string) = 29 | match projFileName with 30 | | f when f.EndsWith("fsproj") -> Fsproj 31 | | f when f.EndsWith("csproj") -> Csproj 32 | | f when f.EndsWith("vbproj") -> Vbproj 33 | | _ -> failwith (sprintf "Project file %s not supported. Unknown project type." projFileName) 34 | 35 | 36 | 37 | // Copies binaries from default VS location to expected bin folder 38 | // But keeps a subdirectory structure for each project in the 39 | // src folder to support multiple project outputs 40 | Target "CopyBinaries" (fun _ -> 41 | !! "src/**/*.??proj" 42 | |> Seq.map (fun f -> ((System.IO.Path.GetDirectoryName f) @@ "bin/Release", "bin" @@ (System.IO.Path.GetFileNameWithoutExtension f))) 43 | |> Seq.iter (fun (fromDir, toDir) -> CopyDir toDir fromDir (fun _ -> true)) 44 | ) 45 | 46 | // -------------------------------------------------------------------------------------- 47 | // Restore Packages 48 | 49 | Target "RestorePackages" (fun _ -> 50 | let nugetExe = NuGetHelper.NuGetDefaults().ToolPath 51 | let nugetCmd = "restore" 52 | let result = ExecProcess (fun info -> 53 | info.FileName <- nugetExe 54 | info.Arguments <- nugetCmd)(TimeSpan.FromSeconds 10.0) 55 | printfn "Restored packages." 56 | ) 57 | 58 | // -------------------------------------------------------------------------------------- 59 | // Clean build results 60 | 61 | Target "Clean" (fun _ -> 62 | CleanDirs ["bin"; "temp"] 63 | ) 64 | 65 | Target "CleanDocs" (fun _ -> 66 | CleanDirs ["docs/output"] 67 | ) 68 | 69 | // -------------------------------------------------------------------------------------- 70 | // Build library & test project 71 | 72 | Target "Build" (fun _ -> 73 | !! solutionFile 74 | |> MSBuildRelease "" "Rebuild" 75 | |> ignore 76 | ) 77 | 78 | // -------------------------------------------------------------------------------------- 79 | // Run the unit tests using test runner 80 | open Fake.Testing.XUnit2 81 | Target "RunTests" (fun _ -> 82 | !! testAssemblies 83 | |> xUnit2 (fun p -> 84 | { p with 85 | TimeOut = TimeSpan.FromMinutes 20. 86 | XmlOutputPath = Some "TestResults.xml" 87 | ToolPath = "packages/xunit.runner.console/tools/xunit.console.exe" }) 88 | ) 89 | 90 | // -------------------------------------------------------------------------------------- 91 | // Build a NuGet package 92 | 93 | Target "NuGet" (fun _ -> 94 | !! "src/**/*.nuspec" 95 | |> Seq.toArray 96 | |> Array.iter (fun nuspec -> 97 | let project = Path.GetFileNameWithoutExtension nuspec 98 | let dir = (Path.GetDirectoryName nuspec) 99 | let packagesFile = dir @@ "packages.config" 100 | let dependencies = NuGetHelper.getDependencies packagesFile 101 | let buildDir = binDir @@ project 102 | NuGetHelper.NuGetPack 103 | (fun p -> 104 | { p with 105 | Copyright = "OpenZipkin Developers" 106 | Project = project 107 | Properties = ["Configuration", "Release"] 108 | ReleaseNotes = release.Notes |> String.concat "\n" 109 | Version = release.NugetVersion 110 | IncludeReferencedProjects = true 111 | OutputPath = buildDir 112 | WorkingDir = dir 113 | Dependencies = dependencies }) 114 | (nuspec.Replace(".nuspec", ".csproj"))) 115 | ) 116 | 117 | Target "PublishNuget" (fun _ -> 118 | let rec publishPackage trialsLeft nupkg = 119 | let nugetExe = NuGetHelper.NuGetDefaults().ToolPath 120 | let key = getBuildParam "key" 121 | let url = "https://www.nuget.org/api/v2/package" 122 | let nugetCmd = sprintf "push \"%s\" %s -source %s" nupkg key url 123 | tracefn "Pushing %s Attempts left: %d" nupkg trialsLeft 124 | try 125 | let result = ExecProcess (fun info -> 126 | info.FileName <- nugetExe 127 | info.WorkingDirectory <- (Path.GetDirectoryName nupkg) 128 | info.Arguments <- nugetCmd) (TimeSpan.FromSeconds 10.0) 129 | if result <> 0 then failwithf "Error during NuGet symbol push. %s %s" nugetExe nugetCmd 130 | with exn -> 131 | if (trialsLeft > 0) then (publishPackage (trialsLeft-1) nupkg) 132 | else raise exn 133 | 134 | !! "**/*.nupkg" 135 | |> Seq.toArray 136 | |> Array.iter (publishPackage 5) 137 | ) 138 | 139 | Target "KeepRunning" (fun _ -> 140 | use watcher = new FileSystemWatcher(DirectoryInfo("docs/content").FullName,"*.*") 141 | watcher.EnableRaisingEvents <- true 142 | 143 | traceImportant "Waiting for help edits. Press any key to stop." 144 | 145 | System.Console.ReadKey() |> ignore 146 | 147 | watcher.EnableRaisingEvents <- false 148 | watcher.Dispose() 149 | ) 150 | 151 | Target "BuildPackage" DoNothing 152 | 153 | // -------------------------------------------------------------------------------------- 154 | // Run all targets by default. Invoke 'build ' to override 155 | 156 | Target "All" DoNothing 157 | 158 | "Clean" 159 | ==> "RestorePackages" 160 | ==> "Build" 161 | ==> "CopyBinaries" 162 | ==> "RunTests" 163 | ==> "All" 164 | 165 | "All" 166 | ==> "NuGet" 167 | ==> "BuildPackage" 168 | 169 | "BuildPackage" 170 | ==> "PublishNuget" 171 | 172 | RunTargetOrDefault "All" 173 | -------------------------------------------------------------------------------- /examples/ZipkinExample/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/ZipkinExample/Program.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Net; 9 | using System.Threading; 10 | using Zipkin; 11 | using Zipkin.Tracer.Kafka; 12 | 13 | namespace ZipkinExample 14 | { 15 | class Program 16 | { 17 | static void Main(string[] args) 18 | { 19 | var random = new Random(); 20 | // make sure Zipkin with Scribe client is working 21 | //var collector = new HttpCollector(new Uri("http://localhost:9411/")); 22 | var collector = new KafkaCollector(KafkaSettings.Default); 23 | var traceId = new TraceHeader(traceId: (ulong)random.Next(), spanId: (ulong)random.Next()); 24 | var span = new Span(traceId, new IPEndPoint(IPAddress.Loopback, 9000), "test-service"); 25 | span.Record(Annotations.ClientSend(DateTime.UtcNow)); 26 | Thread.Sleep(100); 27 | span.Record(Annotations.ServerReceive(DateTime.UtcNow)); 28 | Thread.Sleep(100); 29 | span.Record(Annotations.ServerSend(DateTime.UtcNow)); 30 | Thread.Sleep(100); 31 | span.Record(Annotations.ClientReceive(DateTime.UtcNow)); 32 | 33 | collector.CollectAsync(span).Wait(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/ZipkinExample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ZipkinExample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ZipkinExample")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7d03ba73-ecc3-4295-9740-7b8e00552341")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/ZipkinExample/ZipkinExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7D03BA73-ECC3-4295-9740-7B8E00552341} 8 | Exe 9 | Properties 10 | ZipkinExample 11 | ZipkinExample 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {bb1c4b54-9638-40cb-a785-6228b391e621} 54 | Zipkin.Collector.Kafka 55 | 56 | 57 | {4bb262e2-3927-4e75-952a-f0cf8ddb36b9} 58 | Zipkin.Core 59 | 60 | 61 | 62 | 69 | -------------------------------------------------------------------------------- /src/CommonAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyVersion("0.1.0")] 4 | [assembly: AssemblyFileVersion("0.1.0")] 5 | [assembly: AssemblyCompany("OpenZipkin Developers")] 6 | [assembly: AssemblyCopyright("OpenZipkin 2017")] 7 | [assembly: AssemblyTrademark("Zipkin")] 8 | 9 | namespace System 10 | { 11 | internal static class AssemblyVersionInformation 12 | { 13 | internal const string Version = "0.1.0"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Zipkin.Collector.Kafka/KafkaCollector.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using KafkaNet; 13 | using KafkaNet.Model; 14 | using KafkaNet.Protocol; 15 | using Zipkin.Thrift; 16 | 17 | namespace Zipkin.Tracer.Kafka 18 | { 19 | public class KafkaCollector : ISpanCollector, IDisposable 20 | { 21 | private readonly KafkaSettings _settings; 22 | private readonly Producer _producer; 23 | 24 | public KafkaCollector(KafkaSettings settings) 25 | { 26 | _settings = settings; 27 | var options = new KafkaOptions(settings.ServerUris.Select(x => new Uri(x)).ToArray()); 28 | var router = new BrokerRouter(options); 29 | _producer = new Producer(router, settings.MaxAsyncRequests, settings.MaxMessageBuffer); 30 | } 31 | 32 | public async Task CollectAsync(params Span[] spans) 33 | { 34 | using (var stream = new MemoryStream()) 35 | { 36 | ThriftSpanSerializer.WriteSpans(spans, stream); 37 | stream.Position = 0; 38 | 39 | var message = new Message 40 | { 41 | Value = stream.ToArray() 42 | }; 43 | 44 | var result = await _producer.SendMessageAsync(_settings.ZipkinTopic, new[] { message }); 45 | var res = result.First(); 46 | if (res.Error != 0) 47 | { 48 | throw new ZipkinCollectorException($"An error (code: {res.Error}) occurred while sending trace data to zipkin-kafka"); 49 | } 50 | } 51 | } 52 | 53 | public void Dispose() 54 | { 55 | _producer.Dispose(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Zipkin.Collector.Kafka/KafkaSettings.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Linq; 9 | 10 | namespace Zipkin.Tracer.Kafka 11 | { 12 | [Serializable] 13 | public sealed class KafkaSettings 14 | { 15 | public static KafkaSettings Default => new KafkaSettings("zipkin", 20, 1000, new []{ "http://localhost:9092" }); 16 | public static KafkaSettings Create(params Uri[] serverUris) 17 | => new KafkaSettings("zipkin", 20, 1000, serverUris.Select(x => x.ToString()).ToArray()); 18 | 19 | public KafkaSettings(string zipkinTopic, int maxAsyncRequests, int maxMessageBuffer, string[] serverUris) 20 | { 21 | ZipkinTopic = zipkinTopic; 22 | ServerUris = serverUris; 23 | MaxAsyncRequests = maxAsyncRequests; 24 | MaxMessageBuffer = maxMessageBuffer; 25 | } 26 | 27 | public string[] ServerUris { get; } 28 | public int MaxAsyncRequests { get; } 29 | public int MaxMessageBuffer { get; } 30 | public string ZipkinTopic { get; } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Zipkin.Collector.Kafka/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | 5 | [assembly: AssemblyTitleAttribute("Zipkin.Collector.Kafka")] 6 | [assembly: AssemblyProductAttribute("Zipkin")] 7 | [assembly: AssemblyDescriptionAttribute("A minimalistic .NET client library for Twitter Zipkin tracing.")] 8 | [assembly: InternalsVisibleToAttribute("Zipkin.Core.Tests")] 9 | -------------------------------------------------------------------------------- /src/Zipkin.Collector.Kafka/Zipkin.Collector.Kafka.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BB1C4B54-9638-40CB-A785-6228B391E621} 8 | Library 9 | Properties 10 | Zipkin.Collector.Kafka 11 | Zipkin.Collector.Kafka 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\..\packages\kafka-net.0.9.0.65\lib\net45\kafka-net.dll 35 | True 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Properties\CommonAssemblyInfo.cs 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {4bb262e2-3927-4e75-952a-f0cf8ddb36b9} 57 | Zipkin.Core 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 72 | -------------------------------------------------------------------------------- /src/Zipkin.Collector.Kafka/Zipkin.Collector.Kafka.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Zipkin.Collector.Kafka 5 | $title$ 6 | $version$ 7 | $author$ 8 | $author$ 9 | false 10 | https://github.com/openzipkin/zipkin-csharp/blob/master/LICENSE 11 | https://github.com/openzipkin/zipkin-csharp 12 | $description$ 13 | $description$ 14 | Nuget package 15 | Openzipkin 2017 16 | zipkin tracing distributed performance 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Zipkin.Collector.Kafka/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Zipkin.Core/Annotation.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Net; 9 | 10 | namespace Zipkin 11 | { 12 | /// 13 | /// An Annotation is used to record an occurance in time. 14 | /// 15 | public struct Annotation : IEquatable 16 | { 17 | /// 18 | /// Timestamp marking the occurrence of an event. 19 | /// 20 | public readonly DateTime Timestamp; 21 | 22 | /// 23 | /// Value holding an information about the annotation. 24 | /// See for some 25 | /// build in Zipkin annotation values. 26 | /// 27 | public readonly string Value; 28 | 29 | /// 30 | /// Service endpoint. 31 | /// 32 | public readonly IPEndPoint Endpoint; 33 | 34 | public Annotation(string value, DateTime timestamp, IPEndPoint endpoint) 35 | { 36 | Timestamp = timestamp; 37 | Value = value; 38 | Endpoint = endpoint; 39 | } 40 | 41 | public Annotation(string value, DateTime timestamp) : this() 42 | { 43 | Timestamp = timestamp; 44 | Value = value; 45 | } 46 | 47 | /// 48 | /// Returns a new instance of the with 49 | /// set and all other fields copied 50 | /// from current instance. 51 | /// 52 | public Annotation WithTimestamp(DateTime timestamp) => new Annotation(Value, timestamp, Endpoint); 53 | 54 | /// 55 | /// Returns a new instance of the with 56 | /// set and all other fields copied 57 | /// from current instance. 58 | /// 59 | public Annotation WithEndpoint(IPEndPoint endpoint) => new Annotation(Value, Timestamp, endpoint); 60 | 61 | public bool Equals(Annotation other) => other.Timestamp == Timestamp && Equals(other.Value, Value) && Equals(other.Endpoint, Endpoint); 62 | 63 | public override bool Equals(object obj) => obj is Annotation && Equals((Annotation)obj); 64 | 65 | public override int GetHashCode() 66 | { 67 | unchecked 68 | { 69 | var hashCode = Timestamp.GetHashCode(); 70 | hashCode = (hashCode * 397) ^ (Value != null ? Value.GetHashCode() : 0); 71 | hashCode = (hashCode * 397) ^ (Endpoint != null ? Endpoint.GetHashCode() : 0); 72 | return hashCode; 73 | } 74 | } 75 | 76 | public override string ToString() => $"Annotation({Value}, {Timestamp.ToString("O")}, {Endpoint})"; 77 | } 78 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/AnnotationConstants.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.3) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | 8 | namespace Zipkin 9 | { 10 | public static class AnnotationConstants 11 | { 12 | internal const int TicksPerMicosecond = 10; 13 | 14 | /// 15 | /// The client sent ("cs") a request to a server. There is only one send per 16 | /// span. For example, if there's a transport error, each attempt can be logged 17 | /// as a annotation. 18 | /// 19 | /// If chunking is involved, each chunk could be logged as a separate 20 | /// in the same span. 21 | /// 22 | /// Annotation.host is not the server. It is the host which logged the send 23 | /// event, almost always the client. When logging , instrumentation 24 | /// should also log the . 25 | /// 26 | public const string ClientSend = "cs"; 27 | 28 | /// 29 | /// The client received ("cr") a response from a server. There is only one 30 | /// receive per span. For example, if duplicate responses were received, each 31 | /// can be logged as a annotation. 32 | /// 33 | /// If chunking is involved, each chunk could be logged as a separate 34 | /// in the same span. 35 | /// 36 | /// Annotation.host is not the server. It is the host which logged the receive 37 | /// event, almost always the client. The actual endpoint of the server is 38 | /// recorded separately as when is logged. 39 | /// 40 | public const string ClientReceive = "cr"; 41 | 42 | /// 43 | /// The server sent ("ss") a response to a client. There is only one response 44 | /// per span. If there's a transport error, each attempt can be logged as a 45 | /// annotation. 46 | /// 47 | /// Typically, a trace ends with a server send, so the last timestamp of a trace 48 | /// is often the timestamp of the root span's server send. 49 | /// 50 | /// If chunking is involved, each chunk could be logged as a separate 51 | /// in the same span. 52 | /// 53 | /// Annotation.host is not the client. It is the host which logged the send 54 | /// event, almost always the server. The actual endpoint of the client is 55 | /// recorded separately as when is logged. 56 | /// 57 | public const string ServerSend = "ss"; 58 | 59 | /// 60 | /// The server received ("sr") a request from a client. There is only one 61 | /// request per span. For example, if duplicate responses were received, each 62 | /// can be logged as a annotation. 63 | /// 64 | /// Typically, a trace starts with a server receive, so the first timestamp of a 65 | /// trace is often the timestamp of the root span's server receive. 66 | /// 67 | /// If chunking is involved, each chunk could be logged as a separate 68 | /// in the same span. 69 | /// 70 | /// Annotation.host is not the client. It is the host which logged the receive 71 | /// event, almost always the server. When logging , instrumentation 72 | /// should also log the . 73 | /// 74 | public const string ServerReceive = "sr"; 75 | 76 | /// 77 | /// Optionally logs an attempt to send a message on the wire. Multiple wire send 78 | /// events could indicate network retries. A lag between client or server send 79 | /// and wire send might indicate queuing or processing delay. 80 | /// 81 | public const string WireSend = "ws"; 82 | 83 | /// 84 | /// Optionally logs an attempt to receive a message from the wire. Multiple wire 85 | /// receive events could indicate network retries. A lag between wire receive 86 | /// and client or server receive might indicate queuing or processing delay. 87 | /// 88 | public const string WireReceive = "wr"; 89 | 90 | /// 91 | /// Optionally logs progress of a (, ). For example, this 92 | /// could be one chunk in a chunked request. 93 | /// 94 | public const string ClientSendFragment = "csf"; 95 | 96 | /// 97 | /// Optionally logs progress of a (, ). For example, this 98 | /// could be one chunk in a chunked response. 99 | /// 100 | public const string ClientReceiveFragment = "crf"; 101 | 102 | /// 103 | /// Optionally logs progress of a (, ). For example, this 104 | /// could be one chunk in a chunked response. 105 | /// 106 | public const string ServerSendFragment = "ssf"; 107 | 108 | /// 109 | /// Optionally logs progress of a (, ). For example, this 110 | /// could be one chunk in a chunked request. 111 | /// 112 | public const string ServerReceiveFragment = "srf"; 113 | 114 | /// 115 | /// The domain portion of the URL or host header. Ex. "mybucket.s3.amazonaws.com" 116 | /// 117 | /// Used to filter by host as opposed to ip address. 118 | /// 119 | public const string HttpHost = "http.host"; 120 | 121 | /// 122 | /// The HTTP method, or verb, such as "GET" or "POST". 123 | /// 124 | /// Used to filter against an http route. 125 | /// 126 | public const string HttpMethod = "http.method"; 127 | 128 | /// 129 | /// The absolute http path, without any query parameters. Ex. "/objects/abcd-ff" 130 | /// 131 | /// Used to filter against an http route, portably with zipkin v1. 132 | /// 133 | /// In zipkin v1, only equals filters are supported. Dropping query parameters makes the number 134 | /// of distinct URIs less. For example, one can query for the same resource, regardless of signing 135 | /// parameters encoded in the query line. This does not reduce cardinality to a HTTP single route. 136 | /// For example, it is common to express a route as an http URI template like 137 | /// "/resource/{resource_id}". In systems where only equals queries are available, searching for 138 | /// http/path=/resource won't match if the actual request was /resource/abcd-ff. 139 | /// 140 | /// Historical note: This was commonly expressed as "http.uri" in zipkin, eventhough it was most 141 | /// often just a path. 142 | /// 143 | public const string HttpPath = "http.path"; 144 | 145 | /// 146 | /// The entire URL, including the scheme, host and query parameters if available. Ex. 147 | /// "https://mybucket.s3.amazonaws.com/objects/abcd-ff?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Algorithm=AWS4-HMAC-SHA256..." 148 | /// 149 | /// Combined with HTTP_METHOD, you can understand the fully-qualified request line. 150 | /// 151 | /// This is optional as it may include private data or be of considerable length. 152 | /// 153 | public const string HttpUrl = "http.url"; 154 | 155 | /// 156 | /// The HTTP status code, when not in 2xx range. Ex. "503" 157 | /// 158 | /// Used to filter for error status. 159 | /// 160 | public const string HttpStatusCode = "http.status_code"; 161 | 162 | /// 163 | /// The size of the non-empty HTTP request body, in bytes. Ex. "16384" 164 | /// 165 | /// Large uploads can exceed limits or contribute directly to latency. 166 | /// 167 | public const string HttpRequestSize = "http.request.size"; 168 | 169 | /// 170 | /// The size of the non-empty HTTP response body, in bytes. Ex. "16384" 171 | /// 172 | /// Large downloads can exceed limits or contribute directly to latency. 173 | /// 174 | public const string HttpResponseSize = "http.response.size"; 175 | 176 | /// 177 | /// The value of "lc" is the component or namespace of a local span. 178 | /// 179 | /// BinaryAnnotation.host adds service context needed to support queries. 180 | /// 181 | /// Local Component("lc") supports three key features: flagging, query by 182 | /// service and filtering Span.name by namespace. 183 | /// 184 | /// While structurally the same, local spans are fundamentally different than 185 | /// RPC spans in how they should be interpreted. For example, zipkin v1 tools 186 | /// center on RPC latency and service graphs. Root local-spans are neither 187 | /// indicative of critical path RPC latency, nor have impact on the shape of a 188 | /// service graph. By flagging with "lc", tools can special-case local spans. 189 | /// 190 | /// Zipkin v1 Spans are unqueryable unless they can be indexed by service name. 191 | /// The only path to a service name is by (Binary)?Annotation.host.serviceName. 192 | /// By logging "lc", a local span can be queried even if no other annotations 193 | /// are logged. 194 | /// 195 | /// The value of "lc" is the namespace of Span.name. For example, it might be 196 | /// "finatra2", for a span named "bootstrap". "lc" allows you to resolves 197 | /// conflicts for the same Span.name, for example "finatra/bootstrap" vs 198 | /// "finch/bootstrap". Using local component, you'd search for spans named 199 | /// "bootstrap" where "lc=finch" 200 | /// 201 | public const string LocalComponent = "lc"; 202 | 203 | /// 204 | /// Indicates a client address ("ca") in a span. Most likely, there's only one. 205 | /// Multiple addresses are possible when a client changes its ip or port within 206 | /// a span. 207 | /// 208 | public const string ClientAddress = "ca"; 209 | 210 | /// 211 | /// Indicates a server address ("sa") in a span. Most likely, there's only one. 212 | /// Multiple addresses are possible when a client is redirected, or fails to a 213 | /// different server ip or port. 214 | /// 215 | public const string ServerAddress = "sa"; 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /src/Zipkin.Core/AnnotationType.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.3) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | 8 | namespace Zipkin 9 | { 10 | /// 11 | /// A subset of thrift base types, except BYTES. 12 | /// 13 | public enum AnnotationType 14 | { 15 | /// 16 | /// Set to 0x01 when key is CLIENT_ADDR or SERVER_ADDR 17 | /// 18 | Bool = 0, 19 | /// 20 | /// No encoding, or type is unknown. 21 | /// 22 | Bytes = 1, 23 | Int16 = 2, 24 | Int32 = 3, 25 | Int64 = 4, 26 | Double = 5, 27 | /// 28 | /// the only type zipkin v1 supports search against. 29 | /// 30 | String = 6, 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/Zipkin.Core/Annotations.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Net; 9 | using System.Text; 10 | 11 | namespace Zipkin 12 | { 13 | public static class Annotations 14 | { 15 | private static readonly byte[] TrueBinary = { 1 }; 16 | private static readonly byte[] FalseBinary = { 0 }; 17 | 18 | /// 19 | /// Optionally logs an attempt to send a message on the wire. Multiple wire send 20 | /// events could indicate network retries. A lag between client or server send 21 | /// and wire send might indicate queuing or processing delay. 22 | /// 23 | public static Annotation WireSend(DateTime timestamp) => new Annotation(AnnotationConstants.WireSend, timestamp); 24 | 25 | /// 26 | /// Optionally logs an attempt to receive a message from the wire. Multiple wire 27 | /// receive events could indicate network retries. A lag between wire receive 28 | /// and client or server receive might indicate queuing or processing delay. 29 | /// 30 | public static Annotation WireReceive(DateTime timestamp) => new Annotation(AnnotationConstants.WireReceive, timestamp); 31 | 32 | /// 33 | /// The client sent ("cs") a request to a server. There is only one send per 34 | /// span. For example, if there's a transport error, each attempt can be logged 35 | /// as a annotation. 36 | /// 37 | /// If chunking is involved, each chunk could be logged as a separate 38 | /// in the same span. 39 | /// 40 | /// Annotation.host is not the server. It is the host which logged the send 41 | /// event, almost always the client. When logging , instrumentation 42 | /// should also log the . 43 | /// 44 | public static Annotation ClientSend(DateTime timestamp) => new Annotation(AnnotationConstants.ClientSend, timestamp); 45 | 46 | /// 47 | /// The client received ("cr") a response from a server. There is only one 48 | /// receive per span. For example, if duplicate responses were received, each 49 | /// can be logged as a annotation. 50 | /// 51 | /// If chunking is involved, each chunk could be logged as a separate 52 | /// in the same span. 53 | /// 54 | /// Annotation.host is not the server. It is the host which logged the receive 55 | /// event, almost always the client. The actual endpoint of the server is 56 | /// recorded separately as when is logged. 57 | /// 58 | public static Annotation ClientReceive(DateTime timestamp) => new Annotation(AnnotationConstants.ClientReceive, timestamp); 59 | 60 | /// 61 | /// The server sent ("ss") a response to a client. There is only one response 62 | /// per span. If there's a transport error, each attempt can be logged as a 63 | /// annotation. 64 | /// 65 | /// Typically, a trace ends with a server send, so the last timestamp of a trace 66 | /// is often the timestamp of the root span's server send. 67 | /// 68 | /// If chunking is involved, each chunk could be logged as a separate 69 | /// in the same span. 70 | /// 71 | /// Annotation.host is not the client. It is the host which logged the send 72 | /// event, almost always the server. The actual endpoint of the client is 73 | /// recorded separately as when is logged. 74 | /// 75 | public static Annotation ServerSend(DateTime timestamp) => new Annotation(AnnotationConstants.ServerSend, timestamp); 76 | 77 | /// 78 | /// The server received ("sr") a request from a client. There is only one 79 | /// request per span. For example, if duplicate responses were received, each 80 | /// can be logged as a annotation. 81 | /// 82 | /// Typically, a trace starts with a server receive, so the first timestamp of a 83 | /// trace is often the timestamp of the root span's server receive. 84 | /// 85 | /// If chunking is involved, each chunk could be logged as a separate 86 | /// in the same span. 87 | /// 88 | /// Annotation.host is not the client. It is the host which logged the receive 89 | /// event, almost always the server. When logging , instrumentation 90 | /// should also log the . 91 | /// 92 | public static Annotation ServerReceive(DateTime timestamp) => new Annotation(AnnotationConstants.ServerReceive, timestamp); 93 | 94 | /// 95 | /// Optionally logs progress of a (, ). For example, this 96 | /// could be one chunk in a chunked request. 97 | /// 98 | public static Annotation ClientSendFragment(DateTime timestamp) => new Annotation(AnnotationConstants.ClientSendFragment, timestamp); 99 | 100 | /// 101 | /// Optionally logs progress of a (, ). For example, this 102 | /// could be one chunk in a chunked response. 103 | /// 104 | public static Annotation ClientReceiveFragment(DateTime timestamp) => new Annotation(AnnotationConstants.ClientReceiveFragment, timestamp); 105 | 106 | /// 107 | /// Optionally logs progress of a (, ). For example, this 108 | /// could be one chunk in a chunked response. 109 | /// 110 | public static Annotation ServerSendFragment(DateTime timestamp) => new Annotation(AnnotationConstants.ServerSendFragment, timestamp); 111 | 112 | /// 113 | /// Optionally logs progress of a (, ). For example, this 114 | /// could be one chunk in a chunked request. 115 | /// 116 | public static Annotation ServerReceiveFragment(DateTime timestamp) => new Annotation(AnnotationConstants.ServerSendFragment, timestamp); 117 | 118 | /// 119 | /// Indicates a client address ("ca") in a span. Most likely, there's only one. 120 | /// Multiple addresses are possible when a client changes its ip or port within 121 | /// a span. 122 | /// 123 | public static BinaryAnnotation ClientAddress(IPEndPoint endpoint) => new BinaryAnnotation(AnnotationConstants.ClientAddress, TrueBinary, AnnotationType.Bool, endpoint); 124 | 125 | /// 126 | /// Indicates a server address ("sa") in a span. Most likely, there's only one. 127 | /// Multiple addresses are possible when a client is redirected, or fails to a 128 | /// different server ip or port. 129 | /// 130 | public static BinaryAnnotation ServerAddress(IPEndPoint endpoint) => new BinaryAnnotation(AnnotationConstants.ServerAddress, TrueBinary, AnnotationType.Bool, endpoint); 131 | 132 | /// 133 | /// Creates a for a boolean value. 134 | /// 135 | public static BinaryAnnotation Binary(string key, bool value) => new BinaryAnnotation(key, value ? TrueBinary : FalseBinary, AnnotationType.Bool); 136 | 137 | /// 138 | /// Creates a for a string integer. 139 | /// 140 | public static BinaryAnnotation Binary(string key, string value) => new BinaryAnnotation(key, Encoding.UTF8.GetBytes(value), AnnotationType.String); 141 | } 142 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/BinaryAnnotation.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Net; 9 | using System.Text; 10 | 11 | namespace Zipkin 12 | { 13 | /// 14 | /// Special annotation without time component. They can carry extra 15 | /// information i.e. when calling an HTTP service ⇒ URI of the call. 16 | /// 17 | public struct BinaryAnnotation 18 | { 19 | /// 20 | /// Key of binnary annotation. 21 | /// 22 | public readonly string Key; 23 | 24 | /// 25 | /// Binary annotation's value as binary. 26 | /// 27 | public readonly byte[] Value; 28 | 29 | /// 30 | /// Enum identifying type of value stored inside field. 31 | /// 32 | public readonly AnnotationType AnnotationType; 33 | 34 | /// 35 | /// Service endpoint. 36 | /// 37 | public IPEndPoint Endpoint; 38 | 39 | public BinaryAnnotation(string key, byte[] value, AnnotationType annotationType, IPEndPoint endpoint) : this() 40 | { 41 | Key = key; 42 | Value = value; 43 | AnnotationType = annotationType; 44 | Endpoint = endpoint; 45 | } 46 | 47 | public BinaryAnnotation(string key, byte[] value, AnnotationType annotationType) : this() 48 | { 49 | Key = key; 50 | Value = value; 51 | AnnotationType = annotationType; 52 | } 53 | 54 | public BinaryAnnotation(string key, byte[] value) : this(key, value, AnnotationType.Bytes) { } 55 | 56 | /// 57 | /// Tries to convert field into boolean and assign it to . 58 | /// 59 | /// Variable, where unmarshaled will be assigned to. 60 | /// Returns boolean indicating succees of the operation. 61 | public bool TryConvertValue(out bool value) 62 | { 63 | if (AnnotationType == AnnotationType.Bool && Value.Length == 1) 64 | { 65 | value = Value[0] == 1; 66 | return true; 67 | } 68 | value = false; 69 | return false; 70 | } 71 | 72 | /// 73 | /// Tries to convert field into 16-bit integer and assign it to . 74 | /// 75 | /// Variable, where unmarshaled will be assigned to. 76 | /// Returns boolean indicating succees of the operation. 77 | public bool TryConvertValue(out short value) 78 | { 79 | if (AnnotationType == AnnotationType.Int16) 80 | { 81 | value = BitConverter.ToInt16(Value, 0); 82 | return true; 83 | } 84 | value = 0; 85 | return false; 86 | } 87 | 88 | /// 89 | /// Tries to convert field into 32-bit integer and assign it to . 90 | /// 91 | /// Variable, where unmarshaled will be assigned to. 92 | /// Returns boolean indicating succees of the operation. 93 | public bool TryConvertValue(out int value) 94 | { 95 | if (AnnotationType == AnnotationType.Int32) 96 | { 97 | value = BitConverter.ToInt32(Value, 0); 98 | return true; 99 | } 100 | value = 0; 101 | return false; 102 | } 103 | 104 | /// 105 | /// Tries to convert field into 64-bit integer and assign it to . 106 | /// 107 | /// Variable, where unmarshaled will be assigned to. 108 | /// Returns boolean indicating succees of the operation. 109 | public bool TryConvertValue(out long value) 110 | { 111 | if (AnnotationType == AnnotationType.Int64) 112 | { 113 | value = BitConverter.ToInt64(Value, 0); 114 | return true; 115 | } 116 | value = 0; 117 | return false; 118 | } 119 | 120 | /// 121 | /// Tries to convert field into floating point number 122 | /// (double precision) and assign it to . 123 | /// 124 | /// Variable, where unmarshaled will be assigned to. 125 | /// Returns boolean indicating succees of the operation. 126 | public bool TryConvertValue(out double value) 127 | { 128 | if (AnnotationType == AnnotationType.Double) 129 | { 130 | value = BitConverter.ToDouble(Value, 0); 131 | return true; 132 | } 133 | value = 0; 134 | return false; 135 | } 136 | 137 | /// 138 | /// Tries to convert field into UTF8 string and assign it to . 139 | /// 140 | /// Variable, where unmarshaled will be assigned to. 141 | /// Returns boolean indicating succees of the operation. 142 | public bool TryConvertValue(out string value) 143 | { 144 | if (AnnotationType == AnnotationType.String) 145 | { 146 | value = Encoding.UTF8.GetString(Value); 147 | return true; 148 | } 149 | value = null; 150 | return false; 151 | } 152 | 153 | /// 154 | /// Returns a new instance of the 155 | /// with set and all other fields copied 156 | /// from the current instance. 157 | /// 158 | public BinaryAnnotation WithEndpoint(IPEndPoint endpoint) => new BinaryAnnotation(Key, Value, AnnotationType, endpoint); 159 | 160 | public override string ToString() => $"BinaryAnnotation({Key}, {Value}, {AnnotationType}, {Endpoint})"; 161 | } 162 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/DebugCollector.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System.IO; 8 | using System.Threading.Tasks; 9 | 10 | namespace Zipkin 11 | { 12 | /// 13 | /// Debug collector used for printing spans into provided output. 14 | /// 15 | public class DebugCollector : ISpanCollector 16 | { 17 | private readonly TextWriter _writer; 18 | public DebugCollector(TextWriter writer) 19 | { 20 | _writer = writer; 21 | } 22 | 23 | /// 24 | /// Creates a debug collector instance logging all data on the standard output. 25 | /// 26 | public DebugCollector() : this(System.Console.Out) 27 | { 28 | } 29 | 30 | public async Task CollectAsync(params Span[] spans) 31 | { 32 | foreach (var span in spans) 33 | _writer.WriteLine(span.ToString()); 34 | 35 | _writer.Flush(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/HttpCollector.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.IO; 9 | using System.Net; 10 | using System.Threading.Tasks; 11 | using Thrift.Protocol; 12 | using Thrift.Transport; 13 | using Zipkin.Thrift; 14 | 15 | namespace Zipkin 16 | { 17 | /// 18 | /// A collector sending all data to zipkin endpoint using HTTP protocol. 19 | /// Spans are encoded using Thrift format. 20 | /// 21 | public class HttpCollector : ISpanCollector 22 | { 23 | private Uri _url; 24 | 25 | public HttpCollector(Uri url) 26 | { 27 | if (!url.IsAbsoluteUri) 28 | throw new ArgumentException($"URI '{url}' should be an absolute URI path to zipkin server"); 29 | 30 | if (url.PathAndQuery == "/") 31 | url = new Uri(url, "api/v1/spans"); 32 | 33 | _url = url; 34 | } 35 | 36 | public HttpCollector() : this(new Uri("http://localhost:9411/")) 37 | { 38 | } 39 | 40 | public async Task CollectAsync(params Span[] spans) 41 | { 42 | var request = WebRequest.CreateHttp(_url); 43 | request.Method = "POST"; 44 | request.ContentType = "application/x-thrift"; 45 | 46 | using (var output = new MemoryStream()) 47 | { 48 | ThriftSpanSerializer.WriteSpans(spans, output); 49 | output.Position = 0; 50 | request.ContentLength = output.Length; 51 | using (var stream = await request.GetRequestStreamAsync()) 52 | { 53 | await output.CopyToAsync(stream); 54 | await stream.FlushAsync(); 55 | } 56 | } 57 | 58 | using (var reply = (HttpWebResponse)await request.GetResponseAsync()) 59 | { 60 | if (reply.StatusCode != HttpStatusCode.Accepted) 61 | throw new ZipkinCollectorException($"Zipkin HTTP receiver responded with status code {reply.StatusCode}"); 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/ISpanCollector.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System.Threading.Tasks; 8 | 9 | namespace Zipkin 10 | { 11 | /// 12 | /// An interface used to communicate with one of the Zipkin span receivers. 13 | /// 14 | public interface ISpanCollector 15 | { 16 | /// 17 | /// Asynchronously sends a series of s to Zipkin receiver. 18 | /// 19 | Task CollectAsync(params Span[] spans); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | 5 | [assembly: AssemblyTitleAttribute("Zipkin.Core")] 6 | [assembly: AssemblyProductAttribute("Zipkin")] 7 | [assembly: AssemblyDescriptionAttribute("A minimalistic .NET client library for Twitter Zipkin tracing.")] 8 | [assembly: InternalsVisibleToAttribute("Zipkin.Core.Tests")] 9 | -------------------------------------------------------------------------------- /src/Zipkin.Core/Span.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System.Collections.Generic; 8 | using System.Net; 9 | using System.Text; 10 | 11 | namespace Zipkin 12 | { 13 | /// 14 | /// A set of and elements that correspond to a particular RPC. 15 | /// Spans contain identifying information such as traceId, spandId, parentId, and RPC name. 16 | /// 17 | public sealed class Span 18 | { 19 | /// 20 | /// Trace header containing are identifiers necessary to locate current span. 21 | /// 22 | public readonly TraceHeader TraceHeader; 23 | 24 | /// 25 | /// Name of the service displayed by Zipkin UI. 26 | /// 27 | public readonly string ServiceName; 28 | 29 | public readonly string Name; 30 | 31 | /// 32 | /// Collection of annotations recorder withing current span time frame. 33 | /// 34 | public readonly ICollection Annotations; 35 | 36 | /// 37 | /// Collection of binary annotations used to attach additional metadata with the span itself. 38 | /// 39 | public readonly ICollection BinaryAnnotations; 40 | 41 | /// 42 | /// Endpoint, target span's service is listening on. 43 | /// 44 | public readonly IPEndPoint Endpoint; 45 | 46 | public Span(TraceHeader traceHeader, IPEndPoint endpoint, string serviceName = null, string name = null) 47 | { 48 | TraceHeader = traceHeader; 49 | ServiceName = serviceName ?? "Unknown"; 50 | Name = name ?? "Unknown"; 51 | Annotations = new List(); 52 | BinaryAnnotations = new List(); 53 | Endpoint = endpoint; 54 | } 55 | 56 | public Span(TraceHeader traceHeader, IPEndPoint endpoint, ICollection annotations, ICollection binaryAnnotations, string serviceName, string name) 57 | { 58 | TraceHeader = traceHeader; 59 | ServiceName = serviceName ?? "Unknown"; 60 | Name = name ?? "Unknown"; 61 | Annotations = annotations; 62 | BinaryAnnotations = binaryAnnotations; 63 | Endpoint = endpoint; 64 | } 65 | 66 | public Span(TraceHeader traceHeader, string serviceName = null) 67 | : this(traceHeader, new IPEndPoint(0, 0), serviceName) 68 | { 69 | } 70 | 71 | public Span(ulong traceId, ulong spanId, ulong? parentId = null) 72 | : this(new TraceHeader(traceId, spanId, parentId, true), new IPEndPoint(0, 0)) 73 | { 74 | } 75 | 76 | /// 77 | /// Records an annotation within current span. 78 | /// Also sets it's endpoint if it was not set previously. 79 | /// 80 | public void Record(Annotation annotation) 81 | { 82 | if (annotation.Endpoint == null) 83 | { 84 | annotation = annotation.WithEndpoint(Endpoint); 85 | } 86 | 87 | Annotations.Add(annotation); 88 | } 89 | 90 | /// 91 | /// Records a binary annotation within current span. 92 | /// Also sets it's endpoint if it was not set previously. 93 | /// 94 | public void Record(BinaryAnnotation binaryAnnotation) 95 | { 96 | if (binaryAnnotation.Endpoint == null) 97 | { 98 | binaryAnnotation = binaryAnnotation.WithEndpoint(Endpoint); 99 | } 100 | 101 | BinaryAnnotations.Add(binaryAnnotation); 102 | } 103 | 104 | public override string ToString() 105 | { 106 | var sb = new StringBuilder() 107 | .Append("Span(service:").Append(ServiceName).Append(", name:").Append(Name) 108 | .Append(", trace:").Append(TraceHeader.ToString()) 109 | .Append(", endpoint:").Append(Endpoint.ToString()) 110 | .Append(", annotations:["); 111 | 112 | foreach (var annotation in Annotations) 113 | { 114 | sb.Append(annotation.ToString()).Append(' '); 115 | } 116 | 117 | if (BinaryAnnotations.Count > 0) 118 | { 119 | sb.Append("], binnaryAnnotations:["); 120 | 121 | foreach (var annotation in BinaryAnnotations) 122 | { 123 | sb.Append(annotation.ToString()).Append(' '); 124 | } 125 | } 126 | 127 | sb.Append("])"); 128 | 129 | return sb.ToString(); 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/Thrift/Annotation.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.3) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | using System; 8 | using System.Text; 9 | using Thrift.Protocol; 10 | 11 | namespace Zipkin.Thrift 12 | { 13 | 14 | /// 15 | /// Associates an event that explains latency with a timestamp. 16 | /// 17 | /// Unlike log statements, annotations are often codes: for example "sr". 18 | /// 19 | #if !SILVERLIGHT 20 | [Serializable] 21 | #endif 22 | internal sealed class Annotation : TBase 23 | { 24 | private long _timestamp; 25 | private string _value; 26 | private Endpoint _host; 27 | 28 | /// 29 | /// Microseconds from epoch. 30 | /// 31 | /// This value should use the most precise value possible. For example, 32 | /// gettimeofday or syncing nanoTime against a tick of currentTimeMillis. 33 | /// 34 | public long Timestamp 35 | { 36 | get 37 | { 38 | return _timestamp; 39 | } 40 | set 41 | { 42 | __isset.timestamp = true; 43 | this._timestamp = value; 44 | } 45 | } 46 | 47 | /// 48 | /// Usually a short tag indicating an event, like "sr" or "finagle.retry". 49 | /// 50 | public string Value 51 | { 52 | get 53 | { 54 | return _value; 55 | } 56 | set 57 | { 58 | __isset.@value = true; 59 | this._value = value; 60 | } 61 | } 62 | 63 | /// 64 | /// The host that recorded the value, primarily for query by service name. 65 | /// 66 | public Endpoint Host 67 | { 68 | get 69 | { 70 | return _host; 71 | } 72 | set 73 | { 74 | __isset.host = true; 75 | this._host = value; 76 | } 77 | } 78 | 79 | 80 | internal Isset __isset; 81 | #if !SILVERLIGHT 82 | [Serializable] 83 | #endif 84 | internal struct Isset 85 | { 86 | public bool timestamp; 87 | public bool @value; 88 | public bool host; 89 | } 90 | 91 | public Annotation() 92 | { 93 | } 94 | 95 | public Annotation(long timestamp, string value, Endpoint host) 96 | { 97 | Timestamp = timestamp; 98 | Value = value; 99 | Host = host; 100 | } 101 | 102 | public void Read(TProtocol iprot) 103 | { 104 | iprot.IncrementRecursionDepth(); 105 | try 106 | { 107 | TField field; 108 | iprot.ReadStructBegin(); 109 | while (true) 110 | { 111 | field = iprot.ReadFieldBegin(); 112 | if (field.Type == TType.Stop) 113 | { 114 | break; 115 | } 116 | switch (field.ID) 117 | { 118 | case 1: 119 | if (field.Type == TType.I64) 120 | { 121 | Timestamp = iprot.ReadI64(); 122 | } 123 | else 124 | { 125 | TProtocolUtil.Skip(iprot, field.Type); 126 | } 127 | break; 128 | case 2: 129 | if (field.Type == TType.String) 130 | { 131 | Value = iprot.ReadString(); 132 | } 133 | else 134 | { 135 | TProtocolUtil.Skip(iprot, field.Type); 136 | } 137 | break; 138 | case 3: 139 | if (field.Type == TType.Struct) 140 | { 141 | Host = new Endpoint(); 142 | Host.Read(iprot); 143 | } 144 | else 145 | { 146 | TProtocolUtil.Skip(iprot, field.Type); 147 | } 148 | break; 149 | default: 150 | TProtocolUtil.Skip(iprot, field.Type); 151 | break; 152 | } 153 | iprot.ReadFieldEnd(); 154 | } 155 | iprot.ReadStructEnd(); 156 | } 157 | finally 158 | { 159 | iprot.DecrementRecursionDepth(); 160 | } 161 | } 162 | 163 | public void Write(TProtocol oprot) 164 | { 165 | oprot.IncrementRecursionDepth(); 166 | try 167 | { 168 | TStruct struc = new TStruct("Annotation"); 169 | oprot.WriteStructBegin(struc); 170 | TField field = new TField(); 171 | if (__isset.timestamp) 172 | { 173 | field.Name = "timestamp"; 174 | field.Type = TType.I64; 175 | field.ID = 1; 176 | oprot.WriteFieldBegin(field); 177 | oprot.WriteI64(Timestamp); 178 | oprot.WriteFieldEnd(); 179 | } 180 | if (Value != null && __isset.@value) 181 | { 182 | field.Name = "value"; 183 | field.Type = TType.String; 184 | field.ID = 2; 185 | oprot.WriteFieldBegin(field); 186 | oprot.WriteString(Value); 187 | oprot.WriteFieldEnd(); 188 | } 189 | if (Host != null && __isset.host) 190 | { 191 | field.Name = "host"; 192 | field.Type = TType.Struct; 193 | field.ID = 3; 194 | oprot.WriteFieldBegin(field); 195 | Host.Write(oprot); 196 | oprot.WriteFieldEnd(); 197 | } 198 | oprot.WriteFieldStop(); 199 | oprot.WriteStructEnd(); 200 | } 201 | finally 202 | { 203 | oprot.DecrementRecursionDepth(); 204 | } 205 | } 206 | 207 | public override string ToString() 208 | { 209 | StringBuilder __sb = new StringBuilder("Annotation("); 210 | bool __first = true; 211 | if (__isset.timestamp) 212 | { 213 | if (!__first) { __sb.Append(", "); } 214 | __first = false; 215 | __sb.Append("Timestamp: "); 216 | __sb.Append(Timestamp); 217 | } 218 | if (Value != null && __isset.@value) 219 | { 220 | if (!__first) { __sb.Append(", "); } 221 | __first = false; 222 | __sb.Append("Value: "); 223 | __sb.Append(Value); 224 | } 225 | if (Host != null && __isset.host) 226 | { 227 | if (!__first) { __sb.Append(", "); } 228 | __first = false; 229 | __sb.Append("Host: "); 230 | __sb.Append(Host == null ? "" : Host.ToString()); 231 | } 232 | __sb.Append(")"); 233 | return __sb.ToString(); 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/Zipkin.Core/Thrift/BinaryAnnotation.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.3) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | using System; 8 | using System.Text; 9 | using Thrift.Protocol; 10 | 11 | namespace Zipkin.Thrift 12 | { 13 | /// 14 | /// Binary annotations are tags applied to a Span to give it context. For 15 | /// example, a binary annotation of HTTP_PATH ("http.path") could the path 16 | /// to a resource in a RPC call. 17 | /// 18 | /// Binary annotations of type STRING are always queryable, though more a 19 | /// historical implementation detail than a structural concern. 20 | /// 21 | /// Binary annotations can repeat, and vary on the host. Similar to Annotation, 22 | /// the host indicates who logged the event. This allows you to tell the 23 | /// difference between the client and server side of the same key. For example, 24 | /// the key "http.path" might be different on the client and server side due to 25 | /// rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, 26 | /// you can see the different points of view, which often help in debugging. 27 | /// 28 | #if !SILVERLIGHT 29 | [Serializable] 30 | #endif 31 | internal sealed class BinaryAnnotation : TBase 32 | { 33 | private string _key; 34 | private byte[] _value; 35 | private AnnotationType _annotation_type; 36 | private Endpoint _host; 37 | 38 | /// 39 | /// Name used to lookup spans, such as "http.path" or "finagle.version". 40 | /// 41 | public string Key 42 | { 43 | get 44 | { 45 | return _key; 46 | } 47 | set 48 | { 49 | __isset.key = true; 50 | this._key = value; 51 | } 52 | } 53 | 54 | /// 55 | /// Serialized thrift bytes, in TBinaryProtocol format. 56 | /// 57 | /// For legacy reasons, byte order is big-endian. See THRIFT-3217. 58 | /// 59 | public byte[] Value 60 | { 61 | get 62 | { 63 | return _value; 64 | } 65 | set 66 | { 67 | __isset.@value = true; 68 | this._value = value; 69 | } 70 | } 71 | 72 | /// 73 | /// The thrift type of value, most often STRING. 74 | /// 75 | /// annotation_type shouldn't vary for the same key. 76 | /// 77 | /// 78 | /// 79 | public AnnotationType AnnotationType 80 | { 81 | get 82 | { 83 | return _annotation_type; 84 | } 85 | set 86 | { 87 | __isset.annotation_type = true; 88 | this._annotation_type = value; 89 | } 90 | } 91 | 92 | /// 93 | /// The host that recorded value, allowing query by service name or address. 94 | /// 95 | /// There are two exceptions: when key is "ca" or "sa", this is the source or 96 | /// destination of an RPC. This exception allows zipkin to display network 97 | /// context of uninstrumented services, such as browsers or databases. 98 | /// 99 | public Endpoint Host 100 | { 101 | get 102 | { 103 | return _host; 104 | } 105 | set 106 | { 107 | __isset.host = true; 108 | this._host = value; 109 | } 110 | } 111 | 112 | 113 | internal Isset __isset; 114 | #if !SILVERLIGHT 115 | [Serializable] 116 | #endif 117 | internal struct Isset 118 | { 119 | public bool key; 120 | public bool @value; 121 | public bool annotation_type; 122 | public bool host; 123 | } 124 | 125 | public BinaryAnnotation() 126 | { 127 | } 128 | 129 | public BinaryAnnotation(string key, byte[] value, AnnotationType annotationType, Endpoint host) 130 | { 131 | Key = key; 132 | Value = value; 133 | AnnotationType = annotationType; 134 | Host = host; 135 | } 136 | 137 | public void Read(TProtocol iprot) 138 | { 139 | iprot.IncrementRecursionDepth(); 140 | try 141 | { 142 | TField field; 143 | iprot.ReadStructBegin(); 144 | while (true) 145 | { 146 | field = iprot.ReadFieldBegin(); 147 | if (field.Type == TType.Stop) 148 | { 149 | break; 150 | } 151 | switch (field.ID) 152 | { 153 | case 1: 154 | if (field.Type == TType.String) 155 | { 156 | Key = iprot.ReadString(); 157 | } 158 | else 159 | { 160 | TProtocolUtil.Skip(iprot, field.Type); 161 | } 162 | break; 163 | case 2: 164 | if (field.Type == TType.String) 165 | { 166 | Value = iprot.ReadBinary(); 167 | } 168 | else 169 | { 170 | TProtocolUtil.Skip(iprot, field.Type); 171 | } 172 | break; 173 | case 3: 174 | if (field.Type == TType.I32) 175 | { 176 | AnnotationType = (AnnotationType)iprot.ReadI32(); 177 | } 178 | else 179 | { 180 | TProtocolUtil.Skip(iprot, field.Type); 181 | } 182 | break; 183 | case 4: 184 | if (field.Type == TType.Struct) 185 | { 186 | Host = new Endpoint(); 187 | Host.Read(iprot); 188 | } 189 | else 190 | { 191 | TProtocolUtil.Skip(iprot, field.Type); 192 | } 193 | break; 194 | default: 195 | TProtocolUtil.Skip(iprot, field.Type); 196 | break; 197 | } 198 | iprot.ReadFieldEnd(); 199 | } 200 | iprot.ReadStructEnd(); 201 | } 202 | finally 203 | { 204 | iprot.DecrementRecursionDepth(); 205 | } 206 | } 207 | 208 | public void Write(TProtocol oprot) 209 | { 210 | oprot.IncrementRecursionDepth(); 211 | try 212 | { 213 | TStruct struc = new TStruct("BinaryAnnotation"); 214 | oprot.WriteStructBegin(struc); 215 | TField field = new TField(); 216 | if (Key != null && __isset.key) 217 | { 218 | field.Name = "key"; 219 | field.Type = TType.String; 220 | field.ID = 1; 221 | oprot.WriteFieldBegin(field); 222 | oprot.WriteString(Key); 223 | oprot.WriteFieldEnd(); 224 | } 225 | if (Value != null && __isset.@value) 226 | { 227 | field.Name = "value"; 228 | field.Type = TType.String; 229 | field.ID = 2; 230 | oprot.WriteFieldBegin(field); 231 | oprot.WriteBinary(Value); 232 | oprot.WriteFieldEnd(); 233 | } 234 | if (__isset.annotation_type) 235 | { 236 | field.Name = "annotation_type"; 237 | field.Type = TType.I32; 238 | field.ID = 3; 239 | oprot.WriteFieldBegin(field); 240 | oprot.WriteI32((int)AnnotationType); 241 | oprot.WriteFieldEnd(); 242 | } 243 | if (Host != null && __isset.host) 244 | { 245 | field.Name = "host"; 246 | field.Type = TType.Struct; 247 | field.ID = 4; 248 | oprot.WriteFieldBegin(field); 249 | Host.Write(oprot); 250 | oprot.WriteFieldEnd(); 251 | } 252 | oprot.WriteFieldStop(); 253 | oprot.WriteStructEnd(); 254 | } 255 | finally 256 | { 257 | oprot.DecrementRecursionDepth(); 258 | } 259 | } 260 | 261 | public override string ToString() 262 | { 263 | StringBuilder __sb = new StringBuilder("BinaryAnnotation("); 264 | bool __first = true; 265 | if (Key != null && __isset.key) 266 | { 267 | if (!__first) { __sb.Append(", "); } 268 | __first = false; 269 | __sb.Append("Key: "); 270 | __sb.Append(Key); 271 | } 272 | if (Value != null && __isset.@value) 273 | { 274 | if (!__first) { __sb.Append(", "); } 275 | __first = false; 276 | __sb.Append("Value: "); 277 | __sb.Append(Value); 278 | } 279 | if (__isset.annotation_type) 280 | { 281 | if (!__first) { __sb.Append(", "); } 282 | __first = false; 283 | __sb.Append("Annotation_type: "); 284 | __sb.Append(AnnotationType); 285 | } 286 | if (Host != null && __isset.host) 287 | { 288 | if (!__first) { __sb.Append(", "); } 289 | __first = false; 290 | __sb.Append("Host: "); 291 | __sb.Append(Host == null ? "" : Host.ToString()); 292 | } 293 | __sb.Append(")"); 294 | return __sb.ToString(); 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/Zipkin.Core/Thrift/Endpoint.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.3) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | using System; 8 | using System.Text; 9 | using Thrift.Protocol; 10 | 11 | namespace Zipkin.Thrift 12 | { 13 | /// 14 | /// Indicates the network context of a service recording an annotation with two 15 | /// exceptions. 16 | /// 17 | /// When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, 18 | /// the endpoint indicates the source or destination of an RPC. This exception 19 | /// allows zipkin to display network context of uninstrumented services, or 20 | /// clients such as web browsers. 21 | /// 22 | #if !SILVERLIGHT 23 | [Serializable] 24 | #endif 25 | internal sealed class Endpoint : TBase 26 | { 27 | private int _ipv4; 28 | private short _port; 29 | private string _service_name; 30 | 31 | /// 32 | /// IPv4 host address packed into 4 bytes. 33 | /// 34 | /// Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 35 | /// 36 | public int Ipv4 37 | { 38 | get 39 | { 40 | return _ipv4; 41 | } 42 | set 43 | { 44 | __isset.ipv4 = true; 45 | this._ipv4 = value; 46 | } 47 | } 48 | 49 | /// 50 | /// IPv4 port or 0, if unknown. 51 | /// 52 | /// Note: this is to be treated as an unsigned integer, so watch for negatives. 53 | /// 54 | public short Port 55 | { 56 | get 57 | { 58 | return _port; 59 | } 60 | set 61 | { 62 | __isset.port = true; 63 | this._port = value; 64 | } 65 | } 66 | 67 | /// 68 | /// Classifier of a source or destination in lowercase, such as "zipkin-web". 69 | /// 70 | /// This is the primary parameter for trace lookup, so should be intuitive as 71 | /// possible, for example, matching names in service discovery. 72 | /// 73 | /// Conventionally, when the service name isn't known, service_name = "unknown". 74 | /// However, it is also permissible to set service_name = "" (empty string). 75 | /// The difference in the latter usage is that the span will not be queryable 76 | /// by service name unless more information is added to the span with non-empty 77 | /// service name, e.g. an additional annotation from the server. 78 | /// 79 | /// Particularly clients may not have a reliable service name at ingest. One 80 | /// approach is to set service_name to "" at ingest, and later assign a 81 | /// better label based on binary annotations, such as user agent. 82 | /// 83 | public string ServiceName 84 | { 85 | get 86 | { 87 | return _service_name; 88 | } 89 | set 90 | { 91 | __isset.service_name = true; 92 | this._service_name = value; 93 | } 94 | } 95 | 96 | 97 | internal Isset __isset; 98 | #if !SILVERLIGHT 99 | [Serializable] 100 | #endif 101 | internal struct Isset 102 | { 103 | public bool ipv4; 104 | public bool port; 105 | public bool service_name; 106 | } 107 | 108 | public Endpoint() 109 | { 110 | } 111 | 112 | public Endpoint(int ipv4, short port, string serviceName) 113 | { 114 | Ipv4 = ipv4; 115 | Port = port; 116 | ServiceName = serviceName; 117 | } 118 | 119 | public void Read(TProtocol iprot) 120 | { 121 | iprot.IncrementRecursionDepth(); 122 | try 123 | { 124 | TField field; 125 | iprot.ReadStructBegin(); 126 | while (true) 127 | { 128 | field = iprot.ReadFieldBegin(); 129 | if (field.Type == TType.Stop) 130 | { 131 | break; 132 | } 133 | switch (field.ID) 134 | { 135 | case 1: 136 | if (field.Type == TType.I32) 137 | { 138 | Ipv4 = iprot.ReadI32(); 139 | } 140 | else 141 | { 142 | TProtocolUtil.Skip(iprot, field.Type); 143 | } 144 | break; 145 | case 2: 146 | if (field.Type == TType.I16) 147 | { 148 | Port = iprot.ReadI16(); 149 | } 150 | else 151 | { 152 | TProtocolUtil.Skip(iprot, field.Type); 153 | } 154 | break; 155 | case 3: 156 | if (field.Type == TType.String) 157 | { 158 | ServiceName = iprot.ReadString(); 159 | } 160 | else 161 | { 162 | TProtocolUtil.Skip(iprot, field.Type); 163 | } 164 | break; 165 | default: 166 | TProtocolUtil.Skip(iprot, field.Type); 167 | break; 168 | } 169 | iprot.ReadFieldEnd(); 170 | } 171 | iprot.ReadStructEnd(); 172 | } 173 | finally 174 | { 175 | iprot.DecrementRecursionDepth(); 176 | } 177 | } 178 | 179 | public void Write(TProtocol oprot) 180 | { 181 | oprot.IncrementRecursionDepth(); 182 | try 183 | { 184 | TStruct struc = new TStruct("Endpoint"); 185 | oprot.WriteStructBegin(struc); 186 | TField field = new TField(); 187 | if (__isset.ipv4) 188 | { 189 | field.Name = "ipv4"; 190 | field.Type = TType.I32; 191 | field.ID = 1; 192 | oprot.WriteFieldBegin(field); 193 | oprot.WriteI32(Ipv4); 194 | oprot.WriteFieldEnd(); 195 | } 196 | if (__isset.port) 197 | { 198 | field.Name = "port"; 199 | field.Type = TType.I16; 200 | field.ID = 2; 201 | oprot.WriteFieldBegin(field); 202 | oprot.WriteI16(Port); 203 | oprot.WriteFieldEnd(); 204 | } 205 | if (ServiceName != null && __isset.service_name) 206 | { 207 | field.Name = "service_name"; 208 | field.Type = TType.String; 209 | field.ID = 3; 210 | oprot.WriteFieldBegin(field); 211 | oprot.WriteString(ServiceName); 212 | oprot.WriteFieldEnd(); 213 | } 214 | oprot.WriteFieldStop(); 215 | oprot.WriteStructEnd(); 216 | } 217 | finally 218 | { 219 | oprot.DecrementRecursionDepth(); 220 | } 221 | } 222 | 223 | public override string ToString() 224 | { 225 | StringBuilder __sb = new StringBuilder("Endpoint("); 226 | bool __first = true; 227 | if (__isset.ipv4) 228 | { 229 | if (!__first) { __sb.Append(", "); } 230 | __first = false; 231 | __sb.Append("Ipv4: "); 232 | __sb.Append(Ipv4); 233 | } 234 | if (__isset.port) 235 | { 236 | if (!__first) { __sb.Append(", "); } 237 | __first = false; 238 | __sb.Append("Port: "); 239 | __sb.Append(Port); 240 | } 241 | if (ServiceName != null && __isset.service_name) 242 | { 243 | if (!__first) { __sb.Append(", "); } 244 | __first = false; 245 | __sb.Append("Service_name: "); 246 | __sb.Append(ServiceName); 247 | } 248 | __sb.Append(")"); 249 | return __sb.ToString(); 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/Zipkin.Core/Thrift/Span.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.3) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Text; 10 | using Thrift.Protocol; 11 | 12 | namespace Zipkin.Thrift 13 | { 14 | /// 15 | /// A trace is a series of spans (often RPC calls) which form a latency tree. 16 | /// 17 | /// Spans are usually created by instrumentation in RPC clients or servers, but 18 | /// can also represent in-process activity. Annotations in spans are similar to 19 | /// log statements, and are sometimes created directly by application developers 20 | /// to indicate events of interest, such as a cache miss. 21 | /// 22 | /// The root span is where parent_id = Nil; it usually has the longest duration 23 | /// in the trace. 24 | /// 25 | /// Span identifiers are packed into i64s, but should be treated opaquely. 26 | /// String encoding is fixed-width lower-hex, to avoid signed interpretation. 27 | /// 28 | #if !SILVERLIGHT 29 | [Serializable] 30 | #endif 31 | internal sealed class Span : TBase 32 | { 33 | private long _trace_id; 34 | private string _name; 35 | private long _id; 36 | private long _parent_id; 37 | private List _annotations; 38 | private List _binary_annotations; 39 | private bool _debug; 40 | private long _timestamp; 41 | private long _duration; 42 | 43 | /// 44 | /// Unique 8-byte identifier for a trace, set on all spans within it. 45 | /// 46 | public long TraceId 47 | { 48 | get 49 | { 50 | return _trace_id; 51 | } 52 | set 53 | { 54 | __isset.trace_id = true; 55 | this._trace_id = value; 56 | } 57 | } 58 | 59 | /// 60 | /// Span name in lowercase, rpc method for example. Conventionally, when the 61 | /// span name isn't known, name = "unknown". 62 | /// 63 | public string Name 64 | { 65 | get 66 | { 67 | return _name; 68 | } 69 | set 70 | { 71 | __isset.name = true; 72 | this._name = value; 73 | } 74 | } 75 | 76 | /// 77 | /// Unique 8-byte identifier of this span within a trace. A span is uniquely 78 | /// identified in storage by (trace_id, id). 79 | /// 80 | public long Id 81 | { 82 | get 83 | { 84 | return _id; 85 | } 86 | set 87 | { 88 | __isset.id = true; 89 | this._id = value; 90 | } 91 | } 92 | 93 | /// 94 | /// The parent's Span.id; absent if this the root span in a trace. 95 | /// 96 | public long ParentId 97 | { 98 | get 99 | { 100 | return _parent_id; 101 | } 102 | set 103 | { 104 | __isset.parent_id = true; 105 | this._parent_id = value; 106 | } 107 | } 108 | 109 | /// 110 | /// Associates events that explain latency with a timestamp. Unlike log 111 | /// statements, annotations are often codes: for example SERVER_RECV("sr"). 112 | /// Annotations are sorted ascending by timestamp. 113 | /// 114 | public List Annotations 115 | { 116 | get 117 | { 118 | return _annotations; 119 | } 120 | set 121 | { 122 | __isset.annotations = true; 123 | this._annotations = value; 124 | } 125 | } 126 | 127 | /// 128 | /// Tags a span with context, usually to support query or aggregation. For 129 | /// example, a binary annotation key could be "http.path". 130 | /// 131 | public List BinaryAnnotations 132 | { 133 | get 134 | { 135 | return _binary_annotations; 136 | } 137 | set 138 | { 139 | __isset.binary_annotations = true; 140 | this._binary_annotations = value; 141 | } 142 | } 143 | 144 | /// 145 | /// True is a request to store this span even if it overrides sampling policy. 146 | /// 147 | public bool Debug 148 | { 149 | get 150 | { 151 | return _debug; 152 | } 153 | set 154 | { 155 | __isset.debug = true; 156 | this._debug = value; 157 | } 158 | } 159 | 160 | /// 161 | /// Epoch microseconds of the start of this span, absent if this an incomplete 162 | /// span. 163 | /// 164 | /// This value should be set directly by instrumentation, using the most 165 | /// precise value possible. For example, gettimeofday or syncing nanoTime 166 | /// against a tick of currentTimeMillis. 167 | /// 168 | /// For compatibilty with instrumentation that precede this field, collectors 169 | /// or span stores can derive this via Annotation.timestamp. 170 | /// For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. 171 | /// 172 | /// Timestamp is nullable for input only. Spans without a timestamp cannot be 173 | /// presented in a timeline: Span stores should not output spans missing a 174 | /// timestamp. 175 | /// 176 | /// There are two known edge-cases where this could be absent: both cases 177 | /// exist when a collector receives a span in parts and a binary annotation 178 | /// precedes a timestamp. This is possible when.. 179 | /// - The span is in-flight (ex not yet received a timestamp) 180 | /// - The span's start event was lost 181 | /// 182 | public long Timestamp 183 | { 184 | get 185 | { 186 | return _timestamp; 187 | } 188 | set 189 | { 190 | __isset.timestamp = true; 191 | this._timestamp = value; 192 | } 193 | } 194 | 195 | /// 196 | /// Measurement in microseconds of the critical path, if known. 197 | /// 198 | /// This value should be set directly, as opposed to implicitly via annotation 199 | /// timestamps. Doing so encourages precision decoupled from problems of 200 | /// clocks, such as skew or NTP updates causing time to move backwards. 201 | /// 202 | /// For compatibility with instrumentation that precede this field, collectors 203 | /// or span stores can derive this by subtracting Annotation.timestamp. 204 | /// For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. 205 | /// 206 | /// If this field is persisted as unset, zipkin will continue to work, except 207 | /// duration query support will be implementation-specific. Similarly, setting 208 | /// this field non-atomically is implementation-specific. 209 | /// 210 | /// This field is i64 vs i32 to support spans longer than 35 minutes. 211 | /// 212 | public long Duration 213 | { 214 | get 215 | { 216 | return _duration; 217 | } 218 | set 219 | { 220 | __isset.duration = true; 221 | this._duration = value; 222 | } 223 | } 224 | 225 | 226 | internal Isset __isset; 227 | #if !SILVERLIGHT 228 | [Serializable] 229 | #endif 230 | internal struct Isset 231 | { 232 | public bool trace_id; 233 | public bool name; 234 | public bool id; 235 | public bool parent_id; 236 | public bool annotations; 237 | public bool binary_annotations; 238 | public bool debug; 239 | public bool timestamp; 240 | public bool duration; 241 | } 242 | 243 | public Span() 244 | { 245 | this._annotations = new List(0); 246 | this._binary_annotations = new List(0); 247 | this._debug = false; 248 | this.__isset.debug = true; 249 | } 250 | 251 | public Span(long traceId, string name, long id, long parentId, List annotations, List binaryAnnotations, bool debug, long timestamp, long duration) 252 | : this() 253 | { 254 | TraceId = traceId; 255 | Name = name; 256 | Id = id; 257 | ParentId = parentId; 258 | Annotations = annotations; 259 | BinaryAnnotations = binaryAnnotations; 260 | Debug = debug; 261 | Timestamp = timestamp; 262 | Duration = duration; 263 | } 264 | 265 | public void Read(TProtocol iprot) 266 | { 267 | iprot.IncrementRecursionDepth(); 268 | try 269 | { 270 | TField field; 271 | iprot.ReadStructBegin(); 272 | while (true) 273 | { 274 | field = iprot.ReadFieldBegin(); 275 | if (field.Type == TType.Stop) 276 | { 277 | break; 278 | } 279 | switch (field.ID) 280 | { 281 | case 1: 282 | if (field.Type == TType.I64) 283 | { 284 | TraceId = iprot.ReadI64(); 285 | } 286 | else 287 | { 288 | TProtocolUtil.Skip(iprot, field.Type); 289 | } 290 | break; 291 | case 3: 292 | if (field.Type == TType.String) 293 | { 294 | Name = iprot.ReadString(); 295 | } 296 | else 297 | { 298 | TProtocolUtil.Skip(iprot, field.Type); 299 | } 300 | break; 301 | case 4: 302 | if (field.Type == TType.I64) 303 | { 304 | Id = iprot.ReadI64(); 305 | } 306 | else 307 | { 308 | TProtocolUtil.Skip(iprot, field.Type); 309 | } 310 | break; 311 | case 5: 312 | if (field.Type == TType.I64) 313 | { 314 | ParentId = iprot.ReadI64(); 315 | } 316 | else 317 | { 318 | TProtocolUtil.Skip(iprot, field.Type); 319 | } 320 | break; 321 | case 6: 322 | if (field.Type == TType.List) 323 | { 324 | { 325 | Annotations = new List(); 326 | TList _list0 = iprot.ReadListBegin(); 327 | for (int _i1 = 0; _i1 < _list0.Count; ++_i1) 328 | { 329 | Annotation _elem2; 330 | _elem2 = new Annotation(); 331 | _elem2.Read(iprot); 332 | Annotations.Add(_elem2); 333 | } 334 | iprot.ReadListEnd(); 335 | } 336 | } 337 | else 338 | { 339 | TProtocolUtil.Skip(iprot, field.Type); 340 | } 341 | break; 342 | case 8: 343 | if (field.Type == TType.List) 344 | { 345 | { 346 | BinaryAnnotations = new List(); 347 | TList _list3 = iprot.ReadListBegin(); 348 | for (int _i4 = 0; _i4 < _list3.Count; ++_i4) 349 | { 350 | BinaryAnnotation _elem5; 351 | _elem5 = new BinaryAnnotation(); 352 | _elem5.Read(iprot); 353 | BinaryAnnotations.Add(_elem5); 354 | } 355 | iprot.ReadListEnd(); 356 | } 357 | } 358 | else 359 | { 360 | TProtocolUtil.Skip(iprot, field.Type); 361 | } 362 | break; 363 | case 9: 364 | if (field.Type == TType.Bool) 365 | { 366 | Debug = iprot.ReadBool(); 367 | } 368 | else 369 | { 370 | TProtocolUtil.Skip(iprot, field.Type); 371 | } 372 | break; 373 | case 10: 374 | if (field.Type == TType.I64) 375 | { 376 | Timestamp = iprot.ReadI64(); 377 | } 378 | else 379 | { 380 | TProtocolUtil.Skip(iprot, field.Type); 381 | } 382 | break; 383 | case 11: 384 | if (field.Type == TType.I64) 385 | { 386 | Duration = iprot.ReadI64(); 387 | } 388 | else 389 | { 390 | TProtocolUtil.Skip(iprot, field.Type); 391 | } 392 | break; 393 | default: 394 | TProtocolUtil.Skip(iprot, field.Type); 395 | break; 396 | } 397 | iprot.ReadFieldEnd(); 398 | } 399 | iprot.ReadStructEnd(); 400 | } 401 | finally 402 | { 403 | iprot.DecrementRecursionDepth(); 404 | } 405 | } 406 | 407 | public void Write(TProtocol oprot) 408 | { 409 | oprot.IncrementRecursionDepth(); 410 | try 411 | { 412 | TStruct struc = new TStruct("Span"); 413 | oprot.WriteStructBegin(struc); 414 | TField field = new TField(); 415 | if (__isset.trace_id) 416 | { 417 | field.Name = "trace_id"; 418 | field.Type = TType.I64; 419 | field.ID = 1; 420 | oprot.WriteFieldBegin(field); 421 | oprot.WriteI64(TraceId); 422 | oprot.WriteFieldEnd(); 423 | } 424 | if (Name != null && __isset.name) 425 | { 426 | field.Name = "name"; 427 | field.Type = TType.String; 428 | field.ID = 3; 429 | oprot.WriteFieldBegin(field); 430 | oprot.WriteString(Name); 431 | oprot.WriteFieldEnd(); 432 | } 433 | if (__isset.id) 434 | { 435 | field.Name = "id"; 436 | field.Type = TType.I64; 437 | field.ID = 4; 438 | oprot.WriteFieldBegin(field); 439 | oprot.WriteI64(Id); 440 | oprot.WriteFieldEnd(); 441 | } 442 | if (__isset.parent_id) 443 | { 444 | field.Name = "parent_id"; 445 | field.Type = TType.I64; 446 | field.ID = 5; 447 | oprot.WriteFieldBegin(field); 448 | oprot.WriteI64(ParentId); 449 | oprot.WriteFieldEnd(); 450 | } 451 | if (Annotations != null && __isset.annotations) 452 | { 453 | field.Name = "annotations"; 454 | field.Type = TType.List; 455 | field.ID = 6; 456 | oprot.WriteFieldBegin(field); 457 | { 458 | oprot.WriteListBegin(new TList(TType.Struct, Annotations.Count)); 459 | foreach (Annotation _iter6 in Annotations) 460 | { 461 | _iter6.Write(oprot); 462 | } 463 | oprot.WriteListEnd(); 464 | } 465 | oprot.WriteFieldEnd(); 466 | } 467 | if (BinaryAnnotations != null && __isset.binary_annotations) 468 | { 469 | field.Name = "binary_annotations"; 470 | field.Type = TType.List; 471 | field.ID = 8; 472 | oprot.WriteFieldBegin(field); 473 | { 474 | oprot.WriteListBegin(new TList(TType.Struct, BinaryAnnotations.Count)); 475 | foreach (BinaryAnnotation _iter7 in BinaryAnnotations) 476 | { 477 | _iter7.Write(oprot); 478 | } 479 | oprot.WriteListEnd(); 480 | } 481 | oprot.WriteFieldEnd(); 482 | } 483 | if (__isset.debug) 484 | { 485 | field.Name = "debug"; 486 | field.Type = TType.Bool; 487 | field.ID = 9; 488 | oprot.WriteFieldBegin(field); 489 | oprot.WriteBool(Debug); 490 | oprot.WriteFieldEnd(); 491 | } 492 | if (__isset.timestamp) 493 | { 494 | field.Name = "timestamp"; 495 | field.Type = TType.I64; 496 | field.ID = 10; 497 | oprot.WriteFieldBegin(field); 498 | oprot.WriteI64(Timestamp); 499 | oprot.WriteFieldEnd(); 500 | } 501 | if (__isset.duration) 502 | { 503 | field.Name = "duration"; 504 | field.Type = TType.I64; 505 | field.ID = 11; 506 | oprot.WriteFieldBegin(field); 507 | oprot.WriteI64(Duration); 508 | oprot.WriteFieldEnd(); 509 | } 510 | oprot.WriteFieldStop(); 511 | oprot.WriteStructEnd(); 512 | } 513 | finally 514 | { 515 | oprot.DecrementRecursionDepth(); 516 | } 517 | } 518 | 519 | public override string ToString() 520 | { 521 | StringBuilder __sb = new StringBuilder("Span("); 522 | bool __first = true; 523 | if (__isset.trace_id) 524 | { 525 | if (!__first) { __sb.Append(", "); } 526 | __first = false; 527 | __sb.Append("Trace_id: "); 528 | __sb.Append(TraceId); 529 | } 530 | if (Name != null && __isset.name) 531 | { 532 | if (!__first) { __sb.Append(", "); } 533 | __first = false; 534 | __sb.Append("Name: "); 535 | __sb.Append(Name); 536 | } 537 | if (__isset.id) 538 | { 539 | if (!__first) { __sb.Append(", "); } 540 | __first = false; 541 | __sb.Append("TraceId: "); 542 | __sb.Append(Id); 543 | } 544 | if (__isset.parent_id) 545 | { 546 | if (!__first) { __sb.Append(", "); } 547 | __first = false; 548 | __sb.Append("Parent_id: "); 549 | __sb.Append(ParentId); 550 | } 551 | if (Annotations != null && __isset.annotations) 552 | { 553 | if (!__first) { __sb.Append(", "); } 554 | __first = false; 555 | __sb.Append("Annotations: "); 556 | __sb.Append(Annotations); 557 | } 558 | if (BinaryAnnotations != null && __isset.binary_annotations) 559 | { 560 | if (!__first) { __sb.Append(", "); } 561 | __first = false; 562 | __sb.Append("Binary_annotations: "); 563 | __sb.Append(BinaryAnnotations); 564 | } 565 | if (__isset.debug) 566 | { 567 | if (!__first) { __sb.Append(", "); } 568 | __first = false; 569 | __sb.Append("Debug: "); 570 | __sb.Append(Debug); 571 | } 572 | if (__isset.timestamp) 573 | { 574 | if (!__first) { __sb.Append(", "); } 575 | __first = false; 576 | __sb.Append("Timestamp: "); 577 | __sb.Append(Timestamp); 578 | } 579 | if (__isset.duration) 580 | { 581 | if (!__first) { __sb.Append(", "); } 582 | __first = false; 583 | __sb.Append("Duration: "); 584 | __sb.Append(Duration); 585 | } 586 | __sb.Append(")"); 587 | return __sb.ToString(); 588 | } 589 | } 590 | } 591 | -------------------------------------------------------------------------------- /src/Zipkin.Core/Thrift/ThriftExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using Zipkin; 4 | 5 | namespace Zipkin.Thrift 6 | { 7 | internal static class ThriftExtensions 8 | { 9 | public static Annotation ToThrift(this Zipkin.Annotation annotation) 10 | { 11 | return new Annotation(annotation.Timestamp.ToUnixMicroseconds(), annotation.Value, annotation.Endpoint.ToThrift()); 12 | } 13 | public static BinaryAnnotation ToThrift(this Zipkin.BinaryAnnotation annotation) 14 | { 15 | return new BinaryAnnotation(annotation.Key, annotation.Value, annotation.AnnotationType, annotation.Endpoint.ToThrift()); 16 | } 17 | 18 | public static readonly DateTime UnixEpochStart = new DateTime(1970, 1, 1); 19 | public static long ToUnixMicroseconds(this DateTime date) 20 | { 21 | return (date.Ticks - UnixEpochStart.Ticks)/AnnotationConstants.TicksPerMicosecond; 22 | } 23 | 24 | public static Span ToThrift(this Zipkin.Span span) 25 | { 26 | var s = new Span 27 | { 28 | TraceId = (long)span.TraceHeader.TraceId, 29 | Id = (long)span.TraceHeader.SpanId, 30 | Name = span.Name, 31 | Debug = span.TraceHeader.IsDebug 32 | }; 33 | 34 | if (span.TraceHeader.ParentId.HasValue) s.ParentId = (long)span.TraceHeader.ParentId.Value; 35 | 36 | s.__isset.annotations = span.Annotations.Count != 0; 37 | foreach (var annotation in span.Annotations) 38 | { 39 | var thrifted = annotation.ToThrift(); 40 | var ep = thrifted.__isset.host ? thrifted.Host : span.Endpoint.ToThrift(); 41 | ep.ServiceName = span.ServiceName; 42 | thrifted.Host = ep; 43 | s.Annotations.Add(thrifted); 44 | } 45 | 46 | s.__isset.binary_annotations = span.BinaryAnnotations.Count != 0; 47 | foreach (var binaryAnnotation in span.BinaryAnnotations) 48 | { 49 | var thrifted = binaryAnnotation.ToThrift(); 50 | var ep = thrifted.__isset.host ? thrifted.Host : span.Endpoint.ToThrift(); 51 | ep.ServiceName = span.ServiceName; 52 | thrifted.Host = ep; 53 | s.BinaryAnnotations.Add(thrifted); 54 | } 55 | 56 | return s; 57 | } 58 | 59 | public static Endpoint ToThrift(this IPEndPoint endpoint) 60 | { 61 | return new Endpoint(BitConverter.ToInt32(endpoint.Address.GetAddressBytes(), 0), (short)endpoint.Port, string.Empty); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/Thrift/ThriftSpanSerializer.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Thrift.Protocol; 3 | using Thrift.Transport; 4 | 5 | namespace Zipkin.Thrift 6 | { 7 | /// 8 | /// Serializer able to serialize collections into thrift-encoded binaries. 9 | /// 10 | public static class ThriftSpanSerializer 11 | { 12 | /// 13 | /// Serializes array of spans using Thrift protocol. 14 | /// 15 | public static void WriteSpans(Zipkin.Span[] spans, Stream outputStream) 16 | { 17 | var transport = new TStreamTransport(null, outputStream); 18 | var protocol = new TBinaryProtocol(transport); 19 | 20 | protocol.WriteListBegin(new TList(TType.Struct, spans.Length)); 21 | foreach (var span in spans) 22 | { 23 | var thrift = span.ToThrift(); 24 | thrift.Write(protocol); 25 | } 26 | protocol.WriteListEnd(); 27 | transport.Flush(); 28 | 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/TraceHeader.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Text; 9 | 10 | namespace Zipkin 11 | { 12 | /// 13 | /// A structure containing all of the data necessary to identify span and it's trace among others. 14 | /// 15 | public struct TraceHeader : IEquatable, IComparable 16 | { 17 | /// 18 | /// Name of a HTTP header that could contain information encoded as hex string of 64-bit unsigned integer. 19 | /// 20 | public const string TraceIdHttpHeaderName = "X-B3-TraceId"; 21 | 22 | /// 23 | /// Name of a HTTP header that could contain information encoded as hex string of 64-bit unsigned integer. 24 | /// 25 | public const string SpanIdHttpHeaderName = "X-B3-SpanId"; 26 | 27 | /// 28 | /// Name of a HTTP header that could contain information encoded as hex string of 64-bit unsigned integer. 29 | /// 30 | public const string ParentIdHttpHeaderName = "X-B3-ParentSpanId"; 31 | 32 | /// 33 | /// Name of a HTTP header that could contain information encoded as 1 (true) or 0 (false). 34 | /// 35 | public const string SampledHttpHeaderName = "X-B3-Sampled"; 36 | 37 | /// 38 | /// The overall ID of the trace. Every span in a trace will share this ID. 39 | /// 40 | public readonly ulong TraceId; 41 | 42 | /// 43 | /// The ID for a particular span. This may or may not be the same as the trace id. 44 | /// 45 | public readonly ulong SpanId; 46 | 47 | /// 48 | /// This is an optional ID that will only be present on child spans. 49 | /// That is the span without a parent id is considered the root of the trace. 50 | /// 51 | public readonly ulong? ParentId; 52 | 53 | /// 54 | /// Marks current span with debug flag. 55 | /// 56 | public readonly bool IsDebug; 57 | 58 | public TraceHeader(ulong traceId, ulong spanId, ulong? parentId = null, bool isDebug = false) 59 | { 60 | TraceId = traceId; 61 | SpanId = spanId; 62 | ParentId = parentId; 63 | IsDebug = isDebug; 64 | } 65 | 66 | /// 67 | /// Creates a trace header for the new span being a child of the span identified by current trace header. 68 | /// 69 | public TraceHeader Child(ulong childId) => new TraceHeader(TraceId, childId, SpanId, IsDebug); 70 | 71 | public bool Equals(TraceHeader other) 72 | { 73 | return other.TraceId == TraceId && other.SpanId == SpanId && other.ParentId == ParentId; 74 | } 75 | 76 | public override bool Equals(object obj) 77 | { 78 | return obj is TraceHeader && Equals((TraceHeader)obj); 79 | } 80 | 81 | public override int GetHashCode() 82 | { 83 | unchecked 84 | { 85 | var hashCode = TraceId.GetHashCode(); 86 | hashCode = (hashCode*397) ^ SpanId.GetHashCode(); 87 | hashCode = (hashCode*397) ^ ParentId.GetHashCode(); 88 | return hashCode; 89 | } 90 | } 91 | 92 | public int CompareTo(TraceHeader other) 93 | { 94 | var tcmp = TraceId.CompareTo(other.TraceId); 95 | if (tcmp == 0) 96 | { 97 | var pcmp = ParentId.GetValueOrDefault(0).CompareTo(other.ParentId.GetValueOrDefault(0)); 98 | if (pcmp == 0) 99 | { 100 | return SpanId.CompareTo(other.SpanId); 101 | } 102 | else return pcmp; 103 | } 104 | else return tcmp; 105 | } 106 | 107 | public override string ToString() 108 | { 109 | var sb = new StringBuilder("TraceHeader(traceId:").Append(TraceId).Append(", spanId: ").Append(SpanId); 110 | 111 | if (ParentId.HasValue) 112 | { 113 | sb.Append(", parentId: ").Append(ParentId); 114 | } 115 | 116 | sb.Append(")"); 117 | return sb.ToString(); 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/Zipkin.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4BB262E2-3927-4E75-952A-F0CF8DDB36B9} 8 | Library 9 | Properties 10 | Zipkin.Core 11 | Zipkin.Core 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ..\..\packages\ApacheThrift.0.9.3\lib\Thrift.dll 42 | True 43 | 44 | 45 | 46 | 47 | Properties\CommonAssemblyInfo.cs 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 81 | -------------------------------------------------------------------------------- /src/Zipkin.Core/Zipkin.Core.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Zipkin.Core 5 | $title$ 6 | $version$ 7 | $author$ 8 | $author$ 9 | false 10 | https://github.com/openzipkin/zipkin-csharp/blob/master/LICENSE 11 | https://github.com/openzipkin/zipkin-csharp 12 | $description$ 13 | $description$ 14 | Nuget package 15 | Openzipkin 2017 16 | zipkin tracing distributed performance 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Zipkin.Core/ZipkinCollectorException.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Runtime.Serialization; 9 | 10 | namespace Zipkin 11 | { 12 | [Serializable] 13 | public class ZipkinCollectorException : Exception 14 | { 15 | public ZipkinCollectorException() 16 | { 17 | } 18 | 19 | public ZipkinCollectorException(string message) : base(message) 20 | { 21 | } 22 | 23 | public ZipkinCollectorException(string message, Exception inner) : base(message, inner) 24 | { 25 | } 26 | 27 | protected ZipkinCollectorException( 28 | SerializationInfo info, 29 | StreamingContext context) : base(info, context) 30 | { 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Zipkin.Core/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /tests/Zipkin.Core.Tests/AnnotationTest.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Net; 9 | using Xunit; 10 | using Zipkin.Thrift; 11 | 12 | namespace Zipkin.Tracer.Tests 13 | { 14 | public class AnnotationTest 15 | { 16 | [Fact] 17 | public void Annotation_should_serialize_properly() 18 | { 19 | var annotation = new Annotation("value", new DateTime(2016, 10, 1), new IPEndPoint(IPAddress.Loopback, 1233)); 20 | var thrifted = annotation.ToThrift(); 21 | 22 | Assert.True(thrifted.__isset.host, "Serialized annotation host not set"); 23 | Assert.Equal(16777343, thrifted.Host.Ipv4); // System.BitConverter.ToInt32(IPAddress.Loopback.GetAddressBytes(), 0) 24 | Assert.Equal(annotation.Endpoint.Port, thrifted.Host.Port); 25 | Assert.True(thrifted.__isset.value, "Serialized annotation value not set"); 26 | Assert.Equal(annotation.Value, thrifted.Value); 27 | Assert.True(thrifted.__isset.timestamp, "Serialized annotation timestamp not set"); 28 | Assert.Equal((annotation.Timestamp.Ticks - new DateTime(1970, 1, 1).Ticks) / AnnotationConstants.TicksPerMicosecond, thrifted.Timestamp); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /tests/Zipkin.Core.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Zipkin.Tracer.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Zipkin.Tracer.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("baffcfd1-84ef-4896-a28c-eb97500f5224")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /tests/Zipkin.Core.Tests/SpanTest.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // Copyright (C) 2016 Bazinga Technologies Inc. 4 | // 5 | //----------------------------------------------------------------------- 6 | 7 | using System; 8 | using System.Net; 9 | using Xunit; 10 | using Zipkin.Thrift; 11 | 12 | namespace Zipkin.Tracer.Tests 13 | { 14 | public class SpanTest 15 | { 16 | [Fact] 17 | public void Span_should_serialize_properly() 18 | { 19 | var annotation = new Annotation("value", DateTime.Now, new IPEndPoint(IPAddress.Any, 2222)); 20 | var traceId = new TraceHeader(123, 234, 345, true); 21 | var span = new Span(traceId, new IPEndPoint(IPAddress.Loopback, 1111), "service", "name"); 22 | span.Record(annotation); 23 | 24 | var thrifted = span.ToThrift(); 25 | var host = thrifted.Annotations[0].Host; 26 | 27 | Assert.Equal("service", host.ServiceName); 28 | Assert.True(thrifted.__isset.name, "Serialized span name not set"); 29 | Assert.Equal("name", thrifted.Name); 30 | Assert.False(thrifted.__isset.binary_annotations, "Serialized span binary annotations should not be set"); 31 | Assert.True(thrifted.__isset.trace_id, "Serialized span trace_id not set"); 32 | Assert.Equal(123, thrifted.TraceId); 33 | Assert.True(thrifted.__isset.id, "Serialized span id not set"); 34 | Assert.Equal(234, thrifted.Id); 35 | Assert.True(thrifted.__isset.parent_id, "Serialized span parent_id not set"); 36 | Assert.Equal(345, thrifted.ParentId); 37 | Assert.True(thrifted.Debug); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /tests/Zipkin.Core.Tests/Zipkin.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {BAFFCFD1-84EF-4896-A28C-EB97500F5224} 9 | Library 10 | Properties 11 | Zipkin.Core.Tests 12 | Zipkin.Core.Tests 13 | v4.5 14 | 512 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ..\..\packages\ApacheThrift.0.9.3\lib\Thrift.dll 46 | True 47 | 48 | 49 | ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll 50 | True 51 | 52 | 53 | ..\..\packages\xunit.assert.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.assert.dll 54 | True 55 | 56 | 57 | ..\..\packages\xunit.extensibility.core.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core.dll 58 | True 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {4bb262e2-3927-4e75-952a-f0cf8ddb36b9} 69 | Zipkin.Core 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 82 | 83 | 84 | 85 | 92 | -------------------------------------------------------------------------------- /tests/Zipkin.Core.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tools/thrift/scribe.thrift: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Twitter Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | namespace java com.twitter.zipkin.thriftjava 15 | #@namespace scala com.twitter.zipkin.thriftscala 16 | 17 | enum ResultCode 18 | { 19 | OK, 20 | TRY_LATER 21 | } 22 | 23 | struct LogEntry 24 | { 25 | 1: string category, 26 | 2: string message 27 | } 28 | 29 | service Scribe 30 | { 31 | ResultCode Log(1: list messages); 32 | } 33 | -------------------------------------------------------------------------------- /tools/thrift/thrift-0.9.3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openzipkin-attic/zipkin-csharp/872ee914c5e3509acaecbb3578aa81ce380eee8a/tools/thrift/thrift-0.9.3.exe -------------------------------------------------------------------------------- /tools/thrift/zipkinCore.thrift: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Twitter Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | namespace java com.twitter.zipkin.thriftjava 15 | #@namespace scala com.twitter.zipkin.thriftscala 16 | namespace rb Zipkin 17 | 18 | #************** Annotation.value ************** 19 | /** 20 | * The client sent ("cs") a request to a server. There is only one send per 21 | * span. For example, if there's a transport error, each attempt can be logged 22 | * as a WIRE_SEND annotation. 23 | * 24 | * If chunking is involved, each chunk could be logged as a separate 25 | * CLIENT_SEND_FRAGMENT in the same span. 26 | * 27 | * Annotation.host is not the server. It is the host which logged the send 28 | * event, almost always the client. When logging CLIENT_SEND, instrumentation 29 | * should also log the SERVER_ADDR. 30 | */ 31 | const string CLIENT_SEND = "cs" 32 | /** 33 | * The client received ("cr") a response from a server. There is only one 34 | * receive per span. For example, if duplicate responses were received, each 35 | * can be logged as a WIRE_RECV annotation. 36 | * 37 | * If chunking is involved, each chunk could be logged as a separate 38 | * CLIENT_RECV_FRAGMENT in the same span. 39 | * 40 | * Annotation.host is not the server. It is the host which logged the receive 41 | * event, almost always the client. The actual endpoint of the server is 42 | * recorded separately as SERVER_ADDR when CLIENT_SEND is logged. 43 | */ 44 | const string CLIENT_RECV = "cr" 45 | /** 46 | * The server sent ("ss") a response to a client. There is only one response 47 | * per span. If there's a transport error, each attempt can be logged as a 48 | * WIRE_SEND annotation. 49 | * 50 | * Typically, a trace ends with a server send, so the last timestamp of a trace 51 | * is often the timestamp of the root span's server send. 52 | * 53 | * If chunking is involved, each chunk could be logged as a separate 54 | * SERVER_SEND_FRAGMENT in the same span. 55 | * 56 | * Annotation.host is not the client. It is the host which logged the send 57 | * event, almost always the server. The actual endpoint of the client is 58 | * recorded separately as CLIENT_ADDR when SERVER_RECV is logged. 59 | */ 60 | const string SERVER_SEND = "ss" 61 | /** 62 | * The server received ("sr") a request from a client. There is only one 63 | * request per span. For example, if duplicate responses were received, each 64 | * can be logged as a WIRE_RECV annotation. 65 | * 66 | * Typically, a trace starts with a server receive, so the first timestamp of a 67 | * trace is often the timestamp of the root span's server receive. 68 | * 69 | * If chunking is involved, each chunk could be logged as a separate 70 | * SERVER_RECV_FRAGMENT in the same span. 71 | * 72 | * Annotation.host is not the client. It is the host which logged the receive 73 | * event, almost always the server. When logging SERVER_RECV, instrumentation 74 | * should also log the CLIENT_ADDR. 75 | */ 76 | const string SERVER_RECV = "sr" 77 | /** 78 | * Optionally logs an attempt to send a message on the wire. Multiple wire send 79 | * events could indicate network retries. A lag between client or server send 80 | * and wire send might indicate queuing or processing delay. 81 | */ 82 | const string WIRE_SEND = "ws" 83 | /** 84 | * Optionally logs an attempt to receive a message from the wire. Multiple wire 85 | * receive events could indicate network retries. A lag between wire receive 86 | * and client or server receive might indicate queuing or processing delay. 87 | */ 88 | const string WIRE_RECV = "wr" 89 | /** 90 | * Optionally logs progress of a (CLIENT_SEND, WIRE_SEND). For example, this 91 | * could be one chunk in a chunked request. 92 | */ 93 | const string CLIENT_SEND_FRAGMENT = "csf" 94 | /** 95 | * Optionally logs progress of a (CLIENT_RECV, WIRE_RECV). For example, this 96 | * could be one chunk in a chunked response. 97 | */ 98 | const string CLIENT_RECV_FRAGMENT = "crf" 99 | /** 100 | * Optionally logs progress of a (SERVER_SEND, WIRE_SEND). For example, this 101 | * could be one chunk in a chunked response. 102 | */ 103 | const string SERVER_SEND_FRAGMENT = "ssf" 104 | /** 105 | * Optionally logs progress of a (SERVER_RECV, WIRE_RECV). For example, this 106 | * could be one chunk in a chunked request. 107 | */ 108 | const string SERVER_RECV_FRAGMENT = "srf" 109 | 110 | #***** BinaryAnnotation.key ****** 111 | /** 112 | * The domain portion of the URL or host header. Ex. "mybucket.s3.amazonaws.com" 113 | * 114 | * Used to filter by host as opposed to ip address. 115 | */ 116 | const string HTTP_HOST = "http.host" 117 | 118 | /** 119 | * The HTTP method, or verb, such as "GET" or "POST". 120 | * 121 | * Used to filter against an http route. 122 | */ 123 | const string HTTP_METHOD = "http.method" 124 | 125 | /** 126 | * The absolute http path, without any query parameters. Ex. "/objects/abcd-ff" 127 | * 128 | * Used to filter against an http route, portably with zipkin v1. 129 | * 130 | * In zipkin v1, only equals filters are supported. Dropping query parameters makes the number 131 | * of distinct URIs less. For example, one can query for the same resource, regardless of signing 132 | * parameters encoded in the query line. This does not reduce cardinality to a HTTP single route. 133 | * For example, it is common to express a route as an http URI template like 134 | * "/resource/{resource_id}". In systems where only equals queries are available, searching for 135 | * http/path=/resource won't match if the actual request was /resource/abcd-ff. 136 | * 137 | * Historical note: This was commonly expressed as "http.uri" in zipkin, eventhough it was most 138 | * often just a path. 139 | */ 140 | const string HTTP_PATH = "http.path" 141 | 142 | /** 143 | * The entire URL, including the scheme, host and query parameters if available. Ex. 144 | * "https://mybucket.s3.amazonaws.com/objects/abcd-ff?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Algorithm=AWS4-HMAC-SHA256..." 145 | * 146 | * Combined with HTTP_METHOD, you can understand the fully-qualified request line. 147 | * 148 | * This is optional as it may include private data or be of considerable length. 149 | */ 150 | const string HTTP_URL = "http.url" 151 | 152 | /** 153 | * The HTTP status code, when not in 2xx range. Ex. "503" 154 | * 155 | * Used to filter for error status. 156 | */ 157 | const string HTTP_STATUS_CODE = "http.status_code" 158 | 159 | /** 160 | * The size of the non-empty HTTP request body, in bytes. Ex. "16384" 161 | * 162 | * Large uploads can exceed limits or contribute directly to latency. 163 | */ 164 | const string HTTP_REQUEST_SIZE = "http.request.size" 165 | 166 | /** 167 | * The size of the non-empty HTTP response body, in bytes. Ex. "16384" 168 | * 169 | * Large downloads can exceed limits or contribute directly to latency. 170 | */ 171 | const string HTTP_RESPONSE_SIZE = "http.response.size" 172 | 173 | 174 | /** 175 | * The value of "lc" is the component or namespace of a local span. 176 | * 177 | * BinaryAnnotation.host adds service context needed to support queries. 178 | * 179 | * Local Component("lc") supports three key features: flagging, query by 180 | * service and filtering Span.name by namespace. 181 | * 182 | * While structurally the same, local spans are fundamentally different than 183 | * RPC spans in how they should be interpreted. For example, zipkin v1 tools 184 | * center on RPC latency and service graphs. Root local-spans are neither 185 | * indicative of critical path RPC latency, nor have impact on the shape of a 186 | * service graph. By flagging with "lc", tools can special-case local spans. 187 | * 188 | * Zipkin v1 Spans are unqueryable unless they can be indexed by service name. 189 | * The only path to a service name is by (Binary)?Annotation.host.serviceName. 190 | * By logging "lc", a local span can be queried even if no other annotations 191 | * are logged. 192 | * 193 | * The value of "lc" is the namespace of Span.name. For example, it might be 194 | * "finatra2", for a span named "bootstrap". "lc" allows you to resolves 195 | * conflicts for the same Span.name, for example "finatra/bootstrap" vs 196 | * "finch/bootstrap". Using local component, you'd search for spans named 197 | * "bootstrap" where "lc=finch" 198 | */ 199 | const string LOCAL_COMPONENT = "lc" 200 | 201 | #***** BinaryAnnotation.key where value = [1] and annotation_type = BOOL ****** 202 | /** 203 | * Indicates a client address ("ca") in a span. Most likely, there's only one. 204 | * Multiple addresses are possible when a client changes its ip or port within 205 | * a span. 206 | */ 207 | const string CLIENT_ADDR = "ca" 208 | /** 209 | * Indicates a server address ("sa") in a span. Most likely, there's only one. 210 | * Multiple addresses are possible when a client is redirected, or fails to a 211 | * different server ip or port. 212 | */ 213 | const string SERVER_ADDR = "sa" 214 | 215 | /** 216 | * Indicates the network context of a service recording an annotation with two 217 | * exceptions. 218 | * 219 | * When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, 220 | * the endpoint indicates the source or destination of an RPC. This exception 221 | * allows zipkin to display network context of uninstrumented services, or 222 | * clients such as web browsers. 223 | */ 224 | struct Endpoint { 225 | /** 226 | * IPv4 host address packed into 4 bytes. 227 | * 228 | * Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 229 | */ 230 | 1: i32 ipv4 231 | /** 232 | * IPv4 port or 0, if unknown. 233 | * 234 | * Note: this is to be treated as an unsigned integer, so watch for negatives. 235 | */ 236 | 2: i16 port 237 | /** 238 | * Classifier of a source or destination in lowercase, such as "zipkin-web". 239 | * 240 | * This is the primary parameter for trace lookup, so should be intuitive as 241 | * possible, for example, matching names in service discovery. 242 | * 243 | * Conventionally, when the service name isn't known, service_name = "unknown". 244 | * However, it is also permissible to set service_name = "" (empty string). 245 | * The difference in the latter usage is that the span will not be queryable 246 | * by service name unless more information is added to the span with non-empty 247 | * service name, e.g. an additional annotation from the server. 248 | * 249 | * Particularly clients may not have a reliable service name at ingest. One 250 | * approach is to set service_name to "" at ingest, and later assign a 251 | * better label based on binary annotations, such as user agent. 252 | */ 253 | 3: string service_name 254 | } 255 | 256 | /** 257 | * Associates an event that explains latency with a timestamp. 258 | * 259 | * Unlike log statements, annotations are often codes: for example "sr". 260 | */ 261 | struct Annotation { 262 | /** 263 | * Microseconds from epoch. 264 | * 265 | * This value should use the most precise value possible. For example, 266 | * gettimeofday or syncing nanoTime against a tick of currentTimeMillis. 267 | */ 268 | 1: i64 timestamp 269 | /** 270 | * Usually a short tag indicating an event, like "sr" or "finagle.retry". 271 | */ 272 | 2: string value 273 | /** 274 | * The host that recorded the value, primarily for query by service name. 275 | */ 276 | 3: optional Endpoint host 277 | // don't reuse 4: optional i32 OBSOLETE_duration // how long did the operation take? microseconds 278 | } 279 | 280 | /** 281 | * A subset of thrift base types, except BYTES. 282 | */ 283 | enum AnnotationType { 284 | /** 285 | * Set to 0x01 when key is CLIENT_ADDR or SERVER_ADDR 286 | */ 287 | BOOL, 288 | /** 289 | * No encoding, or type is unknown. 290 | */ 291 | BYTES, 292 | I16, 293 | I32, 294 | I64, 295 | DOUBLE, 296 | /** 297 | * the only type zipkin v1 supports search against. 298 | */ 299 | STRING 300 | } 301 | 302 | /** 303 | * Binary annotations are tags applied to a Span to give it context. For 304 | * example, a binary annotation of HTTP_PATH ("http.path") could the path 305 | * to a resource in a RPC call. 306 | * 307 | * Binary annotations of type STRING are always queryable, though more a 308 | * historical implementation detail than a structural concern. 309 | * 310 | * Binary annotations can repeat, and vary on the host. Similar to Annotation, 311 | * the host indicates who logged the event. This allows you to tell the 312 | * difference between the client and server side of the same key. For example, 313 | * the key "http.path" might be different on the client and server side due to 314 | * rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, 315 | * you can see the different points of view, which often help in debugging. 316 | */ 317 | struct BinaryAnnotation { 318 | /** 319 | * Name used to lookup spans, such as "http.path" or "finagle.version". 320 | */ 321 | 1: string key, 322 | /** 323 | * Serialized thrift bytes, in TBinaryProtocol format. 324 | * 325 | * For legacy reasons, byte order is big-endian. See THRIFT-3217. 326 | */ 327 | 2: binary value, 328 | /** 329 | * The thrift type of value, most often STRING. 330 | * 331 | * annotation_type shouldn't vary for the same key. 332 | */ 333 | 3: AnnotationType annotation_type, 334 | /** 335 | * The host that recorded value, allowing query by service name or address. 336 | * 337 | * There are two exceptions: when key is "ca" or "sa", this is the source or 338 | * destination of an RPC. This exception allows zipkin to display network 339 | * context of uninstrumented services, such as browsers or databases. 340 | */ 341 | 4: optional Endpoint host 342 | } 343 | 344 | /** 345 | * A trace is a series of spans (often RPC calls) which form a latency tree. 346 | * 347 | * Spans are usually created by instrumentation in RPC clients or servers, but 348 | * can also represent in-process activity. Annotations in spans are similar to 349 | * log statements, and are sometimes created directly by application developers 350 | * to indicate events of interest, such as a cache miss. 351 | * 352 | * The root span is where parent_id = Nil; it usually has the longest duration 353 | * in the trace. 354 | * 355 | * Span identifiers are packed into i64s, but should be treated opaquely. 356 | * String encoding is fixed-width lower-hex, to avoid signed interpretation. 357 | */ 358 | struct Span { 359 | /** 360 | * Unique 8-byte identifier for a trace, set on all spans within it. 361 | */ 362 | 1: i64 trace_id 363 | /** 364 | * Span name in lowercase, rpc method for example. Conventionally, when the 365 | * span name isn't known, name = "unknown". 366 | */ 367 | 3: string name, 368 | /** 369 | * Unique 8-byte identifier of this span within a trace. A span is uniquely 370 | * identified in storage by (trace_id, id). 371 | */ 372 | 4: i64 id, 373 | /** 374 | * The parent's Span.id; absent if this the root span in a trace. 375 | */ 376 | 5: optional i64 parent_id, 377 | /** 378 | * Associates events that explain latency with a timestamp. Unlike log 379 | * statements, annotations are often codes: for example SERVER_RECV("sr"). 380 | * Annotations are sorted ascending by timestamp. 381 | */ 382 | 6: list annotations, 383 | /** 384 | * Tags a span with context, usually to support query or aggregation. For 385 | * example, a binary annotation key could be "http.path". 386 | */ 387 | 8: list binary_annotations 388 | /** 389 | * True is a request to store this span even if it overrides sampling policy. 390 | */ 391 | 9: optional bool debug = 0 392 | /** 393 | * Epoch microseconds of the start of this span, absent if this an incomplete 394 | * span. 395 | * 396 | * This value should be set directly by instrumentation, using the most 397 | * precise value possible. For example, gettimeofday or syncing nanoTime 398 | * against a tick of currentTimeMillis. 399 | * 400 | * For compatibilty with instrumentation that precede this field, collectors 401 | * or span stores can derive this via Annotation.timestamp. 402 | * For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. 403 | * 404 | * Timestamp is nullable for input only. Spans without a timestamp cannot be 405 | * presented in a timeline: Span stores should not output spans missing a 406 | * timestamp. 407 | * 408 | * There are two known edge-cases where this could be absent: both cases 409 | * exist when a collector receives a span in parts and a binary annotation 410 | * precedes a timestamp. This is possible when.. 411 | * - The span is in-flight (ex not yet received a timestamp) 412 | * - The span's start event was lost 413 | */ 414 | 10: optional i64 timestamp, 415 | /** 416 | * Measurement in microseconds of the critical path, if known. 417 | * 418 | * This value should be set directly, as opposed to implicitly via annotation 419 | * timestamps. Doing so encourages precision decoupled from problems of 420 | * clocks, such as skew or NTP updates causing time to move backwards. 421 | * 422 | * For compatibility with instrumentation that precede this field, collectors 423 | * or span stores can derive this by subtracting Annotation.timestamp. 424 | * For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. 425 | * 426 | * If this field is persisted as unset, zipkin will continue to work, except 427 | * duration query support will be implementation-specific. Similarly, setting 428 | * this field non-atomically is implementation-specific. 429 | * 430 | * This field is i64 vs i32 to support spans longer than 35 minutes. 431 | */ 432 | 11: optional i64 duration 433 | } 434 | 435 | -------------------------------------------------------------------------------- /tools/thrift/zipkinDependencies.thrift: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Twitter Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | namespace java com.twitter.zipkin.thriftjava 15 | #@namespace scala com.twitter.zipkin.thriftscala 16 | namespace rb Zipkin 17 | 18 | struct DependencyLink { 19 | /** parent service name (caller) */ 20 | 1: string parent 21 | /** child service name (callee) */ 22 | 2: string child 23 | # 3: Moments OBSOLETE_duration_moments 24 | /** calls made during the duration of this link */ 25 | 4: i64 callCount 26 | # histogram? 27 | } 28 | 29 | /* An aggregate representation of services paired with every service they call. */ 30 | struct Dependencies { 31 | /** milliseconds from epoch */ 32 | 1: i64 start_ts 33 | /** milliseconds from epoch */ 34 | 2: i64 end_ts 35 | 3: list links 36 | } 37 | -------------------------------------------------------------------------------- /tools/zipkin/run.bat: -------------------------------------------------------------------------------- 1 | java -jar zipkin-server-0.18.2-exec.jar 2 | -------------------------------------------------------------------------------- /tools/zipkin/zipkin-server-0.18.2-exec.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openzipkin-attic/zipkin-csharp/872ee914c5e3509acaecbb3578aa81ce380eee8a/tools/zipkin/zipkin-server-0.18.2-exec.jar --------------------------------------------------------------------------------