bind_log_analyzer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +7 -0
- data/Gemfile +6 -0
- data/README.md +126 -0
- data/Rakefile +110 -0
- data/bin/bind_log_analyzer +98 -0
- data/bind_log_analyzer.gemspec +24 -0
- data/db/migrate/20120329151738_create_logs.rb +11 -0
- data/doc/database.yml +7 -0
- data/doc/update_bind_log_analyzer.sh +16 -0
- data/lib/bind_log_analyzer/base.rb +87 -0
- data/lib/bind_log_analyzer/connector.rb +47 -0
- data/lib/bind_log_analyzer/exceptions.rb +4 -0
- data/lib/bind_log_analyzer/version.rb +3 -0
- data/lib/bind_log_analyzer.rb +6 -0
- data/lib/models/log.rb +2 -0
- data/spec/bind_log_analyzer_spec.rb +86 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +101 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Bind Log Analyzer
|
2
|
+
|
3
|
+
Simple analysis and SQL storage for Bind DNS server's logs
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
This gem was tested with:
|
8
|
+
|
9
|
+
- ruby-1.9.3-p125
|
10
|
+
- rubygem (1.8.15)
|
11
|
+
- bundler (1.0.21)
|
12
|
+
- activerecord (3.2.2)
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Just install the gem:
|
17
|
+
|
18
|
+
gem install bind_log_analyzer
|
19
|
+
|
20
|
+
The gem requires **active_record** but you probably need to install the right adapter. As example, if you'll use MySQL, install the **mysql2** gem.
|
21
|
+
|
22
|
+
## Configuration
|
23
|
+
|
24
|
+
### Bind
|
25
|
+
|
26
|
+
To configure **Bind** add these lines to _/etc/bind/named.conf.options_ (or whatever your s.o. and bind installation require)
|
27
|
+
|
28
|
+
logging{
|
29
|
+
channel "querylog" {
|
30
|
+
file "/var/log/bind/query.log";
|
31
|
+
print-time yes;
|
32
|
+
};
|
33
|
+
|
34
|
+
category queries { querylog; };
|
35
|
+
};
|
36
|
+
|
37
|
+
Restart bind and make sure than the _query.log_ file contains lines as this:
|
38
|
+
|
39
|
+
28-Mar-2012 16:48:19.694 client 192.168.10.38#58767: query: www.github.com IN A + (192.168.10.1)
|
40
|
+
|
41
|
+
or the regexp will fail :(
|
42
|
+
|
43
|
+
### Database
|
44
|
+
|
45
|
+
To store the logs you can use every database supported by ActiveRecord. Just create a database and a user with the right privileges. You can provide the -s flag to *BindLogAnalyzer* to make it create the table. Otherwise create it by yourself.
|
46
|
+
This is the MySQL CREATE TABLE syntax:
|
47
|
+
|
48
|
+
CREATE TABLE `logs` (
|
49
|
+
`id` int(11) NOT NULL AUTO_INCREMENT,
|
50
|
+
`date` datetime NOT NULL,
|
51
|
+
`client` varchar(255) NOT NULL,
|
52
|
+
`query` varchar(255) NOT NULL,
|
53
|
+
`q_type` varchar(255) NOT NULL,
|
54
|
+
`server` varchar(255) NOT NULL,
|
55
|
+
PRIMARY KEY (`id`)
|
56
|
+
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;
|
57
|
+
|
58
|
+
## Usage
|
59
|
+
|
60
|
+
Use the provided --help to get various options available. This is the default help:
|
61
|
+
|
62
|
+
-h, --help Display this screen
|
63
|
+
-v, --verbose LEVEL Enables verbose output. Use level 1 for WARN, 2 for INFO and 3 for DEBUG
|
64
|
+
-s, --setup Creates the needed tables in the database.
|
65
|
+
-f, --file FILE Indicates the log file to parse. It's mandatory.
|
66
|
+
-c, --config CONFIG A yaml file containing the database configurations under the "database" entry
|
67
|
+
-a, --adapter ADAPTER The database name to save the logs
|
68
|
+
-d, --database DATABASE The database name to save the logs
|
69
|
+
-H, --host HOST The address (IP, hostname or path) of the database
|
70
|
+
-P, --port PORT The port of the database
|
71
|
+
-u, --user USER The username to be used to connect to the database
|
72
|
+
-p, --password PASSWORD The password of the user
|
73
|
+
|
74
|
+
There's only one mandatory argument which is **--file FILE**. With this flag you pass the Bind log file to analyze to *BindLogAnalyzer*.
|
75
|
+
|
76
|
+
The first time you launch *BindLogAnalyzer* you can use the **-s|--setup** flag to make it create the table (using ActiveRecord::Migration).
|
77
|
+
The database credentials can be provided using the needed flags or creating a YAML file containing all the informations under the **database** key. This is an example:
|
78
|
+
|
79
|
+
database:
|
80
|
+
adapter: mysql2
|
81
|
+
database: bindloganalyzer
|
82
|
+
host: localhost
|
83
|
+
port: 3306
|
84
|
+
username: root
|
85
|
+
password:
|
86
|
+
|
87
|
+
## Automatization
|
88
|
+
|
89
|
+
A good way to use this script is to let it be launched by **logrotate** so create the _/etc/logrotate.d/bind_ file with this content:
|
90
|
+
|
91
|
+
/var/log/named/query.log {
|
92
|
+
weekly
|
93
|
+
missingok
|
94
|
+
rotate 8
|
95
|
+
compress
|
96
|
+
delaycompress
|
97
|
+
notifempty
|
98
|
+
create 644 bind bind
|
99
|
+
postrotate
|
100
|
+
if [ -e /var/log/named/query.log.1 ]; then
|
101
|
+
exec su - YOUR_USER -c '/usr/local/bin/update_bind_log_analyzer.sh /var/log/named/query.log.1'
|
102
|
+
fi
|
103
|
+
endscript
|
104
|
+
}
|
105
|
+
|
106
|
+
The script **/usr/local/bin/update_bind_log_analyzer.sh** can be wherever you prefer. Its typical content if you use RVM and a dedicated gemset for *BindLogAnalyzer*, can be:
|
107
|
+
|
108
|
+
#!/bin/bash
|
109
|
+
|
110
|
+
# *************************** #
|
111
|
+
# EDIT THESE VARS #
|
112
|
+
# *************************** #
|
113
|
+
BLA_RVM_GEMSET="1.9.3-p125@bind_log_analyzer"
|
114
|
+
BLA_USER="my_username"
|
115
|
+
BLA_DB_FILE="/etc/bind_log_analyzer/database.yml"
|
116
|
+
|
117
|
+
# *************************** #
|
118
|
+
# DO NOT EDIT BELOW THIS LINE #
|
119
|
+
# *************************** #
|
120
|
+
. /home/$BLA_USER/.rvm/scripts/rvm && source "/home/$BLA_USER/.rvm/scripts/rvm"
|
121
|
+
rvm use $BLA_RVM_GEMSET
|
122
|
+
bind_log_analyzer --config $BLA_DB_FILE --file $1
|
123
|
+
|
124
|
+
## To do
|
125
|
+
|
126
|
+
- Add a web interface to show the queries (with awesome graphs, obviously :)
|
data/Rakefile
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require "bind_log_analyzer/version"
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
require "bundler/gem_tasks"
|
8
|
+
|
9
|
+
require 'yaml'
|
10
|
+
require 'logger'
|
11
|
+
require 'active_record'
|
12
|
+
|
13
|
+
namespace :spec do
|
14
|
+
desc "Run all specs"
|
15
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
16
|
+
#t.pattern = 'spec/**/*_spec.rb'
|
17
|
+
t.rspec_opts = ['--options', 'spec/spec.opts']
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Generate code coverage"
|
21
|
+
RSpec::Core::RakeTask.new(:coverage) do |t|
|
22
|
+
#t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
23
|
+
require 'simplecov'
|
24
|
+
SimpleCov.start
|
25
|
+
#t.rcov = true
|
26
|
+
#t.rcov_opts = ['--exclude', 'spec']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
task :build do
|
31
|
+
system "gem build bind_log_analyzer.gemspec"
|
32
|
+
end
|
33
|
+
|
34
|
+
task :release => :build do
|
35
|
+
system "gem push pkg/bind_log_analyzer-#{BindLogAnalyzer::VERSION}.gem"
|
36
|
+
end
|
37
|
+
|
38
|
+
namespace :db do
|
39
|
+
def create_database config
|
40
|
+
options = {:charset => 'utf8', :collation => 'utf8_unicode_ci'}
|
41
|
+
|
42
|
+
create_db = lambda do |config|
|
43
|
+
ActiveRecord::Base.establish_connection config.merge('database' => nil)
|
44
|
+
ActiveRecord::Base.connection.create_database config['database'], options
|
45
|
+
ActiveRecord::Base.establish_connection config
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
create_db.call config
|
50
|
+
rescue Mysql::Error => sqlerr
|
51
|
+
if sqlerr.errno == 1405
|
52
|
+
print "#{sqlerr.error}. \nPlease provide the root password for your mysql installation\n>"
|
53
|
+
root_password = $stdin.gets.strip
|
54
|
+
|
55
|
+
grant_statement = <<-SQL
|
56
|
+
GRANT ALL PRIVILEGES ON #{config['database']}.*
|
57
|
+
TO '#{config['username']}'@'localhost'
|
58
|
+
IDENTIFIED BY '#{config['password']}' WITH GRANT OPTION;
|
59
|
+
SQL
|
60
|
+
|
61
|
+
create_db.call config.merge('database' => nil, 'username' => 'root', 'password' => root_password)
|
62
|
+
else
|
63
|
+
$stderr.puts sqlerr.error
|
64
|
+
$stderr.puts "Couldn't create database for #{config.inspect}, charset: utf8, collation: utf8_unicode_ci"
|
65
|
+
$stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['charset']
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
task :environment do
|
71
|
+
DATABASE_ENV = ENV['DATABASE_ENV'] || 'development'
|
72
|
+
MIGRATIONS_DIR = ENV['MIGRATIONS_DIR'] || 'db/migrate'
|
73
|
+
end
|
74
|
+
|
75
|
+
task :configuration => :environment do
|
76
|
+
@config = YAML.load_file('config/databases.yml')[DATABASE_ENV]
|
77
|
+
end
|
78
|
+
|
79
|
+
task :configure_connection => :configuration do
|
80
|
+
ActiveRecord::Base.establish_connection @config
|
81
|
+
ActiveRecord::Base.logger = Logger.new STDOUT if @config['logger']
|
82
|
+
end
|
83
|
+
|
84
|
+
desc 'Create the database from config/database.yml for the current DATABASE_ENV'
|
85
|
+
task :create => :configure_connection do
|
86
|
+
create_database @config
|
87
|
+
end
|
88
|
+
|
89
|
+
desc 'Drops the database for the current DATABASE_ENV'
|
90
|
+
task :drop => :configure_connection do
|
91
|
+
ActiveRecord::Base.connection.drop_database @config['database']
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'Migrate the database (options: VERSION=x, VERBOSE=false).'
|
95
|
+
task :migrate => :configure_connection do
|
96
|
+
ActiveRecord::Migration.verbose = true
|
97
|
+
ActiveRecord::Migrator.migrate MIGRATIONS_DIR, ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
|
101
|
+
task :rollback => :configure_connection do
|
102
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
103
|
+
ActiveRecord::Migrator.rollback MIGRATIONS_DIR, step
|
104
|
+
end
|
105
|
+
|
106
|
+
desc "Retrieves the current schema version number"
|
107
|
+
task :version => :configure_connection do
|
108
|
+
puts "Current version: #{ActiveRecord::Migrator.current_version}"
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bind_log_analyzer"
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
@filename = nil
|
7
|
+
@log_level = 0
|
8
|
+
@database_confs = nil
|
9
|
+
@setup_database = false
|
10
|
+
@db_yaml = nil
|
11
|
+
@db_adapter = 'mysql2'
|
12
|
+
@db_host = nil
|
13
|
+
@db_database = nil
|
14
|
+
@db_port = nil
|
15
|
+
@db_username = nil
|
16
|
+
@db_password = nil
|
17
|
+
|
18
|
+
optparse = OptionParser.new do |opts|
|
19
|
+
# Set a banner, displayed at the top
|
20
|
+
# of the help screen.
|
21
|
+
opts.banner = "Usage: ./#{File.basename(__FILE__)} (--file|-f FILENAME [--setup|-s] [--verbose|-v (1|2|3)] [--database|-d database_name] [--config|-c database_yaml_configurations] [--host|-H database_hostname] [--port|-p database_port] [--user|-u database_username] [--password|-p database_password] | --help|-h)"
|
22
|
+
|
23
|
+
# This displays the help screen
|
24
|
+
opts.on( '-h', '--help', 'Display this screen' ) do
|
25
|
+
puts opts
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on( '-v', '--verbose LEVEL', "Enables verbose output. Use level 1 for WARN, 2 for INFO and 3 for DEBUG" ) do |opt|
|
30
|
+
case
|
31
|
+
when 1 === opt.to_i
|
32
|
+
@log_level = 1
|
33
|
+
when 2 === opt.to_i
|
34
|
+
@log_level = 2
|
35
|
+
when opt.to_i >= 3
|
36
|
+
@log_level = 3
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on( '-s', '--setup', "Creates the needed tables in the database." ) do |opt|
|
41
|
+
@setup_database = true
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on( '-f', '--file FILE', "Indicates the log file to parse. It's mandatory." ) do |opt|
|
45
|
+
@filename = opt
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on( '-c', '--config CONFIG', 'A yaml file containing the database configurations under the "database" entry' ) do |opt|
|
49
|
+
@db_yaml = opt
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on( '-a', '--adapter ADAPTER', 'The database name to save the logs' ) do |opt|
|
53
|
+
@db_adapter = opt
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on( '-d', '--database DATABASE', 'The database name to save the logs' ) do |opt|
|
57
|
+
@db_database = opt
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.on( '-H', '--host HOST', 'The address (IP, hostname or path) of the database' ) do |opt|
|
61
|
+
@db_host = opt
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on( '-P', '--port PORT', 'The port of the database' ) do |opt|
|
65
|
+
@db_port = opt
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on( '-u', '--user USER', 'The username to be used to connect to the database' ) do |opt|
|
69
|
+
@db_username = opt
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on( '-p', '--password PASSWORD', 'The password of the user' ) do |opt|
|
73
|
+
@db_password = opt
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
optparse.parse!
|
78
|
+
|
79
|
+
if @db_yaml
|
80
|
+
@database_confs = @db_yaml
|
81
|
+
elsif @db_username || @db_password || @db_host || @db_port || @db_database
|
82
|
+
@database_confs = {
|
83
|
+
adapter: @db_adapter,
|
84
|
+
username: @db_username,
|
85
|
+
password: @db_password,
|
86
|
+
host: @db_host,
|
87
|
+
port: @db_port,
|
88
|
+
database: @db_database
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
if @filename
|
93
|
+
@base = BindLogAnalyzer::Base.new(@database_confs, @filename, @setup_database, @log_level)
|
94
|
+
@base.analyze
|
95
|
+
else
|
96
|
+
puts optparse.banner
|
97
|
+
exit -1
|
98
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "bind_log_analyzer/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "bind_log_analyzer"
|
7
|
+
s.version = BindLogAnalyzer::VERSION
|
8
|
+
s.authors = ["Tommaso Visconti"]
|
9
|
+
s.email = ["tommaso.visconti@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/tommyblue/Bind-Log-Analyzer"
|
11
|
+
s.summary = %q{Log analysis and SQL storage for Bind DNS server}
|
12
|
+
s.description = %q{BindLogAnalyzer analyzes a Bind query log file and stores it's data into a SQL database (ActiveRecord is used for this feature)}
|
13
|
+
|
14
|
+
s.rubyforge_project = "bind_log_analyzer"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "activerecord"
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
s.add_development_dependency "simplecov"
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateLogs < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :logs do |t|
|
4
|
+
t.datetime :date, :null => false
|
5
|
+
t.string :client, :null => false
|
6
|
+
t.string :query, :null => false
|
7
|
+
t.string :q_type, :null => false
|
8
|
+
t.string :server, :null => false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/doc/database.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# *************************** #
|
4
|
+
# EDIT THESE VARS #
|
5
|
+
# *************************** #
|
6
|
+
BLA_PATH="~/Devel/bind-log-analyzer"
|
7
|
+
BLA_RVM_GEMSET="1.9.3-p125@bind_log_analyzer"
|
8
|
+
BLA_USER="my_username"
|
9
|
+
|
10
|
+
# *************************** #
|
11
|
+
# DO NOT EDIT BELOW THIS LINE #
|
12
|
+
# *************************** #
|
13
|
+
cd $BLA_PATH
|
14
|
+
. /home/$BLA_USER/.rvm/scripts/rvm && source "/home/$BLA_USER/.rvm/scripts/rvm"
|
15
|
+
rvm use $BLA_RVM_GEMSET
|
16
|
+
$BLA_PATH/bin/bind_log_analyzer -f $1
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'bind_log_analyzer/exceptions'
|
2
|
+
require 'bind_log_analyzer/connector'
|
3
|
+
require 'models/log'
|
4
|
+
|
5
|
+
module BindLogAnalyzer
|
6
|
+
class Base
|
7
|
+
include BindLogAnalyzer::Connector
|
8
|
+
|
9
|
+
attr_reader :log_filename, :database_confs
|
10
|
+
|
11
|
+
def initialize(database_confs = nil, logfile = nil, setup_database = false, log_level = 0)
|
12
|
+
if database_confs
|
13
|
+
if database_confs.instance_of?(Hash)
|
14
|
+
@database_confs = database_confs
|
15
|
+
else
|
16
|
+
# Load the yaml file
|
17
|
+
if FileTest.exists?(database_confs)
|
18
|
+
@database_confs = YAML::load(File.open(database_confs))['database']
|
19
|
+
else
|
20
|
+
raise BindLogAnalyzer::DatabaseConfsNotValid, "The indicated YAML file doesn't exist or is invalid"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
else
|
24
|
+
# Tries to find the yaml file or prints an error
|
25
|
+
filename = File.join(File.dirname(__FILE__), 'database.yml')
|
26
|
+
if FileTest.exists?(filename)
|
27
|
+
@database_confs = YAML::load(File.open(filename))['database']
|
28
|
+
else
|
29
|
+
raise BindLogAnalyzer::DatabaseConfsNotValid, "Can't find valid database configurations"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
@stored_queries = 0
|
34
|
+
self.logfile = logfile if logfile
|
35
|
+
setup_db(@database_confs, setup_database, log_level)
|
36
|
+
end
|
37
|
+
|
38
|
+
def logfile=(logfile)
|
39
|
+
@log_filename = logfile if FileTest.exists?(logfile)
|
40
|
+
end
|
41
|
+
|
42
|
+
def logfile
|
43
|
+
@log_filename
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_line(line)
|
47
|
+
query = {}
|
48
|
+
regexp = %r{^(\d{2}-\w{3}-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d{3})\s+client\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})#\d+:\s+query:\s+(.*)\s+IN\s+(\w+)\s+\+\s+\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\)$}
|
49
|
+
|
50
|
+
parsed_line = line.scan(regexp)
|
51
|
+
if parsed_line.size > 0
|
52
|
+
# Parse timestamp
|
53
|
+
parsed_timestamp = Date._strptime(parsed_line[0][0], "%d-%b-%Y %H:%M:%S.%L")
|
54
|
+
query_time = Time.local(parsed_timestamp[:year], parsed_timestamp[:mon], parsed_timestamp[:mday], parsed_timestamp[:hour], parsed_timestamp[:min], parsed_timestamp[:sec], parsed_timestamp[:sec_fraction], parsed_timestamp[:zone])
|
55
|
+
|
56
|
+
query[:date] = query_time
|
57
|
+
query[:client] = parsed_line[0][1]
|
58
|
+
query[:query] = parsed_line[0][2]
|
59
|
+
query[:q_type] = parsed_line[0][3]
|
60
|
+
query[:server] = parsed_line[0][4]
|
61
|
+
|
62
|
+
query
|
63
|
+
else
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def store_query(query)
|
69
|
+
log = Log.new(query)
|
70
|
+
@stored_queries += 1 if log.save
|
71
|
+
end
|
72
|
+
|
73
|
+
def analyze
|
74
|
+
return false unless @log_filename
|
75
|
+
|
76
|
+
lines = 0
|
77
|
+
File.new(@log_filename).each do |line|
|
78
|
+
query = self.parse_line(line)
|
79
|
+
if query
|
80
|
+
self.store_query(query)
|
81
|
+
lines += 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
puts "Analyzed #{lines} lines and correctly stored #{@stored_queries} logs"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module BindLogAnalyzer
|
5
|
+
module Connector
|
6
|
+
|
7
|
+
def setup_db(database_params, setup_database = false, log_level = 0)
|
8
|
+
@database_params = database_params
|
9
|
+
|
10
|
+
self.connect
|
11
|
+
|
12
|
+
migrate_tables if setup_database
|
13
|
+
|
14
|
+
self.load_environment
|
15
|
+
if log_level > 0
|
16
|
+
|
17
|
+
log_level_class = {
|
18
|
+
1 => Logger::WARN,
|
19
|
+
2 => Logger::INFO,
|
20
|
+
3 => Logger::DEBUG
|
21
|
+
}
|
22
|
+
|
23
|
+
log = Logger.new STDOUT
|
24
|
+
log.level = log_level_class[log_level]
|
25
|
+
ActiveRecord::Base.logger = log
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def migrate_tables
|
30
|
+
ActiveRecord::Migration.verbose = true
|
31
|
+
migrations_dir = File.join(File.dirname(__FILE__), '..', '..', 'db/migrate')
|
32
|
+
ActiveRecord::Migrator.migrate migrations_dir
|
33
|
+
end
|
34
|
+
|
35
|
+
def connect
|
36
|
+
ActiveRecord::Base.establish_connection(@database_params)
|
37
|
+
end
|
38
|
+
|
39
|
+
def connected?
|
40
|
+
ActiveRecord::Base.connected?
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_environment
|
44
|
+
Dir.glob('./lib/models/*').each { |r| require r }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/models/log.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe BindLogAnalyzer do
|
4
|
+
before :all do
|
5
|
+
@db_params = YAML::load(File.open(File.join(File.dirname(__FILE__), '..', 'config', 'databases.yml')))['database']
|
6
|
+
|
7
|
+
# Create a test logfile
|
8
|
+
@filename = 'test_file.log'
|
9
|
+
@doc = <<EOF
|
10
|
+
28-Mar-2012 16:48:32.411 client 192.168.10.37#60303: query: github.com IN A + (192.168.10.1)
|
11
|
+
28-Mar-2012 16:48:32.412 client 192.168.10.201#60303: query: google.com IN AAAA + (192.168.10.1)
|
12
|
+
28-Mar-2012 16:48:32.898 client 192.168.10.114#53309: query: www.nasa.gov IN A + (192.168.10.1)
|
13
|
+
EOF
|
14
|
+
|
15
|
+
File.open(@filename, 'w') { |f| f.write(@doc) } unless FileTest.exists?(@filename)
|
16
|
+
end
|
17
|
+
|
18
|
+
after :all do
|
19
|
+
#delete the test logfile
|
20
|
+
File.delete(@filename) if FileTest.exists?(@filename)
|
21
|
+
end
|
22
|
+
|
23
|
+
before :each do
|
24
|
+
end
|
25
|
+
|
26
|
+
#it "can't be instantiated without database params" do
|
27
|
+
# base = BindLogAnalyzer::Base.new
|
28
|
+
# ??
|
29
|
+
#end
|
30
|
+
|
31
|
+
it "can be instantiated without a logfile" do
|
32
|
+
base = BindLogAnalyzer::Base.new(@db_params)
|
33
|
+
base.logfile.should == nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "permit setting a logfile from initializer" do
|
37
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
38
|
+
@base.logfile.should == @filename
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should update the logfile name" do
|
42
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
43
|
+
|
44
|
+
# Create a new dummy file
|
45
|
+
@new_filename = 'new_test_file.log'
|
46
|
+
doc = ''
|
47
|
+
File.open(@new_filename, 'w') { |f| f.write(doc) } unless FileTest.exists?(@new_filename)
|
48
|
+
|
49
|
+
@base.logfile = @new_filename
|
50
|
+
@base.logfile.should == @new_filename
|
51
|
+
|
52
|
+
# Delete the file
|
53
|
+
File.delete(@new_filename)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "shouldn't update the logfile name if it doesn't exist" do
|
57
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
58
|
+
@base.logfile = 'unexisting_test_file'
|
59
|
+
@base.logfile.should_not == 'unexisting_test_file'
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should correctly parse a line" do
|
63
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
64
|
+
line = "28-Mar-2012 16:48:32.412 client 192.168.10.201#60303: query: google.com IN AAAA + (192.168.10.1)"
|
65
|
+
test_line = {
|
66
|
+
date: Time.local('2012','mar',28, 16, 48, 31),
|
67
|
+
client: "192.168.10.201",
|
68
|
+
query: "google.com",
|
69
|
+
type: "AAAA",
|
70
|
+
server: "192.168.10.1"
|
71
|
+
}
|
72
|
+
parsed_line = @base.parse_line(line)
|
73
|
+
parsed_line.should == test_line
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should be connected after setup_db is called" do
|
77
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
78
|
+
@base.connected?.should == true
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should be possible to instantiate a Log class after BindLogAnalyzer::Base initialization" do
|
82
|
+
@base = BindLogAnalyzer::Base.new(@db_params, @filename)
|
83
|
+
log = Log.new
|
84
|
+
log.class.should_not == NilClass
|
85
|
+
end
|
86
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bind_log_analyzer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tommaso Visconti
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: &8286660 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *8286660
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &8286020 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *8286020
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: simplecov
|
38
|
+
requirement: &8285380 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *8285380
|
47
|
+
description: BindLogAnalyzer analyzes a Bind query log file and stores it's data into
|
48
|
+
a SQL database (ActiveRecord is used for this feature)
|
49
|
+
email:
|
50
|
+
- tommaso.visconti@gmail.com
|
51
|
+
executables:
|
52
|
+
- bind_log_analyzer
|
53
|
+
extensions: []
|
54
|
+
extra_rdoc_files: []
|
55
|
+
files:
|
56
|
+
- .gitignore
|
57
|
+
- Gemfile
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- bin/bind_log_analyzer
|
61
|
+
- bind_log_analyzer.gemspec
|
62
|
+
- db/migrate/20120329151738_create_logs.rb
|
63
|
+
- doc/database.yml
|
64
|
+
- doc/update_bind_log_analyzer.sh
|
65
|
+
- lib/bind_log_analyzer.rb
|
66
|
+
- lib/bind_log_analyzer/base.rb
|
67
|
+
- lib/bind_log_analyzer/connector.rb
|
68
|
+
- lib/bind_log_analyzer/exceptions.rb
|
69
|
+
- lib/bind_log_analyzer/version.rb
|
70
|
+
- lib/models/log.rb
|
71
|
+
- spec/bind_log_analyzer_spec.rb
|
72
|
+
- spec/spec.opts
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
homepage: https://github.com/tommyblue/Bind-Log-Analyzer
|
75
|
+
licenses: []
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ! '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project: bind_log_analyzer
|
94
|
+
rubygems_version: 1.8.15
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: Log analysis and SQL storage for Bind DNS server
|
98
|
+
test_files:
|
99
|
+
- spec/bind_log_analyzer_spec.rb
|
100
|
+
- spec/spec.opts
|
101
|
+
- spec/spec_helper.rb
|