Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a separate example project for Logs redaction #3744

Merged
merged 7 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions OpenTelemetry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Tests.Stress.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp.AspNetCore", "test\TestApp.AspNetCore\TestApp.AspNetCore.csproj", "{5FDAF679-DE5A-4C73-A49B-8ABCF2399229}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "redaction", "docs\logs\redaction\redaction.csproj", "{A2DF46DE-50D7-4887-8C9D-4BD79CA19FAA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -476,6 +478,10 @@ Global
{5FDAF679-DE5A-4C73-A49B-8ABCF2399229}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5FDAF679-DE5A-4C73-A49B-8ABCF2399229}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5FDAF679-DE5A-4C73-A49B-8ABCF2399229}.Release|Any CPU.Build.0 = Release|Any CPU
{A2DF46DE-50D7-4887-8C9D-4BD79CA19FAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2DF46DE-50D7-4887-8C9D-4BD79CA19FAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2DF46DE-50D7-4887-8C9D-4BD79CA19FAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2DF46DE-50D7-4887-8C9D-4BD79CA19FAA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -512,6 +518,7 @@ Global
{6C7A1595-36D6-4229-BBB5-5A6B5791791D} = {3862190B-E2C5-418E-AFDC-DB281FB5C705}
{9A07D215-90AC-4BAF-BCDB-73D74FD3A5C5} = {3862190B-E2C5-418E-AFDC-DB281FB5C705}
{5FDAF679-DE5A-4C73-A49B-8ABCF2399229} = {77C7929A-2EED-4AA6-8705-B5C443C8AA0F}
{A2DF46DE-50D7-4887-8C9D-4BD79CA19FAA} = {3862190B-E2C5-418E-AFDC-DB281FB5C705}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521}
Expand Down
53 changes: 0 additions & 53 deletions docs/logs/extending-the-sdk/MyClassWithRedactionEnumerator.cs

This file was deleted.

3 changes: 1 addition & 2 deletions docs/logs/extending-the-sdk/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ public static void Main()
builder.AddOpenTelemetry(options =>
{
options.IncludeScopes = true;
options.AddProcessor(new MyRedactionProcessor())
.AddProcessor(new MyProcessor("ProcessorA"))
options.AddProcessor(new MyProcessor("ProcessorA"))
.AddProcessor(new MyProcessor("ProcessorB"))
.AddProcessor(new SimpleLogRecordExportProcessor(new MyExporter("ExporterX")))
.AddMyExporter();
Expand Down
61 changes: 61 additions & 0 deletions docs/logs/redaction/MyClassWithRedactionEnumerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// <copyright file="MyClassWithRedactionEnumerator.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;
using System.Collections.Generic;

namespace Redaction
{
internal class MyClassWithRedactionEnumerator : IReadOnlyList<KeyValuePair<string, object>>
{
private readonly IReadOnlyList<KeyValuePair<string, object>> state;

public MyClassWithRedactionEnumerator(IReadOnlyList<KeyValuePair<string, object>> state)
{
this.state = state;
}

public int Count => this.state.Count;

public KeyValuePair<string, object> this[int index]
{
get
{
var item = this.state[index];
var entryVal = item.Value;
if (entryVal != null && entryVal.ToString() != null && entryVal.ToString().Contains("<secret>"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider a real world example: input contains email address david@otel.org, output will change it to ***@otel.org.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably not as straightforward to do without causing allocation.

{
return new KeyValuePair<string, object>(item.Key, "newRedactedValueHere");
}

return item;
}
}

public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
for (var i = 0; i < this.Count; i++)
{
yield return this[i];
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,19 @@
using OpenTelemetry;
using OpenTelemetry.Logs;

namespace Redaction;

internal class MyRedactionProcessor : BaseProcessor<LogRecord>
{
public override void OnEnd(LogRecord logRecord)
{
if (logRecord.State is IReadOnlyList<KeyValuePair<string, object>> listOfKvp)
if (logRecord.State == null)
{
// When State is null, OTel SDK guarantees StateValues is populated
// TODO: Debug.Assert?
logRecord.StateValues = new MyClassWithRedactionEnumerator(logRecord.StateValues);
}
else if (logRecord.State is IReadOnlyList<KeyValuePair<string, object>> listOfKvp)
{
logRecord.State = new MyClassWithRedactionEnumerator(listOfKvp);
}
Expand Down
38 changes: 38 additions & 0 deletions docs/logs/redaction/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// <copyright file="Program.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 Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;

namespace Redaction;

public class Program
{
public static void Main()
{
using var loggerFactory = LoggerFactory.Create(builder =>
builder.AddOpenTelemetry(options =>
{
options.AddProcessor(new MyRedactionProcessor());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

processors are run sync.. so it might affect latency if executing complex regex etc. But this might be okay for now. Unless we can show how to do this in Exporter (which wraps real exporter).

options.AddConsoleExporter();
}));

var logger = loggerFactory.CreateLogger<Program>();

// message will be redacted by MyRedactionProcessor
logger.LogInformation("OpenTelemetry {sensitiveString}.", "<secret>");
}
}
6 changes: 6 additions & 0 deletions docs/logs/redaction/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Redaction

This program shows an example of how to redact sensitive information from Logs.
utpilla marked this conversation as resolved.
Show resolved Hide resolved
In this example, we attach a custom `Processor` called `MyRedactionProcessor`
which is responsible for replacing any instance of the word "&lt;secret&gt;"
with the value "newRedactedValueHere".
6 changes: 6 additions & 0 deletions docs/logs/redaction/redaction.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPkgVer)" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" />
</ItemGroup>
</Project>
23 changes: 22 additions & 1 deletion src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,28 @@ public override ExportResult Export(in Batch<LogRecord> batch)

if (logRecord.State != null)
{
this.WriteLine($"{"LogRecord.State:",-RightPaddingLength}{logRecord.State}");
if (logRecord.State is IReadOnlyList<KeyValuePair<string, object>> listKvp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may want to do what OTLP exporter does.
#3186 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since ConsoleExporter is only meant to be used for local testing, I was thinking it's probably a good idea to not impose any option from our end and let the user figure out what works best for them.

{
this.WriteLine("LogRecord.State (Key:Value):");
for (int i = 0; i < listKvp.Count; i++)
{
// Special casing {OriginalFormat}
// See https://github.com/open-telemetry/opentelemetry-dotnet/pull/3182
// for explanation.
var valueToTransform = listKvp[i].Key.Equals("{OriginalFormat}")
? new KeyValuePair<string, object>("OriginalFormat (a.k.a Body)", listKvp[i].Value)
: listKvp[i];

if (ConsoleTagTransformer.Instance.TryTransformTag(listKvp[i], out var result))
{
this.WriteLine($"{string.Empty,-4}{result}");
}
}
}
else
{
this.WriteLine($"{"LogRecord.State:",-RightPaddingLength}{logRecord.State}");
}
}
else if (logRecord.StateValues != null)
{
Expand Down