mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
16 changed files with 343 additions and 525 deletions
@ -1,150 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using Lucene.Net.Store; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Driver; |
|||
using MongoDB.Driver.GridFS; |
|||
using LuceneDirectory = Lucene.Net.Store.Directory; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.FullText |
|||
{ |
|||
public sealed class MongoDirectory : BaseDirectory |
|||
{ |
|||
private readonly IGridFSBucket<string> bucket; |
|||
private readonly string directory; |
|||
private readonly DirectoryInfo cacheDirectoryInfo; |
|||
private readonly LuceneDirectory cacheDirectory; |
|||
private bool isDisposed; |
|||
|
|||
public LuceneDirectory CacheDirectory |
|||
{ |
|||
get { return cacheDirectory; } |
|||
} |
|||
|
|||
public DirectoryInfo CacheDirectoryInfo |
|||
{ |
|||
get { return cacheDirectoryInfo; } |
|||
} |
|||
|
|||
public IGridFSBucket<string> Bucket |
|||
{ |
|||
get { return bucket; } |
|||
} |
|||
|
|||
public MongoDirectory(IGridFSBucket<string> bucket, string directory, DirectoryInfo cacheDirectoryInfo) |
|||
{ |
|||
this.bucket = bucket; |
|||
|
|||
this.directory = directory; |
|||
|
|||
this.cacheDirectoryInfo = cacheDirectoryInfo; |
|||
|
|||
cacheDirectoryInfo.Create(); |
|||
cacheDirectory = FSDirectory.Open(cacheDirectoryInfo); |
|||
|
|||
SetLockFactory(new NativeFSLockFactory(cacheDirectoryInfo)); |
|||
} |
|||
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing) |
|||
{ |
|||
isDisposed = true; |
|||
|
|||
cacheDirectory.Dispose(); |
|||
} |
|||
} |
|||
|
|||
public override string GetLockID() |
|||
{ |
|||
return cacheDirectory.GetLockID(); |
|||
} |
|||
|
|||
public override IndexOutput CreateOutput(string name, IOContext context) |
|||
{ |
|||
return new MongoIndexOutput(this, context, name); |
|||
} |
|||
|
|||
public override IndexInput OpenInput(string name, IOContext context) |
|||
{ |
|||
return new MongoIndexInput(this, context, name); |
|||
} |
|||
|
|||
public override void DeleteFile(string name) |
|||
{ |
|||
EnsureNotDisposed(); |
|||
|
|||
var fullName = GetFullName(name); |
|||
|
|||
try |
|||
{ |
|||
Bucket.Delete(fullName); |
|||
} |
|||
catch (GridFSFileNotFoundException) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
public override long FileLength(string name) |
|||
{ |
|||
EnsureNotDisposed(); |
|||
|
|||
var file = FindFile(name) ?? throw new FileNotFoundException(null, GetFullName(name)); |
|||
|
|||
return file.Length; |
|||
} |
|||
|
|||
public override string[] ListAll() |
|||
{ |
|||
EnsureNotDisposed(); |
|||
|
|||
var files = Bucket.Find(Builders<GridFSFileInfo<string>>.Filter.Regex(x => x.Id, new BsonRegularExpression($"^{directory}/"))).ToList(); |
|||
|
|||
return files.Select(x => x.Filename).ToArray(); |
|||
} |
|||
|
|||
public GridFSFileInfo<string>? FindFile(string name) |
|||
{ |
|||
var fullName = GetFullName(name); |
|||
|
|||
return Bucket.Find(Builders<GridFSFileInfo<string>>.Filter.Eq(x => x.Id, fullName)).FirstOrDefault(); |
|||
} |
|||
|
|||
public override void Sync(ICollection<string> names) |
|||
{ |
|||
} |
|||
|
|||
[Obsolete] |
|||
public override bool FileExists(string name) |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
|
|||
public string GetFullName(string name) |
|||
{ |
|||
return $"{directory}/{name}"; |
|||
} |
|||
|
|||
public string GetFullPath(string name) |
|||
{ |
|||
return Path.Combine(cacheDirectoryInfo.FullName, name); |
|||
} |
|||
|
|||
private void EnsureNotDisposed() |
|||
{ |
|||
if (isDisposed) |
|||
{ |
|||
throw new ObjectDisposedException(GetType().FullName); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,105 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.IO; |
|||
using Lucene.Net.Store; |
|||
using MongoDB.Driver.GridFS; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.FullText |
|||
{ |
|||
public sealed class MongoIndexInput : IndexInput |
|||
{ |
|||
private readonly IndexInput cacheInput; |
|||
private readonly MongoDirectory indexDirectory; |
|||
private readonly IOContext context; |
|||
private readonly string indexFileName; |
|||
|
|||
public override long Length |
|||
{ |
|||
get { return cacheInput.Length; } |
|||
} |
|||
|
|||
public MongoIndexInput(MongoDirectory indexDirectory, IOContext context, string indexFileName) |
|||
: base(indexDirectory.GetFullName(indexFileName)) |
|||
{ |
|||
this.indexDirectory = indexDirectory; |
|||
this.indexFileName = indexFileName; |
|||
|
|||
this.context = context; |
|||
|
|||
try |
|||
{ |
|||
var file = indexDirectory.FindFile(indexFileName); |
|||
|
|||
if (file != null) |
|||
{ |
|||
var fileInfo = new FileInfo(indexDirectory.GetFullPath(indexFileName)); |
|||
|
|||
var writtenTime = file.Metadata["WrittenTime"].ToUniversalTime(); |
|||
|
|||
if (!fileInfo.Exists || fileInfo.LastWriteTimeUtc < writtenTime) |
|||
{ |
|||
using (var fs = new FileStream(fileInfo.FullName, FileMode.Create, FileAccess.Write)) |
|||
{ |
|||
var fullName = indexDirectory.GetFullName(indexFileName); |
|||
|
|||
indexDirectory.Bucket.DownloadToStream(fullName, fs); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
catch (GridFSFileNotFoundException) |
|||
{ |
|||
throw new FileNotFoundException(); |
|||
} |
|||
|
|||
cacheInput = indexDirectory.CacheDirectory.OpenInput(indexFileName, context); |
|||
} |
|||
|
|||
public MongoIndexInput(MongoIndexInput source) |
|||
: base("clone") |
|||
{ |
|||
cacheInput = (IndexInput)source.cacheInput.Clone(); |
|||
context = source.context; |
|||
indexDirectory = source.indexDirectory; |
|||
indexFileName = source.indexFileName; |
|||
} |
|||
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing) |
|||
{ |
|||
cacheInput.Dispose(); |
|||
} |
|||
} |
|||
|
|||
public override long GetFilePointer() |
|||
{ |
|||
return cacheInput.GetFilePointer(); |
|||
} |
|||
|
|||
public override byte ReadByte() |
|||
{ |
|||
return cacheInput.ReadByte(); |
|||
} |
|||
|
|||
public override void ReadBytes(byte[] b, int offset, int len) |
|||
{ |
|||
cacheInput.ReadBytes(b, offset, len); |
|||
} |
|||
|
|||
public override void Seek(long pos) |
|||
{ |
|||
cacheInput.Seek(pos); |
|||
} |
|||
|
|||
public override object Clone() |
|||
{ |
|||
return new MongoIndexInput(indexDirectory, context, indexFileName); |
|||
} |
|||
} |
|||
} |
|||
@ -1,114 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using Lucene.Net.Store; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Driver; |
|||
using MongoDB.Driver.GridFS; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.FullText |
|||
{ |
|||
public sealed class MongoIndexOutput : IndexOutput |
|||
{ |
|||
private readonly IndexOutput cacheOutput; |
|||
private readonly MongoDirectory indexDirectory; |
|||
private readonly string indexFileName; |
|||
private bool isFlushed; |
|||
private bool isWritten; |
|||
|
|||
public override long Length |
|||
{ |
|||
get { return cacheOutput.Length; } |
|||
} |
|||
|
|||
public override long Checksum |
|||
{ |
|||
get { return cacheOutput.Checksum; } |
|||
} |
|||
|
|||
public MongoIndexOutput(MongoDirectory indexDirectory, IOContext context, string indexFileName) |
|||
{ |
|||
this.indexDirectory = indexDirectory; |
|||
this.indexFileName = indexFileName; |
|||
|
|||
cacheOutput = indexDirectory.CacheDirectory.CreateOutput(indexFileName, context); |
|||
} |
|||
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing) |
|||
{ |
|||
Flush(); |
|||
|
|||
cacheOutput.Dispose(); |
|||
|
|||
if (isWritten && isFlushed) |
|||
{ |
|||
var fileInfo = new FileInfo(indexDirectory.GetFullPath(indexFileName)); |
|||
|
|||
using (var fs = new FileStream(indexDirectory.GetFullPath(indexFileName), FileMode.Open, FileAccess.Read)) |
|||
{ |
|||
var fullName = indexDirectory.GetFullName(indexFileName); |
|||
|
|||
var options = new GridFSUploadOptions |
|||
{ |
|||
Metadata = new BsonDocument |
|||
{ |
|||
["WrittenTime"] = fileInfo.LastWriteTimeUtc |
|||
} |
|||
}; |
|||
|
|||
try |
|||
{ |
|||
indexDirectory.Bucket.UploadFromStream(fullName, indexFileName, fs, options); |
|||
} |
|||
catch (MongoBulkWriteException ex) when (ex.WriteErrors.Any(x => x.Code == 11000)) |
|||
{ |
|||
indexDirectory.Bucket.Delete(fullName); |
|||
indexDirectory.Bucket.UploadFromStream(fullName, indexFileName, fs, options); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override long GetFilePointer() |
|||
{ |
|||
return cacheOutput.GetFilePointer(); |
|||
} |
|||
|
|||
public override void Flush() |
|||
{ |
|||
cacheOutput.Flush(); |
|||
|
|||
isFlushed = true; |
|||
} |
|||
|
|||
public override void WriteByte(byte b) |
|||
{ |
|||
cacheOutput.WriteByte(b); |
|||
|
|||
isWritten = true; |
|||
} |
|||
|
|||
public override void WriteBytes(byte[] b, int offset, int length) |
|||
{ |
|||
cacheOutput.WriteBytes(b, offset, length); |
|||
|
|||
isWritten = true; |
|||
} |
|||
|
|||
[Obsolete] |
|||
public override void Seek(long pos) |
|||
{ |
|||
cacheOutput.Seek(pos); |
|||
} |
|||
} |
|||
} |
|||
@ -1,117 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Orleans.Concurrency; |
|||
using Squidex.Domain.Apps.Entities.Contents.Text.Lucene; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Log; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable xUnit1004 // Test methods should not be skipped
|
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Text |
|||
{ |
|||
[Trait("Category", "Dependencies")] |
|||
public class TextIndexerBenchmark |
|||
{ |
|||
private const int Size = 200; |
|||
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app"); |
|||
private readonly NamedId<Guid> schemaId = NamedId.Of(Guid.NewGuid(), "my-schema"); |
|||
|
|||
[Fact] |
|||
public async Task Should_index_and_search_in_temp_folder() |
|||
{ |
|||
await IndexAndSearchAsync(TestStorages.TempFolder()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_index_and_search_in_assets() |
|||
{ |
|||
await IndexAndSearchAsync(TestStorages.Assets()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_index_and_search_in_mongoDB() |
|||
{ |
|||
await IndexAndSearchAsync(TestStorages.MongoDB()); |
|||
} |
|||
|
|||
private async Task IndexAndSearchAsync(IIndexStorage storage) |
|||
{ |
|||
var factory = new IndexManager(storage, A.Fake<ISemanticLog>()); |
|||
|
|||
var grain = new LuceneTextIndexGrain(factory); |
|||
|
|||
await grain.ActivateAsync(appId.Id); |
|||
|
|||
var elapsed1 = await IndexAsync(grain); |
|||
var elapsed2 = await SearchAsync(grain); |
|||
var elapsed3 = await SearchAsync(grain); |
|||
|
|||
Assert.Equal(new long[0], new[] { elapsed1, elapsed2, elapsed3 }); |
|||
} |
|||
|
|||
private async Task<long> IndexAsync(LuceneTextIndexGrain grain) |
|||
{ |
|||
var text = new Dictionary<string, string> |
|||
{ |
|||
["iv"] = "Hello World" |
|||
}; |
|||
|
|||
var ids = new Guid[Size]; |
|||
|
|||
for (var i = 0; i < ids.Length; i++) |
|||
{ |
|||
ids[i] = Guid.NewGuid(); |
|||
} |
|||
|
|||
var watch = ValueStopwatch.StartNew(); |
|||
|
|||
foreach (var id in ids) |
|||
{ |
|||
var commands = new IndexCommand[] |
|||
{ |
|||
new UpsertIndexEntry |
|||
{ |
|||
ContentId = id, |
|||
DocId = id.ToString(), |
|||
ServeAll = true, |
|||
ServePublished = true, |
|||
Texts = text |
|||
} |
|||
}; |
|||
|
|||
await grain.IndexAsync(schemaId, commands.AsImmutable()); |
|||
} |
|||
|
|||
return watch.Stop(); |
|||
} |
|||
|
|||
private async Task<long> SearchAsync(LuceneTextIndexGrain grain) |
|||
{ |
|||
var searchContext = new SearchContext |
|||
{ |
|||
Languages = new HashSet<string>() |
|||
}; |
|||
|
|||
var watch = ValueStopwatch.StartNew(); |
|||
|
|||
for (var i = 0; i < Size; i++) |
|||
{ |
|||
var result = await grain.SearchAsync("Hello", default, searchContext); |
|||
|
|||
Assert.NotEmpty(result); |
|||
} |
|||
|
|||
return watch.Stop(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" /> |
|||
<PackageReference Include="Lorem.Universal.Net" Version="3.0.64" /> |
|||
<PackageReference Include="Microsoft.Orleans.Core.Abstractions" Version="3.1.2" /> |
|||
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> |
|||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" /> |
|||
</ItemGroup> |
|||
<PropertyGroup> |
|||
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Entities.MongoDb\Squidex.Domain.Apps.Entities.MongoDb.csproj" /> |
|||
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Entities\Squidex.Domain.Apps.Entities.csproj" /> |
|||
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,49 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using MongoDB.Driver; |
|||
using MongoDB.Driver.GridFS; |
|||
using Squidex.Domain.Apps.Entities.Contents.Text.Lucene; |
|||
using Squidex.Domain.Apps.Entities.Contents.Text.Lucene.Storage; |
|||
using Squidex.Domain.Apps.Entities.MongoDb.FullText; |
|||
using Squidex.Infrastructure.Assets; |
|||
|
|||
namespace Benchmarks |
|||
{ |
|||
public static class IndexStorages |
|||
{ |
|||
public static IIndexStorage Assets() |
|||
{ |
|||
var storage = new AssetIndexStorage(new MemoryAssetStore()); |
|||
|
|||
return storage; |
|||
} |
|||
|
|||
public static IIndexStorage TempFolder() |
|||
{ |
|||
var storage = new FileIndexStorage(); |
|||
|
|||
return storage; |
|||
} |
|||
|
|||
public static IIndexStorage MongoDB() |
|||
{ |
|||
var mongoClient = new MongoClient("mongodb://localhost"); |
|||
var mongoDatabase = mongoClient.GetDatabase("FullText"); |
|||
|
|||
var mongoBucket = new GridFSBucket<string>(mongoDatabase, new GridFSBucketOptions |
|||
{ |
|||
BucketName = $"bucket_{DateTime.UtcNow.Ticks}" |
|||
}); |
|||
|
|||
var storage = new MongoIndexStorage(mongoBucket); |
|||
|
|||
return storage; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using BenchmarkDotNet.Attributes; |
|||
using Benchmarks.Utils; |
|||
using LoremNET; |
|||
using Orleans.Concurrency; |
|||
using Squidex.Domain.Apps.Entities.Contents.Text; |
|||
using Squidex.Domain.Apps.Entities.Contents.Text.Lucene; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Benchmarks |
|||
{ |
|||
[ShortRunJob] |
|||
[StopOnFirstError] |
|||
[RPlotExporter] |
|||
public class IndexingBenchmarks |
|||
{ |
|||
private readonly IIndexStorage storageAssets = IndexStorages.Assets(); |
|||
private readonly IIndexStorage storageTempFolder = IndexStorages.TempFolder(); |
|||
private readonly IIndexStorage storageMongoDB = IndexStorages.MongoDB(); |
|||
private readonly Dictionary<string, string> texts; |
|||
|
|||
public IndexingBenchmarks() |
|||
{ |
|||
texts = new Dictionary<string, string> |
|||
{ |
|||
["iv"] = Lorem.Paragraph(10, 10) |
|||
}; |
|||
} |
|||
|
|||
[Params(1000)] |
|||
public int N { get; set; } |
|||
|
|||
[Params(10)] |
|||
public int M { get; set; } |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public async Task Index_TempFolder() |
|||
{ |
|||
await IndexAndSearchAsync(storageTempFolder); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public async Task Index_Assets() |
|||
{ |
|||
await IndexAndSearchAsync(storageAssets); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public async Task Index_MongoDB() |
|||
{ |
|||
await IndexAndSearchAsync(storageMongoDB); |
|||
} |
|||
|
|||
private async Task IndexAndSearchAsync(IIndexStorage storage) |
|||
{ |
|||
var schemaId = NamedId.Of(Guid.NewGuid(), "my-schema"); |
|||
|
|||
var factory = new IndexManager(storage, new NoopLog()); |
|||
|
|||
var grain = new LuceneTextIndexGrain(factory); |
|||
|
|||
await grain.ActivateAsync(Guid.NewGuid()); |
|||
|
|||
for (var i = 0; i < M; i++) |
|||
{ |
|||
var ids = new Guid[N]; |
|||
|
|||
for (var j = 0; j < ids.Length; j++) |
|||
{ |
|||
ids[j] = Guid.NewGuid(); |
|||
} |
|||
|
|||
foreach (var id in ids) |
|||
{ |
|||
var commands = new IndexCommand[] |
|||
{ |
|||
new UpsertIndexEntry |
|||
{ |
|||
ContentId = id, |
|||
DocId = id.ToString(), |
|||
ServeAll = true, |
|||
ServePublished = true, |
|||
Texts = texts |
|||
} |
|||
}; |
|||
|
|||
await grain.IndexAsync(schemaId, commands.AsImmutable()); |
|||
} |
|||
|
|||
await grain.CommitAsync(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using BenchmarkDotNet.Running; |
|||
|
|||
namespace Benchmarks |
|||
{ |
|||
public static class Program |
|||
{ |
|||
public static void Main() |
|||
{ |
|||
BenchmarkRunner.Run<IndexingBenchmarks>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using Squidex.Infrastructure.Log; |
|||
|
|||
namespace Benchmarks.Utils |
|||
{ |
|||
public sealed class NoopLog : ISemanticLog |
|||
{ |
|||
public ISemanticLog CreateScope(Action<IObjectWriter> objectWriter) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
public void Log<T>(SemanticLogLevel logLevel, T context, Exception exception, LogFormatter<T> action) |
|||
{ |
|||
} |
|||
|
|||
public void Log(SemanticLogLevel logLevel, Exception exception, LogFormatter action) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue