forked from open-telemetry/opentelemetry-dotnet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPeriodicExportingMetricReader.cs
More file actions
158 lines (140 loc) · 6.2 KB
/
Copy pathPeriodicExportingMetricReader.cs
File metadata and controls
158 lines (140 loc) · 6.2 KB
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
// <copyright file="PeriodicExportingMetricReader.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System;
using System.Diagnostics;
using System.Threading;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Metrics
{
/// <summary>
/// MetricReader implementation which collects metrics based on
/// a user-configurable time interval and passes the metrics to
/// the configured MetricExporter.
/// </summary>
public class PeriodicExportingMetricReader : BaseExportingMetricReader
{
internal const int DefaultExportIntervalMilliseconds = 60000;
internal const int DefaultExportTimeoutMilliseconds = 30000;
internal readonly int ExportIntervalMilliseconds;
internal readonly int ExportTimeoutMilliseconds;
private readonly Thread exporterThread;
private readonly AutoResetEvent exportTrigger = new(false);
private readonly ManualResetEvent shutdownTrigger = new(false);
private bool disposed;
/// <summary>
/// Initializes a new instance of the <see cref="PeriodicExportingMetricReader"/> class.
/// </summary>
/// <param name="exporter">Exporter instance to export Metrics to.</param>
/// <param name="exportIntervalMilliseconds">The interval in milliseconds between two consecutive exports. The default value is 60000.</param>
/// <param name="exportTimeoutMilliseconds">How long the export can run before it is cancelled. The default value is 30000.</param>
public PeriodicExportingMetricReader(
BaseExporter<Metric> exporter,
int exportIntervalMilliseconds = DefaultExportIntervalMilliseconds,
int exportTimeoutMilliseconds = DefaultExportTimeoutMilliseconds)
: base(exporter)
{
Guard.ThrowIfInvalidTimeout(exportIntervalMilliseconds);
Guard.ThrowIfZero(exportIntervalMilliseconds);
Guard.ThrowIfInvalidTimeout(exportTimeoutMilliseconds);
if ((this.SupportedExportModes & ExportModes.Push) != ExportModes.Push)
{
throw new InvalidOperationException($"The '{nameof(exporter)}' does not support '{nameof(ExportModes)}.{nameof(ExportModes.Push)}'");
}
this.ExportIntervalMilliseconds = exportIntervalMilliseconds;
this.ExportTimeoutMilliseconds = exportTimeoutMilliseconds;
this.exporterThread = new Thread(new ThreadStart(this.ExporterProc))
{
IsBackground = true,
Name = $"OpenTelemetry-{nameof(PeriodicExportingMetricReader)}-{exporter.GetType().Name}",
};
this.exporterThread.Start();
}
/// <inheritdoc />
protected override bool OnShutdown(int timeoutMilliseconds)
{
var result = true;
try
{
this.shutdownTrigger.Set();
}
catch (ObjectDisposedException)
{
return false;
}
if (timeoutMilliseconds == Timeout.Infinite)
{
this.exporterThread.Join();
result = this.exporter.Shutdown() && result;
}
else
{
var sw = Stopwatch.StartNew();
result = this.exporterThread.Join(timeoutMilliseconds) && result;
var timeout = timeoutMilliseconds - sw.ElapsedMilliseconds;
result = this.exporter.Shutdown((int)Math.Max(timeout, 0)) && result;
}
return result;
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.exportTrigger.Dispose();
this.shutdownTrigger.Dispose();
}
this.disposed = true;
}
base.Dispose(disposing);
}
private void ExporterProc()
{
int index;
int timeout;
var triggers = new WaitHandle[] { this.exportTrigger, this.shutdownTrigger };
var sw = Stopwatch.StartNew();
while (true)
{
timeout = (int)(this.ExportIntervalMilliseconds - (sw.ElapsedMilliseconds % this.ExportIntervalMilliseconds));
try
{
index = WaitHandle.WaitAny(triggers, timeout);
}
catch (ObjectDisposedException)
{
return;
}
switch (index)
{
case 0: // export
OpenTelemetrySdkEventSource.Log.MetricReaderEvent("PeriodicExportingMetricReader calling MetricReader.Collect because Export was triggered.");
this.Collect(this.ExportTimeoutMilliseconds);
break;
case 1: // shutdown
OpenTelemetrySdkEventSource.Log.MetricReaderEvent("PeriodicExportingMetricReader calling MetricReader.Collect because Shutdown was triggered.");
this.Collect(this.ExportTimeoutMilliseconds); // TODO: do we want to use the shutdown timeout here?
return;
case WaitHandle.WaitTimeout: // timer
OpenTelemetrySdkEventSource.Log.MetricReaderEvent("PeriodicExportingMetricReader calling MetricReader.Collect because the export interval has elapsed.");
this.Collect(this.ExportTimeoutMilliseconds);
break;
}
}
}
}
}