simple_structured_logger 0.1.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd3c01ed526e586a28acf3c9dcc9c3d8220bea4547c264568cf32c4293c9a77c
4
- data.tar.gz: dc3beaa6509ca46fb2be4405e6211b75b373f87221dca5f7ba66e25ed723c4a2
3
+ metadata.gz: 824ec497606b6c639fdc7209c9854243512457bb7b97bc3ae3b6300354f85916
4
+ data.tar.gz: 56bdbf5858c33164a08ec88ee96cb0842c8f11a2caeef1123af14f3a2b0a73f7
5
5
  SHA512:
6
- metadata.gz: 9a1a5ba635be779a15ea83a4d17d8e71af96f0dc5cc0324bc2ac83af8e76fb959047d236b9e1191990481dfeaaf390469c0b737bfbc532f3bc24fe4979cdf636
7
- data.tar.gz: 94830ac9e6452c82183564f9a67e58560d9eacd617a7aea1d69c1d291ffc4187da7726c386895361f78da3e1faa4f020c12c452ac4b01e639c1cbf06bf13a1c2
6
+ metadata.gz: 8296498d2355f715ccc7a8b089f3129ed39688ebb8503e940f1b729d18d754142878524c48f3ab9cc8221910be0ea6200259ad9c1b1d4fa994f8e2dcab363ad2
7
+ data.tar.gz: 384a55748e300dcc7903be2609094af12fd88a4e2d3c58abf5646fb819314e9cb68b034c25890c2562355fe26babcd89fc422d617fd3373dff255ff0dc6b9ab9
@@ -0,0 +1,59 @@
1
+ name: "CodeQL"
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ # The branches below must be a subset of the branches above
8
+ branches: [ master ]
9
+ schedule:
10
+ - cron: '24 16 * * 1'
11
+
12
+ jobs:
13
+ analyze:
14
+ name: Analyze
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ actions: read
18
+ contents: read
19
+ security-events: write
20
+
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ language: [ 'ruby' ]
25
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
26
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
27
+
28
+ steps:
29
+ - name: Checkout repository
30
+ uses: actions/checkout@v2
31
+
32
+ # Initializes the CodeQL tools for scanning.
33
+ - name: Initialize CodeQL
34
+ uses: github/codeql-action/init@v1
35
+ with:
36
+ languages: ${{ matrix.language }}
37
+ # If you wish to specify custom queries, you can do so here or in a config file.
38
+ # By default, queries listed here will override any specified in a config file.
39
+ # Prefix the list here with "+" to use these queries and those in the config file.
40
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
41
+
42
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
43
+ # If this step fails, then you should remove it and run the build manually (see below)
44
+ - name: Autobuild
45
+ uses: github/codeql-action/autobuild@v1
46
+
47
+ # ℹ️ Command-line programs to run using the OS shell.
48
+ # 📚 https://git.io/JvXDl
49
+
50
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
51
+ # and modify them (or add more) to build your code if your project
52
+ # uses a compiled language
53
+
54
+ #- run: |
55
+ # make bootstrap
56
+ # make release
57
+
58
+ - name: Perform CodeQL Analysis
59
+ uses: github/codeql-action/analyze@v1
@@ -19,15 +19,15 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby-version: ['2.6', '2.7', '3.0']
22
+ ruby-version: ['2.6', '2.7', '3.0', '3.1']
23
23
 
24
24
  steps:
25
25
  - uses: actions/checkout@v2
26
26
  - name: Set up Ruby
27
- uses: ruby/setup-ruby@v1
27
+ uses: ruby/setup-ruby@v1
28
28
  with:
29
29
  ruby-version: ${{ matrix.ruby-version }}
30
30
  bundler-cache: true
31
31
  - name: Run tests
32
32
  run: bundle exec rake
