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

Get LogRecord.ScopeValues after end of scope #3304

Closed
ramkumarchan opened this issue May 24, 2022 · 4 comments
Closed

Get LogRecord.ScopeValues after end of scope #3304

ramkumarchan opened this issue May 24, 2022 · 4 comments
Labels
question Further information is requested

Comments

@ramkumarchan
Copy link

Hello,

In test VerifyIncludeScopes_True in LogRecordTest.cs, the scope key value pairs are verified before the scope ends. Is it possible to obtain and verify the scope key value pairs after the scope has ended ?

Existing :

using var loggerFactory = InitializeLoggerFactory(out List<LogRecord> exportedItems, configure: options => options.IncludeScopes = true);
var logger = loggerFactory.CreateLogger<LogRecordTest>();
using var scope = logger.BeginScope("string_scope");
logger.LogInformation("OpenTelemetry!");
var logRecord = exportedItems[0];
...
// Verify Scope Values using logRecord.ForEachScope
scopes.Clear();

Question :

using var loggerFactory = InitializeLoggerFactory(out List<LogRecord> exportedItems, configure: options => options.IncludeScopes = true);
var logger = loggerFactory.CreateLogger<LogRecordTest>();
using var scope = logger.BeginScope("string_scope");
logger.LogInformation("OpenTelemetry!");
var logRecord = exportedItems[0];
...
scopes.Clear();
// How can we get or verify scope values for logRecord here ? 
@ramkumarchan ramkumarchan added the question Further information is requested label May 24, 2022
@CodeBlanch
Copy link
Member

@ramkumarchan Can you give me a little more information about what you are trying to accomplish? Are trying to write tests or something more real like an exporter?

logRecord.ForEachScope is how you access the scopes.

What the tests are doing is looping over the scopes and adding them to a list...

            List<object> scopes = new List<object>();
            logRecord.ForEachScope<object>(
                (scope, state) =>
                {
                    scopes.Add(scope.Scope);
                },
                null);

So if you call scopes.Clear() you have lost the data the test captured 😄

@ramkumarchan
Copy link
Author

@CodeBlanch I am trying to write simple unit test to assert the scope tag values. Below is a sample code of my unit test. Assuming LogInfo() method cannot modified, how to assert scope tags ?

public void VerifyScopes()
{
    var logRecords = new List<LogRecord>();
    using var loggerFactory = LoggerFactory.Create(builder =>
    {
        builder.AddOpenTelemetry(options =>
        {
            options.IncludeScopes = true;
            options.AddInMemoryExporter(logRecords);
        });
    });
    
    var logger = loggerFactory.CreateLogger("test");
    LogInfo(logger);
    
    List<object> scopes = new List<object>();
    logRecords[0].ForEachScope<object>(
        (scope, state) =>
        {
            scopes.Add(scope.Scope);
        },
        null);
    // scopes.Count is 0 
    // How to assert tag values here ? 
}

private void LogInfo(ILogger logger)
{
    var tags = new Dictionary<string, object>("contextId", 12345);
    using var scope = logger.BeginScope(tags)
    {
          logger.LogInformation("OpenTelemetry!");
    }
}

@CodeBlanch
Copy link
Member

@ramkumarchan

So scopes are only available to processors during the OnEnd lifecycle. BatchLogRecordExportProcessor compensates for this by buffering the scopes so they are available later on. InMemoryExporter uses SimpleLogRecordExportProcessor which doesn't do that buffering so your scopes are disappearing. It just so happens that I merged a PR earlier today to add buffering inside the InMemoryExporter. Once that is released, your test will work like magic 🧙‍♂️

If you want to get it working before that, try something like this...

        [Fact]
        public void VerifyScopes()
        {
            var exporter = new TestExporter();

            using (var loggerFactory = LoggerFactory.Create(builder =>
            {
                builder.AddOpenTelemetry(options =>
                {
                    options.IncludeScopes = true;
                    options.AddProcessor(new BatchLogRecordExportProcessor(exporter));
                });
            }))
            {
                var logger = loggerFactory.CreateLogger("test");
                LogInfo(logger);
            } // Dispose here causes the batch to flush to exporter

            List<object> scopes = new List<object>();
            exporter.ExportedItems[0].ForEachScope<object>(
                (scope, state) =>
                {
                    scopes.Add(scope.Scope);
                },
                null);

            Assert.Single(scopes);

            Dictionary<string, object> scope = scopes[0] as Dictionary<string, object>;

            Assert.NotNull(scope);
            Assert.Equal(12345, scope["contextId"]);
        }

        private void LogInfo(ILogger logger)
        {
            var tags = new Dictionary<string, object>() { ["contextId"] = 12345 };
            using var scope = logger.BeginScope(tags);
            logger.LogInformation("OpenTelemetry!");
        }

        private sealed class TestExporter : BaseExporter<LogRecord>
        {
            public List<LogRecord> ExportedItems { get; } = new();

            public override ExportResult Export(in Batch<LogRecord> batch)
            {
                foreach (LogRecord logRecord in batch)
                {
                    this.ExportedItems.Add(logRecord);
                }

                return ExportResult.Success;
            }
        }

@ramkumarchan
Copy link
Author

ramkumarchan commented Jun 15, 2022

Great ! Thanks for the explanation @CodeBlanch !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants