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 setters for State, StateValues, and FormattedMessage fields for LogRecord. #2864

Closed
wants to merge 39 commits into from
Closed
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
152ce3b
initial
Yun-Ting Feb 4, 2022
fb1a89c
added guard
Yun-Ting Feb 4, 2022
ffca6e2
test
Yun-Ting Feb 5, 2022
117b202
doc compliance
Yun-Ting Feb 5, 2022
f273d7d
name
Yun-Ting Feb 5, 2022
8fb50ea
changelog and SA
Yun-Ting Feb 5, 2022
f8021e7
reorder
Yun-Ting Feb 5, 2022
aad3f57
revert
Yun-Ting Feb 7, 2022
12dcaae
add getters to unshipped api
Yun-Ting Feb 7, 2022
93e8e4b
remove gets from shipped API
Yun-Ting Feb 7, 2022
881263a
starting over
Yun-Ting Feb 7, 2022
d0285ad
use private set
Yun-Ting Feb 8, 2022
2f562b4
use processor
Yun-Ting Feb 9, 2022
de8850c
loosening checks
Yun-Ting Feb 10, 2022
1e5a52a
split
Yun-Ting Feb 10, 2022
70f8c98
rename
Yun-Ting Feb 10, 2022
4070f16
order
Yun-Ting Feb 10, 2022
114bd69
oops
Yun-Ting Feb 10, 2022
b314015
public setter
Yun-Ting Feb 10, 2022
505745e
Merge branch 'main' into yunl/logRecordSetter
cijothomas Feb 11, 2022
3cbbe9f
addressed a comment
Yun-Ting Feb 12, 2022
629c266
Merge branch 'yunl/logRecordSetter' of https://github.com/Yun-Ting/op…
Yun-Ting Feb 12, 2022
ee350f7
addressed comments
Yun-Ting Feb 12, 2022
11196f4
added basic tests
Yun-Ting Feb 15, 2022
2f89560
Update src/OpenTelemetry/CHANGELOG.md
Yun-Ting Feb 15, 2022
fc42572
removed
Yun-Ting Feb 15, 2022
1e345e1
addressed comments: consistency
Yun-Ting Feb 16, 2022
9b70068
merge from main
Yun-Ting Mar 29, 2022
a8d4dcb
merge from main
Yun-Ting Apr 14, 2022
e880a3c
fix merge
Yun-Ting Apr 14, 2022
b750adf
new line
Yun-Ting Apr 14, 2022
8a4b24b
docs
Yun-Ting Apr 15, 2022
f2c3802
merge from main
Yun-Ting Apr 15, 2022
d914071
fix
Yun-Ting Apr 15, 2022
21b249a
comment
Yun-Ting Apr 15, 2022
426a797
merge from main
Yun-Ting Apr 15, 2022
f4109ec
minor
Yun-Ting Apr 15, 2022
9bb8beb
public api
Yun-Ting Apr 15, 2022
c6311ef
doc and merge main
Yun-Ting Apr 21, 2022
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
54 changes: 54 additions & 0 deletions docs/logs/extending-the-sdk/MyRedactionProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// <copyright file="MyRedactionProcessor.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;
using OpenTelemetry.Logs;

internal class MyRedactionProcessor : BaseProcessor<LogRecord>
{
private readonly string name;

public MyRedactionProcessor(string name)
{
this.name = name;
}

public override void OnEnd(LogRecord logRecord)
{
var listKvp = logRecord.State as IReadOnlyList<KeyValuePair<string, object>>;
int kvpCount = listKvp == null ? 0 : listKvp.Count;

for (int i = 0; i < kvpCount; ++i)
{
var entry = listKvp[i];
if (kvpCount > 1 && StringComparer.Ordinal.Equals(entry.Key, "{OriginalFormat}"))
Yun-Ting marked this conversation as resolved.
Show resolved Hide resolved
{
continue;
}

if (entry.Value is string str && str.Contains("sensitive information"))
{
Console.WriteLine($"{this.name}.OnEnd(LogRecord.State before redaction: {logRecord.State})");
logRecord.State = new List<KeyValuePair<string, object>> { new KeyValuePair<string, object>("newStateValueKey", "newStateValueValue") };
Yun-Ting marked this conversation as resolved.
Show resolved Hide resolved

var newListKvp = logRecord.State as IReadOnlyList<KeyValuePair<string, object>>;
Console.WriteLine($"{this.name}.OnEnd(LogRecord.State after redaction: {newListKvp[0].Key}, {newListKvp[0].Value})");
}
}
}
}
4 changes: 4 additions & 0 deletions docs/logs/extending-the-sdk/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public static void Main()
options.AddProcessor(new MyProcessor("ProcessorA"))
.AddProcessor(new MyProcessor("ProcessorB"))
.AddProcessor(new SimpleLogRecordExportProcessor(new MyExporter("ExporterX")))
.AddProcessor(new MyRedactionProcessor("RedctionProcessor"))
.AddMyExporter();
}));

Expand Down Expand Up @@ -64,6 +65,9 @@ public static void Main()
{
logger.LogError("{name} is broken.", "refrigerator");
}

// message will be redacted by MyRedactionProcessor
logger.LogInformation("Message to log: {message}.", "sensitive information");
}

internal struct Food
Expand Down
3 changes: 3 additions & 0 deletions src/OpenTelemetry/.publicApi/net461/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
OpenTelemetry.BaseExporter<T>.ForceFlush(int timeoutMilliseconds = -1) -> bool
OpenTelemetry.Batch<T>.Batch(T[] items, int count) -> void
OpenTelemetry.Batch<T>.Count.get -> long
OpenTelemetry.Logs.LogRecord.FormattedMessage.set -> void
OpenTelemetry.Logs.LogRecord.State.set -> void
OpenTelemetry.Logs.LogRecord.StateValues.set -> void
OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration
OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration.Boundaries.get -> double[]
OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration.Boundaries.set -> void
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
OpenTelemetry.BaseExporter<T>.ForceFlush(int timeoutMilliseconds = -1) -> bool
OpenTelemetry.Batch<T>.Batch(T[] items, int count) -> void
OpenTelemetry.Batch<T>.Count.get -> long
OpenTelemetry.Logs.LogRecord.FormattedMessage.set -> void
OpenTelemetry.Logs.LogRecord.State.set -> void
OpenTelemetry.Logs.LogRecord.StateValues.set -> void
OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration
OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration.Boundaries.get -> double[]
OpenTelemetry.Metrics.ExplicitBucketHistogramConfiguration.Boundaries.set -> void
Expand Down
3 changes: 3 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Add setters for `State`, `StateValues`, and `FormattedMessage` for LogRecord.
([2864](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2864))

## 1.2.0-rc5

Released 2022-Apr-12
Expand Down
10 changes: 5 additions & 5 deletions src/OpenTelemetry/Logs/LogRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,21 @@ internal LogRecord(

public EventId EventId { get; }

public string FormattedMessage { get; }
public string FormattedMessage { get; set; }

/// <summary>
/// Gets the raw state attached to the log. Set to <see
/// Gets or sets the raw state attached to the log. Set to <see
/// langword="null"/> when <see
/// cref="OpenTelemetryLoggerOptions.ParseStateValues"/> is enabled.
/// </summary>
public object State { get; }
public object State { get; set; }

/// <summary>
/// Gets the parsed state values attached to the log. Set when <see
/// Gets or sets the parsed state values attached to the log. Set when <see
/// cref="OpenTelemetryLoggerOptions.ParseStateValues"/> is enabled
/// otherwise <see langword="null"/>.
/// </summary>
public IReadOnlyList<KeyValuePair<string, object>> StateValues { get; }
public IReadOnlyList<KeyValuePair<string, object>> StateValues { get; set; }
Yun-Ting marked this conversation as resolved.
Show resolved Hide resolved

public Exception Exception { get; }

Expand Down
149 changes: 149 additions & 0 deletions test/OpenTelemetry.Tests/Logs/LogRecordTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ namespace OpenTelemetry.Logs.Tests
{
public sealed class LogRecordTest
{
private enum Field
{
FormattedMessage,
State,
StateValues,
}

[Fact]
public void CheckCategoryNameForLog()
{
Expand Down Expand Up @@ -243,6 +250,122 @@ public void CheckStateForExceptionLogged()
Assert.Equal(message, state.ToString());
}

[Fact]
public void CheckStateCanBeSet()
{
using var loggerFactory = InitializeLoggerFactory(out List<LogRecord> exportedItems, configure: null);
var logger = loggerFactory.CreateLogger<LogRecordTest>();

var message = $"This does not matter.";
logger.LogInformation(message);

var logRecord = exportedItems[0];
logRecord.State = "newState";

var expectedState = "newState";
Assert.Equal(expectedState, logRecord.State);
}

[Fact]
public void CheckStateValuesCanBeSet()
{
using var loggerFactory = InitializeLoggerFactory(out List<LogRecord> exportedItems, configure: options => options.ParseStateValues = true);
var logger = loggerFactory.CreateLogger<LogRecordTest>();

logger.Log(
LogLevel.Information,
0,
new List<KeyValuePair<string, object>> { new KeyValuePair<string, object>("Key1", "Value1") },
null,
(s, e) => "OpenTelemetry!");

var logRecord = exportedItems[0];
var expectedStateValues = new List<KeyValuePair<string, object>> { new KeyValuePair<string, object>("Key2", "Value2") };
logRecord.StateValues = expectedStateValues;

Assert.Equal(expectedStateValues, logRecord.StateValues);
}

[Fact]
public void CheckFormattedMessageCanBeSet()
{
using var loggerFactory = InitializeLoggerFactory(out List<LogRecord> exportedItems, configure: options => options.IncludeFormattedMessage = true);
var logger = loggerFactory.CreateLogger<LogRecordTest>();

logger.LogInformation("OpenTelemetry {Greeting} {Subject}!", "Hello", "World");
var logRecord = exportedItems[0];
var expectedFormattedMessage = "OpenTelemetry Good Night!";
logRecord.FormattedMessage = expectedFormattedMessage;

Assert.Equal(expectedFormattedMessage, logRecord.FormattedMessage);
}

[Fact]
public void CheckStateCanBeSetByProcessor()
{
var exportedItems = new List<LogRecord>();
var exporter = new InMemoryExporter<LogRecord>(exportedItems);
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.AddProcessor(new RedactionProcessor(Field.State));
options.AddInMemoryExporter(exportedItems);
});
});

