elasticelmah 0.0.1
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/elasticelmah.rb +131 -0
- data/tests/elasticelmah_tests.rb +129 -0
- metadata +113 -0
data/lib/elasticelmah.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'elasticsearch'
|
2
|
+
require 'log4r'
|
3
|
+
require 'time'
|
4
|
+
module ElasticElmah
|
5
|
+
|
6
|
+
class Outputter < Log4r::Outputter
|
7
|
+
attr_reader :log
|
8
|
+
def initialize(_name, hash={})
|
9
|
+
super(_name, hash)
|
10
|
+
@index = "logs"
|
11
|
+
|
12
|
+
@index = hash[:index] if hash.has_key?(:index)
|
13
|
+
@closed = false
|
14
|
+
@initialized = false
|
15
|
+
@client = Elasticsearch::Client.new #log: true
|
16
|
+
end
|
17
|
+
|
18
|
+
def time_now
|
19
|
+
Time.now.utc
|
20
|
+
end
|
21
|
+
def time_stamp
|
22
|
+
time_now.strftime('%Y-%m-%dT%H:%M:%S.%7N%:z') #"0001-01-01T00:00:00.0 00 000 0+01:00"
|
23
|
+
end
|
24
|
+
|
25
|
+
def closed?
|
26
|
+
@closed
|
27
|
+
end
|
28
|
+
|
29
|
+
def close
|
30
|
+
@closed = true
|
31
|
+
@level = OFF
|
32
|
+
OutputterFactory.create_methods(self)
|
33
|
+
Logger.log_internal {"Outputter '#{@name}' closed and set to OFF"}
|
34
|
+
end
|
35
|
+
|
36
|
+
def serialize(l)
|
37
|
+
# attr_reader :level, :tracer, :data, :name, :fullname
|
38
|
+
# [level] The integer level of the log event. Use LNAMES[level]
|
39
|
+
# to get the actual level name.
|
40
|
+
# [tracer] The execution stack returned by <tt>caller</tt> at the
|
41
|
+
# log event. It is nil if the invoked Logger's trace is false.
|
42
|
+
# [data] The object that was passed into the logging method.
|
43
|
+
# [name] The name of the logger that was invoked.
|
44
|
+
# [fullname] The fully qualified name of the logger that was invoked.
|
45
|
+
return {
|
46
|
+
loggerName:l.name,
|
47
|
+
level: Log4r::LNAMES[l.level],
|
48
|
+
message: l.data != nil && l.data.respond_to?(:message) ? l.data.message : l.data,
|
49
|
+
threadName:"",
|
50
|
+
timeStamp:time_stamp,
|
51
|
+
locationInfo:{
|
52
|
+
className:"?",
|
53
|
+
fileName:"?",
|
54
|
+
lineNumber:"?",
|
55
|
+
methodName:"?"
|
56
|
+
},
|
57
|
+
userName:"",
|
58
|
+
properties:{},
|
59
|
+
exceptionString: l.tracer!=nil ? l.tracer.join("\n") : "",
|
60
|
+
domain:"",
|
61
|
+
identity:""
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
#######
|
66
|
+
private
|
67
|
+
#######
|
68
|
+
def create_index
|
69
|
+
@client.indices.create(index: @index, body: {
|
70
|
+
settings: {
|
71
|
+
index: { number_of_shards: 1, number_of_replicas: 0 }
|
72
|
+
},
|
73
|
+
mappings:{
|
74
|
+
"LoggingEvent" => logging_event_mappings
|
75
|
+
}
|
76
|
+
})
|
77
|
+
#@client.indices.put_mapping(index: @index, type: 'LoggingEvent', body: logging_event_mappings)
|
78
|
+
end
|
79
|
+
def logging_event_mappings
|
80
|
+
{
|
81
|
+
_source: {
|
82
|
+
enabled: true,
|
83
|
+
compress: false
|
84
|
+
},
|
85
|
+
_ttl: {
|
86
|
+
enabled: true,
|
87
|
+
default: "24d"
|
88
|
+
},
|
89
|
+
_timestamp: {
|
90
|
+
enabled: true,
|
91
|
+
path: "timeStamp",
|
92
|
+
store: true
|
93
|
+
},
|
94
|
+
properties: {
|
95
|
+
timeStamp: {
|
96
|
+
type: "date"
|
97
|
+
},
|
98
|
+
message:{type: "string"},
|
99
|
+
exceptionString:{type: "string"},
|
100
|
+
domain:{type: "string"},
|
101
|
+
identity:{type: "string"},
|
102
|
+
userName: {type: "string"},
|
103
|
+
locationInfo:{
|
104
|
+
type: "object",
|
105
|
+
properties: {
|
106
|
+
className:{type: "string"},
|
107
|
+
fileName:{type: "string"},
|
108
|
+
lineNumber:{type: "string"},
|
109
|
+
methodName:{type: "string"}
|
110
|
+
}
|
111
|
+
},
|
112
|
+
threadName:{type: "string"},
|
113
|
+
loggerName:{type: "string"},
|
114
|
+
level:{type:"string"},
|
115
|
+
properties:{ type: "object", store: "yes" }
|
116
|
+
}
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
# perform the write
|
121
|
+
def canonical_log(logevent)
|
122
|
+
if !@initialized
|
123
|
+
@initialized= true
|
124
|
+
if !@client.indices.exists(index: @index)
|
125
|
+
create_index
|
126
|
+
end
|
127
|
+
end
|
128
|
+
@client.index index: @index, type: 'LoggingEvent', body: serialize(logevent)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
2
|
+
|
3
|
+
require 'elasticelmah'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'securerandom'
|
6
|
+
class OutputterWithFakedElastic < ElasticElmah::Outputter
|
7
|
+
attr_reader :written
|
8
|
+
def initialize(_name, hash={})
|
9
|
+
super(_name, hash)
|
10
|
+
@written = []
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def canonical_log(logevent)
|
15
|
+
@written.push(logevent)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class OutputterWithFakedElasticAndTimestamp < OutputterWithFakedElastic
|
20
|
+
def initialize(_name, hash={})
|
21
|
+
super(_name, hash)
|
22
|
+
@written = []
|
23
|
+
end
|
24
|
+
def time_now
|
25
|
+
Time.new(2008,6,21, 12,30,0, "+01:00")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TestElasticElmahOutputterForReal < Test::Unit::TestCase
|
30
|
+
def setup
|
31
|
+
Log4r::Logger.root.level = Log4r::INFO
|
32
|
+
@logger = Log4r::Logger.new 'testout_for_real'
|
33
|
+
@index = SecureRandom.uuid
|
34
|
+
@outp = ElasticElmah::Outputter.new 'outp_for_real',{index: @index}
|
35
|
+
@logger.add('outp_for_real')
|
36
|
+
@client = Elasticsearch::Client.new# log: true
|
37
|
+
end
|
38
|
+
def teardown
|
39
|
+
@client.indices.delete(index: @index)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_generated_from_logging_event_data
|
43
|
+
@logger.error('some_error')
|
44
|
+
@client.indices.flush(index: @index )
|
45
|
+
result = @client.search index: @index
|
46
|
+
assert_equal 1, result["hits"]["hits"].length
|
47
|
+
hits = result["hits"]["hits"]
|
48
|
+
assert_equal 'some_error', hits[0]["_source"]["message"]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
class TestElasticElmahOutputter < Test::Unit::TestCase
|
54
|
+
def setup
|
55
|
+
Log4r::Logger.root.level = Log4r::INFO
|
56
|
+
@logger = Log4r::Logger.new 'testoutp'
|
57
|
+
|
58
|
+
@outp = OutputterWithFakedElasticAndTimestamp.new 'outp1'
|
59
|
+
@logger.add('outp1')
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_generated_from_logging_event_data
|
63
|
+
@logger.error('some_error')
|
64
|
+
assert_equal([{:loggerName=>"testoutp",
|
65
|
+
:level=>"ERROR",
|
66
|
+
:message=>"some_error",
|
67
|
+
:threadName=>"",
|
68
|
+
:timeStamp=>"2008-06-21T12:30:00.0000000+01:00",
|
69
|
+
:locationInfo=>
|
70
|
+
{:className=>"?", :fileName=>"?", :lineNumber=>"?", :methodName=>"?"},
|
71
|
+
:userName=>"",
|
72
|
+
:properties=>{},
|
73
|
+
:exceptionString=>"",
|
74
|
+
:domain=>"",
|
75
|
+
:identity=>""}],
|
76
|
+
@outp.written.map do |written| @outp.serialize(written) end)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class FakeLogger
|
81
|
+
attr_reader :name, :fullname
|
82
|
+
def initialize(name,fullname)
|
83
|
+
@name = name
|
84
|
+
@fullname = fullname
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class TestElasticElmahSerializer < Test::Unit::TestCase
|
89
|
+
def setup
|
90
|
+
@outp = OutputterWithFakedElasticAndTimestamp.new 'outp2'
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def test_will_serialize_exception_string
|
95
|
+
logger = FakeLogger.new('logger_1', 'fullname_of_logger_1')
|
96
|
+
tracer = caller
|
97
|
+
data = Exception.new("Message")
|
98
|
+
l = Log4r::LogEvent.new(Log4r::INFO, logger, tracer, data)
|
99
|
+
s = @outp.serialize(l)
|
100
|
+
assert_equal("Message",s[:message])
|
101
|
+
assert_equal(caller.join("\n"),s[:exceptionString])
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_will_serialize_exception_without_exception_string
|
105
|
+
expected = {
|
106
|
+
loggerName:"logger_1",
|
107
|
+
level:"INFO",
|
108
|
+
message:"Message",
|
109
|
+
threadName:"",
|
110
|
+
timeStamp:"2008-06-21T12:30:00.0000000+01:00",
|
111
|
+
locationInfo:{
|
112
|
+
className:"?",
|
113
|
+
fileName:"?",
|
114
|
+
lineNumber:"?",
|
115
|
+
methodName:"?"
|
116
|
+
},
|
117
|
+
userName:"",
|
118
|
+
properties:{},
|
119
|
+
exceptionString:"",
|
120
|
+
domain:"",
|
121
|
+
identity:""
|
122
|
+
}
|
123
|
+
logger = FakeLogger.new('logger_1', 'fullname_of_logger_1')
|
124
|
+
tracer = nil
|
125
|
+
data = Exception.new("Message")
|
126
|
+
l = Log4r::LogEvent.new(Log4r::INFO, logger, tracer, data)
|
127
|
+
assert_equal(expected, @outp.serialize(l))
|
128
|
+
end
|
129
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: elasticelmah
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Oskar Gewalli
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-02-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: elasticsearch
|
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
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: log4r
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: ! 'An appender for log4r that appends to elastic search
|
79
|
+
|
80
|
+
'
|
81
|
+
email: ''
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- lib/elasticelmah.rb
|
87
|
+
- tests/elasticelmah_tests.rb
|
88
|
+
homepage:
|
89
|
+
licenses: []
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 1.8.24
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: ElasticSearch log4r appender
|
112
|
+
test_files:
|
113
|
+
- tests/elasticelmah_tests.rb
|