├── .circleci
└── config.yml
├── .editorconfig
├── .gitignore
├── .vscode
└── tasks.json
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── LightStep.sln
├── NuGet.config
├── README.md
├── build.cake
├── build.sh
├── examples
├── LightStep.CSharpAspectTestApp
│ ├── App.config
│ ├── Aspects
│ │ └── Traceable.cs
│ ├── HttpWorker.cs
│ ├── LightStep.CSharpAspectTestApp.csproj
│ ├── Program.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── README.md
│ └── packages.config
├── LightStep.CSharpDITestApp
│ ├── LightStep.CSharpDITestApp.csproj
│ ├── Program.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── packages.config
└── LightStep.CSharpTestApp
│ ├── LightStep.CSharpTestApp.csproj
│ └── Program.cs
├── ls128x128.png
├── src
└── LightStep
│ ├── Baggage.cs
│ ├── Collector
│ ├── Collector.cs
│ ├── ILightStepHttpClient.cs
│ ├── IReportTranslator.cs
│ ├── LightStepHttpClient.cs
│ ├── ProtoConverter.cs
│ ├── ProtoConverterExtensions.cs
│ └── ReportTranslator.cs
│ ├── HighResolutionDateTime.cs
│ ├── ISpanRecorder.cs
│ ├── LightStep.cs
│ ├── LightStep.csproj
│ ├── LightStepConstants.cs
│ ├── LightStepSpanRecorder.cs
│ ├── LogData.cs
│ ├── Options.cs
│ ├── Propagation
│ ├── B3Propagator.cs
│ ├── BinaryPropagator.cs
│ ├── ConsolePropagator.cs
│ ├── EnvoyPropagator.cs
│ ├── HttpHeadersPropagator.cs
│ ├── IPropagator.cs
│ ├── Keys.cs
│ ├── PropagatorStack.cs
│ ├── Propagators.cs
│ └── TextMapPropagator.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── Reference.cs
│ ├── SatelliteOptions.cs
│ ├── Span.cs
│ ├── SpanBuilder.cs
│ ├── SpanContext.cs
│ ├── SpanData.cs
│ ├── Tracer.cs
│ ├── TransportOptions.cs
│ ├── TypedContextExtensions.cs
│ ├── Utilities.cs
│ ├── collector.proto
│ ├── google
│ ├── api
│ │ ├── annotations.proto
│ │ └── http.proto
│ └── protobuf
│ │ └── timestamp.proto
│ └── lightstep.proto
├── test
├── LightStep.Tests
│ ├── LightStep.Tests.csproj
│ ├── LightStepProtoTests.cs
│ ├── MockLogProvider.cs
│ ├── PropagatorTests.cs
│ ├── SimpleMockRecorder.cs
│ ├── SpanTests.cs
│ └── TracerLogTest.cs
└── LightStep.TracerPerf.Tests
│ ├── LightStep.TracerPerf.Tests.csproj
│ ├── README.md
│ ├── TracerLogTest.cs
│ ├── TracerMemoryTest.cs
│ ├── TracerNetworkTest.cs
│ └── TracerTestBase.cs
└── tracerSign.snk
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build-and-test:
4 | docker:
5 | - image: aparker/circleci-dotnet-mono:latest
6 | steps:
7 | - checkout
8 | - run: dotnet tool install -g dotnet-xunit-to-junit
9 | - run: dotnet tool install -g Cake.Tool
10 | - run: export PATH="$PATH:/root/.dotnet/tools"
11 | - run:
12 | name: Update PATH and Define Environment Variable at Runtime
13 | command: |
14 | echo 'export PATH="$PATH:/root/.dotnet/tools"' >> $BASH_ENV
15 | source $BASH_ENV
16 | - run: dotnet cake build.cake --target=Test
17 | - run: dotnet xunit-to-junit ./build/test_results.xml ./build/xunit/LightStep.Tests.dll.junit.xml
18 | - run: bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports"
19 | - store_test_results:
20 | path: build
21 | publish:
22 | docker:
23 | - image: aparker/circleci-dotnet-mono:latest
24 | steps:
25 | - checkout
26 | - run: dotnet tool install -g dotnet-xunit-to-junit
27 | - run: dotnet tool install -g Cake.Tool
28 | - run:
29 | name: Update PATH and Define Environment Variable at Runtime
30 | command: |
31 | echo 'export PATH="$PATH:/root/.dotnet/tools"' >> $BASH_ENV
32 | source $BASH_ENV
33 | - run: dotnet cake build.cake --target=Publish
34 | - run: dotnet xunit-to-junit ./build/test_results.xml ./build/xunit/LightStep.Tests.dll.junit.xml
35 | - run: bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports"
36 | - store_test_results:
37 | path: build
38 |
39 | workflows:
40 | version: 2
41 | untagged-build:
42 | jobs:
43 | - build-and-test
44 | tagged-build:
45 | jobs:
46 | - publish:
47 | filters:
48 | branches:
49 | ignore: /.*/
50 | tags:
51 | only: /^v.*/
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 4
7 | insert_final_newline = false
8 | trim_trailing_whitespace = true
9 |
10 | [*.sln]
11 | indent_style = tab
12 |
13 | [*.csproj]
14 | indent_size = 2
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
332 | # cakebuild tools
333 | tools/
334 |
335 | dist/
336 | build/
337 |
338 | # this is generated at build, ignoring it here
339 | src/LightStep/Properties/AssemblyInfo.cs
340 |
341 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "type": "cake",
8 | "script": "Default",
9 | "group": {
10 | "kind": "build",
11 | "isDefault": true
12 | }
13 | },
14 | {
15 | "type": "cake",
16 | "script": "Test",
17 | "problemMatcher": [],
18 | "group": "test"
19 | },
20 | {
21 | "type": "cake",
22 | "script": "Clean",
23 | "problemMatcher": []
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | _v0.12.0_
3 | *Note* This release modifies the `Tracer` object to make it disposable. You may wish to check your integrations and wrappers to ensure that this doesn't break anything.
4 | - The `Tracer` now implements `IDisposable`. This is intended to be used by implementors to add an "off" switch that can stop span recording.
5 | - The version of certain Protobuf libraries required has been relaxed to it's pre-0.11 state.
6 | - We no longer make a copy of the span buffer to count how many spans are in it in certain cases, improving performance.
7 |
8 | _v0.11.0_
9 | *Note* This release changes the public API surface. It should not impact you, but it might if you're manging span contexts manually.
10 | - The `SpanContext` signature has changed and parent span ID is now a ulong as well (this continues work released in 0.8.0). Performance should be improved for serialization to proto.
11 | - The minimum target framework is now .NET 4.7.2. Packages have been updated as well. Performance is up, memory consumption and object allocations are down!
12 |
13 | _v0.10.0_
14 | - Refactored several interfaces to allow integrators to re-implement the reporting HttpClient more easily.
15 | - Improved version detection of the underlying CLR
16 |
17 | _v0.9.0_
18 | - Reduce logging verbosity and log output. Several statements that appeared at DEBUG now appear at TRACE.
19 | - Improve the resolution of span start/finish timestamps.
20 | - Awaited methods should no longer block on synchronous threads.
21 |
22 | _v0.8.1_
23 | - Update Google Protobuf to latest stable
24 |
25 | _v0.8.0_
26 | - Improves serialization performance by removing unneeded conversions
27 |
28 | _v0.7.0_
29 | - B3 Header propagation is case-insensitive
30 |
31 | _v0.6.0_
32 | - Update Flush to return Task and update usage in Tracer to await it's completion
33 | - Add basic .editorconfig to help standardize file formats
34 | - Update csproj to use license expression instead of url when packaging
35 | - Improve B3 header support, including 128 bit trace IDs
36 |
37 | _v0.5.0_
38 | - Certain span operations (`Log`, `SetBaggageItem`, `Finish`, `SetTag`, `SetOperationName`) will no longer throw `InvalidOperationException` if called on a finished span. Instead, they will log a message at the error level.
39 |
40 | _v0.4.5_
41 | - Support v0.12.1 of OpenTracing C#
42 | - Initial support for inject/extract of binary headers (`x-ot-span-context`). See usage examples in `test/Lightstep.Tests/PropagatorTests.cs`.
43 | - Headers for Http/TextMap Propagator are now case-insensitive
44 |
45 | _v0.4.5-beta_
46 | - Headers for Http/TextMap Propagator are now case-insensitive
47 |
48 | _v0.4.4-beta_
49 | - Initial support for inject/extract of binary headers (`x-ot-span-context`). See usage examples in `test/Lightstep.Tests/PropagatorTests.cs`.
50 |
51 | _v0.4.3_
52 | - Improves safety of serializing reports by moving translate method to a try/catch block.
53 |
54 | _v0.4.2_
55 | - Addresses an issue where an unhandled exception could occur if a span duration was negative.
56 |
57 | _v0.4.1_
58 | - Addresses an issue where we would attempt to parse all strings as JSON objects when serializing spans to the wire. We now use a more performant method to determine if an input string is JSON.
59 |
60 | _v0.4.0_
61 | - Addresses an issue where duplicate request headers were sent to the LightStep Satellite.
62 | - Updated the default host and port of the Tracer.
63 | - Removed the requirement for an access token when creating an Options object.
64 |
65 | _v0.3.0_
66 | - Add support for meta event reporting to the LightStep SaaS.
67 |
68 | _v0.2.0_
69 | - In order to align with other LightStep tracer implementations, `Inject` and `Extract` methods in `TextMapPropagator` have changed:
70 | - `Inject` will now convert `TraceId` and `SpanId` to a hexadecimal string in the carrier.
71 | - `Extract` will now convert the incoming `TraceId` and `SpanId` from a hexadecimal string into a `uint64`.
72 |
73 | _v0.1.2_
74 | - Increase the verbosity level for certain frequent operations:
75 | - Inject/Extract logging from DEBUG to TRACE
76 | - Span buffer count and reset span message from DEBUG to TRACE
77 | - Certain `LightStepHttpClient` methods were `public` rather than `internal`, this has been corrected -
78 | - `Translate`
79 | - `SendReport`
80 |
81 | _v0.1.1_
82 | - Guard against conversion of malformed `SpanData` by dropping bad spans.
83 |
84 | _v0.1.0_
85 | - `TransportOptions` are now available. You can select JSON or Binary Protobufs.
86 | - We now build using [Cake](https://cakebuild.net).
87 | - All value types are now properly handled in span serialization.
88 | - The platform version detection code has been improved and should be resilient to dynamic injection scenarios.
89 | - Logging now exists for many operations via the LibLog library. See the README for more details.
90 | - Error handling around dropped reports has been improved, and we now report dropped spans to LightStep.
91 | - The `LightStepHttpClient` now respects the `ReportTimeout` value. **Breaking Change**
92 | - Previously, this value was not respected by the HTTP client and was using the default 100s timeout.
93 | - The `Options` class has been refactored to support a more fluent style of configuration. **Breaking Change**
94 | - By default, `SatelliteOptions` are now created when `Options` is created that points to the LightStep public satellite pool.
95 | - Please see the readme for more information on configuring the tracer.
96 | - Tags which are passed with an empty value will now set the string value "null" when reporting to LightStep.
97 |
98 | _v0.0.8_
99 | - Addresses an issue where the `Tracer` would not `Flush` regularly.
100 | - Addresses an issue where tags with null values would cause the tracer to not report spans.
101 | - Addresses an issue where the `LightStepHttpClient` would stop sending spans if it received a non-success status code from the LightStep Satellite.
102 | - *Change* The `LightStepHttpClient` will prefer HTTP/1.1 to HTTP/2. Change ths using `Options.UseHttp2`.
103 | - The NuGet package now includes PDB files.
104 | - The `Options` object now exposes a property `Run` that will determine if the Tracer should flush spans.
105 |
106 | _v0.0.7_
107 | - When instantiating a `Tracer`, you can now pass additional tags in `Options` to apply to all spans. You can also override existing LightStep span tags.
108 |
109 | _v0.0.6_
110 | - Addresses an issue where the signing key wasn't actually being used to sign the assembly.
111 | - Removes unneded `net452` target framework from LightStep project.
112 | - Reduced C# target version to 7.0 to allow builds on unpatched VS2017.
113 |
114 | _v0.0.5_
115 | - Addresses an issue where incoming headers using base16 would cause exceptions on conversion.
116 |
117 | _v0.0.4_
118 | - Expose a new option to force HTTP/1.1 rather than attempting to use HTTP/2 (`Options.UseHttp2`) which defaults to `true`.
119 | - The LightStep Assembly is now signed via Strong Naming.
120 | - Addresses an issue where OpenTracing Header key values were not set correctly.
121 | - Addresses an issue where span duration was improperly reported as ticks rather than microseconds.
122 | - Addresses an issue where on older .NET versions, secure connections would fail due to TLS negotiation. We now force all versions to attempt to negotiate TLS 1.2, then fall back.
123 | - Addresses an issue where rapid creation of Spans in a short timeframe belonging to the same Trace could result in Span ID collisions.
124 |
125 | _v0.0.3_
126 | - Addresses an issue where we would always try to connect to the Satellite via `http`, even if `usePlaintext` was set to `false`.
127 |
128 | _v0.0.2_
129 | - Add support for parsing B3 Headers (such as those used by Zipkin)
130 | - Support `net45` and `netstandard2`
131 |
132 | _v0.0.1_
133 | - Initial Release
134 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guide
2 |
3 | First, thanks for your contribution! 🎉
4 |
5 | We gladly accept issues and pull requests to this repository. If this is a security-related issue, please email us directly at infosec@lightstep.com.
6 |
7 | Please note the following general guidelines and advice -
8 |
9 | ## Issues
10 | When submitting an issue, please include the following in addition to an explanation of the issue:
11 | - Version of the library you're using.
12 | - The runtime and platform version you're using (e.g., .NET 4.6.1 or .NET Core 2.1).
13 | - Any stack trace or diagnostic logs you may have that demonstrate the issue.
14 |
15 | ## Pull Requests
16 | Before making a pull request for a feature, please open an issue in order to gain consensus on the work you're doing.
17 |
18 | All pull requests should be rebased against `master`, and all tests should pass before a PR will be merged.
19 |
20 | In addition, ensure that:
21 | - Test coverage has been added, where appropriate.
22 | - The CHANGELOG and README have been updated.
23 | - If you are making a breaking change, indicate it in the CHANGELOG.
24 |
25 | ## Releases
26 | To make a release, commit a tag to master of the format `vmajor.minor.patch` or `vmajor.minor.patch-alpha/beta`. CircleCI should automatically build and publish the resulting artifact.
27 |
28 | ## Developing
29 |
30 | This library is intended for cross-platform usage, as well as cross-platform development. Please ensure that any dependencies added or changed fully support cross-platform .NET via Mono and .NET Core.
31 |
32 | _Development Dependencies_
33 | - .NET Framework 4.5+ (On MacOS/Linux, you need Mono 5.16, stable channel). **If on MacOS, install Mono via its installer and not Homebrew**
34 | - .NET Core 2.1+
35 | - Cake (see _Local Builds_)
36 | - PostSharp (Windows only, for `LightStep.CSharpAspectTestApp`)
37 |
38 | _Local Builds_
39 |
40 | We use [Cake](https://cakebuild.net/) as a build tool. Run `dotnet tool install -g Cake.Tool` to make Cake globally available, then run `dotnet cake build.cake` to run tests. This requires .NET Core 2.1+.
41 |
42 | You should be able to use any C# development environment, such as [Visual Studio Code](https://code.visualstudio.com/) with the C# extension, [Visual Studio 2017](https://visualstudio.microsoft.com/), or [JetBrains Rider](https://www.jetbrains.com/rider/).
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 LightStep
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 |
--------------------------------------------------------------------------------
/LightStep.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30517.126
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LightStep", "src\LightStep\LightStep.csproj", "{E5F75D2A-B882-46BB-A173-0F77E46498E2}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BE409569-9FA2-4741-8ABF-327D56E6598E}"
9 | ProjectSection(SolutionItems) = preProject
10 | CHANGELOG.md = CHANGELOG.md
11 | README.md = README.md
12 | EndProjectSection
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{1359ACF9-43F2-4E09-94FD-32EB2C6239BA}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LightStep.Tests", "test\LightStep.Tests\LightStep.Tests.csproj", "{2DB2D4A0-64AB-4C5B-ACB5-0C2EB76D756C}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LightStep.CSharpTestApp", "examples\LightStep.CSharpTestApp\LightStep.CSharpTestApp.csproj", "{65A11CA6-E7E9-43B6-A36D-6B54EA9BD758}"
19 | EndProject
20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{113EDBB5-081F-4581-B687-9293B4D74312}"
21 | EndProject
22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightStep.CSharpAspectTestApp", "examples\LightStep.CSharpAspectTestApp\LightStep.CSharpAspectTestApp.csproj", "{1B83F7CF-E2AA-4A81-9C0B-2DA716D5C982}"
23 | EndProject
24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightStep.CSharpDITestApp", "examples\LightStep.CSharpDITestApp\LightStep.CSharpDITestApp.csproj", "{358E688A-D19E-4149-85CD-E804738C0C3F}"
25 | EndProject
26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LightStep.TracerPerf.Tests", "test\LightStep.TracerPerf.Tests\LightStep.TracerPerf.Tests.csproj", "{B54D212A-812F-4102-9852-15D4F084434D}"
27 | EndProject
28 | Global
29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
30 | Debug|Any CPU = Debug|Any CPU
31 | Release|Any CPU = Release|Any CPU
32 | EndGlobalSection
33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
34 | {E5F75D2A-B882-46BB-A173-0F77E46498E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {E5F75D2A-B882-46BB-A173-0F77E46498E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {E5F75D2A-B882-46BB-A173-0F77E46498E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {E5F75D2A-B882-46BB-A173-0F77E46498E2}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {2DB2D4A0-64AB-4C5B-ACB5-0C2EB76D756C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {2DB2D4A0-64AB-4C5B-ACB5-0C2EB76D756C}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {2DB2D4A0-64AB-4C5B-ACB5-0C2EB76D756C}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {2DB2D4A0-64AB-4C5B-ACB5-0C2EB76D756C}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {65A11CA6-E7E9-43B6-A36D-6B54EA9BD758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43 | {65A11CA6-E7E9-43B6-A36D-6B54EA9BD758}.Debug|Any CPU.Build.0 = Debug|Any CPU
44 | {65A11CA6-E7E9-43B6-A36D-6B54EA9BD758}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {65A11CA6-E7E9-43B6-A36D-6B54EA9BD758}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {1B83F7CF-E2AA-4A81-9C0B-2DA716D5C982}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {1B83F7CF-E2AA-4A81-9C0B-2DA716D5C982}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {1B83F7CF-E2AA-4A81-9C0B-2DA716D5C982}.Release|Any CPU.ActiveCfg = Release|Any CPU
49 | {1B83F7CF-E2AA-4A81-9C0B-2DA716D5C982}.Release|Any CPU.Build.0 = Release|Any CPU
50 | {358E688A-D19E-4149-85CD-E804738C0C3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51 | {358E688A-D19E-4149-85CD-E804738C0C3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
52 | {358E688A-D19E-4149-85CD-E804738C0C3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {358E688A-D19E-4149-85CD-E804738C0C3F}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {B54D212A-812F-4102-9852-15D4F084434D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55 | {B54D212A-812F-4102-9852-15D4F084434D}.Debug|Any CPU.Build.0 = Debug|Any CPU
56 | {B54D212A-812F-4102-9852-15D4F084434D}.Release|Any CPU.ActiveCfg = Release|Any CPU
57 | {B54D212A-812F-4102-9852-15D4F084434D}.Release|Any CPU.Build.0 = Release|Any CPU
58 | EndGlobalSection
59 | GlobalSection(SolutionProperties) = preSolution
60 | HideSolutionNode = FALSE
61 | EndGlobalSection
62 | GlobalSection(NestedProjects) = preSolution
63 | {E5F75D2A-B882-46BB-A173-0F77E46498E2} = {BE409569-9FA2-4741-8ABF-327D56E6598E}
64 | {2DB2D4A0-64AB-4C5B-ACB5-0C2EB76D756C} = {1359ACF9-43F2-4E09-94FD-32EB2C6239BA}
65 | {65A11CA6-E7E9-43B6-A36D-6B54EA9BD758} = {113EDBB5-081F-4581-B687-9293B4D74312}
66 | {1B83F7CF-E2AA-4A81-9C0B-2DA716D5C982} = {113EDBB5-081F-4581-B687-9293B4D74312}
67 | {358E688A-D19E-4149-85CD-E804738C0C3F} = {113EDBB5-081F-4581-B687-9293B4D74312}
68 | {B54D212A-812F-4102-9852-15D4F084434D} = {1359ACF9-43F2-4E09-94FD-32EB2C6239BA}
69 | EndGlobalSection
70 | GlobalSection(ExtensibilityGlobals) = postSolution
71 | SolutionGuid = {0890664C-9CE4-4960-90A4-AD6565C970E3}
72 | EndGlobalSection
73 | EndGlobal
74 |
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # lightstep-tracer-csharp
2 |
3 | > ❗ **This instrumentation is no longer recommended**. Please review [documentation on setting up and configuring the OpenTelemetry client for .NET](https://github.com/open-telemetry/opentelemetry-dotnet) for more information on migrating.
4 |
5 | The LightStep distributed tracing library for C#
6 |
7 | [](https://www.nuget.org/packages/LightStep) [](https://circleci.com/gh/lightstep/lightstep-tracer-csharp) [](https://codecov.io/gh/lightstep/lightstep-tracer-csharp)
8 |
9 | # Installation
10 | Install the package via NuGet into your solution, or use `Install-Package LightStep` / `dotnet add package LightStep`.
11 |
12 | # Basic Usage
13 | It's recommended to initialize the tracer once at the beginning of your application and assign it as the global tracer, as follows:
14 | ```c#
15 | var tracerOptions = new Options();
16 | var tracer = new Tracer(tracerOptions);
17 | GlobalTracer.Register(tracer);
18 | ```
19 |
20 | Once you've initialized a tracer, you can begin to create and emit spans.
21 |
22 | Please refer to the [OpenTracing C# documentation](https://github.com/opentracing/opentracing-csharp) for information on how to create spans.
23 |
24 | You can also refer to the `examples` folder for sample code and projects.
25 |
26 | # Advanced Usage
27 |
28 | There's several options that can be adjusted when instantiating a `LightStepTracer`.
29 |
30 | ## `Options`
31 | | Method | Description |
32 | | -------- | ----------- |
33 | | WithTags(IDictionary) | Default tags to apply to all spans created by the tracer. |
34 | | WithReportPeriod(TimeSpan) | How frequently the Tracer should batch and send Spans to LightStep (5s default) |
35 | | WithReportTimeout(TimeSpan) | Timeout for sending spans to the Satellite (30s default) |
36 | | WithToken(string) | The LightStep Project Access Token |
37 | | WithSatellite(SatelliteOptions) | A SatelliteOptions object that specifies the host, port, and if we should use HTTPS |
38 | | WithHttp2(bool) | If this is true, we use HTTP/2 to communicate with the Satellite. We reccomend you enable this option if you're on a modern version of .NET (4.6.1+ or .NET Core) |
39 | | WithAutomaticReporting(bool) | If false, disables the automatic flushing of buffered spans. |
40 | | WithMaxBufferedSpans(int) | The maximum amount of spans to record in a single buffer. |
41 | | WithTransport(enum) | Which transport to use when sending spans to the Satellite. |
42 |
43 | ## `SatelliteOptions`
44 | | Property | Description |
45 | | -------- | ----------- |
46 | | SatelliteHost | The hostname of a Satelite (i.e., `collector.lightstep.com`)
47 | | SatellitePort | The port number where the Satellite is listening for HTTP traffic (defaults to 443)
48 | | UsePlaintext | Should we use HTTP or HTTPS traffic? (Defaults to HTTPS)
49 |
50 | The C# Tracer will prefer TLS 1.2 when available on all .NET Runtime versions, but should fall back to TLS 1.1 or 1.0 in that order.
51 |
52 | The following is an example of overriding the LightStep Component Name and adding a new custom tag for all spans -
53 |
54 | ```csharp
55 | var satelliteOptions = new SatelliteOptions("satellite.mydomain.com");
56 | var overrideTags = new Dictionary
57 | {
58 | {LightStepConstants.ComponentNameKey, "test_component"},
59 | {"my_tag", "foobar"}
60 | };
61 | var tracerOptions = new Options("TEST_TOKEN").WithSatellite(satelliteOptions).WithTags(overrideTags);
62 | var tracer = new Tracer(tracerOptions);
63 | ```
64 |
65 | ## Logging
66 | This tracer uses [LibLog](https://github.com/damianh/LibLog), a transparent logging abstraction that provides built-in support for NLog, Log4Net, Serilog, and Loupe.
67 | If you use a logging provider that isn't identified by LibLog, see [this gist](https://gist.github.com/damianh/fa529b8346a83f7f49a9) on how to implement a custom logging provider.
68 |
69 | ## Integration Notes
70 | You may notice that there's a lot of overloads for creating a `Tracer`! You have the flexibility to override and re-implement much of this library. In 0.10.0+, the `ILightStepHttpClient` interface has
71 | been decoupled from report translation, allowing you control over the exact mechanism by which the tracer reports spans to Lightstep. You can reference [this issue](https://github.com/lightstep/lightstep-tracer-csharp/issues/92)
72 | for more information and a discussion about why you might want to do this.
73 |
74 | For most users, sticking with the defaults is fine. You should also look at the `Tracer(Options, IPropagator, ILightStepHttpClient)` ctor for custom integration - the span recorder and span translator overloads are not terribly interesting.
75 | Creating and managing propagators allows you to either select a built-in propagator (textmap, B3, etc.), create a propagator 'stack' (if you potentially have multiple input or output trace context formats), or write your own propagator.
76 |
--------------------------------------------------------------------------------
/build.cake:
--------------------------------------------------------------------------------
1 | #tool "xunit.runner.console"
2 | #addin nuget:?package=Cake.Coverlet
3 |
4 | var target = Argument("target", "Default");
5 | var configuration = Argument("configuration", "Release");
6 | var debugConfiguration = Argument("configuration", "Debug");
7 | var buildDir = Directory("./build");
8 | var distDir = Directory("./dist");
9 | var solution = "./LightStep.sln";
10 | var library = "./src/LightStep/LightStep.csproj";
11 | var testLib = "./test/LightStep.Tests/LightStep.Tests.csproj";
12 | var lightStepAssemblyInfoFile = "./src/LightStep/Properties/AssemblyInfo.cs";
13 | var version = EnvironmentVariable("CIRCLE_TAG") ?? "v0.0.0";
14 | version = version.TrimStart('v');
15 | var buildNo = String.IsNullOrWhiteSpace(EnvironmentVariable("CIRCLE_BUILD_NUM")) ? "0" : EnvironmentVariable("CIRCLE_BUILD_NUM");
16 | var semVersion = string.Concat(version + "-" + buildNo);
17 | var transformedVersion = string.Concat(version + "." + buildNo);
18 | if (version.Contains("-"))
19 | {
20 | transformedVersion = string.Concat(version.Substring(0, version.LastIndexOf("-")) + "." + buildNo);
21 | }
22 | var nuGetApiKey = EnvironmentVariable("NuGet");
23 | var testAssemblyFriendlyName = "LightStep.Tests,PublicKey=002400000480000094000000060200000024000052534131000400000100010099deeadb052e9763d2dc7827700d80e349e5d16585c92416171e6689a4bd38a3acea971d5899d5e2cd4239c3dc799558138e961f8d0f5095fef969672172833868f2cc2d908970370af834beef9dad328182fee2aaf0d0bb568ffd1f829362b88718734541d334c6a2cdf0049f5a0ee5e4962d0db3f49f86bf742f9531bd9c8c";
24 |
25 | Task("Clean")
26 | .Does( ()=>
27 | {
28 | CleanDirectory(buildDir);
29 | CleanDirectory(distDir);
30 | CleanDirectories("./**/obj/*.*");
31 | CleanDirectories($"./**/bin/{configuration}/*.*");
32 | CleanDirectories($"./**/bin/{debugConfiguration}/*.*");
33 | });
34 |
35 | Task("Restore")
36 | .IsDependentOn("Clean")
37 | .Does( ()=>
38 | {
39 | DotNetCoreRestore(library);
40 | DotNetCoreRestore(testLib);
41 | });
42 |
43 | Task("Build")
44 | .IsDependentOn("Restore")
45 | .Does(() =>
46 | {
47 | CreateAssemblyInfo(lightStepAssemblyInfoFile, new AssemblyInfoSettings {
48 | Product = "LightStep",
49 | Version = transformedVersion,
50 | FileVersion = transformedVersion,
51 | InformationalVersion = version,
52 | Copyright = string.Format("Copyright (c) LightStep 2018 - {0}", DateTime.Now.Year),
53 | InternalsVisibleTo = new List() { testAssemblyFriendlyName }
54 | });
55 | var assemblyInfo = ParseAssemblyInfo(lightStepAssemblyInfoFile);
56 | Information("Version: {0}", assemblyInfo.AssemblyVersion);
57 | Information("File version: {0}", assemblyInfo.AssemblyFileVersion);
58 | Information("Informational version: {0}", assemblyInfo.AssemblyInformationalVersion);
59 | MSBuild(library, settings => settings
60 | .SetConfiguration(configuration)
61 | .WithTarget("Rebuild")
62 | .WithProperty("Version", assemblyInfo.AssemblyInformationalVersion)
63 | .SetVerbosity(Verbosity.Minimal));
64 | });
65 |
66 | Task("Test")
67 | .IsDependentOn("Build")
68 | .Does(() =>
69 | {
70 | var unitProject = "./test/LightStep.Tests/LightStep.Tests.csproj";
71 | var coverletSettings = new CoverletSettings {
72 | CollectCoverage = true,
73 | CoverletOutputFormat = CoverletOutputFormat.opencover,
74 | CoverletOutputDirectory = Directory("./build"),
75 | CoverletOutputName = $"coverage.xml",
76 | ExcludeByFile = { "../../src/LightStep/Collector/Collector.cs", "../../src/LightStep/LightStep.cs" }
77 | };
78 | DotNetCoreTest(unitProject, new DotNetCoreTestSettings {
79 | Logger = "xunit;LogFilePath=../../build/test_results.xml"
80 | }, coverletSettings);
81 | });
82 |
83 | Task("Publish")
84 | .IsDependentOn("Test")
85 | .WithCriteria(() => EnvironmentVariable("CI") == "true")
86 | .Does(() =>
87 | {
88 | var nupkg = GetFiles("./src/LightStep/bin/Release/*.nupkg").First();
89 | DotNetCoreNuGetPush(nupkg.FullPath, new DotNetCoreNuGetPushSettings {
90 | Source = "https://www.nuget.org/api/v2/package",
91 | ApiKey = nuGetApiKey
92 | });
93 | });
94 |
95 | Task("Default")
96 | .IsDependentOn("Test");
97 |
98 | RunTarget(target);
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##########################################################################
4 | # This is the Cake bootstrapper script for Linux and OS X.
5 | # This file was downloaded from https://github.com/cake-build/resources
6 | # Feel free to change this file to fit your needs.
7 | ##########################################################################
8 |
9 | # Define directories.
10 | SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
11 | TOOLS_DIR=$SCRIPT_DIR/tools
12 | ADDINS_DIR=$TOOLS_DIR/Addins
13 | MODULES_DIR=$TOOLS_DIR/Modules
14 | NUGET_EXE=$TOOLS_DIR/nuget.exe
15 | CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe
16 | PACKAGES_CONFIG=$TOOLS_DIR/packages.config
17 | PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum
18 | ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config
19 | MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config
20 |
21 | # Define md5sum or md5 depending on Linux/OSX
22 | MD5_EXE=
23 | if [[ "$(uname -s)" == "Darwin" ]]; then
24 | MD5_EXE="md5 -r"
25 | else
26 | MD5_EXE="md5sum"
27 | fi
28 |
29 | # Define default arguments.
30 | SCRIPT="build.cake"
31 | CAKE_ARGUMENTS=()
32 |
33 | # Parse arguments.
34 | for i in "$@"; do
35 | case $1 in
36 | -s|--script) SCRIPT="$2"; shift ;;
37 | --) shift; CAKE_ARGUMENTS+=("$@"); break ;;
38 | *) CAKE_ARGUMENTS+=("$1") ;;
39 | esac
40 | shift
41 | done
42 |
43 | # Make sure the tools folder exist.
44 | if [ ! -d "$TOOLS_DIR" ]; then
45 | mkdir "$TOOLS_DIR"
46 | fi
47 |
48 | # Make sure that packages.config exist.
49 | if [ ! -f "$TOOLS_DIR/packages.config" ]; then
50 | echo "Downloading packages.config..."
51 | curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages
52 | if [ $? -ne 0 ]; then
53 | echo "An error occurred while downloading packages.config."
54 | exit 1
55 | fi
56 | fi
57 |
58 | # Download NuGet if it does not exist.
59 | if [ ! -f "$NUGET_EXE" ]; then
60 | echo "Downloading NuGet..."
61 | curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
62 | if [ $? -ne 0 ]; then
63 | echo "An error occurred while downloading nuget.exe."
64 | exit 1
65 | fi
66 | fi
67 |
68 | # Restore tools from NuGet.
69 | pushd "$TOOLS_DIR" >/dev/null
70 | if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then
71 | find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf
72 | fi
73 |
74 | mono "$NUGET_EXE" install -ExcludeVersion
75 | if [ $? -ne 0 ]; then
76 | echo "Could not restore NuGet tools."
77 | exit 1
78 | fi
79 |
80 | $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5"
81 |
82 | popd >/dev/null
83 |
84 | # Restore addins from NuGet.
85 | if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then
86 | pushd "$ADDINS_DIR" >/dev/null
87 |
88 | mono "$NUGET_EXE" install -ExcludeVersion
89 | if [ $? -ne 0 ]; then
90 | echo "Could not restore NuGet addins."
91 | exit 1
92 | fi
93 |
94 | popd >/dev/null
95 | fi
96 |
97 | # Restore modules from NuGet.
98 | if [ -f "$MODULES_PACKAGES_CONFIG" ]; then
99 | pushd "$MODULES_DIR" >/dev/null
100 |
101 | mono "$NUGET_EXE" install -ExcludeVersion
102 | if [ $? -ne 0 ]; then
103 | echo "Could not restore NuGet modules."
104 | exit 1
105 | fi
106 |
107 | popd >/dev/null
108 | fi
109 |
110 | # Make sure that Cake has been installed.
111 | if [ ! -f "$CAKE_EXE" ]; then
112 | echo "Could not find Cake.exe at '$CAKE_EXE'."
113 | exit 1
114 | fi
115 |
116 | # Start Cake
117 | exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}"
118 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpAspectTestApp/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpAspectTestApp/Aspects/Traceable.cs:
--------------------------------------------------------------------------------
1 | using OpenTracing.Util;
2 | using PostSharp.Aspects;
3 | using PostSharp.Serialization;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace LightStep.CSharpAspectTestApp.Aspects
11 | {
12 | [PSerializable]
13 | public sealed class Traceable : OnMethodBoundaryAspect
14 | {
15 | public override void OnEntry(MethodExecutionArgs args)
16 | {
17 | // when we enter a method, build a new span and mark it as active
18 | var scope = GlobalTracer.Instance.BuildSpan(args.Method.Name).StartActive();
19 | Console.WriteLine($"new span id {scope.Span.Context.SpanId}");
20 | }
21 |
22 | public override void OnSuccess(MethodExecutionArgs args)
23 | {
24 | // only finish a span in OnExit, as it gets called as a finalizer
25 | Console.WriteLine("The {0} method executed successfully.", args.Method.Name);
26 | }
27 |
28 | public override void OnExit(MethodExecutionArgs args)
29 | {
30 | // when a method exits (either successfully or unsuccessfully), dispose of the scope to free it on our singleton tracer
31 | var scope = GlobalTracer.Instance.ScopeManager.Active;
32 | Console.WriteLine($"finishing span id {scope.Span.Context.SpanId}");
33 | scope.Dispose();
34 | }
35 |
36 | public override void OnException(MethodExecutionArgs args)
37 | {
38 | // if an exception occurs, get the active scope then set the exception log, add tags, whatever.
39 | var scope = GlobalTracer.Instance.ScopeManager.Active;
40 | Console.WriteLine("An exception was thrown in {0}.", args.Method.Name);
41 | scope.Span.Log(args.Exception.StackTrace);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpAspectTestApp/HttpWorker.cs:
--------------------------------------------------------------------------------
1 | using LightStep.CSharpAspectTestApp.Aspects;
2 | using OpenTracing.Util;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Net.Http;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace LightStep.CSharpAspectTestApp
11 | {
12 | class HttpWorker
13 | {
14 | private HttpClient httpClient;
15 |
16 | public HttpWorker()
17 | {
18 | httpClient = new HttpClient();
19 | }
20 |
21 | [Traceable]
22 | public async void Get(string url)
23 | {
24 | GlobalTracer.Instance.ActiveSpan.SetTag("args", url);
25 | var content = await GetString(url);
26 | Write(content);
27 | }
28 |
29 | [Traceable]
30 | public static void Write(string content)
31 | {
32 | Console.WriteLine(content);
33 | }
34 |
35 | [Traceable]
36 | public async Task GetString(string url)
37 | {
38 | var content = await httpClient.GetStringAsync(url);
39 | GlobalTracer.Instance.ActiveSpan.Log(content);
40 | return content;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpAspectTestApp/LightStep.CSharpAspectTestApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | AnyCPU
8 | {1B83F7CF-E2AA-4A81-9C0B-2DA716D5C982}
9 | Exe
10 | LightStep.CSharpAspectTestApp
11 | LightStep.CSharpAspectTestApp
12 | v4.6.1
13 | 512
14 | true
15 |
16 |
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 | ..\..\packages\OpenTracing.0.12.1-rc4\lib\net45\OpenTracing.dll
40 | True
41 |
42 |
43 | ..\..\packages\PostSharp.Redist.6.0.27\lib\net45\PostSharp.dll
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | {e5f75d2a-b882-46bb-a173-0f77e46498e2}
72 | LightStep
73 |
74 |
75 |
76 |
77 |
78 | 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}.
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpAspectTestApp/Program.cs:
--------------------------------------------------------------------------------
1 | using LightStep.CSharpAspectTestApp.Aspects;
2 | using LightStep;
3 | using OpenTracing.Util;
4 | using System;
5 | using System.Configuration;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace LightStep.CSharpAspectTestApp
12 | {
13 | class Program
14 | {
15 | static void Main(string[] args)
16 | {
17 | // create your tracer options, initialize it, assign it to the GlobalTracer
18 | var lsKey = Environment.GetEnvironmentVariable("LS_KEY");
19 | var lsSettings = new SatelliteOptions("collector.lightstep.com");
20 | var lsOptions = new Options(lsKey).WithSatellite(lsSettings);
21 | var tracer = new Tracer(lsOptions);
22 |
23 | GlobalTracer.Register(tracer);
24 |
25 | // do some work in parallel, this work also includes awaited calls
26 | Parallel.For(1, 100, i => DoThing(i));
27 |
28 | // block until you enter a key
29 | Console.ReadKey();
30 | }
31 |
32 | [Traceable]
33 | static void DoThing(int idx)
34 | {
35 | GlobalTracer.Instance.ActiveSpan.SetTag("args", idx);
36 | var client = new HttpWorker();
37 | client.Get($"https://jsonplaceholder.typicode.com/todos/{idx}");
38 | }
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpAspectTestApp/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("LightStep.CSharpAspectTestApp")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("LightStep.CSharpAspectTestApp")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
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("1b83f7cf-e2aa-4a81-9c0b-2da716d5c982")]
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("0.1.29.0")]
35 | [assembly: AssemblyVersion("0.1.29.0")]
36 | [assembly: AssemblyFileVersion("0.1.29.0")]
37 |
38 | [assembly: AssemblyInformationalVersion("0.1.29-enhancement-addCake.1-2018-11-12")]
39 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpAspectTestApp/README.md:
--------------------------------------------------------------------------------
1 | # Tracing With Aspects
2 |
3 | This is a small sample application demonstrating how to use an Aspect framework (such as PostSharp) to apply Tracing to methods.
4 |
5 | ## Installation and Setup
6 |
7 | Set the `LS_KEY` environment variable to your project's API Key.
8 |
9 | **Important**: This sample will only build on Windows platforms, as PostSharp does not support all CoreCLR targets for compilation.
--------------------------------------------------------------------------------
/examples/LightStep.CSharpAspectTestApp/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpDITestApp/LightStep.CSharpDITestApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {358E688A-D19E-4149-85CD-E804738C0C3F}
8 | Exe
9 | Properties
10 | LightStep.CSharpDITestApp
11 | LightStep.CSharpDITestApp
12 | v4.7.1
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\..\packages\Castle.Core.4.2.0\lib\net45\Castle.Core.dll
38 | True
39 |
40 |
41 | ..\..\packages\Castle.Windsor.4.1.1\lib\net45\Castle.Windsor.dll
42 | True
43 |
44 |
45 | ..\..\packages\OpenTracing.0.12.1-rc4\lib\net45\OpenTracing.dll
46 | True
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | {e5f75d2a-b882-46bb-a173-0f77e46498e2}
66 | LightStep
67 |
68 |
69 |
70 |
77 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpDITestApp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using Castle.MicroKernel.Registration;
4 | using Castle.Windsor;
5 | using OpenTracing;
6 | using LightStep;
7 |
8 | namespace LightStep.CSharpDITestApp
9 | {
10 | internal class Program
11 | {
12 | public static void Main(string[] args)
13 | {
14 | // replace these options with your satellite and api key
15 | var tracerOptions = new Options("TEST_TOKEN").WithSatellite(new SatelliteOptions("localhost", 9996, true));
16 | var container = new WindsorContainer();
17 | // during registration, pass your options to the concrete LightStep Tracer implementation
18 | container.Register(Component.For().ImplementedBy().DependsOn(Dependency.OnValue("options", tracerOptions)));
19 | var tracer = container.Resolve();
20 |
21 | // create some spans
22 | for (var i = 0; i < 500; i++)
23 | using (var scope = tracer.BuildSpan("testParent").WithTag("testSpan", "true").StartActive(true))
24 | {
25 | scope.Span.Log("test");
26 | tracer.ActiveSpan.Log($"iteration {i}");
27 | Console.WriteLine("sleeping for a bit");
28 | Thread.Sleep(new Random().Next(5, 10));
29 | var innerSpan = tracer.BuildSpan("childSpan").Start();
30 | innerSpan.SetTag("innerTestTag", "true");
31 | Console.WriteLine("sleeping more...");
32 | Thread.Sleep(new Random().Next(10, 20));
33 | innerSpan.Finish();
34 | }
35 |
36 | // note that OpenTracing.ITracer does not have flush as a method, so to manually flush you'll need to
37 | // get a cast of the tracer.
38 | Tracer t = (Tracer) tracer;
39 | t.Flush();
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/examples/LightStep.CSharpDITestApp/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("LightStep.CSharpDITestApp")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("LightStep.CSharpDITestApp")]
12 | [assembly: AssemblyCopyright("Copyright © 2018")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("358E688A-D19E-4149-85CD-E804738C0C3F")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/examples/LightStep.CSharpDITestApp/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpTestApp/LightStep.CSharpTestApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | netcoreapp2.1
5 | LightStep.CSharpTestApp
6 | LightStep.CSharpTestApp
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ..\..\..\..\.nuget\packages\opentracing\0.12.0\lib\net45\OpenTracing.dll
20 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/LightStep.CSharpTestApp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading;
4 | using Google.Protobuf.WellKnownTypes;
5 | using OpenTracing.Util;
6 | using global::Serilog;
7 | using Serilog.Events;
8 | using Serilog.Sinks.SystemConsole;
9 |
10 | namespace LightStep.CSharpTestApp
11 | {
12 | internal class Program
13 | {
14 | private static void Main(string[] args)
15 | {
16 | Log.Logger = new LoggerConfiguration()
17 | .MinimumLevel.Debug()
18 | .WriteTo.Console()
19 | .CreateLogger();
20 |
21 | var tracer = new Tracer(new Options());
22 | GlobalTracer.Register(tracer);
23 |
24 | for (var i = 0; i < 500; i++)
25 | using (var scope = tracer.BuildSpan("testParent").WithTag("testSpan", "true").StartActive(true))
26 | {
27 | scope.Span.Log("test");
28 | tracer.ActiveSpan.Log($"iteration {i}");
29 |
30 | Thread.Sleep(new Random().Next(5, 10));
31 | var innerSpan = tracer.BuildSpan("childSpan").Start();
32 | innerSpan.SetTag("innerTestTag", "true");
33 |
34 | Thread.Sleep(new Random().Next(10, 20));
35 | innerSpan.Finish();
36 | }
37 | tracer.Flush();
38 | Console.ReadKey();
39 | }
40 |
41 | }
42 | }
--------------------------------------------------------------------------------
/ls128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lightstep/lightstep-tracer-csharp/59c3e63658fd69bf732ddc019e90607d9b014c11/ls128x128.png
--------------------------------------------------------------------------------
/src/LightStep/Baggage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace LightStep
5 | {
6 | ///
7 | /// Baggage is a dictionary of key:value information that is propagated via the SpanContext.
8 | ///
9 | public class Baggage
10 | {
11 | private readonly IDictionary _items =
12 | new Dictionary(StringComparer.OrdinalIgnoreCase);
13 |
14 | ///
15 | /// Sets key to value.
16 | ///
17 | ///
18 | ///
19 | public void Set(string key, string value)
20 | {
21 | _items[key] = value;
22 | }
23 |
24 | ///
25 | /// Gets value for key; Will return null if key does not exist.
26 | ///
27 | ///
28 | ///
29 | public string Get(string key)
30 | {
31 | string value;
32 | return _items.TryGetValue(key, out value) ? value : null;
33 | }
34 |
35 | ///
36 | /// Gets all from baggage.
37 | ///
38 | ///
39 | public IEnumerable> GetAll()
40 | {
41 | return _items;
42 | }
43 |
44 | ///
45 | /// Combines two baggage into one.
46 | ///
47 | ///
48 | public void Merge(Baggage other)
49 | {
50 | Merge(other?.GetAll());
51 | }
52 |
53 | private void Merge(IEnumerable> other)
54 | {
55 | if (other == null)
56 | return;
57 |
58 | // Copy entries into local dictionary instead of setting it directly
59 | // to make sure the case insensitive comparer is used.
60 | foreach (var kvp in other) Set(kvp.Key, kvp.Value);
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/LightStep/Collector/ILightStepHttpClient.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Threading.Tasks;
3 |
4 | namespace LightStep.Collector
5 | {
6 | public interface ILightStepHttpClient
7 | {
8 | ///
9 | /// Send a report of spans to the LightStep Satellite.
10 | ///
11 | /// An
12 | /// A . This is usually not very interesting.
13 | Task SendReport(ReportRequest report);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/LightStep/Collector/IReportTranslator.cs:
--------------------------------------------------------------------------------
1 | namespace LightStep.Collector
2 | {
3 | public interface IReportTranslator
4 | {
5 | ///
6 | /// Translate SpanData to a protobuf ReportRequest for sending to the Satellite.
7 | ///
8 | /// An enumerable of
9 | /// A
10 | ReportRequest Translate(ISpanRecorder spanBuffer);
11 | }
12 | }
--------------------------------------------------------------------------------
/src/LightStep/Collector/LightStepHttpClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Net.Http;
6 | using System.Net.Http.Headers;
7 | using System.Threading.Tasks;
8 | using Google.Protobuf;
9 | using LightStep.Logging;
10 | using Google.Protobuf.WellKnownTypes;
11 |
12 | namespace LightStep.Collector
13 | {
14 | ///
15 | /// Contains methods to communicate to a LightStep Satellite via Proto over HTTP.
16 | ///
17 | public class LightStepHttpClient : ILightStepHttpClient
18 | {
19 | private readonly Options _options;
20 | private HttpClient _client;
21 | private readonly string _url;
22 | private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();
23 |
24 | ///
25 | /// Create a new client.
26 | ///
27 | /// URL to send results to.
28 | /// An object.
29 | /// An object. If none is provide, a default client will be used.
30 | public LightStepHttpClient(string url, Options options)
31 | {
32 | _url = url;
33 | _options = options;
34 | _client = new HttpClient() {Timeout = _options.ReportTimeout};
35 | }
36 |
37 | internal HttpRequestMessage CreateStringRequest(ReportRequest report)
38 | {
39 | var jsonFormatter = new JsonFormatter(new JsonFormatter.Settings(true));
40 | var jsonReport = jsonFormatter.Format(report);
41 | var request = new HttpRequestMessage(HttpMethod.Post, _url)
42 | {
43 | Version = _options.UseHttp2 ? new Version(2, 0) : new Version(1, 1),
44 | Content = new StringContent(jsonReport)
45 | };
46 | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
47 | return request;
48 | }
49 |
50 | internal HttpRequestMessage CreateBinaryRequest(ReportRequest report)
51 | {
52 | var binaryReport = report.ToByteArray();
53 | var request = new HttpRequestMessage(HttpMethod.Post, _url)
54 | {
55 | Version = _options.UseHttp2 ? new Version(2, 0) : new Version(1, 1),
56 | Content = new ByteArrayContent(binaryReport)
57 | };
58 | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
59 | return request;
60 | }
61 |
62 | internal HttpRequestMessage BuildRequest(ReportRequest report)
63 | {
64 | HttpRequestMessage requestMessage = (_options.Transport & TransportOptions.JsonProto) != 0 ? CreateStringRequest(report) : CreateBinaryRequest(report);
65 |
66 | // add LightStep access token to request header
67 | requestMessage.Content.Headers.Add(LightStepConstants.AccessTokenConstant, report.Auth.AccessToken);
68 |
69 | return requestMessage;
70 |
71 | }
72 |
73 | ///
74 | /// Send a report of spans to the LightStep Satellite.
75 | ///
76 | /// An
77 | /// A . This is usually not very interesting.
78 | [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
79 | public async Task SendReport(ReportRequest report)
80 | {
81 | // force net45 to attempt tls12 first and fallback appropriately
82 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
83 |
84 | _client.DefaultRequestHeaders.Accept.Clear();
85 | _client.DefaultRequestHeaders.Accept.Add(
86 | new MediaTypeWithQualityHeaderValue("application/octet-stream"));
87 |
88 | var requestMessage = BuildRequest(report);
89 |
90 | ReportResponse responseValue;
91 |
92 | try
93 | {
94 | var response = await _client.SendAsync(requestMessage);
95 | response.EnsureSuccessStatusCode();
96 | var responseData = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
97 | responseValue = ReportResponse.Parser.ParseFrom(responseData);
98 | _logger.Trace($"Report HTTP Response {response.StatusCode}");
99 | }
100 | catch (HttpRequestException ex)
101 | {
102 | _logger.WarnException("Exception caught while sending report, resetting HttpClient", ex);
103 | _client.Dispose();
104 | _client = new HttpClient {Timeout = _options.ReportTimeout};
105 | throw;
106 | }
107 | catch (Exception ex) when (ex is TaskCanceledException || ex is OperationCanceledException)
108 | {
109 | _logger.WarnException("Timed out sending report to satellite", ex);
110 | throw;
111 | }
112 | catch (Exception ex)
113 | {
114 | _logger.WarnException("Unknown error sending report.", ex);
115 | throw;
116 | }
117 |
118 | return responseValue;
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/src/LightStep/Collector/ProtoConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Google.Protobuf.WellKnownTypes;
5 |
6 | namespace LightStep.Collector
7 | {
8 | ///
9 | public partial class SpanContext
10 | {
11 | ///
12 | /// Converts a to a
13 | ///
14 | /// A SpanContext
15 | /// Proto SpanContext
16 | public SpanContext MakeSpanContextFromOtSpanContext(LightStep.SpanContext ctx)
17 | {
18 | SpanId = ctx.SpanIdValue;
19 | TraceId = ctx.TraceIdValue;
20 |
21 | ctx.GetBaggageItems().ToList().ForEach(baggage => Baggage.Add(baggage.Key, baggage.Value));
22 | return this;
23 | }
24 | }
25 |
26 | ///
27 | public partial class Span
28 | {
29 | const long TicksPerMicrosecond = 10;
30 | ///
31 | /// Converts a to a
32 | ///
33 | /// A SpanData
34 | /// Proto Span
35 | public Span MakeSpanFromSpanData(SpanData span)
36 | {
37 | // ticks are not equal to microseconds, so convert
38 | DurationMicros = Convert.ToUInt64(Math.Abs(span.Duration.Ticks) / TicksPerMicrosecond);
39 | OperationName = span.OperationName;
40 | SpanContext = new SpanContext().MakeSpanContextFromOtSpanContext(span.Context);
41 | StartTimestamp = Timestamp.FromDateTime(span.StartTimestamp.UtcDateTime);
42 | foreach (var logData in span.LogData) Logs.Add(new Log().MakeLogFromLogData(logData));
43 | foreach (var keyValuePair in span.Tags) Tags.Add(new KeyValue().MakeKeyValueFromKvp(keyValuePair));
44 |
45 | if (span.Context.ParentSpanIdValue != 0L)
46 | References.Add(Reference.MakeReferenceFromParentSpanIdValue(span.Context.ParentSpanIdValue));
47 |
48 | return this;
49 | }
50 | }
51 |
52 | ///
53 | public partial class Reference
54 | {
55 | ///
56 | /// Converts a ParentSpanId string into a
57 | ///
58 | /// A ParentSpanId as a string
59 | /// Proto Reference
60 | public static Reference MakeReferenceFromParentSpanId(string id)
61 | {
62 | var reference = new Reference();
63 | reference.Relationship = Types.Relationship.ChildOf;
64 | ulong spanId;
65 | try
66 | {
67 | spanId = Convert.ToUInt64(id);
68 | }
69 | catch (FormatException)
70 | {
71 | spanId = Convert.ToUInt64(id, 16);
72 | }
73 | reference.SpanContext = new SpanContext {SpanId = spanId};
74 |
75 | return reference;
76 | }
77 |
78 | public static Reference MakeReferenceFromParentSpanIdValue(ulong value)
79 | {
80 | var reference = new Reference
81 | {
82 | Relationship = Types.Relationship.ChildOf, SpanContext = new SpanContext {SpanId = value}
83 | };
84 | return reference;
85 | }
86 | }
87 |
88 | ///
89 | public partial class Log
90 | {
91 | ///
92 | /// Converts a into a
93 | ///
94 | /// A LogData object
95 | /// Proto Log
96 | public Log MakeLogFromLogData(LogData log)
97 | {
98 | Timestamp = Timestamp.FromDateTime(log.Timestamp.DateTime.ToUniversalTime());
99 | foreach (var keyValuePair in log.Fields) Fields.Add(new KeyValue().MakeKeyValueFromKvp(keyValuePair));
100 |
101 | return this;
102 | }
103 | }
104 |
105 | ///
106 | public partial class KeyValue
107 | {
108 | ///
109 | /// Converts a into a
110 | ///
111 | /// A KeyValuePair used in Tags, etc.
112 | /// Proto KeyValue
113 | public KeyValue MakeKeyValueFromKvp(KeyValuePair input)
114 | {
115 | Key = input.Key;
116 | if (input.Value == null) StringValue = "null";
117 | else if (input.Value.IsFloatDataType()) DoubleValue = Convert.ToDouble(input.Value);
118 | else if (input.Value.IsIntDataType()) IntValue = Convert.ToInt64(input.Value);
119 | else if (input.Value.IsBooleanDataType()) BoolValue = Convert.ToBoolean(input.Value);
120 | else if (input.Value.IsJson()) JsonValue = Convert.ToString(input.Value);
121 | else StringValue = Convert.ToString(input.Value);
122 |
123 | return new KeyValue(this);
124 | }
125 | }
126 | }
--------------------------------------------------------------------------------
/src/LightStep/Collector/ProtoConverterExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Json;
3 |
4 | namespace LightStep.Collector
5 | {
6 | ///
7 | /// Helper methods for the ProtoConverter
8 | ///
9 | public static class ProtoConverterExtensions
10 | {
11 | ///
12 | /// Extension method to determine if an object is a number or number-like value.
13 | ///
14 | /// Any value.
15 | /// True if the value is a number or number-like value, false otherwise.
16 | public static bool IsIntDataType(this object value)
17 | {
18 | var v = value is sbyte
19 | || value is byte
20 | || value is short
21 | || value is ushort
22 | || value is int
23 | || value is uint
24 | || value is long
25 | || value is ulong;
26 | return v;
27 | }
28 |
29 | public static bool IsFloatDataType(this object value)
30 | {
31 | var v = value is float || value is double || value is decimal;
32 | return v;
33 | }
34 |
35 | ///
36 | /// Extension method to determine if an object is a boolean value.
37 | ///
38 | /// Any value.
39 | /// True is the value is a boolean value, false otherwise.
40 | public static bool IsBooleanDataType(this object value)
41 | {
42 | var v = value is bool;
43 | return v;
44 | }
45 |
46 | public static bool IsJson(this object value)
47 | {
48 | if (value is String) {
49 | var st = (String)value;
50 | st = st.Trim(' ').Trim('"');
51 | if (st.Length > 0) {
52 | return (st[0] == '{' || st[0] == '[') && (st[st.Length -1] == '}' || st[st.Length - 1] == ']');
53 | }
54 | }
55 | return false;
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/src/LightStep/Collector/ReportTranslator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Google.Protobuf.WellKnownTypes;
8 | using LightStep.Logging;
9 |
10 | namespace LightStep.Collector
11 | {
12 | class ReportTranslator : IReportTranslator
13 | {
14 | private readonly Options _options;
15 | private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();
16 |
17 | public ReportTranslator(Options options)
18 | {
19 | _options = options;
20 | }
21 |
22 | ///
23 | /// Translate SpanData to a protobuf ReportRequest for sending to the Satellite.
24 | ///
25 | /// An enumerable of
26 | /// A
27 | public ReportRequest Translate(ISpanRecorder spanBuffer)
28 | {
29 | _logger.Trace($"Serializing {spanBuffer.GetSpanCount()} spans to proto.");
30 | var timer = new Stopwatch();
31 | timer.Start();
32 |
33 | var request = new ReportRequest
34 | {
35 | Reporter = new Reporter
36 | {
37 | ReporterId = _options.TracerGuid
38 | },
39 | Auth = new Auth { AccessToken = _options.AccessToken }
40 | };
41 | _options.Tags.ToList().ForEach(t => request.Reporter.Tags.Add(new KeyValue().MakeKeyValueFromKvp(t)));
42 | spanBuffer.GetSpans().ToList().ForEach(span => {
43 | try
44 | {
45 | request.Spans.Add(new Span().MakeSpanFromSpanData(span));
46 | }
47 | catch (Exception ex)
48 | {
49 | _logger.WarnException("Caught exception converting spans.", ex);
50 | spanBuffer.DroppedSpanCount++;
51 | }
52 | });
53 |
54 | var metrics = new InternalMetrics
55 | {
56 | StartTimestamp = Timestamp.FromDateTime(spanBuffer.ReportStartTime.ToUniversalTime()),
57 | DurationMicros = Convert.ToUInt64((spanBuffer.ReportEndTime - spanBuffer.ReportStartTime).Ticks / 10),
58 | Counts = { new MetricsSample() { Name = "spans.dropped", IntValue = spanBuffer.DroppedSpanCount } }
59 | };
60 | request.InternalMetrics = metrics;
61 |
62 | timer.Stop();
63 | _logger.Trace($"Serialization complete in {timer.ElapsedMilliseconds}ms. Request size: {request.CalculateSize()}b.");
64 |
65 | return request;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/LightStep/HighResolutionDateTime.cs:
--------------------------------------------------------------------------------
1 | // this code from https://raw.githubusercontent.com/openzipkin/zipkin4net/master/Src/zipkin4net/Src/Utils/HighResolutionDateTime.cs
2 | using System;
3 | #if !NET_CORE
4 | using System.Runtime.InteropServices;
5 | #endif
6 |
7 | namespace LightStep
8 | {
9 | internal static class HighResolutionDateTime
10 | {
11 | #if NET_CORE
12 | public static bool IsAvailable { get { return false; } }
13 | #else
14 | public static bool IsAvailable { get; private set; }
15 | #endif
16 |
17 | #if !NET_CORE
18 | [DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi)]
19 | private static extern void GetSystemTimePreciseAsFileTime(out long filetime);
20 | #endif
21 |
22 | public static DateTime UtcNow
23 | {
24 | get
25 | {
26 | #if !NET_CORE
27 | if (!IsAvailable)
28 | {
29 | throw new InvalidOperationException("High resolution clock isn't available.");
30 | }
31 |
32 | long filetime;
33 | GetSystemTimePreciseAsFileTime(out filetime);
34 |
35 | return DateTime.FromFileTimeUtc(filetime);
36 | #else
37 | throw new NotImplementedException();
38 | #endif
39 | }
40 | }
41 |
42 | #if !NET_CORE
43 | static HighResolutionDateTime()
44 | {
45 | if (!HasSufficientWindowsVersion(Environment.OSVersion.Platform, Environment.OSVersion.Version))
46 | {
47 | IsAvailable = false;
48 | return;
49 | }
50 |
51 | // Make sure the API is actually available
52 | try
53 | {
54 | long filetime;
55 | GetSystemTimePreciseAsFileTime(out filetime);
56 | IsAvailable = true;
57 | }
58 | catch (EntryPointNotFoundException)
59 | {
60 | IsAvailable = false;
61 | }
62 | }
63 |
64 | ///
65 | /// Check whether the given OS is Windows 8, Windows Server 2012 or above.
66 | ///
67 | /// PlatformId on which the application is currently running
68 | /// Windows version on which the application is currently running
69 | ///
70 | private static bool HasSufficientWindowsVersion(PlatformID platformId, Version windowsVersion)
71 | {
72 | var minimumRequiredVersion = new Version(6, 2, 9200, 0); // Windows 8, Windows Server 2012
73 |
74 | return (platformId == PlatformID.Win32NT) && (windowsVersion >= minimumRequiredVersion);
75 | }
76 | #endif
77 | }
78 | }
--------------------------------------------------------------------------------
/src/LightStep/ISpanRecorder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace LightStep
5 | {
6 | ///
7 | /// Records spans generated by the tracer. Used to batch/send spans to a collector.
8 | ///
9 | public interface ISpanRecorder
10 | {
11 | ///
12 | /// The start (creation) time of the span buffer.
13 | ///
14 | DateTime ReportStartTime { get; }
15 | ///
16 | /// The finishing (flushing) time of the span buffer.
17 | ///
18 | DateTime ReportEndTime { get; set; }
19 | ///
20 | /// The count of dropped spans for this, or a prior, span buffer.
21 | ///
22 | int DroppedSpanCount { get; set; }
23 |
24 | ///
25 | /// Saves a span.
26 | ///
27 | ///
28 | void RecordSpan(SpanData span);
29 |
30 | ///
31 | /// Returns this instance of the span buffer.
32 | ///
33 | ///
34 | ISpanRecorder GetSpanBuffer();
35 |
36 | ///
37 | /// Clears the span record.
38 | ///
39 | void ClearSpanBuffer();
40 |
41 | ///
42 | /// Increments the dropped span count.
43 | ///
44 | ///
45 | void RecordDroppedSpans(int count);
46 |
47 | ///
48 | /// Gets the spans stored in the buffer.
49 | ///
50 | ///
51 | IEnumerable GetSpans();
52 |
53 | ///
54 | /// Gets the count of spans in the buffer.
55 | ///
56 | ///
57 | int GetSpanCount();
58 | }
59 | }
--------------------------------------------------------------------------------
/src/LightStep/LightStep.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | LightStep
4 | LightStep
5 | 7.0
6 | net472;netstandard2.0
7 |
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Austin Parker
44 | LightStep
45 | en-US
46 | LightStep
47 | OpenTracing compliant tracer for LightStep.
48 | LightStep 2020
49 | tracing
50 | https://github.com/lightstep/lightstep-tracer-csharp/blob/master/CHANGELOG.md
51 | https://raw.githubusercontent.com/lightstep/lightstep-tracer-csharp/master/ls128x128.png
52 | http://www.lightstep.com
53 | MIT
54 | false
55 | git
56 | https://raw.githubusercontent.com/lightstep/lightstep-tracer-csharp
57 |
58 |
59 |
60 | true
61 | false
62 | ../../tracerSign.snk
63 | <_UseRoslynPublicSignHack>false
64 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
65 | false
66 | true
67 |
68 |
--------------------------------------------------------------------------------
/src/LightStep/LightStepConstants.cs:
--------------------------------------------------------------------------------
1 | namespace LightStep
2 | {
3 | ///
4 | /// Constants and other values used by LightStep.
5 | ///
6 | public static class LightStepConstants
7 | {
8 | public static readonly string ParentSpanGuidKey = "parent_span_guid";
9 | public static readonly string GuidKey = "lightstep.guid";
10 | public static readonly string HostnameKey = "lightstep.hostname";
11 | public static readonly string ComponentNameKey = "lightstep.component_name";
12 | public static readonly string CommandLineKey = "lightstep.command_line";
13 |
14 | public static readonly string TracerPlatformKey = "lightstep.tracer_platform";
15 | public static readonly string TracerPlatformValue = "csharp";
16 | public static readonly string TracerPlatformVersionKey = "lightstep.tracer_platform_version";
17 | public static readonly string TracerVersionKey = "lightstep.tracer_version";
18 |
19 | public static readonly string SatelliteReportPath = "api/v2/reports";
20 |
21 | public static readonly string AccessTokenConstant = "Lightstep-Access-Token";
22 |
23 | public static class MetaEvent {
24 | public static readonly string MetaEventKey = "lightstep.meta_event";
25 | public static readonly string PropagationFormatKey = "lightstep.propagation_format";
26 | public static readonly string TraceIdKey = "lightstep.trace_id";
27 | public static readonly string SpanIdKey = "lightstep.span_id";
28 | public static readonly string TracerGuidKey = "lightstep.tracer_guid";
29 | public static readonly string ExtractOperation = "lightstep.extract_span";
30 | public static readonly string InjectOperation = "lightstep.inject_span";
31 | public static readonly string SpanStartOperation = "lightstep.span_start";
32 | public static readonly string SpanFinishOperation = "lightstep.span_finish";
33 | public static readonly string TracerCreateOperation = "lightstep.tracer_create";
34 | }
35 |
36 | }
37 | }
--------------------------------------------------------------------------------
/src/LightStep/LightStepSpanRecorder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using LightStep.Logging;
5 |
6 | namespace LightStep
7 | {
8 | ///
9 | public sealed class LightStepSpanRecorder : ISpanRecorder
10 | {
11 | private List Spans { get; } = new List();
12 | private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();
13 |
14 | ///
15 | public DateTime ReportStartTime { get; } = DateTime.Now;
16 |
17 | ///
18 | public DateTime ReportEndTime { get; set; }
19 |
20 | ///
21 | public int DroppedSpanCount { get; set; }
22 |
23 | ///
24 | public void RecordSpan(SpanData span)
25 | {
26 | lock (Spans)
27 | {
28 | Spans.Add(span);
29 | }
30 | }
31 |
32 | ///
33 | public ISpanRecorder GetSpanBuffer()
34 | {
35 | lock (Spans)
36 | {
37 | ReportEndTime = DateTime.Now;
38 | return this;
39 | }
40 | }
41 |
42 | ///
43 | public void ClearSpanBuffer()
44 | {
45 | lock (Spans)
46 | {
47 | Spans.Clear();
48 | }
49 | }
50 |
51 | ///
52 | public void RecordDroppedSpans(int count)
53 | {
54 | DroppedSpanCount += count;
55 | }
56 |
57 | ///
58 | public IEnumerable GetSpans()
59 | {
60 | lock (Spans)
61 | {
62 | return Spans.ToList();
63 | }
64 | }
65 |
66 | public int GetSpanCount()
67 | {
68 | return Spans.Count;
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/src/LightStep/LogData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace LightStep
5 | {
6 | ///
7 | /// A single log event
8 | ///
9 | public struct LogData
10 | {
11 | ///
12 | /// Create a new log event
13 | ///
14 | /// A from the beginning of the span
15 | /// An enumerable of to log.
16 | public LogData(DateTimeOffset timestamp, IEnumerable> fields)
17 | {
18 | Timestamp = timestamp;
19 | Fields = fields;
20 | }
21 |
22 | ///
23 | /// The time since the beginning of the span.
24 | ///
25 | public DateTimeOffset Timestamp { get; }
26 |
27 | ///
28 | /// The log fields.
29 | ///
30 | public IEnumerable> Fields { get; }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/LightStep/Options.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Runtime.Versioning;
7 | using System.Text.RegularExpressions;
8 | using LightStep.Logging;
9 |
10 | namespace LightStep
11 | {
12 | ///
13 | /// Options for configuring the LightStep tracer.
14 | ///
15 | public class Options
16 | {
17 | private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();
18 |
19 | ///
20 | /// An identifier for the Tracer.
21 | ///
22 | public readonly ulong TracerGuid = new Random().NextUInt64();
23 |
24 | ///
25 | /// API key for a LightStep project.
26 | ///
27 | public string AccessToken { get; private set; }
28 |
29 | ///
30 | /// If true, tracer will start reporting
31 | ///
32 | ///
33 | public bool Run { get; private set; }
34 | ///
35 | /// True if the satellite connection should use HTTP/2, false otherwise.
36 | ///
37 | public bool UseHttp2 { get; private set; }
38 |
39 | ///
40 | /// LightStep Satellite endpoint configuration.
41 | ///
42 | public SatelliteOptions Satellite { get; private set; }
43 |
44 | ///
45 | /// How often the reporter will send spans to a LightStep Satellite.
46 | ///
47 | public TimeSpan ReportPeriod { get; private set; }
48 |
49 | ///
50 | /// Timeout for sending spans to a LightStep Satellite.
51 | ///
52 | public TimeSpan ReportTimeout { get; private set; }
53 |
54 | ///
55 | /// Maximum amount of spans to buffer in a single report.
56 | ///
57 | public int ReportMaxSpans { get; private set;}
58 |
59 | ///
60 | /// Tags that should be applied to each span generated by this tracer.
61 | ///
62 | public IDictionary Tags { get; private set; }
63 |
64 | ///
65 | /// If the tracer should send JSON rather than binary protobufs to the satellite.
66 | ///
67 | public TransportOptions Transport { get; private set; }
68 |
69 | ///
70 | /// Determines if tracer should report meta events to LightStep
71 | ///
72 | public Boolean EnableMetaEventLogging { get; internal set; }
73 |
74 | public Action ExceptionHandler { get; internal set; }
75 | public Boolean ExceptionHandlerRegistered { get; internal set; }
76 |
77 | public Options WithMetaEventLogging()
78 | {
79 | _logger.Debug("Enabling Meta Events");
80 | EnableMetaEventLogging = true;
81 | return this;
82 | }
83 |
84 | public Options WithToken(string token)
85 | {
86 | _logger.Debug($"Setting access token to {token}");
87 | AccessToken = token;
88 | return this;
89 | }
90 |
91 | public Options WithHttp2()
92 | {
93 | _logger.Debug("Enabling HTTP/2 support.");
94 | UseHttp2 = true;
95 | return this;
96 | }
97 |
98 | public Options WithSatellite(SatelliteOptions options)
99 | {
100 | _logger.Debug($"Setting satellite to {options}");
101 | Satellite = options;
102 | return this;
103 | }
104 |
105 | public Options WithReportPeriod(TimeSpan period)
106 | {
107 | _logger.Debug($"Setting reporting period to {period}");
108 | ReportPeriod = period;
109 | return this;
110 | }
111 |
112 | public Options WithReportTimeout(TimeSpan timeout)
113 | {
114 | _logger.Debug($"Setting report timeout to {timeout}");
115 | ReportTimeout = timeout;
116 | return this;
117 | }
118 |
119 | public Options WithTags(IDictionary tags)
120 | {
121 | _logger.Debug($"Setting default tags to: {tags.Select(kvp => $"{kvp.Key}:{kvp.Value},")}");
122 | Tags = MergeTags(tags);
123 | return this;
124 | }
125 |
126 | public Options WithAutomaticReporting(bool shouldRun)
127 | {
128 | _logger.Debug($"Setting automatic reporting to {shouldRun}");
129 | Run = shouldRun;
130 | return this;
131 | }
132 |
133 | public Options WithMaxBufferedSpans(int count)
134 | {
135 | _logger.Debug($"Setting max spans per buffer to {count}");
136 | ReportMaxSpans = count;
137 | return this;
138 | }
139 |
140 | public Options WithTransport(TransportOptions transport)
141 | {
142 | _logger.Debug($"Setting JSON reports to {transport}");
143 | Transport = transport;
144 | return this;
145 | }
146 |
147 | public Options WithExceptionHandler(Action handler)
148 | {
149 | _logger.Debug($"Registering exception handler {handler}");
150 | ExceptionHandler = handler;
151 | ExceptionHandlerRegistered = true;
152 | return this;
153 | }
154 |
155 | ///
156 | /// Creates a new set of options for the LightStep tracer.
157 | ///
158 | /// Project access token, if required.
159 | public Options(string token = "")
160 | {
161 | Tags = InitializeDefaultTags();
162 | ReportPeriod = TimeSpan.FromMilliseconds(5000);
163 | ReportTimeout = TimeSpan.FromSeconds(30);
164 | AccessToken = token;
165 | Satellite = new SatelliteOptions("collector.lightstep.com", 443, false);
166 | UseHttp2 = false;
167 | Run = true;
168 | ReportMaxSpans = int.MaxValue;
169 | Transport = TransportOptions.BinaryProto;
170 | EnableMetaEventLogging = false;
171 | ExceptionHandlerRegistered = false;
172 | }
173 |
174 | private IDictionary MergeTags(IDictionary input)
175 | {
176 | var attributes = InitializeDefaultTags();
177 | var mergedAttributes = new Dictionary(input);
178 | foreach (var item in attributes)
179 | {
180 | if (!mergedAttributes.ContainsKey(item.Key))
181 | {
182 | mergedAttributes.Add(item.Key, item.Value);
183 | }
184 | }
185 |
186 | return mergedAttributes;
187 | }
188 |
189 | private IDictionary InitializeDefaultTags()
190 | {
191 | var attributes = new Dictionary
192 | {
193 | [LightStepConstants.TracerPlatformKey] = LightStepConstants.TracerPlatformValue,
194 | [LightStepConstants.TracerPlatformVersionKey] = GetPlatformVersion(),
195 | [LightStepConstants.TracerVersionKey] = GetTracerVersion(),
196 | [LightStepConstants.ComponentNameKey] = GetComponentName(),
197 | [LightStepConstants.HostnameKey] = GetHostName(),
198 | [LightStepConstants.CommandLineKey] = GetCommandLine()
199 | };
200 | return attributes;
201 | }
202 |
203 | private static string GetTracerVersion()
204 | {
205 | return typeof(LightStep.Tracer).Assembly.GetCustomAttribute().InformationalVersion;
206 | }
207 |
208 | private static string GetComponentName()
209 | {
210 | var entryAssembly = "";
211 | try
212 | {
213 | entryAssembly = Assembly.GetEntryAssembly().GetName().Name;
214 | }
215 | catch (NullReferenceException)
216 | {
217 | // could not get assembly name, possibly because we're running a test
218 | entryAssembly = "unknown";
219 | }
220 | return entryAssembly;
221 | }
222 |
223 | private static string GetPlatformVersion()
224 | {
225 | var version = ".NET Unknown";
226 | version = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
227 | return version.Remove(0, version.IndexOf(' ', 0));
228 | }
229 |
230 |
231 | private static string GetHostName()
232 | {
233 | return Environment.MachineName;
234 | }
235 |
236 | private static string GetCommandLine()
237 | {
238 | return Environment.CommandLine;
239 | }
240 | }
241 | }
--------------------------------------------------------------------------------
/src/LightStep/Propagation/B3Propagator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using OpenTracing.Propagation;
3 |
4 | namespace LightStep.Propagation
5 | {
6 | ///
7 | public class B3Propagator : IPropagator
8 | {
9 | public const string TraceIdName = "X-B3-TraceId";
10 | public const string SpanIdName = "X-B3-SpanId";
11 | public const string SampledName = "X-B3-Sampled";
12 |
13 | ///
14 | public void Inject(SpanContext context, IFormat format, TCarrier carrier)
15 | {
16 | if (carrier is ITextMap text)
17 | {
18 | text.Set(TraceIdName, context.OriginalTraceId);
19 | text.Set(SpanIdName, context.SpanId);
20 | text.Set(SampledName, "1");
21 | }
22 | }
23 |
24 | ///
25 | public SpanContext Extract(IFormat format, TCarrier carrier)
26 | {
27 | if (carrier is ITextMap text)
28 | {
29 | ulong? traceId = null;
30 | string OriginalTraceId = null;
31 | ulong? spanId = null;
32 |
33 | foreach (var entry in text)
34 | {
35 | if (TraceIdName.Equals(entry.Key, StringComparison.OrdinalIgnoreCase))
36 | {
37 | traceId = ParseTraceId(entry.Value);
38 | OriginalTraceId = entry.Value;
39 | }
40 | else if (SpanIdName.Equals(entry.Key, StringComparison.OrdinalIgnoreCase))
41 | {
42 | spanId = Convert.ToUInt64(entry.Value, 16);
43 | }
44 | }
45 |
46 | if (traceId.HasValue && spanId.HasValue)
47 | {
48 | return new SpanContext(traceId.Value, spanId.Value, originalTraceId: OriginalTraceId);
49 | }
50 | }
51 |
52 | return null;
53 | }
54 |
55 | private static ulong ParseTraceId(string str)
56 | {
57 | ulong traceId;
58 |
59 | if (ContainsHexChar(str))
60 | {
61 | if (str.Length <= 16)
62 | {
63 | traceId = Convert.ToUInt64(str, 16);
64 | }
65 | else
66 | {
67 | traceId = Convert.ToUInt64(str.Substring(str.Length - 16), 16);
68 | }
69 | }
70 | else
71 | {
72 | if (str.Length <= 20)
73 | {
74 | traceId = Convert.ToUInt64(str);
75 | }
76 | else
77 | {
78 | traceId = Convert.ToUInt64(str.Substring(str.Length - 20));
79 | }
80 | }
81 |
82 | return traceId;
83 | }
84 |
85 | private static bool ContainsHexChar(string traceId)
86 | {
87 | foreach (var c in traceId)
88 | {
89 | if (char.IsLetter(c))
90 | {
91 | return true;
92 | }
93 | }
94 |
95 | return false;
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/src/LightStep/Propagation/BinaryPropagator.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lightstep/lightstep-tracer-csharp/59c3e63658fd69bf732ddc019e90607d9b014c11/src/LightStep/Propagation/BinaryPropagator.cs
--------------------------------------------------------------------------------
/src/LightStep/Propagation/ConsolePropagator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using OpenTracing.Propagation;
3 |
4 | namespace LightStep.Propagation
5 | {
6 | ///
7 | public class ConsolePropagator : IPropagator
8 | {
9 | ///
10 | public void Inject(SpanContext context, IFormat format, TCarrier carrier)
11 | {
12 | Console.WriteLine($"Inject({context}, {format}, {carrier})");
13 | }
14 |
15 | ///
16 | public SpanContext Extract(IFormat format, TCarrier carrier)
17 | {
18 | Console.WriteLine($"Extract({format}, {carrier}");
19 | return null;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/LightStep/Propagation/EnvoyPropagator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Google.Protobuf;
4 | using LightStep.Logging;
5 | using OpenTracing.Propagation;
6 |
7 | namespace LightStep.Propagation
8 | {
9 | public class EnvoyPropagator : IPropagator
10 | {
11 | private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();
12 |
13 | ///
14 | public void Inject(SpanContext context, IFormat format, TCarrier carrier)
15 | {
16 | _logger.Trace($"Injecting {context} of {format.GetType()} to {carrier.GetType()}");
17 | if (carrier is IBinary s)
18 | {
19 | var ctx = new BinaryCarrier
20 | {
21 | BasicCtx = new BasicTracerCarrier
22 | {
23 | SpanId = context.SpanIdValue,
24 | TraceId = context.TraceIdValue,
25 | Sampled = true
26 | }
27 | };
28 | foreach (var item in context.GetBaggageItems()) ctx.BasicCtx.BaggageItems.Add(item.Key, item.Value);
29 | var ctxArray = ctx.ToByteArray();
30 | var ctxStream = new MemoryStream(ctxArray);
31 | s.Set(ctxStream);
32 | }
33 | }
34 |
35 | ///
36 | public SpanContext Extract(IFormat format, TCarrier carrier)
37 | {
38 | if (carrier is IBinary s)
39 | {
40 | var ctx = BinaryCarrier.Parser.ParseFrom(s.Get());
41 | var traceId = ctx.BasicCtx.TraceId;
42 | var spanId = ctx.BasicCtx.SpanId;
43 | var baggage = new Baggage();
44 |
45 | foreach (var item in ctx.BasicCtx.BaggageItems)
46 | {
47 | baggage.Set(item.Key, item.Value);
48 | }
49 |
50 | return new SpanContext(traceId, spanId, baggage);
51 | }
52 |
53 | return null;
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/src/LightStep/Propagation/HttpHeadersPropagator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using OpenTracing.Propagation;
3 |
4 | namespace LightStep.Propagation
5 | {
6 | /** TODO: this is a blind copy of the Java client; once HTTP carrier encoding has been defined, update this as well
7 | *
8 | */
9 | ///
10 | public class HttpHeadersPropagator : IPropagator
11 | {
12 | ///
13 | public void Inject(SpanContext context, IFormat format, TCarrier carrier)
14 | {
15 | var textMapPropagator = new TextMapPropagator();
16 | textMapPropagator.Inject(context, format, carrier);
17 | }
18 |
19 | ///
20 | public SpanContext Extract(IFormat format, TCarrier carrier)
21 | {
22 | var textMapPropagator = new TextMapPropagator();
23 | return textMapPropagator.Extract(format, carrier, StringComparison.OrdinalIgnoreCase);
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/LightStep/Propagation/IPropagator.cs:
--------------------------------------------------------------------------------
1 | using OpenTracing.Propagation;
2 |
3 | namespace LightStep.Propagation
4 | {
5 | ///
6 | /// Implements the and methods for passing contexts
7 | /// between process boundaries.
8 | ///
9 | public interface IPropagator
10 | {
11 | ///
12 | /// Injects a into a .
13 | ///
14 | /// The instance to inject into the
15 | /// The of the
16 | /// The carrier for the
17 | ///
18 | /// The type, which also parametrizes the
19 | ///
20 | void Inject(SpanContext context, IFormat format, TCarrier carrier);
21 |
22 | ///
23 | /// Extracts a from a .
24 | ///
25 | /// The or the
26 | /// The of the
27 | ///
28 | /// The type, which also parametrizes the
29 | ///
30 | /// The instance to create a span.
31 | SpanContext Extract(IFormat format, TCarrier carrier);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/LightStep/Propagation/Keys.cs:
--------------------------------------------------------------------------------
1 | namespace LightStep.Propagation
2 | {
3 | ///
4 | /// Key prefixes used in baggage/traces/etc.
5 | ///
6 | public static class Keys
7 | {
8 | ///
9 | /// OpenTracing Baggage Prefix
10 | ///
11 | public const string BaggagePrefix = "ot-baggage-";
12 |
13 | ///
14 | /// OpenTracing TraceId Prefix
15 | ///
16 | public const string TraceId = "ot-tracer-traceid";
17 |
18 | ///
19 | /// OpenTracing SpanId Prefix
20 | ///
21 | public const string SpanId = "ot-tracer-spanid";
22 |
23 | ///
24 | /// OpenTracing Sampled Prefix
25 | ///
26 | public const string Sampled = "ot-tracer-sampled";
27 | }
28 | }
--------------------------------------------------------------------------------
/src/LightStep/Propagation/PropagatorStack.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using LightStep.Logging;
4 | using OpenTracing.Propagation;
5 |
6 | namespace LightStep.Propagation
7 | {
8 | ///
9 | public class PropagatorStack : IPropagator
10 | {
11 | private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();
12 |
13 | ///
14 | public PropagatorStack(IFormat format)
15 | {
16 | if (format == null) throw new ArgumentNullException(nameof(format));
17 | _logger.Trace($"Creating new PropagatorStack with format {format}");
18 | Format = format;
19 | Propagators = new List();
20 | }
21 |
22 | ///
23 | /// The format of the propagators
24 | ///
25 | public IFormat Format { get; }
26 |
27 | ///
28 | /// A list of propagators to attempt to match.
29 | ///
30 | public List Propagators { get; }
31 |
32 | ///
33 | public void Inject(SpanContext context, IFormat format, TCarrier carrier)
34 | {
35 | Propagators.ForEach(propagator =>
36 | {
37 | _logger.Trace($"Injecting context to {propagator.GetType()}");
38 | propagator.Inject(context, format, carrier);
39 | }
40 | );
41 | }
42 |
43 | ///
44 | public SpanContext Extract(IFormat format, TCarrier carrier)
45 | {
46 | for (var i = Propagators.Count - 1; i >= 0; i--)
47 | {
48 | _logger.Trace($"Trying to extract from {Propagators[i].GetType()}");
49 | var context = Propagators[i].Extract(format, carrier);
50 | if (context != null) return context;
51 | }
52 |
53 | return null;
54 | }
55 |
56 | ///
57 | /// Add a new propagator to a stack.
58 | ///
59 | ///
60 | ///
61 | ///
62 | public PropagatorStack AddPropagator(IPropagator propagator)
63 | {
64 | if (propagator == null) throw new ArgumentNullException(nameof(propagator));
65 | _logger.Trace($"Adding {propagator.GetType()} to PropagatorStack.");
66 | Propagators.Add(propagator);
67 | return this;
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/src/LightStep/Propagation/Propagators.cs:
--------------------------------------------------------------------------------
1 | namespace LightStep.Propagation
2 | {
3 | ///
4 | /// Static references to each propagator available.
5 | ///
6 | public static class Propagators
7 | {
8 | ///
9 | /// Writes context values to the console, used for local development.
10 | ///
11 | public static readonly IPropagator Console = new ConsolePropagator();
12 |
13 | ///
14 | /// A key:value store for string pairs.
15 | ///
16 | public static readonly IPropagator TextMap = new TextMapPropagator();
17 |
18 | ///
19 | /// Supports B3 headers, such as those used in Zipkin or StageMonitor.
20 | ///
21 | public static readonly IPropagator B3Propagator = new B3Propagator();
22 |
23 | ///
24 | /// Supports HTTP Header Propagation
25 | ///
26 | public static readonly IPropagator HttpHeadersPropagator = new HttpHeadersPropagator();
27 | }
28 | }
--------------------------------------------------------------------------------
/src/LightStep/Propagation/TextMapPropagator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LightStep.Logging;
3 | using OpenTracing.Propagation;
4 |
5 | namespace LightStep.Propagation
6 | {
7 | ///
8 | public class TextMapPropagator : IPropagator
9 | {
10 | private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();
11 | ///
12 | public void Inject(SpanContext context, IFormat format, TCarrier carrier)
13 | {
14 | _logger.Trace($"Injecting {context} of {format.GetType()} to {carrier.GetType()}");
15 | if (carrier is ITextMap text)
16 | {
17 | foreach (var entry in context.GetBaggageItems()) text.Set(Keys.BaggagePrefix + entry.Key, entry.Value);
18 |
19 | text.Set(Keys.SpanId, context.SpanId);
20 | text.Set(Keys.TraceId, context.TraceId);
21 | text.Set(Keys.Sampled, "true");
22 | }
23 | else
24 | {
25 | _logger.Warn($"Unknown carrier during inject.");
26 | throw new InvalidOperationException($"Unknown carrier {carrier.GetType()}");
27 | }
28 | }
29 |
30 | ///
31 | public SpanContext Extract(IFormat format, TCarrier carrier)
32 | {
33 | return Extract(format, carrier, StringComparison.Ordinal);
34 | }
35 |
36 | public SpanContext Extract(IFormat format, TCarrier carrier, StringComparison comparison)
37 | {
38 | _logger.Trace($"Extracting {format.GetType()} from {carrier.GetType()}");
39 | if (carrier is ITextMap text)
40 | {
41 | ulong? traceId = null;
42 | ulong? spanId = null;
43 | var baggage = new Baggage();
44 |
45 | foreach (var entry in text)
46 | {
47 | if (Keys.TraceId.Equals(entry.Key, comparison))
48 | {
49 | traceId = Convert.ToUInt64(entry.Value, 16);
50 | }
51 | else if (Keys.SpanId.Equals(entry.Key, comparison))
52 | {
53 | spanId = Convert.ToUInt64(entry.Value, 16);
54 | }
55 | else if (entry.Key.StartsWith(Keys.BaggagePrefix, comparison))
56 | {
57 | var key = entry.Key.Substring(Keys.BaggagePrefix.Length);
58 | baggage.Set(key, entry.Value);
59 | }
60 | }
61 |
62 | if (traceId.HasValue && spanId.HasValue)
63 | {
64 | _logger.Trace($"Existing trace/spanID found, returning SpanContext.");
65 | return new SpanContext(traceId.Value, spanId.Value);
66 | }
67 | }
68 |
69 | return null;
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/src/LightStep/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by Cake.
4 | //
5 | //------------------------------------------------------------------------------
6 | using System.Reflection;
7 | using System.Runtime.CompilerServices;
8 |
9 | [assembly: AssemblyProduct("LightStep")]
10 | [assembly: AssemblyVersion("0.0.0.0")]
11 | [assembly: AssemblyFileVersion("0.0.0.0")]
12 | [assembly: AssemblyInformationalVersion("0.0.0")]
13 | [assembly: AssemblyCopyright("Copyright (c) LightStep 2018 - 2020")]
14 |
15 | [assembly: InternalsVisibleTo("LightStep.Tests,PublicKey=002400000480000094000000060200000024000052534131000400000100010099deeadb052e9763d2dc7827700d80e349e5d16585c92416171e6689a4bd38a3acea971d5899d5e2cd4239c3dc799558138e961f8d0f5095fef969672172833868f2cc2d908970370af834beef9dad328182fee2aaf0d0bb568ffd1f829362b88718734541d334c6a2cdf0049f5a0ee5e4962d0db3f49f86bf742f9531bd9c8c")]
16 |
17 |
--------------------------------------------------------------------------------
/src/LightStep/Reference.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace LightStep
5 | {
6 | public sealed class Reference : IEquatable
7 | {
8 | public SpanContext Context { get; }
9 |
10 | public string ReferenceType { get; }
11 |
12 | public Reference(SpanContext context, string referenceType)
13 | {
14 | Context = context ?? throw new ArgumentNullException(nameof(context));
15 | ReferenceType = referenceType ?? throw new ArgumentNullException(nameof(referenceType));
16 | }
17 |
18 | public override bool Equals(object obj)
19 | {
20 | return Equals(obj as Reference);
21 | }
22 |
23 | public bool Equals(Reference other)
24 | {
25 | return other != null &&
26 | EqualityComparer.Default.Equals(Context, other.Context)
27 | && ReferenceType == other.ReferenceType;
28 | }
29 |
30 | public override int GetHashCode()
31 | {
32 | var hashCode = 2083322454;
33 | hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Context);
34 | hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ReferenceType);
35 | return hashCode;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/LightStep/SatelliteOptions.cs:
--------------------------------------------------------------------------------
1 | namespace LightStep
2 | {
3 | ///
4 | /// Options to configure a LightStep satellite.
5 | ///
6 | public class SatelliteOptions
7 | {
8 | ///
9 | /// Create a new Satellite Endpoint.
10 | ///
11 | /// satellite hostname
12 | /// satellite port
13 | /// unused
14 | public SatelliteOptions(string host, int port = 443, bool usePlaintext = false)
15 | {
16 | SatelliteHost = host;
17 | SatellitePort = port;
18 | UsePlaintext = usePlaintext;
19 | }
20 |
21 | ///
22 | /// Hostname of a Satellite
23 | ///
24 | public string SatelliteHost { get; }
25 |
26 | ///
27 | /// Port number of a Satellite
28 | ///
29 | public int SatellitePort { get; }
30 |
31 | ///
32 | /// Currently unused
33 | ///
34 | public bool UsePlaintext { get; }
35 |
36 | public override string ToString()
37 | {
38 | return $"Host: {SatelliteHost}, Port: {SatellitePort}, Use Plaintext: {UsePlaintext}";
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/src/LightStep/Span.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Security.Cryptography;
5 | using LightStep.Logging;
6 | using OpenTracing;
7 | using OpenTracing.Tag;
8 |
9 | namespace LightStep
10 | {
11 | ///
12 | public sealed class Span : ISpan
13 | {
14 | private readonly SpanContext _context;
15 | private readonly List _errors = new List();
16 | private readonly object _lock = new object();
17 | private readonly List _logs = new List();
18 | private readonly List _references;
19 | private readonly Dictionary _tags;
20 |
21 | private readonly Tracer _tracer;
22 | private bool _finished;
23 | private DateTimeOffset _finishTimestamp;
24 |
25 | private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();
26 |
27 | ///
28 | /// Create a new Span.
29 | ///
30 | /// Tracer that will record the span.
31 | /// Operation name being recorded by the span.
32 | /// The at which the span begun.
33 | /// Tags for the span.
34 | /// References to other spans.
35 | public Span(
36 | Tracer tracer,
37 | string operationName,
38 | DateTimeOffset startTimestamp,
39 | IDictionary tags,
40 | IReadOnlyCollection references)
41 | {
42 | _tracer = tracer;
43 | OperationName = operationName;
44 | StartTimestamp = startTimestamp;
45 |
46 | _tags = tags == null
47 | ? new Dictionary()
48 | : new Dictionary(tags);
49 |
50 | _references = references == null
51 | ? new List()
52 | : references.ToList();
53 |
54 |
55 | var parentContext = FindPreferredParentRef(_references);
56 |
57 | OperationName = operationName.Trim();
58 | StartTimestamp = startTimestamp;
59 |
60 | if (parentContext == null)
61 | {
62 | // we are a root span
63 | _context = new SpanContext(GetRandomId(), GetRandomId(), new Baggage());
64 | ParentId = null;
65 | }
66 | else
67 | {
68 | // we are a child span
69 | _context = new SpanContext(parentContext.TraceIdValue, GetRandomId(), MergeBaggages(_references),
70 | parentContext.SpanIdValue);
71 | ParentId = parentContext.SpanId;
72 | }
73 | if (_tracer._options.EnableMetaEventLogging && Utilities.IsNotMetaSpan(this))
74 | {
75 | _tracer.BuildSpan(LightStepConstants.MetaEvent.TracerCreateOperation)
76 | .IgnoreActiveSpan()
77 | .WithTag(LightStepConstants.MetaEvent.MetaEventKey, true)
78 | .WithTag(LightStepConstants.MetaEvent.SpanIdKey, _context.SpanId)
79 | .WithTag(LightStepConstants.MetaEvent.TraceIdKey, _context.TraceId)
80 | .Start()
81 | .Finish();
82 | }
83 |
84 | }
85 |
86 | ///
87 | /// SpanID of this span's parent.
88 | ///
89 | private string ParentId { get; }
90 |
91 | ///
92 | /// The start time of the span.
93 | ///
94 | private DateTimeOffset StartTimestamp { get; }
95 |
96 | ///
97 | /// The finish time of the span.
98 | ///
99 | /// A span must be finished before setting the finish timestamp.
100 | private DateTimeOffset FinishTimestamp
101 | {
102 | get
103 | {
104 | if (_finishTimestamp == DateTimeOffset.MinValue)
105 | throw new InvalidOperationException("Must call Finish() before FinishTimestamp");
106 |
107 | return _finishTimestamp;
108 | }
109 | }
110 |
111 | ///
112 | /// The operation name of the span.
113 | ///
114 | private string OperationName { get; set; }
115 |
116 | public Dictionary Tags => new Dictionary(_tags);
117 | public List Logs => new List(_logs);
118 | public List Errors => new List(_errors);
119 |
120 | ///
121 | /// The span's
122 | ///
123 | private SpanContext Context
124 | {
125 | get
126 | {
127 | lock (_lock)
128 | {
129 | return _context;
130 | }
131 | }
132 | }
133 |
134 | ISpanContext ISpan.Context => Context;
135 |
136 | ///
137 | public ISpan Log(IEnumerable> fields)
138 | {
139 | return Log(DateTimeOffset.Now, fields);
140 | }
141 |
142 | ///
143 | public ISpan Log(DateTimeOffset timestamp, IEnumerable> fields)
144 | {
145 | lock (_lock)
146 | {
147 | CheckIfSpanFinished("Adding logs {0} at {1} to already finished span.", fields, timestamp);
148 | _logs.Add(new LogData(timestamp, fields));
149 | return this;
150 | }
151 | }
152 |
153 | ///
154 | public ISpan Log(string eventName)
155 | {
156 | return Log(DateTimeOffset.Now, eventName);
157 | }
158 |
159 | ///
160 | public ISpan Log(DateTimeOffset timestamp, string eventName)
161 | {
162 | return Log(timestamp, new Dictionary {{"event", eventName}});
163 | }
164 |
165 | ///
166 | public ISpan SetBaggageItem(string key, string value)
167 | {
168 | lock (_lock)
169 | {
170 | CheckIfSpanFinished("Adding baggage [{0}:{1}] to finished span.", key, value);
171 | _context.SetBaggageItem(key, value);
172 | return this;
173 | }
174 | }
175 |
176 | ///
177 | public string GetBaggageItem(string key)
178 | {
179 | lock (_lock)
180 | {
181 | return _context.GetBaggageItem(key);
182 | }
183 | }
184 |
185 | ///
186 | public void Finish()
187 | {
188 | Finish(HighResolutionDateTime.IsAvailable ? new DateTimeOffset(HighResolutionDateTime.UtcNow) : DateTimeOffset.UtcNow);
189 | }
190 |
191 | ///
192 | public void Finish(DateTimeOffset finishTimestamp)
193 | {
194 | lock (_lock)
195 | {
196 | CheckIfSpanFinished("Tried to finish already finished span");
197 | _finishTimestamp = finishTimestamp;
198 | _finished = true;
199 | OnFinished();
200 | }
201 | }
202 |
203 | private static ulong GetRandomId()
204 | {
205 | var provider = new RNGCryptoServiceProvider();
206 | var buffer = new byte[64];
207 | provider.GetBytes(buffer);
208 | return BitConverter.ToUInt64(buffer, 0);
209 | }
210 |
211 | private static SpanContext FindPreferredParentRef(IList references)
212 | {
213 | if (!references.Any()) return null;
214 |
215 | foreach (var reference in references)
216 | if (References.ChildOf.Equals(reference.ReferenceType))
217 | return reference.Context;
218 |
219 | return references.First().Context;
220 | }
221 |
222 | private static Baggage MergeBaggages(IList references)
223 | {
224 | var baggage = new Baggage();
225 | foreach (var reference in references)
226 | if (reference.Context.GetBaggageItems() != null)
227 | foreach (var bagItem in reference.Context.GetBaggageItems())
228 | baggage.Set(bagItem.Key, bagItem.Value);
229 |
230 | return baggage;
231 | }
232 |
233 | private void CheckIfSpanFinished(string format, params object[] args)
234 | {
235 | if (_finished)
236 | {
237 | var ex = new InvalidOperationException(string.Format(format, args));
238 | _errors.Add(ex);
239 | _logger.Error(ex.Message);
240 | }
241 | }
242 |
243 | private ISpan SetObjectTag(string key, object value)
244 | {
245 | lock (_lock)
246 | {
247 | CheckIfSpanFinished("Setting tag [{0}:{1}] on finished span", key, value);
248 | _tags[key] = value;
249 | return this;
250 | }
251 | }
252 |
253 | private void OnFinished()
254 | {
255 | var spanData = new SpanData
256 | {
257 | Context = this.TypedContext(),
258 | OperationName = OperationName,
259 | StartTimestamp = StartTimestamp,
260 | Duration = FinishTimestamp - StartTimestamp,
261 | Tags = _tags,
262 | LogData = _logs
263 | };
264 |
265 | _tracer.AppendFinishedSpan(spanData);
266 | if(_tracer._options.EnableMetaEventLogging && Utilities.IsNotMetaSpan(this))
267 | {
268 | _tracer.BuildSpan(LightStepConstants.MetaEvent.SpanFinishOperation)
269 | .IgnoreActiveSpan()
270 | .WithTag(LightStepConstants.MetaEvent.MetaEventKey, true)
271 | .WithTag(LightStepConstants.MetaEvent.SpanIdKey, this.TypedContext().SpanId)
272 | .WithTag(LightStepConstants.MetaEvent.TraceIdKey, this.TypedContext().TraceId)
273 | .Start()
274 | .Finish();
275 | }
276 | }
277 |
278 | #region Setters
279 |
280 | ///
281 | public ISpan SetOperationName(string operationName)
282 | {
283 | lock (_lock)
284 | {
285 | if (string.IsNullOrWhiteSpace(operationName)) throw new ArgumentNullException(nameof(operationName));
286 |
287 | CheckIfSpanFinished("Setting operationName [{0}] on finished span.", operationName);
288 | OperationName = operationName;
289 | return this;
290 | }
291 | }
292 |
293 | ///
294 | public ISpan SetTag(string key, bool value)
295 | {
296 | if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
297 |
298 | return SetObjectTag(key, value);
299 | }
300 |
301 | ///
302 | public ISpan SetTag(string key, double value)
303 | {
304 | if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
305 |
306 | return SetObjectTag(key, value);
307 | }
308 |
309 | ///
310 | public ISpan SetTag(string key, int value)
311 | {
312 | if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
313 |
314 | return SetObjectTag(key, value);
315 | }
316 |
317 | ///
318 | public ISpan SetTag(string key, string value)
319 | {
320 | if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
321 |
322 | return SetObjectTag(key, value);
323 | }
324 |
325 | ///
326 | public ISpan SetTag(BooleanTag tag, bool value)
327 | {
328 | if (string.IsNullOrWhiteSpace(tag.Key)) throw new ArgumentNullException(nameof(tag.Key));
329 |
330 | return SetObjectTag(tag.Key, value);
331 | }
332 |
333 | ///
334 | public ISpan SetTag(IntOrStringTag tag, string value)
335 | {
336 | if (string.IsNullOrWhiteSpace(tag.Key)) throw new ArgumentNullException(nameof(tag.Key));
337 |
338 | return SetObjectTag(tag.Key, value);
339 | }
340 |
341 | ///
342 | public ISpan SetTag(IntTag tag, int value)
343 | {
344 | if (string.IsNullOrWhiteSpace(tag.Key)) throw new ArgumentNullException(nameof(tag.Key));
345 |
346 | return SetObjectTag(tag.Key, value);
347 | }
348 |
349 | ///
350 | public ISpan SetTag(StringTag tag, string value)
351 | {
352 | if (string.IsNullOrWhiteSpace(tag.Key)) throw new ArgumentNullException(nameof(tag.Key));
353 |
354 | return SetObjectTag(tag.Key, value);
355 | }
356 |
357 | #endregion
358 | }
359 | }
--------------------------------------------------------------------------------
/src/LightStep/SpanBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using OpenTracing;
5 | using OpenTracing.Tag;
6 |
7 | namespace LightStep
8 | {
9 | ///
10 | public class SpanBuilder : ISpanBuilder
11 | {
12 | private readonly string _operationName;
13 | private readonly List _references = new List();
14 | private readonly Tracer _tracer;
15 | private bool _ignoreActiveSpan;
16 | private DateTimeOffset _startTimestamp = HighResolutionDateTime.IsAvailable ? new DateTimeOffset(HighResolutionDateTime.UtcNow) : DateTimeOffset.UtcNow;
17 | private Dictionary _tags = new Dictionary();
18 |
19 | ///
20 | public SpanBuilder(Tracer tracer, string operationName)
21 | {
22 | if (string.IsNullOrWhiteSpace(operationName)) throw new ArgumentNullException(nameof(operationName));
23 |
24 | _tracer = tracer ?? throw new ArgumentNullException(nameof(tracer));
25 | _operationName = operationName;
26 | }
27 |
28 | ///
29 | public ISpanBuilder AsChildOf(ISpanContext parent)
30 | {
31 | if (parent == null) return this;
32 |
33 | return AddReference(References.ChildOf, parent);
34 | }
35 |
36 | ///
37 | public ISpanBuilder AsChildOf(ISpan parent)
38 | {
39 | if (parent == null) return this;
40 |
41 | return AsChildOf(parent.Context);
42 | }
43 |
44 | ///
45 | public ISpanBuilder AddReference(string referenceType, ISpanContext referencedContext)
46 | {
47 | if (string.IsNullOrWhiteSpace(referenceType)) throw new ArgumentNullException(nameof(referenceType));
48 |
49 | if (referencedContext != null)
50 | _references.Add(new Reference((SpanContext) referencedContext, referenceType));
51 |
52 | return this;
53 | }
54 |
55 | ///
56 | public ISpanBuilder IgnoreActiveSpan()
57 | {
58 | _ignoreActiveSpan = true;
59 | return this;
60 | }
61 |
62 | ///
63 | public ISpanBuilder WithTag(StringTag tag, string value)
64 | {
65 | _tags[tag.Key] = value;
66 | return this;
67 | }
68 |
69 | ///
70 | public ISpanBuilder WithStartTimestamp(DateTimeOffset startTimestamp)
71 | {
72 | _startTimestamp = startTimestamp;
73 | return this;
74 | }
75 |
76 | ///
77 | public IScope StartActive()
78 | {
79 | return StartActive(true);
80 | }
81 |
82 | ///
83 | public IScope StartActive(bool finishSpanOnDispose)
84 | {
85 | var span = Start();
86 | return _tracer.ScopeManager.Activate(span, finishSpanOnDispose);
87 | }
88 |
89 | ///
90 | public ISpanBuilder WithTag(string key, bool value)
91 | {
92 | if (_tags == null) _tags = new Dictionary();
93 |
94 | _tags[key] = value;
95 | return this;
96 | }
97 |
98 | ///
99 | public ISpanBuilder WithTag(string key, double value)
100 | {
101 | if (_tags == null) _tags = new Dictionary();
102 |
103 | _tags[key] = value;
104 | return this;
105 | }
106 |
107 | ///
108 | public ISpanBuilder WithTag(BooleanTag tag, bool value)
109 | {
110 | _tags[tag.Key] = value;
111 | return this;
112 | }
113 |
114 | ///
115 | public ISpanBuilder WithTag(IntOrStringTag tag, string value)
116 | {
117 | _tags[tag.Key] = value;
118 | return this;
119 | }
120 |
121 | ///
122 | public ISpanBuilder WithTag(IntTag tag, int value)
123 | {
124 | _tags[tag.Key] = value;
125 | return this;
126 | }
127 |
128 | ///
129 | public ISpanBuilder WithTag(string key, int value)
130 | {
131 | if (_tags == null) _tags = new Dictionary();
132 |
133 | _tags[key] = value;
134 | return this;
135 | }
136 |
137 | ///
138 | public ISpanBuilder WithTag(string key, string value)
139 | {
140 | if (_tags == null) _tags = new Dictionary();
141 |
142 | _tags[key] = value;
143 | return this;
144 | }
145 |
146 | ///
147 | public ISpan Start()
148 | {
149 | var activeSpanContext = _tracer.ActiveSpan?.Context;
150 | if (!_references.Any() && !_ignoreActiveSpan && activeSpanContext != null)
151 | AddReference(References.ChildOf, activeSpanContext);
152 | return new Span(_tracer, _operationName, _startTimestamp, _tags, _references);
153 | }
154 |
155 | private ISpanBuilder FollowsFrom(ISpanContext spanContext)
156 | {
157 | return AddReference(References.FollowsFrom, spanContext);
158 | }
159 |
160 | ///
161 | /// Indicates that this span follows from a prior span.
162 | ///
163 | /// An
164 | ///
165 | public ISpanBuilder FollowsFrom(ISpan span)
166 | {
167 | return FollowsFrom(span?.Context);
168 | }
169 | }
170 | }
--------------------------------------------------------------------------------
/src/LightStep/SpanContext.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using OpenTracing;
3 |
4 | namespace LightStep
5 | {
6 | ///
7 | public class SpanContext : ISpanContext
8 | {
9 | private readonly Baggage _baggage = new Baggage();
10 |
11 | ///
12 | public SpanContext(ulong traceId, ulong spanId, Baggage baggage = null, ulong parentId = 0L, string originalTraceId = null)
13 | {
14 | TraceIdValue = traceId;
15 | OriginalTraceId = originalTraceId ?? TraceId;
16 | SpanIdValue = spanId;
17 | ParentSpanIdValue = parentId;
18 | _baggage.Merge(baggage);
19 | }
20 |
21 | ///
22 | /// The parent span ID, if any.
23 | ///
24 | public ulong ParentSpanIdValue { get; }
25 |
26 | public string ParentSpanId => ParentSpanIdValue.ToString("x");
27 |
28 | ///
29 | /// The trace ID represetned as a ulong (UInt64).
30 | ///
31 | public ulong TraceIdValue { get; }
32 |
33 | ///
34 | public string TraceId => TraceIdValue.ToString("x");
35 |
36 | ///
37 | /// The original trace ID used to create this context.
38 | /// This may be a 64 or 128 bit hex value.
39 | ///
40 | public string OriginalTraceId { get; }
41 |
42 | ///
43 | /// The Span ID represented as a ulong (UInt64).
44 | ///
45 | public ulong SpanIdValue { get; }
46 |
47 | ///
48 | public string SpanId => SpanIdValue.ToString("x");
49 |
50 | ///
51 | public IEnumerable> GetBaggageItems()
52 | {
53 | return _baggage.GetAll();
54 | }
55 |
56 | ///
57 | /// Get a single item from the baggage.
58 | ///
59 | ///
60 | ///
61 | public string GetBaggageItem(string key)
62 | {
63 | return _baggage.Get(key);
64 | }
65 |
66 | ///
67 | /// Set an item on the baggage.
68 | ///
69 | ///
70 | ///
71 | ///
72 | public ISpanContext SetBaggageItem(string key, string value)
73 | {
74 | _baggage.Set(key, value);
75 | return this;
76 | }
77 |
78 | public override string ToString()
79 | {
80 | return $"[traceId: {TraceId}, spanId: {SpanId}, parentId: {ParentSpanId ?? "none"}";
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/src/LightStep/SpanData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace LightStep
5 | {
6 | ///
7 | /// An internal representation of a span.
8 | ///
9 | public class SpanData
10 | {
11 | ///
12 | /// The Span Context
13 | ///
14 | public SpanContext Context { get; internal set; }
15 |
16 | ///
17 | /// The span operation
18 | ///
19 | public string OperationName { get; internal set; }
20 |
21 | ///
22 | /// The start of the span.
23 | ///
24 | public DateTimeOffset StartTimestamp { get; internal set; }
25 |
26 | ///
27 | /// How long the span ran.
28 | ///
29 | public TimeSpan Duration { get; internal set; }
30 |
31 | ///
32 | /// Tags for the span.
33 | ///
34 | public IDictionary Tags { get; internal set; }
35 |
36 | ///
37 | /// Logs emitted as part of the span.
38 | ///
39 | public IList LogData { get; internal set; }
40 |
41 | public override string ToString()
42 | {
43 | return
44 | $"[{OperationName}] ctx: {Context}, startTime: {StartTimestamp}, duration: {Duration.TotalMilliseconds}";
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/LightStep/Tracer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using LightStep.Collector;
8 | using LightStep.Propagation;
9 | using OpenTracing;
10 | using OpenTracing.Propagation;
11 | using OpenTracing.Util;
12 | using LightStep.Logging;
13 |
14 | namespace LightStep
15 | {
16 | ///
17 | public sealed class Tracer : ITracer, IDisposable
18 | {
19 | private readonly object _lock = new object();
20 | internal readonly Options _options;
21 | private readonly IPropagator _propagator;
22 | private readonly ILightStepHttpClient _httpClient;
23 | private readonly IReportTranslator _translator;
24 | private ISpanRecorder _spanRecorder;
25 | private readonly Timer _reportLoop;
26 | private static readonly ILog _logger = LogProvider.GetCurrentClassLogger();
27 | private int currentDroppedSpanCount;
28 | private bool _firstReportHasRun;
29 |
30 | ///
31 | public Tracer(Options options) : this(new AsyncLocalScopeManager(), Propagators.TextMap, options,
32 | new LightStepSpanRecorder(), null)
33 | {
34 | }
35 |
36 | ///
37 | public Tracer(Options options, ISpanRecorder spanRecorder) : this(new AsyncLocalScopeManager(),
38 | Propagators.TextMap, options, spanRecorder, null)
39 | {
40 | }
41 |
42 | ///
43 | public Tracer(Options options, IScopeManager scopeManager) : this(scopeManager, Propagators.TextMap, options,
44 | new LightStepSpanRecorder(), null)
45 | {
46 | }
47 |
48 | ///
49 | public Tracer(Options options, ISpanRecorder spanRecorder, IPropagator propagator) : this(
50 | new AsyncLocalScopeManager(), propagator, options, spanRecorder, null)
51 | {
52 | }
53 | ///
54 | public Tracer(Options options, ISpanRecorder spanRecorder, ILightStepHttpClient client) : this(
55 | new AsyncLocalScopeManager(), Propagators.TextMap, options, spanRecorder, client)
56 | {
57 | }
58 | ///
59 | public Tracer(Options options, IPropagator propagator, ILightStepHttpClient client) : this(
60 | new AsyncLocalScopeManager(), propagator, options, new LightStepSpanRecorder(), client)
61 | {
62 | }
63 | ///
64 | public Tracer(Options options, ISpanRecorder spanRecorder, IReportTranslator translator,
65 | ILightStepHttpClient client) : this(
66 | new AsyncLocalScopeManager(), Propagators.TextMap, options, spanRecorder, client, translator)
67 | {
68 | }
69 | ///
70 | private Tracer(IScopeManager scopeManager, IPropagator propagator, Options options, ISpanRecorder spanRecorder, ILightStepHttpClient client, IReportTranslator translator = null)
71 | {
72 | ScopeManager = scopeManager;
73 | _spanRecorder = spanRecorder;
74 | _propagator = propagator;
75 | _options = options;
76 | _logger.Debug(
77 | $"Creating new tracer with GUID {_options.TracerGuid}. Project Access Token: {_options.AccessToken}, Report Period: {_options.ReportPeriod}, Report Timeout: {_options.ReportTimeout}.");
78 | var protocol = _options.Satellite.UsePlaintext ? "http" : "https";
79 | var url =
80 | $"{protocol}://{_options.Satellite.SatelliteHost}:{_options.Satellite.SatellitePort}/{LightStepConstants.SatelliteReportPath}";
81 | _httpClient = client ?? new LightStepHttpClient(url, _options);
82 | _translator = translator ?? new ReportTranslator(_options);
83 | _logger.Debug($"Tracer is reporting to {url}.");
84 | _reportLoop = new Timer(async e => await Flush().ConfigureAwait(false), null, TimeSpan.Zero, _options.ReportPeriod);
85 | _firstReportHasRun = false;
86 | }
87 |
88 | ///
89 | public IScopeManager ScopeManager { get; }
90 |
91 | ///
92 | public ISpan ActiveSpan => ScopeManager?.Active?.Span;
93 |
94 | ///
95 | public ISpanBuilder BuildSpan(string operationName)
96 | {
97 | return new SpanBuilder(this, operationName);
98 | }
99 |
100 | ///
101 | public void Inject(ISpanContext spanContext, IFormat format, TCarrier carrier)
102 | {
103 | _propagator.Inject((SpanContext) spanContext, format, carrier);
104 | if (_options.EnableMetaEventLogging) {
105 | this.BuildSpan(LightStepConstants.MetaEvent.InjectOperation)
106 | .IgnoreActiveSpan()
107 | .WithTag(LightStepConstants.MetaEvent.MetaEventKey, true)
108 | .WithTag(LightStepConstants.MetaEvent.SpanIdKey, spanContext.SpanId)
109 | .WithTag(LightStepConstants.MetaEvent.TraceIdKey, spanContext.TraceId)
110 | .WithTag(LightStepConstants.MetaEvent.PropagationFormatKey, format.GetType().ToString())
111 | .Start()
112 | .Finish();
113 | }
114 | }
115 |
116 | ///
117 | public ISpanContext Extract(IFormat format, TCarrier carrier)
118 | {
119 | var ctx = _propagator.Extract(format, carrier);
120 | if (_options.EnableMetaEventLogging) {
121 | this.BuildSpan(LightStepConstants.MetaEvent.ExtractOperation)
122 | .IgnoreActiveSpan()
123 | .WithTag(LightStepConstants.MetaEvent.MetaEventKey, true)
124 | .WithTag(LightStepConstants.MetaEvent.SpanIdKey, ctx.SpanId)
125 | .WithTag(LightStepConstants.MetaEvent.TraceIdKey, ctx.TraceId)
126 | .WithTag(LightStepConstants.MetaEvent.PropagationFormatKey, format.GetType().ToString())
127 | .Start()
128 | .Finish();
129 | }
130 | return ctx;
131 | }
132 |
133 | ///
134 | /// Transmits the current contents of the span buffer to the LightStep Satellite.
135 | /// Note that this creates a copy of the current spans and clears the span buffer!
136 | ///
137 | public async Task Flush()
138 | {
139 | if (_options.Run)
140 | {
141 | if (_options.EnableMetaEventLogging && _firstReportHasRun == false)
142 | {
143 | BuildSpan(LightStepConstants.MetaEvent.TracerCreateOperation)
144 | .IgnoreActiveSpan()
145 | .WithTag(LightStepConstants.MetaEvent.MetaEventKey, true)
146 | .WithTag(LightStepConstants.MetaEvent.TracerGuidKey, _options.TracerGuid)
147 | .Start()
148 | .Finish();
149 | _firstReportHasRun = true;
150 | }
151 | // save current spans and clear the buffer
152 | ISpanRecorder currentBuffer;
153 | lock (_lock)
154 | {
155 | currentBuffer = _spanRecorder.GetSpanBuffer();
156 | _spanRecorder = new LightStepSpanRecorder();
157 | _logger.Trace($"{currentBuffer.GetSpanCount()} spans in buffer.");
158 | }
159 |
160 | /**
161 | * there are two ways spans can be dropped:
162 | * 1. the buffer drops a span because it's too large, malformed, etc.
163 | * 2. the report failed to be sent to the satellite.
164 | * since flush is async and there can potentially be any number of buffers in flight to the satellite,
165 | * we need to set the current drop count on the tracer to be the amount of dropped spans from the buffer
166 | * plus the existing dropped spans, then mutate the current buffer to this new total value.
167 | */
168 | currentDroppedSpanCount += currentBuffer.DroppedSpanCount;
169 | currentBuffer.DroppedSpanCount = currentDroppedSpanCount;
170 |
171 | try
172 | {
173 | // since translate can throw exceptions, place it in the try and drop spans as appropriate
174 | var data = _translator.Translate(currentBuffer);
175 | var resp = await _httpClient.SendReport(data).ConfigureAwait(false);
176 |
177 | if (resp.Errors.Count > 0)
178 | {
179 | _logger.Warn($"Errors in report: {resp.Errors}");
180 | }
181 | // if the satellite is in developer mode, set the tracer to development mode as well
182 | // don't re-enable if it's already enabled though
183 | // TODO: iterate through all commands to find devmode flag
184 | if (resp.Commands.Count > 0 && resp.Commands[0].DevMode && _options.EnableMetaEventLogging == false)
185 | {
186 | _logger.Info("Enabling meta event logging");
187 | _options.EnableMetaEventLogging = true;
188 | }
189 |
190 | lock (_lock)
191 | {
192 | _logger.Trace($"Resetting tracer dropped span count as the last report was successful.");
193 | currentDroppedSpanCount = 0;
194 | }
195 |
196 | }
197 | catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException || ex is OperationCanceledException || ex is Exception)
198 | {
199 | lock (_lock)
200 | {
201 | _logger.Warn($"Adding {currentBuffer.GetSpanCount()} spans to dropped span count (current total: {currentDroppedSpanCount})");
202 | currentDroppedSpanCount += currentBuffer.GetSpanCount();
203 | if (this._options.ExceptionHandlerRegistered)
204 | {
205 | this._options.ExceptionHandler.Invoke(ex);
206 | }
207 | }
208 | }
209 |
210 |
211 | }
212 | }
213 |
214 | internal void AppendFinishedSpan(SpanData span)
215 | {
216 | lock (_lock)
217 | {
218 | if (_spanRecorder.GetSpanCount() < _options.ReportMaxSpans )
219 | {
220 | _spanRecorder.RecordSpan(span);
221 | }
222 | else
223 | {
224 | _spanRecorder.RecordDroppedSpans(1);
225 | _logger.Warn($"Dropping span due to too many spans in buffer.");
226 | }
227 | }
228 | }
229 |
230 | public void Dispose()
231 | {
232 | _reportLoop?.Dispose();
233 | }
234 | }
235 | }
--------------------------------------------------------------------------------
/src/LightStep/TransportOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LightStep
4 | {
5 | ///
6 | /// Options for the transport used to send spans to LightStep.
7 | ///
8 | [Flags]
9 | public enum TransportOptions
10 | {
11 | ///
12 | /// Binary protobuf encoding over HTTP
13 | ///
14 | BinaryProto,
15 | ///
16 | /// JSON protobuf encoding over HTTP
17 | ///
18 | JsonProto
19 | }
20 | }
--------------------------------------------------------------------------------
/src/LightStep/TypedContextExtensions.cs:
--------------------------------------------------------------------------------
1 | using OpenTracing;
2 |
3 | namespace LightStep
4 | {
5 | ///
6 | /// Extensions methods for retrieving a span context.
7 | ///
8 | public static class TypedContextExtensions
9 | {
10 | ///
11 | /// Get a strongly typed from a
12 | ///
13 | ///
14 | ///
15 | public static SpanContext TypedContext(this ISpan span)
16 | {
17 | return (SpanContext) span?.Context;
18 | }
19 |
20 | ///
21 | /// Get a strongly typed from an
22 | ///
23 | ///
24 | ///
25 | public static SpanContext TypedContext(this ISpanContext context)
26 | {
27 | return (SpanContext) context;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/LightStep/Utilities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LightStep
4 | {
5 | ///
6 | /// Utilities and other helpers.
7 | ///
8 | public static class Utilities
9 | {
10 | ///
11 | /// Get a random uint64.
12 | ///
13 | ///
14 | ///
15 | public static ulong NextUInt64(this Random rand)
16 | {
17 | var buffer = new byte[sizeof(ulong)];
18 | rand.NextBytes(buffer);
19 | return BitConverter.ToUInt64(buffer, 0);
20 | }
21 |
22 | public static bool IsNotMetaSpan(Span span)
23 | {
24 | return !span.Tags.ContainsKey(LightStepConstants.MetaEvent.MetaEventKey);
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/LightStep/collector.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package LightStep.Collector;
4 |
5 | option go_package = "collectorpb";
6 | option objc_class_prefix = "LSPB";
7 | option java_multiple_files = true;
8 | option java_package = "com.lightstep.tracer.grpc";
9 |
10 | import "google/protobuf/timestamp.proto";
11 | import "google/api/annotations.proto";
12 |
13 | message SpanContext {
14 | uint64 trace_id = 1;
15 | uint64 span_id = 2;
16 | map baggage = 3;
17 | }
18 |
19 | // Represent both tags and log fields.
20 | message KeyValue {
21 | string key = 1;
22 | oneof value {
23 | // Holds arbitrary string data; well-formed JSON strings should go in
24 | // json_value.
25 | string string_value = 2;
26 | int64 int_value = 3;
27 | double double_value = 4;
28 | bool bool_value = 5;
29 | // Must be a well-formed JSON value. Truncated JSON should go in
30 | // string_value. Should not be used for tags.
31 | string json_value = 6;
32 | }
33 | }
34 |
35 | message Log {
36 | google.protobuf.Timestamp timestamp = 1;
37 | repeated KeyValue fields = 2;
38 | }
39 |
40 | message Reference {
41 | enum Relationship {
42 | CHILD_OF = 0;
43 | FOLLOWS_FROM = 1;
44 | }
45 | Relationship relationship = 1;
46 | SpanContext span_context = 2;
47 | }
48 |
49 | message Span {
50 | SpanContext span_context = 1;
51 | string operation_name = 2;
52 | repeated Reference references = 3;
53 | google.protobuf.Timestamp start_timestamp = 4;
54 | uint64 duration_micros = 5;
55 | repeated KeyValue tags = 6;
56 | repeated Log logs = 7;
57 | }
58 |
59 | message Reporter {
60 | uint64 reporter_id = 1;
61 | repeated KeyValue tags = 4;
62 | }
63 |
64 | message MetricsSample {
65 | string name = 1;
66 | oneof value {
67 | int64 int_value = 2;
68 | double double_value = 3;
69 | }
70 | }
71 |
72 | message InternalMetrics {
73 | google.protobuf.Timestamp start_timestamp = 1;
74 | uint64 duration_micros = 2;
75 | repeated Log logs = 3;
76 | repeated MetricsSample counts = 4;
77 | repeated MetricsSample gauges = 5;
78 | }
79 |
80 | message Auth {
81 | string access_token = 1;
82 | }
83 |
84 | message ReportRequest {
85 | Reporter reporter = 1;
86 | Auth auth = 2;
87 | repeated Span spans = 3;
88 | int64 timestamp_offset_micros = 5;
89 | InternalMetrics internal_metrics = 6;
90 | }
91 |
92 | message Command {
93 | bool disable = 1;
94 | bool dev_mode = 2;
95 | }
96 |
97 | message ReportResponse {
98 | repeated Command commands = 1;
99 | google.protobuf.Timestamp receive_timestamp = 2;
100 | google.protobuf.Timestamp transmit_timestamp = 3;
101 | repeated string errors = 4;
102 | repeated string warnings = 5;
103 | repeated string infos = 6;
104 | }
105 |
106 | service CollectorService {
107 | rpc Report(ReportRequest) returns (ReportResponse) {
108 | option (google.api.http) = {
109 | post: "/api/v2/reports"
110 | body: "*"
111 | additional_bindings {
112 | get: "/api/v2/reports"
113 | }
114 | };
115 | }
116 | }
--------------------------------------------------------------------------------
/src/LightStep/google/api/annotations.proto:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, Google 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 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | import "google/api/http.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
23 | option java_multiple_files = true;
24 | option java_outer_classname = "AnnotationsProto";
25 | option java_package = "com.google.api";
26 | option objc_class_prefix = "GAPI";
27 |
28 | extend google.protobuf.MethodOptions {
29 | // See `HttpRule`.
30 | HttpRule http = 72295728;
31 | }
--------------------------------------------------------------------------------
/src/LightStep/google/api/http.proto:
--------------------------------------------------------------------------------
1 | // Copyright 2016 Google 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 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | option cc_enable_arenas = true;
20 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
21 | option java_multiple_files = true;
22 | option java_outer_classname = "HttpProto";
23 | option java_package = "com.google.api";
24 | option objc_class_prefix = "GAPI";
25 |
26 |
27 | // Defines the HTTP configuration for a service. It contains a list of
28 | // [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
29 | // to one or more HTTP REST API methods.
30 | message Http {
31 | // A list of HTTP configuration rules that apply to individual API methods.
32 | //
33 | // **NOTE:** All service configuration rules follow "last one wins" order.
34 | repeated HttpRule rules = 1;
35 | }
36 |
37 | // `HttpRule` defines the mapping of an RPC method to one or more HTTP
38 | // REST APIs. The mapping determines what portions of the request
39 | // message are populated from the path, query parameters, or body of
40 | // the HTTP request. The mapping is typically specified as an
41 | // `google.api.http` annotation, see "google/api/annotations.proto"
42 | // for details.
43 | //
44 | // The mapping consists of a field specifying the path template and
45 | // method kind. The path template can refer to fields in the request
46 | // message, as in the example below which describes a REST GET
47 | // operation on a resource collection of messages:
48 | //
49 | //
50 | // service Messaging {
51 | // rpc GetMessage(GetMessageRequest) returns (Message) {
52 | // option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}";
53 | // }
54 | // }
55 | // message GetMessageRequest {
56 | // message SubMessage {
57 | // string subfield = 1;
58 | // }
59 | // string message_id = 1; // mapped to the URL
60 | // SubMessage sub = 2; // `sub.subfield` is url-mapped
61 | // }
62 | // message Message {
63 | // string text = 1; // content of the resource
64 | // }
65 | //
66 | // The same http annotation can alternatively be expressed inside the
67 | // `GRPC API Configuration` YAML file.
68 | //
69 | // http:
70 | // rules:
71 | // - selector: .Messaging.GetMessage
72 | // get: /v1/messages/{message_id}/{sub.subfield}
73 | //
74 | // This definition enables an automatic, bidrectional mapping of HTTP
75 | // JSON to RPC. Example:
76 | //
77 | // HTTP | RPC
78 | // -----|-----
79 | // `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))`
80 | //
81 | // In general, not only fields but also field paths can be referenced
82 | // from a path pattern. Fields mapped to the path pattern cannot be
83 | // repeated and must have a primitive (non-message) type.
84 | //
85 | // Any fields in the request message which are not bound by the path
86 | // pattern automatically become (optional) HTTP query
87 | // parameters. Assume the following definition of the request message:
88 | //
89 | //
90 | // message GetMessageRequest {
91 | // message SubMessage {
92 | // string subfield = 1;
93 | // }
94 | // string message_id = 1; // mapped to the URL
95 | // int64 revision = 2; // becomes a parameter
96 | // SubMessage sub = 3; // `sub.subfield` becomes a parameter
97 | // }
98 | //
99 | //
100 | // This enables a HTTP JSON to RPC mapping as below:
101 | //
102 | // HTTP | RPC
103 | // -----|-----
104 | // `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))`
105 | //
106 | // Note that fields which are mapped to HTTP parameters must have a
107 | // primitive type or a repeated primitive type. Message types are not
108 | // allowed. In the case of a repeated type, the parameter can be
109 | // repeated in the URL, as in `...?param=A¶m=B`.
110 | //
111 | // For HTTP method kinds which allow a request body, the `body` field
112 | // specifies the mapping. Consider a REST update method on the
113 | // message resource collection:
114 | //
115 | //
116 | // service Messaging {
117 | // rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
118 | // option (google.api.http) = {
119 | // put: "/v1/messages/{message_id}"
120 | // body: "message"
121 | // };
122 | // }
123 | // }
124 | // message UpdateMessageRequest {
125 | // string message_id = 1; // mapped to the URL
126 | // Message message = 2; // mapped to the body
127 | // }
128 | //
129 | //
130 | // The following HTTP JSON to RPC mapping is enabled, where the
131 | // representation of the JSON in the request body is determined by
132 | // protos JSON encoding:
133 | //
134 | // HTTP | RPC
135 | // -----|-----
136 | // `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })`
137 | //
138 | // The special name `*` can be used in the body mapping to define that
139 | // every field not bound by the path template should be mapped to the
140 | // request body. This enables the following alternative definition of
141 | // the update method:
142 | //
143 | // service Messaging {
144 | // rpc UpdateMessage(Message) returns (Message) {
145 | // option (google.api.http) = {
146 | // put: "/v1/messages/{message_id}"
147 | // body: "*"
148 | // };
149 | // }
150 | // }
151 | // message Message {
152 | // string message_id = 1;
153 | // string text = 2;
154 | // }
155 | //
156 | //
157 | // The following HTTP JSON to RPC mapping is enabled:
158 | //
159 | // HTTP | RPC
160 | // -----|-----
161 | // `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")`
162 | //
163 | // Note that when using `*` in the body mapping, it is not possible to
164 | // have HTTP parameters, as all fields not bound by the path end in
165 | // the body. This makes this option more rarely used in practice of
166 | // defining REST APIs. The common usage of `*` is in custom methods
167 | // which don't use the URL at all for transferring data.
168 | //
169 | // It is possible to define multiple HTTP methods for one RPC by using
170 | // the `additional_bindings` option. Example:
171 | //
172 | // service Messaging {
173 | // rpc GetMessage(GetMessageRequest) returns (Message) {
174 | // option (google.api.http) = {
175 | // get: "/v1/messages/{message_id}"
176 | // additional_bindings {
177 | // get: "/v1/users/{user_id}/messages/{message_id}"
178 | // }
179 | // };
180 | // }
181 | // }
182 | // message GetMessageRequest {
183 | // string message_id = 1;
184 | // string user_id = 2;
185 | // }
186 | //
187 | //
188 | // This enables the following two alternative HTTP JSON to RPC
189 | // mappings:
190 | //
191 | // HTTP | RPC
192 | // -----|-----
193 | // `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
194 | // `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")`
195 | //
196 | // # Rules for HTTP mapping
197 | //
198 | // The rules for mapping HTTP path, query parameters, and body fields
199 | // to the request message are as follows:
200 | //
201 | // 1. The `body` field specifies either `*` or a field path, or is
202 | // omitted. If omitted, it assumes there is no HTTP body.
203 | // 2. Leaf fields (recursive expansion of nested messages in the
204 | // request) can be classified into three types:
205 | // (a) Matched in the URL template.
206 | // (b) Covered by body (if body is `*`, everything except (a) fields;
207 | // else everything under the body field)
208 | // (c) All other fields.
209 | // 3. URL query parameters found in the HTTP request are mapped to (c) fields.
210 | // 4. Any body sent with an HTTP request can contain only (b) fields.
211 | //
212 | // The syntax of the path template is as follows:
213 | //
214 | // Template = "/" Segments [ Verb ] ;
215 | // Segments = Segment { "/" Segment } ;
216 | // Segment = "*" | "**" | LITERAL | Variable ;
217 | // Variable = "{" FieldPath [ "=" Segments ] "}" ;
218 | // FieldPath = IDENT { "." IDENT } ;
219 | // Verb = ":" LITERAL ;
220 | //
221 | // The syntax `*` matches a single path segment. It follows the semantics of
222 | // [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
223 | // Expansion.
224 | //
225 | // The syntax `**` matches zero or more path segments. It follows the semantics
226 | // of [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.3 Reserved
227 | // Expansion. NOTE: it must be the last segment in the path except the Verb.
228 | //
229 | // The syntax `LITERAL` matches literal text in the URL path.
230 | //
231 | // The syntax `Variable` matches the entire path as specified by its template;
232 | // this nested template must not contain further variables. If a variable
233 | // matches a single path segment, its template may be omitted, e.g. `{var}`
234 | // is equivalent to `{var=*}`.
235 | //
236 | // NOTE: the field paths in variables and in the `body` must not refer to
237 | // repeated fields or map fields.
238 | //
239 | // Use CustomHttpPattern to specify any HTTP method that is not included in the
240 | // `pattern` field, such as HEAD, or "*" to leave the HTTP method unspecified for
241 | // a given URL path rule. The wild-card rule is useful for services that provide
242 | // content to Web (HTML) clients.
243 | message HttpRule {
244 | // Selects methods to which this rule applies.
245 | //
246 | // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
247 | string selector = 1;
248 |
249 | // Determines the URL pattern is matched by this rules. This pattern can be
250 | // used with any of the {get|put|post|delete|patch} methods. A custom method
251 | // can be defined using the 'custom' field.
252 | oneof pattern {
253 | // Used for listing and getting information about resources.
254 | string get = 2;
255 |
256 | // Used for updating a resource.
257 | string put = 3;
258 |
259 | // Used for creating a resource.
260 | string post = 4;
261 |
262 | // Used for deleting a resource.
263 | string delete = 5;
264 |
265 | // Used for updating a resource.
266 | string patch = 6;
267 |
268 | // Custom pattern is used for defining custom verbs.
269 | CustomHttpPattern custom = 8;
270 | }
271 |
272 | // The name of the request field whose value is mapped to the HTTP body, or
273 | // `*` for mapping all fields not captured by the path pattern to the HTTP
274 | // body. NOTE: the referred field must not be a repeated field and must be
275 | // present at the top-level of request message type.
276 | string body = 7;
277 |
278 | // Additional HTTP bindings for the selector. Nested bindings must
279 | // not contain an `additional_bindings` field themselves (that is,
280 | // the nesting may only be one level deep).
281 | repeated HttpRule additional_bindings = 11;
282 | }
283 |
284 | // A custom pattern is used for defining custom HTTP verb.
285 | message CustomHttpPattern {
286 | // The name of this custom HTTP verb.
287 | string kind = 1;
288 |
289 | // The path matched by this custom verb.
290 | string path = 2;
291 | }
--------------------------------------------------------------------------------
/src/LightStep/google/protobuf/timestamp.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option cc_enable_arenas = true;
37 | option go_package = "github.com/golang/protobuf/ptypes/timestamp";
38 | option java_package = "com.google.protobuf";
39 | option java_outer_classname = "TimestampProto";
40 | option java_multiple_files = true;
41 | option objc_class_prefix = "GPB";
42 |
43 | // A Timestamp represents a point in time independent of any time zone
44 | // or calendar, represented as seconds and fractions of seconds at
45 | // nanosecond resolution in UTC Epoch time. It is encoded using the
46 | // Proleptic Gregorian Calendar which extends the Gregorian calendar
47 | // backwards to year one. It is encoded assuming all minutes are 60
48 | // seconds long, i.e. leap seconds are "smeared" so that no leap second
49 | // table is needed for interpretation. Range is from
50 | // 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
51 | // By restricting to that range, we ensure that we can convert to
52 | // and from RFC 3339 date strings.
53 | // See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
54 | //
55 | // # Examples
56 | //
57 | // Example 1: Compute Timestamp from POSIX `time()`.
58 | //
59 | // Timestamp timestamp;
60 | // timestamp.set_seconds(time(NULL));
61 | // timestamp.set_nanos(0);
62 | //
63 | // Example 2: Compute Timestamp from POSIX `gettimeofday()`.
64 | //
65 | // struct timeval tv;
66 | // gettimeofday(&tv, NULL);
67 | //
68 | // Timestamp timestamp;
69 | // timestamp.set_seconds(tv.tv_sec);
70 | // timestamp.set_nanos(tv.tv_usec * 1000);
71 | //
72 | // Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
73 | //
74 | // FILETIME ft;
75 | // GetSystemTimeAsFileTime(&ft);
76 | // UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
77 | //
78 | // // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
79 | // // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
80 | // Timestamp timestamp;
81 | // timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
82 | // timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
83 | //
84 | // Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
85 | //
86 | // long millis = System.currentTimeMillis();
87 | //
88 | // Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
89 | // .setNanos((int) ((millis % 1000) * 1000000)).build();
90 | //
91 | //
92 | // Example 5: Compute Timestamp from current time in Python.
93 | //
94 | // timestamp = Timestamp()
95 | // timestamp.GetCurrentTime()
96 | //
97 | // # JSON Mapping
98 | //
99 | // In JSON format, the Timestamp type is encoded as a string in the
100 | // [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
101 | // format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
102 | // where {year} is always expressed using four digits while {month}, {day},
103 | // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
104 | // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
105 | // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
106 | // is required. A proto3 JSON serializer should always use UTC (as indicated by
107 | // "Z") when printing the Timestamp type and a proto3 JSON parser should be
108 | // able to accept both UTC and other timezones (as indicated by an offset).
109 | //
110 | // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
111 | // 01:30 UTC on January 15, 2017.
112 | //
113 | // In JavaScript, one can convert a Date object to this format using the
114 | // standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
115 | // method. In Python, a standard `datetime.datetime` object can be converted
116 | // to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
117 | // with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
118 | // can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
119 | // http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
120 | // ) to obtain a formatter capable of generating timestamps in this format.
121 | //
122 | //
123 | message Timestamp {
124 |
125 | // Represents seconds of UTC time since Unix epoch
126 | // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
127 | // 9999-12-31T23:59:59Z inclusive.
128 | int64 seconds = 1;
129 |
130 | // Non-negative fractions of a second at nanosecond resolution. Negative
131 | // second values with fractions must still have non-negative nanos values
132 | // that count forward in time. Must be from 0 to 999,999,999
133 | // inclusive.
134 | int32 nanos = 2;
135 | }
--------------------------------------------------------------------------------
/src/LightStep/lightstep.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package LightStep;
4 |
5 | option go_package = "lightsteppb";
6 |
7 | // The standard carrier for binary context propagation into LightStep.
8 | message BinaryCarrier {
9 | // "text_ctx" was deprecated following lightstep-tracer-cpp-0.36
10 | repeated bytes deprecated_text_ctx = 1;
11 |
12 | // The Opentracing "basictracer" proto.
13 | BasicTracerCarrier basic_ctx = 2;
14 | }
15 |
16 | // Copy of https://github.com/opentracing/basictracer-go/blob/master/wire/wire.proto
17 | message BasicTracerCarrier {
18 | fixed64 trace_id = 1;
19 | fixed64 span_id = 2;
20 | bool sampled = 3;
21 | map baggage_items = 4;
22 | }
--------------------------------------------------------------------------------
/test/LightStep.Tests/LightStep.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | netcoreapp2.1
5 |
6 |
7 |
8 | runtime; build; native; contentfiles; analyzers
9 | all
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | true
24 | false
25 | ../../tracerSign.snk
26 | <_UseRoslynPublicSignHack>false
27 |
28 |
--------------------------------------------------------------------------------
/test/LightStep.Tests/LightStepProtoTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using OpenTracing.Propagation;
5 | using Xunit;
6 | using LightStep.Collector;
7 |
8 | namespace LightStep.Tests
9 | {
10 | public class LightStepProtoTests
11 | {
12 | private Tracer GetTracer(ISpanRecorder recorder = null)
13 | {
14 | var spanRecorder = recorder ?? new SimpleMockRecorder();
15 | var satelliteOptions = new SatelliteOptions("localhost", 80, true);
16 | var tracerOptions = new Options("TEST").WithSatellite(satelliteOptions).WithAutomaticReporting(false);
17 | return new Tracer(tracerOptions, spanRecorder);
18 | }
19 |
20 | private LightStepHttpClient GetClient(TransportOptions t = TransportOptions.BinaryProto)
21 | {
22 | var satelliteOptions = new SatelliteOptions("localhost", 80, true);
23 | var tracerOptions = new Options("TEST").WithSatellite(satelliteOptions).WithAutomaticReporting(false).WithTransport(t);
24 | return new LightStepHttpClient("http://localhost:80", tracerOptions);
25 | }
26 |
27 | private ReportTranslator GetTranslator()
28 | {
29 | var satelliteOptions = new SatelliteOptions("localhost", 80, true);
30 | var tracerOptions = new Options("TEST").WithSatellite(satelliteOptions).WithAutomaticReporting(false);
31 | return new ReportTranslator(tracerOptions);
32 | }
33 |
34 | [Fact]
35 | public void ReportShouldBeJsonWithJsonOption()
36 | {
37 | var recorder = new SimpleMockRecorder();
38 | var translator = GetTranslator();
39 | var tracer = GetTracer(recorder);
40 | var span = tracer.BuildSpan("test").Start();
41 | span.Finish();
42 |
43 | var client = GetClient(TransportOptions.JsonProto);
44 | var translatedSpans = translator.Translate(recorder.GetSpanBuffer());
45 | var report = client.BuildRequest(translatedSpans);
46 | Assert.Equal("application/json", report.Content.Headers.ContentType.MediaType);
47 | var contentString = report.Content.ReadAsStringAsync().Result;
48 | Assert.Contains("test", contentString);
49 | }
50 |
51 | [Fact]
52 | public void ReportShouldBeBinaryWithoutJsonOption()
53 | {
54 | var recorder = new SimpleMockRecorder();
55 | var translator = GetTranslator();
56 | var tracer = GetTracer(recorder);
57 | var span = tracer.BuildSpan("test").Start();
58 | span.Finish();
59 |
60 | var client = GetClient();
61 | var translatedSpans = translator.Translate(recorder.GetSpanBuffer());
62 | var report = client.BuildRequest(translatedSpans);
63 | Assert.Equal("application/octet-stream", report.Content.Headers.ContentType.MediaType);
64 | }
65 |
66 | [Fact]
67 | public void InternalMetricsShouldExist()
68 | {
69 | var recorder = new SimpleMockRecorder();
70 | var translator = GetTranslator();
71 | var tracer = GetTracer(recorder);
72 | var span = tracer.BuildSpan("test").Start();
73 | span.Finish();
74 |
75 | var client = GetClient();
76 |
77 | var translatedSpans = translator.Translate(recorder.GetSpanBuffer());
78 | Assert.Equal("spans.dropped", translatedSpans.InternalMetrics.Counts[0].Name);
79 | }
80 |
81 | [Fact]
82 | public void DroppedSpanCountShouldSerializeCorrectly()
83 | {
84 | var mockBuffer = new SimpleMockRecorder();
85 | var translator = GetTranslator();
86 | mockBuffer.RecordDroppedSpans(1);
87 |
88 | var client = GetClient();
89 | var translatedBuffer = translator.Translate(mockBuffer.GetSpanBuffer());
90 |
91 | Assert.Equal(1, translatedBuffer.InternalMetrics.Counts[0].IntValue);
92 | }
93 |
94 | [Fact]
95 | public void DroppedSpanCountShouldIncrementOnBadSpan()
96 | {
97 | var recorder = new SimpleMockRecorder();
98 | var translator = GetTranslator();
99 | var badSpan = new SpanData {
100 | Duration = new TimeSpan(-1),
101 | OperationName = "badSpan"
102 | };
103 | recorder.RecordSpan(badSpan);
104 | var client = GetClient();
105 | var translatedBuffer = translator.Translate(recorder.GetSpanBuffer());
106 | Assert.Equal(1, translatedBuffer.InternalMetrics.Counts[0].IntValue);
107 | }
108 |
109 | [Fact]
110 | public void ConverterShouldConvertValues()
111 | {
112 | var recorder = new SimpleMockRecorder();
113 | var translator = GetTranslator();
114 | var tracer = GetTracer(recorder);
115 | var span = tracer.BuildSpan("testOperation")
116 | .WithTag("boolTrueTag", true)
117 | .WithTag("boolFalseTag", false)
118 | .WithTag("intTag", 0)
119 | .WithTag("stringTag", "test")
120 | .WithTag("doubleTag", 0.1)
121 | .WithTag("nullTag", null)
122 | .WithTag("jsonTag", @"{""key"":""value""}")
123 | .Start();
124 | span.Finish();
125 |
126 | var client = GetClient();
127 |
128 | var translatedSpans = translator.Translate(recorder.GetSpanBuffer());
129 | var translatedSpan = translatedSpans.Spans[0];
130 |
131 | foreach (var tag in translatedSpan.Tags)
132 | {
133 | switch (tag.Key)
134 | {
135 | case "boolTrueFlag":
136 | Assert.True(tag.BoolValue);
137 | break;
138 | case "boolFalseFlag":
139 | Assert.False(tag.BoolValue);
140 | break;
141 | case "intTag":
142 | Assert.Equal(0, tag.IntValue);
143 | break;
144 | case "stringTag":
145 | Assert.Equal("test", tag.StringValue);
146 | break;
147 | case "doubleTag":
148 | Assert.Equal(0.1, tag.DoubleValue);
149 | break;
150 | case "nullTag":
151 | Assert.Equal("null", tag.StringValue);
152 | break;
153 | case "jsonTag":
154 | Assert.Equal(@"{""key"":""value""}", tag.JsonValue);
155 | break;
156 | default:
157 | continue;
158 | }
159 | }
160 | }
161 |
162 | [Fact]
163 | public void SpanContextSpanIdShouldConvert()
164 | {
165 | var spanContext = new SpanContext(4659, 4660);
166 | var protoSpanContext = new LightStep.Collector.SpanContext().MakeSpanContextFromOtSpanContext(spanContext);
167 | Assert.Equal(4659ul, protoSpanContext.TraceId);
168 | Assert.Equal(4660ul, protoSpanContext.SpanId);
169 | }
170 | }
171 | }
--------------------------------------------------------------------------------
/test/LightStep.Tests/MockLogProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using LightStep.Logging;
4 | using LightStep.Logging.LogProviders;
5 |
6 | namespace LightStep.Tests
7 | {
8 | internal class MockLogProvider : ILogProvider
9 | {
10 | public Logger GetLogger(string name)
11 | {
12 | return Log;
13 | }
14 |
15 | public IDisposable OpenNestedContext(string message)
16 | {
17 | return new DisposableAction(() => { });
18 | }
19 |
20 | public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
21 | {
22 | return new DisposableAction(() => { });
23 | }
24 |
25 | private static readonly object _lock = new object();
26 |
27 | private static readonly List> Logs
28 | = new List>();
29 |
30 | private static bool Log(LogLevel logLevel, Func messageFunc, Exception exception,
31 | params object[] formatParameters)
32 | {
33 | string message = null;
34 | if (messageFunc != null)
35 | {
36 | message = messageFunc();
37 | if (formatParameters != null)
38 | {
39 | message =
40 | LogMessageFormatter.FormatStructuredMessage(message,
41 | formatParameters,
42 | out _);
43 | }
44 | }
45 |
46 | if (message != null || exception != null)
47 | {
48 | lock (_lock)
49 | {
50 | Logs.Add(Tuple.Create(logLevel, message, exception));
51 | }
52 | }
53 |
54 | return true;
55 | }
56 |
57 | public static Tuple[] PurgeAndFetchLogs()
58 | {
59 | lock (_lock)
60 | {
61 | var result = Logs.ToArray();
62 | Logs.Clear();
63 | return result;
64 | }
65 | }
66 |
67 | public class UseMockLogProviderScope : IDisposable
68 | {
69 | private readonly ILogProvider _oldLogProvider;
70 |
71 | public UseMockLogProviderScope()
72 | {
73 | _oldLogProvider = LogProvider.CurrentLogProvider;
74 | LogProvider.SetCurrentLogProvider(new MockLogProvider());
75 | }
76 |
77 | public void Dispose()
78 | {
79 | LogProvider.SetCurrentLogProvider(_oldLogProvider);
80 | }
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/test/LightStep.Tests/PropagatorTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using LightStep.Propagation;
6 | using OpenTracing.Propagation;
7 | using Xunit;
8 |
9 | namespace LightStep.Tests
10 | {
11 | public class PropagatorTests
12 | {
13 | [Fact]
14 | public void PropagatorStackInTracerShouldInjectAndExtract()
15 | {
16 | var ps = new PropagatorStack(BuiltinFormats.HttpHeaders);
17 | ps.AddPropagator(new B3Propagator());
18 | ps.AddPropagator(new HttpHeadersPropagator());
19 | ps.AddPropagator(new TextMapPropagator());
20 |
21 | var sr = new SimpleMockRecorder();
22 | var satOpts = new SatelliteOptions("localhost", 80, true);
23 | var tracerOpts = new Options("TEST").WithSatellite(satOpts).WithAutomaticReporting(false);
24 |
25 | var tracer = new Tracer(tracerOpts, sr, ps);
26 |
27 | var span = tracer.BuildSpan("propTest").Start();
28 |
29 | var traceId = span.TypedContext().TraceId;
30 | var spanId = span.TypedContext().SpanId;
31 |
32 | var data = new Dictionary();
33 |
34 | tracer.Inject(span.Context, BuiltinFormats.HttpHeaders, new TextMapInjectAdapter(data));
35 |
36 | Assert.Equal(traceId, data["ot-tracer-traceid"]);
37 | Assert.Equal(traceId, data["X-B3-TraceId"]);
38 | Assert.Equal(spanId, data["ot-tracer-spanid"]);
39 | Assert.Equal(spanId, data["X-B3-SpanId"]);
40 | Assert.Equal("true", data["ot-tracer-sampled"]);
41 | Assert.Equal("1", data["X-B3-Sampled"]);
42 |
43 | span.Finish();
44 |
45 | var ctx = tracer.Extract(BuiltinFormats.HttpHeaders, new TextMapExtractAdapter(data));
46 |
47 | Assert.Equal(ctx.SpanId, spanId);
48 | Assert.Equal(ctx.TraceId, traceId);
49 | }
50 |
51 | [Fact]
52 | public void PropagatorStackShouldAddPropagator()
53 | {
54 | var b3Propagator = new B3Propagator();
55 | var ps = new PropagatorStack(BuiltinFormats.HttpHeaders);
56 |
57 | Assert.Equal(ps.AddPropagator(Propagators.HttpHeadersPropagator), ps);
58 | Assert.Equal(ps.AddPropagator(b3Propagator), ps);
59 | Assert.Equal(2, ps.Propagators.Count);
60 | Assert.Equal(ps.Propagators[0], Propagators.HttpHeadersPropagator);
61 | Assert.Equal(ps.Propagators[1], b3Propagator);
62 | }
63 |
64 | [Fact]
65 | public void PropagatorStackShouldHandleCustomFormatConstructor()
66 | {
67 | var customFmt = new CustomFormatter();
68 | var ps = new PropagatorStack(customFmt);
69 | Assert.True(ps.Propagators.Count == 0);
70 | Assert.Equal(ps.Format, customFmt);
71 | }
72 |
73 | [Fact]
74 | public void PropagatorStackShouldHandleEmptyConstructor()
75 | {
76 | var ps = new PropagatorStack(BuiltinFormats.TextMap);
77 | Assert.True(ps.Propagators.Count == 0);
78 | Assert.Equal(ps.Format, BuiltinFormats.TextMap);
79 | }
80 |
81 | [Fact]
82 | public void PropagatorStackShouldInjectExtractAllPropagators()
83 | {
84 | var ps = new PropagatorStack(BuiltinFormats.TextMap);
85 | var httpPropagator = new HttpHeadersPropagator();
86 | var b3Propagator = new B3Propagator();
87 | var textPropagator = new TextMapPropagator();
88 |
89 | ps.AddPropagator(httpPropagator);
90 | ps.AddPropagator(b3Propagator);
91 | ps.AddPropagator(textPropagator);
92 |
93 | var carrier = new Dictionary();
94 | var context = new SpanContext(0, 0);
95 |
96 | ps.Inject(context, BuiltinFormats.TextMap, new TextMapInjectAdapter(carrier));
97 |
98 | var propagators = new List {httpPropagator, b3Propagator, textPropagator};
99 |
100 | foreach (var t in propagators)
101 | {
102 | var extractedContext =
103 | t.Extract(BuiltinFormats.TextMap, new TextMapExtractAdapter(carrier));
104 | Assert.NotNull(extractedContext);
105 | Assert.Equal(context.TraceId, extractedContext.TraceId);
106 | Assert.Equal(context.SpanId, extractedContext.SpanId);
107 | }
108 | }
109 |
110 | [Fact]
111 | public void EnvoyPropagatorShouldDecodeABinaryContextFromString()
112 | {
113 | UInt64 traceId = 4952807665017200957;
114 | UInt64 spanId = 14848807816610383171;
115 |
116 | var base64Context = "EhQJQwUbbwmQEc4RPaEuilTou0QYAQ==";
117 | var bs = Convert.FromBase64String(base64Context);
118 |
119 | var envoyPropagator = new EnvoyPropagator();
120 | var streamCarrier = new MemoryStream(bs);
121 |
122 | var extractedContext = envoyPropagator.Extract(BuiltinFormats.Binary, new BinaryExtractAdapter(streamCarrier));
123 | Assert.NotNull(extractedContext);
124 | Assert.Equal(traceId, extractedContext.SpanIdValue);
125 | Assert.Equal(spanId, extractedContext.TraceIdValue);
126 |
127 | var reStreamCarrier = new MemoryStream();
128 | envoyPropagator.Inject(extractedContext, BuiltinFormats.Binary, new BinaryInjectAdapter(reStreamCarrier));
129 | var reBase64String = Convert.ToBase64String(reStreamCarrier.ToArray());
130 | Assert.NotNull(reStreamCarrier);
131 | Assert.Equal(base64Context, reBase64String);
132 | }
133 |
134 | [Fact]
135 | public void EnvoyPropagatorShouldEncodeASpanContext()
136 | {
137 | var ctx = new SpanContext(1, 1);
138 | var envoyPropagator = new EnvoyPropagator();
139 | var carrierStream = new MemoryStream();
140 |
141 | envoyPropagator.Inject(ctx, BuiltinFormats.Binary, new BinaryInjectAdapter(carrierStream));
142 |
143 | Assert.NotNull(carrierStream);
144 | Assert.True(carrierStream.Length > 0);
145 |
146 | var extractedContext =
147 | envoyPropagator.Extract(BuiltinFormats.Binary, new BinaryExtractAdapter(carrierStream));
148 | Assert.NotNull(extractedContext);
149 | Assert.Equal("1", extractedContext.SpanId);
150 | Assert.Equal("1", extractedContext.TraceId);
151 | }
152 |
153 | [Fact]
154 | public void PropagatorStackShouldThrowOnNullConstructor()
155 | {
156 | Assert.Throws(() => new PropagatorStack(null));
157 | }
158 |
159 | [Fact]
160 | public void PropagatorStackShouldThrowOnNullPropagator()
161 | {
162 | var ps = new PropagatorStack(BuiltinFormats.TextMap);
163 | Assert.Throws(() => ps.AddPropagator(null));
164 | }
165 |
166 | public enum CasingType
167 | {
168 | LowerCase,
169 | UpperCase,
170 | MixedCase,
171 | }
172 |
173 | private string ToCase(string input, CasingType casingType)
174 | {
175 | switch (casingType)
176 | {
177 | case CasingType.LowerCase:
178 | return input.ToLower();
179 | case CasingType.UpperCase:
180 | return input.ToUpper();
181 | }
182 |
183 | return input;
184 | }
185 |
186 | [Theory]
187 | [InlineData(CasingType.LowerCase)]
188 | [InlineData(CasingType.UpperCase)]
189 | [InlineData(CasingType.MixedCase)]
190 | public void HttpHeaderPropagatorIsCaseInsensitive(CasingType casingType)
191 | {
192 | UInt64 spanId = 4952807665017200957;
193 | UInt64 traceId = 14848807816610383171;
194 |
195 | var headers = new Dictionary
196 | {
197 | {ToCase("Ot-Tracer-Spanid", casingType), spanId.ToString("X")},
198 | {ToCase("Ot-Tracer-Traceid", casingType), traceId.ToString("X")},
199 | };
200 |
201 | var httpPropagator = new HttpHeadersPropagator();
202 | var extractedContext =
203 | httpPropagator.Extract(BuiltinFormats.HttpHeaders,
204 | new TextMapExtractAdapter(headers));
205 |
206 | Assert.NotNull(extractedContext);
207 | Assert.Equal(spanId.ToString("x"), extractedContext.SpanId);
208 | Assert.Equal(traceId.ToString("x"), extractedContext.TraceId);
209 | }
210 |
211 | [Theory]
212 | [InlineData(CasingType.LowerCase)]
213 | [InlineData(CasingType.UpperCase)]
214 | [InlineData(CasingType.MixedCase)]
215 | public void TextHeaderPropagatorIsCaseSensitive(CasingType casingType)
216 | {
217 | UInt64 spanId = 4952807665017200957;
218 | UInt64 traceId = 14848807816610383171;
219 |
220 | var headers = new Dictionary
221 | {
222 | {ToCase("Ot-Tracer-Spanid", casingType), spanId.ToString("X")},
223 | {ToCase("Ot-Tracer-Traceid", casingType), traceId.ToString("X")},
224 | };
225 |
226 | var textPropagator = new TextMapPropagator();
227 | var extractedContext =
228 | textPropagator.Extract(BuiltinFormats.TextMap,
229 | new TextMapExtractAdapter(headers));
230 |
231 | if (casingType != CasingType.LowerCase)
232 | {
233 | Assert.Null(extractedContext);
234 | return;
235 | }
236 |
237 | Assert.NotNull(extractedContext);
238 | Assert.Equal(spanId.ToString("x"), extractedContext.SpanId);
239 | Assert.Equal(traceId.ToString("x"), extractedContext.TraceId);
240 | }
241 |
242 | [Fact]
243 | public void B3PropagatorShouldHandle128bitTraceIds()
244 | {
245 | var propagator = new B3Propagator();
246 | var traceId = "aef5705a090040838f1359ebafa5c0c6";
247 | var truncatedTraceId = "8f1359ebafa5c0c6";
248 | var spanId = "1";
249 |
250 | var headers = new Dictionary
251 | {
252 | {B3Propagator.TraceIdName, traceId},
253 | {B3Propagator.SpanIdName, spanId}
254 | };
255 | var extractedContext = propagator.Extract(BuiltinFormats.TextMap, new TextMapExtractAdapter(headers));
256 | Assert.Equal(traceId, extractedContext.OriginalTraceId);
257 | Assert.Equal(truncatedTraceId, extractedContext.TraceId);
258 | Assert.Equal(spanId, extractedContext.SpanId);
259 |
260 | headers.Clear();
261 | propagator.Inject(extractedContext, BuiltinFormats.TextMap, new TextMapInjectAdapter(headers));
262 | Assert.Equal(traceId, headers[B3Propagator.TraceIdName]);
263 | Assert.Equal(spanId, headers[B3Propagator.SpanIdName]);
264 | }
265 | }
266 |
267 | internal class CustomFormatter : IFormat
268 | {
269 | // dummy class for testing custom formatters
270 | }
271 | }
--------------------------------------------------------------------------------
/test/LightStep.Tests/SimpleMockRecorder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace LightStep.Tests
6 | {
7 | public class SimpleMockRecorder : ISpanRecorder
8 | {
9 | private List Spans { get; } = new List();
10 |
11 | public DateTime ReportStartTime { get; } = DateTime.Now;
12 | public DateTime ReportEndTime { get; set; }
13 | public int DroppedSpanCount { get; set; }
14 |
15 | public void RecordSpan(SpanData span)
16 | {
17 | Spans.Add(span);
18 | }
19 |
20 | public ISpanRecorder GetSpanBuffer()
21 | {
22 | ReportEndTime = DateTime.Now;
23 | return this;
24 | }
25 |
26 | public void ClearSpanBuffer()
27 | {
28 | Spans.Clear();
29 | }
30 |
31 | public void RecordDroppedSpans(int count)
32 | {
33 | DroppedSpanCount += count;
34 | }
35 |
36 | public IEnumerable GetSpans()
37 | {
38 | return Spans;
39 | }
40 |
41 | public int GetSpanCount()
42 | {
43 | return Spans.Count();
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/test/LightStep.Tests/SpanTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Concurrent;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using LightStep.Logging;
6 | using OpenTracing.Tag;
7 | using Xunit;
8 |
9 | namespace LightStep.Tests
10 | {
11 | public class SpanTests
12 | {
13 | private Tracer GetTracer(ISpanRecorder recorder = null)
14 | {
15 | var spanRecorder = recorder ?? new SimpleMockRecorder();
16 | var satelliteOptions = new SatelliteOptions("localhost", 80, true);
17 | var tracerOptions = new Options("TEST").WithSatellite(satelliteOptions).WithAutomaticReporting(false);
18 | return new Tracer(tracerOptions, spanRecorder);
19 | }
20 |
21 | [Fact]
22 | public void SpansShouldAllowThreadSafeAccess()
23 | {
24 | var recorder = new SimpleMockRecorder();
25 | var tracer = GetTracer(recorder);
26 | var span = tracer.BuildSpan("testOperation").Start();
27 |
28 | var t1 = Task.Run(() =>
29 | {
30 | span.Log("t1 run");
31 | span.SetTag("sameTag", "t1");
32 | span.SetTag("t1Tag", "t1");
33 | });
34 | var t2 = Task.Run(() =>
35 | {
36 | span.Log("t2 run");
37 | span.SetTag("sameTag", "t2");
38 | span.SetTag("t2Tag", "t2");
39 | });
40 |
41 | t1.Wait();
42 | t2.Wait();
43 |
44 | span.Finish();
45 |
46 | var finishedSpan = recorder.GetSpans().First();
47 |
48 | // we expect there to be 2 logs and 3 tags
49 | Assert.True(finishedSpan.LogData.Count == 2);
50 | Assert.True(finishedSpan.Tags.Count == 3);
51 | }
52 |
53 | [Fact]
54 | public void SpansShouldBuildProperStringKeyTags()
55 | {
56 | var recorder = new SimpleMockRecorder();
57 | var tracer = GetTracer(recorder);
58 | var span = tracer.BuildSpan("testOperation")
59 | .WithTag("boolTrueTag", true)
60 | .WithTag("boolFalseTag", false)
61 | .WithTag("intTag", 0)
62 | .WithTag("stringTag", "test")
63 | .WithTag("doubleTag", 0.1)
64 | .Start();
65 | span.Finish();
66 | var finishedSpan = recorder.GetSpans().First();
67 |
68 | Assert.True((bool) finishedSpan.Tags["boolTrueTag"]);
69 | Assert.False((bool) finishedSpan.Tags["boolFalseTag"]);
70 | Assert.Equal(0, finishedSpan.Tags["intTag"]);
71 | Assert.Equal("test", finishedSpan.Tags["stringTag"]);
72 | Assert.Equal(0.1, finishedSpan.Tags["doubleTag"]);
73 | }
74 |
75 | [Fact]
76 | public void SpansShouldBuildProperTypedKeyTags()
77 | {
78 | var recorder = new SimpleMockRecorder();
79 | var tracer = GetTracer(recorder);
80 | var span = tracer.BuildSpan("testOperation")
81 | .WithTag(new BooleanTag("testBoolTag"), true)
82 | .WithTag(new IntTag("testIntTag"), 1)
83 | .WithTag(new StringTag("testStringTag"), "test")
84 | .WithTag(new IntOrStringTag("testIntOrStringTagAsString"), "string")
85 | .WithTag(new IntOrStringTag("testIntOrStringTagAsInt"), 1)
86 | .Start();
87 | span.Finish();
88 | var finishedSpan = recorder.GetSpans().First();
89 |
90 | Assert.True((bool) finishedSpan.Tags["testBoolTag"]);
91 | Assert.Equal(1, finishedSpan.Tags["testIntTag"]);
92 | Assert.Equal("test", finishedSpan.Tags["testStringTag"]);
93 | Assert.Equal("string", finishedSpan.Tags["testIntOrStringTagAsString"]);
94 | Assert.Equal(1, finishedSpan.Tags["testIntOrStringTagAsInt"]);
95 | }
96 |
97 | [Fact]
98 | public void SpansShouldRecordLogs()
99 | {
100 | var recorder = new SimpleMockRecorder();
101 | var tracer = GetTracer(recorder);
102 | var span = tracer.BuildSpan("testOperation").Start();
103 | span.Log("hello world!");
104 | span.Finish();
105 |
106 | var finishedSpan = recorder.GetSpans().First();
107 | // project the sequence of logdata into an array with one item, the aforementioned log message
108 | var finishedSpanLogData = finishedSpan.LogData.Select(
109 | item => item.Fields.First(
110 | f => f.Value.Equals("hello world!")
111 | ).Value).ToArray()[0];
112 |
113 | Assert.Equal("hello world!", (string) finishedSpanLogData);
114 | }
115 |
116 | [Fact]
117 | public void SpanIdsShouldBeRandom()
118 | {
119 | // using this like a HashSet since HashSet is not thread-safe
120 | var spanIds = new ConcurrentDictionary();
121 | var iterations = 1_000_000;
122 | var collisionCount = 0;
123 | var tracer = GetTracer();
124 |
125 | // CLR black magic happens here to determine degree of parallelism
126 | Parallel.For(0, iterations, i =>
127 | {
128 | var span = tracer.BuildSpan("junkSpan").Start();
129 | span.Finish();
130 | if (!spanIds.TryAdd(span.Context.SpanId, new byte()))
131 | {
132 | collisionCount++;
133 | }
134 | });
135 |
136 | // if there were no collisions on insert, we had uniqueness!
137 | Assert.Equal(0, collisionCount);
138 | }
139 |
140 | [Theory]
141 | [InlineData(false)]
142 | [InlineData(true)]
143 | public void SpansShouldLogErrorForAfterFinishActions(bool afterFinish)
144 | {
145 | var tracer = GetTracer();
146 | var span = tracer.BuildSpan("randomSpan").Start();
147 |
148 | using (new MockLogProvider.UseMockLogProviderScope())
149 | {
150 | if (afterFinish) span.Finish();
151 |
152 | // These actions shouldn't cause exceptions either
153 | // before or after the span is finished
154 | span.SetOperationName("somethingElseRandom");
155 | span.SetTag("key", "value");
156 | span.Log("event");
157 | span.SetBaggageItem("baggageKey", "baggageValue");
158 |
159 | if (!afterFinish) span.Finish();
160 |
161 | // Just testing action taken after span is finished
162 | var logs =
163 | MockLogProvider.PurgeAndFetchLogs()
164 | .Where(l => l.Item1 == LogLevel.Error)
165 | .ToArray();
166 | if (!afterFinish)
167 | {
168 | Assert.Empty(logs);
169 | return;
170 | }
171 |
172 | Assert.Equal(4, logs.Length);
173 | Assert.Contains("finished span", logs[0].Item2);
174 | Assert.Contains("finished span", logs[1].Item2);
175 | Assert.Contains("finished span", logs[2].Item2);
176 | Assert.Contains("finished span", logs[3].Item2);
177 | }
178 | }
179 | }
180 | }
--------------------------------------------------------------------------------
/test/LightStep.Tests/TracerLogTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace LightStep.Tests
6 | {
7 | class TracerLogTest
8 | {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test/LightStep.TracerPerf.Tests/LightStep.TracerPerf.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | false
6 | LightStep.TracerPerf.Tests
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/test/LightStep.TracerPerf.Tests/README.md:
--------------------------------------------------------------------------------
1 | # tracer-tests
2 | For benchmarking tracers of distributed tracing that implements OpenTracing
3 |
4 | To collect data in terms of memory, network, and log usages, bring in tracer dependencies and run the NUnit tests.
5 |
--------------------------------------------------------------------------------
/test/LightStep.TracerPerf.Tests/TracerLogTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using Serilog;
3 |
4 | namespace LightStep.TracerPerf.Tests
5 | {
6 | public class TracerLogTest : TracerTestBase
7 | {
8 | [SetUp]
9 | public void SetUp()
10 | {
11 | Log.Logger = new LoggerConfiguration()
12 | .MinimumLevel.Verbose()
13 | .WriteTo.Console()
14 | .CreateLogger();
15 |
16 | Log.Information("Log is setup!");
17 | }
18 |
19 | [Test]
20 | public void TestExecute([Values("NoFinishNoDispose", "ExplicitFinish", "FinishOnDispose", "DisposeNoFinish")]
21 | string tracingMethod)
22 | {
23 | Execute(TracingMethods[tracingMethod]);
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/test/LightStep.TracerPerf.Tests/TracerMemoryTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using NUnit.Framework;
3 |
4 | namespace LightStep.TracerPerf.Tests
5 | {
6 | [SingleThreaded]
7 | public class TracerMemoryTest : TracerTestBase
8 | {
9 | [SetUp]
10 | public void SetUp()
11 | {
12 | Iter = 100;
13 | Chunk = 10000;
14 | }
15 |
16 | [Test]
17 | public void TestExecute_NoFinishNoDispose()
18 | {
19 | Console.WriteLine(nameof(NoFinishNoDispose));
20 | var heapInfo = Execute(NoFinishNoDispose);
21 | foreach (var num in heapInfo)
22 | {
23 | Console.WriteLine(num);
24 | }
25 | }
26 |
27 | [Test]
28 | public void TestExecute_ExplicitFinish()
29 | {
30 | Console.WriteLine(nameof(ExplicitFinish));
31 | var heapInfo = Execute(ExplicitFinish);
32 | foreach (var num in heapInfo)
33 | {
34 | Console.WriteLine(num);
35 | }
36 | }
37 |
38 | [Test]
39 | public void TestExecute_FinishOnDispose()
40 | {
41 | Console.WriteLine(nameof(FinishOnDispose));
42 | var heapInfo = Execute(FinishOnDispose);
43 | foreach (var num in heapInfo)
44 | {
45 | Console.WriteLine(num);
46 | }
47 | }
48 |
49 | [Test]
50 | public void TestExecute_DisposeNoFinish()
51 | {
52 | Console.WriteLine(nameof(DisposeNoFinish));
53 | var heapInfo = Execute(DisposeNoFinish);
54 | foreach (var num in heapInfo)
55 | {
56 | Console.WriteLine(num);
57 | }
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/test/LightStep.TracerPerf.Tests/TracerNetworkTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using NUnit.Framework;
4 |
5 | namespace LightStep.TracerPerf.Tests
6 | {
7 | [SingleThreaded]
8 | public class TracerNetworkTest : TracerTestBase
9 | {
10 | [SetUp]
11 | public void SetUp()
12 | {
13 | Iter = 10000;
14 | Chunk = 1000;
15 | BufferSize = 2000;
16 | ReportPeriod = .5;
17 | }
18 |
19 | [Test, Order(1)]
20 | public void TestExecute_Localhost()
21 | {
22 | Host = "localhost";
23 | Execute(ExplicitFinish);
24 | Thread.Sleep(TimeSpan.FromSeconds(20));
25 | }
26 |
27 | [Test, Order(2)]
28 | public void TestExecute_GarbageHost()
29 | {
30 | Host = "garbage";
31 | Execute(FinishOnDispose);
32 | Thread.Sleep(TimeSpan.FromSeconds(10));
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/test/LightStep.TracerPerf.Tests/TracerTestBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using LightStep;
5 | using OpenTracing;
6 |
7 | namespace LightStep.TracerPerf.Tests
8 | {
9 | public class TracerTestBase
10 | {
11 | protected string Host { get; set; } = "localhost" ;
12 | protected int Port { get; set; } = 8360;
13 | protected double ReportPeriod { get; set; } = .5;
14 | protected int BufferSize { get; set; } = 200;
15 | protected string Token { get; set; } = "developer";
16 | protected long Iter { get; set; } = 10;
17 | protected long Chunk { get; set; } = 10;
18 | protected Tracer Tracer;
19 |
20 | protected static readonly Action NoFinishNoDispose = t => t.BuildSpan("test").StartActive(false);
21 |
22 | protected static Action ExplicitFinish => t =>
23 | {
24 | t.BuildSpan("test").StartActive(true);
25 | t.ActiveSpan.Finish();
26 | };
27 |
28 | protected static readonly Action FinishOnDispose = t =>
29 | {
30 | var scope = t.BuildSpan("test").StartActive(true);
31 | scope.Dispose();
32 | };
33 |
34 | protected static readonly Action DisposeNoFinish = t =>
35 | {
36 | var scope = t.BuildSpan("test").StartActive(false);
37 | scope.Dispose();
38 | };
39 |
40 | protected readonly Dictionary> TracingMethods = new Dictionary>()
41 | {
42 | { "NoFinishNoDispose", NoFinishNoDispose },
43 | { "ExplicitFinish", ExplicitFinish },
44 | { "FinishOnDispose", FinishOnDispose },
45 | { "DisposeNoFinish", DisposeNoFinish },
46 | };
47 |
48 | private void Init()
49 | {
50 | var overrideTags = new Dictionary
51 | {
52 | {
53 | LightStepConstants.ComponentNameKey, "ServiceName"
54 | },
55 | };
56 | var satelliteOptions = new SatelliteOptions(Host, Port, true);
57 | Options options = new Options(Token)
58 | .WithSatellite(satelliteOptions)
59 | .WithTags(overrideTags)
60 | .WithMaxBufferedSpans(BufferSize)
61 | .WithReportPeriod(TimeSpan.FromSeconds(ReportPeriod));
62 | Tracer = new Tracer(options);
63 | }
64 |
65 | protected List Execute(Action buildSpan)
66 | {
67 | Init();
68 | var heapInfo = new List();
69 | for (var i = 0; i < Iter; i++)
70 | {
71 | for (var j = 0; j < Chunk; j++)
72 | {
73 | buildSpan(Tracer);
74 | }
75 | var gcMemoryInfo = GC.GetGCMemoryInfo();
76 | heapInfo.Add(gcMemoryInfo.HeapSizeBytes);
77 | Console.WriteLine(gcMemoryInfo.HeapSizeBytes);
78 | Tracer.Flush();
79 | }
80 |
81 | Tracer = null;
82 | var min = Enumerable.Min(heapInfo);
83 | return heapInfo.Select(e => e - min).ToList();
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/tracerSign.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lightstep/lightstep-tracer-csharp/59c3e63658fd69bf732ddc019e90607d9b014c11/tracerSign.snk
--------------------------------------------------------------------------------