-
Notifications
You must be signed in to change notification settings - Fork 147
/
Copy pathSymbolUploadApi.cs
131 lines (113 loc) · 5.1 KB
/
SymbolUploadApi.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// <copyright file="SymbolUploadApi.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>
#nullable enable
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Threading.Tasks;
using Datadog.Trace.Agent;
using Datadog.Trace.Agent.DiscoveryService;
using Datadog.Trace.Agent.Transports;
using Datadog.Trace.Configuration;
using Datadog.Trace.Logging;
namespace Datadog.Trace.Debugger.Upload
{
internal class SymbolUploadApi : DebuggerUploadApiBase
{
private const int MaxRetries = 3;
private const int StartingSleepDuration = 3;
private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor<SymbolUploadApi>();
private readonly IApiRequestFactory _apiRequestFactory;
private readonly ArraySegment<byte> _eventMetadata;
private readonly bool _enableCompression;
private SymbolUploadApi(
IApiRequestFactory apiRequestFactory,
IDiscoveryService discoveryService,
IGitMetadataTagsProvider gitMetadataTagsProvider,
ArraySegment<byte> eventMetadata,
bool enableCompression)
: base(apiRequestFactory, gitMetadataTagsProvider)
{
_apiRequestFactory = apiRequestFactory;
_eventMetadata = eventMetadata;
_enableCompression = enableCompression;
discoveryService.SubscribeToChanges(c => Endpoint = c.SymbolDbEndpoint);
}
public static IBatchUploadApi Create(
IApiRequestFactory apiRequestFactory,
IDiscoveryService discoveryService,
IGitMetadataTagsProvider gitMetadataTagsProvider,
string serviceName,
bool enableCompression)
{
ArraySegment<byte> GetEventMetadataAsArraySegment()
{
var eventMetadata = $@"{{""ddsource"": ""dd_debugger"", ""service"": ""{serviceName}"", ""runtimeId"": ""{Tracer.RuntimeId}""}}";
var count = Encoding.UTF8.GetByteCount(eventMetadata);
var eventAsBytes = new byte[count];
Encoding.UTF8.GetBytes(eventMetadata, 0, eventMetadata.Length, eventAsBytes, 0);
return new ArraySegment<byte>(eventAsBytes);
}
var eventMetadata = GetEventMetadataAsArraySegment();
return new SymbolUploadApi(apiRequestFactory, discoveryService, gitMetadataTagsProvider, eventMetadata, enableCompression);
}
public override async Task<bool> SendBatchAsync(ArraySegment<byte> symbols)
{
if (symbols.Array == null || symbols.Count == 0)
{
return false;
}
var uri = BuildUri();
if (string.IsNullOrEmpty(uri))
{
Log.Warning("Symbol database endpoint is not defined");
return false;
}
var request = _apiRequestFactory.Create(new Uri(uri));
var retries = 0;
var sleepDuration = StartingSleepDuration;
MultipartFormItem symbolsItem;
if (_enableCompression)
{
using var memoryStream = new MemoryStream();
#if NETFRAMEWORK
using var gzipStream = new Vendors.ICSharpCode.SharpZipLib.GZip.GZipOutputStream(memoryStream);
await gzipStream.WriteAsync(symbols.Array, 0, symbols.Array.Length).ConfigureAwait(false);
#else
using var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress);
await gzipStream.WriteAsync(symbols.Array, 0, symbols.Array.Length).ConfigureAwait(false);
#endif
symbolsItem = new MultipartFormItem("file", "application/gzip", "file.gz", new ArraySegment<byte>(memoryStream.ToArray()));
}
else
{
symbolsItem = new MultipartFormItem("file", MimeTypes.Json, "file.json", symbols);
}
var items = new[] { symbolsItem, new MultipartFormItem("event", MimeTypes.Json, "event.json", _eventMetadata) };
while (retries < MaxRetries)
{
using var response = await request.PostAsync(items).ConfigureAwait(false);
if (response.StatusCode is >= 200 and <= 299)
{
return true;
}
retries++;
if (response.ShouldRetry())
{
sleepDuration *= (int)Math.Pow(2, retries);
await Task.Delay(sleepDuration).ConfigureAwait(false);
}
else
{
var content = await response.ReadAsStringAsync().ConfigureAwait(false);
Log.Error<int, string>("Failed to upload symbol with status code {StatusCode} and message: {ResponseContent}", response.StatusCode, content);
return false;
}
}
return false;
}
}
}