Add an InMemoryExporter<T> (#1335)
* skeleton of the in-memory exporter
* add log helper
* configuration
* simple
* update the test cases
* mdl
* simplify readme
* better names
* minor tweak on the flow
* contravariance
* Revert "contravariance"
This reverts commit 94a474f46c.
This commit is contained in:
parent
e7b61e3938
commit
4ce2beccdd
|
|
@ -0,0 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
* Initial release
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// <copyright file="InMemoryExporter.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 OpenTelemetry;
|
||||
|
||||
namespace OpenTelemetry.Exporter
|
||||
{
|
||||
public class InMemoryExporter<T> : BaseExporter<T>
|
||||
where T : class
|
||||
{
|
||||
private readonly ICollection<object> exportedItems;
|
||||
|
||||
public InMemoryExporter(InMemoryExporterOptions options = null)
|
||||
{
|
||||
this.exportedItems = options?.ExportedItems;
|
||||
}
|
||||
|
||||
public override ExportResult Export(in Batch<T> batch)
|
||||
{
|
||||
if (this.exportedItems == null)
|
||||
{
|
||||
return ExportResult.Failure;
|
||||
}
|
||||
|
||||
foreach (var data in batch)
|
||||
{
|
||||
this.exportedItems.Add(data);
|
||||
}
|
||||
|
||||
return ExportResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// <copyright file="InMemoryExporterHelperExtensions.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 OpenTelemetry.Exporter;
|
||||
#if NETSTANDARD2_0
|
||||
using OpenTelemetry.Logs;
|
||||
#endif
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace OpenTelemetry
|
||||
{
|
||||
public static class InMemoryExporterHelperExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds InMemory exporter to the TracerProvider.
|
||||
/// </summary>
|
||||
/// <param name="builder"><see cref="TracerProviderBuilder"/> builder to use.</param>
|
||||
/// <param name="configure">Exporter configuration options.</param>
|
||||
/// <returns>The instance of <see cref="TracerProviderBuilder"/> to chain the calls.</returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The objects should not be disposed.")]
|
||||
public static TracerProviderBuilder AddInMemoryExporter(this TracerProviderBuilder builder, Action<InMemoryExporterOptions> configure = null)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
var options = new InMemoryExporterOptions();
|
||||
configure?.Invoke(options);
|
||||
return builder.AddProcessor(new SimpleExportProcessor<Activity>(new InMemoryExporter<Activity>(options)));
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
public static OpenTelemetryLoggerOptions AddInMemoryExporter(this OpenTelemetryLoggerOptions loggerOptions, Action<InMemoryExporterOptions> configure = null)
|
||||
{
|
||||
if (loggerOptions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerOptions));
|
||||
}
|
||||
|
||||
var options = new InMemoryExporterOptions();
|
||||
configure?.Invoke(options);
|
||||
|
||||
return loggerOptions.AddProcessor(new SimpleExportProcessor<LogRecord>(new InMemoryExporter<LogRecord>(options)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// <copyright file="InMemoryExporterOptions.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.Collections.Generic;
|
||||
|
||||
namespace OpenTelemetry.Exporter
|
||||
{
|
||||
public class InMemoryExporterOptions
|
||||
{
|
||||
public ICollection<object> ExportedItems { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net452;net46;netstandard2.0</TargetFrameworks>
|
||||
<Description>In-memory exporter for OpenTelemetry .NET</Description>
|
||||
<PackageTags>$(PackageTags)</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn),1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# In-memory Exporter for OpenTelemetry .NET
|
||||
|
||||
[](https://www.nuget.org/packages/OpenTelemetry.Exporter.InMemory)
|
||||
[](https://www.nuget.org/packages/OpenTelemetry.Exporter.InMemory)
|
||||
|
||||
The in-memory exporter stores data in a user provided memory buffer.
|
||||
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
dotnet add package OpenTelemetry.Exporter.InMemory
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```csharp
|
||||
var list = new List<object>();
|
||||
var activityExporter = new InMemoryExporter<Activity>(
|
||||
new InMemoryExporterOptions { ExportedItems = list });
|
||||
var logExporter = new InMemoryExporter<LogRecord>(
|
||||
new InMemoryExporterOptions { ExportedItems = list });
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
* [OpenTelemetry Project](https://opentelemetry.io/)
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.InMemory\OpenTelemetry.Exporter.InMemory.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
// <copyright file="TestActivityExporter.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.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace OpenTelemetry.Tests.Shared
|
||||
{
|
||||
internal class TestActivityExporter : BaseExporter<Activity>
|
||||
{
|
||||
internal readonly BlockingCollection<Activity> Exported = new BlockingCollection<Activity>();
|
||||
private bool disposed;
|
||||
|
||||
public override ExportResult Export(in Batch<Activity> batch)
|
||||
{
|
||||
foreach (var activity in batch)
|
||||
{
|
||||
this.Exported.Add(activity);
|
||||
}
|
||||
|
||||
return ExportResult.Success;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!this.disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
this.Exported.Dispose();
|
||||
}
|
||||
|
||||
this.disposed = true;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,9 +15,10 @@
|
|||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using OpenTelemetry.Tests.Shared;
|
||||
using OpenTelemetry.Exporter;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Trace.Tests
|
||||
|
|
@ -33,17 +34,18 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[Fact]
|
||||
public void CheckConstructorWithInvalidValues()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new TestActivityExporter(), maxQueueSize: 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new TestActivityExporter(), maxExportBatchSize: 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new TestActivityExporter(), maxQueueSize: 1, maxExportBatchSize: 2049));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new TestActivityExporter(), scheduledDelayMilliseconds: 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new TestActivityExporter(), exporterTimeoutMilliseconds: -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new InMemoryExporter<Activity>(), maxQueueSize: 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new InMemoryExporter<Activity>(), maxExportBatchSize: 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new InMemoryExporter<Activity>(), maxQueueSize: 1, maxExportBatchSize: 2049));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new InMemoryExporter<Activity>(), scheduledDelayMilliseconds: 0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BatchExportProcessor<Activity>(new InMemoryExporter<Activity>(), exporterTimeoutMilliseconds: -1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckIfBatchIsExportingOnQueueLimit()
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
var exportedItems = new List<object>();
|
||||
using var exporter = new InMemoryExporter<Activity>(new InMemoryExporterOptions { ExportedItems = exportedItems });
|
||||
using var processor = new BatchExportProcessor<Activity>(
|
||||
exporter,
|
||||
maxQueueSize: 1,
|
||||
|
|
@ -52,12 +54,12 @@ namespace OpenTelemetry.Trace.Tests
|
|||
|
||||
processor.OnEnd(new Activity("start"));
|
||||
|
||||
for (int i = 0; i < 10 && exporter.Exported.Count == 0; i++)
|
||||
for (int i = 0; i < 10 && exportedItems.Count == 0; i++)
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
|
||||
Assert.Single(exporter.Exported);
|
||||
Assert.Single(exportedItems);
|
||||
|
||||
Assert.Equal(1, processor.ProcessedCount);
|
||||
Assert.Equal(1, processor.ReceivedCount);
|
||||
|
|
@ -67,7 +69,7 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[Fact]
|
||||
public void CheckForceFlushWithInvalidTimeout()
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
using var exporter = new InMemoryExporter<Activity>();
|
||||
using var processor = new BatchExportProcessor<Activity>(exporter, maxQueueSize: 2, maxExportBatchSize: 1);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => processor.ForceFlush(-2));
|
||||
}
|
||||
|
|
@ -78,7 +80,8 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[InlineData(1)]
|
||||
public void CheckForceFlushExport(int timeout)
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
var exportedItems = new List<object>();
|
||||
using var exporter = new InMemoryExporter<Activity>(new InMemoryExporterOptions { ExportedItems = exportedItems });
|
||||
using var processor = new BatchExportProcessor<Activity>(
|
||||
exporter,
|
||||
maxQueueSize: 3,
|
||||
|
|
@ -92,7 +95,7 @@ namespace OpenTelemetry.Trace.Tests
|
|||
|
||||
// waiting to see if time is triggering the exporter
|
||||
Thread.Sleep(1_000);
|
||||
Assert.Empty(exporter.Exported);
|
||||
Assert.Empty(exportedItems);
|
||||
|
||||
// forcing flush
|
||||
processor.ForceFlush(timeout);
|
||||
|
|
@ -103,7 +106,7 @@ namespace OpenTelemetry.Trace.Tests
|
|||
Thread.Sleep(1_000);
|
||||
}
|
||||
|
||||
Assert.Equal(2, exporter.Exported.Count);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
Assert.Equal(2, processor.ProcessedCount);
|
||||
Assert.Equal(2, processor.ReceivedCount);
|
||||
|
|
@ -116,7 +119,8 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[InlineData(1)]
|
||||
public void CheckShutdownExport(int timeout)
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
var exportedItems = new List<object>();
|
||||
using var exporter = new InMemoryExporter<Activity>(new InMemoryExporterOptions { ExportedItems = exportedItems });
|
||||
using var processor = new BatchExportProcessor<Activity>(
|
||||
exporter,
|
||||
maxQueueSize: 3,
|
||||
|
|
@ -132,7 +136,7 @@ namespace OpenTelemetry.Trace.Tests
|
|||
Thread.Sleep(1_000);
|
||||
}
|
||||
|
||||
Assert.Single(exporter.Exported);
|
||||
Assert.Single(exportedItems);
|
||||
|
||||
Assert.Equal(1, processor.ProcessedCount);
|
||||
Assert.Equal(1, processor.ReceivedCount);
|
||||
|
|
|
|||
|
|
@ -15,9 +15,10 @@
|
|||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using OpenTelemetry.Tests.Shared;
|
||||
using OpenTelemetry.Exporter;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Trace.Tests
|
||||
|
|
@ -33,14 +34,15 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[Fact]
|
||||
public void CheckExportedOnEnd()
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
var exportedItems = new List<object>();
|
||||
using var exporter = new InMemoryExporter<Activity>(new InMemoryExporterOptions { ExportedItems = exportedItems });
|
||||
using var processor = new ReentrantExportProcessor<Activity>(exporter);
|
||||
|
||||
processor.OnEnd(new Activity("start1"));
|
||||
Assert.Single(exporter.Exported);
|
||||
Assert.Single(exportedItems);
|
||||
|
||||
processor.OnEnd(new Activity("start2"));
|
||||
Assert.Equal(2, exporter.Exported.Count);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -49,18 +51,19 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[InlineData(1)]
|
||||
public void CheckForceFlushExport(int timeout)
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
var exportedItems = new List<object>();
|
||||
using var exporter = new InMemoryExporter<Activity>(new InMemoryExporterOptions { ExportedItems = exportedItems });
|
||||
using var processor = new ReentrantExportProcessor<Activity>(exporter);
|
||||
|
||||
processor.OnEnd(new Activity("start1"));
|
||||
processor.OnEnd(new Activity("start2"));
|
||||
|
||||
// checking before force flush
|
||||
Assert.Equal(2, exporter.Exported.Count);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
|
||||
// forcing flush
|
||||
processor.ForceFlush(timeout);
|
||||
Assert.Equal(2, exporter.Exported.Count);
|
||||
Assert.Equal(2, exportedItems.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -69,16 +72,17 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[InlineData(1)]
|
||||
public void CheckShutdownExport(int timeout)
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
var exportedItems = new List<object>();
|
||||
using var exporter = new InMemoryExporter<Activity>(new InMemoryExporterOptions { ExportedItems = exportedItems });
|
||||
using var processor = new ReentrantExportProcessor<Activity>(exporter);
|
||||
|
||||
processor.OnEnd(new Activity("start"));
|
||||
|
||||
// checking before shutdown
|
||||
Assert.Single(exporter.Exported);
|
||||
Assert.Single(exportedItems);
|
||||
|
||||
processor.Shutdown(timeout);
|
||||
Assert.Single(exporter.Exported);
|
||||
Assert.Single(exportedItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,10 @@
|
|||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using OpenTelemetry.Tests.Shared;
|
||||
using OpenTelemetry.Exporter;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Trace.Tests
|
||||
|
|
@ -33,14 +34,15 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[Fact]
|
||||
public void CheckExportedOnEnd()
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
var exported = new List<object>();
|
||||
using var exporter = new InMemoryExporter<Activity>(new InMemoryExporterOptions { ExportedItems = exported });
|
||||
using var processor = new SimpleExportProcessor<Activity>(exporter);
|
||||
|
||||
processor.OnEnd(new Activity("start1"));
|
||||
Assert.Single(exporter.Exported);
|
||||
Assert.Single(exported);
|
||||
|
||||
processor.OnEnd(new Activity("start2"));
|
||||
Assert.Equal(2, exporter.Exported.Count);
|
||||
Assert.Equal(2, exported.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -49,18 +51,19 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[InlineData(1)]
|
||||
public void CheckForceFlushExport(int timeout)
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
var exported = new List<object>();
|
||||
using var exporter = new InMemoryExporter<Activity>(new InMemoryExporterOptions { ExportedItems = exported });
|
||||
using var processor = new SimpleExportProcessor<Activity>(exporter);
|
||||
|
||||
processor.OnEnd(new Activity("start1"));
|
||||
processor.OnEnd(new Activity("start2"));
|
||||
|
||||
// checking before force flush
|
||||
Assert.Equal(2, exporter.Exported.Count);
|
||||
Assert.Equal(2, exported.Count);
|
||||
|
||||
// forcing flush
|
||||
processor.ForceFlush(timeout);
|
||||
Assert.Equal(2, exporter.Exported.Count);
|
||||
Assert.Equal(2, exported.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -69,16 +72,17 @@ namespace OpenTelemetry.Trace.Tests
|
|||
[InlineData(1)]
|
||||
public void CheckShutdownExport(int timeout)
|
||||
{
|
||||
using var exporter = new TestActivityExporter();
|
||||
var exported = new List<object>();
|
||||
using var exporter = new InMemoryExporter<Activity>(new InMemoryExporterOptions { ExportedItems = exported });
|
||||
using var processor = new SimpleExportProcessor<Activity>(exporter);
|
||||
|
||||
processor.OnEnd(new Activity("start"));
|
||||
|
||||
// checking before shutdown
|
||||
Assert.Single(exporter.Exported);
|
||||
Assert.Single(exported);
|
||||
|
||||
processor.Shutdown(timeout);
|
||||
Assert.Single(exporter.Exported);
|
||||
Assert.Single(exported);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue