AWS Cloudwatch Logger
![Coverage Status](https://coveralls.io/repos/github/car-throttle/aws-cloudwatch-logger/badge.svg?branch=master)
Send your application logs from your Docker containers directly to AWS Cloudwatch.
This is particularly ideal if you're running containers on AWS EC2 Container Service,
although this has also been used to send logs from containers running on self-hosted hardware.
Usage
$ npm install --save @car-throttle/aws-cloudwatch-logger
const CloudwatchLogger = require('@car-throttle/aws-cloudwatch-logger');
const logger = CloudwatchLogger.createLogger();
logger.debug('This is a lovely debug message!');
logger.info('Some information for you', { this: 'that' });
logger.warn('Important warning for you');
logger.error(new Error('Oh man, this errored'));
In development, these will be lovely util.inspect
'd objects, but in production the format will be minified to:
{"timestamp":"2017-08-23T21:37:51.574Z","type":"debug","message":"This is a lovely debug message!"}
{"timestamp":"2017-08-23T21:37:51.574Z","type":"info","message":"Some information for you","data":{"this":"that"}}
{"timestamp":"2017-08-23T21:37:51.574Z","type":"warn","message":"Important warning for you"}
{"timestamp":"2017-08-23T21:37:51.574Z","type":"error","error":{"code":null,"name":"Error","message":"Oh man, this errored","stack":["Error: Oh man, this errored","at Object.<anonymous> (/Users/james/Sites/Car-Throttle/aws-cloudwatch-logger/example.js:17:14)","at Module._compile (module.js:569:30)","...","at bootstrap_node.js:575:3"]}}
Middleware
const CloudwatchLogger = require('@car-throttle/aws-cloudwatch-logger');
const crypto = require('crypto');
const express = require('express');
const app = express();
const logger = CloudwatchLogger.createLogger();
app.use(CloudwatchLogger.createMiddleware());
app.get('/', (req, res) => res.json('Hello world!'));
app.post('/', (req, res) => res.status(201).json({
entry_id: crypto.randomBytes(12).toString('hex'),
success: true,
}));
And of course in production they will be minified.
API
const CloudwatchLogger = require('@car-throttle/aws-cloudwatch-logger');
const uuid = require('uuid');
const logger = CloudwatchLogger.createLogger({
level: 'info',
stream: writeStream,
});
const middleware = CloudwatchLogger.createMiddleware({
logger,
header: 'X-Request-ID',
headerId: () => uuid.v4(),
req(req) {
return {
method: req.method,
url: req.originalUrl || req.url,
headers: req.headers,
path: req.path,
query: JSON.parse(JSON.stringify(req.query || {})),
body: JSON.parse(JSON.stringify(req.body || {})),
};
},
res(res) {
return {
statusCode: res.statusCode,
headers: res._headers || {},
};
},
format(record, req, res) {
if (req.user && req.user.id) record.req.user = { id: req.user.id };
},
});
CloudwatchLogger.formatErr(new Error('This is not the error you are looking for, move along!'));
Interesting use cases
Attaching the error to the request log
Attaching the error object to res.error
and then reattaching it in the format
function right before the record is
sent to CloudWatch.
const CloudwatchLogger = require('@car-throttle/aws-cloudwatch-logger');
const express = require('express');
const app = express();
app.use(CloudwatchLogger.createMiddleware({
format(record, req, res) {
if (res.error) record.res.error = res.error;
},
}));
app.use((req, res, next) => {
const err = new Error(`Route not found: ${req.url}`);
err.code = 'ROUTE_NOT_FOUND';
err.name = 'NotFoundError';
err.status = 404;
next(err);
});
app.use((err, req, res, next) => {
const output = {
code: err.code || null,
name: err.name || 'Error',
message: err.message || `${err}`,
status: err.status || 500,
};
res.error = CloudwatchLogger.formatErr(err);
res.status(output.status).json(output);
});
Configuring ECS
- Enter the
awslogs-group
and awslogs-region
when you change the container definition log configuration to
awslogs
. - Create an ECS-task-definition IAM role and attach a policy to allow that role to add Cloudwatch logs.
- Don't forget to configure your ECS
instances!