├── .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
--------------------------------------------------------------------------------