2
2
// Licensed under the MIT License. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
+ using System . Collections . Concurrent ;
5
6
using System . Collections . Generic ;
6
7
using System . Linq ;
7
8
using System . Threading ;
8
9
using System . Threading . Tasks ;
9
10
using Microsoft . Azure . WebJobs . Script ;
10
11
using Microsoft . Azure . WebJobs . Script . Description ;
12
+ using Microsoft . Azure . WebJobs . Script . Diagnostics ;
11
13
using Microsoft . Diagnostics . Tracing ;
14
+ using WebJobs . Script . WebHost . Models ;
12
15
13
16
namespace WebJobs . Script . WebHost . Diagnostics
14
17
{
15
18
public static class MetricsEventManager
16
19
{
17
20
private static FunctionActivityTracker instance = null ;
18
21
private static object functionActivityTrackerLockObject = new object ( ) ;
22
+ private static string siteName ;
19
23
20
- public static void FunctionStarted ( )
24
+ static MetricsEventManager ( )
25
+ {
26
+ siteName = GetNormalizedString ( Environment . GetEnvironmentVariable ( "WEBSITE_SITE_NAME" ) ) ;
27
+ }
28
+
29
+ public static void FunctionStarted ( FunctionStartedEvent startedEvent )
21
30
{
22
31
lock ( functionActivityTrackerLockObject )
23
32
{
24
33
if ( instance == null )
25
34
{
26
35
instance = new FunctionActivityTracker ( ) ;
27
36
}
28
- instance . FunctionStarted ( ) ;
37
+ instance . FunctionStarted ( startedEvent ) ;
29
38
}
30
39
}
31
40
32
- public static void FunctionCompleted ( )
41
+ public static void FunctionCompleted ( FunctionStartedEvent completedEvent )
33
42
{
34
43
lock ( functionActivityTrackerLockObject )
35
44
{
36
45
if ( instance != null )
37
46
{
38
- instance . FunctionCompleted ( ) ;
47
+ instance . FunctionCompleted ( completedEvent ) ;
39
48
if ( ! instance . IsActive )
40
49
{
41
50
instance . StopEtwTaskAndRaiseFinishedEvent ( ) ;
@@ -52,7 +61,6 @@ public static void HostStarted(ScriptHost scriptHost)
52
61
return ;
53
62
}
54
63
55
- var siteName = GetNormalizedString ( Environment . GetEnvironmentVariable ( "WEBSITE_SITE_NAME" ) ) ;
56
64
foreach ( var function in scriptHost . Functions )
57
65
{
58
66
if ( function == null || function . Metadata == null )
@@ -97,7 +105,9 @@ private class FunctionActivityTracker : IDisposable
97
105
private DateTime startTime = DateTime . UtcNow ;
98
106
private ulong totalExecutionCount = 0 ;
99
107
private ulong runningFunctionCount = 0 ;
108
+ private const int MetricEventIntervalInSeconds = 5 ;
100
109
private CancellationTokenSource etwTaskcancellationSource = new CancellationTokenSource ( ) ;
110
+ private ConcurrentQueue < FunctionMetrics > functionMetricsQueue = new ConcurrentQueue < FunctionMetrics > ( ) ;
101
111
102
112
internal FunctionActivityTracker ( )
103
113
{
@@ -106,10 +116,18 @@ internal FunctionActivityTracker()
106
116
{
107
117
try
108
118
{
119
+ int currentSecond = MetricEventIntervalInSeconds ;
109
120
while ( ! etwTaskcancellationSource . Token . IsCancellationRequested )
110
121
{
111
- RaiseMetricEtwEvent ( ExecutionStage . InProgress ) ;
112
- await Task . Delay ( TimeSpan . FromSeconds ( 5 ) , etwTaskcancellationSource . Token ) ;
122
+ RaiseMetricsPerFunctionEvent ( ) ;
123
+ currentSecond = currentSecond + 1 ;
124
+ if ( currentSecond >= MetricEventIntervalInSeconds )
125
+ {
126
+ RaiseMetricEtwEvent ( ExecutionStage . InProgress ) ;
127
+ currentSecond = 0 ;
128
+ }
129
+
130
+ await Task . Delay ( TimeSpan . FromSeconds ( 1 ) , etwTaskcancellationSource . Token ) ;
113
131
}
114
132
}
115
133
catch ( TaskCanceledException )
@@ -120,13 +138,7 @@ internal FunctionActivityTracker()
120
138
} ,
121
139
etwTaskcancellationSource . Token ) ;
122
140
}
123
-
124
- private enum ExecutionStage
125
- {
126
- InProgress ,
127
- Finished
128
- }
129
-
141
+
130
142
internal bool IsActive
131
143
{
132
144
get
@@ -149,23 +161,33 @@ public void Dispose()
149
161
GC . SuppressFinalize ( this ) ;
150
162
}
151
163
152
- internal void FunctionStarted ( )
153
- {
164
+ internal void FunctionStarted ( FunctionStartedEvent startedEvent )
165
+ {
154
166
totalExecutionCount ++ ;
155
167
runningFunctionCount ++ ;
156
- }
157
168
158
- internal void FunctionCompleted ( )
169
+ var metricEventPerFunction = new FunctionMetrics ( startedEvent . FunctionMetadata . Name , ExecutionStage . Started , 0 ) ;
170
+ functionMetricsQueue . Enqueue ( metricEventPerFunction ) ;
171
+ }
172
+
173
+ internal void FunctionCompleted ( FunctionStartedEvent completedEvent )
159
174
{
160
175
if ( runningFunctionCount > 0 )
161
176
{
162
177
runningFunctionCount -- ;
163
178
}
179
+
180
+ var functionStage = ( completedEvent . Success == false ) ? ExecutionStage . Failed : ExecutionStage . Succeeded ;
181
+ long executionTimeInMS = ( long ) completedEvent . EndTime . Subtract ( completedEvent . StartTime ) . TotalMilliseconds ;
182
+
183
+ var monitoringEvent = new FunctionMetrics ( completedEvent . FunctionMetadata . Name , functionStage , executionTimeInMS ) ;
184
+ functionMetricsQueue . Enqueue ( monitoringEvent ) ;
164
185
}
165
186
166
187
internal void StopEtwTaskAndRaiseFinishedEvent ( )
167
188
{
168
189
etwTaskcancellationSource . Cancel ( ) ;
190
+ RaiseMetricsPerFunctionEvent ( ) ;
169
191
RaiseMetricEtwEvent ( ExecutionStage . Finished ) ;
170
192
}
171
193
@@ -180,6 +202,44 @@ private static void WriteFunctionsMetricEvent(string executionId, ulong executio
180
202
{
181
203
MetricEventSource . Log . RaiseFunctionsMetricEvent ( executionId , executionTimeSpan , executionCount , executionStage ) ;
182
204
}
205
+
206
+ private void RaiseMetricsPerFunctionEvent ( )
207
+ {
208
+ List < FunctionMetrics > metricsEventsList = GetMetricsQueueSnapshot ( ) ;
209
+
210
+ var aggregatedEventsPerFunction = from item in metricsEventsList
211
+ group item by item . FunctionName into FunctionGroups
212
+ select new
213
+ {
214
+ FunctionName = FunctionGroups . Key ,
215
+ StartedCount = Convert . ToUInt64 ( FunctionGroups . Count ( x => x . ExecutionStage == ExecutionStage . Started ) ) ,
216
+ FailedCount = Convert . ToUInt64 ( FunctionGroups . Count ( x => x . ExecutionStage == ExecutionStage . Failed ) ) ,
217
+ SucceededCount = Convert . ToUInt64 ( FunctionGroups . Count ( x => x . ExecutionStage == ExecutionStage . Succeeded ) ) ,
218
+ TotalExectionTimeInMs = Convert . ToUInt64 ( FunctionGroups . Sum ( x => Convert . ToDecimal ( x . ExecutionTimeInMS ) ) )
219
+ } ;
220
+
221
+ foreach ( var functionEvent in aggregatedEventsPerFunction )
222
+ {
223
+ MetricEventSource . Log . RaiseMetricsPerFunctionEvent ( siteName , functionEvent . FunctionName , functionEvent . TotalExectionTimeInMs , functionEvent . StartedCount , functionEvent . SucceededCount , functionEvent . FailedCount ) ;
224
+ }
225
+ }
226
+
227
+ private List < FunctionMetrics > GetMetricsQueueSnapshot ( )
228
+ {
229
+ var queueSnapshot = new List < FunctionMetrics > ( ) ;
230
+ var currentQueueLength = functionMetricsQueue . Count ;
231
+
232
+ for ( int iterator = 0 ; iterator < currentQueueLength ; iterator ++ )
233
+ {
234
+ FunctionMetrics queueItem ;
235
+ if ( functionMetricsQueue . TryDequeue ( out queueItem ) )
236
+ {
237
+ queueSnapshot . Add ( queueItem ) ;
238
+ }
239
+ }
240
+
241
+ return queueSnapshot ;
242
+ }
183
243
}
184
244
185
245
[ EventSource ( Guid = "08D0D743-5C24-43F9-9723-98277CEA5F9B" ) ]
@@ -196,6 +256,15 @@ public void RaiseFunctionsMetricEvent(string executionId, ulong executionTimeSpa
196
256
}
197
257
}
198
258
259
+ [ Event ( 57907 , Level = EventLevel . Informational , Channel = EventChannel . Operational ) ]
260
+ public void RaiseMetricsPerFunctionEvent ( string siteName , string functionName , ulong executionTimeInMs , ulong functionStartedCount , ulong functionCompletedCount , ulong functionFailedCount )
261
+ {
262
+ if ( IsEnabled ( ) )
263
+ {
264
+ WriteEvent ( 57907 , siteName , functionName , executionTimeInMs , functionStartedCount , functionCompletedCount , functionFailedCount ) ;
265
+ }
266
+ }
267
+
199
268
[ Event ( 57908 , Level = EventLevel . Informational , Channel = EventChannel . Operational ) ]
200
269
public void RaiseFunctionsInfoEvent ( string siteName , string functionName , string inputBindings , string outputBindings , string scriptType , bool isDisabled )
201
270
{
0 commit comments