logtail 0.1.0 → 0.1.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1be8318ce433573150e153c4def0b1583a7ff79cb23e66160b3a07662fe40346
4
- data.tar.gz: 0ef24a7258f255d275ed26d18de1c1d31948c270eacbb93b85a1ff8fa1d635f9
3
+ metadata.gz: 9958b51a6821adf3e371746dde1dee7d661e4f9b40e492f69e249ed85d22ca80
4
+ data.tar.gz: 2b9db72344079fe012179853c192385a51419f082d44f1883b35401aba6bc869
5
5
  SHA512:
6
- metadata.gz: 856d92f191144d4b827b5088fedc0963ef1a96a7e4ec8631e5883cb275ad9b7f5928dcfff0c2047dafb75ee0d8a40d798750457bbc3028b148fb3b6c1cf15320
7
- data.tar.gz: 0ab7a3f459aaaffe7494e626f535190f2bb643b458b17cd9209ba4b399f6daf336fb8707851272c5b928050b573b5d29db61c4cf020ed3997ead233ca865b193
6
+ metadata.gz: 9826d94548c436782434831a5161267a6b1d0e8814441cde1b2cb51f53257d015b42c6ff7dabc422b42bd045432d34d8bf241e4305cad4a59d5cc0ec156a8f6d
7
+ data.tar.gz: f71fb5e1af5d3cb71b50880c120f91e1eb573bd72fe671a32ec96b90a093b0986433db76a7c0f892051a1a1131c15a432018247a90d836a255ffaf162a98f130
data/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [1.0.0] - 2021-02-11
10
+ ## [0.1.0] - 2021-02-11
11
11
 
12
12
  - The first version of the client.
13
+
14
+ ## [0.1.2] - 2021-04-22
15
+
16
+ - Fixed string encoding.
17
+
18
+ ## [0.1.3] - 2021-06-11
19
+
20
+ - Fixed detection of the frame that calls the logger.
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ def puts_with_level(message, level = :info)
14
14
  end
15
15
  end
16
16
 
17
- task :test_the_pipes, [:api_key] do |t, args|
17
+ task :test_the_pipes, [:source_token] do |t, args|
18
18
  support_email = "support@logtail.com"
19
19
  # Do not modify below this line. It's important to keep the `Logtail::Logger`
20
20
  # because it provides an API for logging structured data and capturing context.
@@ -26,7 +26,7 @@ task :test_the_pipes, [:api_key] do |t, args|
26
26
 
27
27
  current_context = Logtail::CurrentContext.instance.snapshot
28
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)
29
+ http_device = Logtail::LogDevices::HTTP.new(args.source_token, flush_continuously: false)
30
30
  response = http_device.deliver_one(entry)
31
31
  if response.is_a?(Exception)
32
32
  message = <<~HEREDOC
@@ -36,7 +36,7 @@ module Logtail
36
36
  # you can drop the log messages instead by passing a {DroppingSizedQueue} via the
37
37
  # `:request_queue` option.
38
38
  #
39
- # @param api_key [String] The API key provided to you after you add your application to
39
+ # @param source_token [String] The API key provided to you after you add your application to
40
40
  # [Logtail](https://logtail.com).
41
41
  # @param [Hash] options the options to create a HTTP log device with.
42
42
  # @option attributes [Symbol] :batch_size (1000) Determines the maximum of log lines in
@@ -64,13 +64,13 @@ module Logtail
64
64
  # The default is set via {LOGTAIL_HOST}.
65
65
  #
66
66
  # @example Basic usage
67
- # Logtail::Logger.new(Logtail::LogDevices::HTTP.new("my_logtail_api_key"))
67
+ # Logtail::Logger.new(Logtail::LogDevices::HTTP.new("my_logtail_source_token"))
68
68
  #
69
69
  # @example Apply back pressure instead of dropping messages
70
- # http_log_device = Logtail::LogDevices::HTTP.new("my_logtail_api_key", request_queue: SizedQueue.new(25))
70
+ # http_log_device = Logtail::LogDevices::HTTP.new("my_logtail_source_token", request_queue: SizedQueue.new(25))
71
71
  # Logtail::Logger.new(http_log_device)
72
- def initialize(api_key, options = {})
73
- @api_key = api_key || raise(ArgumentError.new("The api_key parameter cannot be blank"))
72
+ def initialize(source_token, options = {})
73
+ @source_token = source_token || raise(ArgumentError.new("The source_token parameter cannot be blank"))
74
74
  @logtail_host = options[:logtail_host] || ENV['LOGTAIL_HOST'] || LOGTAIL_HOST
75
75
  @logtail_port = options[:logtail_port] || ENV['LOGTAIL_PORT'] || LOGTAIL_PORT
76
76
  @logtail_scheme = options[:logtail_scheme] || ENV['LOGTAIL_SCHEME'] || LOGTAIL_SCHEME
@@ -168,7 +168,7 @@ MESSAGE
168
168
  end
169
169
 
170
170
  raise <<-MESSAGE
171
-
171
+
172
172
  Log delivery failed! No request was made.
173
173
 
174
174
  You can enable internal debug logging with the following:
@@ -201,10 +201,20 @@ MESSAGE
201
201
  req['Authorization'] = authorization_payload
202
202
  req['Content-Type'] = CONTENT_TYPE
203
203
  req['User-Agent'] = USER_AGENT
204
- req.body = msgs.to_msgpack
204
+ req.body = msgs.map { |msg| force_utf8_encoding(msg.to_hash) }.to_msgpack
205
205
  req
206
206
  end
207
207
 
208
+ def force_utf8_encoding(data)
209
+ if data.respond_to?(:force_encoding)
210
+ data.dup.force_encoding('UTF-8')
211
+ elsif data.respond_to?(:transform_values)
212
+ data.transform_values { |val| force_utf8_encoding(val) }
213
+ else
214
+ data
215
+ end
216
+ end
217
+
208
218
  # Flushes the message buffer asynchronously. The reason we provide this
209
219
  # method is because the message buffer limit is constricted by the
210
220
  # Logtail API. The application limit is multiples of the buffer limit,
@@ -361,7 +371,7 @@ MESSAGE
361
371
 
362
372
  # Builds the `Authorization` header value for HTTP delivery to the Logtail API.
363
373
  def authorization_payload
364
- "Bearer #{@api_key}"
374
+ "Bearer #{@source_token}"
365
375
  end
366
376
  end
367
377
  end
@@ -1,5 +1,6 @@
1
1
  require "socket"
2
2
  require "time"
3
+ require "pathname"
3
4
 
4
5
  require "logtail/contexts"
5
6
  require "logtail/events"
@@ -11,6 +12,7 @@ module Logtail
11
12
  BINARY_LIMIT_THRESHOLD = 1_000.freeze
12
13
  DT_PRECISION = 6.freeze
13
14
  MESSAGE_MAX_BYTES = 8192.freeze
15
+ LOGGER_FILE = '/logtail/logger.rb'.freeze
14
16
 
15
17
  attr_reader :context_snapshot, :event, :level, :message, :progname, :tags, :time
16
18
 
@@ -37,6 +39,7 @@ module Logtail
37
39
  @tags = options[:tags]
38
40
  @context_snapshot = context_snapshot
39
41
  @event = event
42
+ @runtime_context = current_runtime_context || {}
40
43
  end
41
44
 
42
45
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
@@ -45,7 +48,7 @@ module Logtail
45
48
  hash = {
46
49
  :level => level,
47
50
  :dt => formatted_dt,
48
- :message => message
51
+ :message => message,
49
52
  }
50
53
 
51
54
  if !tags.nil? && tags.length > 0
@@ -60,6 +63,10 @@ module Logtail
60
63
  hash[:context] = context_snapshot
61
64
  end
62
65
 
66
+ hash[:context] ||= {}
67
+ hash[:context][:runtime] ||= {}
68
+ hash[:context][:runtime].merge!(@runtime_context)
69
+
63
70
  if options[:only]
