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(logger): Support for external observability providers #1511

Merged
merged 10 commits into from
Jun 30, 2023
13 changes: 11 additions & 2 deletions docs/snippets/logger/bringYourOwnFormatterClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import {
LogAttributes,
UnformattedAttributes,
} from '@aws-lambda-powertools/logger/lib/types';
import { LogItem } from '@aws-lambda-powertools/logger/lib/log';

// Replace this line with your own type
type MyCompanyLog = LogAttributes;

class MyCompanyLogFormatter extends LogFormatter {
public formatAttributes(attributes: UnformattedAttributes): MyCompanyLog {
return {
public formatAttributes(
attributes: UnformattedAttributes,
additionalLogAttributes: LogAttributes
): LogItem {
const baseAttributes: MyCompanyLog = {
message: attributes.message,
service: attributes.serviceName,
environment: attributes.environment,
Expand All @@ -31,6 +35,11 @@ class MyCompanyLogFormatter extends LogFormatter {
sampleRateValue: attributes.sampleRateValue,
},
};

const logItem = new LogItem({ attributes: baseAttributes });
logItem.addAttributes(additionalLogAttributes); // add any attributes not explicitly defined

return logItem;
}
}

Expand Down
26 changes: 14 additions & 12 deletions packages/logger/src/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { randomInt } from 'node:crypto';
import { Console } from 'node:console';
import type { Context, Handler } from 'aws-lambda';
import { Utility } from '@aws-lambda-powertools/commons';
import { LogFormatterInterface, PowertoolLogFormatter } from './formatter';
import { LogFormatterInterface, PowertoolsLogFormatter } from './formatter';
import { LogItem } from './log';
import merge from 'lodash.merge';
import { ConfigServiceInterface, EnvironmentVariablesService } from './config';
Expand Down Expand Up @@ -617,16 +617,13 @@ class Logger extends Utility implements ClassThatLogs {
this.getPowertoolLogData()
);

const logItem = new LogItem({
baseAttributes: this.getLogFormatter().formatAttributes(
unformattedBaseAttributes
),
persistentAttributes: this.getPersistentLogAttributes(),
});

// Add ephemeral attributes
let additionalLogAttributes: LogAttributes = {};
additionalLogAttributes = merge(
additionalLogAttributes,
this.getPersistentLogAttributes()
);
if (typeof input !== 'string') {
logItem.addAttributes(input);
additionalLogAttributes = merge(additionalLogAttributes, input);
}
extraInput.forEach((item: Error | LogAttributes | string) => {
const attributes: LogAttributes =
Expand All @@ -636,9 +633,14 @@ class Logger extends Utility implements ClassThatLogs {
? { extra: item }
: item;

logItem.addAttributes(attributes);
additionalLogAttributes = merge(additionalLogAttributes, attributes);
});

const logItem = this.getLogFormatter().formatAttributes(
unformattedBaseAttributes,
additionalLogAttributes
);

return logItem;
}

Expand Down Expand Up @@ -915,7 +917,7 @@ class Logger extends Utility implements ClassThatLogs {
* @returns {void}
*/
private setLogFormatter(logFormatter?: LogFormatterInterface): void {
this.logFormatter = logFormatter || new PowertoolLogFormatter();
this.logFormatter = logFormatter || new PowertoolsLogFormatter();
}

/**
Expand Down
9 changes: 6 additions & 3 deletions packages/logger/src/formatter/LogFormatter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LogFormatterInterface } from '.';
import { LogAttributes, UnformattedAttributes } from '../types';
import { LogItem } from '../log';

/**
* This class defines and implements common methods for the formatting of log attributes.
Expand All @@ -13,11 +14,13 @@ abstract class LogFormatter implements LogFormatterInterface {
* It formats key-value pairs of log attributes.
*
* @param {UnformattedAttributes} attributes
* @returns {LogAttributes}
* @param {LogAttributes} additionalLogAttributes
* @returns {LogItem}
*/
public abstract formatAttributes(
attributes: UnformattedAttributes
): LogAttributes;
attributes: UnformattedAttributes,
additionalLogAttributes: LogAttributes
): LogItem;

/**
* It formats a given Error parameter.
Expand Down
9 changes: 7 additions & 2 deletions packages/logger/src/formatter/LogFormatterInterface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LogAttributes, UnformattedAttributes } from '../types';
import { LogItem } from '../log';

/**
* @interface
Expand All @@ -8,9 +9,13 @@ interface LogFormatterInterface {
* It formats key-value pairs of log attributes.
*
* @param {UnformattedAttributes} attributes
* @returns {PowertoolLog}
* @param {LogAttributes} additionalLogAttributes
* @returns {LogItem}
*/
formatAttributes(attributes: UnformattedAttributes): LogAttributes;
formatAttributes(
attributes: UnformattedAttributes,
additionalLogAttributes: LogAttributes
): LogItem;

/**
* It formats a given Error parameter.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { LogFormatter } from '.';
import { UnformattedAttributes } from '../types';
import { PowertoolLog } from '../types/formats';
import { LogAttributes, UnformattedAttributes } from '../types';
import { PowertoolsLog } from '../types/formats';
import { LogItem } from '../log';

/**
* This class is used to transform a set of log key-value pairs
Expand All @@ -9,15 +10,19 @@ import { PowertoolLog } from '../types/formats';
* @class
* @extends {LogFormatter}
*/
class PowertoolLogFormatter extends LogFormatter {
class PowertoolsLogFormatter extends LogFormatter {
/**
* It formats key-value pairs of log attributes.
*
* @param {UnformattedAttributes} attributes
* @returns {PowertoolLog}
* @param {LogAttributes} additionalLogAttributes
* @returns {PowertoolsLog}
*/
public formatAttributes(attributes: UnformattedAttributes): PowertoolLog {
return {
public formatAttributes(
attributes: UnformattedAttributes,
additionalLogAttributes: LogAttributes
): LogItem {
const baseAttributes: PowertoolsLog = {
cold_start: attributes.lambdaContext?.coldStart,
function_arn: attributes.lambdaContext?.invokedFunctionArn,
function_memory_size: attributes.lambdaContext?.memoryLimitInMB,
Expand All @@ -30,7 +35,13 @@ class PowertoolLogFormatter extends LogFormatter {
timestamp: this.formatTimestamp(attributes.timestamp),
xray_trace_id: attributes.xRayTraceId,
};

const powertoolLogItem = new LogItem({ attributes: baseAttributes });

powertoolLogItem.addAttributes(additionalLogAttributes);

return powertoolLogItem;
}
}

export { PowertoolLogFormatter };
export { PowertoolsLogFormatter };
2 changes: 1 addition & 1 deletion packages/logger/src/formatter/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './LogFormatter';
export * from './LogFormatterInterface';
export * from './PowertoolLogFormatter';
export * from './PowertoolsLogFormatter';
10 changes: 3 additions & 7 deletions packages/logger/src/log/LogItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@ import { LogAttributes } from '../types';
class LogItem implements LogItemInterface {
private attributes: LogAttributes = {};

public constructor(params: {
baseAttributes: LogAttributes;
persistentAttributes: LogAttributes;
}) {
public constructor(params: { attributes: LogAttributes }) {
// Add attributes in the log item in this order:
// - Base attributes supported by the Powertool by default
// - Persistent attributes provided by developer, not formatted
// - Persistent attributes provided by developer, not formatted (done later)
// - Ephemeral attributes provided as parameters for a single log item (done later)
this.addAttributes(params.baseAttributes);
this.addAttributes(params.persistentAttributes);
this.addAttributes(params.attributes);
}

public addAttributes(attributes: LogAttributes): LogItem {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { LogAttributes, LogLevel } from '..';

type PowertoolLog = LogAttributes & {
type PowertoolsLog = LogAttributes & {
/**
* timestamp
*
Expand Down Expand Up @@ -90,4 +90,4 @@ type PowertoolLog = LogAttributes & {
lambda_request_id?: string;
};

export type { PowertoolLog };
export type { PowertoolsLog };
2 changes: 1 addition & 1 deletion packages/logger/src/types/formats/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './PowertoolLog';
export * from './PowertoolsLog';
34 changes: 17 additions & 17 deletions packages/logger/tests/unit/Logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '@aws-lambda-powertools/commons';
import { createLogger, Logger } from '../../src';
import { EnvironmentVariablesService } from '../../src/config';
import { PowertoolLogFormatter } from '../../src/formatter';
import { PowertoolsLogFormatter } from '../../src/formatter';
import {
ClassThatLogs,
LogJsonIndent,
Expand Down Expand Up @@ -798,7 +798,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: 0,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand Down Expand Up @@ -1621,7 +1621,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand All @@ -1644,7 +1644,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand All @@ -1667,7 +1667,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand Down Expand Up @@ -1733,7 +1733,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand All @@ -1756,7 +1756,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand All @@ -1782,7 +1782,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand All @@ -1805,7 +1805,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 20,
logLevelThresholds: {
...logLevelThresholds,
Expand Down Expand Up @@ -1849,7 +1849,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand All @@ -1872,7 +1872,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand Down Expand Up @@ -1902,7 +1902,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: INDENTATION,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand Down Expand Up @@ -1943,7 +1943,7 @@ describe('Class: Logger', () => {
envVarsService: expect.any(EnvironmentVariablesService),
logEvent: false,
logIndentation: 0,
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
logLevel: 8,
logLevelThresholds: {
...logLevelThresholds,
Expand All @@ -1970,7 +1970,7 @@ describe('Class: Logger', () => {

test('child logger should have the same logFormatter as its parent', () => {
// Prepare
class MyCustomLogFormatter extends PowertoolLogFormatter {}
class MyCustomLogFormatter extends PowertoolsLogFormatter {}
const parentLogger = new Logger({
logFormatter: new MyCustomLogFormatter(),
});
Expand All @@ -1988,7 +1988,7 @@ describe('Class: Logger', () => {

test('child logger with custom logFormatter in options should have provided logFormatter', () => {
// Prepare
class MyCustomLogFormatter extends PowertoolLogFormatter {}
class MyCustomLogFormatter extends PowertoolsLogFormatter {}
const parentLogger = new Logger();

// Act
Expand All @@ -1999,7 +1999,7 @@ describe('Class: Logger', () => {
// Assess
expect(parentLogger).toEqual(
expect.objectContaining({
logFormatter: expect.any(PowertoolLogFormatter),
logFormatter: expect.any(PowertoolsLogFormatter),
})
);

Expand All @@ -2012,7 +2012,7 @@ describe('Class: Logger', () => {

test('child logger should have exact same attributes as the parent logger created with all non-default options', () => {
// Prepare
class MyCustomLogFormatter extends PowertoolLogFormatter {}
class MyCustomLogFormatter extends PowertoolsLogFormatter {}
class MyCustomEnvironmentVariablesService extends EnvironmentVariablesService {}

const options: ConstructorOptions = {
Expand Down
Loading