33
-
33
+
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  /vendor/bundle
11
+ /*.gem
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 2.7.3
1
+ ruby 3.1.1
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Michael Bianco
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 CHANGED
@@ -3,23 +3,107 @@
3
3
 
4
4
  # SimpleStructuredLogger
5
5
 
6
- Dead-simple structured logging in ruby with a dead-simple codebase. That's it.
6
+ Dead-simple structured logging in ruby with a dead-simple codebase. No dependencies, everything logs to stdout, and simple hooks to customize. That's it.
7
7
 
8
8
  ```ruby
9
9
  gem 'simple_structured_logger'
10
10
  ```
11
11
 
12
+ ## Usage
13
+
14
+ You can use this logger anywhere. Class methods, instance methods, use it as a logger for libraries, etc.
15
+
16
+ Some examples:
17
+
18
+ ```ruby
19
+ # in a console or simple script
20
+ include SimpleStructuredLogger
21
+ log.info 'core message', key: Time.now.to_i
22
+
23
+ # in class & instance methods
24
+ class LoggingInModule
25
+ include SimpleStructuredLogger
26
+
27
+ def self.log_something
28
+ log.info 'including the module enables a class and instance method', key: Time.now.to_i
29
+ end
30
+
31
+ def log_something_else
32
+ log.info 'the class and instance method share the same logging context', key: Time.now.to_i
33
+ end
34
+ end
35
+
36
+ # So, how do I set the context? How can I customize how it's set?
37
+ SimpleStructuredLogger.configure do
38
+ expand_context do |context|
39
+ # you can pass in a object and use `expand_context` to extract the relevant keys
40
+ if context[:user]
41
+ context[:user_id] = context[:user].id
42
+ context[:user_name] = context[:user].name
43
+ end
44
+
45
+ context
46
+ end
47
+ end
48
+
49
+ class ExampleJob
50
+ def perform(user_id)
51
+ user = get_user(user_id, job_argument)
52
+ log.set_context(user: user, job: self.class, job_argument: job_argument)
53
+ log.info 'the log will contain the user_id, job_argument, and job class'
54
+
55
+ # you can also add additional default pairs without resetting context
56
+ log.default_tags[:something] = 'else'
57
+ end
58
+ end
59
+
60
+ # Can you pass object arguments as values and automatically expand them? Well, yes, you can!
61
+ SimpleStructuredLogger.configure do
62
+ expand_log do |tags, default_tags|
63
+ if tags[:stripe_resource] && tags[:stripe_resource].respond_to?(:id)
64
+ stripe_resource = tags.delete(:stripe_resource)
65
+ tags[:stripe_resource_id] = stripe_resource.id
66
+ tags[:stripe_resource_type] = stripe_resource.class.to_s
67
+ end
68
+
69
+ # this is a really nice pattern I like to use. The `metric` key can trigger a call out to your observability tooling
70
+ if tags[:metric]
71
+ dimensions = default_tags.slice(:stripe_user_id, :other_default_tag)
72
+ metrics.track_counter(tags[:metric], dimensions: dimensions)
73
+ end
74
+
75
+ tags
76
+ end
77
+ end
78
+
79
+ # want simple formatting? You got it!
80
+ SimpleStructuredLogger.logger.formatter = proc do |severity, _datetime, _progname, msg|
81
+ "#{severity}: #{msg}\n"
82
+ end
83
+
84
+ # Configure the logger directly if you need to
85
+ SimpleStructuredLogger.logger.level(Logger::INFO)
86
+ ```
87
+
88
+ Want to change the log level quickly? Without modifying source?
89
+
90
+ ```shell
91
+ LOG_LEVEL=DEBUG ruby your_script.rb
92
+
93
+ # case does not matter
94
+ LOG_LEVEL=info ruby your_script.rb
95
+ ```
96
+
12
97
  ## Design Goals
13
98
 
14
99
  * Extremely simple codebase that's easy to read and override
15
- * Structured logging that reads nicely
100
+ * Structured logging that reads nicely and is easy to filter using grep or something like Papertrail
16
101
  * Ability to easily set context, and expand context with user-configurable hook
17
102
  * Ability to easily add structured log pre-processing. I want to be able to pass
18
- in an object specific to my application and for the relavent important keys to
103
+ in an object specific to my application and for the relevant important keys to
19
104
  be expanded automatically.
20
105
  * `Rails.logger = SimpleStructuredLogger.new(STDOUT)`
21
- * Not designed around massive systems or scale
22
- * Don't support multiple log destinations
106
+ * Not designed around massive systems or scale: no JSON logging, multiple log destinations, and other fanciness.
23
107
  * Don't build in fancy pre-processing for errors or other common ruby objects
24
108
 
25
109
  ### Opinionated Devops Setup
@@ -37,6 +121,10 @@ gem 'simple_structured_logger'
37
121
  * https://github.com/nishidayuya/structured_logger
38
122
  * https://github.com/rocketjob/semantic_logger
39
123
 
124
+ ## Related
125
+
126
+ * https://github.com/roidrage/lograge
127
+
40
128
  ## Why is structured logging important?
41
129
 
42
130
  * https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying
@@ -45,33 +133,14 @@ gem 'simple_structured_logger'
45
133
  ## What about Rail's tagged logging?
46
134
 
47
135
  Tagged logging is not structured logging. I want to be able to search through
48
- PaperTrail and easily grab an audit trail for a specific context, i.e. `the_job=FailingJob the_user=1`.
49
-
50
- ## Usage
51
-
52
- ```ruby
53
- # config/initializers/logger.rb
54
- if Rails.env.development? || Rails.env.test?
55
- SimpleStructuredLogger::Writer.instance.logger = Rails.logger
56
- end
57
-
58
- # models/order.rb
59
- class Order
60
- include SimpleStructuredLogger
61
-
62
- def initialize
63
- log.reset_context!
64
- log.default_tags[:global] = 'key'
65
- end
66
-
67
- def do_something
68
- log.info 'simple structured logging', key: value, shopify_id: 123
69
- end
70
- end
71
- ```
136
+ PaperTrail/Splunk/etc and easily grab an audit trail for a specific context, i.e. `the_job=FailingJob the_user=1`.
72
137
 
73
138
  ## Testing
74
139
 
75
140
  ```
76
141
  bundle exec rake
77
142
  ```
143
+
144
+ ## TODO
145
+
146
+ - [ ] Support logs as blocks?
@@ -2,6 +2,14 @@ require 'logger'
2
2
  require 'singleton'
3
3
 
4
4
  module SimpleStructuredLogger
5
+ def self.configure(&block)
6
+ SimpleStructuredLogger::Configuration.instance_eval(&block)
7
+ end
8
+
9
+ def self.logger
10
+ SimpleStructuredLogger::Writer.instance.logger
11
+ end
12
+
5
13
  def log
6
14
  SimpleStructuredLogger::Writer.instance
7
15
  end
@@ -13,25 +21,27 @@ module SimpleStructuredLogger
13
21
  SimpleStructuredLogger::Writer.instance
14
22
  end
15
23
  end
16
-
17
24
  end
18
25
 
19
26
  module Configuration
20
27
  extend self
21
28
 
29
+ @expand_context = nil
30
+ @expand_log = nil
31
+
22
32
  def expand_context(&block)
23
33
  if block.nil?
24
- @expand_context = block
25
- else
26
34
  @expand_context
35
+ else
36
+ @expand_context = block
27
37
  end
28
38
  end
29
39
 
30
40
  def expand_log(&block)
31
41
  if block.nil?
32
- @expand_log = block
33
- else
34
42
  @expand_log
43
+ else
44
+ @expand_log = block
35
45
  end
36
46
  end
37
47
  end
@@ -44,6 +54,16 @@ module SimpleStructuredLogger
44
54
  def initialize
45
55
  @logger = ::Logger.new(STDOUT)
46
56
  @default_tags = {}
57
+
58
+ set_log_level_from_environment
59
+ end
60
+
61
+ def set_log_level_from_environment
62
+ env_log_level = ENV['LOG_LEVEL']
63
+
64
+ if !env_log_level.nil? && Logger::Severity.const_defined?(env_log_level.upcase)
65
+ @logger.level = Logger::Severity.const_get(env_log_level.upcase)
66
+ end
47
67
  end
48
68
 
49
69
  def reset_context!
@@ -53,8 +73,8 @@ module SimpleStructuredLogger
53
73
  def set_context(context)
54
74
  reset_context!
55
75
 
56
- if self.respond_to?(:expand_context)
57
- context = self.expand_context(context)
76
+ if SimpleStructuredLogger::Configuration.expand_context
77
+ context = SimpleStructuredLogger::Configuration.expand_context.call(context)
58
78
  end
59
79
 
60
80
  @default_tags.merge!(context)
@@ -73,26 +93,18 @@ module SimpleStructuredLogger
73
93
  end
74
94
 
75
95
  def warn(msg, opts={})
76
- # TODO consider returning the generated log string with structured keys
77
-
78
96
  @logger.warn("#{msg}: #{stringify_tags(opts)}")
79
97
  end
80
98
 
81
- private
99
+ private def stringify_tags(additional_tags)
100
+ additional_tags = additional_tags.dup
82
101
 
83
- def generate_log(msg, opts)
84
- #code
102
+ if SimpleStructuredLogger::Configuration.expand_log
103
+ additional_tags = SimpleStructuredLogger::Configuration.expand_log.call(additional_tags, self.default_tags)
85
104
  end
86
105
 
87
- def stringify_tags(additional_tags)
88
- additional_tags = additional_tags.dup
89
-
90
- if self.respond_to?(:expand_log)
91
- additional_tags = self.expand_log(additional_tags)
92
- end
93
-
94
- @default_tags.merge(additional_tags).map { |k,v| "#{k}=#{v}" }.join(' ')
95
- end
106
+ @default_tags.merge(additional_tags).map {|k, v| "#{k}=#{v}" }.join(' ')
107
+ end
96
108
 
97
109
  end
98
110
  end
@@ -4,20 +4,19 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "simple_structured_logger"
7
- spec.version = '0.1.5'
7
+ spec.version = '1.0.0'
8
8
  spec.authors = ["Michael Bianco"]
9
9
  spec.email = ["mike@mikebian.co"]
10
+ spec.licenses = ['MIT']
10
11
 
11
- spec.summary = "Dead-simple structured logging in ruby with a simple codebase."
12
- # spec.description = %q{TODO: Write a longer description or delete this line.}
12
+ spec.summary = "Dead-simple structured logging in ruby with a simple codebase"
13
13
  spec.homepage = "https://github.com/iloveitaly/simple_structured_logger"
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
- spec.bindir = "exe"
17
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
16
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
17
  spec.require_paths = ["lib"]
19
18
 
20
- spec.add_development_dependency "bundler", "~> 2.2.15"
21
- spec.add_development_dependency "rake", "~> 12.3.3"
22
- spec.add_development_dependency "minitest", "~> 5.0"
19
+ spec.add_development_dependency "bundler", "~> 2.3.10"
20
+ spec.add_development_dependency "rake", "~> 13.0.6"
21
+ spec.add_development_dependency "minitest", "~> 5.15"
23
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_structured_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bianco
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-05 00:00:00.000000000 Z
11
+ date: 2022-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,54 +16,58 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.2.15
19
+ version: 2.3.10
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.2.15
26
+ version: 2.3.10
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 12.3.3
33
+ version: 13.0.6
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 12.3.3
40
+ version: 13.0.6
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '5.0'
47
+ version: '5.15'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '5.0'
54
+ version: '5.15'
55
55
  description:
56
56
  email:
57
57
  - mike@mikebian.co
58
- executables: []
58
+ executables:
59
+ - console
60
+ - setup
59
61
  extensions: []
60
62
  extra_rdoc_files: []
61
63
  files:
62
64
  - ".github/dependabot.yml"
65
+ - ".github/workflows/codeql-analysis.yml"
63
66
  - ".github/workflows/ruby.yml"
64
67
  - ".gitignore"
65
68
  - ".tool-versions"
66
69
  - Gemfile
70
+ - LICENSE
67
71
  - README.md
68
72
  - Rakefile
69
73
  - bin/console
@@ -71,7 +75,8 @@ files:
71
75
  - lib/simple_structured_logger.rb
72
76
  - simple_structured_logger.gemspec
73
77
  homepage: https://github.com/iloveitaly/simple_structured_logger
74
- licenses: []
78
+ licenses:
79
+ - MIT
75
80
  metadata: {}
76
81
  post_install_message:
77
82
  rdoc_options: []
@@ -88,8 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
93
  - !ruby/object:Gem::Version
89
94
  version: '0'
90
95
  requirements: []
91
- rubygems_version: 3.1.6
96
+ rubygems_version: 3.3.7
92
97
  signing_key:
93
98
  specification_version: 4
94
- summary: Dead-simple structured logging in ruby with a simple codebase.
99
+ summary: Dead-simple structured logging in ruby with a simple codebase
95
100
  test_files: []