64
71
  hash.select do |key, _value|
65
72
  options[:only].include?(key)
@@ -106,5 +113,42 @@ module Logtail
106
113
  rescue Exception
107
114
  nil
108
115
  end
116
+
117
+ def current_runtime_context
118
+ last_logger_invocation_index = caller_locations.rindex { |frame| logtail_logger_frame?(frame) }
119
+ return {} if last_logger_invocation_index.nil?
120
+
121
+ calling_frame_index = last_logger_invocation_index + 1
122
+ frame = caller_locations[calling_frame_index]
123
+
124
+ return convert_to_runtime_context(frame)
125
+ end
126
+
127
+ def convert_to_runtime_context(frame)
128
+ {
129
+ file: path_relative_to_app_root(frame),
130
+ line: frame.lineno,
131
+ frame_label: frame.label,
132
+ }
133
+ end
134
+
135
+ def logtail_logger_frame?(frame)
136
+ !frame.absolute_path.nil? && frame.absolute_path.end_with?(LOGGER_FILE)
137
+ end
138
+
139
+ def path_relative_to_app_root(frame)
140
+ Pathname.new(frame.absolute_path).relative_path_from(root_path).to_s
141
+ end
142
+
143
+ def root_path
144
+ if Object.const_defined?('Rails')
145
+ Rails.root
146
+ elsif Object.const_defined?('Rack::Directory')
147
+ Pathname.new(Rack::Directory.new('').root)
148
+ else
149
+ base_file = caller_locations.last.absolute_path
150
+ Pathname.new(File.dirname(base_file || '/'))
151
+ end
152
+ end
109
153
  end
110
154
  end
@@ -34,14 +34,13 @@ module Logtail
34
34
  def build_log_entry(severity, time, progname, logged_obj)
35
35
  context_snapshot = CurrentContext.instance.snapshot
36
36
  level = SEVERITY_MAP.fetch(severity)
37
- tags = extract_active_support_tagged_logging_tags
37
+ tags = extract_active_support_tagged_logging_tags.clone
38
38
 
39
39
  if logged_obj.is_a?(Event)
40
40
  LogEntry.new(level, time, progname, logged_obj.message, context_snapshot, logged_obj,
41
41
  tags: tags)
42
42
  elsif logged_obj.is_a?(Hash)
43
43
  # Extract the tags
44
- tags = tags.clone
45
44
  tags.push(logged_obj.delete(:tag)) if logged_obj.key?(:tag)
46
45
  tags.concat(logged_obj.delete(:tags)) if logged_obj.key?(:tags)
47
46
  tags.uniq!
@@ -56,7 +55,8 @@ module Logtail
56
55
 
57
56
  # Because of all the crazy ways Rails has attempted tags, we need this crazy method.
58
57
  def extract_active_support_tagged_logging_tags
59
- Thread.current[:activesupport_tagged_logging_tags] ||
58
+ @current_tags ||
59
+ Thread.current[:activesupport_tagged_logging_tags] ||
60
60
  Thread.current[tagged_logging_object_key_name] ||
61
61
  EMPTY_ARRAY
62
62
  end
@@ -135,7 +135,7 @@ module Logtail
135
135
  # logger = Logtail::Logger.new(STDOUT)
136
136
  #
137
137
  # @example Logging to the Logtail HTTP device
138
- # http_device = Logtail::LogDevices::HTTP.new("my-logtail-api-key")
138
+ # http_device = Logtail::LogDevices::HTTP.new("my-logtail-source-token")
139
139
  # logger = Logtail::Logger.new(http_device)
140
140
  #
141
141
  # @example Logging to a file (with rotation)
@@ -143,7 +143,7 @@ module Logtail
143
143
  # logger = Logtail::Logger.new(file_device)
144
144
  #
145
145
  # @example Logging to a file and the Logtail HTTP device (multiple log devices)
146
- # http_device = Logtail::LogDevices::HTTP.new("my-logtail-api-key")
146
+ # http_device = Logtail::LogDevices::HTTP.new("my-logtail-source-token")
147
147
  # file_logger = ::Logger.new("path/to/file.log")
148
148
  # logger = Logtail::Logger.new(http_device, file_logger)
149
149
  def initialize(*io_devices_and_loggers)
@@ -1,3 +1,3 @@
1
1
  module Logtail
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -102,7 +102,7 @@ describe Logtail::LogDevices::HTTP do
102
102
  request_queue = http.instance_variable_get(:@request_queue)
103
103
  request_attempt = request_queue.deq
104
104
  expect(request_attempt.request).to be_kind_of(Net::HTTP::Post)
105
- expect(request_attempt.request.body).to start_with("\x92\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT"))
105
+ expect(request_attempt.request.body).to start_with("\x92\x84\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT"))
106
106
 
107
107
  message_queue = http.instance_variable_get(:@msg_queue)
108
108
  expect(message_queue.size).to eq(0)
@@ -127,7 +127,7 @@ describe Logtail::LogDevices::HTTP do
127
127
  it "should deliver requests on an interval" do
128
128
  stub = stub_request(:post, "https://in.logtail.com/").
129
129
  with(
130
- :body => start_with("\x92\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT")),
130
+ :body => start_with("\x92\x84\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT")),
131
131
  :headers => {
132
132
  'Authorization' => 'Bearer MYKEY',
133
133
  'Content-Type' => 'application/msgpack',
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Logtail::LogEntry do
4
- let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
4
+ let(:time) { Time.utc(2021, 06, 11, 12, 0, 0) }
5
5
 
6
6
  describe "#to_msgpack" do
7
7
  it "should encode properly with an event and context" do
@@ -16,7 +16,20 @@ describe Logtail::LogEntry do
16
16
  context = {custom: {a: "b"}}
17
17
  log_entry = described_class.new("INFO", time, nil, "log message", context, event)
18
18
  msgpack = log_entry.to_msgpack
19
- expect(msgpack).to start_with("\x85\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z".force_encoding("ASCII-8BIT"))
19
+ expect(msgpack).to start_with("\x85\xA5level\xA4INFO\xA2dt\xBB2021-06-11T12:00:00.000000Z".force_encoding("ASCII-8BIT"))
20
+ end
21
+ end
22
+
23
+ describe "#to_hash" do
24
+ it "should include runtime context information" do
25
+ log_entry = Logtail::Logger::PassThroughFormatter.new.call("INFO", time, "", "log message")
26
+
27
+ hash = log_entry.to_hash
28
+ expect(hash[:context]).to_not be_nil
29
+ expect(hash[:context][:runtime]).to_not be_nil
30
+ expect(hash[:context][:runtime][:file]).to end_with('/spec/logtail/log_entry_spec.rb')
31
+ expect(hash[:context][:runtime][:line]).to be(25)
32
+ expect(hash[:context][:runtime][:frame_label]).to_not be_nil
20
33
  end
21
34
  end
22
35
  end
@@ -161,7 +161,7 @@ describe Logtail::Logger do
161
161
  end
162
162
 
163
163
  context "with the HTTP log device" do
164
- let(:io) { Logtail::LogDevices::HTTP.new("my_key") }
164
+ let(:io) { Logtail::LogDevices::HTTP.new("my_source_token") }
165
165
 
166
166
  it "should use the PassThroughFormatter" do
167
167
  expect(logger.formatter).to be_kind_of(Logtail::Logger::PassThroughFormatter)
@@ -189,7 +189,7 @@ describe Logtail::Logger do
189
189
 
190
190
  describe "#formatter=" do
191
191
  it "should not allow changing the formatter when the device is HTTP" do
192
- http_device = Logtail::LogDevices::HTTP.new("api_key")
192
+ http_device = Logtail::LogDevices::HTTP.new("source_token")
193
193
  logger = Logtail::Logger.new(http_device)
194
194
  expect { logger.formatter = ::Logger::Formatter.new }.to raise_error(ArgumentError)
195
195
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logtail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Logtail
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-26 00:00:00.000000000 Z
11
+ date: 2021-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack