diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln
index ec895d0d87a..25b30662416 100644
--- a/OpenTelemetry.sln
+++ b/OpenTelemetry.sln
@@ -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
@@ -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
@@ -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}
diff --git a/docs/logs/extending-the-sdk/MyClassWithRedactionEnumerator.cs b/docs/logs/extending-the-sdk/MyClassWithRedactionEnumerator.cs
deleted file mode 100644
index 4e5f3107460..00000000000
--- a/docs/logs/extending-the-sdk/MyClassWithRedactionEnumerator.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-// 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.
-//
-
-using System.Collections;
-using System.Collections.Generic;
-
-internal class MyClassWithRedactionEnumerator : IReadOnlyList>
-{
- private readonly IReadOnlyList> state;
-
- public MyClassWithRedactionEnumerator(IReadOnlyList> state)
- {
- this.state = state;
- }
-
- public int Count => this.state.Count;
-
- public KeyValuePair this[int index] => this.state[index];
-
- public IEnumerator> GetEnumerator()
- {
- foreach (var entry in this.state)
- {
- var entryVal = entry.Value;
- if (entryVal != null && entryVal.ToString() != null && entryVal.ToString().Contains(""))
- {
- yield return new KeyValuePair(entry.Key, "newRedactedValueHere");
- }
- else
- {
- yield return entry;
- }
- }
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return this.GetEnumerator();
- }
-}
diff --git a/docs/logs/extending-the-sdk/Program.cs b/docs/logs/extending-the-sdk/Program.cs
index 2ea9dad4953..2a2703f6efe 100644
--- a/docs/logs/extending-the-sdk/Program.cs
+++ b/docs/logs/extending-the-sdk/Program.cs
@@ -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();
diff --git a/docs/logs/redaction/MyClassWithRedactionEnumerator.cs b/docs/logs/redaction/MyClassWithRedactionEnumerator.cs
new file mode 100644
index 00000000000..7cac155957b
--- /dev/null
+++ b/docs/logs/redaction/MyClassWithRedactionEnumerator.cs
@@ -0,0 +1,61 @@
+//
+// 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.
+//
+
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Redaction
+{
+ internal class MyClassWithRedactionEnumerator : IReadOnlyList>
+ {
+ private readonly IReadOnlyList> state;
+
+ public MyClassWithRedactionEnumerator(IReadOnlyList> state)
+ {
+ this.state = state;
+ }
+
+ public int Count => this.state.Count;
+
+ public KeyValuePair this[int index]
+ {
+ get
+ {
+ var item = this.state[index];
+ var entryVal = item.Value;
+ if (entryVal != null && entryVal.ToString() != null && entryVal.ToString().Contains(""))
+ {
+ return new KeyValuePair(item.Key, "newRedactedValueHere");
+ }
+
+ return item;
+ }
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ for (var i = 0; i < this.Count; i++)
+ {
+ yield return this[i];
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+ }
+}
diff --git a/docs/logs/extending-the-sdk/MyRedactionProcessor.cs b/docs/logs/redaction/MyRedactionProcessor.cs
similarity index 72%
rename from docs/logs/extending-the-sdk/MyRedactionProcessor.cs
rename to docs/logs/redaction/MyRedactionProcessor.cs
index 2ad8371ae69..70c704dbad0 100644
--- a/docs/logs/extending-the-sdk/MyRedactionProcessor.cs
+++ b/docs/logs/redaction/MyRedactionProcessor.cs
@@ -18,11 +18,19 @@
using OpenTelemetry;
using OpenTelemetry.Logs;
+namespace Redaction;
+
internal class MyRedactionProcessor : BaseProcessor
{
public override void OnEnd(LogRecord logRecord)
{
- if (logRecord.State is IReadOnlyList> 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> listOfKvp)
{
logRecord.State = new MyClassWithRedactionEnumerator(listOfKvp);
}
diff --git a/docs/logs/redaction/Program.cs b/docs/logs/redaction/Program.cs
new file mode 100644
index 00000000000..f381856234e
--- /dev/null
+++ b/docs/logs/redaction/Program.cs
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+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());
+ options.AddConsoleExporter();
+ }));
+
+ var logger = loggerFactory.CreateLogger();
+
+ // message will be redacted by MyRedactionProcessor
+ logger.LogInformation("OpenTelemetry {sensitiveString}.", "");
+ }
+}
diff --git a/docs/logs/redaction/README.md b/docs/logs/redaction/README.md
new file mode 100644
index 00000000000..07fe87b94bc
--- /dev/null
+++ b/docs/logs/redaction/README.md
@@ -0,0 +1,6 @@
+# Redaction
+
+This example shows how to redact sensitive information from Logs.
+In this example, we attach a custom `Processor` called `MyRedactionProcessor`
+which is responsible for replacing any instance of the word "<secret>"
+with the value "newRedactedValueHere".
diff --git a/docs/logs/redaction/redaction.csproj b/docs/logs/redaction/redaction.csproj
new file mode 100644
index 00000000000..a0d4b1e148b
--- /dev/null
+++ b/docs/logs/redaction/redaction.csproj
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs b/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs
index f089b0e95a2..811838e20c0 100644
--- a/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs
+++ b/src/OpenTelemetry.Exporter.Console/ConsoleLogRecordExporter.cs
@@ -84,7 +84,28 @@ public override ExportResult Export(in Batch batch)
if (logRecord.State != null)
{
- this.WriteLine($"{"LogRecord.State:",-RightPaddingLength}{logRecord.State}");
+ if (logRecord.State is IReadOnlyList> listKvp)
+ {
+ 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("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)
{