logtail 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +33 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/CHANGELOG.md +12 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE.md +15 -0
  8. data/README.md +4 -0
  9. data/Rakefile +72 -0
  10. data/lib/logtail.rb +36 -0
  11. data/lib/logtail/config.rb +154 -0
  12. data/lib/logtail/config/integrations.rb +17 -0
  13. data/lib/logtail/context.rb +9 -0
  14. data/lib/logtail/contexts.rb +12 -0
  15. data/lib/logtail/contexts/http.rb +31 -0
  16. data/lib/logtail/contexts/release.rb +52 -0
  17. data/lib/logtail/contexts/runtime.rb +23 -0
  18. data/lib/logtail/contexts/session.rb +24 -0
  19. data/lib/logtail/contexts/system.rb +29 -0
  20. data/lib/logtail/contexts/user.rb +28 -0
  21. data/lib/logtail/current_context.rb +168 -0
  22. data/lib/logtail/event.rb +36 -0
  23. data/lib/logtail/events.rb +10 -0
  24. data/lib/logtail/events/controller_call.rb +44 -0
  25. data/lib/logtail/events/error.rb +40 -0
  26. data/lib/logtail/events/exception.rb +10 -0
  27. data/lib/logtail/events/sql_query.rb +26 -0
  28. data/lib/logtail/events/template_render.rb +25 -0
  29. data/lib/logtail/integration.rb +40 -0
  30. data/lib/logtail/integrator.rb +50 -0
  31. data/lib/logtail/log_devices.rb +8 -0
  32. data/lib/logtail/log_devices/http.rb +368 -0
  33. data/lib/logtail/log_devices/http/flushable_dropping_sized_queue.rb +52 -0
  34. data/lib/logtail/log_devices/http/request_attempt.rb +20 -0
  35. data/lib/logtail/log_entry.rb +110 -0
  36. data/lib/logtail/logger.rb +270 -0
  37. data/lib/logtail/logtail.rb +36 -0
  38. data/lib/logtail/timer.rb +21 -0
  39. data/lib/logtail/util.rb +7 -0
  40. data/lib/logtail/util/non_nil_hash_builder.rb +40 -0
  41. data/lib/logtail/version.rb +3 -0
  42. data/logtail.gemspec +43 -0
  43. data/spec/README.md +13 -0
  44. data/spec/logtail/current_context_spec.rb +113 -0
  45. data/spec/logtail/events/controller_call_spec.rb +12 -0
  46. data/spec/logtail/events/error_spec.rb +15 -0
  47. data/spec/logtail/log_devices/http_spec.rb +185 -0
  48. data/spec/logtail/log_entry_spec.rb +22 -0
  49. data/spec/logtail/logger_spec.rb +227 -0
  50. data/spec/spec_helper.rb +22 -0
  51. data/spec/support/logtail.rb +5 -0
  52. data/spec/support/socket_hostname.rb +12 -0
  53. data/spec/support/timecop.rb +3 -0
  54. data/spec/support/webmock.rb +3 -0
  55. metadata +238 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1be8318ce433573150e153c4def0b1583a7ff79cb23e66160b3a07662fe40346
