log 0.0.8
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/lib/log/log_consumer.rb +109 -0
- data/lib/log/version.rb +5 -0
- data/lib/log.rb +130 -0
- data/test/log/log_test.rb +165 -0
- metadata +66 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
# Author: Yaron Dror (yaron.dror.bb@gmail.com)
|
2
|
+
# Description: Implementation of the Log module - consumer\producer library.
|
3
|
+
# Note: The library could be enhance in the future due to project requirement (such as logging
|
4
|
+
# to mail, archives, remote servers etc)
|
5
|
+
|
6
|
+
require 'thread'
|
7
|
+
require 'params'
|
8
|
+
|
9
|
+
module BBFS
|
10
|
+
|
11
|
+
module Log
|
12
|
+
Params.float 'log_param_thread_sleep_time_in_seconds', 0.5 , \
|
13
|
+
'log param. Thread sleep time in seconds'
|
14
|
+
|
15
|
+
# Base class for all consumers
|
16
|
+
# Child consumers must implement the 'consume' virtual method
|
17
|
+
class Consumer
|
18
|
+
# Initializes the consumer queue and starts a thread. The thread waits for data
|
19
|
+
# on the queue, and when data is popped, activates the virtual 'consume' method.
|
20
|
+
def initialize
|
21
|
+
@consumer_queue = Queue.new
|
22
|
+
Thread.new do
|
23
|
+
while (true)
|
24
|
+
consume @consumer_queue.pop
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# push incoming data to the consumer queue
|
30
|
+
def push_data data
|
31
|
+
@consumer_queue.push data.clone
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# BufferConsumerProducer acts as a consumer and as a producer.
|
36
|
+
# It has it's own consumers which are added to it.
|
37
|
+
# It saves all the data it consumes in a buffer which has a size and time limits.
|
38
|
+
# When one of the limits is exceeded, it flushes the buffer to it's own consumers
|
39
|
+
class BufferConsumerProducer < Consumer
|
40
|
+
def initialize buffer_size_in_mega_bytes, buffer_time_out_in_seconds
|
41
|
+
super()
|
42
|
+
@buffer_size_in_bytes = buffer_size_in_mega_bytes * 1000000
|
43
|
+
@buffer_time_out_in_seconds = buffer_time_out_in_seconds
|
44
|
+
@time_at_last_flush = Time.now.to_i
|
45
|
+
@buffer = []
|
46
|
+
@consumers = []
|
47
|
+
Thread.new do
|
48
|
+
while (true)
|
49
|
+
if @consumer_queue.empty? then
|
50
|
+
@consumer_queue.push nil
|
51
|
+
sleep Params['log_param_thread_sleep_time_in_seconds']
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_consumer consumer
|
58
|
+
@consumers.push consumer
|
59
|
+
end
|
60
|
+
|
61
|
+
def consume data
|
62
|
+
@buffer.push data if not data.nil?
|
63
|
+
if (@buffer.inspect.size >= @buffer_size_in_bytes) or
|
64
|
+
((Time.now.to_i - @time_at_last_flush) >= @buffer_time_out_in_seconds) then
|
65
|
+
flush_to_consumers
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# flush the DB to the consumers
|
70
|
+
def flush_to_consumers
|
71
|
+
@consumers.each { |consumer| consumer.push_data @buffer}
|
72
|
+
@buffer.clear
|
73
|
+
@time_at_last_flush = Time.now.to_i
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# The console consumer logs the data to the standard output
|
78
|
+
class ConsoleConsumer < Consumer
|
79
|
+
def consume data
|
80
|
+
puts data
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# The file consumer logs the data to a file
|
85
|
+
class FileConsumer < Consumer
|
86
|
+
def initialize file_name
|
87
|
+
super()
|
88
|
+
@file_name = file_name
|
89
|
+
if File.exist? @file_name then
|
90
|
+
File.delete @file_name
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def consume data
|
95
|
+
begin
|
96
|
+
file_handler = File.new @file_name, 'a'
|
97
|
+
file_handler.puts data
|
98
|
+
rescue
|
99
|
+
Exception
|
100
|
+
puts "Failed to open log file:#{@file_name}"
|
101
|
+
puts $!.class
|
102
|
+
puts $!
|
103
|
+
ensure
|
104
|
+
file_handler.close
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/log/version.rb
ADDED
data/lib/log.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# Author: Yaron Dror (yaron.dror.bb@gmail.com)
|
2
|
+
# Description: The file contains the code which implements the 'Log' module
|
3
|
+
# Run: Add to 'require' list.
|
4
|
+
# Note: The logger will be automatically initialized if log_param_auto_start is true
|
5
|
+
# If log_param_auto_start is false then 'Log.init' method will be called
|
6
|
+
# on the first attempt to log.
|
7
|
+
|
8
|
+
require 'params'
|
9
|
+
require 'thread'
|
10
|
+
require 'log/log_consumer.rb'
|
11
|
+
|
12
|
+
module BBFS
|
13
|
+
# Module: Log.
|
14
|
+
# Abstruct: The Log is used to log info\warning\error\debug messages
|
15
|
+
# Note: The logger will be automatically initialized if log_param_auto_start is true
|
16
|
+
# If log_param_auto_start is false then 'Log.init' method will be called
|
17
|
+
# on the first attempt to log.
|
18
|
+
module Log
|
19
|
+
#Auxiliary method to retrieve the executable name
|
20
|
+
def Log.executable_name
|
21
|
+
/([a-zA-Z0-9\-_\.]+):\d+/ =~ caller[caller.size-1]
|
22
|
+
return $1
|
23
|
+
end
|
24
|
+
|
25
|
+
# Global params
|
26
|
+
Params.boolean 'log_param_auto_start',false, \
|
27
|
+
'log param. If true, log will start automatically. ' + \
|
28
|
+
'Else, init should be called. ' + \
|
29
|
+
'Else Init will be called automatically on the first attempt to log.'
|
30
|
+
Params.integer 'log_debug_level', 0 , 'Log level.'
|
31
|
+
Params.integer 'log_param_number_of_mega_bytes_stored_before_flush', 0, \
|
32
|
+
'log param. Number of mega bytes stored before they are flushed.'
|
33
|
+
Params.float 'log_param_max_elapsed_time_in_seconds_from_last_flush', 0, \
|
34
|
+
'log param. Max elapsed time in seconds from last flush'
|
35
|
+
Params.boolean 'log_write_to_file', true , \
|
36
|
+
'If true then the logger will write the messages to a file.'
|
37
|
+
Params.boolean 'log_write_to_console', false , \
|
38
|
+
'If true then the logger will write the messages to the console.'
|
39
|
+
Params.string 'log_file_name', File.expand_path("~/.bbfs/log/#{Log.executable_name}.log") , \
|
40
|
+
'Default log file name: ~/.bbfs/log/<executable_name>.log'
|
41
|
+
|
42
|
+
@consumers = []
|
43
|
+
@log_initialized = false
|
44
|
+
|
45
|
+
#Note that the logger will be automatically initialized if log_param_auto_start is true
|
46
|
+
if Params['log_param_auto_start'] then
|
47
|
+
Log.init
|
48
|
+
end
|
49
|
+
|
50
|
+
# Init the Log consumers
|
51
|
+
def Log.init
|
52
|
+
Log.clear_consumers
|
53
|
+
# Console - If enabled, will flush the log immediately to the console
|
54
|
+
if Params['log_write_to_console'] then
|
55
|
+
console_consumer = ConsoleConsumer.new
|
56
|
+
@consumers.push console_consumer
|
57
|
+
end
|
58
|
+
# BufferConsumerProducer - If enabled, will use the file consumer to flush a buffer to a file
|
59
|
+
if Params['log_write_to_file'] then
|
60
|
+
file_consumer = FileConsumer.new Params['log_file_name']
|
61
|
+
buffer_consumer_producer = BufferConsumerProducer.new \
|
62
|
+
Params['log_param_number_of_mega_bytes_stored_before_flush'], \
|
63
|
+
Params['log_param_max_elapsed_time_in_seconds_from_last_flush']
|
64
|
+
buffer_consumer_producer.add_consumer file_consumer
|
65
|
+
@consumers.push buffer_consumer_producer
|
66
|
+
end
|
67
|
+
@log_initialized = true
|
68
|
+
Log.info 'BBFS Log initialized.' # log first data
|
69
|
+
Log.info "Log file path:'#{Params['log_file_name']}'" if Params['log_write_to_file']
|
70
|
+
Params.get_init_messages().each { |msg|
|
71
|
+
Log.info msg
|
72
|
+
} unless Params.get_init_messages().empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
# Clears consumers
|
76
|
+
def Log.clear_consumers
|
77
|
+
@consumers.clear
|
78
|
+
end
|
79
|
+
|
80
|
+
# Adding consumer to the logger
|
81
|
+
def Log.add_consumer consumer
|
82
|
+
@consumers.push consumer
|
83
|
+
end
|
84
|
+
|
85
|
+
# formatting the data and push to consumers
|
86
|
+
def Log.basic msg, type
|
87
|
+
Log.init if not @log_initialized
|
88
|
+
/([a-zA-Z0-9\-_\.]+:\d+)/ =~ caller[1]
|
89
|
+
data = "[BBFS LOG] [#{Time.now()}] [#{type}] [#{$1}] [#{msg}]"
|
90
|
+
@consumers.each { |consumer| consumer.push_data data }
|
91
|
+
end
|
92
|
+
|
93
|
+
# Log warning massages
|
94
|
+
def Log.warning msg
|
95
|
+
Log.basic msg, 'WARNING'
|
96
|
+
end
|
97
|
+
|
98
|
+
# Log error massages
|
99
|
+
def Log.error msg
|
100
|
+
Log.basic msg, 'ERROR'
|
101
|
+
end
|
102
|
+
|
103
|
+
# Log info massages
|
104
|
+
def Log.info msg
|
105
|
+
Log.basic msg, 'INFO'
|
106
|
+
end
|
107
|
+
|
108
|
+
# Log debug level 1 massages
|
109
|
+
def Log.debug1 msg
|
110
|
+
if Params['log_debug_level'] > 0 then
|
111
|
+
Log.basic msg, 'DEBUG-1'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Log debug level 2 massages
|
116
|
+
def Log.debug2 msg
|
117
|
+
if Params['log_debug_level'] > 1 then
|
118
|
+
Log.basic msg, 'DEBUG-2'
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Log debug level 3 massages
|
123
|
+
def Log.debug3 msg
|
124
|
+
if Params['log_debug_level'] > 2 then
|
125
|
+
Log.basic msg, 'DEBUG-3'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# Author: Yaron Dror (yaron.dror.bb@gmail.com)
|
2
|
+
# Description: Log test file.
|
3
|
+
# run: rake test.
|
4
|
+
# Note: This file will be tested along with all project tests.
|
5
|
+
|
6
|
+
require 'params'
|
7
|
+
require 'test/unit'
|
8
|
+
require_relative '../../lib/log.rb'
|
9
|
+
require_relative '../../lib/log/log_consumer.rb'
|
10
|
+
|
11
|
+
module BBFS
|
12
|
+
|
13
|
+
module Log
|
14
|
+
# Creating a test consumer class to be able to read the data pushed
|
15
|
+
# to the consumer by the logger
|
16
|
+
class TestConsumer < Consumer
|
17
|
+
attr_reader :test_queue
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
super
|
21
|
+
@test_queue = Queue.new()
|
22
|
+
end
|
23
|
+
|
24
|
+
def consume data
|
25
|
+
@test_queue.push data
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TestLog < Test::Unit::TestCase
|
30
|
+
|
31
|
+
LOG_TEST_FILE_NAME = 'log_test.rb:'
|
32
|
+
LOG_PREFIX = 'BBFS LOG'
|
33
|
+
|
34
|
+
def initialize name
|
35
|
+
super
|
36
|
+
Params['log_write_to_console'] = false
|
37
|
+
Params['log_write_to_file'] = false
|
38
|
+
Log.init
|
39
|
+
@test_consumer = TestConsumer.new
|
40
|
+
Log.add_consumer @test_consumer
|
41
|
+
@line_regexp = Regexp.new(/\[(.*)\] \[(.*)\] \[(.*)\] \[(.*)\] \[(.*)\]/)
|
42
|
+
end
|
43
|
+
|
44
|
+
#check expected format: [LOG_PREFIX] [Time] [INFO] [FILE_NAME:LINE] [MESSAGE]
|
45
|
+
def assert_message line, time, type, file_line, msg
|
46
|
+
format_containers = @line_regexp.match(line)
|
47
|
+
assert_equal format_containers.captures.size, 5
|
48
|
+
if format_containers.captures.size == 5 then
|
49
|
+
assert_equal format_containers.captures[0], LOG_PREFIX
|
50
|
+
assert_equal format_containers.captures[1], time.to_s
|
51
|
+
assert_equal format_containers.captures[2], type
|
52
|
+
assert_equal format_containers.captures[3], file_line
|
53
|
+
assert_equal format_containers.captures[4], msg
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_check_info_format
|
58
|
+
#set test phase
|
59
|
+
test_message = 'This is a test INFO message.'
|
60
|
+
testTime = Time.now
|
61
|
+
Log.info test_message
|
62
|
+
line = __LINE__ - 1
|
63
|
+
#check test phase
|
64
|
+
pop_message = @test_consumer.test_queue.pop
|
65
|
+
assert_message pop_message, testTime, 'INFO', LOG_TEST_FILE_NAME + line.to_s, test_message
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_check_warning_format
|
69
|
+
#set test phase
|
70
|
+
test_message = 'This is a test WARNING message.'
|
71
|
+
testTime = Time.now
|
72
|
+
Log.warning test_message
|
73
|
+
line = __LINE__ - 1
|
74
|
+
|
75
|
+
#check test phase
|
76
|
+
pop_message = @test_consumer.test_queue.pop
|
77
|
+
assert_message pop_message, testTime, 'WARNING', LOG_TEST_FILE_NAME + line.to_s, test_message
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_check_error_format
|
81
|
+
#set test phase
|
82
|
+
test_message = 'This is a test ERROR message.'
|
83
|
+
testTime = Time.now
|
84
|
+
Log.error test_message
|
85
|
+
line = __LINE__ - 1
|
86
|
+
|
87
|
+
#check test phase
|
88
|
+
pop_message = @test_consumer.test_queue.pop
|
89
|
+
assert_message pop_message, testTime, 'ERROR', LOG_TEST_FILE_NAME + line.to_s, test_message
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_check_debug_level_0
|
93
|
+
#set test phase
|
94
|
+
Params['log_debug_level'] = 0
|
95
|
+
Log.debug1 'This is a test debug-1 message.'
|
96
|
+
Log.debug2 'This is a test debug-2 message.'
|
97
|
+
Log.debug3 'This is a test debug-3 message.'
|
98
|
+
#check test phase
|
99
|
+
queue_size = @test_consumer.test_queue.size
|
100
|
+
assert_equal queue_size, 0 , \
|
101
|
+
"At debug level 0, no debug messages should be found.Test found:#{queue_size} messages."
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_check_debug_level_1
|
105
|
+
#set test phase
|
106
|
+
Params['log_debug_level'] = 1
|
107
|
+
test_message_1 = 'This is a test DEBUG-1 message.'
|
108
|
+
test_message_2 = 'This is a test DEBUG-2 message.'
|
109
|
+
test_message_3 = 'This is a test DEBUG-3 message.'
|
110
|
+
testTime = Time.now
|
111
|
+
Log.debug1 test_message_1
|
112
|
+
line = __LINE__ - 1
|
113
|
+
Log.debug2 test_message_2
|
114
|
+
Log.debug3 test_message_3
|
115
|
+
|
116
|
+
#check test phase
|
117
|
+
pop_message = @test_consumer.test_queue.pop
|
118
|
+
assert_message pop_message, testTime, 'DEBUG-1', LOG_TEST_FILE_NAME + line.to_s, test_message_1
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_check_debug_level_2
|
122
|
+
#set test phase
|
123
|
+
Params['log_debug_level'] = 2
|
124
|
+
test_message_1 = 'This is a test DEBUG-1 message.'
|
125
|
+
test_message_2 = 'This is a test DEBUG-2 message.'
|
126
|
+
test_message_3 = 'This is a test DEBUG-3 message.'
|
127
|
+
testTime = Time.now
|
128
|
+
Log.debug1 test_message_1
|
129
|
+
line_1 = __LINE__ - 1
|
130
|
+
Log.debug2 test_message_2
|
131
|
+
line_2 = __LINE__ - 1
|
132
|
+
Log.debug3 test_message_3
|
133
|
+
|
134
|
+
#check test phase
|
135
|
+
pop_message = @test_consumer.test_queue.pop
|
136
|
+
assert_message pop_message, testTime, 'DEBUG-1', LOG_TEST_FILE_NAME + line_1.to_s, test_message_1
|
137
|
+
pop_message = @test_consumer.test_queue.pop
|
138
|
+
assert_message pop_message, testTime, 'DEBUG-2', LOG_TEST_FILE_NAME + line_2.to_s, test_message_2
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_check_debug_level_3
|
142
|
+
#set test phase
|
143
|
+
Params['log_debug_level'] = 3
|
144
|
+
test_message_1 = 'This is a test DEBUG-1 message.'
|
145
|
+
test_message_2 = 'This is a test DEBUG-2 message.'
|
146
|
+
test_message_3 = 'This is a test DEBUG-3 message.'
|
147
|
+
testTime = Time.now
|
148
|
+
Log.debug1 'This is a test DEBUG-1 message.'
|
149
|
+
line_1 = __LINE__ - 1
|
150
|
+
Log.debug2 'This is a test DEBUG-2 message.'
|
151
|
+
line_2 = __LINE__ - 1
|
152
|
+
Log.debug3 'This is a test DEBUG-3 message.'
|
153
|
+
line_3 = __LINE__ - 1
|
154
|
+
|
155
|
+
#check test phase
|
156
|
+
pop_message = @test_consumer.test_queue.pop
|
157
|
+
assert_message pop_message, testTime, 'DEBUG-1', LOG_TEST_FILE_NAME + line_1.to_s, test_message_1
|
158
|
+
pop_message = @test_consumer.test_queue.pop
|
159
|
+
assert_message pop_message, testTime, 'DEBUG-2', LOG_TEST_FILE_NAME + line_2.to_s, test_message_2
|
160
|
+
pop_message = @test_consumer.test_queue.pop
|
161
|
+
assert_message pop_message, testTime, 'DEBUG-3', LOG_TEST_FILE_NAME + line_3.to_s, test_message_3
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: log
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.8
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Yaron Dror
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: params
|
16
|
+
requirement: !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: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: logging data to file, console etc. Data can be pushed using info\warning\error\debug-levels
|
31
|
+
categories
|
32
|
+
email: yaron.dror.bb@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- lib/log.rb
|
38
|
+
- lib/log/log_consumer.rb
|
39
|
+
- lib/log/version.rb
|
40
|
+
- test/log/log_test.rb
|
41
|
+
homepage: http://github.com/yarondbb/bbfs
|
42
|
+
licenses: []
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.8.23
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: Library for logging data.
|
65
|
+
test_files:
|
66
|
+
- test/log/log_test.rb
|