22// Licensed under the MIT License. See License.txt in the project root for license information.
33
44using System ;
5- using System . IO ;
65using System . IO . Abstractions ;
7- using System . Linq ;
86using System . Threading ;
97using System . Threading . Tasks ;
108using Microsoft . Azure . WebJobs . Script . Diagnostics ;
11- using Microsoft . Azure . WebJobs . Script . Diagnostics . Extensions ;
129using Microsoft . Azure . WebJobs . Script . Metrics ;
1310using Microsoft . Azure . WebJobs . Script . WebHost . Configuration ;
1411using Microsoft . Extensions . Logging ;
1512using Microsoft . Extensions . Options ;
16- using Newtonsoft . Json ;
1713
1814namespace Microsoft . Azure . WebJobs . Script . WebHost . Metrics
1915{
20- public class FlexConsumptionMetricsPublisher : IMetricsPublisher , IDisposable
16+ public sealed partial class FlexConsumptionMetricsPublisher : IMetricsPublisher , IDisposable
2117 {
2218 private readonly IOptionsMonitor < StandbyOptions > _standbyOptions ;
2319 private readonly FlexConsumptionMetricsPublisherOptions _options ;
2420 private readonly IEnvironment _environment ;
2521 private readonly ILogger < FlexConsumptionMetricsPublisher > _logger ;
2622 private readonly IHostMetricsProvider _metricsProvider ;
27- private readonly object _lock = new object ( ) ;
23+ private readonly object _lock = new ( ) ;
2824 private readonly IFileSystem _fileSystem ;
2925 private readonly LegionMetricsFileManager _metricsFileManager ;
3026
31- private Timer _metricsPublisherTimer ;
32- private bool _started = false ;
3327 private DateTime _currentActivityIntervalStart ;
3428 private DateTime _activityIntervalHighWatermark = DateTime . MinValue ;
35- private ValueStopwatch _intervalStopwatch ;
3629 private IDisposable _standbyOptionsOnChangeSubscription ;
37- private TimeSpan _metricPublishInterval ;
38- private TimeSpan _initialPublishDelay ;
3930 private DateTime _lastPublishTime = DateTime . UtcNow ;
31+ private Lifecycle _lifecycle ;
4032
4133 public FlexConsumptionMetricsPublisher ( IEnvironment environment , IOptionsMonitor < StandbyOptions > standbyOptions , IOptions < FlexConsumptionMetricsPublisherOptions > options ,
4234 ILogger < FlexConsumptionMetricsPublisher > logger , IFileSystem fileSystem , IHostMetricsProvider metricsProvider )
@@ -71,29 +63,43 @@ public FlexConsumptionMetricsPublisher(IEnvironment environment, IOptionsMonitor
7163
7264 internal LegionMetricsFileManager MetricsFileManager => _metricsFileManager ;
7365
66+ private bool IsStarted => _lifecycle is not null ;
67+
7468 public void Start ( )
7569 {
76- Initialize ( ) ;
70+ if ( _lifecycle is not null )
71+ {
72+ return ;
73+ }
74+
75+ lock ( _lock )
76+ {
77+ if ( _lifecycle is not null )
78+ {
79+ return ;
80+ }
81+
82+ IsAlwaysReady = _environment
83+ . GetEnvironmentVariable ( EnvironmentSettingNames . FunctionsAlwaysReadyInstance ) == "1" ;
7784
78- _logger . LogInformation ( $ "Starting metrics publisher (AlwaysReady={ IsAlwaysReady } , MetricsPath='{ _metricsFileManager . MetricsFilePath } ').") ;
85+ _logger . LogInformation (
86+ $ "Starting metrics publisher (AlwaysReady={ IsAlwaysReady } ,"
87+ + $ " MetricsPath='{ _metricsFileManager . MetricsFilePath } ').") ;
7988
80- _metricsPublisherTimer = new Timer ( OnFunctionMetricsPublishTimer , null , _initialPublishDelay , _metricPublishInterval ) ;
81- _started = true ;
89+ _lifecycle = new (
90+ this ,
91+ TimeSpan . FromMilliseconds ( _options . InitialPublishDelayMS ) ,
92+ TimeSpan . FromMilliseconds ( _options . MetricsPublishIntervalMS ) ) ;
93+ }
8294 }
8395
84- /// <summary>
85- /// Initialize any environmentally derived state after specialization, prior to starting the publisher.
86- /// </summary>
87- internal void Initialize ( )
96+ public void Dispose ( )
8897 {
89- _metricPublishInterval = TimeSpan . FromMilliseconds ( _options . MetricsPublishIntervalMS ) ;
90- _initialPublishDelay = TimeSpan . FromMilliseconds ( _options . InitialPublishDelayMS ) ;
91- _intervalStopwatch = ValueStopwatch . StartNew ( ) ;
92-
93- IsAlwaysReady = _environment . GetEnvironmentVariable ( EnvironmentSettingNames . FunctionsAlwaysReadyInstance ) == "1" ;
98+ Interlocked . Exchange ( ref _lifecycle , null ) ? . Dispose ( ) ;
99+ Interlocked . Exchange ( ref _standbyOptionsOnChangeSubscription , null ) ? . Dispose ( ) ;
94100 }
95101
96- internal async Task OnPublishMetrics ( DateTime now )
102+ internal async Task OnPublishMetrics ( DateTime now , ValueStopwatch stopwatch )
97103 {
98104 try
99105 {
@@ -122,7 +128,7 @@ internal async Task OnPublishMetrics(DateTime now)
122128 {
123129 metrics = new Metrics
124130 {
125- TotalTimeMS = ( long ) _intervalStopwatch . GetElapsedTime ( ) . TotalMilliseconds ,
131+ TotalTimeMS = ( long ) stopwatch . GetElapsedTime ( ) . TotalMilliseconds ,
126132 ExecutionCount = FunctionExecutionCount ,
127133 ExecutionTimeMS = FunctionExecutionTimeMS ,
128134 IsAlwaysReady = IsAlwaysReady ,
@@ -149,15 +155,6 @@ internal async Task OnPublishMetrics(DateTime now)
149155 // ensure no background exceptions escape
150156 _logger . LogError ( ex , $ "Error publishing metrics.") ;
151157 }
152- finally
153- {
154- _intervalStopwatch = ValueStopwatch . StartNew ( ) ;
155- }
156- }
157-
158- private async void OnFunctionMetricsPublishTimer ( object state )
159- {
160- await OnPublishMetrics ( DateTime . UtcNow ) ;
161158 }
162159
163160 private void OnStandbyOptionsChange ( )
@@ -175,7 +172,7 @@ public void OnFunctionStarted(string functionName, string invocationId)
175172
176173 internal void OnFunctionStarted ( string functionName , string invocationId , DateTime now )
177174 {
178- if ( ! _started )
175+ if ( ! IsStarted )
179176 {
180177 return ;
181178 }
@@ -199,7 +196,7 @@ public void OnFunctionCompleted(string functionName, string invocationId)
199196
200197 internal void OnFunctionCompleted ( string functionName , string invocationId , DateTime now )
201198 {
202- if ( ! _started )
199+ if ( ! IsStarted )
203200 {
204201 return ;
205202 }
@@ -267,15 +264,6 @@ private static double RoundUp(double metric, int granularity)
267264 return Math . Ceiling ( metric / granularity ) * granularity ;
268265 }
269266
270- public void Dispose ( )
271- {
272- _metricsPublisherTimer ? . Dispose ( ) ;
273- _metricsPublisherTimer = null ;
274-
275- _standbyOptionsOnChangeSubscription ? . Dispose ( ) ;
276- _standbyOptionsOnChangeSubscription = null ;
277- }
278-
279267 internal class Metrics
280268 {
281269 /// <summary>
0 commit comments