termite 0.0.10 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
1
+ module Termite
2
+ class HasturLogger
3
+ def initialize(socket, addr, port, labels)
4
+ @socket, @addr, @port = socket, addr, port
5
+ @labels = labels || {}
6
+ end
7
+
8
+ def to_usec(time)
9
+ (time.to_f * 1_000_000).round
10
+ end
11
+
12
+ def send_message(severity, raw_message, app_data, time=Time.now, data='{}')
13
+ severity = Logger::LOGGER_LEVEL_MAP.invert[severity].to_s
14
+ tid = Ecology.thread_id(::Thread.current)
15
+ hostname = Socket.gethostname
16
+ pid = Process.pid
17
+ application, component = app_data[:app], app_data[:component]
18
+
19
+ message = {
20
+ :_route => :log,
21
+ :timestamp => to_usec(time),
22
+ :message => raw_message
23
+ }
24
+
25
+ labels = {
26
+ :severity => severity,
27
+ :pid => pid,
28
+ :tid => tid,
29
+ :app => application,
30
+ :component => component,
31
+ :hostname => hostname
32
+ }
33
+
34
+ labels.merge!(MultiJson.decode(data)) if data && data != '{}'
35
+ labels.merge!(@labels) if @labels && !@labels.empty?
36
+ message[:labels] = labels
37
+
38
+ @socket.send MultiJson.encode(message), 0, @addr, @port
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,47 @@
1
+ require "syslog"
2
+
3
+ module Termite
4
+ class SyslogLogger
5
+ def initialize(socket, server_addr, server_port, transport)
6
+ @socket, @server_addr, @server_port, @transport = socket, server_addr, server_port, transport
7
+ end
8
+
9
+ def send_message(severity, full_message, app_data, time=Time.now, data='{}')
10
+ tid = Ecology.thread_id(::Thread.current)
11
+ day = time.strftime("%b %d").sub(/0(\d)/, ' \\1')
12
+ time_of_day = time.strftime("%T")
13
+ hostname = Socket.gethostname
14
+ application = app_data[:combined] || app_data[:app]
15
+
16
+ # Convert Ruby log level to syslog severity
17
+ tag = Syslog::LOG_LOCAL6 + Logger::SYSLOG_SEVERITY_MAP[Logger::LEVEL_SYSLOG_MAP[severity]]
18
+ syslog_string = "<#{tag}>#{day} #{time_of_day} #{hostname} #{application} [#{Process.pid}]: [#{tid}] "
19
+
20
+ # ruby_severity is the Ruby Logger severity as a symbol
21
+ ruby_severity = Logger::LOGGER_LEVEL_MAP.invert[severity]
22
+
23
+ full_message.split("\n").each do |line|
24
+ syslog_message = "#{line} #{data}"
25
+ case @transport
26
+ when "UDP"
27
+ send_udp(severity, syslog_string, syslog_message, application)
28
+ when "syscall", "libc"
29
+ send_libc(severity, syslog_message, application) rescue nil
30
+ else
31
+ send_libc(severity, syslog_message, application) rescue
32
+ send_udp(severity, syslog_string, syslog_message, application)
33
+ end
34
+ end
35
+ end
36
+
37
+ def send_udp(severity, syslog_string, syslog_message, application)
38
+ @socket.send(syslog_string + syslog_message, 0, @server_addr, @server_port) rescue nil
39
+ end
40
+
41
+ def send_libc(severity, syslog_message, application)
42
+ Syslog.open(application, Syslog::LOG_PID | Syslog::LOG_CONS) do |s|
43
+ s.send(Logger::LEVEL_SYSLOG_MAP[severity], syslog_message)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,3 +1,3 @@
1
1
  module Termite
2
- VERSION = "0.0.10"
2
+ VERSION = "0.0.20"
3
3
  end
@@ -0,0 +1,3 @@
1
+ module Termite
2
+ VERSION = "0.0.14"
3
+ end
@@ -25,8 +25,9 @@ EOS
25
25
 
