├── .nuget
├── NuGet.exe
└── NuGet.Config
├── src
└── Medidata.ZipkinTracer.Core
│ ├── .nuget
│ ├── NuGet.exe
│ └── NuGet.Config
│ ├── Models
│ ├── AnnotationBase.cs
│ ├── Endpoint.cs
│ ├── References
│ │ ├── AnnotationType.cs
│ │ └── ZipkinConstants.cs
│ ├── BinaryAnnotation.cs
│ ├── Annotation.cs
│ ├── Span.cs
│ └── Serialization
│ │ └── Json
│ │ ├── JsonEndpoint.cs
│ │ ├── JsonAnnotation.cs
│ │ ├── JsonBinaryAnnotation.cs
│ │ └── JsonSpan.cs
│ ├── package-nuget.bat
│ ├── packages.config
│ ├── Helpers
│ ├── SyncHelper.cs
│ └── ParserHelper.cs
│ ├── Medidata.ZipkinTracer.Core.nuspec
│ ├── IZipkinConfig.cs
│ ├── ITracerClient.cs
│ ├── ITraceProvider.cs
│ ├── Middlewares
│ └── ZipkinMiddleware.cs
│ ├── SpanCollector.cs
│ ├── ZipkinTracerExtensions.cs
│ ├── Handlers
│ └── ZipkinMessageHandler.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── ServiceEndpoint.cs
│ ├── SpanProcessorTaskFactory.cs
│ ├── ZipkinConfig.cs
│ ├── SpanProcessor.cs
│ ├── TraceProvider.cs
│ ├── Medidata.ZipkinTracer.Core.csproj
│ ├── SpanTracer.cs
│ └── ZipkinClient.cs
├── .gitignore
├── tests
└── Medidata.ZipkinTracer.Core.Test
│ ├── app.config
│ ├── packages.config
│ ├── ZipkinEndpointTests.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── Models
│ └── Serialization
│ │ └── Json
│ │ └── JsonSpanTests.cs
│ ├── SpanProcesssorTaskFactoryTests.cs
│ ├── SpanCollectorTests.cs
│ ├── SpanProcessorTests.cs
│ ├── HttpModule
│ └── RequestContextModuleTests.cs
│ ├── ZipkinConfigTests.cs
│ ├── Helpers
│ └── ParserHelperTests.cs
│ ├── Medidata.ZipkinTracer.Core.Test.csproj
│ ├── TraceProviderTests.cs
│ ├── SpanTracerTests.cs
│ └── ZipkinClientTests.cs
├── LICENSE.txt
├── Medidata.ZipkinTracerModule.sln
└── README.md
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdsol/Medidata.ZipkinTracerModule/develop/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdsol/Medidata.ZipkinTracerModule/develop/src/Medidata.ZipkinTracer.Core/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | src/*/bin/*
2 | src/*/obj/*
3 | tests/*/bin/*
4 | tests/*/obj/*
5 | src/*/lib/*
6 | *.user
7 | *.suo
8 | *.pdb
9 |
10 | TestResults/
11 | packages/
12 | .vs/*/*
13 |
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/AnnotationBase.cs:
--------------------------------------------------------------------------------
1 | namespace Medidata.ZipkinTracer.Models
2 | {
3 | public abstract class AnnotationBase
4 | {
5 | public Endpoint Host { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/package-nuget.bat:
--------------------------------------------------------------------------------
1 | "C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" "Medidata.ZipkinTracer.Core.csproj" /p:Configuration=Release /t:Clean
2 | "..\..\.nuget\nuget.exe" pack Medidata.ZipkinTracer.Core.csproj -Build -MSBuildVersion 14 -Properties Configuration=Release
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/Endpoint.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 |
3 | namespace Medidata.ZipkinTracer.Models
4 | {
5 | public class Endpoint
6 | {
7 | public IPAddress IPAddress { get; set; }
8 |
9 | public ushort Port { get; set; }
10 |
11 | public string ServiceName { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/References/AnnotationType.cs:
--------------------------------------------------------------------------------
1 | namespace Medidata.ZipkinTracer.Models
2 | {
3 | public enum AnnotationType
4 | {
5 | Boolean = 0,
6 | ByteArray = 1,
7 | Int16 = 2,
8 | Int32 = 3,
9 | Int64 = 4,
10 | Double = 5,
11 | String = 6,
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/BinaryAnnotation.cs:
--------------------------------------------------------------------------------
1 | namespace Medidata.ZipkinTracer.Models
2 | {
3 | public class BinaryAnnotation: AnnotationBase
4 | {
5 | public string Key { get; set; }
6 |
7 | public object Value { get; set; }
8 |
9 | public AnnotationType AnnotationType => Value.GetType().AsAnnotationType();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/Annotation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Medidata.ZipkinTracer.Models
4 | {
5 | public class Annotation: AnnotationBase
6 | {
7 | public DateTimeOffset Timestamp { get; set; } = DateTimeOffset.UtcNow;
8 |
9 | public string Value { get; set; }
10 |
11 | public int DurationMilliseconds { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/References/ZipkinConstants.cs:
--------------------------------------------------------------------------------
1 | namespace Medidata.ZipkinTracer.Models
2 | {
3 | public static class ZipkinConstants
4 | {
5 | public static readonly string ClientSend = "cs";
6 | public static readonly string ClientReceive = "cr";
7 | public static readonly string ServerSend = "ss";
8 | public static readonly string ServerReceive = "sr";
9 | public static readonly string LocalComponent = "lc";
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/Span.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Medidata.ZipkinTracer.Models
5 | {
6 | public class Span
7 | {
8 | public string TraceId { get; set; }
9 |
10 | public string Name { get; set; }
11 |
12 | public string Id { get; set; }
13 |
14 | public string ParentId { get; set; }
15 |
16 | public IList Annotations { get; } = new List();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/Medidata.ZipkinTracer.Core.Test/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Helpers/SyncHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Medidata.ZipkinTracer.Core.Helpers
4 | {
5 | public static class SyncHelper
6 | {
7 | public static void ExecuteSafely(object sync, Func canExecute, Action actiontoExecuteSafely)
8 | {
9 | if (canExecute())
10 | {
11 | lock (sync)
12 | {
13 | if (canExecute())
14 | {
15 | actiontoExecuteSafely();
16 | }
17 | }
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/Medidata.ZipkinTracer.Core.Test/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Medidata.ZipkinTracer.Core.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $id$
5 | $version$
6 | $title$
7 | $author$
8 | $author$
9 | https://github.com/mdsol/Medidata.ZipkinTracerModule
10 | Medidata Zipkin Tracer Core Service
11 | Initial Beta Release of the ZipkinTracer Core Service for .Net Projects
12 | Copyright 2014
13 | ZipkinTracer Core
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/IZipkinConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Owin;
4 |
5 | namespace Medidata.ZipkinTracer.Core
6 | {
7 | public interface IZipkinConfig
8 | {
9 | Predicate Bypass { get; set; }
10 |
11 | Uri ZipkinBaseUri { get; set; }
12 |
13 | Func Domain { get; set; }
14 |
15 | uint SpanProcessorBatchSize { get; set; }
16 |
17 | IList ExcludedPathList { get; set; }
18 |
19 | double SampleRate { get; set; }
20 |
21 | IList NotToBeDisplayedDomainList { get; set; }
22 |
23 | bool Create128BitTraceId { get; set; }
24 |
25 | bool ShouldBeSampled(string sampled, string requestPath);
26 |
27 | void Validate();
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/Serialization/Json/JsonEndpoint.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 |
4 | namespace Medidata.ZipkinTracer.Models
5 | {
6 | internal class JsonEndpoint
7 | {
8 | private readonly Endpoint endpoint;
9 |
10 | [JsonProperty("ipv4")]
11 | public string IPv4 => endpoint.IPAddress.ToString();
12 |
13 | [JsonProperty("port")]
14 | public ushort Port => endpoint.Port;
15 |
16 | [JsonProperty("serviceName")]
17 | public string ServiceName => endpoint.ServiceName;
18 |
19 | public JsonEndpoint(Endpoint endpoint)
20 | {
21 | if (endpoint == null)
22 | throw new ArgumentNullException(nameof(endpoint));
23 |
24 | this.endpoint = endpoint;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/ITracerClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using Medidata.ZipkinTracer.Models;
4 |
5 | namespace Medidata.ZipkinTracer.Core
6 | {
7 | public interface ITracerClient
8 | {
9 | bool IsTraceOn { get; }
10 |
11 | ITraceProvider TraceProvider { get; }
12 |
13 | Span StartServerTrace(Uri requestUri, string methodName);
14 |
15 | Span StartClientTrace(Uri remoteUri, string methodName, ITraceProvider trace);
16 |
17 | void EndServerTrace(Span serverSpan);
18 |
19 | void EndClientTrace(Span clientSpan, int statusCode);
20 |
21 | void Record(Span span, [CallerMemberName] string value = null);
22 |
23 | void RecordBinary(Span span, string key, T value);
24 |
25 | void RecordLocalComponent(Span span, string value);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/Serialization/Json/JsonAnnotation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 |
4 | namespace Medidata.ZipkinTracer.Models
5 | {
6 | internal class JsonAnnotation
7 | {
8 | private readonly Annotation annotation;
9 |
10 | [JsonProperty("endpoint")]
11 | public JsonEndpoint Endpoint => new JsonEndpoint(annotation.Host);
12 |
13 | [JsonProperty("value")]
14 | public string Value => annotation.Value;
15 |
16 | [JsonProperty("timestamp")]
17 | public long Timestamp => annotation.Timestamp.ToUnixTimeMicroseconds();
18 |
19 | public JsonAnnotation(Annotation annotation)
20 | {
21 | if (annotation == null)
22 | throw new ArgumentNullException(nameof(annotation));
23 |
24 | this.annotation = annotation;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/Serialization/Json/JsonBinaryAnnotation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 |
4 | namespace Medidata.ZipkinTracer.Models
5 | {
6 | internal class JsonBinaryAnnotation
7 | {
8 | private readonly BinaryAnnotation binaryAnnotation;
9 |
10 | [JsonProperty("endpoint")]
11 | public JsonEndpoint Endpoint => new JsonEndpoint(binaryAnnotation.Host);
12 |
13 | [JsonProperty("key")]
14 | public string Key => binaryAnnotation.Key;
15 |
16 | [JsonProperty("value")]
17 | public string Value => binaryAnnotation.Value.ToString();
18 |
19 | public JsonBinaryAnnotation(BinaryAnnotation binaryAnnotation)
20 | {
21 | if (binaryAnnotation == null)
22 | throw new ArgumentNullException(nameof(binaryAnnotation));
23 |
24 | this.binaryAnnotation = binaryAnnotation;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/ITraceProvider.cs:
--------------------------------------------------------------------------------
1 | namespace Medidata.ZipkinTracer.Core
2 | {
3 | ///
4 | /// TraceProvider interface
5 | ///
6 | public interface ITraceProvider
7 | {
8 | ///
9 | /// Gets a TraceId
10 | ///
11 | string TraceId { get; }
12 |
13 | ///
14 | /// Gets a SpanId
15 | ///
16 | string SpanId { get; }
17 |
18 | ///
19 | /// Gets a ParentSpanId
20 | ///
21 | string ParentSpanId { get; }
22 |
23 | ///
24 | /// Gets IsSampled
25 | ///
26 | bool IsSampled { get; }
27 |
28 | ///
29 | /// Gets a Trace for outgoing HTTP request.
30 | ///
31 | /// The trace
32 | ITraceProvider GetNext();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Medidata Solutions Worldwide
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Middlewares/ZipkinMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.Owin;
3 | using Owin;
4 |
5 | namespace Medidata.ZipkinTracer.Core.Middlewares
6 | {
7 | public class ZipkinMiddleware : OwinMiddleware
8 | {
9 | private readonly IZipkinConfig _config;
10 |
11 | public ZipkinMiddleware(OwinMiddleware next, IZipkinConfig options) : base(next)
12 | {
13 | _config = options;
14 | }
15 |
16 | public override async Task Invoke(IOwinContext context)
17 | {
18 | if (_config.Bypass != null && _config.Bypass(context.Request))
19 | {
20 | await Next.Invoke(context);
21 | return;
22 | }
23 |
24 | var zipkin = new ZipkinClient(_config, context);
25 | var span = zipkin.StartServerTrace(context.Request.Uri, context.Request.Method);
26 | await Next.Invoke(context);
27 | zipkin.EndServerTrace(span);
28 | }
29 | }
30 |
31 | public static class AppBuilderExtensions
32 | {
33 | public static void UseZipkin(this IAppBuilder app, IZipkinConfig config)
34 | {
35 | config.Validate();
36 | app.Use(config);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Models/Serialization/Json/JsonSpan.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Newtonsoft.Json;
5 |
6 | namespace Medidata.ZipkinTracer.Models
7 | {
8 | internal class JsonSpan
9 | {
10 | private readonly Span span;
11 |
12 | [JsonProperty("traceId")]
13 | public string TraceId => span.TraceId;
14 |
15 | [JsonProperty("name")]
16 | public string Name => span.Name;
17 |
18 | [JsonProperty("id")]
19 | public string Id => span.Id;
20 |
21 | [JsonProperty("parentId", NullValueHandling = NullValueHandling.Ignore)]
22 | public string ParentId => string.IsNullOrWhiteSpace(span.ParentId) ? null : span.ParentId;
23 |
24 | [JsonProperty("annotations")]
25 | public IEnumerable Annotations =>
26 | span.GetAnnotationsByType().Select(annotation => new JsonAnnotation(annotation));
27 |
28 | [JsonProperty("binaryAnnotations")]
29 | public IEnumerable BinaryAnnotations =>
30 | span.GetAnnotationsByType().Select(annotation => new JsonBinaryAnnotation(annotation));
31 |
32 | public JsonSpan(Span span)
33 | {
34 | if (span == null)
35 | throw new ArgumentNullException(nameof(span));
36 |
37 | this.span = span;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/SpanCollector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using Medidata.ZipkinTracer.Models;
4 | using Medidata.ZipkinTracer.Core.Helpers;
5 |
6 | namespace Medidata.ZipkinTracer.Core
7 | {
8 | public class SpanCollector
9 | {
10 | private const int MAX_QUEUE_SIZE = 100;
11 | internal static BlockingCollection spanQueue;
12 |
13 | internal SpanProcessor spanProcessor;
14 |
15 | public bool IsStarted { get; private set; }
16 | private readonly object syncObj = new object();
17 |
18 | public SpanCollector(Uri uri, uint maxProcessorBatchSize)
19 | {
20 | if (spanQueue == null)
21 | {
22 | spanQueue = new BlockingCollection(MAX_QUEUE_SIZE);
23 | }
24 |
25 | spanProcessor = new SpanProcessor(uri, spanQueue, maxProcessorBatchSize);
26 | }
27 |
28 | public virtual void Collect(Span span)
29 | {
30 | spanQueue.Add(span);
31 | }
32 |
33 | public virtual void Start()
34 | {
35 | SyncHelper.ExecuteSafely(syncObj, () => !IsStarted, () => { spanProcessor.Start(); IsStarted = true; });
36 | }
37 |
38 | public virtual void Stop()
39 | {
40 | SyncHelper.ExecuteSafely(syncObj, () => IsStarted, () => { spanProcessor.Stop() ; IsStarted = false; });
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Helpers/ParserHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 |
4 | namespace Medidata.ZipkinTracer.Core.Helpers
5 | {
6 | internal static class ParserHelper
7 | {
8 | ///
9 | /// Checks if string value can be converted to 128bit (Guid) or 64bit (long)
10 | ///
11 | ///
12 | ///
13 | internal static bool IsParsableTo128Or64Bit(this string value)
14 | {
15 | if (value.IsParsableToGuid()) return true;
16 | return value.IsParsableToLong();
17 | }
18 |
19 | ///
20 | /// Checks if hex string value is parsable to Guid
21 | ///
22 | ///
23 | ///
24 | internal static bool IsParsableToGuid(this string value)
25 | {
26 | Guid result;
27 | return Guid.TryParseExact(value, "N", out result);
28 | }
29 |
30 | ///
31 | /// Checks if hex string value is parsable to long
32 | ///
33 | ///
34 | ///
35 | internal static bool IsParsableToLong(this string value)
36 | {
37 | long result;
38 | return !string.IsNullOrWhiteSpace(value) && long.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out result);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/Medidata.ZipkinTracer.Core.Test/ZipkinEndpointTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using Ploeh.AutoFixture;
4 |
5 | namespace Medidata.ZipkinTracer.Core.Test
6 | {
7 | [TestClass]
8 | public class ZipkinEndpointTests
9 | {
10 | private IFixture fixture;
11 |
12 | [TestInitialize]
13 | public void Init()
14 | {
15 | fixture = new Fixture();
16 | }
17 |
18 | [TestMethod]
19 | public void GetLocalEndpoint()
20 | {
21 | var serviceName = fixture.Create();
22 | var port = fixture.Create();
23 |
24 | var zipkinEndpoint = new ServiceEndpoint();
25 | var endpoint = zipkinEndpoint.GetLocalEndpoint(serviceName, port);
26 |
27 | Assert.IsNotNull(endpoint);
28 | Assert.AreEqual(serviceName, endpoint.ServiceName);
29 | Assert.IsNotNull(endpoint.IPAddress);
30 | Assert.IsNotNull(endpoint.Port);
31 | }
32 |
33 | [TestMethod]
34 | public void GetRemoteEndpoint()
35 | {
36 | var remoteUri = new Uri("http://localhost");
37 | var serviceName = fixture.Create();
38 |
39 | var zipkinEndpoint = new ServiceEndpoint();
40 | var endpoint = zipkinEndpoint.GetRemoteEndpoint(remoteUri, serviceName);
41 |
42 | Assert.IsNotNull(endpoint);
43 | Assert.AreEqual(serviceName, endpoint.ServiceName);
44 | Assert.IsNotNull(endpoint.IPAddress);
45 | Assert.IsNotNull(endpoint.Port);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/Medidata.ZipkinTracer.Core.Test/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("Medidata.ZipkinTracer.Core.Test")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Medidata Solutions Inc")]
12 | [assembly: AssemblyProduct("Medidata.ZipkinTracer.Core.Test")]
13 | [assembly: AssemblyCopyright("Copyright © Medidata Solutions Inc 2014")]
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("78961bfd-538b-4a56-8d2d-7cd6b7e6e65d")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/ZipkinTracerExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Medidata.ZipkinTracer.Models;
7 |
8 | namespace Medidata.ZipkinTracer
9 | {
10 | public static class ZipkinTracerExtensions
11 | {
12 | private static readonly Dictionary annotationTypeMappings =
13 | new Dictionary()
14 | {
15 | { typeof(bool), AnnotationType.Boolean },
16 | { typeof(byte[]), AnnotationType.ByteArray },
17 | { typeof(short), AnnotationType.Int16 },
18 | { typeof(int), AnnotationType.Int32 },
19 | { typeof(long), AnnotationType.Int64 },
20 | { typeof(double), AnnotationType.Double },
21 | { typeof(string), AnnotationType.String }
22 | };
23 |
24 | public static AnnotationType AsAnnotationType(this Type type)
25 | {
26 | return annotationTypeMappings.ContainsKey(type) ? annotationTypeMappings[type] : AnnotationType.String;
27 | }
28 |
29 | public static IEnumerable GetAnnotationsByType(this Span span)
30 | where TAnnotation: AnnotationBase
31 | {
32 | return span.Annotations.OfType();
33 | }
34 |
35 | public static long ToUnixTimeMicroseconds(this DateTimeOffset value)
36 | {
37 | return Convert.ToInt64(
38 | (value - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalMilliseconds * 1000
39 | );
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/Handlers/ZipkinMessageHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace Medidata.ZipkinTracer.Core.Handlers
6 | {
7 | public class ZipkinMessageHandler : DelegatingHandler
8 | {
9 | private readonly ITracerClient _client;
10 |
11 | public ZipkinMessageHandler(ITracerClient client)
12 | : this(client, new HttpClientHandler())
13 | {
14 | }
15 |
16 | public ZipkinMessageHandler(ITracerClient client, HttpMessageHandler innerHandler)
17 | : base(innerHandler)
18 | {
19 | _client = client;
20 | }
21 |
22 | protected override async Task SendAsync(
23 | HttpRequestMessage request, CancellationToken cancellationToken)
24 | {
25 | if (!_client.IsTraceOn)
26 | {
27 | return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
28 | }
29 |
30 | var nextTrace = _client.TraceProvider.GetNext();
31 |
32 | request.Headers.Add(TraceProvider.TraceIdHeaderName, nextTrace.TraceId);
33 | request.Headers.Add(TraceProvider.SpanIdHeaderName, nextTrace.SpanId);
34 | request.Headers.Add(TraceProvider.ParentSpanIdHeaderName, nextTrace.ParentSpanId);
35 | request.Headers.Add(TraceProvider.SampledHeaderName, nextTrace.ParentSpanId);
36 |
37 | var span = _client.StartClientTrace(request.RequestUri, request.Method.ToString(), nextTrace);
38 | var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
39 |
40 | _client.EndClientTrace(span, (int)response.StatusCode);
41 | return response;
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/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("Medidata.ZipkinTracer.Core")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyCompany("Medidata Solutions, Inc.")]
11 | [assembly: AssemblyProduct("Medidata.ZipkinTracer.Core")]
12 | [assembly: AssemblyCopyright("Copyright © Medidata Solutions, Inc. 2016")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | #if DEBUG
17 | [assembly: AssemblyConfiguration("Debug")]
18 | #else
19 | [assembly: AssemblyConfiguration("Release")]
20 | #endif
21 |
22 | // Setting ComVisible to false makes the types in this assembly not visible
23 | // to COM components. If you need to access a type in this assembly from
24 | // COM, set the ComVisible attribute to true on that type.
25 | [assembly: ComVisible(false)]
26 |
27 | // The following GUID is for the ID of the typelib if this project is exposed to COM
28 | [assembly: Guid("dfbe97d6-8b81-4bee-b2ce-23ef0f4d9953")]
29 |
30 | // Version information for an assembly consists of the following four values:
31 | //
32 | // Major Version
33 | // Minor Version
34 | // Build Number
35 | // Revision
36 | //
37 | // You can specify all the values or you can default the Build and Revision Numbers
38 | // by using the '*' as shown below:
39 | // [assembly: AssemblyVersion("1.0.*")]
40 | [assembly: AssemblyVersion("3.2.0")]
41 | [assembly: AssemblyFileVersion("3.2.0")]
42 | [assembly: AssemblyInformationalVersion("3.2.0")]
43 | [assembly: InternalsVisibleTo("Medidata.ZipkinTracer.Core.Test")]
--------------------------------------------------------------------------------
/tests/Medidata.ZipkinTracer.Core.Test/Models/Serialization/Json/JsonSpanTests.cs:
--------------------------------------------------------------------------------
1 | using Medidata.ZipkinTracer.Models;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using Ploeh.AutoFixture;
4 | using System.Linq;
5 |
6 | namespace Medidata.ZipkinTracer.Core.Test.Models.Serialization.Json
7 | {
8 | [TestClass]
9 | public class JsonSpanTests
10 | {
11 | private IFixture fixture;
12 |
13 | [TestInitialize]
14 | public void Init()
15 | {
16 | fixture = new Fixture();
17 | }
18 |
19 | [TestMethod]
20 | public void JsonSpan()
21 | {
22 | // Arrange
23 | var span = new Span
24 | {
25 | Id = fixture.Create(),
26 | Name = fixture.Create(),
27 | ParentId = fixture.Create(),
28 | TraceId = fixture.Create(),
29 | };
30 | span.Annotations.Add(fixture.Create());
31 | span.Annotations.Add(fixture.Create());
32 | span.Annotations.Add(fixture.Create());
33 | span.Annotations.Add(fixture.Create());
34 |
35 | // Act
36 | var result = new JsonSpan(span);
37 |
38 | // Assert
39 | Assert.AreEqual(span.TraceId, result.TraceId);
40 | Assert.AreEqual(span.Name, result.Name);
41 | Assert.AreEqual(span.Id, result.Id);
42 | Assert.AreEqual(span.ParentId, result.ParentId);
43 | Assert.AreEqual(2, result.Annotations.Count());
44 | Assert.AreEqual(2, result.BinaryAnnotations.Count());
45 | }
46 |
47 | [TestMethod]
48 | public void JsonSpan_ParentIdIsWhiteSpace()
49 | {
50 | // Arrange
51 | var span = new Span
52 | {
53 | ParentId = string.Empty
54 | };
55 |
56 | // Act
57 | var result = new JsonSpan(span);
58 |
59 | // Assert
60 | Assert.IsNull(result.ParentId);
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/ServiceEndpoint.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Net;
4 | using System.Net.Sockets;
5 | using Medidata.ZipkinTracer.Models;
6 |
7 | namespace Medidata.ZipkinTracer.Core
8 | {
9 | public class ServiceEndpoint
10 | {
11 | public virtual Endpoint GetLocalEndpoint(string serviceName, ushort port)
12 | {
13 | return new Endpoint()
14 | {
15 | ServiceName = serviceName,
16 | IPAddress = GetLocalIPAddress(),
17 | Port = port
18 | };
19 | }
20 |
21 | public virtual Endpoint GetRemoteEndpoint(Uri remoteServer, string remoteServiceName)
22 | {
23 | var addressBytes = GetRemoteIPAddress(remoteServer).GetAddressBytes();
24 | if (BitConverter.IsLittleEndian)
25 | {
26 | Array.Reverse(addressBytes);
27 | }
28 |
29 | var ipAddressStr = BitConverter.ToInt32(addressBytes, 0);
30 | var hostIpAddressStr = (int)IPAddress.HostToNetworkOrder(ipAddressStr);
31 |
32 | return new Endpoint()
33 | {
34 | ServiceName = remoteServiceName,
35 | IPAddress = GetRemoteIPAddress(remoteServer),
36 | Port = (ushort)remoteServer.Port
37 | };
38 | }
39 |
40 | private static IPAddress GetLocalIPAddress()
41 | {
42 | if (!System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
43 | {
44 | return null;
45 | }
46 |
47 | IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
48 |
49 | return host
50 | .AddressList
51 | .FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
52 | }
53 |
54 | private static IPAddress GetRemoteIPAddress(Uri remoteServer)
55 | {
56 | var adressList = Dns.GetHostAddresses(remoteServer.Host);
57 | return adressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Medidata.ZipkinTracerModule.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25123.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Medidata.ZipkinTracer.Core", "src\Medidata.ZipkinTracer.Core\Medidata.ZipkinTracer.Core.csproj", "{EB432891-204B-4B97-BFB1-A820E424284E}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{ACDBD61A-453C-4AA4-97FB-CD1520DD1D16}"
9 | ProjectSection(SolutionItems) = preProject
10 | .nuget\NuGet.Config = .nuget\NuGet.Config
11 | .nuget\NuGet.exe = .nuget\NuGet.exe
12 | .nuget\NuGet.targets = .nuget\NuGet.targets
13 | .nuget\packages.config = .nuget\packages.config
14 | EndProjectSection
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Medidata.ZipkinTracer.Core.Test", "tests\Medidata.ZipkinTracer.Core.Test\Medidata.ZipkinTracer.Core.Test.csproj", "{0158C8BD-D881-4EE8-A8C6-54707A67A00D}"
17 | EndProject
18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7CF24590-AC06-44E4-AF93-88A9B2CD1264}"
19 | ProjectSection(SolutionItems) = preProject
20 | README.md = README.md
21 | EndProjectSection
22 | EndProject
23 | Global
24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
25 | Debug|Any CPU = Debug|Any CPU
26 | Release|Any CPU = Release|Any CPU
27 | EndGlobalSection
28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
29 | {EB432891-204B-4B97-BFB1-A820E424284E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {EB432891-204B-4B97-BFB1-A820E424284E}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {EB432891-204B-4B97-BFB1-A820E424284E}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {EB432891-204B-4B97-BFB1-A820E424284E}.Release|Any CPU.Build.0 = Release|Any CPU
33 | {0158C8BD-D881-4EE8-A8C6-54707A67A00D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {0158C8BD-D881-4EE8-A8C6-54707A67A00D}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {0158C8BD-D881-4EE8-A8C6-54707A67A00D}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 | {0158C8BD-D881-4EE8-A8C6-54707A67A00D}.Release|Any CPU.Build.0 = Release|Any CPU
37 | EndGlobalSection
38 | GlobalSection(SolutionProperties) = preSolution
39 | HideSolutionNode = FALSE
40 | EndGlobalSection
41 | EndGlobal
42 |
--------------------------------------------------------------------------------
/src/Medidata.ZipkinTracer.Core/SpanProcessorTaskFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using Medidata.ZipkinTracer.Core.Logging;
6 | using Medidata.ZipkinTracer.Core.Helpers;
7 |
8 | namespace Medidata.ZipkinTracer.Core
9 | {
10 | public class SpanProcessorTaskFactory
11 | {
12 | private Task spanProcessorTaskInstance;
13 | private CancellationTokenSource cancellationTokenSource;
14 | private ILog logger;
15 | private const int defaultDelayTime = 500;
16 | private const int encounteredAnErrorDelayTime = 30000;
17 |
18 | readonly object sync = new object();
19 |
20 | public SpanProcessorTaskFactory(ILog logger, CancellationTokenSource cancellationTokenSource)
21 | {
22 | this.logger = logger ?? LogProvider.GetCurrentClassLogger();
23 | this.cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource();
24 | }
25 |
26 | public SpanProcessorTaskFactory()
27 | :this(LogProvider.GetCurrentClassLogger(), new CancellationTokenSource())
28 | {
29 | }
30 |
31 | [ExcludeFromCodeCoverage] //excluded from code coverage since this class is a 1 liner that starts up a background thread
32 | public virtual void CreateAndStart(Action action)
33 | {
34 | SyncHelper.ExecuteSafely(sync, () => spanProcessorTaskInstance == null || spanProcessorTaskInstance.Status == TaskStatus.Faulted,
35 | () =>
36 | {
37 | spanProcessorTaskInstance = Task.Factory.StartNew(() => ActionWrapper(action), cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
38 | });
39 | }
40 |
41 | public virtual void StopTask()
42 | {
43 | SyncHelper.ExecuteSafely(sync, () => cancellationTokenSource.Token.CanBeCanceled, () => cancellationTokenSource.Cancel());
44 | }
45 |
46 | internal async void ActionWrapper(Action action)
47 | {
48 | while (!IsTaskCancelled())
49 | {
50 | int delayTime = defaultDelayTime;
51 | try
52 | {
53 | action();
54 | }
55 | catch (Exception ex)
56 | {
57 | logger.ErrorException("Error in SpanProcessorTask", ex);
58 | delayTime = encounteredAnErrorDelayTime;
59 | }
60 |
61 | // stop loop if task is cancelled while delay is in process
62 | try
63 | {
64 | await Task.Delay(delayTime, cancellationTokenSource.Token);
65 | }
66 | catch (TaskCanceledException)
67 | {
68 | break;
69 | }
70 |
71 | }
72 | }
73 |
74 | public virtual bool IsTaskCancelled()
75 | {
76 | return cancellationTokenSource.IsCancellationRequested;
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/tests/Medidata.ZipkinTracer.Core.Test/SpanProcesssorTaskFactoryTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using Medidata.ZipkinTracer.Core.Logging;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using Rhino.Mocks;
6 |
7 | namespace Medidata.ZipkinTracer.Core.Collector.Test
8 | {
9 | [TestClass]
10 | public class SpanProcesssorTaskFactoryTests
11 | {
12 | private SpanProcessorTaskFactory spanProcessorTaskFactory;
13 | private CancellationTokenSource cancellationTokenSource;
14 | private bool actionCalled;
15 | private ILog logger;
16 |
17 | [TestInitialize]
18 | public void Init()
19 | {
20 | logger = MockRepository.GenerateStub();
21 | cancellationTokenSource = new CancellationTokenSource();
22 | spanProcessorTaskFactory = new SpanProcessorTaskFactory(logger, cancellationTokenSource);
23 | actionCalled = false;
24 | }
25 |
26 | [TestMethod]
27 | public void StopTask()
28 | {
29 | Assert.IsFalse(cancellationTokenSource.IsCancellationRequested);
30 |
31 | spanProcessorTaskFactory.StopTask();
32 |
33 | Assert.IsTrue(cancellationTokenSource.IsCancellationRequested);
34 | }
35 |
36 | [TestMethod]
37 | public void IsTaskCancelled()
38 | {
39 | Assert.IsFalse(cancellationTokenSource.IsCancellationRequested);
40 | Assert.IsFalse(spanProcessorTaskFactory.IsTaskCancelled());
41 |
42 | cancellationTokenSource.Cancel();
43 | Assert.IsTrue(cancellationTokenSource.IsCancellationRequested);
44 | Assert.IsTrue(spanProcessorTaskFactory.IsTaskCancelled());
45 | }
46 |
47 | [TestMethod]
48 | public void ActionWrapper()
49 | {
50 | var myAction = new Action(() => { actionCalled = true; });
51 | Assert.IsFalse(actionCalled);
52 |
53 | spanProcessorTaskFactory.ActionWrapper(myAction);
54 | Assert.IsTrue(actionCalled);
55 |
56 | cancellationTokenSource.Cancel();
57 | }
58 |
59 | [TestMethod]
60 | public void ActionWrapper_Exception()
61 | {
62 | Exception ex = new Exception("Exception!");
63 | bool logErrorCalled = false;
64 | logger.Stub(x => x.Log(Arg.Is.Equal(LogLevel.Error), Arg>.Is.Null, Arg.Is.Null, Arg