├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── fluent-plugin-elb-log.gemspec ├── fluent.conf.sample ├── lib └── fluent │ └── plugin │ └── in_elb_log.rb └── test ├── helper.rb └── plugin └── in_elb_log.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | .irb_history 19 | .ruby-version 20 | .vscode 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | - CC_TEST_REPORTER_ID=bca5d9771c392e374d213de82dde44181a12b4c8b5d5bd37b249c81ca5bf32f1 4 | - GIT_COMMITTED_AT=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then git log -1 --pretty=format:%ct; else git log -1 --skip 1 --pretty=format:%ct; fi) 5 | language: ruby 6 | 7 | rvm: 8 | - 2.1 9 | - 2.2 10 | - 2.3.3 11 | - 2.4.1 12 | branches: 13 | only: 14 | - master 15 | 16 | before_script: 17 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 18 | - chmod +x ./cc-test-reporter 19 | after_script: 20 | - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT 21 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in fluent-plugin-elb-log.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 shinsaka 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amazon ELB log input plugin for fluentd 2 | 3 | [![Gem Version](https://badge.fury.io/rb/fluent-plugin-elb-log.svg)](https://badge.fury.io/rb/fluent-plugin-elb-log) 4 | [![Build Status](https://travis-ci.org/shinsaka/fluent-plugin-elb-log.svg?branch=master)](https://travis-ci.org/shinsaka/fluent-plugin-elb-log) 5 | [![Code Climate](https://codeclimate.com/github/shinsaka/fluent-plugin-elb-log/badges/gpa.svg)](https://codeclimate.com/github/shinsaka/fluent-plugin-elb-log) 6 | [![Test Coverage](https://codeclimate.com/github/shinsaka/fluent-plugin-elb-log/badges/coverage.svg)](https://codeclimate.com/github/shinsaka/fluent-plugin-elb-log/coverage) 7 | 8 | ## Overview 9 | - Amazon Web Services ELB log input plubin for fluentd 10 | 11 | ## Requirements 12 | 13 | | fluent-plugin-elb-log | fluentd | ruby | 14 | |-----------------------|------------|--------| 15 | | >= 0.3.0 | >= v0.14.0 | >= 2.1 | 16 | | < 0.3.0 | >= v0.12.0 | >= 1.9 | 17 | 18 | ## Installation 19 | 20 | $ fluentd-gem fluent-plugin-elb-log 21 | 22 | ## AWS ELB Settings 23 | - settings see: [Elastic Load Balancing](http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/enable-access-logs.html) 24 | - developer guide: [](http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/access-log-collection.html) 25 | 26 | ## Different from version 0.4.x 27 | - Using version 3 of the AWS SDK for Ruby. 28 | 29 | ## Support Application Load Balancer (ver 0.4.0 or later) 30 | - Support Access Logs for Application Load Balancer 31 | - https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html 32 | - Existing ELB is called Classic Load Balancer 33 | - http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/access-log-collection.html 34 | 35 | ## When SSL certification error 36 | log: 37 | ``` 38 | SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed 39 | ``` 40 | Do env setting follows: 41 | ``` 42 | SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt (If you using amazon linux) 43 | ``` 44 | 45 | ## Configuration 46 | 47 | ```config 48 | 49 | @type elb_log 50 | 51 | # following attibutes are required 52 | region 53 | s3_bucketname 54 | s3_prefix 55 | timestamp_file 56 | buf_file 57 | refresh_interval 58 | tag 59 | delete 60 | include_all_message 61 | start_time 62 | exclude_pattern_logfile_elb_name 63 | use_sqs 64 | 65 | # following attibutes are required if you don't use IAM Role 66 | access_key_id 67 | secret_access_key 68 | 69 | ``` 70 | 71 | `use_sqs` automatically creates an SQS queue named `fluent-plugin-elb-log-` 72 | and sets up the `all object create event` S3 event notification for the chosen S3 bucket. Stopping fluentd deletes both autmatically. 73 | To make it work, the following IAM policy should be attached to the instance IAM role or the user: 74 | ``` 75 | { 76 | "Version": "2012-10-17", 77 | "Statement": [ 78 | { 79 | "Sid": "FluentdPermissions", 80 | "Effect": "Allow", 81 | "Action": [ 82 | "sqs:DeleteMessage", 83 | "s3:GetObject", 84 | "sqs:ReceiveMessage", 85 | "sqs:DeleteQueue", 86 | "sqs:GetQueueAttributes", 87 | "s3:ListBucket", 88 | "s3:PutBucketNotification", 89 | "sqs:CreateQueue" 90 | ], 91 | "Resource": [ 92 | "arn:aws:sqs:*:123456789012:fluent-plugin-elb-log-*", 93 | "arn:aws:s3:::alb-logs-bucket/*", 94 | "arn:aws:s3:::alb-logs-bucket" 95 | ] 96 | } 97 | ] 98 | } 99 | ``` 100 | When `use_sqs` is false: 101 | - 300 seconds is a good value for the `refresh_interval` 102 | - the plugin executes processing the whole S3 bucket (with the respect of `s3_prefix` and `start_time`/`timestamp_file`) every `refresh_interval` 103 | 104 | When `use_sqs` is true: 105 | - `refresh_interval` of 30-60 seconds should be fine. 106 | - the plugin executes processing the whole S3 bucket (with the respect of `s3_prefix` and `start_time`/`timestamp_file`) only once (at start) and then 107 | polls the SQS queue every `refresh_interval`. 108 | 109 | ### Example setting 110 | ```config 111 | 112 | @type elb_log 113 | region us-east-1 114 | s3_bucketname my-elblog-bucket 115 | s3_prefix prodcution/web 116 | timestamp_file /tmp/elb_last_at.dat 117 | buf_file /tmp/fluentd-elblog.tmpfile 118 | refresh_interval 30 119 | tag elb.access 120 | delete false 121 | include_all_message false 122 | exclude_pattern_logfile_elb_name "^app\.(uat|qa)\." 123 | start_time 2025-02-27T10:45:00 124 | use_sqs true 125 | access_key_id XXXXXXXXXXXXXXXXXXXX 126 | secret_access_key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 127 | 128 | 129 | 130 | @type record_transformer 131 | 132 | timestamp ${record["request_creation_time"]} 133 | logfile_name ${record["key"]} 134 | 135 | remove_keys prefix,logfile_date,logfile_elb_name,logfile_hash,logfile_timestamp,logfile_timestamp_unixtime,key,time,s3_last_modified_unixtime 136 | 137 | 138 | 139 | # @type stdout 140 | @type opensearch 141 | hosts node-1,node-2,node-3 142 | port 9200 143 | scheme https 144 | logstash_format true 145 | logstash_prefix alb-logs 146 | user fluentd 147 | password secret 148 | ssl_verify true 149 | ca_file /etc/fluentd/root-ca.pem 150 | flush_interval 300s 151 | 152 | ``` 153 | 154 | ### json output example 155 | ```json 156 | { 157 | "account_id":"123456789012", 158 | "region":"ap-northeast-1", 159 | "logfile_date":"2015/06/15", 160 | "logfile_elb_name":"my-elb-name", 161 | "elb_ip_address":"52.0.0.0", 162 | "logfile_hash":"12squv5w", 163 | "logfile_timestamp":"20150615T0400Z", 164 | "key":"TEST/AWSLogs/123456789012/elasticloadbalancing/ap-northeast-1/2015/06/15/123456789012_elasticloadbalancing_ap-northeast-1_my-elb-name_20150615T0400Z_52.68.215.138_69squv5w.log", 165 | "prefix":"TEST", 166 | "elb_timestamp_unixtime":1434340800, 167 | "time":"2015-06-15T03:47:12.728427+0000", 168 | "elb":"my-elb-name", 169 | "client":"54.1.1.1", 170 | "client_port":"43759", 171 | "target":"10.0.0.1", 172 | "target_port":"80", 173 | "request_processing_time":4.0e-05, 174 | "target_processing_time":0.105048, 175 | "response_processing_time":2.4e-05, 176 | "elb_status_code":"200", 177 | "target_status_code":"200", 178 | "received_bytes":0, 179 | "sent_bytes":4622, 180 | "request_method":"GET", 181 | "request_uri":"https://my-elb-test.example.com/", 182 | "request_protocol":"HTTP/1.1", 183 | "user_agent":"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", 184 | "ssl_cipher":"DHE-RSA-AES128-SHA", 185 | "ssl_protocol":"TLSv1.2", 186 | "type":"http", 187 | "target_group_arn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/lbgrp1/605122a4e4ee9f2d", 188 | "trace_id": "\"Root=1-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx\"", 189 | "domain_name": "-", 190 | "chosen_cert_arn": "-", 191 | "matched_rule_priority": "0", 192 | "request_creation_time": "2099-10-26T06:10:03.050000Z", 193 | "actions_executed": "forward", 194 | "redirect_url": "-", 195 | "error_reason": "-", 196 | "target_port_list": "\"192.168.0.1:443\"", 197 | "target_status_code_list": "\"301\"", 198 | "classification": "-", 199 | "classification_reason": "-", 200 | "conn_trace_id": "TID_xxxxxxxxxxxxxxxxxxxxxxx" 201 | } 202 | ``` 203 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | require 'rake/testtask' 4 | Rake::TestTask.new(:test) do |test| 5 | test.libs << 'lib' << 'test' 6 | test.pattern = 'test/**/*.rb' 7 | test.verbose = true 8 | test.warning = false 9 | end 10 | 11 | desc 'Run tests for all' 12 | task :default => :test 13 | -------------------------------------------------------------------------------- /fluent-plugin-elb-log.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "fluent-plugin-elb-log" 7 | spec.version = "1.4.0" 8 | spec.authors = ["shinsaka","jazzl0ver"] 9 | spec.email = ["shinx1265@gmail.com"] 10 | spec.summary = "Amazon ELB log input plugin" 11 | spec.description = "Amazon ELB log input plugin for fluentd" 12 | spec.homepage = "https://github.com/jazzl0ver/fluent-plugin-elb-log" 13 | spec.license = "MIT" 14 | 15 | spec.files = `git ls-files -z`.split("\x0") 16 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 18 | spec.require_paths = ["lib"] 19 | 20 | spec.add_dependency "fluentd", ">= 0.14.0", "< 2" 21 | spec.add_dependency "aws-sdk-s3", "~> 1" 22 | spec.add_dependency "aws-sdk-ec2", "~> 1" 23 | 24 | spec.add_development_dependency "bundler", ">=1.17" 25 | spec.add_development_dependency "rake", "~> 12" 26 | spec.add_development_dependency "test-unit", "~> 3.2" 27 | spec.add_development_dependency "webmock", "~>3" 28 | spec.add_development_dependency "simplecov", "~>0" 29 | end 30 | -------------------------------------------------------------------------------- /fluent.conf.sample: -------------------------------------------------------------------------------- 1 | 2 | @type elb_log 3 | region us-east-1 4 | s3_bucketname my-elblog-bucket 5 | s3_prefix prodcution/web 6 | timestamp_file /tmp/elb_last_at.dat 7 | buf_file /tmp/fluentd-elblog.tmpfile 8 | refresh_interval 30 9 | tag elb.access 10 | use_sqs true 11 | access_key_id XXXXXXXXXXXXXXXXXXXX 12 | secret_access_key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 13 | 14 | 15 | 16 | @type record_transformer 17 | 18 | timestamp ${record["request_creation_time"]} 19 | logfile_name ${record["key"]} 20 | 21 | remove_keys prefix,logfile_date,logfile_elb_name,logfile_hash,logfile_timestamp,logfile_timestamp_unixtime,key,time,s3_last_modified_unixtime 22 | 23 | 24 | 25 | @type stdout 26 | 27 | -------------------------------------------------------------------------------- /lib/fluent/plugin/in_elb_log.rb: -------------------------------------------------------------------------------- 1 | require 'time' 2 | require 'zlib' 3 | require 'fileutils' 4 | require 'aws-sdk-s3' 5 | require 'aws-sdk-ec2' 6 | require 'aws-sdk-sqs' 7 | require 'fluent/input' 8 | require 'digest/sha1' 9 | 10 | class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input 11 | Fluent::Plugin.register_input('elb_log', self) 12 | 13 | helpers :timer 14 | 15 | LOGFILE_REGEXP = /^((?.+?)\/|)AWSLogs\/(?[0-9]{12})\/elasticloadbalancing\/(?.+?)\/(?[0-9]{4}\/[0-9]{2}\/[0-9]{2})\/[0-9]{12}_elasticloadbalancing_.+?_(?[^_]+)_(?[0-9]{8}T[0-9]{4}Z)_(?.+?)_(?.+)\.log(.gz)?$/ 16 | ACCESSLOG_REGEXP = /^((?[a-z0-9]+) )?(?