26
26
  s.require_paths = ["lib"]
27
27
 
28
- s.add_dependency "multi_json"
29
28
  s.add_dependency "ecology", "~>0.0.6"
29
+ s.add_dependency "multi_json"
30
+ s.add_dependency "rainbow", "~> 1.1.3"
30
31
 
31
32
  s.add_development_dependency "bundler", "~> 1.0.10"
32
33
  s.add_development_dependency "scope", "~> 0.2.1"
@@ -29,17 +29,17 @@ ECOLOGY_CONTENTS
29
29
  end
30
30
 
31
31
  should "send back extra JSON data and a default component when specified" do
32
- expect_add(@logger.socket, 2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SplodgingLib")
32
+ expect_add(2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SplodgingLib")
33
33
  @logger.fatal("oh no!")
34
34
  end
35
35
 
36
36
  should "allow overriding the default component" do
37
- expect_add(@logger.socket, 2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SpliyingLib")
37
+ expect_add(2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SpliyingLib")
38
38
  @logger.fatal("oh no!", {}, :component => "SpliyingLib")
39
39
  end
40
40
 
41
41
  should "allow overriding the default component with nothing" do
42
- expect_add(@logger.socket, 2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp")
42
+ expect_add(2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp")
43
43
  @logger.fatal("oh no!", {}, :component => nil)
44
44
  end
45
45
  end
@@ -53,6 +53,7 @@ ECOLOGY_CONTENTS
53
53
  should "override parameters passed to Termite Logger" do
54
54
  log_mock = mock("Ruby Logger")
55
55
  ::Logger.expects(:new).with("/tmp/bobo.txt", 10, 1024000).returns(log_mock)
56
+ ::Logger.expects(:new).with("/var/lib/sam.log", "daily", 1048576).returns(log_mock)
56
57
  Termite::Logger.new("/var/lib/sam.log", "daily")
57
58
  end
58
59
  end
@@ -11,15 +11,16 @@ class TermiteExtraLoggerTest < Scope::TestCase
11
11
  setup do
12
12
  @logger = Termite::Logger.new("/tmp/test_log_output.txt") # Test with output file
13
13
  @logger.level = Logger::DEBUG
14
- @logger.socket.expects(:send)
14
+ Syslog.expects(:open)
15
15
  @mock_logger_1 = mock()
16
+ @mock_logger_2 = mock()
16
17
  @logger.add_logger(@mock_logger_1)
17
18
  @logger.add_logger(@mock_logger_2)
18
19
  end
19
20
 
20
- should "correctly send logs to Syslog" do
21
- @mock_logger_1.expects(:fatal)
22
- @mock_logger_2.expects(:fatal)
21
+ should "correctly send logs to additional loggers" do
22
+ @mock_logger_1.expects(:<<).with("foo!")
23
+ @mock_logger_2.expects(:<<).with("foo!")
23
24
  @logger.add(Logger::FATAL, "foo!", {})
24
25
  end
25
26
  end
@@ -0,0 +1,58 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper.rb")
2
+
3
+ class HasturLoggerTest < Scope::TestCase
4
+
5
+ def expect_send(raw_message, data)
6
+ message = {
7
+ :_route => :log,
8
+ :timestamp => @time_usec,
9
+ :message => raw_message
10
+ }
11
+
12
+ labels = {
13
+ :severity => "warn",
14
+ :pid => @pid,
15
+ :tid => @tid,
16
+ :app => @app_data[:app],
17
+ :component => @app_data[:component],
18
+ :hostname => @host
19
+ }
20
+
21
+ message[:labels] = labels.merge(MultiJson.decode(data)).merge(@labels)
22
+ json_message = MultiJson.encode(message)
23
+
24
+ @socket.expects(:send).with(json_message, 0, @addr, @port)
25
+ end
26
+
27
+ context "with a hastur logger" do
28
+ setup do
29
+ @socket = mock("UDP Socket")
30
+ @labels = {"hi" => "lo", "fast" => "slo"}
31
+ @addr = "127.0.0.1"
32
+ @port = 8125
33
+ @logger = Termite::HasturLogger.new(@socket, @addr, @port, @labels)
34
+ end
35
+
36
+ context "will send a message" do
37
+ setup do
38
+ @severity = 2
39
+ @raw_message = "message"
40
+ @app_data = {:combined => "app:comp", :app => "app", :component => "comp"}
41
+ @pid = 123
42
+ @tid = "main"
43
+ @time = 345
44
+ @time_usec = 345_000_000
45
+ @host = "host"
46
+ Ecology.stubs(:thread_id).returns(@tid)
47
+ Process.stubs(:pid).returns(@pid)
48
+ Time.stubs(:now).returns(@time)
49
+ Socket.stubs(:gethostname).returns(@host)
50
+ end
51
+
52
+ should "send back extra JSON data and labels when specified" do
53
+ expect_send("oh no!", '{"app_group":"SuperSpiffyGroup","precedence":7}')
54
+ @logger.send_message(@severity, "oh no!", @app_data, Time.now, '{"app_group":"SuperSpiffyGroup","precedence":7}')
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,53 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper.rb")
2
+
3
+ class LevelTest < Scope::TestCase
4
+ context "without termite ecology" do
5
+ setup do
6
+ @logger = Termite::Logger.new(:level => "error")
7
+ end
8
+
9
+ should "correctly set level to error" do
10
+ assert_equal Logger::ERROR, @logger.level
11
+ end
12
+ end
13
+
14
+ context "with termite ecology" do
15
+ setup do
16
+ Ecology.reset
17
+
18
+ set_up_ecology <<ECOLOGY_TEXT
19
+ {
20
+ "application": "foo_app",
21
+ "logging":
22
+ {
23
+ "default_component": "whatcomponent",
24
+ "level": "info"
25
+ }
26
+ }
27
+ ECOLOGY_TEXT
28
+ end
29
+
30
+ context "and no overrides" do
31
+ setup do
32
+ @logger = Termite::Logger.new
33
+ end
34
+
35
+ should "correctly have level set at info" do
36
+ assert_equal Logger::INFO, @logger.level
37
+ end
38
+ end
39
+
40
+ context "and debug overriding at env level" do
41
+ setup do
42
+ ENV["TERMITE_DEBUG"] = "1"
43
+ @logger = Termite::Logger.new()
44
+ end
45
+
46
+ should "correctly have level set at debug" do
47
+ assert_equal Logger::DEBUG, @logger.level
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end
@@ -9,34 +9,32 @@ class RescueTest < Scope::TestCase
9
9
  end
10
10
 
11
11
  should "continue even if all loggers raise errors" do
12
- # First, UDP will raise an error
13
- @logger.socket.expects(:send).raises(StandardError, "You suck!")
14
-
15
- # Termite should fall back to trying Ruby Syslog...
12
+ # First, termite should try Ruby Syslog...
16
13
  syslog_mock = mock("Syslog connection")
17
14
  Syslog.expects(:open).yields(syslog_mock)
18
- syslog_mock.expects(:error).with("UDP syslog failed! Falling back to libc syslog!")
19
15
  syslog_mock.expects(:crit).raises(StandardError, "You suck even more than that!")
20
16
 
21
- # And it should still try to write to a file logger
22
- @logger.file_logger.expects(:fatal).raises(StandardError, "You suck lots!")
17
+ # Then, it will fal back to UDP and raise an error
18
+ @logger.socket.expects(:send).raises(StandardError, "You suck!")
19
+
20
+ # And it should still try to write to a file logger - this is now just an extra logger
21
+ # @logger.file_logger.expects(:fatal).raises(StandardError, "You suck lots!")
23
22
 
24
23
  extra_logger = mock("Additional logger")
25
24
  @logger.add_logger(extra_logger)
26
25
  # And it should try to write to any extra loggers
27
- extra_logger.expects(:fatal).raises(StandardError, "You suck constantly!")
26
+ extra_logger.expects(:<<).raises(StandardError, "You suck constantly!")
28
27
 
29
28
  # And yet, nothing should be raised to the outside world
30
29
  begin
31
30
  @logger.fatal("Woe is me!")
32
31
  assert true, "Nothing was raised! Yay!"
33
32
  rescue Exception
34
- flunk "Logging an event raised an assertion outside the logger!"
33
+ flunk "Logging an event raised an assertion outside the logger!"
35
34
  end
36
35
  end
37
36
 
38
37
  should "continue even if internal logic gives an error" do
39
- #Time.expects(:now).raises(Exception.new "You suck!")
40
38
  Ecology.expects(:thread_id).raises(Exception.new "Ecology thread_id dies!")
41
39
  begin
42
40
  @logger.fatal("Woe is me!")
@@ -0,0 +1,183 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper.rb")
2
+
3
+ class SinksTest < Scope::TestCase
4
+ context "with a hastur sink ecology" do
5
+ setup do
6
+ Ecology.reset
7
+
8
+ set_up_ecology <<ECOLOGY_CONTENTS
9
+ {
10
+ "application": "MyApp",
11
+ "logging": {
12
+ "default_component": "SplodgingLib",
13
+ "extra_json_fields": {
14
+ "app_group": "SuperSpiffyGroup",
15
+ "precedence": 7
16
+ },
17
+ "console_print": "off",
18
+ "sinks": [
19
+ {
20
+ "type": "syslog",
21
+ "transport": "UDP"
22
+ },
23
+ {
24
+ "type": "hastur",
25
+ "labels": {
26
+ "hi": "lo",
27
+ "fast": "slo"
28
+ }
29
+ }
30
+ ]
31
+ }
32
+ }
33
+ ECOLOGY_CONTENTS
34
+ end
35
+
36
+ context "with a default termite logger" do
37
+ setup do
38
+ @hastur_mock = mock("Hastur Logger")
39
+ Termite::HasturLogger.expects(:new).returns(@hastur_mock)
40
+ @logger = Termite::Logger.new
41
+ end
42
+
43
+ should "send back extra JSON data and a default component when specified" do
44
+ @hastur_mock.expects(:send_message).with(4, 'oh no!', {:combined => "MyApp:SplodgingLib", :app => "MyApp", :component => "SplodgingLib"}, anything, '{"app_group":"SuperSpiffyGroup","precedence":7}')
45
+ @logger.fatal("oh no!")
46
+ end
47
+
48
+ should "allow overriding the default component" do
49
+ @hastur_mock.expects(:send_message).with(4, 'oh no!', {:combined => "MyApp:SpliyingLib", :app => "MyApp", :component => "SpliyingLib"}, anything, '{"app_group":"SuperSpiffyGroup","precedence":7}')
50
+ @logger.fatal("oh no!", {}, :component => "SpliyingLib")
51
+ end
52
+
53
+ should "allow overriding the default component with nothing" do
54
+ @hastur_mock.expects(:send_message).with(4, 'oh no!', {:combined => nil, :app => "MyApp", :component => nil}, anything, '{"app_group":"SuperSpiffyGroup","precedence":7}')
55
+ @logger.fatal("oh no!", {}, :component => nil)
56
+ end
57
+ end
58
+ end
59
+
60
+ context "with a sinked UDP ecology" do
61
+ setup do
62
+ Ecology.reset
63
+
64
+ set_up_ecology <<ECOLOGY_CONTENTS
65
+ {
66
+ "application": "MyApp",
67
+ "logging": {
68
+ "default_component": "SplodgingLib",
69
+ "extra_json_fields": {
70
+ "app_group": "SuperSpiffyGroup",
71
+ "precedence": 7
72
+ },
73
+ "console_print": "off",
74
+ "sinks": [
75
+ {
76
+ "type": "syslog",
77
+ "transport": "UDP"
78
+ }
79
+ ]
80
+ }
81
+ }
82
+ ECOLOGY_CONTENTS
83
+ end
84
+
85
+ context "with a default termite logger" do
86
+ setup do
87
+ @logger = Termite::Logger.new
88
+ end
89
+
90
+ should "send back extra JSON data and a default component when specified" do
91
+ expect_udp(@logger.socket, 2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SplodgingLib")
92
+ @logger.fatal("oh no!")
93
+ end
94
+
95
+ should "allow overriding the default component" do
96
+ expect_udp(@logger.socket, 2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SpliyingLib")
97
+ @logger.fatal("oh no!", {}, :component => "SpliyingLib")
98
+ end
99
+
100
+ should "allow overriding the default component with nothing" do
101
+ expect_udp(@logger.socket, 2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp")
102
+ @logger.fatal("oh no!", {}, :component => nil)
103
+ end
104
+ end
105
+ end
106
+
107
+ context "with a sinked ecology" do
108
+ setup do
109
+ Ecology.reset
110
+
111
+ set_up_ecology <<ECOLOGY_CONTENTS
112
+ {
113
+ "application": "MyApp",
114
+ "logging": {
115
+ "default_component": "SplodgingLib",
116
+ "extra_json_fields": {
117
+ "app_group": "SuperSpiffyGroup",
118
+ "precedence": 7
119
+ },
120
+ "console_print": "off",
121
+ "sinks": [
122
+ {
123
+ "type": "stdout",
124
+ "color": "green",
125
+ "min_level": "debug",
126
+ "max_level": "warn"
127
+ },
128
+ {
129
+ "type": "stderr",
130
+ "color": "red",
131
+ "min_level": "error"
132
+ },
133
+ {
134
+ "type": "file",
135
+ "filename": "/tmp/bobo.txt",
136
+ "min_level": "warn",
137
+ "shift_age": 10,
138
+ "shift_size": 1024000
139
+ },
140
+ {
141
+ "type": "syslog"
142
+ }
143
+ ]
144
+ }
145
+ }
146
+ ECOLOGY_CONTENTS
147
+ end
148
+
149
+ context "with a default termite logger" do
150
+ setup do
151
+ @logger = Termite::Logger.new
152
+ end
153
+
154
+ should "send back extra JSON data and a default component when specified" do
155
+ expect_add(2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SplodgingLib")
156
+ @logger.fatal("oh no!")
157
+ end
158
+
159
+ should "allow overriding the default component" do
160
+ expect_add(2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp:SpliyingLib")
161
+ @logger.fatal("oh no!", {}, :component => "SpliyingLib")
162
+ end
163
+
164
+ should "allow overriding the default component with nothing" do
165
+ expect_add(2, 'oh no! {"app_group":"SuperSpiffyGroup","precedence":7}', :application => "MyApp")
166
+ @logger.fatal("oh no!", {}, :component => nil)
167
+ end
168
+ end
169
+
170
+ should "pass initialize parameters to Ruby Logger" do
171
+ log_mock = mock("Ruby Logger")
172
+ ::Logger.expects(:new).with("/tmp/bobo.txt", 10, 1024000).returns(log_mock)
173
+ Termite::Logger.new
174
+ end
175
+
176
+ should "override parameters passed to Termite Logger" do
177
+ log_mock = mock("Ruby Logger")
178
+ ::Logger.expects(:new).with("/tmp/bobo.txt", 10, 1024000).returns(log_mock)
179
+ ::Logger.expects(:new).with("/var/lib/sam.log", "daily", 1048576).returns(log_mock)
180
+ Termite::Logger.new("/var/lib/sam.log", "daily")
181
+ end
182
+ end
183
+ end