semlogr 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +7 -0
  4. data/Gemfile +2 -0
  5. data/README.md +1 -1
  6. data/Rakefile +2 -0
  7. data/bin/console +1 -0
  8. data/lib/semlogr/component_registry.rb +2 -0
  9. data/lib/semlogr/config/logger_config.rb +10 -1
  10. data/lib/semlogr/config/sink_config.rb +10 -1
  11. data/lib/semlogr/enrichers/event_type.rb +2 -0
  12. data/lib/semlogr/enrichers/host.rb +2 -0
  13. data/lib/semlogr/enrichers/log_context.rb +2 -0
  14. data/lib/semlogr/enrichers/property.rb +2 -0
  15. data/lib/semlogr/enrichers/thread.rb +2 -0
  16. data/lib/semlogr/error.rb +2 -0
  17. data/lib/semlogr/events/log_event.rb +3 -1
  18. data/lib/semlogr/formatters/json_formatter.rb +2 -0
  19. data/lib/semlogr/formatters/property_value_formatter.rb +13 -12
  20. data/lib/semlogr/formatters/text_formatter.rb +4 -2
  21. data/lib/semlogr/log_context.rb +2 -0
  22. data/lib/semlogr/log_severity.rb +2 -0
  23. data/lib/semlogr/logger.rb +2 -0
  24. data/lib/semlogr/null_logger.rb +2 -0
  25. data/lib/semlogr/properties/output_properties.rb +2 -0
  26. data/lib/semlogr/self_logger.rb +56 -0
  27. data/lib/semlogr/sinks/aggregate.rb +9 -1
  28. data/lib/semlogr/sinks/batching.rb +86 -0
  29. data/lib/semlogr/sinks/colored_console.rb +4 -2
  30. data/lib/semlogr/sinks/console.rb +2 -0
  31. data/lib/semlogr/sinks/enriching.rb +12 -1
  32. data/lib/semlogr/sinks/file.rb +2 -0
  33. data/lib/semlogr/sinks/filtering.rb +13 -1
  34. data/lib/semlogr/templates/parser.rb +19 -5
  35. data/lib/semlogr/templates/property_token.rb +32 -11
  36. data/lib/semlogr/templates/template.rb +2 -0
  37. data/lib/semlogr/templates/template_cache.rb +2 -0
  38. data/lib/semlogr/templates/text_token.rb +2 -0
  39. data/lib/semlogr/utils/bounded_queue.rb +41 -0
  40. data/lib/semlogr/version.rb +3 -1
  41. data/lib/semlogr.rb +6 -2
  42. data/samples/basic.rb +2 -0
  43. data/samples/context.rb +2 -0
  44. data/samples/enrichment.rb +2 -0
  45. data/samples/filtering.rb +2 -0
  46. data/samples/log_context.rb +2 -0
  47. data/samples/self_logger.rb +27 -0
  48. data/samples/sinks.rb +2 -0
  49. data/semlogr.gemspec +3 -0
  50. metadata +20 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1d29805d04a2f5a85ceb89d32355a7e14281fe0a91a29d51dea9598f2a62813
4
- data.tar.gz: af62ca05343ca10d137b0f9b5f3b0d634e9dd2f316857f8f0872732195b76543
3
+ metadata.gz: 72f3e3b75d0a87bbfaba4006891ae607b6ede75a90eb6abd1df4c63e61154197
4
+ data.tar.gz: c94f6f1201b911f7a4882d41b71b557ae5a9d3bafc6e37328b57bf35b6fd3181
5
5
  SHA512:
6
- metadata.gz: 4d2338b9270cd216909a23f16a8771e152230fd9b68fa492543de01ccc14a5871649bcba3abfca882deca43fb867e85d1f8ed6b7c34920539ce78fbbda969436
7
- data.tar.gz: ef241d71fa093dd5321be58abf8843e404e16409b506a9068ac9910bc27178a10c355e33a4c810f85176a365ddfdcb3a7e8971bd1ab8163f827a4c39fea0b8c5
6
+ metadata.gz: 37d8e017fb82402551aa2a43440b6eea26bc7127cf452a815762911d84def00640be109774d8032571e9f6d3dce433448235e7f884cc7cff823efbfe2ade9d41
7
+ data.tar.gz: f570b3e43ae0e442c3723ab73fbf36d6915509cb87e9478303502b04f3bc9b99840a98bc3df815c3db189bee9767a108af0cb16e5c9bf6fd87e76e72f369f48d
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.2
2
+ TargetRubyVersion: 2.3
3
3
 
