UNPKG

alb-log-parser

Version:

A basic parser for ALB access logs, forked from elb log parser git@github.com:toshihirock/node-elb-log-parser.git

134 lines (122 loc) 4.2 kB
#! /usr/bin/env node /** * Field names, in order of appearance in the ALB log lines */ const fields = [ 'type', 'timestamp', 'elb', 'client:port', 'target:port', 'request_processing_time', 'target_processing_time', 'response_processing_time', 'elb_status_code', 'target_status_code', 'received_bytes', 'sent_bytes', 'request', 'user_agent', 'ssl_cipher', 'ssl_protocol', 'target_group_arn', 'trace_id', 'domain_name', 'chosen_cert_arn', 'matched_rule_priority', 'request_creation_time', 'actions_executed', 'redirect_url', 'error_reason', 'target:port_list', 'target_status_code_list', 'classification', 'classification_reason' ] module.exports = function (line) { // // Trailing newline? NOTHX // if (line.match(/\n$/)) { line = line.slice(0, line.length - 1); } const parsed = parseAlbLogLine(line) return parsed }; if (require.main === module) { var split = require('split'); var Transform = require('stream').Transform; process.stdin .pipe(split()) .pipe(new Transform({ decodeStrings: false, transform: function (line, encoding, callback) { if (line) { this.push(JSON.stringify(module.exports(line)) + '\n'); } callback(); } })) .pipe(process.stdout); } /** * Parse one line of an AWS Application Load Balancer log * * @param {string} line */ function parseAlbLogLine(line) { const parsed = {} let counter = 0 let finished = false let quoteSeen = false let element = '' for (const c of line + ' ') { if (finished) { if (element) { let fieldName = fields[counter] // Convert all numeric strings to numbers if (element.match(/^-?\d+.?\d*$/)) { element = Number(element) } if (fieldName === 'request') { _decorateFromRequest(element, parsed) } // H/T @jason-linthwaite (https://github.com/jason-linthwaite) if (!fieldName) continue if (fieldName.match(/^\S+?:port$/)) { _decorateFromPortField(fieldName, element, parsed) } else { parsed[fieldName] = element } element = '' counter++ } finished = false } // treat whitespace as a delimiter *except* when inside of quotes if (c.match(/^\s$/) && !quoteSeen) finished = true if (c === '"') { // beginning or end of a quote delimited string if (quoteSeen) finished = true // if we've seen one quote, this closes the quote delimited string quoteSeen = !quoteSeen // Toggle the quote flag } else { // Append the character to the element unless this character terminates the element if (!finished) element += c } } return parsed } function _decorateFromPortField(fieldName, element, parsed) { // We don't actually send back 'client:port' and 'target:port'; we send back // 'client', 'client_port', 'target', and 'target_port' const field = fieldName.match(/^(\S+?):port/)[1] const sepIndex = element.lastIndexOf(':') if (sepIndex === -1) { parsed[field] = '-' parsed[`${field}_port`] = -1 } else { parsed[field] = element.substring(0, sepIndex) parsed[`${field}_port`] = parseInt(element.substring(sepIndex + 1, element.length)) } return parsed } /** * Helper for parseAlbLogLine * * @param {string} element * @param {object} parsed */ function _decorateFromRequest(element, parsed) { const url = require('url'); const [request_method, request_uri, request_http_version] = element.split(/\s+/) parsed.request_method = request_method parsed.request_uri = request_uri parsed.request_http_version = request_http_version const parsedUrl = url.parse(request_uri) parsed.request_uri_scheme = parsedUrl.protocol parsed.request_uri_host = parsedUrl.hostname if (parsedUrl.port) { parsed.request_uri_port = parseInt(parsedUrl.port) } parsed.request_uri_path = parsedUrl.pathname parsed.request_uri_query = parsedUrl.query return parsed }