cloudwatchlogger 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0938bf9df510f455dee57614bedada1436b18be4
4
+ data.tar.gz: 68914c8ee7364ebfbbf9efbb64ef1c156f5d0b8c
5
+ SHA512:
6
+ metadata.gz: b4b5535c0195a478633513eb4fdb17d0855063e32ab54882674b2cf4617acde5b0b8fd290fddce35994e37801889f7a575f56e12b04f4859b98e932c1390b3c9
7
+ data.tar.gz: cb1c4a3124dbf2f9b1bee5443feee6a8dcf78ef14a6e10a3e968e6d4d1c9f815b3bf38644b190f00fa081d66fc2366947c730ab44e62e565e0837fd593bed6be
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ platforms :jruby do
6
+ gem 'jruby-openssl'
7
+ gem 'multi_json', '~> 1' # https://github.com/bundler/bundler/issues/4157
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 amvse
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ Overview
2
+ --------
3
+
4
+ Send logged messages to [AWS CloudWatch Logs](http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/WhatIsCloudWatchLogs.html) using the ruby [AWS SDK](http://docs.aws.amazon.com/sdkforruby/api/index.html).
5
+
6
+ Can be used in place of Ruby's Logger
7
+ (<http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/>)
8
+
9
+ In fact, it (currently) returns an instance of Logger.
10
+
11
+ Forked from [loggiler](https://github.com/freeformz/logglier).
12
+
13
+ Usage
14
+ -----
15
+ ```ruby
16
+ require 'cloudwatchlogger'
17
+
18
+ log = CloudWatchLogger.new({access_key_id: 'YOUR_ACCESS_KEY_ID', secret_access_key: 'YOUR_SECRET_ACCESS_KEY'}, 'YOUR_CLOUDWATCH_LOG_GROUP')
19
+
20
+ log.info("Hello World from Ruby")
21
+ ```
22
+
23
+ ### With Rails
24
+
25
+ config/environments/production.rb
26
+ ```ruby
27
+ RailsApplication::Application.configure do
28
+ config.logger = CloudWatchLogger.new({access_key_id: 'YOUR_ACCESS_KEY_ID', secret_access_key: 'YOUR_SECRET_ACCESS_KEY'}, 'YOUR_CLOUDWATCH_LOG_GROUP')
29
+ end
30
+ ```
31
+
32
+
33
+ ### With Rails 4
34
+
35
+ config/initializers/cloudwatchlogger.rb
36
+ ```ruby
37
+ cloudwatchlogger = CloudWatchLogger.new({access_key_id: 'YOUR_ACCESS_KEY_ID', secret_access_key: 'YOUR_SECRET_ACCESS_KEY'}, 'YOUR_CLOUDWATCH_LOG_GROUP')
38
+ Rails.logger.extend(ActiveSupport::Logger.broadcast(cloudwatchlogger))
39
+ ```
40
+
41
+ Logging
42
+ -------
43
+
44
+ CloudWatchLogger.new returns a ruby Logger object, so take a look at:
45
+
46
+ http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/
47
+
48
+ The Logger's logdev has some special format handling though.
49
+
50
+ ### Logging a string
51
+
52
+ ```ruby
53
+ log.warn "test"
54
+ ```
55
+
56
+ Will produce the following log message in CloudWatch Logs:
57
+
58
+ ```
59
+ "<Date> severity=WARN, test"
60
+ ```
61
+
62
+ ### Logging a Hash
63
+
64
+ ```ruby
65
+ log.warn :boom => :box, :bar => :soap
66
+ ```
67
+
68
+ Will produce the following log message in CloudWatch Logs:
69
+
70
+ ```
71
+ "<Date> severity=WARN, boom=box, bar=soap"
72
+ ```
73
+
74
+ Bugs
75
+ -----
76
+
77
+ https://github.com/amvse/cloudwatchlogger/issues
78
+
79
+ Pull requests welcome.
@@ -0,0 +1,27 @@
1
+ dir = File.dirname(__FILE__)
2
+ require File.expand_path(File.join(dir, 'lib', 'cloudwatchlogger', 'version'))
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'cloudwatchlogger'
6
+ s.version = CloudWatchLogger::VERSION
7
+ s.date = Time.now
8
+ s.summary = 'AWS CloudWatchLogs compatiable logger for ruby.'
9
+ s.description = 'Logger => CloudWatchLogs'
10
+
11
+ s.license = "http://opensource.org/licenses/MIT"
12
+
13
+ s.authors = ["Zane Shannon"]
14
+ s.email = 'zcs@amvse.com'
15
+ s.homepage = 'http://github.com/amvse/cloudwatchlogger'
16
+
17
+ s.files = %w{ README.md Gemfile LICENSE cloudwatchlogger.gemspec } + Dir["lib/**/*.rb"]
18
+ s.require_paths = ['lib']
19
+ s.test_files = Dir["spec/**/*.rb"]
20
+
21
+ s.required_ruby_version = '>= 1.8.6'
22
+ s.required_rubygems_version = '>= 1.3.6'
23
+
24
+ s.add_runtime_dependency 'uuid', '~> 2'
25
+ s.add_runtime_dependency 'multi_json', '~> 1'
26
+ s.add_runtime_dependency 'aws-sdk', '~> 2'
27
+ end
@@ -0,0 +1,23 @@
1
+ require File.join(File.dirname(__FILE__), 'cloudwatchlogger', 'client')
2
+
3
+ require 'logger'
4
+
5
+ module CloudWatchLogger
6
+
7
+ class LogGroupNameRequired < ArgumentError; end
8
+ class LogEventRejected < ArgumentError; end
9
+
10
+ def self.new(credentials, log_group_name, log_stream_name=nil, opts={})
11
+ client = CloudWatchLogger::Client.new(credentials, log_group_name, log_stream_name, opts)
12
+ logger = Logger.new(client)
13
+
14
+ if client.respond_to?(:formatter)
15
+ logger.formatter = client.formatter
16
+ elsif client.respond_to?(:datetime_format)
17
+ logger.datetime_format = client.datetime_format
18
+ end
19
+
20
+ logger
21
+ end
22
+
23
+ end
@@ -0,0 +1,89 @@
1
+ require 'multi_json'
2
+ require 'socket'
3
+ require 'uuid'
4
+
5
+ module CloudWatchLogger
6
+ module Client
7
+
8
+ def self.new(credentials, log_group_name, log_stream_name=nil, opts={})
9
+ unless log_group_name
10
+ raise LogGroupNameRequired.new
11
+ end
12
+
13
+ CloudWatchLogger::Client::AWS_SDK.new(credentials, log_group_name, log_stream_name, opts)
14
+ end
15
+
16
+ module InstanceMethods
17
+
18
+ def masherize_key(prefix,key)
19
+ [prefix,key.to_s].compact.join('.')
20
+ end
21
+
22
+ def masher(hash, prefix=nil)
23
+ hash.map do |v|
24
+ if v[1].is_a?(Hash)
25
+ masher(v[1],masherize_key(prefix,v[0]))
26
+ else
27
+ "#{masherize_key(prefix,v[0])}=" << case v[1]
28
+ when Symbol
29
+ v[1].to_s
30
+ else
31
+ v[1].inspect
32
+ end
33
+ end
34
+ end.join(", ")
35
+ end
36
+
37
+ def formatter
38
+ proc do |severity, datetime, progname, msg|
39
+ processid=Process.pid
40
+ if @format == :json && msg.is_a?(Hash)
41
+ MultiJson.dump(msg.merge({ :severity => severity,
42
+ :datetime => datetime,
43
+ :progname => progname,
44
+ :pid => processid }))
45
+ else
46
+ message = "#{datetime} "
47
+ message << massage_message(msg, severity, processid)
48
+ end
49
+ end
50
+ end
51
+
52
+ def massage_message(incoming_message, severity, processid)
53
+ outgoing_message = ""
54
+
55
+ outgoing_message << "pid=#{processid}, severity=#{severity}, "
56
+
57
+ case incoming_message
58
+ when Hash
59
+ outgoing_message << masher(incoming_message)
60
+ when String
61
+ outgoing_message << incoming_message
62
+ else
63
+ outgoing_message << incoming_message.inspect
64
+ end
65
+ outgoing_message
66
+ end
67
+
68
+ def setup_credentials(credentials)
69
+ @credentials = credentials
70
+ end
71
+
72
+ def setup_log_group_name(name)
73
+ @log_group_name = name
74
+ end
75
+
76
+ def setup_log_stream_name(name)
77
+ @log_stream_name = name
78
+ if @log_stream_name.nil?
79
+ uuid = UUID.new
80
+ @log_stream_name = "#{Socket.gethostname}-#{uuid.generate}"
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
88
+
89
+ require File.join(File.dirname(__FILE__), 'client', 'aws_sdk')
@@ -0,0 +1,26 @@
1
+ require File.join(File.dirname(__FILE__), 'aws_sdk', 'threaded')
2
+
3
+ module CloudWatchLogger
4
+ module Client
5
+ class AWS_SDK
6
+ include CloudWatchLogger::Client::InstanceMethods
7
+
8
+ attr_reader :input_uri, :deliverer
9
+
10
+ def initialize(credentials, log_group_name, log_stream_name, opts={})
11
+ setup_credentials(credentials)
12
+ setup_log_group_name(log_group_name)
13
+ setup_log_stream_name(log_stream_name)
14
+ @deliverer = CloudWatchLogger::Client::AWS_SDK::DeliveryThreadManager.new(@credentials, @log_group_name, @log_stream_name, opts)
15
+ end
16
+
17
+ def write(message)
18
+ @deliverer.deliver(message)
19
+ end
20
+
21
+ def close
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,122 @@
1
+ require 'aws-sdk'
2
+ require 'thread'
3
+
4
+ module CloudWatchLogger
5
+ module Client
6
+ class AWS_SDK
7
+
8
+ # Used by the Threaded client to manage the delivery thread
9
+ # recreating it if is lost due to a fork.
10
+ #
11
+ class DeliveryThreadManager
12
+ def initialize(credentials, log_group_name, log_stream_name, opts={})
13
+ @credentials, @log_group_name, @log_stream_name, @opts = credentials, log_group_name, log_stream_name, opts
14
+ start_thread
15
+ end
16
+
17
+ # Pushes a message to the delivery thread, starting one if necessary
18
+ def deliver(message)
19
+ start_thread unless @thread.alive?
20
+ @thread.deliver(message)
21
+ #Race condition? Sometimes we need to rescue this and start a new thread
22
+ rescue NoMethodError
23
+ @thread.kill #Try not to leak threads, should already be dead anyway
24
+ start_thread
25
+ retry
26
+ end
27
+
28
+ private
29
+
30
+ def start_thread
31
+ @thread = DeliveryThread.new(@credentials, @log_group_name, @log_stream_name, @opts)
32
+ end
33
+ end
34
+
35
+ class DeliveryThread < Thread
36
+
37
+ def initialize(credentials, log_group_name, log_stream_name, opts={})
38
+ @credentials, @log_group_name, @log_stream_name, @opts = credentials, log_group_name, log_stream_name, opts
39
+ opts[:open_timeout] = opts[:open_timeout] || 120
40
+ opts[:read_timeout] = opts[:read_timeout] || 120
41
+
42
+ @queue = Queue.new
43
+ @exiting = false
44
+
45
+ super do
46
+ loop do
47
+
48
+ if @client.nil?
49
+ connect! opts
50
+ end
51
+
52
+ msg = @queue.pop
53
+ break if msg == :__delivery_thread_exit_signal__
54
+
55
+ begin
56
+ event = {
57
+ log_group_name: @log_group_name,
58
+ log_stream_name: @log_stream_name,
59
+ log_events: [{
60
+ timestamp: (Time.now.utc.to_f.round(3)*1000).to_i,
61
+ message: msg
62
+ }]
63
+ }
64
+
65
+ if token = @sequence_token
66
+ event[:sequence_token] = token
67
+ end
68
+ response = @client.put_log_events(event)
69
+ unless response.rejected_log_events_info.nil?
70
+ raise CloudWatchLogger::LogEventRejected
71
+ end
72
+ @sequence_token = response.next_sequence_token
73
+ rescue Aws::CloudWatchLogs::Errors::InvalidSequenceTokenException => err
74
+ @sequence_token = err.message.split(' ').last
75
+ retry
76
+ end
77
+ end
78
+ end
79
+
80
+ at_exit {
81
+ exit!
82
+ join
83
+ }
84
+ end
85
+
86
+ # Signals the queue that we're exiting
87
+ def exit!
88
+ @exiting = true
89
+ @queue.push :__delivery_thread_exit_signal__
90
+ end
91
+
92
+ # Pushes a message onto the internal queue
93
+ def deliver(message)
94
+ @queue.push(message)
95
+ end
96
+
97
+ def connect!(opts={})
98
+ @client = Aws::CloudWatchLogs::Client.new(
99
+ region: @opts[:region] || 'us-east-1',
100
+ access_key_id: @credentials[:access_key_id],
101
+ secret_access_key: @credentials[:secret_access_key],
102
+ http_open_timeout: opts[:open_timeout],
103
+ http_read_timeout: opts[:read_timeout]
104
+ )
105
+ begin
106
+ @client.create_log_stream(
107
+ log_group_name: @log_group_name,
108
+ log_stream_name: @log_stream_name
109
+ )
110
+ rescue Aws::CloudWatchLogs::Errors::ResourceNotFoundException => err
111
+ @client.create_log_group(
112
+ log_group_name: @log_group_name
113
+ )
114
+ retry
115
+ rescue Aws::CloudWatchLogs::Errors::ResourceAlreadyExistsException => err
116
+ end
117
+ end
118
+ end
119
+
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,5 @@
1
+ module CloudWatchLogger
2
+
3
+ VERSION = '0.1.0'
4
+
5
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudwatchlogger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Zane Shannon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: uuid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: multi_json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2'
55
+ description: Logger => CloudWatchLogs
56
+ email: zcs@amvse.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - Gemfile
62
+ - LICENSE
63
+ - README.md
64
+ - cloudwatchlogger.gemspec
65
+ - lib/cloudwatchlogger.rb
66
+ - lib/cloudwatchlogger/client.rb
67
+ - lib/cloudwatchlogger/client/aws_sdk.rb
68
+ - lib/cloudwatchlogger/client/aws_sdk/threaded.rb
69
+ - lib/cloudwatchlogger/version.rb
70
+ homepage: http://github.com/amvse/cloudwatchlogger
71
+ licenses:
72
+ - http://opensource.org/licenses/MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.8.6
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: 1.3.6
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.5.1
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: AWS CloudWatchLogs compatiable logger for ruby.
94
+ test_files: []