4
4
  Documentation:
5
5
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ### 0.3.1
4
+
5
+ * Added batching sink providing ability to create sinks that reliably emit batches of events.
6
+ * Added initial version of property formatting, allowing standard Ruby format strings to be used to format property values.
7
+ * Added self logger to provide internal diagnostic information.
8
+ * Made it so that errors raised by sinks/enrichers/filters will not to crash the entire application. Instead they are swallowed and the error is written to the self log.
9
+
3
10
  ### 0.3.0
4
11
 
5
12
  * Moved LogContext
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
data/README.md CHANGED
@@ -23,7 +23,7 @@ then:
23
23
  Create an instance of the logger configuring one or more sinks.
24
24
 
25
25
  ```ruby
26
- require "semlogr"
26
+ require 'semlogr'
27
27
 
28
28
  Semlogr.logger = Semlogr.create_logger do |c|
29
29
  c.log_at :info
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
  require 'rubocop/rake_task'
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bundler/setup'
4
5
  require 'semlogr'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/error'
2
4
 
3
5
  module Semlogr
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/component_registry'
2
4
  require 'semlogr/sinks/aggregate'
3
5
  require 'semlogr/sinks/enriching'
@@ -25,7 +27,7 @@ module Semlogr
25
27
  end
26
28
 
27
29
  def enrich_with(enricher, *params)
28
- enricher = ComponentRegistry.resolve(:enricher, enricher, *params) if enricher.is_a?(Symbol)
30
+ enricher = resolve_enricher(enricher, params)
29
31
  @enrichers << enricher
30
32
  end
31
33
 
@@ -43,6 +45,13 @@ module Semlogr
43
45
 
44
46
  Logger.new(@min_severity, sink)
45
47
  end
48
+
49
+ private
50
+
51
+ def resolve_enricher(enricher, params)
52
+ return enricher unless enricher.is_a?(Symbol)
53
+ ComponentRegistry.resolve(:enricher, enricher, *params)
54
+ end
46
55
  end
47
56
  end
48
57
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/component_registry'
2
4
  require 'semlogr/sinks/enriching'
3
5
  require 'semlogr/sinks/filtering'
@@ -21,7 +23,7 @@ module Semlogr
21
23
  end
22
24
 
23
25
  def enrich_with(enricher, *params)
24
- enricher = ComponentRegistry.resolve(:enricher, enricher, *params) if enricher.is_a?(Symbol)
26
+ enricher = resolve_enricher(enricher, params)
25
27
  @enrichers << enricher
26
28
  end
27
29
 
@@ -39,6 +41,13 @@ module Semlogr
39
41
  sink = Sinks::Enriching.new(@enrichers, sink) if @enrichers.any?
40
42
  sink
41
43
  end
44
+
45
+ private
46
+
47
+ def resolve_enricher(enricher, params)
48
+ return enricher unless enricher.is_a?(Symbol)
49
+ ComponentRegistry.resolve(:enricher, enricher, *params)
50
+ end
42
51
  end
43
52
  end
44
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/component_registry'
2
4
 
3
5
  module Semlogr
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'socket'
2
4
  require 'semlogr/component_registry'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/log_context'
2
4
 
3
5
  module Semlogr
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/component_registry'
2
4
 
3
5
  module Semlogr
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/component_registry'
2
4
 
3
5
  module Semlogr
data/lib/semlogr/error.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Semlogr
2
4
  class Error < StandardError
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest/xxhash'
2
4
  require 'semlogr/templates/parser'
3
5
 
@@ -45,7 +47,7 @@ module Semlogr
45
47
  end
46
48
 
47
49
  def to_s
48
- output = ''
50
+ output = +''
49
51
 
50
52
  render(output)
51
53
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'oj'
2
4
 
3
5
  module Semlogr
@@ -1,26 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Semlogr
2
4
  module Formatters
3
5
  class PropertyValueFormatter
4
- QUOTE = '"'.freeze
6
+ NO_VALUE = '(nil)'
7
+ NEW_LINE = "\n"
5
8
 
6
- def self.format(output, property_value)
7
- case property_value
9
+ def self.format(value)
10
+ case value
8
11
  when nil
9
- output << '(nil)'.freeze
12
+ NO_VALUE
10
13
  when String
11
- output << QUOTE
12
- output << property_value
13
- output << QUOTE
14
+ "\"#{value}\""
14
15
  when StandardError
15
- output << "#{property_value.class}: #{property_value.message}"
16
+ formatted_error = +"#{value.class}: #{value.message}"
16
17
 
17
- if property_value.backtrace
18
- output << "\n\s\s#{property_value.backtrace.join("\n\s\s")}"
18
+ if value.backtrace&.any?
19
+ formatted_error << "\n\s\s#{value.backtrace.join("\n\s\s")}"
19
20
  end
20
21
 
21
- output << "\n"
22
+ formatted_error << NEW_LINE
22
23
  else
23
- output << property_value.to_s
24
+ value.to_s
24
25
  end
25
26
  end
26
27
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/templates/parser'
2
4
  require 'semlogr/properties/output_properties'
3
5
  require 'semlogr/templates/property_token'
@@ -5,14 +7,14 @@ require 'semlogr/templates/property_token'
5
7
  module Semlogr
6
8
  module Formatters
7
9
  class TextFormatter
8
- DEFAULT_TEMPLATE = "[{timestamp}] {severity}: {message}\n{error}".freeze
10
+ DEFAULT_TEMPLATE = "[{timestamp}] {severity}: {message}\n{error}"
9
11
 
10
12
  def initialize(template: DEFAULT_TEMPLATE)
11
13
  @template = Templates::Parser.parse(template)
12
14
  end
13
15
 
14
16
  def format(log_event)
15
- output = ''
17
+ output = +''
16
18
  output_properties = Properties::OutputProperties.create(log_event)
17
19
 
18
20
  @template.tokens.each do |token|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Semlogr
2
4
  class LogContext
3
5
  def self.current
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Semlogr
2
4
  class LogSeverity
3
5
  include Comparable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/config/logger_config'
2
4
  require 'semlogr/log_severity'
3
5
  require 'semlogr/events/log_event'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Semlogr
2
4
  class NullLogger
3
5
  def debug?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Semlogr
2
4
  module Properties
3
5
  class OutputProperties
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Semlogr
4
+ class SelfLogger
5
+ def self.debug(message, error = nil)
6
+ log(:debug, message, error)
7
+ end
8
+
9
+ def self.info(message, error = nil)
10
+ log(:info, message, error)
11
+ end
12
+
13
+ def self.warn(message, error = nil)
14
+ log(:warn, message, error)
15
+ end
16
+
17
+ def self.error(message, error = nil)
18
+ log(:error, message, error)
19
+ end
20
+
21
+ def self.fatal(message, error = nil)
22
+ log(:fatal, message, error)
23
+ end
24
+
25
+ class << self
26
+ attr_accessor :logger
27
+
28
+ private
29
+
30
+ def log(severity, message, error)
31
+ return unless logger
32
+
33
+ logger << format_message(severity, message, error)
34
+ end
35
+
36
+ def format_message(severity, message, error)
37
+ formatted = +"[#{Time.now.iso8601(3)}] #{severity.upcase}: #{message}"
38
+
39
+ if error
40
+ case error
41
+ when StandardError
42
+ formatted << "\n#{error.class}: #{error.message}"
43
+
44
+ if error.backtrace&.any?
45
+ formatted << "\n\s\s#{error.backtrace.join("\n\s\s")}"
46
+ end
47
+ else
48
+ formatted << "\n#{error}"
49
+ end
50
+ end
51
+
52
+ formatted << "\n"
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'semlogr/self_logger'
4
+
1
5
  module Semlogr
2
6
  module Sinks
3
7
  class Aggregate
@@ -7,7 +11,11 @@ module Semlogr
7
11
 
8
12
  def emit(log_event)
9
13
  @sinks.each do |sink|
10
- sink.emit(log_event)
14
+ begin
15
+ sink.emit(log_event)
16
+ rescue StandardError => e
17
+ SelfLogger.error("Failed to emit log event to sink #{sink.class}", e)
18
+ end
11
19
  end
12
20
  end
13
21
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'semlogr/utils/bounded_queue'
4
+ require 'timeout'
5
+
6
+ module Semlogr
7
+ module Sinks
8
+ class Batching
9
+ MAX_FLUSH_ATTEMPTS = 6
10
+
11
+ def initialize(opts = {})
12
+ @flush_interval = opts[:flush_interval] || 3
13
+ @final_flush_timeout = opts[:final_flush_timeout] || 60
14
+ @batch_size = opts[:batch_size] || 1_000
15
+ @queue_max_size = opts[:queue_max_size] || 100_000
16
+ @queue = Utils::BoundedQueue.new(@queue_max_size)
17
+ @flush_mutex = Mutex.new
18
+ @running = false
19
+
20
+ start_flush_thread
21
+
22
+ at_exit { stop_flush_thread }
23
+ end
24
+
25
+ def emit(log_event)
26
+ return unless @running
27
+
28
+ @queue.push(log_event)
29
+ end
30
+
31
+ private
32
+
33
+ def flush
34
+ @flush_mutex.synchronize do
35
+ loop do
36
+ log_events = @queue.pop_count(@batch_size)
37
+ success = emit_batch_with_retries(log_events)
38
+
39
+ break unless success
40
+ break if log_events.empty? || log_events.size < @batch_size
41
+ end
42
+ end
43
+ end
44
+
45
+ def emit_batch_with_retries(log_events)
46
+ return true if log_events.empty?
47
+
48
+ flush_attempts = 0
49
+
50
+ begin
51
+ emit_batch(log_events)
52
+ rescue StandardError
53
+ flush_attempts += 1
54
+
55
+ if flush_attempts <= MAX_FLUSH_ATTEMPTS
56
+ sleep 2**flush_attempts
57
+ retry
58
+ end
59
+
60
+ return false
61
+ end
62
+
63
+ true
64
+ end
65
+
66
+ def start_flush_thread
67
+ @running = true
68
+
69
+ Thread.new do
70
+ loop do
71
+ break unless @running
72
+
73
+ sleep @flush_interval
74
+ flush
75
+ end
76
+ end
77
+ end
78
+
79
+ def stop_flush_thread
80
+ @running = false
81
+
82
+ Timeout.timeout(@final_flush_timeout) { flush }
83
+ end
84
+ end
85
+ end
86
+ end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/sinks/console'
2
4
  require 'semlogr/properties/output_properties'
3
5
 
4
6
  module Semlogr
5
7
  module Sinks
6
8
  class ColoredConsole
7
- DEFAULT_TEMPLATE = "[{timestamp}] {severity}: {message}\n{error}".freeze
9
+ DEFAULT_TEMPLATE = "[{timestamp}] {severity}: {message}\n{error}"
8
10
 
9
11
  LOG_SEVERITY_COLORS = {
10
12
  LogSeverity::DEBUG => :white,
@@ -26,7 +28,7 @@ module Semlogr
26
28
  end
27
29
 
28
30
  def emit(log_event)
29
- output = ''
31
+ output = +''
30
32
  output_properties = Properties::OutputProperties.create(log_event)
31
33
 
32
34
  @template.tokens.each do |token|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/formatters/text_formatter'
2
4
 
3
5
  module Semlogr
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'semlogr/self_logger'
4
+
1
5
  module Semlogr
2
6
  module Sinks
3
7
  class Enriching
@@ -7,7 +11,14 @@ module Semlogr
7
11
  end
8
12
 
9
13
  def emit(log_event)
10
- @enrichers.each { |enricher| enricher.enrich(log_event) }
14
+ @enrichers.each do |enricher|
15
+ begin
16
+ enricher.enrich(log_event)
17
+ rescue StandardError => e
18
+ SelfLogger.error("Failed to enrich log event using enricher #{enricher.class}", e)
19
+ end
20
+ end
21
+
11
22
  @sink.emit(log_event)
12
23
  end
13
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
  require 'semlogr/formatters/text_formatter'
3
5
 
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'semlogr/self_logger'
4
+
1
5
  module Semlogr
2
6
  module Sinks
3
7
  class Filtering
@@ -7,7 +11,15 @@ module Semlogr
7
11
  end
8
12
 
9
13
  def emit(log_event)
10
- filtered = @filters.any? { |filter| filter.call(log_event) }
14
+ filtered = @filters.any? do |filter|
15
+ begin
16
+ filter.call(log_event)
17
+ rescue StandardError => e
18
+ SelfLogger.error("Failed to filter log event using filter #{filter.class}", e)
19
+
20
+ false
21
+ end
22
+ end
11
23
 
12
24
  @sink.emit(log_event) unless filtered
13
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/templates/template'
2
4
  require 'semlogr/templates/text_token'
3
5
  require 'semlogr/templates/property_token'
@@ -8,8 +10,9 @@ module Semlogr
8
10
  class Parser
9
11
  @template_cache = TemplateCache.new(1000)
10
12
 
11
- PROPERTY_TOKEN_START = '{'.freeze
12
- PROPERTY_TOKEN_END = '}'.freeze
13
+ PROPERTY_TOKEN_START = '{'
14
+ PROPERTY_TOKEN_END = '}'
15
+ FILTER_TOKEN_START = ':'
13
16
 
14
17
  def self.parse(template)
15
18
  return Template::EMPTY unless template && !template.empty?
@@ -54,14 +57,25 @@ module Semlogr
54
57
 
55
58
  token = nil
56
59
  pos = start
60
+ filter_start = nil
57
61
 
58
62
  while pos < template.size
59
- if template[pos] == PROPERTY_TOKEN_END
63
+ case template[pos]
64
+ when PROPERTY_TOKEN_END
60
65
  raw_text = template[start..pos]
61
- property_name = raw_text[1..-2]
62
- token = PropertyToken.new(raw_text, property_name.to_sym)
66
+ filter = nil
67
+
68
+ if filter_start.nil?
69
+ property_name = template[start + 1..pos - 1]
70
+ else
71
+ property_name = template[start + 1..filter_start - 1]
72
+ filter = template[filter_start + 1..pos - 1]
73
+ end
63
74
 
75
+ token = PropertyToken.new(raw_text, property_name.to_sym, filter)
64
76
  return [token, pos + 1]
77
+ when FILTER_TOKEN_START
78
+ filter_start ||= pos
65
79
  end
66
80
 
67
81
  pos += 1
@@ -1,30 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/formatters/property_value_formatter'
4
+ require 'semlogr/self_logger'
2
5
 
3
6
  module Semlogr
4
7
  module Templates
5
8
  class PropertyToken
6
- attr_accessor :property_name
9
+ attr_reader :raw_text, :property_name, :format_string
7
10
 
8
- def initialize(raw_text, property_name)
11
+ def initialize(raw_text, property_name, format = nil)
9
12
  @raw_text = raw_text
10
13
  @property_name = property_name
14
+ @format_string = format ? "%#{format}" : nil
11
15
  end
12
16
 
13
17
  def render(output, properties)
14
- if properties.key?(@property_name)
15
- property_value = properties[@property_name]
16
-
17
- Formatters::PropertyValueFormatter.format(output, property_value)
18
- else
19
- output << @raw_text
20
- end
18
+ output <<
19
+ if properties.key?(property_name)
20
+ format_property_value(properties[property_name])
21
+ else
22
+ raw_text
23
+ end
24
+ rescue StandardError => e
25
+ SelfLogger.error("Failed to render property token: #{property_name}", e)
26
+
27
+ output << raw_text
21
28
  end
22
29
 
23
30
  def ==(other)
24
31
  return false unless other
32
+ return false unless other.respond_to?(:raw_text)
25
33
  return false unless other.respond_to?(:property_name)
34
+ return false unless other.respond_to?(:format_string)
26
35
 
27
- @property_name == other.property_name
36
+ raw_text == other.raw_text && \
37
+ property_name == other.property_name && \
38
+ format_string == other.format_string
28
39
  end
29
40
 
30
41
  def eql?(other)
@@ -32,7 +43,17 @@ module Semlogr
32
43
  end
33
44
 
34
45
  def hash
35
- @property_name.hash
46
+ [raw_text, property_name, format_string].hash
47
+ end
48
+
49
+ private
50
+
51
+ def format_property_value(property_value)
52
+ if format_string
53
+ format(format_string, property_value)
54
+ else
55
+ Formatters::PropertyValueFormatter.format(property_value)
56
+ end
36
57
  end
37
58
  end
38
59
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'semlogr/templates/text_token'
2
4
 
3
5
  module Semlogr
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'lru_redux'
2
4
 
3
5
  module Semlogr
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Semlogr
2
4
  module Templates
3
5
  class TextToken
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+
5
+ module Semlogr
6
+ module Utils
7
+ class BoundedQueue
8
+ def initialize(max_size)
9
+ @max_size = max_size
10
+ @queue = Queue.new
11
+ @queue_mutex = Mutex.new
12
+ end
13
+
14
+ def size
15
+ @queue.size
16
+ end
17
+
18
+ def push(item)
19
+ @queue_mutex.synchronize do
20
+ return if size >= @max_size
21
+
22
+ @queue << item
23
+ end
24
+ end
25
+
26
+ def pop
27
+ @queue.pop
28
+ end
29
+
30
+ def pop_count(count)
31
+ items = []
32
+
33
+ @queue_mutex.synchronize do
34
+ items << @queue.pop until @queue.empty? || items.size == count
35
+ end
36
+
37
+ items
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Semlogr
2
- VERSION = '0.3.0'.freeze
4
+ VERSION = '0.3.1'
3
5
  end
data/lib/semlogr.rb CHANGED
@@ -1,7 +1,10 @@
1
- require 'semlogr/version'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'semlogr/logger'
3
- require 'semlogr/null_logger'
4
4
  require 'semlogr/log_context'
5
+ require 'semlogr/null_logger'
6
+ require 'semlogr/self_logger'
7
+ require 'semlogr/version'
5
8
 
6
9
  # Built-in enrichers
7
10
  require 'semlogr/enrichers/event_type'
@@ -11,6 +14,7 @@ require 'semlogr/enrichers/property'
11
14
  require 'semlogr/enrichers/thread'
12
15
 
13
16
  # Built-in sinks
17
+ require 'semlogr/sinks/batching'
14
18
  require 'semlogr/sinks/console'
15
19
  require 'semlogr/sinks/colored_console'
16
20
  require 'semlogr/sinks/file'
data/samples/basic.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/setup'
2
4
  require 'semlogr'
3
5
 
data/samples/context.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/setup'
2
4
  require 'semlogr'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/setup'
2
4
  require 'semlogr'
3
5
 
data/samples/filtering.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/setup'
2
4
  require 'semlogr'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/setup'
2
4
  require 'semlogr'
3
5
 
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'semlogr'
5
+
6
+ class TestSink
7
+ def emit(_log_event)
8
+ raise StandardError, 'emit error'
9
+ end
10
+ end
11
+
12
+ class TestEnricher
13
+ def enrich(_log_event)
14
+ raise StandardError, 'enrich error'
15
+ end
16
+ end
17
+
18
+ Semlogr::SelfLogger.logger = STDERR
19
+ Semlogr.logger = Semlogr::Logger.create do |c|
20
+ c.write_to :console
21
+
22
+ c.write_to TestSink.new
23
+ c.enrich_with TestEnricher.new
24
+ c.filter ->(_log_event) { raise StandardError, 'filter error' }
25
+ end
26
+
27
+ Semlogr.info('Customer {id} checked out', id: 123)
data/samples/sinks.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/setup'
2
4
  require 'semlogr'
3
5
 
data/semlogr.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
 
@@ -31,4 +33,5 @@ Gem::Specification.new do |spec|
31
33
  spec.add_development_dependency 'rspec', '~> 3.7'
32
34
  spec.add_development_dependency 'rubocop', '0.53'
33
35
  spec.add_development_dependency 'simplecov', '~>0.15'
36
+ spec.add_development_dependency 'timecop', '~>0.9'
34
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: semlogr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Sedich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-17 00:00:00.000000000 Z
11
+ date: 2018-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: digest-xxhash
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0.15'
167
+ - !ruby/object:Gem::Dependency
168
+ name: timecop
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '0.9'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '0.9'
167
181
  description: A modern semantic logger for Ruby inspired by Serilog.
168
182
  email:
169
183
  - stefan.sedich@gmail.com
@@ -200,7 +214,9 @@ files:
200
214
  - lib/semlogr/logger.rb
201
215
  - lib/semlogr/null_logger.rb
202
216
  - lib/semlogr/properties/output_properties.rb
217
+ - lib/semlogr/self_logger.rb
203
218
  - lib/semlogr/sinks/aggregate.rb
219
+ - lib/semlogr/sinks/batching.rb
204
220
  - lib/semlogr/sinks/colored_console.rb
205
221
  - lib/semlogr/sinks/console.rb
206
222
  - lib/semlogr/sinks/enriching.rb
@@ -211,12 +227,14 @@ files:
211
227
  - lib/semlogr/templates/template.rb
212
228
  - lib/semlogr/templates/template_cache.rb
213
229
  - lib/semlogr/templates/text_token.rb
230
+ - lib/semlogr/utils/bounded_queue.rb
214
231
  - lib/semlogr/version.rb
215
232
  - samples/basic.rb
216
233
  - samples/context.rb
217
234
  - samples/enrichment.rb
218
235
  - samples/filtering.rb
219
236
  - samples/log_context.rb
237
+ - samples/self_logger.rb
220
238
  - samples/sinks.rb
221
239
  - semlogr.gemspec
222
240
  homepage: https://github.com/semlogr/semlogr