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

feat: Add bring your own log formatter to logger #375

Merged
merged 3 commits into from
Aug 21, 2023

Conversation

amirkaws
Copy link
Contributor

Issue number: #366

Summary

This PR allows users to being their own log formatter

Changes

No changes to exiting functionalities only adding a new feature.

User experience

    /**
     * Handler for requests to Lambda function.
     */
    public class Function
    {
        /// <summary>
        /// Function constructor
        /// </summary>
        public Function()
        {
            Logger.UseFormatter(new CustomLogFormatter());
        }

        [Logging(CorrelationIdPath = "/headers/my_request_id_header", SamplingRate = 0.7)]
        public async Task<APIGatewayProxyResponse> FunctionHandler
            (APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
        {
            ...
        }
    }

CustomLogFormatter implementation

    public class CustomLogFormatter : ILogFormatter
    {
        public object FormatLogEntry(LogEntry logEntry)
        {
            return new
            {
                Message = logEntry.Message,
                Service = logEntry.Service,
                CorrelationIds = new 
                {
                    AwsRequestId = logEntry.LambdaContext?.AwsRequestId,
                    XRayTraceId = logEntry.XRayTraceId,
                    CorrelationId = logEntry.CorrelationId
                },
                LambdaFunction = new
                {
                    Name = logEntry.LambdaContext?.FunctionName,
                    Arn = logEntry.LambdaContext?.InvokedFunctionArn,
                    MemoryLimitInMB = logEntry.LambdaContext?.MemoryLimitInMB,
                    Version = logEntry.LambdaContext?.FunctionVersion,
                    ColdStart = logEntry.ColdStart,
                },
                Level = logEntry.Level.ToString(),
                Timestamp = logEntry.Timestamp.ToString("o"),
                Logger = new
                {
                    Name = logEntry.Name,
                    SampleRate = logEntry.SamplingRate
                },
            };
        }
    }

Example CloudWatch Logs excerpt

    {
        "Message": "Test Message",
        "Service": "lambda-example",
        "CorrelationIds": {
            "AwsRequestId": "52fdfc07-2182-154f-163f-5f0f9a621d72",
            "XRayTraceId": "1-61b7add4-66532bb81441e1b060389429",
            "CorrelationId": "correlation_id_value"
        },
        "LambdaFunction": {
            "Name": "test",
            "Arn": "arn:aws:lambda:eu-west-1:12345678910:function:test",
            "MemorySize": 128,
            "Version": "$LATEST",
            "ColdStart": true
        },
        "Level": "Information",
        "Timestamp": "2021-12-13T20:32:22.5774262Z",
        "Logger": {
            "Name": "AWS.Lambda.Powertools.Logging.Logger",
            "SampleRate": 0.7
        }
    }

Checklist

Please leave checklist items unchecked if they do not apply to your change.

Is this a breaking change?

RFC issue number:

Checklist:

  • Migration process documented
  • Implement warnings (if it can live side by side)

Acknowledgment

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.

@auto-assign auto-assign bot requested review from hjgraca and sliedig August 18, 2023 10:17
@boring-cyborg boring-cyborg bot added area/logging Core logging utility documentation Improvements or additions to documentation tests labels Aug 18, 2023
@pull-request-size pull-request-size bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Aug 18, 2023
@github-actions github-actions bot added the feature New features or minor changes label Aug 18, 2023
try
{
var logObject = logFormatter.FormatLogEntry(logEntry);
if (logObject is not null)
Copy link
Contributor

Choose a reason for hiding this comment

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

In what scenarios can logObject be null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if custom formatter implemented by user for any reason returns null as output.

{
foreach (var (key, value) in CurrentScope.ExtraKeys)
{
if (!string.IsNullOrWhiteSpace(key))
Copy link
Contributor

Choose a reason for hiding this comment

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

don't think is possible at runtime to have an empty or null key for dictionary, this could be simplified with logEntry.ExtraKeys = new Dictionary<string, string>(extraKeys);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It cannot, I can remove the null check but is's an append to the existing dictionary, so cannot be replaced.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have replaced it with a new throw new LogFormatException (custom exception)

case LoggingConstants.KeyCorrelationId:
logEntry.CorrelationId = value as string;
break;
default:
Copy link
Contributor

Choose a reason for hiding this comment

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

In here we are changing the premise that what is logged should be exactly what is returned from the formatter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't change the object returning from formatter and we log it as is. This is not for the object returned from the formatter, it's for creating an LogEntry to pass it to the formatter.

@sonarcloud
Copy link

sonarcloud bot commented Aug 18, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 12 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

Copy link
Contributor

@hjgraca hjgraca left a comment

Choose a reason for hiding this comment

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

GTM. Great work Amir. I think there is room for improving the code in the future, specifically in PowertoolsLogger line 300 to line 330, those loops and checks could be improved.

@amirkaws amirkaws merged commit 9b17227 into aws-powertools:develop Aug 21, 2023
7 checks passed
@amirkaws amirkaws deleted the custom-log-formatter branch August 22, 2023 07:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/logging Core logging utility documentation Improvements or additions to documentation feature New features or minor changes size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants