154 lines
5.2 KiB
C#
154 lines
5.2 KiB
C#
// <copyright file="PrometheusExporterMetricsHttpServer.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.IO;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using OpenTelemetry.Exporter.Prometheus.Implementation;
|
|
|
|
namespace OpenTelemetry.Exporter
|
|
{
|
|
/// <summary>
|
|
/// A HTTP listener used to expose Prometheus metrics.
|
|
/// </summary>
|
|
public class PrometheusExporterMetricsHttpServer : IDisposable
|
|
{
|
|
private readonly PrometheusExporter exporter;
|
|
private readonly HttpListener httpListener = new HttpListener();
|
|
private readonly object syncObject = new object();
|
|
|
|
private CancellationTokenSource tokenSource;
|
|
private Task workerThread;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PrometheusExporterMetricsHttpServer"/> class.
|
|
/// </summary>
|
|
/// <param name="exporter">The <see cref="PrometheusExporter"/> instance.</param>
|
|
public PrometheusExporterMetricsHttpServer(PrometheusExporter exporter)
|
|
{
|
|
this.exporter = exporter ?? throw new ArgumentNullException(nameof(exporter));
|
|
this.httpListener.Prefixes.Add(exporter.Options.Url);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start exporter.
|
|
/// </summary>
|
|
/// <param name="token">An optional <see cref="CancellationToken"/> that can be used to stop the htto server.</param>
|
|
public void Start(CancellationToken token = default)
|
|
{
|
|
lock (this.syncObject)
|
|
{
|
|
if (this.tokenSource != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// link the passed in token if not null
|
|
this.tokenSource = token == default ?
|
|
new CancellationTokenSource() :
|
|
CancellationTokenSource.CreateLinkedTokenSource(token);
|
|
|
|
this.workerThread = Task.Factory.StartNew(this.WorkerThread, default, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop exporter.
|
|
/// </summary>
|
|
public void Stop()
|
|
{
|
|
lock (this.syncObject)
|
|
{
|
|
if (this.tokenSource == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.tokenSource.Cancel();
|
|
this.workerThread.Wait();
|
|
this.tokenSource = null;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Dispose()
|
|
{
|
|
this.Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases the unmanaged resources used by this class and optionally releases the managed resources.
|
|
/// </summary>
|
|
/// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.</param>
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (this.httpListener != null && this.httpListener.IsListening)
|
|
{
|
|
this.Stop();
|
|
this.httpListener.Close();
|
|
}
|
|
}
|
|
|
|
private void WorkerThread()
|
|
{
|
|
this.httpListener.Start();
|
|
|
|
try
|
|
{
|
|
using var scope = SuppressInstrumentationScope.Begin();
|
|
while (!this.tokenSource.IsCancellationRequested)
|
|
{
|
|
var ctxTask = this.httpListener.GetContextAsync();
|
|
ctxTask.Wait(this.tokenSource.Token);
|
|
|
|
var ctx = ctxTask.Result;
|
|
|
|
ctx.Response.StatusCode = 200;
|
|
ctx.Response.ContentType = PrometheusMetricBuilder.ContentType;
|
|
|
|
using var output = ctx.Response.OutputStream;
|
|
using var writer = new StreamWriter(output);
|
|
this.exporter.MakePullRequest();
|
|
this.exporter.WriteMetricsCollection(writer);
|
|
}
|
|
}
|
|
catch (OperationCanceledException ex)
|
|
{
|
|
PrometheusExporterEventSource.Log.CanceledExport(ex);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PrometheusExporterEventSource.Log.FailedExport(ex);
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
this.httpListener.Stop();
|
|
this.httpListener.Close();
|
|
}
|
|
catch (Exception exFromFinally)
|
|
{
|
|
PrometheusExporterEventSource.Log.FailedShutdown(exFromFinally);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|