Security News
PyPI’s New Archival Feature Closes a Major Security Gap
PyPI now allows maintainers to archive projects, improving security and helping users make informed decisions about their dependencies.
@serverless-guru/logger
Advanced tools
Logger is an opinionated logger utility for Javascript. Its aim is to simplify log analysis with CloudWatchLogs Insight.
{
"timestamp": 1729066777619,
"service": "myService",
"level": "INFO",
"correlationId": "092f5cf0-d1c8-4a71-a8a0-3c86aeb1c212",
"message": "my message",
"context": {
"handlerNamespace": "multiply",
"factor": 2
},
"payload": {
"key1": "value1",
"key2": 3,
"key3": {
"key31": "value31"
}
}
}
logger.error('global error', {key1: 'value1'})
{
"timestamp": 1729066777619,
"service": "myService",
"level": "ERROR",
"correlationId": "3bfd61c4-8934-4ae9-b646-d57144094986",
"message": "invalid factor",
"context": {
"handlerNamespace": "multiply",
"factor": 2
},
"payload": {
"key1": "value1"
}
}
logger.error('global error', new RangeError('invalid factor', {
cause: {
factor: event.factor,
limit: 10,
reason: 'too big'
}
}))
{
"timestamp": 1729066777619,
"service": "myService",
"level": "ERROR",
"correlationId": "3bfd61c4-8934-4ae9-b646-d57144094986",
"message": "global error",
"context": {
"handlerNamespace": "multiply",
"factor": 2
},
"error": {
"name": "RangeError",
"location": "/path/to/file.js:341",
"message": "invalid factor",
"stack": "RangeError: invalid factor\n at main (/path/to/file.js:341:15)\n at /path/to/file2.js:953:30\n at new Promise (<anonymous>)\n at AwsInvokeLocal.invokeLocalNodeJs (/path/to/file3.js:906:12)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)",
"cause": {
"factor": 12,
"limit": 10,
"reason": "too big"
}
}
}
npm i --save @serverless-guru/logger
The Logger
instance can be re-used across modules, allowing to keep globally defined context keys.
helpers/logger.js
const { Logger } = require("@serverless-guru/logger");
const logger = new Logger("myService", "myFirstApplication");
const metricUnits = Logger.METRIC_UNITS;
module.exports = { logger, metricUnits };
helpers/math.js
const { logger } = require('./logger')
export const multiply = async (n, factor) => {
const sleepMs = Math.floor(Math.random() * 1000 * factor)
await delay(sleepMs)
const result = n * factor
logger.debug('Multiply', { n, duration: sleepMs, result })
return result
}
const delay = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms))
}
handlers/multiply.js
const { logger, metricUnits } = require("../helpers/logger.js");
const LOG_FORMAT = process.env.AWS_LAMBDA_LOG_FORMAT || 'Text'
const main = async (event, context) => {
try {
logger.setCorrelationId(context.awsRequestId)
logger.addContextKey({
handlerNamespace: 'multiply',
logFormat: LOG_FORMAT,
})
logger.logInputEvent({ event });
if (event.factor) {
logger.addContextKey({ factor: event.factor })
if (event.factor > 10) {
const cause = { factor: event.factor, limit: 10, reason: 'too big' }
logger.error('invalid factor', cause)
throw new RangeError('invalid factor', { cause })
}
}
const start = new Date().getTime()
const promises = [1, 2, 3, 4, 5].map((n) => multiply(n, event.factor || 1))
const result = await Promise.all(promises)
const end = new Date().getTime()
logger.info('Result', { result }, {}, ['factor'])
logger.metric('multiply', {
name: 'Duration',
unit: metricUnits.Milliseconds,
value: end - start,
dimensions: [['LOG_FORMAT', LOG_FORMAT]],
})
} catch(error) {
logger.error('global error', error)
} finally {
logger.clearLogContext()
}
};
module.exports = { main };
Why define a correlationId when we already have a requestId provided by AWS?.
The requestId is unique inside a single Lambda invocation. A correlationId can be passed to other services and allows to extract logs from multiple services invoked during a specific activity.
Let's consider the case of a Lambda behind an API Gateway. This function sends a message to SQS, which is then processed by another Lambda function invoking a remote API.
setCorrelationId
method to assign the correlationId
from the payload to all log outputscorrelationId
is part of the payload sent to SQSsetCorrelationId
method to assign the correlationId
from the SQS event to all log outputscorrelationId
is added to the invocation payload of the remote API.Using CloudWatchLogs insight, it is now possible to query simultaneously both Lambda LogGroups, API Gateway LogGroup with a single simple query:
fields @timestamp, @message
| filter correlationId="092f5cf0-d1c8-4a71-a8a0-3c86aeb1c212"
| limit 200
To get the logs of all events for the specific correlationId
across multiple services.
const logger = new Logger(serviceName,applicationName,correlationId)
Set a correlationId used across all log statements. Useful when the correlationId is received as payload to the Lambda function.
logger.setCorrelationId(correlationId)
Retrieves the current correlationId. Useful when the correlationId needs to be passed to API calls or other service integrations.
const correlationId = logger.getCorrelationId()
Logs the object passed as argument when the environment variable LOG_EVENT is set to "true". Generally used to conditionally log the incoming event, but it can be used for any other payload too.
The message key will always be Input Event
.
logger.logInputEvent(payload)
To conditionally log the incoming event, the Lambda context and the environment variables:
logger.logInputEvent({event, context, env: process.env})
Add keys to the context object. Keys added to the context are available in all log outputs under the top level context
key.
useful to automatically add values to all future logs.
logger.addContextKey(contextObject)
Clears the all context keys. This needs to be invoked at the end of each Lambda invocation to avoid re-using context keys across subsequent invocation.
logger.clearLogContext()
Prints a log message.
logger.log(level, message, payload, context, sensitiveAttributes)
info
, debug
, warn
, error
message
. It is good practice to keep it concise and describe the activity. Re-use the same message across multiple logs, identify the individual activities using context or payload values.Any key, be it in the payload
or the context
, having one of this values will be masked in the output:
Masking can be disabled, by setting the environment variable LOG_MASK="false"
.
This generates a log output in EMF format, creating a metric in Cloudwatch Metrics.
The metrics will be available under the namespace defined by this.applicationName
.
logger.metric(activity: string, meta: MetricMeta)
[[name1, value1], [name2, value2]]
Note: To be able to use EMF, your log group needs to be sert to standard (default) and not infrequent access.
Lambda allows to use CloudWatchLogs Structured format (recommended), which not only stores the logs in JSON, but also allows to set the log level directly on the log group.
Serverless V3 doesn't allow to set the format directly from the function. You need to configure it via Cloudformation resources by extending the definition of the function generated by the framework.
The logical key for a function in Cloudformation is the logical key of the function with the suffix LambaFunction
.
service: myService
provider:
name: aws
runtime: nodejs20.x
architecture: 'arm64'
functions:
Multiply:
handler: src/handlers/multiply.handler
name: multiply
environment:
LOG_EVENT: 'true'
resources:
resources:
MultiplyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
LoggingConfig:
LogFormat: JSON
ApplicationLogLevel: WARN
SystemLogLevel: INFO
With Serverless v4, the logFormat can be directly defined in the framework definition, either globally under provider
or per function.
service: myService
provider:
name: aws
runtime: nodejs20.x
architecture: 'arm64'
logs:
lambda:
logFormat: JSON
applicationLogLevel: WARN
systemLogLevel: INFO
functions:
Multiply:
handler: src/handlers/multiply.handler
name: multiply
environment:
LOG_EVENT: 'true'
logs:
logFormat: JSON
applicationLogLevel: WARN
systemLogLevel: INFO
FAQs
Common logger utility
We found that @serverless-guru/logger demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PyPI now allows maintainers to archive projects, improving security and helping users make informed decisions about their dependencies.
Research
Security News
Malicious npm package postcss-optimizer delivers BeaverTail malware, targeting developer systems; similarities to past campaigns suggest a North Korean connection.
Security News
CISA's KEV data is now on GitHub, offering easier access, API integration, commit history tracking, and automated updates for security teams and researchers.