scrolls 0.9.0.pre → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile +2 -0
- data/LICENSE +2 -2
- data/README.md +53 -14
- data/Rakefile +4 -2
- data/docs/global-context.md +1 -1
- data/docs/syslog.md +14 -1
- data/lib/scrolls/{iolog.rb → iologger.rb} +1 -1
- data/lib/scrolls/logger.rb +113 -87
- data/lib/scrolls/parser.rb +4 -2
- data/lib/scrolls/sysloglogger.rb +17 -0
- data/lib/scrolls/utils.rb +55 -13
- data/lib/scrolls/version.rb +1 -1
- data/lib/scrolls.rb +38 -6
- data/test/test_helper.rb +4 -1
- data/test/test_parser.rb +16 -1
- data/test/test_scrolls.rb +30 -12
- metadata +7 -9
- data/lib/scrolls/log.rb +0 -262
- data/lib/scrolls/syslog.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2c46a03de696acee9e14e71d9e2a373415e9fa60532a7ac51896f71b5e0286ca
|
4
|
+
data.tar.gz: 9d9af16e271f1ff468fb778c8d5e05d9f215909adf77cdbf144204d2b3d8836e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9aa2a50b5c00379872e54384b8f0b6050f82ab5f86e66bb2eca544a7ea5845ac4672dcc758e948d9b3af84b6d61af7972ca937be21854010002325c89b176865
|
7
|
+
data.tar.gz: 992fc115e62d9a2ea39305a72db4bfd0868c0f64d2e1a47082136ecf01e2d7a16461bbfb69e5bb38da628a4e1331685c4f6da275277e9793dfe5c5a8a808c496
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright
|
4
|
-
Copyright
|
3
|
+
Copyright (c) 2013-2021 Curt Micol <asenchi@asenchi.com>
|
4
|
+
Copyright (c) 2012, Heroku
|
5
5
|
|
6
6
|
Permission is hereby granted, free of charge, to any person obtaining
|
7
7
|
a copy of this software and associated documentation files (the
|
data/README.md
CHANGED
@@ -20,26 +20,65 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
Scrolls follows the belief that logs should be treated as data. One way to think of them is the blood of your infrastructure. Logs are a realtime view of what is happening on your systems.
|
22
22
|
|
23
|
-
##
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### 0.9.0 and later
|
24
26
|
|
25
|
-
|
27
|
+
```ruby
|
28
|
+
require 'scrolls'
|
26
29
|
|
27
|
-
|
30
|
+
Scrolls.init(
|
31
|
+
timestamp: true,
|
32
|
+
global_context: {app: "scrolls", deploy: "production"},
|
33
|
+
exceptions: "multi"
|
34
|
+
)
|
28
35
|
|
29
|
-
|
36
|
+
Scrolls.log(at: "test")
|
30
37
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
* Misc Features
|
38
|
+
Scrolls.context(context: "block") do
|
39
|
+
Scrolls.log(at: "exec")
|
40
|
+
end
|
35
41
|
|
36
|
-
|
42
|
+
begin
|
43
|
+
raise
|
44
|
+
rescue Exception => e
|
45
|
+
Scrolls.log_exception(e, at: "raise")
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
You can also use `Scrolls#log` and `Scrolls#log_exception` without initalizing:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'scrolls'
|
53
|
+
|
54
|
+
Scrolls.log(test: "test")
|
55
|
+
```
|
56
|
+
|
57
|
+
### Defaults
|
58
|
+
|
59
|
+
Here are the defaults `Scrolls#init`:
|
60
|
+
|
61
|
+
```
|
62
|
+
stream: STDOUT
|
63
|
+
facility: Syslog::LOG_USER
|
64
|
+
time_unit: "seconds"
|
65
|
+
timestamp: false
|
66
|
+
exceptions: "single"
|
67
|
+
global_context: {}
|
68
|
+
syslog_options: Syslog::LOG_PID|Syslog::LOG_CONS
|
69
|
+
escape_keys: false
|
70
|
+
strict_logfmt: false
|
71
|
+
```
|
72
|
+
|
73
|
+
## Older Versions
|
74
|
+
|
75
|
+
### Pre 0.9.0
|
37
76
|
|
38
77
|
```ruby
|
39
78
|
require 'scrolls'
|
40
79
|
|
41
80
|
Scrolls.add_timestamp = true
|
42
|
-
Scrolls.global_context(:app => "scrolls", :deploy =>
|
81
|
+
Scrolls.global_context(:app => "scrolls", :deploy => "production")
|
43
82
|
|
44
83
|
Scrolls.log(:at => "test")
|
45
84
|
|
@@ -57,10 +96,10 @@ end
|
|
57
96
|
Produces:
|
58
97
|
|
59
98
|
```
|
60
|
-
now="
|
61
|
-
now="
|
62
|
-
now="
|
63
|
-
now="
|
99
|
+
now="2017-09-01T00:37:13Z" app=scrolls deploy=production at=test
|
100
|
+
now="2017-09-01T00:37:13Z" app=scrolls deploy=production context=block at=exec
|
101
|
+
now="2017-09-01T00:37:13Z" app=scrolls deploy=production at=exception class=RuntimeError exception_id=70149797587080
|
102
|
+
now="2017-09-01T00:37:13Z" app=scrolls deploy=production at=exception class=RuntimeError exception_id=70149797587080 site="./test-scrolls.rb:16:in <main>"
|
64
103
|
```
|
65
104
|
|
66
105
|
## History
|
data/Rakefile
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
+
|
2
3
|
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
3
5
|
|
4
6
|
ENV['TESTOPTS'] = "-v"
|
5
7
|
|
6
|
-
require "rake/testtask"
|
7
8
|
Rake::TestTask.new do |t|
|
8
|
-
t.
|
9
|
+
t.test_files = FileList["test/**/test_*.rb"]
|
10
|
+
t.verbose = true
|
9
11
|
end
|
10
12
|
|
11
13
|
task :default => :test
|
data/docs/global-context.md
CHANGED
data/docs/syslog.md
CHANGED
@@ -6,6 +6,14 @@ By default Scrolls writes log messages to `STDOUT`. With the release of [v0.2.8]
|
|
6
6
|
Scrolls.stream = "syslog"
|
7
7
|
```
|
8
8
|
|
9
|
+
Or using `Scrolls#init` in versions 0.9.0 and after:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
Scrolls.init(
|
13
|
+
stream: "syslog"
|
14
|
+
)
|
15
|
+
```
|
16
|
+
|
9
17
|
This defaults to syslog facility USER and log level ERROR. You can adjust the log facility like so:
|
10
18
|
|
11
19
|
```ruby
|
@@ -14,4 +22,9 @@ Scrolls.facility = "local7"
|
|
14
22
|
|
15
23
|
Scrolls generally doesn't care about log levels. The library defaults to ERROR (or 3), but ultimately is of the opinion that levels are useless. The reasoning behind this is that applications should log useful data, all of the time. Debugging data is great for development, but should never be deployed. The richness of structured logging allows exceptions and error messages to sit along side the context of the data in which the error was thrown, there is no need to send to an "emergency" level.
|
16
24
|
|
17
|
-
With that said, if one wanted to adjust the log level, you can set an environment variable `LOG_LEVEL
|
25
|
+
With that said, if one wanted to adjust the log level, you can set an environment variable `LOG_LEVEL` or use one of the level methods. This allows this particular feature to be rather fluid throughout your application.
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
Scrolls.info(d: "data")
|
29
|
+
Scrolls.warn(d: "data")
|
30
|
+
```
|
data/lib/scrolls/logger.rb
CHANGED
@@ -1,100 +1,107 @@
|
|
1
|
+
require "syslog"
|
2
|
+
|
1
3
|
require "scrolls/parser"
|
2
|
-
require "scrolls/
|
3
|
-
require "scrolls/
|
4
|
+
require "scrolls/iologger"
|
5
|
+
require "scrolls/sysloglogger"
|
6
|
+
require "scrolls/utils"
|
4
7
|
|
5
8
|
module Scrolls
|
9
|
+
# Default log facility
|
10
|
+
LOG_FACILITY = ENV['LOG_FACILITY'] || Syslog::LOG_USER
|
11
|
+
|
12
|
+
# Default log level
|
13
|
+
LOG_LEVEL = (ENV['LOG_LEVEL'] || 6).to_i
|
14
|
+
|
15
|
+
# Default syslog options
|
16
|
+
SYSLOG_OPTIONS = Syslog::LOG_PID|Syslog::LOG_CONS
|
17
|
+
|
6
18
|
class TimeUnitError < RuntimeError; end
|
19
|
+
class LogLevelError < StandardError; end
|
7
20
|
|
21
|
+
# Top level class to hold our global context
|
22
|
+
#
|
23
|
+
# Global context is defined using Scrolls#init
|
8
24
|
class GlobalContext
|
9
|
-
|
10
|
-
|
11
|
-
@context = context || {}
|
25
|
+
def initialize(ctx)
|
26
|
+
@ctx = ctx || {}
|
12
27
|
end
|
13
28
|
|
14
29
|
def to_h
|
15
|
-
@
|
30
|
+
@ctx
|
16
31
|
end
|
17
32
|
end
|
18
33
|
|
19
|
-
class
|
20
|
-
LOG_FACILITY = ENV['LOG_FACILITY'] || Syslog::LOG_USER
|
21
|
-
LOG_FACILITY_MAP = {
|
22
|
-
"auth" => Syslog::LOG_AUTH,
|
23
|
-
"authpriv" => Syslog::LOG_AUTHPRIV,
|
24
|
-
"cron" => Syslog::LOG_CRON,
|
25
|
-
"daemon" => Syslog::LOG_DAEMON,
|
26
|
-
"ftp" => Syslog::LOG_FTP,
|
27
|
-
"kern" => Syslog::LOG_KERN,
|
28
|
-
"mail" => Syslog::LOG_MAIL,
|
29
|
-
"news" => Syslog::LOG_NEWS,
|
30
|
-
"syslog" => Syslog::LOG_SYSLOG,
|
31
|
-
"user" => Syslog::LOG_USER,
|
32
|
-
"uucp" => Syslog::LOG_UUCP,
|
33
|
-
"local0" => Syslog::LOG_LOCAL0,
|
34
|
-
"local1" => Syslog::LOG_LOCAL1,
|
35
|
-
"local2" => Syslog::LOG_LOCAL2,
|
36
|
-
"local3" => Syslog::LOG_LOCAL3,
|
37
|
-
"local4" => Syslog::LOG_LOCAL4,
|
38
|
-
"local5" => Syslog::LOG_LOCAL5,
|
39
|
-
"local6" => Syslog::LOG_LOCAL6,
|
40
|
-
"local7" => Syslog::LOG_LOCAL7,
|
41
|
-
}
|
42
|
-
|
43
|
-
LOG_LEVEL = (ENV['LOG_LEVEL'] || 6).to_i
|
44
|
-
LOG_LEVEL_MAP = {
|
45
|
-
"emergency" => 0,
|
46
|
-
"alert" => 1,
|
47
|
-
"critical" => 2,
|
48
|
-
"error" => 3,
|
49
|
-
"warning" => 4,
|
50
|
-
"notice" => 5,
|
51
|
-
"info" => 6,
|
52
|
-
"debug" => 7
|
53
|
-
}
|
34
|
+
class Logger
|
54
35
|
|
55
36
|
attr_reader :logger
|
56
37
|
attr_accessor :exceptions, :timestamp
|
57
38
|
|
58
39
|
def initialize(options={})
|
59
|
-
@stream
|
60
|
-
@
|
61
|
-
@time_unit
|
62
|
-
@timestamp
|
63
|
-
@exceptions
|
64
|
-
@global_ctx
|
65
|
-
|
40
|
+
@stream = options.fetch(:stream, STDOUT)
|
41
|
+
@log_facility = options.fetch(:facility, LOG_FACILITY)
|
42
|
+
@time_unit = options.fetch(:time_unit, "seconds")
|
43
|
+
@timestamp = options.fetch(:timestamp, false)
|
44
|
+
@exceptions = options.fetch(:exceptions, "single")
|
45
|
+
@global_ctx = options.fetch(:global_context, {})
|
46
|
+
@syslog_opts = options.fetch(:syslog_options, SYSLOG_OPTIONS)
|
47
|
+
@escape_keys = options.fetch(:escape_keys, false)
|
48
|
+
@strict_logfmt = options.fetch(:strict_logfmt, false)
|
49
|
+
|
50
|
+
# Our main entry point to ensure our options are setup properly
|
66
51
|
setup!
|
67
52
|
end
|
68
53
|
|
69
54
|
def context
|
70
|
-
Thread.current
|
55
|
+
if Thread.current.thread_variables.include?(:scrolls_context)
|
56
|
+
Thread.current.thread_variable_get(:scrolls_context)
|
57
|
+
else
|
58
|
+
Thread.current.thread_variable_set(:scrolls_context, {})
|
59
|
+
end
|
71
60
|
end
|
72
61
|
|
73
62
|
def context=(h)
|
74
|
-
Thread.current
|
63
|
+
Thread.current.thread_variable_set(:scrolls_context, h || {})
|
75
64
|
end
|
76
65
|
|
77
66
|
def stream
|
78
67
|
@stream
|
79
68
|
end
|
80
69
|
|
81
|
-
def stream=(
|
70
|
+
def stream=(s)
|
82
71
|
# Return early to avoid setup
|
83
|
-
return if
|
84
|
-
|
85
|
-
@stream =
|
72
|
+
return if s == @stream
|
73
|
+
|
74
|
+
@stream = s
|
86
75
|
setup_stream
|
87
76
|
end
|
88
77
|
|
78
|
+
def escape_keys?
|
79
|
+
@escape_keys
|
80
|
+
end
|
81
|
+
|
82
|
+
def strict_logfmt?
|
83
|
+
@strict_logfmt
|
84
|
+
end
|
85
|
+
|
86
|
+
def syslog_options
|
87
|
+
@syslog_opts
|
88
|
+
end
|
89
|
+
|
89
90
|
def facility
|
90
|
-
@facility
|
91
|
+
@facility
|
91
92
|
end
|
92
93
|
|
93
94
|
def facility=(f)
|
94
95
|
if f
|
95
|
-
|
96
|
-
#
|
97
|
-
|
96
|
+
setup_facility(f)
|
97
|
+
# If we are using syslog, we need to setup our connection again
|
98
|
+
if stream == "syslog"
|
99
|
+
@logger = Scrolls::SyslogLogger.new(
|
100
|
+
progname,
|
101
|
+
syslog_options,
|
102
|
+
facility
|
103
|
+
)
|
104
|
+
end
|
98
105
|
end
|
99
106
|
end
|
100
107
|
|
@@ -104,7 +111,7 @@ module Scrolls
|
|
104
111
|
|
105
112
|
def time_unit=(u)
|
106
113
|
@time_unit = u
|
107
|
-
|
114
|
+
setup_time_unit
|
108
115
|
end
|
109
116
|
|
110
117
|
def global_context
|
@@ -138,14 +145,16 @@ module Scrolls
|
|
138
145
|
begin
|
139
146
|
res = yield
|
140
147
|
rescue StandardError => e
|
141
|
-
|
142
|
-
:
|
143
|
-
:
|
144
|
-
:
|
145
|
-
:
|
146
|
-
:
|
147
|
-
:
|
148
|
-
)
|
148
|
+
logdata.merge!({
|
149
|
+
at: "exception",
|
150
|
+
reraise: true,
|
151
|
+
class: e.class,
|
152
|
+
message: e.message,
|
153
|
+
exception_id: e.object_id.abs,
|
154
|
+
elapsed: calculate_time(start, Time.now)
|
155
|
+
})
|
156
|
+
logdata.delete_if { |k,v| k if v == "" }
|
157
|
+
log(logdata)
|
149
158
|
raise e
|
150
159
|
end
|
151
160
|
log(logdata.merge(:at => "finish", :elapsed => calculate_time(start, Time.now)))
|
@@ -153,17 +162,20 @@ module Scrolls
|
|
153
162
|
end
|
154
163
|
end
|
155
164
|
|
156
|
-
def log_exception(
|
165
|
+
def log_exception(e, data=nil)
|
157
166
|
unless @defined
|
158
167
|
@stream = STDERR
|
159
168
|
setup_stream
|
160
169
|
end
|
161
170
|
|
162
|
-
#
|
163
|
-
|
171
|
+
# We check our arguments for type
|
172
|
+
case data
|
173
|
+
when String
|
164
174
|
rawhash = { "log_message" => data }
|
165
|
-
|
175
|
+
when Hash
|
166
176
|
rawhash = data
|
177
|
+
else
|
178
|
+
rawhash = {}
|
167
179
|
end
|
168
180
|
|
169
181
|
if gc = @global_context.to_h
|
@@ -171,12 +183,14 @@ module Scrolls
|
|
171
183
|
end
|
172
184
|
|
173
185
|
excepdata = {
|
174
|
-
:
|
175
|
-
:
|
176
|
-
:
|
177
|
-
:
|
186
|
+
at: "exception",
|
187
|
+
class: e.class,
|
188
|
+
message: e.message,
|
189
|
+
exception_id: e.object_id.abs
|
178
190
|
}
|
179
191
|
|
192
|
+
excepdata.delete_if { |k,v| k if v == "" }
|
193
|
+
|
180
194
|
if e.backtrace
|
181
195
|
if single_line_exceptions?
|
182
196
|
lines = e.backtrace.map { |line| line.gsub(/[`'"]/, "") }
|
@@ -213,14 +227,15 @@ module Scrolls
|
|
213
227
|
private
|
214
228
|
|
215
229
|
def setup!
|
216
|
-
|
230
|
+
setup_global_context
|
217
231
|
prepend_timestamp?
|
232
|
+
setup_facility
|
218
233
|
setup_stream
|
219
234
|
single_line_exceptions?
|
220
|
-
|
235
|
+
setup_time_unit
|
221
236
|
end
|
222
237
|
|
223
|
-
def
|
238
|
+
def setup_global_context
|
224
239
|
# Builds up an immutable object for our global_context
|
225
240
|
# This is not backwards compatiable and was introduced after 0.3.7.
|
226
241
|
# Removes ability to add to global context once we initialize our
|
@@ -233,15 +248,28 @@ module Scrolls
|
|
233
248
|
@timestamp
|
234
249
|
end
|
235
250
|
|
251
|
+
def setup_facility(f=nil)
|
252
|
+
if f
|
253
|
+
@facility = LOG_FACILITY_MAP.fetch(f, LOG_FACILITY)
|
254
|
+
else
|
255
|
+
@facility = LOG_FACILITY_MAP.fetch(@log_facility, LOG_FACILITY)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
236
259
|
def setup_stream
|
237
260
|
unless @stream == STDOUT
|
261
|
+
# Set this so we know we aren't using our default stream
|
238
262
|
@defined = true
|
239
263
|
end
|
240
264
|
|
241
265
|
if @stream == "syslog"
|
242
|
-
@logger = Scrolls::SyslogLogger.new(
|
266
|
+
@logger = Scrolls::SyslogLogger.new(
|
267
|
+
progname,
|
268
|
+
syslog_options,
|
269
|
+
facility
|
270
|
+
)
|
243
271
|
else
|
244
|
-
@logger =
|
272
|
+
@logger = IOLogger.new(@stream)
|
245
273
|
end
|
246
274
|
end
|
247
275
|
|
@@ -250,7 +278,7 @@ module Scrolls
|
|
250
278
|
true
|
251
279
|
end
|
252
280
|
|
253
|
-
def
|
281
|
+
def setup_time_unit
|
254
282
|
unless %w{s ms seconds milliseconds}.include? @time_unit
|
255
283
|
raise TimeUnitError, "Specify the following: s, ms, seconds, milliseconds"
|
256
284
|
end
|
@@ -264,11 +292,8 @@ module Scrolls
|
|
264
292
|
@t = 1.0
|
265
293
|
end
|
266
294
|
end
|
267
|
-
|
268
|
-
def sync_stream(out = STDOUT)
|
269
|
-
IOLog.new(out)
|
270
|
-
end
|
271
295
|
|
296
|
+
# We need this for our syslog setup
|
272
297
|
def progname
|
273
298
|
File.basename($0)
|
274
299
|
end
|
@@ -280,6 +305,7 @@ module Scrolls
|
|
280
305
|
|
281
306
|
def log_level_ok?(level)
|
282
307
|
if level
|
308
|
+
raise LogLevelError, "Log level unknown" unless LOG_LEVEL_MAP.key?(level)
|
283
309
|
LOG_LEVEL_MAP[level.to_s] <= LOG_LEVEL
|
284
310
|
else
|
285
311
|
true
|
@@ -288,8 +314,8 @@ module Scrolls
|
|
288
314
|
|
289
315
|
def write(data)
|
290
316
|
if log_level_ok?(data[:level])
|
291
|
-
msg = Scrolls::Parser.unparse(data)
|
292
|
-
logger.log(msg)
|
317
|
+
msg = Scrolls::Parser.unparse(data, escape_keys=escape_keys?, strict_logfmt=strict_logfmt?)
|
318
|
+
@logger.log(msg)
|
293
319
|
end
|
294
320
|
end
|
295
321
|
|
data/lib/scrolls/parser.rb
CHANGED
@@ -4,8 +4,10 @@ module Scrolls
|
|
4
4
|
module Parser
|
5
5
|
extend self
|
6
6
|
|
7
|
-
def unparse(data)
|
7
|
+
def unparse(data, escape_keys=false, strict_logfmt=false)
|
8
8
|
data.map do |(k,v)|
|
9
|
+
k = Scrolls::Utils.escape_chars(k) if escape_keys
|
10
|
+
|
9
11
|
if (v == true)
|
10
12
|
"#{k}=true"
|
11
13
|
elsif (v == false)
|
@@ -21,7 +23,7 @@ module Scrolls
|
|
21
23
|
has_single_quote = v.index("'")
|
22
24
|
has_double_quote = v.index('"')
|
23
25
|
if v =~ /[ =:,]/
|
24
|
-
if has_single_quote && has_double_quote
|
26
|
+
if (has_single_quote || strict_logfmt) && has_double_quote
|
25
27
|
v = '"' + v.gsub(/\\|"/) { |c| "\\#{c}" } + '"'
|
26
28
|
elsif has_double_quote
|
27
29
|
v = "'" + v.gsub('\\', '\\\\\\') + "'"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Scrolls
|
2
|
+
class SyslogLogger
|
3
|
+
def initialize(ident = 'scrolls',
|
4
|
+
options = Scrolls::SYSLOG_OPTIONS,
|
5
|
+
facility = Scrolls::LOG_FACILITY)
|
6
|
+
if Syslog.opened?
|
7
|
+
@syslog = Syslog.reopen(ident, options, facility)
|
8
|
+
else
|
9
|
+
@syslog = Syslog.open(ident, options, facility)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def log(data)
|
14
|
+
@syslog.log(Syslog::LOG_INFO, "%s", data)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/scrolls/utils.rb
CHANGED
@@ -1,19 +1,61 @@
|
|
1
1
|
module Scrolls
|
2
|
-
module Utils
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
# Helpful map of syslog facilities
|
4
|
+
LOG_FACILITY_MAP = {
|
5
|
+
"auth" => Syslog::LOG_AUTH,
|
6
|
+
"authpriv" => Syslog::LOG_AUTHPRIV,
|
7
|
+
"cron" => Syslog::LOG_CRON,
|
8
|
+
"daemon" => Syslog::LOG_DAEMON,
|
9
|
+
"ftp" => Syslog::LOG_FTP,
|
10
|
+
"kern" => Syslog::LOG_KERN,
|
11
|
+
"mail" => Syslog::LOG_MAIL,
|
12
|
+
"news" => Syslog::LOG_NEWS,
|
13
|
+
"syslog" => Syslog::LOG_SYSLOG,
|
14
|
+
"user" => Syslog::LOG_USER,
|
15
|
+
"uucp" => Syslog::LOG_UUCP,
|
16
|
+
"local0" => Syslog::LOG_LOCAL0,
|
17
|
+
"local1" => Syslog::LOG_LOCAL1,
|
18
|
+
"local2" => Syslog::LOG_LOCAL2,
|
19
|
+
"local3" => Syslog::LOG_LOCAL3,
|
20
|
+
"local4" => Syslog::LOG_LOCAL4,
|
21
|
+
"local5" => Syslog::LOG_LOCAL5,
|
22
|
+
"local6" => Syslog::LOG_LOCAL6,
|
23
|
+
"local7" => Syslog::LOG_LOCAL7,
|
24
|
+
}
|
25
|
+
|
26
|
+
# Helpful map of syslog log levels
|
27
|
+
LOG_LEVEL_MAP = {
|
28
|
+
"emerg" => 0, # Syslog::LOG_EMERG
|
29
|
+
"emergency" => 0, # Syslog::LOG_EMERG
|
30
|
+
"alert" => 1, # Syslog::LOG_ALERT
|
31
|
+
"crit" => 2, # Syslog::LOG_CRIT
|
32
|
+
"critical" => 2, # Syslog::LOG_CRIT
|
33
|
+
"error" => 3, # Syslog::LOG_ERR
|
34
|
+
"warn" => 4, # Syslog::LOG_WARNING
|
35
|
+
"warning" => 4, # Syslog::LOG_WARNING
|
36
|
+
"notice" => 5, # Syslog::LOG_NOTICE
|
37
|
+
"info" => 6, # Syslog::LOG_INFO
|
38
|
+
"debug" => 7 # Syslog::LOG_DEBUG
|
39
|
+
}
|
40
|
+
|
41
|
+
ESCAPE_CHAR = {
|
42
|
+
"&" => "&",
|
43
|
+
"<" => "<",
|
44
|
+
">" => ">",
|
45
|
+
"'" => "'",
|
46
|
+
'"' => """,
|
47
|
+
"/" => "/"
|
48
|
+
}
|
49
|
+
|
50
|
+
ESCAPE_CHAR_PATTERN = Regexp.union(*ESCAPE_CHAR.keys)
|
51
|
+
|
52
|
+
module Utils
|
11
53
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
54
|
+
def self.escape_chars(d)
|
55
|
+
if d.is_a?(String) and d =~ ESCAPE_CHAR_PATTERN
|
56
|
+
esc = d.to_s.gsub(ESCAPE_CHAR_PATTERN) {|c| ESCAPE_CHAR[c] }
|
57
|
+
else
|
58
|
+
esc = d
|
17
59
|
end
|
18
60
|
end
|
19
61
|
|
data/lib/scrolls/version.rb
CHANGED
data/lib/scrolls.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require "thread"
|
2
1
|
require "scrolls/logger"
|
3
2
|
require "scrolls/version"
|
4
3
|
|
@@ -8,9 +7,26 @@ module Scrolls
|
|
8
7
|
# Public: Initialize a Scrolls logger
|
9
8
|
#
|
10
9
|
# options - A hash of key/values for configuring Scrolls
|
10
|
+
# stream - Stream to output data (default: STDOUT)
|
11
|
+
# log_facility - Syslog facility (default: Syslog::LOG_USER)
|
12
|
+
# time_unit - Unit of time (default: seconds)
|
13
|
+
# timestamp - Prepend logs with a timestamp (default: false)
|
14
|
+
# exceptions - Method for outputting exceptions (default: single line)
|
15
|
+
# global_context - Immutable context to prepend all messages with
|
16
|
+
# syslog_options - Syslog options (default: Syslog::LOG_PID|Syslog::LOG_CONS)
|
17
|
+
# escape_keys - Escape chars in keys
|
18
|
+
# strict_logfmt - Always use double quotes to quote values
|
11
19
|
#
|
12
20
|
def init(options={})
|
13
|
-
|
21
|
+
# Set a hint whether #init was called.
|
22
|
+
@initialized = true
|
23
|
+
@log = Logger.new(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Get the primary logger
|
27
|
+
#
|
28
|
+
def logger
|
29
|
+
@log.logger
|
14
30
|
end
|
15
31
|
|
16
32
|
# Public: Set a context in a block for logs
|
@@ -48,26 +64,32 @@ module Scrolls
|
|
48
64
|
# => nil
|
49
65
|
#
|
50
66
|
def log(data, &blk)
|
67
|
+
# Allows us to call #log directly and initialize defaults
|
68
|
+
@log = Logger.new({}) unless @initialized
|
69
|
+
|
51
70
|
@log.log(data, &blk)
|
52
71
|
end
|
53
72
|
|
54
73
|
# Public: Log an exception
|
55
74
|
#
|
56
|
-
# data - A hash of key/values to log
|
57
75
|
# e - An exception to pass to the logger
|
76
|
+
# data - A hash of key/values to log
|
58
77
|
#
|
59
78
|
# Examples:
|
60
79
|
#
|
61
80
|
# begin
|
62
81
|
# raise Exception
|
63
82
|
# rescue Exception => e
|
64
|
-
# Scrolls.log_exception({test: "test"}
|
83
|
+
# Scrolls.log_exception(e, {test: "test"})
|
65
84
|
# end
|
66
85
|
# test=test at=exception class=Exception message=Exception exception_id=70321999017240
|
67
86
|
# ...
|
68
87
|
#
|
69
|
-
def log_exception(
|
70
|
-
|
88
|
+
def log_exception(e, data)
|
89
|
+
# Allows us to call #log directly and initialize defaults
|
90
|
+
@log = Logger.new({}) unless @initialized
|
91
|
+
|
92
|
+
@log.log_exception(e, data)
|
71
93
|
end
|
72
94
|
|
73
95
|
# Public: Setup a logging facility (default: Syslog::LOG_USER)
|
@@ -200,6 +222,7 @@ module Scrolls
|
|
200
222
|
# => nil
|
201
223
|
#
|
202
224
|
def debug(data, &blk)
|
225
|
+
data = coalesce_strings_to_hash(data)
|
203
226
|
data = data.merge(:level => "debug") if data.is_a?(Hash)
|
204
227
|
@log.log(data, &blk)
|
205
228
|
end
|
@@ -218,6 +241,7 @@ module Scrolls
|
|
218
241
|
# => nil
|
219
242
|
#
|
220
243
|
def error(data, &blk)
|
244
|
+
data = coalesce_strings_to_hash(data)
|
221
245
|
data = data.merge(:level => "warning") if data.is_a?(Hash)
|
222
246
|
@log.log(data, &blk)
|
223
247
|
end
|
@@ -236,6 +260,7 @@ module Scrolls
|
|
236
260
|
# => nil
|
237
261
|
#
|
238
262
|
def fatal(data, &blk)
|
263
|
+
data = coalesce_strings_to_hash(data)
|
239
264
|
data = data.merge(:level => "error") if data.is_a?(Hash)
|
240
265
|
@log.log(data, &blk)
|
241
266
|
end
|
@@ -254,6 +279,7 @@ module Scrolls
|
|
254
279
|
# => nil
|
255
280
|
#
|
256
281
|
def info(data, &blk)
|
282
|
+
data = coalesce_strings_to_hash(data)
|
257
283
|
data = data.merge(:level => "info") if data.is_a?(Hash)
|
258
284
|
@log.log(data, &blk)
|
259
285
|
end
|
@@ -272,6 +298,7 @@ module Scrolls
|
|
272
298
|
# => nil
|
273
299
|
#
|
274
300
|
def warn(data, &blk)
|
301
|
+
data = coalesce_strings_to_hash(data)
|
275
302
|
data = data.merge(:level => "notice") if data.is_a?(Hash)
|
276
303
|
@log.log(data, &blk)
|
277
304
|
end
|
@@ -290,6 +317,7 @@ module Scrolls
|
|
290
317
|
# => nil
|
291
318
|
#
|
292
319
|
def unknown(data, &blk)
|
320
|
+
data = coalesce_strings_to_hash(data)
|
293
321
|
data = data.merge(:level => "alert") if data.is_a?(Hash)
|
294
322
|
@log.log(data, &blk)
|
295
323
|
end
|
@@ -300,4 +328,8 @@ module Scrolls
|
|
300
328
|
@log
|
301
329
|
end
|
302
330
|
|
331
|
+
def coalesce_strings_to_hash(string_or_something_else)
|
332
|
+
return string_or_something_else unless string_or_something_else.is_a?(String)
|
333
|
+
{ "log_message" => string_or_something_else }
|
334
|
+
end
|
303
335
|
end
|
data/test/test_helper.rb
CHANGED
data/test/test_parser.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path("../test_helper", __FILE__)
|
2
2
|
|
3
|
-
class TestScrollsParser < Test
|
3
|
+
class TestScrollsParser < Minitest::Test
|
4
4
|
include Scrolls::Parser
|
5
5
|
|
6
6
|
def test_parse_bool
|
@@ -79,6 +79,21 @@ class TestScrollsParser < Test::Unit::TestCase
|
|
79
79
|
assert_equal 't="2012-06-19T16:02:35+01:00"', unparse(data)
|
80
80
|
end
|
81
81
|
|
82
|
+
def test_unparse_escape_keys
|
83
|
+
html = "<p>p</p>"
|
84
|
+
slash = "p/p"
|
85
|
+
|
86
|
+
data = { html => "d", slash => "d" }
|
87
|
+
assert_equal '<p>p</p>=d p/p=d',
|
88
|
+
unparse(data, escape_keys=true)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_unparse_strict_logfmt
|
92
|
+
data = { s: 'echo "hello"' }
|
93
|
+
assert_equal 's="echo \"hello\""', unparse(data, escape_keys=false, strict_logfmt=true)
|
94
|
+
assert_equal data.inspect, parse(unparse(data, escape_keys=false, strict_logfmt=true)).inspect
|
95
|
+
end
|
96
|
+
|
82
97
|
def test_parse_time
|
83
98
|
time = Time.new(2012, 06, 19, 16, 02, 35, "+01:00")
|
84
99
|
string = "t=2012-06-19T16:02:35+01:00"
|
data/test/test_scrolls.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path("../test_helper", __FILE__)
|
2
2
|
|
3
|
-
class TestScrolls < Test
|
3
|
+
class TestScrolls < Minitest::Test
|
4
4
|
def setup
|
5
5
|
@out = StringIO.new
|
6
6
|
Scrolls.init(
|
@@ -8,12 +8,9 @@ class TestScrolls < Test::Unit::TestCase
|
|
8
8
|
)
|
9
9
|
end
|
10
10
|
|
11
|
-
def teardown
|
12
|
-
end
|
13
|
-
|
14
11
|
def test_default_construct
|
15
12
|
Scrolls.init
|
16
|
-
assert_equal Scrolls::
|
13
|
+
assert_equal Scrolls::IOLogger, Scrolls.logger.class
|
17
14
|
end
|
18
15
|
|
19
16
|
def test_default_global_context
|
@@ -93,6 +90,21 @@ class TestScrolls < Test::Unit::TestCase
|
|
93
90
|
end
|
94
91
|
end
|
95
92
|
|
93
|
+
def test_deeply_nested_context_after_exception
|
94
|
+
Scrolls.log(:o => "o") do
|
95
|
+
begin
|
96
|
+
Scrolls.log(:io => 'io') do
|
97
|
+
raise "Error from inside of nested logging"
|
98
|
+
end
|
99
|
+
rescue
|
100
|
+
Scrolls.log(:o => 'o')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
@out.truncate(124)
|
104
|
+
output = "o=o at=start\nio=io at=start\nio=io at=exception reraise=true class=RuntimeError message=\"Error from inside of nested logging\""
|
105
|
+
assert_equal output, @out.string
|
106
|
+
end
|
107
|
+
|
96
108
|
def test_default_time_unit
|
97
109
|
assert_equal "seconds", Scrolls.time_unit
|
98
110
|
end
|
@@ -103,12 +115,18 @@ class TestScrolls < Test::Unit::TestCase
|
|
103
115
|
end
|
104
116
|
|
105
117
|
def test_setting_incorrect_time_unit
|
106
|
-
|
118
|
+
assert_raises Scrolls::TimeUnitError do
|
107
119
|
Scrolls.time_unit = "years"
|
108
120
|
Scrolls.log(:tu => "yrs")
|
109
121
|
end
|
110
122
|
end
|
111
123
|
|
124
|
+
def test_unknown_log_level
|
125
|
+
assert_raises Scrolls::LogLevelError do
|
126
|
+
Scrolls.log(:level => "nope")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
112
130
|
def test_logging
|
113
131
|
Scrolls.log(:test => "basic")
|
114
132
|
assert_equal "test=basic\n", @out.string
|
@@ -124,7 +142,7 @@ class TestScrolls < Test::Unit::TestCase
|
|
124
142
|
begin
|
125
143
|
raise Exception
|
126
144
|
rescue Exception => e
|
127
|
-
Scrolls.log_exception({:test => "exception"}
|
145
|
+
Scrolls.log_exception(e, {:test => "exception"})
|
128
146
|
end
|
129
147
|
|
130
148
|
oneline_bt = @out.string.gsub("\n", 'XX')
|
@@ -136,7 +154,7 @@ class TestScrolls < Test::Unit::TestCase
|
|
136
154
|
begin
|
137
155
|
raise Exception
|
138
156
|
rescue Exception => e
|
139
|
-
Scrolls.log_exception({:o => "o"}
|
157
|
+
Scrolls.log_exception(e, {:o => "o"})
|
140
158
|
end
|
141
159
|
|
142
160
|
oneline_bt = @out.string.gsub("\n", 'XX')
|
@@ -205,22 +223,22 @@ class TestScrolls < Test::Unit::TestCase
|
|
205
223
|
|
206
224
|
def test_sending_string_error
|
207
225
|
Scrolls.error("error")
|
208
|
-
assert_equal "log_message=error\n", @out.string
|
226
|
+
assert_equal "log_message=error level=warning\n", @out.string
|
209
227
|
end
|
210
228
|
|
211
229
|
def test_sending_string_fatal
|
212
230
|
Scrolls.fatal("fatal")
|
213
|
-
assert_equal "log_message=fatal\n", @out.string
|
231
|
+
assert_equal "log_message=fatal level=error\n", @out.string
|
214
232
|
end
|
215
233
|
|
216
234
|
def test_sending_string_warn
|
217
235
|
Scrolls.warn("warn")
|
218
|
-
assert_equal "log_message=warn\n", @out.string
|
236
|
+
assert_equal "log_message=warn level=notice\n", @out.string
|
219
237
|
end
|
220
238
|
|
221
239
|
def test_sending_string_unknown
|
222
240
|
Scrolls.unknown("unknown")
|
223
|
-
assert_equal "log_message=unknown\n", @out.string
|
241
|
+
assert_equal "log_message=unknown level=alert\n", @out.string
|
224
242
|
end
|
225
243
|
|
226
244
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scrolls
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Curt Micol
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Logging, easier, more consistent.
|
14
14
|
email:
|
@@ -25,11 +25,10 @@ files:
|
|
25
25
|
- docs/global-context.md
|
26
26
|
- docs/syslog.md
|
27
27
|
- lib/scrolls.rb
|
28
|
-
- lib/scrolls/
|
29
|
-
- lib/scrolls/log.rb
|
28
|
+
- lib/scrolls/iologger.rb
|
30
29
|
- lib/scrolls/logger.rb
|
31
30
|
- lib/scrolls/parser.rb
|
32
|
-
- lib/scrolls/
|
31
|
+
- lib/scrolls/sysloglogger.rb
|
33
32
|
- lib/scrolls/utils.rb
|
34
33
|
- lib/scrolls/version.rb
|
35
34
|
- scrolls.gemspec
|
@@ -51,12 +50,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
50
|
version: '0'
|
52
51
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
52
|
requirements:
|
54
|
-
- - "
|
53
|
+
- - ">="
|
55
54
|
- !ruby/object:Gem::Version
|
56
|
-
version:
|
55
|
+
version: '0'
|
57
56
|
requirements: []
|
58
|
-
|
59
|
-
rubygems_version: 2.6.11
|
57
|
+
rubygems_version: 3.2.29
|
60
58
|
signing_key:
|
61
59
|
specification_version: 4
|
62
60
|
summary: When do we log? All the time.
|
data/lib/scrolls/log.rb
DELETED
@@ -1,262 +0,0 @@
|
|
1
|
-
require "scrolls/parser"
|
2
|
-
require "scrolls/utils"
|
3
|
-
require "scrolls/iolog"
|
4
|
-
require "scrolls/syslog"
|
5
|
-
|
6
|
-
module Scrolls
|
7
|
-
|
8
|
-
class TimeUnitError < RuntimeError; end
|
9
|
-
|
10
|
-
module Log
|
11
|
-
extend self
|
12
|
-
|
13
|
-
extend Parser
|
14
|
-
extend Utils
|
15
|
-
|
16
|
-
LOG_LEVEL = (ENV['LOG_LEVEL'] || 6).to_i
|
17
|
-
LOG_LEVEL_MAP = {
|
18
|
-
"emergency" => 0,
|
19
|
-
"alert" => 1,
|
20
|
-
"critical" => 2,
|
21
|
-
"error" => 3,
|
22
|
-
"warning" => 4,
|
23
|
-
"notice" => 5,
|
24
|
-
"info" => 6,
|
25
|
-
"debug" => 7
|
26
|
-
}
|
27
|
-
|
28
|
-
def context
|
29
|
-
Thread.current[:scrolls_context] ||= {}
|
30
|
-
end
|
31
|
-
|
32
|
-
def context=(h)
|
33
|
-
Thread.current[:scrolls_context] = h
|
34
|
-
end
|
35
|
-
|
36
|
-
def global_context
|
37
|
-
get_global_context
|
38
|
-
end
|
39
|
-
|
40
|
-
def global_context=(data)
|
41
|
-
set_global_context(data)
|
42
|
-
end
|
43
|
-
|
44
|
-
def add_global_context(new_data)
|
45
|
-
default_global_context unless @global_context
|
46
|
-
@global_context.update { |previous_data| previous_data.merge(new_data) }
|
47
|
-
end
|
48
|
-
|
49
|
-
def facility=(f)
|
50
|
-
@facility = LOG_FACILITY_MAP[f] if f
|
51
|
-
if Scrolls::SyslogLogger.opened?
|
52
|
-
Scrolls::SyslogLogger.new(progname, facility)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def facility
|
57
|
-
@facility ||= default_log_facility
|
58
|
-
end
|
59
|
-
|
60
|
-
def stream=(out=nil)
|
61
|
-
@defined = out.nil? ? false : true
|
62
|
-
if out == 'syslog'
|
63
|
-
@stream = Scrolls::SyslogLogger.new(progname, facility)
|
64
|
-
else
|
65
|
-
@stream = sync_stream(out)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def stream
|
70
|
-
@stream ||= sync_stream
|
71
|
-
end
|
72
|
-
|
73
|
-
def time_unit=(u)
|
74
|
-
set_time_unit(u)
|
75
|
-
end
|
76
|
-
|
77
|
-
def time_unit
|
78
|
-
@tunit ||= default_time_unit
|
79
|
-
end
|
80
|
-
|
81
|
-
def add_timestamp=(b)
|
82
|
-
@add_timestamp = !!b
|
83
|
-
end
|
84
|
-
|
85
|
-
def add_timestamp
|
86
|
-
@add_timestamp || false
|
87
|
-
end
|
88
|
-
|
89
|
-
def single_line_exceptions=(b)
|
90
|
-
@single_line_exceptions = !!b
|
91
|
-
end
|
92
|
-
|
93
|
-
def single_line_exceptions?
|
94
|
-
@single_line_exceptions || false
|
95
|
-
end
|
96
|
-
|
97
|
-
def log(data, &blk)
|
98
|
-
# If we get a string lets bring it into our structure.
|
99
|
-
if data.kind_of? String
|
100
|
-
rawhash = { "log_message" => data }
|
101
|
-
else
|
102
|
-
rawhash = data
|
103
|
-
end
|
104
|
-
|
105
|
-
if gc = get_global_context
|
106
|
-
ctx = gc.merge(context)
|
107
|
-
logdata = ctx.merge(rawhash)
|
108
|
-
end
|
109
|
-
|
110
|
-
# By merging the logdata into the timestamp, rather than vice-versa, we
|
111
|
-
# ensure that the timestamp comes first in the Hash, and is placed first
|
112
|
-
# on the output, which helps with readability.
|
113
|
-
logdata = { :now => Time.now.utc }.merge(logdata) if add_timestamp
|
114
|
-
|
115
|
-
unless blk
|
116
|
-
write(logdata)
|
117
|
-
else
|
118
|
-
start = Time.now
|
119
|
-
res = nil
|
120
|
-
log(logdata.merge(:at => "start"))
|
121
|
-
begin
|
122
|
-
res = yield
|
123
|
-
rescue StandardError => e
|
124
|
-
log(logdata.merge(
|
125
|
-
:at => "exception",
|
126
|
-
:reraise => true,
|
127
|
-
:class => e.class,
|
128
|
-
:message => e.message,
|
129
|
-
:exception_id => e.object_id.abs,
|
130
|
-
:elapsed => calc_time(start, Time.now)
|
131
|
-
))
|
132
|
-
raise e
|
133
|
-
end
|
134
|
-
log(logdata.merge(:at => "finish", :elapsed => calc_time(start, Time.now)))
|
135
|
-
res
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def log_exception(data, e)
|
140
|
-
sync_stream(STDERR) unless @defined
|
141
|
-
|
142
|
-
# If we get a string lets bring it into our structure.
|
143
|
-
if data.kind_of? String
|
144
|
-
rawhash = { "log_message" => data }
|
145
|
-
else
|
146
|
-
rawhash = data
|
147
|
-
end
|
148
|
-
|
149
|
-
if gc = get_global_context
|
150
|
-
logdata = gc.merge(rawhash)
|
151
|
-
end
|
152
|
-
|
153
|
-
excepdata = {
|
154
|
-
:at => "exception",
|
155
|
-
:class => e.class,
|
156
|
-
:message => e.message,
|
157
|
-
:exception_id => e.object_id.abs
|
158
|
-
}
|
159
|
-
|
160
|
-
if e.backtrace
|
161
|
-
if single_line_exceptions?
|
162
|
-
btlines = []
|
163
|
-
e.backtrace.each do |line|
|
164
|
-
btlines << line.gsub(/[`'"]/, "")
|
165
|
-
end
|
166
|
-
|
167
|
-
if btlines.length > 0
|
168
|
-
squish = { :site => btlines.join('\n') }
|
169
|
-
log(logdata.merge(excepdata.merge(squish)))
|
170
|
-
end
|
171
|
-
else
|
172
|
-
log(logdata.merge(excepdata))
|
173
|
-
|
174
|
-
e.backtrace.each do |line|
|
175
|
-
log(logdata.merge(excepdata).merge(
|
176
|
-
:at => "exception",
|
177
|
-
:class => e.class,
|
178
|
-
:exception_id => e.object_id.abs,
|
179
|
-
:site => line.gsub(/[`'"]/, "")
|
180
|
-
))
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def with_context(prefix)
|
187
|
-
return unless block_given?
|
188
|
-
old = context
|
189
|
-
self.context = old.merge(prefix)
|
190
|
-
res = yield if block_given?
|
191
|
-
ensure
|
192
|
-
self.context = old
|
193
|
-
res
|
194
|
-
end
|
195
|
-
|
196
|
-
private
|
197
|
-
|
198
|
-
def get_global_context
|
199
|
-
default_global_context unless @global_context
|
200
|
-
@global_context.value
|
201
|
-
end
|
202
|
-
|
203
|
-
def set_global_context(data=nil)
|
204
|
-
default_global_context unless @global_context
|
205
|
-
@global_context.update { |_| data }
|
206
|
-
end
|
207
|
-
|
208
|
-
def default_global_context
|
209
|
-
@global_context = Atomic.new({})
|
210
|
-
end
|
211
|
-
|
212
|
-
def set_time_unit(u=nil)
|
213
|
-
unless ["ms","milli","milliseconds","s","seconds"].include?(u)
|
214
|
-
raise TimeUnitError, "Specify only 'seconds' or 'milliseconds'"
|
215
|
-
end
|
216
|
-
|
217
|
-
if ["ms", "milli", "milliseconds", 1000].include?(u)
|
218
|
-
@tunit = "milliseconds"
|
219
|
-
@t = 1000.0
|
220
|
-
else
|
221
|
-
default_time_unit
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def default_time_unit
|
226
|
-
@t = 1.0
|
227
|
-
@tunit = "seconds"
|
228
|
-
end
|
229
|
-
|
230
|
-
def calc_time(start, finish)
|
231
|
-
default_time_unit unless @t
|
232
|
-
((finish - start).to_f * @t)
|
233
|
-
end
|
234
|
-
|
235
|
-
def sync_stream(out = STDOUT)
|
236
|
-
IOLog.new(out)
|
237
|
-
end
|
238
|
-
|
239
|
-
def write(data)
|
240
|
-
if log_level_ok?(data[:level])
|
241
|
-
msg = unparse(data)
|
242
|
-
stream.log(msg)
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
def log_level_ok?(level)
|
247
|
-
if level
|
248
|
-
LOG_LEVEL_MAP[level.to_s] <= LOG_LEVEL
|
249
|
-
else
|
250
|
-
true
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
def progname
|
255
|
-
File.basename($0)
|
256
|
-
end
|
257
|
-
|
258
|
-
def default_log_facility
|
259
|
-
LOG_FACILITY
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
data/lib/scrolls/syslog.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'syslog'
|
2
|
-
|
3
|
-
module Scrolls
|
4
|
-
|
5
|
-
LOG_FACILITY = ENV['LOG_FACILITY'] || Syslog::LOG_USER
|
6
|
-
LOG_FACILITY_MAP = {
|
7
|
-
"auth" => Syslog::LOG_AUTH,
|
8
|
-
"authpriv" => Syslog::LOG_AUTHPRIV,
|
9
|
-
"cron" => Syslog::LOG_CRON,
|
10
|
-
"daemon" => Syslog::LOG_DAEMON,
|
11
|
-
"ftp" => Syslog::LOG_FTP,
|
12
|
-
"kern" => Syslog::LOG_KERN,
|
13
|
-
"mail" => Syslog::LOG_MAIL,
|
14
|
-
"news" => Syslog::LOG_NEWS,
|
15
|
-
"syslog" => Syslog::LOG_SYSLOG,
|
16
|
-
"user" => Syslog::LOG_USER,
|
17
|
-
"uucp" => Syslog::LOG_UUCP,
|
18
|
-
"local0" => Syslog::LOG_LOCAL0,
|
19
|
-
"local1" => Syslog::LOG_LOCAL1,
|
20
|
-
"local2" => Syslog::LOG_LOCAL2,
|
21
|
-
"local3" => Syslog::LOG_LOCAL3,
|
22
|
-
"local4" => Syslog::LOG_LOCAL4,
|
23
|
-
"local5" => Syslog::LOG_LOCAL5,
|
24
|
-
"local6" => Syslog::LOG_LOCAL6,
|
25
|
-
"local7" => Syslog::LOG_LOCAL7,
|
26
|
-
}
|
27
|
-
|
28
|
-
class SyslogLogger
|
29
|
-
def initialize(ident = 'scrolls', facility = Syslog::LOG_USER)
|
30
|
-
options = Syslog::LOG_PID|Syslog::LOG_CONS
|
31
|
-
if Syslog.opened?
|
32
|
-
@syslog = Syslog.reopen(ident, options, facility)
|
33
|
-
else
|
34
|
-
@syslog = Syslog.open(ident, options, facility)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def log(data)
|
39
|
-
@syslog.log(Syslog::LOG_INFO, "%s", data)
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.opened?
|
43
|
-
Syslog.opened?
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|