Display span information on Z page (#491)
* Classes scaffolding for zPages Implemented Http Server to create HTML that displays span count using SimpleSpanProcessor * incorporating review comments - made ContentType const in zpages stats builder - modified return statements in zpages exporter - removed implementation folder from ZPages.csproj - changed default uri for zpages test application - changed data type of spanCount to long, and doing Interlocked.Increment when span starts * fixing minor error * Delete Exporters.csproj Fix for https://github.com/open-telemetry/opentelemetry-dotnet/issues/480 * Delete Exporters.csproj Fix for https://github.com/open-telemetry/opentelemetry-dotnet/issues/480 * displaying span status on zpages * Adding Span Error Count and fixing build failure * Adding Span Latency to zpages http server * Adding class for storing span information * Adding methods for calculating latency, count and error count values in last hour and last minutes * Building HTML for zpage and adding timer for calculating metrics * Implementation changes - Added ZPages Span Processor - Changed the logic for metrics calculation * Update src/OpenTelemetry.Exporter.ZPages/Implementation/ZPagesStatsBuilder.cs Co-authored-by: Sergey Kanzhelev <S.Kanzhelev@live.com> * Incoroporating Review Comments Co-authored-by: Sergey Kanzhelev <S.Kanzhelev@live.com> Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
parent
4ed65d3ddd
commit
0867c66a0f
|
|
@ -17,6 +17,7 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using OpenTelemetry.Exporter.ZPages;
|
||||
using OpenTelemetry.Exporter.ZPages.Implementation;
|
||||
using OpenTelemetry.Trace;
|
||||
using OpenTelemetry.Trace.Configuration;
|
||||
using OpenTelemetry.Trace.Export;
|
||||
|
|
@ -29,28 +30,49 @@ namespace Samples
|
|||
{
|
||||
var zpagesOptions = new ZPagesExporterOptions() { Url = "http://localhost:7284/rpcz/" };
|
||||
var zpagesExporter = new ZPagesExporter(zpagesOptions);
|
||||
var spanProcessor = new SimpleSpanProcessor(zpagesExporter);
|
||||
var spanProcessor = new ZPagesSpanProcessor(zpagesExporter);
|
||||
ZPagesSpans.RetentionTime = 3600000;
|
||||
var httpServer = new ZPagesExporterStatsHttpServer(zpagesExporter, spanProcessor);
|
||||
|
||||
// Start the server
|
||||
httpServer.Start();
|
||||
|
||||
// Configure exporter
|
||||
using var tracerFactory = TracerFactory.Create(builder => builder
|
||||
using (var tracerFactory = TracerFactory.Create(builder => builder
|
||||
.AddProcessorPipeline(b => b
|
||||
.SetExporter(zpagesExporter)
|
||||
.SetExportingProcessor(e => spanProcessor)));
|
||||
var tracer = tracerFactory.GetTracer("zpages-test");
|
||||
|
||||
while (true)
|
||||
.SetExportingProcessor(e => spanProcessor))))
|
||||
{
|
||||
// Create a scoped span. It will end automatically when using statement ends
|
||||
using (tracer.WithSpan(tracer.StartSpan("Main")))
|
||||
{
|
||||
Console.WriteLine("Starting Span");
|
||||
}
|
||||
var tracer = tracerFactory.GetTracer("zpages-test");
|
||||
|
||||
Thread.Sleep(500);
|
||||
while (true)
|
||||
{
|
||||
// Create a scoped span.
|
||||
TelemetrySpan telemetrySpan = tracer.StartSpan("Main");
|
||||
telemetrySpan.Status = Status.Unavailable;
|
||||
|
||||
using (tracer.WithSpan(telemetrySpan))
|
||||
{
|
||||
Console.WriteLine("Starting Span");
|
||||
}
|
||||
|
||||
Thread.Sleep(3000);
|
||||
|
||||
telemetrySpan.End();
|
||||
|
||||
// Create a scoped span.
|
||||
TelemetrySpan telemetrySpan2 = tracer.StartSpan("TestSpan");
|
||||
telemetrySpan2.Status = Status.Ok;
|
||||
|
||||
using (tracer.WithSpan(telemetrySpan2))
|
||||
{
|
||||
Console.WriteLine("Starting Span2");
|
||||
}
|
||||
|
||||
Thread.Sleep(5000);
|
||||
|
||||
telemetrySpan2.End();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
// <copyright file="ZPagesSpanInformation.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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using OpenTelemetry.Trace.Export;
|
||||
|
||||
namespace OpenTelemetry.Exporter.ZPages.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the span information aggregated according to span name.
|
||||
/// </summary>
|
||||
public class ZPagesSpanInformation
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ZPagesSpanInformation"/> class.
|
||||
/// </summary>
|
||||
public ZPagesSpanInformation()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ZPagesSpanInformation"/> class when span data is provided.
|
||||
/// </summary>
|
||||
/// <param name="spanData">Input span data to be used for initialization.</param>
|
||||
public ZPagesSpanInformation(SpanData spanData)
|
||||
{
|
||||
this.Name = spanData.Name;
|
||||
this.Count = 1;
|
||||
this.EndedCount = 0;
|
||||
this.LastUpdated = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds();
|
||||
this.TotalLatency = 0;
|
||||
this.AvgLatencyTotal = 0;
|
||||
this.ErrorCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the span.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total count of the span.
|
||||
/// </summary>
|
||||
public long Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total count of the ended span.
|
||||
/// </summary>
|
||||
public long EndedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total error count of the span.
|
||||
/// </summary>
|
||||
public long ErrorCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total average latency of the span.
|
||||
/// </summary>
|
||||
public long AvgLatencyTotal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total latency of all spans.
|
||||
/// </summary>
|
||||
public long TotalLatency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last updated timestamp.
|
||||
/// </summary>
|
||||
public long LastUpdated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculates and returns the total average latency.
|
||||
/// </summary>
|
||||
/// <returns>Total average latency.</returns>
|
||||
public long GetTotalAverageLatency()
|
||||
{
|
||||
this.AvgLatencyTotal = this.TotalLatency / this.EndedCount;
|
||||
this.LastUpdated = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds();
|
||||
return this.AvgLatencyTotal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// <copyright file="ZPagesSpans.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.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Timers;
|
||||
|
||||
namespace OpenTelemetry.Exporter.ZPages.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the ZPages Span Queue which stores all the required span information.
|
||||
/// </summary>
|
||||
public static class ZPagesSpans
|
||||
{
|
||||
private static long startTime;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes static members of the <see cref="ZPagesSpans"/> class.
|
||||
/// </summary>
|
||||
static ZPagesSpans()
|
||||
{
|
||||
ZQueue = new LinkedList<Dictionary<string, ZPagesSpanInformation>>();
|
||||
ProcessingSpanList = new Dictionary<string, long>();
|
||||
CurrentMinuteSpanList = new ConcurrentDictionary<string, ZPagesSpanInformation>();
|
||||
CurrentHourSpanList = new ConcurrentDictionary<string, ZPagesSpanInformation>();
|
||||
TotalSpanCount = new Dictionary<string, long>();
|
||||
TotalEndedSpanCount = new Dictionary<string, long>();
|
||||
TotalSpanErrorCount = new Dictionary<string, long>();
|
||||
TotalSpanLatency = new Dictionary<string, long>();
|
||||
startTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets ZQueue which stores the minute-wise span information.
|
||||
/// </summary>
|
||||
public static LinkedList<Dictionary<string, ZPagesSpanInformation>> ZQueue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current minute span information list.
|
||||
/// </summary>
|
||||
public static ConcurrentDictionary<string, ZPagesSpanInformation> CurrentMinuteSpanList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current hour span information list.
|
||||
/// </summary>
|
||||
public static ConcurrentDictionary<string, ZPagesSpanInformation> CurrentHourSpanList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the processing span information list. This holds the names of the spans which have not ended yet, along with the active count.
|
||||
/// </summary>
|
||||
public static Dictionary<string, long> ProcessingSpanList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the count of spans name-wise.
|
||||
/// </summary>
|
||||
public static Dictionary<string, long> TotalSpanCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the count of ended spans name-wise.
|
||||
/// </summary>
|
||||
public static Dictionary<string, long> TotalEndedSpanCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the count of span errors according to span name.
|
||||
/// </summary>
|
||||
public static Dictionary<string, long> TotalSpanErrorCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total latency of spans name-wise.
|
||||
/// </summary>
|
||||
public static Dictionary<string, long> TotalSpanLatency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the retention time (in milliseconds) for the metrics.
|
||||
/// </summary>
|
||||
public static long RetentionTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggers Calculations every minute.
|
||||
/// </summary>
|
||||
/// <param name="source">Source.</param>
|
||||
/// <param name="e">Event Arguments.</param>
|
||||
public static void PurgeCurrentMinuteData(object source, ElapsedEventArgs e)
|
||||
{
|
||||
// Enqueue the last minute's span information list to ZQueue
|
||||
ZQueue.AddFirst(new Dictionary<string, ZPagesSpanInformation>(CurrentMinuteSpanList));
|
||||
|
||||
// Clear the current minute span list to start recording new spans only
|
||||
CurrentMinuteSpanList.Clear();
|
||||
|
||||
// Remove the stale span information which is at the end of the list
|
||||
if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - startTime >= RetentionTime)
|
||||
{
|
||||
ZQueue.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers Calculations every hour.
|
||||
/// </summary>
|
||||
/// <param name="source">Source.</param>
|
||||
/// <param name="e">Event Arguments.</param>
|
||||
public static void PurgeCurrentHourData(object source, ElapsedEventArgs e)
|
||||
{
|
||||
// Clear the last hour's span information list
|
||||
CurrentHourSpanList.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,6 @@ namespace OpenTelemetry.Exporter.ZPages.Implementation
|
|||
{
|
||||
internal class ZPagesStatsBuilder
|
||||
{
|
||||
public const string ContentType = "text/plain; version = 0.0.4";
|
||||
public const string ContentType = "text/html";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,16 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using OpenTelemetry.Exporter.ZPages.Implementation;
|
||||
using OpenTelemetry.Trace.Export;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace OpenTelemetry.Exporter.ZPages
|
||||
{
|
||||
|
|
@ -26,6 +32,8 @@ namespace OpenTelemetry.Exporter.ZPages
|
|||
public class ZPagesExporter : SpanExporter
|
||||
{
|
||||
internal readonly ZPagesExporterOptions Options;
|
||||
private Timer minuteTimer;
|
||||
private Timer hourTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ZPagesExporter"/> class.
|
||||
|
|
@ -34,11 +42,22 @@ namespace OpenTelemetry.Exporter.ZPages
|
|||
public ZPagesExporter(ZPagesExporterOptions options)
|
||||
{
|
||||
this.Options = options;
|
||||
|
||||
// Create a timer with one minute interval
|
||||
this.minuteTimer = new Timer(60000);
|
||||
this.minuteTimer.Elapsed += new ElapsedEventHandler(ZPagesSpans.PurgeCurrentMinuteData);
|
||||
this.minuteTimer.Enabled = true;
|
||||
|
||||
// Create a timer with one hour interval
|
||||
this.hourTimer = new Timer(3600000);
|
||||
this.hourTimer.Elapsed += new ElapsedEventHandler(ZPagesSpans.PurgeCurrentHourData);
|
||||
this.hourTimer.Enabled = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<ExportResult> ExportAsync(IEnumerable<SpanData> batch, CancellationToken cancellationToken)
|
||||
{
|
||||
var spanDatas = batch as SpanData[] ?? batch.ToArray();
|
||||
return Task.FromResult(ExportResult.Success);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,11 @@
|
|||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTelemetry.Exporter.ZPages.Implementation;
|
||||
|
|
@ -30,7 +33,7 @@ namespace OpenTelemetry.Exporter.ZPages
|
|||
public class ZPagesExporterStatsHttpServer : IDisposable
|
||||
{
|
||||
private readonly ZPagesExporter exporter;
|
||||
private readonly SimpleSpanProcessor spanProcessor;
|
||||
private readonly ZPagesSpanProcessor spanProcessor;
|
||||
private readonly HttpListener httpListener = new HttpListener();
|
||||
private readonly object lck = new object();
|
||||
|
||||
|
|
@ -42,7 +45,7 @@ namespace OpenTelemetry.Exporter.ZPages
|
|||
/// </summary>
|
||||
/// <param name="exporter">The <see cref="ZPagesExporterStatsHttpServer"/> instance.</param>
|
||||
/// <param name="spanProcessor">The <see cref="SimpleSpanProcessor"/> instance.</param>
|
||||
public ZPagesExporterStatsHttpServer(ZPagesExporter exporter, SimpleSpanProcessor spanProcessor)
|
||||
public ZPagesExporterStatsHttpServer(ZPagesExporter exporter, ZPagesSpanProcessor spanProcessor)
|
||||
{
|
||||
this.exporter = exporter;
|
||||
this.spanProcessor = spanProcessor;
|
||||
|
|
@ -52,7 +55,7 @@ namespace OpenTelemetry.Exporter.ZPages
|
|||
/// <summary>
|
||||
/// Start exporter.
|
||||
/// </summary>
|
||||
/// <param name="token">An optional <see cref="CancellationToken"/> that can be used to stop the htto server.</param>
|
||||
/// <param name="token">An optional <see cref="CancellationToken"/> that can be used to stop the http server.</param>
|
||||
public void Start(CancellationToken token = default)
|
||||
{
|
||||
lock (this.lck)
|
||||
|
|
@ -108,7 +111,7 @@ namespace OpenTelemetry.Exporter.ZPages
|
|||
{
|
||||
while (!this.tokenSource.IsCancellationRequested)
|
||||
{
|
||||
var ctxTask = this.httpListener.GetContextAsync();
|
||||
Task<HttpListenerContext> ctxTask = this.httpListener.GetContextAsync();
|
||||
ctxTask.Wait(this.tokenSource.Token);
|
||||
|
||||
var ctx = ctxTask.Result;
|
||||
|
|
@ -116,9 +119,62 @@ namespace OpenTelemetry.Exporter.ZPages
|
|||
ctx.Response.StatusCode = 200;
|
||||
ctx.Response.ContentType = ZPagesStatsBuilder.ContentType;
|
||||
|
||||
using var output = ctx.Response.OutputStream;
|
||||
using var writer = new StreamWriter(output);
|
||||
writer.WriteLine("Span Count : " + this.spanProcessor.GetSpanCount());
|
||||
using (Stream output = ctx.Response.OutputStream)
|
||||
{
|
||||
using (var writer = new StreamWriter(output))
|
||||
{
|
||||
writer.WriteLine("<!DOCTYPE html>");
|
||||
writer.WriteLine("<html><head><title>RPC Stats</title>" +
|
||||
"<meta charset=\"utf-8\">" +
|
||||
"<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css\">" +
|
||||
"<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>" +
|
||||
"<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js\"></script>" +
|
||||
"</head>");
|
||||
writer.WriteLine("<body><div class=\"col-sm-1\"></div><div class=\"container col-sm-10\"><div class=\"jumbotron table-responsive\"><h1>RPC Stats</h2>" +
|
||||
"<table class=\"table table-bordered table-hover table-striped\">" +
|
||||
"<thead><tr><th>Span Name</th><th>Total Count</th><th>Count in last minute</th><th>Count in last hour</th><th>Average Latency (ms)</th>" +
|
||||
"<th>Average Latency in last minute (ms)</th><th>Average Latency in last hour (ms)</th><th>Total Errors</th><th>Errors in last minute</th><th>Errors in last minute</th><th>Last Updated</th></tr></thead>" +
|
||||
"<tbody>");
|
||||
|
||||
ConcurrentDictionary<string, ZPagesSpanInformation> currentHourSpanList = ZPagesSpans.CurrentHourSpanList;
|
||||
ConcurrentDictionary<string, ZPagesSpanInformation> currentMinuteSpanList = ZPagesSpans.CurrentMinuteSpanList;
|
||||
|
||||
// Put span information in each row of the table
|
||||
foreach (var spanName in currentHourSpanList.Keys)
|
||||
{
|
||||
ZPagesSpanInformation minuteSpanInformation = new ZPagesSpanInformation();
|
||||
ZPagesSpanInformation hourSpanInformation = new ZPagesSpanInformation();
|
||||
long countInLastMinute = 0;
|
||||
long countInLastHour = 0;
|
||||
long averageLatencyInLastMinute = 0;
|
||||
long averageLatencyInLastHour = 0;
|
||||
long errorCountInLastMinute = 0;
|
||||
long errorCountInLastHour = 0;
|
||||
|
||||
if (currentMinuteSpanList.ContainsKey(spanName))
|
||||
{
|
||||
currentMinuteSpanList.TryGetValue(spanName, out minuteSpanInformation);
|
||||
countInLastMinute = minuteSpanInformation.EndedCount + ZPagesSpans.ProcessingSpanList[spanName];
|
||||
averageLatencyInLastMinute = minuteSpanInformation.AvgLatencyTotal;
|
||||
errorCountInLastMinute = minuteSpanInformation.ErrorCount;
|
||||
}
|
||||
|
||||
currentHourSpanList.TryGetValue(spanName, out hourSpanInformation);
|
||||
countInLastHour = hourSpanInformation.EndedCount + ZPagesSpans.ProcessingSpanList[spanName];
|
||||
averageLatencyInLastHour = hourSpanInformation.AvgLatencyTotal;
|
||||
errorCountInLastHour = hourSpanInformation.ErrorCount;
|
||||
|
||||
long totalAverageLatency = ZPagesSpans.TotalSpanLatency[spanName] / ZPagesSpans.TotalEndedSpanCount[spanName];
|
||||
|
||||
writer.WriteLine("<tr><td>" + hourSpanInformation.Name + "</td><td>" + ZPagesSpans.TotalSpanCount[spanName] + "</td><td>" + countInLastMinute + "</td><td>" + countInLastHour + "</td>" +
|
||||
"<td>" + totalAverageLatency + "</td><td>" + averageLatencyInLastMinute + "</td><td>" + averageLatencyInLastHour + "</td>" +
|
||||
"<td>" + ZPagesSpans.TotalSpanErrorCount[spanName] + "</td><td>" + errorCountInLastMinute + "</td><td>" + errorCountInLastHour + "</td><td>" + DateTimeOffset.FromUnixTimeMilliseconds(hourSpanInformation.LastUpdated) + " GMT" + "</td></tr>");
|
||||
}
|
||||
|
||||
writer.WriteLine("</tbody></table>");
|
||||
writer.WriteLine("</div></div></body></html>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
// <copyright file="ZPagesSpanProcessor.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.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using OpenTelemetry.Exporter.ZPages.Implementation;
|
||||
using OpenTelemetry.Trace.Export;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace OpenTelemetry.Exporter.ZPages
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the zpages span processor that exports spans in OnEnd call without batching.
|
||||
/// </summary>
|
||||
public class ZPagesSpanProcessor : SpanProcessor
|
||||
{
|
||||
private readonly ZPagesExporter exporter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ZPagesSpanProcessor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="exporter">Zpage Span processor instance.</param>
|
||||
public ZPagesSpanProcessor(ZPagesExporter exporter)
|
||||
{
|
||||
this.exporter = exporter;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnStart(SpanData span)
|
||||
{
|
||||
if (!ZPagesSpans.ProcessingSpanList.ContainsKey(span.Name))
|
||||
{
|
||||
// If the span name is not in the processing span list, add it to the span list, the total count list, the ended count list and the error count list.
|
||||
ZPagesSpans.ProcessingSpanList.Add(span.Name, 1);
|
||||
ZPagesSpans.TotalSpanCount.Add(span.Name, 1);
|
||||
ZPagesSpans.TotalEndedSpanCount.Add(span.Name, 0);
|
||||
ZPagesSpans.TotalSpanErrorCount.Add(span.Name, 0);
|
||||
ZPagesSpans.TotalSpanLatency.Add(span.Name, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the span name already exists, then increment the numbers in processing list as well as the total count list.
|
||||
ZPagesSpans.ProcessingSpanList.TryGetValue(span.Name, out var activeCount);
|
||||
ZPagesSpans.ProcessingSpanList[span.Name] = activeCount + 1;
|
||||
ZPagesSpans.TotalSpanCount.TryGetValue(span.Name, out var totalCount);
|
||||
ZPagesSpans.TotalSpanCount[span.Name] = totalCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEnd(SpanData span)
|
||||
{
|
||||
try
|
||||
{
|
||||
// do not await, just start export
|
||||
// it can still throw in synchronous part
|
||||
_ = this.exporter.ExportAsync(new[] { span }, CancellationToken.None);
|
||||
|
||||
// If the span name is not in the current minute list, add it to the span list.
|
||||
if (!ZPagesSpans.CurrentMinuteSpanList.ContainsKey(span.Name))
|
||||
{
|
||||
ZPagesSpans.CurrentMinuteSpanList.TryAdd(span.Name, new ZPagesSpanInformation(span));
|
||||
}
|
||||
|
||||
// If the span name is not in the current hour list, add it to the span list.
|
||||
if (!ZPagesSpans.CurrentHourSpanList.ContainsKey(span.Name))
|
||||
{
|
||||
ZPagesSpans.CurrentHourSpanList.TryAdd(span.Name, new ZPagesSpanInformation(span));
|
||||
}
|
||||
|
||||
ZPagesSpans.CurrentMinuteSpanList.TryGetValue(span.Name, out var minuteSpanInformation);
|
||||
ZPagesSpans.CurrentHourSpanList.TryGetValue(span.Name, out var hourSpanInformation);
|
||||
ZPagesSpans.ProcessingSpanList.TryGetValue(span.Name, out var activeCount);
|
||||
|
||||
// Decrement the active span count in processing list, Increment the count of ended spans and calculate the average latency values for one minute and one hour.
|
||||
ZPagesSpans.ProcessingSpanList[span.Name] = activeCount - 1;
|
||||
minuteSpanInformation.EndedCount++;
|
||||
hourSpanInformation.EndedCount++;
|
||||
minuteSpanInformation.TotalLatency += span.EndTimestamp.ToUnixTimeMilliseconds() - span.StartTimestamp.ToUnixTimeMilliseconds();
|
||||
hourSpanInformation.TotalLatency += span.EndTimestamp.ToUnixTimeMilliseconds() - span.StartTimestamp.ToUnixTimeMilliseconds();
|
||||
ZPagesSpans.TotalSpanLatency[span.Name] += span.EndTimestamp.ToUnixTimeMilliseconds() - span.StartTimestamp.ToUnixTimeMilliseconds();
|
||||
minuteSpanInformation.GetTotalAverageLatency();
|
||||
hourSpanInformation.GetTotalAverageLatency();
|
||||
ZPagesSpans.TotalEndedSpanCount.TryGetValue(span.Name, out var endedCount);
|
||||
ZPagesSpans.TotalEndedSpanCount[span.Name] = endedCount + 1;
|
||||
|
||||
// Increment the error count, if it applies in all applicable lists.
|
||||
if (!span.Status.IsOk)
|
||||
{
|
||||
minuteSpanInformation.ErrorCount++;
|
||||
hourSpanInformation.ErrorCount++;
|
||||
ZPagesSpans.TotalSpanErrorCount.TryGetValue(span.Name, out var errorCount);
|
||||
ZPagesSpans.TotalSpanErrorCount[span.Name] = errorCount + 1;
|
||||
}
|
||||
|
||||
// Set the last updated timestamp.
|
||||
minuteSpanInformation.LastUpdated = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds();
|
||||
hourSpanInformation.LastUpdated = new DateTimeOffset(DateTime.Now).ToUnixTimeMilliseconds();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// Log.SpanProcessorException("OnEnd", ex);
|
||||
Console.Write("OnEnd", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task ShutdownAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTelemetry.Internal;
|
||||
|
|
@ -27,7 +29,6 @@ namespace OpenTelemetry.Trace.Export
|
|||
{
|
||||
private readonly SpanExporter exporter;
|
||||
private bool disposed = false;
|
||||
private long spanCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SimpleSpanProcessor"/> class.
|
||||
|
|
@ -38,15 +39,9 @@ namespace OpenTelemetry.Trace.Export
|
|||
this.exporter = exporter ?? throw new ArgumentNullException(nameof(exporter));
|
||||
}
|
||||
|
||||
public long GetSpanCount()
|
||||
{
|
||||
return this.spanCount;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnStart(SpanData span)
|
||||
{
|
||||
Interlocked.Increment(ref this.spanCount);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
Loading…
Reference in New Issue