/
MetricWrapper.cs
221 lines (181 loc) · 7.47 KB
/
MetricWrapper.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
using System;
using System.Collections.Generic;
using com.signalfuse.metrics.protobuf;
using Microsoft.Azure.WebJobs;
namespace azurefunctionscsharp
{
public class MetricWrapper : IDisposable
{
// signalfx env variables
private static string AUTH_TOKEN = "SIGNALFX_AUTH_TOKEN";
private static string TIMEOUT_MS = "SIGNALFX_SEND_TIMEOUT";
// azure execution context variables
private static string REGION_NAME = "REGION_NAME";
private static string WEBSITE_SITE_NAME = "WEBSITE_SITE_NAME";
private static string APP_POOL_ID = "APP_POOL_ID";
//metric names
protected static string METRIC_NAME_PREFIX = "azure.function.";
protected static string METRIC_NAME_INVOCATIONS = "invocations";
protected static string METRIC_NAME_ERRORS = "errors";
protected static string METRIC_NAME_DURATION = "duration";
//dimension names
protected static string FUNCTION_NAME = "azure_function_name";
protected static string METRIC_SOURCE = "azure_function_wrapper";
protected static string RESOURCE_NAME = "azure_resource_name";
protected static string REGION_DIMENSION = "azure_region";
protected static string IS_AZURE_WRAPPER = "is_Azure_Function";
protected static string WRAPPER_VERSION = "1.0.0";
private readonly System.Diagnostics.Stopwatch watch;
private readonly IDictionary<string, string> defaultDimensions;
private readonly ISignalFxReporter reporter;
public MetricWrapper(ExecutionContext context) : this(context, null)
{
}
public MetricWrapper(ExecutionContext context,
List<Dimension> dimensions) : this(context, dimensions, GetEnvironmentVariable(AUTH_TOKEN))
{
}
public MetricWrapper(ExecutionContext context,
List<Dimension> dimensions,
String authToken)
{
int timeoutMs = 300; //default timeout 300ms
try {
timeoutMs = Int32.Parse(GetEnvironmentVariable(TIMEOUT_MS));
} catch (Exception e) {
//ignore and use default timeout
}
// create endpoint
SignalFxAzureFuncEndpoint signalFxEndpoint = new SignalFxAzureFuncEndpoint();
// create reporter with endpoint
reporter = new SignalFxReporter(signalFxEndpoint.ToString(), authToken, timeoutMs);
// get default dimensions
defaultDimensions = GetDefaultDimensions(context);
// set wrapper singleton context
MetricSender.setWrapper(this);
watch = System.Diagnostics.Stopwatch.StartNew();
sendMetricCounter(METRIC_NAME_INVOCATIONS, MetricType.COUNTER);
}
private void sendMetricCounter(string metricName, MetricType metricType)
{
Datum datum = new Datum();
datum.intValue = 1;
sendMetric(metricName, metricType, datum);
}
private void sendMetric(string metricName, MetricType metricType,
Datum datum)
{
DataPoint dp = new DataPoint();
dp.metric = METRIC_NAME_PREFIX + metricName;
dp.metricType = metricType;
dp.value = datum;
MetricSender.sendMetric(dp);
}
protected internal void sendMetric(DataPoint dp)
{
// send the metric
AddDimensions(dp, defaultDimensions);
DataPointUploadMessage msg = new DataPointUploadMessage();
msg.datapoints.Add(dp);
reporter.Send(msg);
}
public void Dispose()
{
//end stopwatch and send duration
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
Datum timer = new Datum();
timer.doubleValue = elapsedMs;
sendMetric(METRIC_NAME_DURATION, MetricType.GAUGE, timer);
}
public void Error()
{
sendMetricCounter(METRIC_NAME_ERRORS, MetricType.COUNTER);
}
public static string GetEnvironmentVariable(string name)
{
return System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
}
private static Dictionary<string, string> GetDefaultDimensions(ExecutionContext context)
{
Dictionary<string, string> defaultDimensions = new Dictionary<string, string>();
string functionName = context.FunctionName;
string resourceName = GetEnvironmentVariable(WEBSITE_SITE_NAME);
string resourceNameSecondary = GetEnvironmentVariable(APP_POOL_ID);
string region = GetRegionName(GetEnvironmentVariable(REGION_NAME));
functionName = string.IsNullOrEmpty(functionName) ? "undefined" : functionName;
defaultDimensions.Add(FUNCTION_NAME, functionName);
if (!string.IsNullOrEmpty(resourceName))
{
defaultDimensions.Add(RESOURCE_NAME, resourceName);
}
else if (!string.IsNullOrEmpty(resourceNameSecondary))
{
defaultDimensions.Add(RESOURCE_NAME, resourceNameSecondary);
}
else
{
defaultDimensions.Add(RESOURCE_NAME, "undefined");
}
region = string.IsNullOrEmpty(region) ? "undefined" : region;
defaultDimensions.Add(REGION_DIMENSION, region);
defaultDimensions.Add("metric_source", METRIC_SOURCE);
defaultDimensions.Add("function_wrapper_version", WRAPPER_VERSION);
defaultDimensions.Add(IS_AZURE_WRAPPER, "true");
return defaultDimensions;
}
protected virtual void AddDimensions(DataPoint dataPoint, IDictionary<string, string> dimensions)
{
foreach (KeyValuePair<string, string> entry in dimensions)
{
if (!string.IsNullOrEmpty(entry.Value))
{
AddDimension(dataPoint, entry.Key, entry.Value);
}
}
}
protected virtual void AddDimension(DataPoint dataPoint, string key, string value)
{
Dimension dimension = new Dimension();
dimension.key = key;
dimension.value = value;
dataPoint.dimensions.Add(dimension);
}
private static string GetRegionName(String region)
{
if (region != null)
{
switch (region)
{
case "East US 2": return "eastus2";
case "West US 2": return "westus2";
case "South Central US": return "southcentralus";
case "West Central US": return "westcentralus";
case "East US": return "eastus";
case "North Central US": return "northcentralus";
case "North Europe": return "northeurope";
case "Canada East": return "canadaeast";
case "Central US": return "centralus";
case "West US": return "westus";
case "West Europe": return "westeurope";
case "Central India": return "centralindia";
case "Southeast Asia": return "southeastasia";
case "Canada Central": return "canadacentral";
case "Korea Central": return "koreacentral";
case "France Central": return "francecentral";
case "South India": return "southindia";
case "Australia East": return "australiaeast";
case "Australia Southeast": return "australiasoutheast";
case "Japan West": return "japanwest";
case "UK West": return "ukwest";
case "UK South": return "uksouth";
case "Japan East": return "japaneast";
case "East Asia": return "eastasia";
case "Brazil South": return "brazilsouth";
default: return null;
}
}
return null;
}
}
}