4
+ data.tar.gz: 0ef24a7258f255d275ed26d18de1c1d31948c270eacbb93b85a1ff8fa1d635f9
5
+ SHA512:
6
+ metadata.gz: 856d92f191144d4b827b5088fedc0963ef1a96a7e4ec8631e5883cb275ad9b7f5928dcfff0c2047dafb75ee0d8a40d798750457bbc3028b148fb3b6c1cf15320
7
+ data.tar.gz: 0ab7a3f459aaaffe7494e626f535190f2bb643b458b17cd9209ba4b399f6daf336fb8707851272c5b928050b573b5d29db61c4cf020ed3997ead233ca865b193
@@ -0,0 +1,33 @@
1
+ name: build
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+
8
+ runs-on: ubuntu-20.04
9
+
10
+ strategy:
11
+ matrix:
12
+ ruby-version:
13
+ - 3.0.0
14
+ - 2.7.2
15
+ - 2.6.6
16
+ - 2.5.8
17
+ - 2.4.10
18
+ - 2.3.8
19
+ - 2.2.10
20
+ - jruby-9.2.14.0
21
+ - truffleruby-21.0.0
22
+
23
+ steps:
24
+ - uses: actions/checkout@v2
25
+
26
+ - name: Set up Ruby ${{ matrix.ruby-version }}
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: ${{ matrix.ruby-version }}
30
+ bundler-cache: true
31
+
32
+ - name: Run tests
33
+ run: bundle exec rspec --format documentation
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ /doc/
2
+ /pkg/
3
+ /spec/reports/
4
+ /tmp/
5
+
6
+ # rspec failure tracking
7
+ .rspec_status
8
+ .DS_Store
9
+ .rvmrc
10
+ .ruby-version
11
+ coverage
12
+ Gemfile.lock
13
+ *.swp
14
+ *.gem
15
+
16
+ Gemfile.lock
17
+ gemfiles/*.lock
18
+
19
+ /.bundle
20
+ /.yardoc
21
+ /doc
22
+ /log
23
+ /tmp
24
+ /pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --order rand
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2021-02-11
11
+
12
+ - The first version of the client.
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in logtail.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
data/LICENSE.md ADDED
@@ -0,0 +1,15 @@
1
+ # License
2
+
3
+ Copyright (c) 2021, Logtail
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose
6
+ with or without fee is hereby granted, provided that the above copyright notice
7
+ and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
13
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
14
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
15
+ THIS SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # 🪵 Logtail - Ruby Logging For Everyone
2
+
3
+ [![ISC License](https://img.shields.io/badge/license-ISC-ff69b4.svg)](LICENSE.md)
4
+ [![Build Status](https://github.com/logtail/logtail-ruby/workflows/build/badge.svg)](https://github.com/logtail/logtail-ruby/actions?query=workflow%3Abuild)
data/Rakefile ADDED
@@ -0,0 +1,72 @@
1
+ require "bundler/gem_tasks"
2
+ require "logtail"
3
+
4
+ def puts_with_level(message, level = :info)
5
+ case level
6
+ when :info
7
+ puts("\e[31m#{message}\e[0m")
8
+ when :error
9
+ puts("\e[31m#{message}\e[0m")
10
+ when :success
11
+ puts("\e[32m#{message}\e[0m")
12
+ else
13
+ puts(message)
14
+ end
15
+ end
16
+
17
+ task :test_the_pipes, [:api_key] do |t, args|
18
+ support_email = "support@logtail.com"
19
+ # Do not modify below this line. It's important to keep the `Logtail::Logger`
20
+ # because it provides an API for logging structured data and capturing context.
21
+ header = <<~HEREDOC
22
+ ### I want our own pixelart too, but no time for that for now ###
23
+ HEREDOC
24
+
25
+ puts header
26
+
27
+ current_context = Logtail::CurrentContext.instance.snapshot
28
+ entry = Logtail::LogEntry.new(:info, Time.now, nil, "Testing the pipes (click the inspect icon to view more details)", current_context, nil)
29
+ http_device = Logtail::LogDevices::HTTP.new(args.api_key, flush_continuously: false)
30
+ response = http_device.deliver_one(entry)
31
+ if response.is_a?(Exception)
32
+ message = <<~HEREDOC
33
+ Unable to deliver logs.
34
+ Here's what we received from the Logtail API:
35
+ #{response.inspect}
36
+ If you continue to have trouble please contact support:
37
+ #{support_email}
38
+ HEREDOC
39
+ puts_with_level(message, :error)
40
+ elsif response.is_a?(Net::HTTPResponse)
41
+ if response.code.start_with? '2'
42
+ puts_with_level("Logs successfully sent! View them at https://logtail.com",
43
+ :success)
44
+ else
45
+ message =
46
+ <<~HEREDOC
47
+ Unable to deliver logs.
48
+ We received a #{response.code} response from the Logtail API:
49
+ #{response.body.inspect}
50
+ If you continue to have trouble please contact support:
51
+ #{support_email}
52
+ HEREDOC
53
+ puts_with_level(message, :error)
54
+ end
55
+ end
56
+ end
57
+
58
+ task :console do
59
+ require 'irb'
60
+ require 'irb/completion'
61
+ require 'logtail'
62
+ $VERBOSE = nil
63
+
64
+ def reload!
65
+ files = $LOADED_FEATURES.select { |feat| feat =~ /\/logtail\// }
66
+ files.each { |file| load file }
67
+ "reloaded"
68
+ end
69
+
70
+ ARGV.clear
71
+ IRB.start
72
+ end
data/lib/logtail.rb ADDED
@@ -0,0 +1,36 @@
1
+ # Base (must come first, order matters)
2
+ require "logtail/version"
3
+ require "logtail/config"
4
+ require "logtail/util"
5
+
6
+ # Load frameworks
7
+
8
+ # Other (sorted alphabetically)
9
+ require "logtail/contexts"
10
+ require "logtail/current_context"
11
+ require "logtail/events"
12
+ require "logtail/integration"
13
+ require "logtail/log_devices"
14
+ require "logtail/log_entry"
15
+ require "logtail/logger"
16
+ require "logtail/timer"
17
+ require "logtail/integrator"
18
+ require "logtail/integration"
19
+
20
+ module Logtail
21
+ # Access the main configuration object. Please see {{Logtail::Config}} for more details.
22
+ def self.config
23
+ Config.instance
24
+ end
25
+
26
+ # Starts a timer for timing events. Please see {{Logtail::Logtail.start}} for more details.
27
+ def self.start_timer
28
+ Timer.start
29
+ end
30
+
31
+ # Adds context to all logs written within the passed block. Please see
32
+ # {{Logtail::CurrentContext.with}} for a more detailed description with examples.
33
+ def self.with_context(context, &block)
34
+ CurrentContext.with(context, &block)
35
+ end
36
+ end
@@ -0,0 +1,154 @@
1
+ require "logger"
2
+ require "singleton"
3
+
4
+ module Logtail
5
+ # Singleton class for reading and setting Logtail configuration.
6
+ #
7
+ # For Rails apps, this is installed into `config.logtail`. See examples below.
8
+ #
9
+ # @example Rails example
10
+ # config.logtail.append_metadata = false
11
+ # @example Everything else
12
+ # config = Logtail::Config.instance
13
+ # config.append_metdata = false
14
+ class Config
15
+ # @private
16
+ class NoLoggerError < StandardError; end
17
+
18
+ # @private
19
+ class SimpleLogFormatter < ::Logger::Formatter
20
+ # This method is invoked when a log event occurs
21
+ def call(severity, timestamp, progname, msg)
22
+ "[Logtail] #{String === msg ? msg : msg.inspect}\n"
23
+ end
24
+ end
25
+
26
+ DEVELOPMENT_NAME = "development".freeze
27
+ PRODUCTION_NAME = "production".freeze
28
+ STAGING_NAME = "staging".freeze
29
+ TEST_NAME = "test".freeze
30
+
31
+ include Singleton
32
+
33
+ attr_writer :http_body_limit
34
+
35
+ # Convenience method for logging debug statements to the debug logger
36
+ # set in this class.
37
+ # @private
38
+ def debug(&block)
39
+ debug_logger = Config.instance.debug_logger
40
+ if debug_logger
41
+ message = yield
42
+ debug_logger.debug(message)
43
+ end
44
+ true
45
+ end
46
+
47
+ # This is useful for debugging. This Sets a debug_logger to view internal Logtail library
48
+ # log messages. The default is `nil`. Meaning log to nothing.
49
+ #
50
+ # See {#debug_to_file!} and {#debug_to_stdout!} for convenience methods that handle creating
51
+ # and setting the logger.
52
+ #
53
+ # @example Rails
54
+ # config.logtail.debug_logger = ::Logger.new(STDOUT)
55
+ # @example Everything else
56
+ # Logtail::Config.instance.debug_logger = ::Logger.new(STDOUT)
57
+ def debug_logger=(value)
58
+ @debug_logger = value
59
+ end
60
+
61
+ # Accessor method for {#debug_logger=}.
62
+ def debug_logger
63
+ @debug_logger
64
+ end
65
+
66
+ # A convenience method for writing internal Logtail debug messages to a file.
67
+ #
68
+ # @example Rails
69
+ # config.Logtail.debug_to_file!("#{Rails.root}/log/logtail.log")
70
+ # @example Everything else
71
+ # Logtail::Config.instance.debug_to_file!("log/logtail.log")
72
+ def debug_to_file!(file_path)
73
+ FileUtils.mkdir_p( File.dirname(file_path) )
74
+ file = File.open(file_path, "ab")
75
+ file_logger = ::Logger.new(file)
76
+ file_logger.formatter = SimpleLogFormatter.new
77
+ self.debug_logger = file_logger
78
+ end
79
+
80
+ # A convenience method for writing internal Logtail debug messages to STDOUT.
81
+ #
82
+ # @example Rails
83
+ # config.logtail.debug_to_stdout!
84
+ # @example Everything else
85
+ # Logtail::Config.instance.debug_to_stdout!
86
+ def debug_to_stdout!
87
+ stdout_logger = ::Logger.new(STDOUT)
88
+ stdout_logger.formatter = SimpleLogFormatter.new
89
+ self.debug_logger = stdout_logger
90
+ end
91
+
92
+ # The environment your app is running in. Defaults to `RACK_ENV` and `RAILS_ENV`.
93
+ # It should be rare that you have to set this. If the aforementioned env vars are not
94
+ # set please do.
95
+ #
96
+ # @example If you do not set `RACK_ENV` or `RAILS_ENV`
97
+ # Logtail::Config.instance.environment = "staging"
98
+ def environment=(value)
99
+ @environment = value
100
+ end
101
+
102
+ # Accessor method for {#environment=}
103
+ def environment
104
+ @environment ||= ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
105
+ end
106
+
107
+ # Convenience method for accessing the various `Logtail::Integrations::*` class
108
+ # settings. These provides settings for enabling, disabled, and silencing integrations.
109
+ # See {Integrations} for a full list of available methods.
110
+ def integrations
111
+ Integrations
112
+ end
113
+
114
+ # This is the _main_ logger Logtail writes to. All of the Logtail integrations write to
115
+ # this logger instance. It should be set to your global logger. For Rails, this is set
116
+ # automatically to `Rails.logger`, you should not have to set this.
117
+ #
118
+ # @example Non-rails frameworks
119
+ # my_global_logger = Logtail::Logger.new(STDOUT)
120
+ # Logtail::Config.instance.logger = my_global_logger
121
+ def logger=(value)
122
+ @logger = value
123
+ end
124
+
125
+ # Accessor method for {#logger=}.
126
+ def logger
127
+ if @logger.is_a?(Proc)
128
+ @logger.call()
129
+ else
130
+ @logger ||= Logger.new(STDOUT)
131
+ end
132
+ end
133
+
134
+ # @private
135
+ def development?
136
+ environment == DEVELOPMENT_NAME
137
+ end
138
+
139
+ # @private
140
+ def test?
141
+ environment == TEST_NAME
142
+ end
143
+
144
+ # @private
145
+ def production?
146
+ environment == PRODUCTION_NAME
147
+ end
148
+
149
+ # @private
150
+ def staging?
151
+ environment == STAGING_NAME
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,17 @@
1
+ module Logtail
2
+ class Config
3
+ # Convenience module for accessing the various `Logtail::Integrations::*` classes
4
+ # through the {Logtail::Config} object. Logtail couples configuration with the class
5
+ # responsible for implementing it. This provides for a tighter design, but also
6
+ # requires the user to understand and access the various classes. This module aims
7
+ # to provide a simple ruby-like configuration interface for internal Logtail classes.
8
+ #
9
+ # For example:
10
+ #
11
+ # config = Logtail::Config.instance
12
+ # config.integrations.active_record.silence = true
13
+ module Integrations
14
+ extend self
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Logtail
2
+ # Base class for all `Logtail::Contexts::*` classes.
3
+ # @private
4
+ class Context
5
+ def to_hash
6
+ raise(NotImplementedError.new)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ require "logtail/contexts/http"
2
+ require "logtail/contexts/release"
3
+ require "logtail/contexts/runtime"
4
+ require "logtail/contexts/session"
5
+ require "logtail/contexts/system"
6
+ require "logtail/contexts/user"
7
+
8
+ module Logtail
9
+ # @private
10
+ module Contexts
11
+ end
12
+ end
@@ -0,0 +1,31 @@
1
+ require "logtail/context"
2
+
3
+ module Logtail
4
+ module Contexts
5
+ # @private
6
+ class HTTP < Context
7
+ attr_reader :host, :method, :path, :remote_addr, :request_id
8
+
9
+ def initialize(attributes)
10
+ @host = attributes[:host]
11
+ @method = attributes[:method]
12
+ @path = attributes[:path]
13
+ @remote_addr = attributes[:remote_addr]
14
+ @request_id = attributes[:request_id]
15
+ end
16
+
17
+ # Builds a hash representation containing simple objects, suitable for serialization (JSON).
18
+ def to_hash
19
+ @to_hash ||= {
20
+ http: Util::NonNilHashBuilder.build do |h|
21
+ h.add(:host, host)
22
+ h.add(:method, method)
23
+ h.add(:path, path)
24
+ h.add(:remote_addr, remote_addr)
25
+ h.add(:request_id, request_id)
26
+ end
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end