Skip to content

Instantly share code, notes, and snippets.

@larenelg
Last active May 25, 2022 06:53
Show Gist options
  • Save larenelg/326728ada62742dbd128b5ff202c451e to your computer and use it in GitHub Desktop.
Save larenelg/326728ada62742dbd128b5ff202c451e to your computer and use it in GitHub Desktop.
Forward Cloudwatch Logs to Seq Lambda
'use strict';
/*
Make sure you are logging to Console using the Compact JSON Formatter, e.g.
```
public class LambdaEntryPoint : APIGatewayHttpApiV2ProxyFunction
{
protected override void Init(IWebHostBuilder builder)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console(new CompactJsonFormatter())
.CreateLogger();
builder.UseStartup<Startup>().UseSerilog();
}
}
```
*/
const https = require('https');
const zlib = require('zlib');
const loggerConfig = {
hostname: process.env.SEQ_SERVER_HOSTNAME,
port: process.env.SEQ_SERVER_PORT,
apiKey: process.env.SEQ_API_KEY,
};
// entry point
exports.handler = (event, context, callback) => {
const payload = Buffer.from(event.awslogs.data, 'base64');
function postEventsToSeq(parsedEvents) {
const payload = parsedEvents.map(JSON.stringify).join('\n');
try {
const options = {
hostname: loggerConfig.hostname,
port: loggerConfig.port,
path: '/api/events/raw',
method: 'POST',
headers: {
'Content-Type': 'application/vnd.serilog.clef',
'Content-Length': payload.length,
'X-Seq-ApiKey': loggerConfig.apiKey
},
};
const req = https.request(options, (res) => {
console.log('Status Code:', res.statusCode);
let response = [];
res.on('data', (data) => {
response.push(data);
});
res.on('end', () => {
console.log('Response message:', response.join(''));
callback();
});
});
req.on('error', (err) => {
console.log('Problem with request:', err.toString());
callback(err);
});
// write data to request body
req.write(payload);
req.end();
} catch (ex) {
console.log(ex.message);
callback(ex.message);
}
}
function checkIfClef(logEvent) {
try {
const maybeClef = JSON.parse(logEvent.message.trim());
if (maybeClef.hasOwnProperty("@t")) {
return maybeClef;
} else {
throw new Error('Wrong format JSON');
}
} catch (ex) {
// a text-only log, probably from AWS
return {
"@t": new Date(logEvent.timestamp).toISOString(),
"@m": logEvent.message.trim(), // remove trailing \n
};
}
}
zlib.gunzip(payload, (error, result) => {
if (error) {
callback(error);
} else {
const resultParsed = JSON.parse(result.toString('ascii'));
const parsedEvents = resultParsed.logEvents.map((logEvent) => checkIfClef(logEvent));
postEventsToSeq(parsedEvents);
}
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment