sapience 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +2 -0
  5. data/.ruby-version +1 -0
  6. data/.simplecov +15 -0
  7. data/.travis.yml +27 -0
  8. data/CODE_OF_CONDUCT.md +49 -0
  9. data/Gemfile +12 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +43 -0
  12. data/Rakefile +13 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/lib/sapience/ansi_colors.rb +27 -0
  16. data/lib/sapience/appender/file.rb +138 -0
  17. data/lib/sapience/appender/sentry.rb +72 -0
  18. data/lib/sapience/appender/statsd.rb +68 -0
  19. data/lib/sapience/appender/wrapper.rb +74 -0
  20. data/lib/sapience/base.rb +381 -0
  21. data/lib/sapience/concerns/compatibility.rb +53 -0
  22. data/lib/sapience/configuration.rb +86 -0
  23. data/lib/sapience/core_ext/thread.rb +14 -0
  24. data/lib/sapience/formatters/base.rb +36 -0
  25. data/lib/sapience/formatters/color.rb +68 -0
  26. data/lib/sapience/formatters/default.rb +41 -0
  27. data/lib/sapience/formatters/json.rb +22 -0
  28. data/lib/sapience/formatters/raw.rb +12 -0
  29. data/lib/sapience/log.rb +221 -0
  30. data/lib/sapience/loggable.rb +29 -0
  31. data/lib/sapience/logger.rb +236 -0
  32. data/lib/sapience/rails.rb +15 -0
  33. data/lib/sapience/sapience.rb +357 -0
  34. data/lib/sapience/subscriber.rb +127 -0
  35. data/lib/sapience/version.rb +3 -0
  36. data/lib/sapience.rb +35 -0
  37. data/sapience.gemspec +39 -0
  38. data/test_app/.gitignore +42 -0
  39. data/test_app/.rspec +2 -0
  40. data/test_app/.ruby-version +1 -0
  41. data/test_app/Gemfile +36 -0
  42. data/test_app/README.md +24 -0
  43. data/test_app/Rakefile +6 -0
  44. data/test_app/app/assets/config/manifest.js +2 -0
  45. data/test_app/app/assets/images/.keep +0 -0
  46. data/test_app/app/assets/javascripts/posts.js +2 -0
  47. data/test_app/app/assets/stylesheets/application.css +15 -0
  48. data/test_app/app/assets/stylesheets/posts.css +4 -0
  49. data/test_app/app/assets/stylesheets/scaffold.css +84 -0
  50. data/test_app/app/channels/application_cable/channel.rb +4 -0
  51. data/test_app/app/channels/application_cable/connection.rb +4 -0
  52. data/test_app/app/controllers/application_controller.rb +3 -0
  53. data/test_app/app/controllers/concerns/.keep +0 -0
  54. data/test_app/app/controllers/posts_controller.rb +58 -0
  55. data/test_app/app/helpers/application_helper.rb +2 -0
  56. data/test_app/app/helpers/posts_helper.rb +2 -0
  57. data/test_app/app/jobs/application_job.rb +2 -0
  58. data/test_app/app/mailers/application_mailer.rb +4 -0
  59. data/test_app/app/models/application_record.rb +3 -0
  60. data/test_app/app/models/concerns/.keep +0 -0
  61. data/test_app/app/models/post.rb +3 -0
  62. data/test_app/app/models/user.rb +2 -0
  63. data/test_app/app/views/layouts/application.html.erb +13 -0
  64. data/test_app/app/views/layouts/mailer.html.erb +13 -0
  65. data/test_app/app/views/layouts/mailer.text.erb +1 -0
  66. data/test_app/app/views/posts/_form.html.erb +32 -0
  67. data/test_app/app/views/posts/create.html.erb +2 -0
  68. data/test_app/app/views/posts/destroy.html.erb +2 -0
  69. data/test_app/app/views/posts/edit.html.erb +6 -0
  70. data/test_app/app/views/posts/index.html.erb +31 -0
  71. data/test_app/app/views/posts/new.html.erb +5 -0
  72. data/test_app/app/views/posts/show.html.erb +19 -0
  73. data/test_app/app/views/posts/update.html.erb +2 -0
  74. data/test_app/bin/bundle +3 -0
  75. data/test_app/bin/rails +4 -0
  76. data/test_app/bin/rake +4 -0
  77. data/test_app/bin/setup +34 -0
  78. data/test_app/bin/update +29 -0
  79. data/test_app/config/application.rb +26 -0
  80. data/test_app/config/boot.rb +3 -0
  81. data/test_app/config/cable.yml +9 -0
  82. data/test_app/config/database.yml +25 -0
  83. data/test_app/config/environment.rb +5 -0
  84. data/test_app/config/environments/development.rb +49 -0
  85. data/test_app/config/environments/production.rb +78 -0
  86. data/test_app/config/environments/test.rb +42 -0
  87. data/test_app/config/initializers/application_controller_renderer.rb +6 -0
  88. data/test_app/config/initializers/backtrace_silencers.rb +7 -0
  89. data/test_app/config/initializers/cookies_serializer.rb +5 -0
  90. data/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  91. data/test_app/config/initializers/inflections.rb +16 -0
  92. data/test_app/config/initializers/mime_types.rb +4 -0
  93. data/test_app/config/initializers/new_framework_defaults.rb +24 -0
  94. data/test_app/config/initializers/session_store.rb +3 -0
  95. data/test_app/config/initializers/wrap_parameters.rb +14 -0
  96. data/test_app/config/locales/en.yml +23 -0
  97. data/test_app/config/puma.rb +45 -0
  98. data/test_app/config/routes.rb +3 -0
  99. data/test_app/config/secrets.yml +22 -0
  100. data/test_app/config.ru +5 -0
  101. data/test_app/db/migrate/20160812092236_create_users.rb +13 -0
  102. data/test_app/db/migrate/20160812093621_create_posts.rb +11 -0
  103. data/test_app/db/schema.rb +33 -0
  104. data/test_app/db/seeds.rb +7 -0
  105. data/test_app/lib/assets/.keep +0 -0
  106. data/test_app/lib/tasks/.keep +0 -0
  107. data/test_app/log/.keep +0 -0
  108. data/test_app/public/404.html +67 -0
  109. data/test_app/public/422.html +67 -0
  110. data/test_app/public/500.html +66 -0
  111. data/test_app/public/apple-touch-icon-precomposed.png +0 -0
  112. data/test_app/public/apple-touch-icon.png +0 -0
  113. data/test_app/public/favicon.ico +0 -0
  114. data/test_app/public/robots.txt +5 -0
  115. data/test_app/spec/controllers/posts_controller_spec.rb +7 -0
  116. data/test_app/spec/helpers/posts_helper_spec.rb +15 -0
  117. data/test_app/spec/models/post_spec.rb +5 -0
  118. data/test_app/spec/models/user_spec.rb +5 -0
  119. data/test_app/spec/rails_helper.rb +23 -0
  120. data/test_app/spec/requests/posts_spec.rb +10 -0
  121. data/test_app/spec/routing/posts_routing_spec.rb +39 -0
  122. data/test_app/spec/spec_helper.rb +60 -0
  123. data/test_app/tmp/.keep +0 -0
  124. data/test_app/vendor/assets/stylesheets/.keep +0 -0
  125. metadata +298 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2b51a1bdbf72bc1ea60b09d75b32a6f78f0ed143
