Skip to content

Commit

Permalink
ILogger integration - part 2 (#1315)
Browse files Browse the repository at this point in the history
* add log processor

* fix changelog

* macros

* more conditional compilations

* improve struct

* remove duplicated stuff

* remove Sdk.CreateLoggerProviderBuilder

* clean up

* update changelog

Co-authored-by: Cijo Thomas <[email protected]>
  • Loading branch information
reyang and cijothomas authored Oct 5, 2020
1 parent 1f04397 commit d1a3f53
Show file tree
Hide file tree
Showing 11 changed files with 630 additions and 47 deletions.
85 changes: 85 additions & 0 deletions docs/logs/getting-started/MyProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// <copyright file="MyProcessor.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.Diagnostics;
using OpenTelemetry;
using OpenTelemetry.Logs;

internal class MyProcessor : LogProcessor
{
private readonly string name;

public MyProcessor(string name = "MyProcessor")
{
this.name = name;
}

public override void OnLog(in LogRecord record)
{
var state = record.State;

if (state is IReadOnlyCollection<KeyValuePair<string, object>> dict)
{
var isUnstructuredLog = dict.Count == 1;

if (isUnstructuredLog)
{
foreach (var entry in dict)
{
Console.WriteLine($"{record.Timestamp:yyyy-MM-ddTHH:mm:ss.fffffffZ} {record.CategoryName}({record.LogLevel}, Id={record.EventId}): {entry.Value}");
}
}
else
{
Console.WriteLine($"{record.Timestamp:yyyy-MM-ddTHH:mm:ss.fffffffZ} {record.CategoryName}({record.LogLevel}, Id={record.EventId}):");
foreach (var entry in dict)
{
if (string.Equals(entry.Key, "{OriginalFormat}", StringComparison.Ordinal))
{
Console.WriteLine($" $format: {entry.Value}");
continue;
}

Console.WriteLine($" {entry.Key}: {entry.Value}");
}
}

if (record.Exception != null)
{
Console.WriteLine($" $exception: {record.Exception}");
}
}
}

protected override bool OnForceFlush(int timeoutMilliseconds)
{
Console.WriteLine($"{this.name}.OnForceFlush({timeoutMilliseconds})");
return true;
}

protected override bool OnShutdown(int timeoutMilliseconds)
{
Console.WriteLine($"{this.name}.OnShutdown({timeoutMilliseconds})");
return true;
}

protected override void Dispose(bool disposing)
{
Console.WriteLine($"{this.name}.Dispose({disposing})");
}
}
16 changes: 15 additions & 1 deletion docs/logs/getting-started/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,31 @@
// </copyright>

using System.Collections.Generic;
#if NETCOREAPP2_1
using Microsoft.Extensions.DependencyInjection;
#endif
using Microsoft.Extensions.Logging;
using OpenTelemetry;

public class Program
{
public static void Main()
{
#if NETCOREAPP2_1
var serviceCollection = new ServiceCollection().AddLogging(builder =>
#else
using var loggerFactory = LoggerFactory.Create(builder =>
#endif
{
builder.AddOpenTelemetry();
builder.AddOpenTelemetry(options => options.AddProcessor(new MyProcessor()));
});

#if NETCOREAPP2_1
using var serviceProvider = serviceCollection.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
#else
var logger = loggerFactory.CreateLogger<Program>();
#endif

// unstructured log
logger.LogInformation("Hello, World!");
Expand Down
14 changes: 13 additions & 1 deletion docs/logs/getting-started/getting-started.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPkgVer)" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp2.1'">
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPkgVer)" />
</ItemGroup>
</Project>
2 changes: 0 additions & 2 deletions src/OpenTelemetry.Api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

## Unreleased

* Added `ILogger`/`Microsoft.Extensions.Logging` integration
([#1308](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1308))
* `IActivityTagEnumerator` is now `IActivityEnumerator<T>`. Added
`EnumerateLinks` extension method on `Activity` for retrieving links
efficiently
Expand Down
3 changes: 3 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
* Renamed `SamplingDecision` options (`NotRecord` to `Drop`, `Record` to
`RecordOnly`, and `RecordAndSampled` to `RecordAndSample`)
([#1297](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1297))
* Added `ILogger`/`Microsoft.Extensions.Logging` integration
([#1308](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1308))
([#1315](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1315))

## 0.6.0-beta.1

Expand Down
191 changes: 191 additions & 0 deletions src/OpenTelemetry/Logs/CompositeLogProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// <copyright file="CompositeLogProcessor.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>

#if NETSTANDARD2_0
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Logs
{
public class CompositeLogProcessor : LogProcessor
{
private DoublyLinkedListNode<LogProcessor> head;
private DoublyLinkedListNode<LogProcessor> tail;
private bool disposed;

public CompositeLogProcessor(IEnumerable<LogProcessor> processors)
{
if (processors == null)
{
throw new ArgumentNullException(nameof(processors));
}

using var iter = processors.GetEnumerator();

if (!iter.MoveNext())
{
throw new ArgumentException($"{nameof(processors)} collection is empty");
}

this.head = new DoublyLinkedListNode<LogProcessor>(iter.Current);
this.tail = this.head;

while (iter.MoveNext())
{
this.AddProcessor(iter.Current);
}
}

public CompositeLogProcessor AddProcessor(LogProcessor processor)
{
if (processor == null)
{
throw new ArgumentNullException(nameof(processor));
}

var node = new DoublyLinkedListNode<LogProcessor>(processor)
{
Previous = this.tail,
};
this.tail.Next = node;
this.tail = node;

return this;
}

/// <inheritdoc/>
public override void OnLog(in LogRecord record)
{
var cur = this.head;

while (cur != null)
{
cur.Value.OnLog(record);
cur = cur.Next;
}
}

/// <inheritdoc/>
protected override bool OnForceFlush(int timeoutMilliseconds)
{
var cur = this.head;

var sw = Stopwatch.StartNew();

while (cur != null)
{
if (timeoutMilliseconds == Timeout.Infinite)
{
_ = cur.Value.ForceFlush(Timeout.Infinite);
}
else
{
var timeout = (long)timeoutMilliseconds - sw.ElapsedMilliseconds;

if (timeout <= 0)
{
return false;
}

var succeeded = cur.Value.ForceFlush((int)timeout);

if (!succeeded)
{
return false;
}
}

cur = cur.Next;
}

return true;
}

/// <inheritdoc/>
protected override bool OnShutdown(int timeoutMilliseconds)
{
var cur = this.head;
var result = true;
var sw = Stopwatch.StartNew();

while (cur != null)
{
if (timeoutMilliseconds == Timeout.Infinite)
{
result = cur.Value.Shutdown(Timeout.Infinite) && result;
}
else
{
var timeout = (long)timeoutMilliseconds - sw.ElapsedMilliseconds;

// notify all the processors, even if we run overtime
result = cur.Value.Shutdown((int)Math.Max(timeout, 0)) && result;
}

cur = cur.Next;
}

return result;
}

protected override void Dispose(bool disposing)
{
if (this.disposed)
{
return;
}

if (disposing)
{
var cur = this.head;

while (cur != null)
{
try
{
cur.Value?.Dispose();
}
catch (Exception ex)
{
OpenTelemetrySdkEventSource.Log.SpanProcessorException(nameof(this.Dispose), ex);
}

cur = cur.Next;
}
}

this.disposed = true;
}

private class DoublyLinkedListNode<T>
{
public readonly T Value;

public DoublyLinkedListNode(T value)
{
this.Value = value;
}

public DoublyLinkedListNode<T> Previous { get; set; }

public DoublyLinkedListNode<T> Next { get; set; }
}
}
}
#endif
Loading

0 comments on commit d1a3f53

Please sign in to comment.