var logger = loggerFactory.CreateLogger<LogRecordTest>();
logger.LogInformation($"This does not matter.");

var state = exportedItems[0].State as IReadOnlyList<KeyValuePair<string, object>>;
Assert.Equal("newStateKey", state[0].Key.ToString());
Assert.Equal("newStateValue", state[0].Value.ToString());
}

[Fact]
public void CheckStateValuesCanBeSetByProcessor()
{
var exportedItems = new List<LogRecord>();
var exporter = new InMemoryExporter<LogRecord>(exportedItems);
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.AddProcessor(new RedactionProcessor(Field.StateValues));
options.AddInMemoryExporter(exportedItems);
options.ParseStateValues = true;
});
});

var logger = loggerFactory.CreateLogger<LogRecordTest>();
logger.LogInformation("This does not matter.");

var stateValue = exportedItems[0];
Assert.Equal(new KeyValuePair<string, object>("newStateValueKey", "newStateValueValue"), stateValue.StateValues[0]);
}

[Fact]
public void CheckFormattedMessageCanBeSetByProcessor()
{
var exportedItems = new List<LogRecord>();
var exporter = new InMemoryExporter<LogRecord>(exportedItems);
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.AddProcessor(new RedactionProcessor(Field.FormattedMessage));
options.AddInMemoryExporter(exportedItems);
options.IncludeFormattedMessage = true;
});
});

var logger = loggerFactory.CreateLogger<LogRecordTest>();
logger.LogInformation("OpenTelemetry {Greeting} {Subject}!", "Hello", "World");

var item = exportedItems[0];
Assert.Equal("OpenTelemetry Good Night!", item.FormattedMessage);
}

[Fact]
public void CheckTraceIdForLogWithinDroppedActivity()
{
Expand Down Expand Up @@ -668,6 +791,32 @@ IEnumerator IEnumerable.GetEnumerator()
}
}

private class RedactionProcessor : BaseProcessor<LogRecord>
{
private readonly Field fieldToUpdate;

public RedactionProcessor(Field fieldToUpdate)
{
this.fieldToUpdate = fieldToUpdate;
}

public override void OnEnd(LogRecord logRecord)
{
if (this.fieldToUpdate == Field.State)
{
logRecord.State = new List<KeyValuePair<string, object>> { new KeyValuePair<string, object>("newStateKey", "newStateValue") };
}
else if (this.fieldToUpdate == Field.StateValues)
{
logRecord.StateValues = new List<KeyValuePair<string, object>> { new KeyValuePair<string, object>("newStateValueKey", "newStateValueValue") };
}
else
{
logRecord.FormattedMessage = "OpenTelemetry Good Night!";
}
}
}

private class ListState : IEnumerable<KeyValuePair<string, object>>
{
private readonly List<KeyValuePair<string, object>> list;
Expand Down