4
+ data.tar.gz: 87a7710812da15bc3cdb80eb2cdedd1681658cd8
5
+ SHA512:
6
+ metadata.gz: 25c9ac8836d07d4ec09623176e2b12b603be340073b7061f42d28e9378dfc853a8ea4fa667f31d2a544b3d7afe02508459f47759867f6a1dc32277862d18fb6b
7
+ data.tar.gz: a52fbcd6f66d607db46b44b074ff943b066a5fd88c5f03916745f395f9032334acd4839a9d612948fdd21e416e987beb424981ac6076f7e0d45935b7e02554d0
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ repo_token: 2pF5ERW8NKVYqrkGeGTvzGP7oYLidlQ41
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ Gemfile.lock
2
+ spec/examples.txt
3
+ coverage/
4
+ .DS_Store
5
+ .idea
6
+ log/
7
+
8
+ /.bundle/
9
+
10
+ **/*.sqlite3
11
+
12
+ **/examples.txt
13
+ **/*.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format Fuubar
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.1
data/.simplecov ADDED
@@ -0,0 +1,15 @@
1
+ require "simplecov-json"
2
+ require "coveralls"
3
+ Coveralls.wear!
4
+
5
+ SimpleCov.refuse_coverage_drop
6
+
7
+ SimpleCov.formatters = [
8
+ SimpleCov::Formatter::HTMLFormatter,
9
+ SimpleCov::Formatter::JSONFormatter,
10
+ ]
11
+ SimpleCov.start do
12
+ add_filter "/spec/"
13
+ add_filter "/bin/"
14
+ add_filter "/gemfiles/"
15
+ end
data/.travis.yml ADDED
@@ -0,0 +1,27 @@
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+ before_install:
5
+ - rvm get head
6
+ - gem update --system
7
+ - gem install bundler
8
+ install:
9
+ - bundle install -j8 -r3
10
+ script:
11
+ - if [[ "${STYLE}" = "true" ]]; then bundle exec rake reevoocop; fi;
12
+ - bundle exec rspec spec
13
+ rvm:
14
+ - jruby-9.0.5.0
15
+ - rbx-3.9
16
+ - 2.2.4
17
+ env: STYLE=false
18
+
19
+ matrix:
20
+ fast_finish: true
21
+ include:
22
+ - rvm: 2.3.1
23
+ env: STYLE=true
24
+
25
+ addons:
26
+ code_climate:
27
+ repo_token:
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at mikael@zoolutions.se. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sapience.gemspec
4
+ gemspec
5
+
6
+ gem "pry-nav"
7
+ gem "sentry-raven"
8
+ gem "statsd-ruby"
9
+ gem "minitest_to_rspec"
10
+ gem "transpec"
11
+ gem "codeclimate", require: false
12
+ gem 'coveralls', require: false
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Mikael Henriksson
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Sapience
2
+
3
+ [![Code Climate](https://codeclimate.com/github/reevoo/sapience-rb/badges/gpa.svg)](https://codeclimate.com/github/reevoo/sapience-rb)[![Test Coverage](https://codeclimate.com/github/reevoo/sapience-rb/badges/coverage.svg)](https://codeclimate.com/github/reevoo/sapience-rb/coverage)[![Issue Count](https://codeclimate.com/github/reevoo/sapience-rb/badges/issue_count.svg)](https://codeclimate.com/github/reevoo/sapience-rb)
4
+
5
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/sapience`. To experiment with that code, run `bin/console` for an interactive prompt.
6
+
7
+ TODO: Delete this and the text above, and describe your gem
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'sapience'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install sapience
24
+
25
+ ## Usage
26
+
27
+ TODO: Write usage instructions here
28
+
29
+ ## Development
30
+
31
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
32
+
33
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
34
+
35
+ ## Contributing
36
+
37
+ Bug reports and pull requests are welcome on GitHub at https://github.com/reevoo/sapience. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
38
+
39
+
40
+ ## License
41
+
42
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
43
+
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ begin
7
+ require "reevoocop/rake_task"
8
+ ReevooCop::RakeTask.new(:reevoocop) do |task|
9
+ task.options = ["-D"]
10
+ end
11
+ rescue LoadError # rubocop:disable Lint/HandleExceptions
12
+ end
13
+ task default: [:reevocop, :spec]
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sapience"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+ module Sapience
2
+ # Formatting & colors used by optional color formatter
3
+ module AnsiColors
4
+ CLEAR = "\e[0m"
5
+ BOLD = "\e[1m"
6
+ BLACK = "\e[30m"
7
+ RED = "\e[31m"
8
+ GREEN = "\e[32m"
9
+ YELLOW = "\e[33m"
10
+ BLUE = "\e[34m"
11
+ MAGENTA = "\e[35m"
12
+ CYAN = "\e[36m"
13
+ WHITE = "\e[37m"
14
+
15
+ # Maps the log level to a color for colorized formatters
16
+ # Since this map is not frozen, it can be modified as needed
17
+ LEVEL_MAP = {
18
+ trace: MAGENTA,
19
+ debug: GREEN,
20
+ info: CYAN,
21
+ warn: BOLD,
22
+ error: RED,
23
+ fatal: RED,
24
+ }
25
+ end
26
+
27
+ end
@@ -0,0 +1,138 @@
1
+ # File appender
2
+ #
3
+ # Writes log messages to a file or open iostream
4
+ #
5
+ module Sapience
6
+ module Appender
7
+ class File < Sapience::Subscriber
8
+
9
+ # Create a File Logger appender instance.
10
+ #
11
+ # Parameters
12
+ # :file_name [String|IO]
13
+ # Name of file to write to.
14
+ # Or, an IO stream to which to write the log message to.
15
+ #
16
+ # :level [:trace | :debug | :info | :warn | :error | :fatal]
17
+ # Override the log level for this appender.
18
+ # Default: Sapience.config.default_level
19
+ #
20
+ # :formatter: [Object|Proc]
21
+ # An instance of a class that implements #call, or a Proc to be used to format
22
+ # the output from this appender
23
+ # Default: Use the built-in formatter (See: #call)
24
+ #
25
+ # :filter [Regexp|Proc]
26
+ # RegExp: Only include log messages where the class name matches the supplied
27
+ # regular expression. All other messages will be ignored.
28
+ # Proc: Only include log messages where the supplied Proc returns true
29
+ # The Proc must return true or false.
30
+ #
31
+ # Example
32
+ # require 'sapience'
33
+ #
34
+ # # Enable trace level logging
35
+ # Sapience.config.default_level = :info
36
+ #
37
+ # # Log to screen
38
+ # Sapience.add_appender(:file, io: STDOUT, formatter: :color)
39
+ #
40
+ # # And log to a file at the same time
41
+ # Sapience::Logger.add_appender(:file, file_name: 'application.log', formatter: :color)
42
+ #
43
+ # logger = Sapience['test']
44
+ # logger.info 'Hello World'
45
+ #
46
+ # Example 2. To log all levels to file and only :info and above to screen:
47
+ #
48
+ # require 'sapience'
49
+ #
50
+ # # Enable trace level logging
51
+ # Sapience.config.default_level = :trace
52
+ #
53
+ # # Log to screen but only display :info and above
54
+ # Sapience.add_appender(:file, io: STDOUT, level: :info)
55
+ #
56
+ # # And log to a file at the same time, including all :trace level data
57
+ # Sapience.add_appender(:file, file_name: 'application.log')
58
+ #
59
+ # logger = Sapience['test']
60
+ # logger.info 'Hello World'
61
+ def initialize(options = {}, deprecated_level = nil, deprecated_filter = nil, &block)
62
+ # Old style arguments: (file_name, level=nil, filter=nil, &block)
63
+ options =
64
+ if options.is_a?(Hash)
65
+ options.dup
66
+ else
67
+ file_name = options
68
+ opts = {}
69
+ if file_name.respond_to?(:write) && file_name.respond_to?(:close)
70
+ opts[:io] = file_name
71
+ else
72
+ opts[:file_name] = file_name
73
+ end
74
+ opts[:level] = deprecated_level if deprecated_level
75
+ opts[:filter] = deprecated_filter if deprecated_filter
76
+ opts
77
+ end
78
+
79
+ if io = options.delete(:io)
80
+ @log = io
81
+ else
82
+ @file_name = options.delete(:file_name)
83
+ fail "Sapience::Appender::File missing mandatory parameter :file_name or :io" unless @file_name
84
+ reopen
85
+ end
86
+
87
+ # Set the log level and formatter if supplied
88
+ super(options, &block)
89
+ end
90
+
91
+ # After forking an active process call #reopen to re-open
92
+ # open the file handles etc to resources
93
+ #
94
+ # Note: This method will only work if :file_name was supplied
95
+ # on the initializer.
96
+ # If :io was supplied, it will need to be re-opened manually.
97
+ def reopen
98
+ return unless @file_name
99
+ ensure_folder_exist
100
+
101
+ @log = open(@file_name, (::File::WRONLY | ::File::APPEND | ::File::CREAT))
102
+ # Force all log entries to write immediately without buffering
103
+ # Allows multiple processes to write to the same log file simultaneously
104
+ @log.sync = true
105
+ @log.set_encoding(Encoding::BINARY) if @log.respond_to?(:set_encoding)
106
+ @log
107
+ end
108
+
109
+ def ensure_folder_exist
110
+ return if ::File.exist?(@file_name)
111
+
112
+ dir_name = ::File.dirname(@file_name)
113
+
114
+ FileUtils.mkdir_p(dir_name)
115
+ end
116
+
117
+ # Pass log calls to the underlying Rails, log4j or Ruby logger
118
+ # trace entries are mapped to debug since :trace is not supported by the
119
+ # Ruby or Rails Loggers
120
+ def log(log)
121
+ return false unless should_log?(log)
122
+
123
+ # Since only one appender thread will be writing to the file at a time
124
+ # it is not necessary to protect access to the file with a semaphore
125
+ # Allow this logger to filter out log levels lower than it's own
126
+ @log.write(formatter.call(log, self) << "\n")
127
+ true
128
+ end
129
+
130
+ # Flush all pending logs to disk.
131
+ # Waits for all sent documents to be writted to disk
132
+ def flush
133
+ @log.flush if @log.respond_to?(:flush)
134
+ end
135
+
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,72 @@
1
+ begin
2
+ require "sentry-raven"
3
+ rescue LoadError
4
+ raise 'Gem sentry-raven is required for logging purposes. Please add the gem "sentry-raven" to your Gemfile.'
5
+ end
6
+
7
+ # Send log messages to sentry
8
+ #
9
+ # Example:
10
+ # Sapience.add_appender(:sentry, {})
11
+ #
12
+ class Sapience::Appender::Sentry < Sapience::Subscriber
13
+ # Create Appender
14
+ #
15
+ # Parameters
16
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
17
+ # Override the log level for this appender.
18
+ # Default: :error
19
+ #
20
+ # formatter: [Object|Proc|Symbol|Hash]
21
+ # An instance of a class that implements #call, or a Proc to be used to format
22
+ # the output from this appender
23
+ # Default: Use the built-in formatter (See: #call)
24
+ #
25
+ # filter: [Regexp|Proc]
26
+ # RegExp: Only include log messages where the class name matches the supplied.
27
+ # regular expression. All other messages will be ignored.
28
+ # Proc: Only include log messages where the supplied Proc returns true
29
+ # The Proc must return true or false.
30
+ #
31
+ # host: [String]
32
+ # Name of this host to appear in log messages.
33
+ # Default: Sapience.config.host
34
+ #
35
+ # application: [String]
36
+ # Name of this application to appear in log messages.
37
+ # Default: Sapience.config.application
38
+ def initialize(options = {}, &block)
39
+ options = options.is_a?(Hash) ? options.dup : { level: options }
40
+ options[:level] ||= :error
41
+
42
+ super(options, &block)
43
+ end
44
+
45
+ # Send an error notification to sentry
46
+ def log(log)
47
+ return false unless should_log?(log)
48
+
49
+ context = formatter.call(log, self)
50
+ if log.exception
51
+ context.delete(:exception)
52
+ Raven.capture_exception(log.exception, context)
53
+ else
54
+ message = {
55
+ error_class: context.delete(:name),
56
+ error_message: context.delete(:message),
57
+ context: context,
58
+ }
59
+ message[:backtrace] = log.backtrace if log.backtrace
60
+ Raven.capture_message(message[:error_message], message)
61
+ end
62
+ true
63
+ end
64
+
65
+ private
66
+
67
+ # Use Raw Formatter by default
68
+ def default_formatter
69
+ Sapience::Formatters::Raw.new
70
+ end
71
+
72
+ end
@@ -0,0 +1,68 @@
1
+ require "uri"
2
+ begin
3
+ require "statsd-ruby"
4
+ rescue LoadError
5
+ raise 'Gem statsd-ruby is required for logging metrics. Please add the gem "statsd-ruby" to your Gemfile.'
6
+ end
7
+
8
+ # Example:
9
+ # Sapience.add_appender(:statsd, {url: "udp://localhost:2222"})
10
+ #
11
+ class Sapience::Appender::Statsd < Sapience::Subscriber
12
+ # Create Appender
13
+ #
14
+ # Parameters:
15
+ # url: [String]
16
+ # Valid URL to post to.
17
+ # Example:
18
+ # udp://localhost:8125
19
+ # Example, send all metrics to a particular namespace:
20
+ # udp://localhost:8125/namespace
21
+ # Default: udp://localhost:8125
22
+ def initialize(options = {}, &block)
23
+ options = options.is_a?(Hash) ? options.dup : { level: options }
24
+ url = options.delete(:url) || "udp://localhost:8125"
25
+ uri = URI.parse(url)
26
+ fail('Statsd only supports udp. Example: "udp://localhost:8125"') if uri.scheme != "udp"
27
+
28
+ @statsd = ::Statsd.new(uri.host, uri.port)
29
+ path = uri.path.chomp("/")
30
+ @statsd.namespace = path.sub("/", "") if path != ""
31
+
32
+ super(options, &block)
33
+ end
34
+
35
+ # Send an error notification to sentry
36
+ def log(log)
37
+ metric = log.metric
38
+ return false unless metric
39
+
40
+ if log.duration
41
+ timing(metric, log.duration)
42
+ else
43
+ amount = (log.metric_amount || 1).round
44
+ if amount < 0
45
+ decrement(metric, amount)
46
+ else
47
+ increment(metric, amount)
48
+ end
49
+ end
50
+ true
51
+ end
52
+
53
+ def timing(metric, duration)
54
+ @statsd.timing(metric, duration)
55
+ end
56
+
57
+ def increment(metric, amount)
58
+ @statsd.batch do
59
+ amount.times { @statsd.increment(metric) }
60
+ end
61
+ end
62
+
63
+ def decrement(metric, amount)
64
+ @statsd.batch do
65
+ amount.abs.times { @statsd.decrement(metric) }
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,74 @@
1
+ # Send log messages to any standard Ruby logging class.
2
+ #
3
+ # Forwards logging call to loggers such as Logger, log4r, etc.
4
+ #
5
+ module Sapience
6
+ module Appender
7
+ class Wrapper < Sapience::Subscriber
8
+ attr_reader :logger
9
+
10
+ # Forward all logging calls to the supplied logging instance.
11
+ #
12
+ # Parameters
13
+ # logger: [Object]
14
+ # Instance of an existing logger conforming to the Ruby Logger methods.
15
+ #
16
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
17
+ # Override the log level for this appender.
18
+ # Default: Sapience.config.default_level
19
+ #
20
+ # formatter: [Object|Proc]
21
+ # An instance of a class that implements #call, or a Proc to be used to format
22
+ # the output from this appender
23
+ # Default: Use the built-in formatter (See: #call)
24
+ #
25
+ # filter: [Regexp|Proc]
26
+ # RegExp: Only include log messages where the class name matches the supplied.
27
+ # regular expression. All other messages will be ignored.
28
+ # Proc: Only include log messages where the supplied Proc returns true
29
+ # The Proc must return true or false.
30
+ #
31
+ # Ruby Logger
32
+ # require 'logger'
33
+ # require 'sapience'
34
+ #
35
+ # ruby_logger = Logger.new(STDOUT)
36
+ # Sapience.add_appender(:wrapper, logger: ruby_logger)
37
+ #
38
+ # logger = Sapience['test']
39
+ # logger.info('Hello World', some: :payload)
40
+ #
41
+ def initialize(options, &block)
42
+ # Backward compatibility
43
+ options = { logger: options } unless options.is_a?(Hash)
44
+ options = options.dup
45
+ @logger = options.delete(:logger)
46
+
47
+ # Check if the custom appender responds to all the log levels. For example Ruby ::Logger
48
+ if does_not_implement = LEVELS[1..-1].find { |i| !@logger.respond_to?(i) }
49
+ fail(ArgumentError, "Supplied logger does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}")
50
+ end
51
+
52
+ fail "Sapience::Appender::Wrapper missing mandatory parameter :logger" unless @logger
53
+ super(options, &block)
54
+ end
55
+
56
+ # Pass log calls to the underlying Rails, log4j or Ruby logger
57
+ # trace entries are mapped to debug since :trace is not supported by the
58
+ # Ruby or Rails Loggers
59
+ def log(log)
60
+ return false unless should_log?(log)
61
+
62
+ @logger.send(log.level == :trace ? :debug : log.level, formatter.call(log, self))
63
+ true
64
+ end
65
+
66
+ # Flush all pending logs to disk.
67
+ # Waits for all sent documents to be writted to disk
68
+ def flush
69
+ @logger.flush if @logger.respond_to?(:flush)
70
+ end
71
+
72
+ end
73
+ end
74
+ end