lumberjack_data_dog 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 746fb699cb9f259ed561b070a1a3939573c052a2354cc7edbe7bf2b72084cf1e
4
+ data.tar.gz: 0647be2db0eac1df4c77ef89e5859396cd345a5b1291419a8ece171aff3bd48a
5
+ SHA512:
6
+ metadata.gz: 6232d656b9202c58b504c8eeb1a1904dbb85d06ed3d80d2e302d7cd0fd4657b2874685d3396e4b777ded9f7cd180fd1ddbbd1b192e78be0a160ac32b3806b4ce
7
+ data.tar.gz: '01209a5f00a204706e100cb6d389f5fac1b46f0ebe5ab39b12558c32811cf727813d5a1fe9b810599cd43d93b51182354a089e7de70e9d8312157c55b8aa7daf'
@@ -0,0 +1,12 @@
1
+ # Dependabot update strategy
2
+ version: 2
3
+ updates:
4
+ - package-ecosystem: bundler
5
+ directory: "/"
6
+ schedule:
7
+ interval: weekly
8
+ allow:
9
+ # Automatically keep all runtime dependencies updated
10
+ - dependency-name: "*"
11
+ dependency-type: "production"
12
+ versioning-strategy: lockfile-only
@@ -0,0 +1,46 @@
1
+ name: Continuous Integration
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ - actions-*
8
+ tags:
9
+ - v*
10
+ pull_request:
11
+ branches-ignore:
12
+ - actions-*
13
+ workflow_dispatch:
14
+
15
+ env:
16
+ BUNDLE_CLEAN: "true"
17
+ BUNDLE_PATH: vendor/bundle
18
+ BUNDLE_JOBS: 3
19
+ BUNDLE_RETRY: 3
20
+
21
+ jobs:
22
+ build:
23
+ name: ${{ matrix.ruby }} build
24
+ runs-on: ubuntu-latest
25
+ strategy:
26
+ matrix:
27
+ include:
28
+ - ruby: "ruby"
29
+ standardrb: true
30
+ - ruby: "3.0"
31
+ - ruby: "2.7"
32
+ - ruby: "2.5"
33
+ steps:
34
+ - uses: actions/checkout@v2
35
+ - name: Set up Ruby
36
+ uses: ruby/setup-ruby@v1
37
+ with:
38
+ ruby-version: ${{ matrix.ruby}}
39
+ - name: Install gems
40
+ run: |
41
+ bundle install
42
+ - name: Run Tests
43
+ run: bundle exec rake
44
+ - name: standardrb
45
+ if: matrix.standardrb
46
+ run: bundle exec standardrb
data/.standard.yml ADDED
@@ -0,0 +1,8 @@
1
+ ruby_version: 2.5
2
+
3
+ format: progress
4
+
5
+ ignore:
6
+ - 'spec/**/*':
7
+ - Lint/UselessAssignment
8
+ - Lint/Void
data/CHANGE_LOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## 1.0.0
8
+
9
+ ### Added
10
+
11
+ - Initial release
data/MIT_LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2025 Brian Durand
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Lumberjack DataDog
2
+
3
+ [![Continuous Integration](https://github.com/bdurand/lumberjack_data_dog/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/lumberjack_data_dog/actions/workflows/continuous_integration.yml)
4
+ [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
5
+ [![Gem Version](https://badge.fury.io/rb/lumberjack_data_dog.svg)](https://badge.fury.io/rb/lumberjack_data_dog)
6
+
7
+ This gem provides a logging setup for using the [lumberjack](https://github.com/bdurand/lumberjack) gem with DataDog. It sets up JSON logging and maps values to DataDog's [standard attributes](https://docs.datadoghq.com/logs/processing/attributes_naming_convention/).
8
+
9
+ ## Features
10
+
11
+ - **DataDog Standard Attribute Mapping**: Automatically maps Lumberjack log fields to DataDog's standard attributes:
12
+ - `time` → `timestamp`
13
+ - `severity` → `status`
14
+ - `progname` → `logger.name`
15
+ - `pid` → `pid`
16
+ - **Exception Handling**: Structured exception logging with `kind`, `message`, and `stack` fields
17
+ - **Duration Logging**: Automatic conversion of duration values to nanoseconds for DataDog
18
+ - **Configurable Message Truncation**: Limit message length to prevent oversized logs
19
+ - **Thread Information**: Optional thread name logging
20
+ - **Tag Remapping**: Flexible tag transformation and remapping
21
+ - **Pretty JSON**: Optional pretty-printed JSON output for development
22
+
23
+ ## Usage
24
+
25
+ ### Basic Setup
26
+
27
+ ```ruby
28
+ require 'lumberjack_data_dog'
29
+
30
+ # Create a logger that outputs to STDOUT
31
+ logger = Lumberjack::DataDog.setup
32
+
33
+ # Log messages
34
+ logger.info("Application started")
35
+ logger.warn("This is a warning", user_id: 123)
36
+ logger.error("Something went wrong", request_id: "abc-123")
37
+ ```
38
+
39
+ ### Advanced Configuration
40
+
41
+ ```ruby
42
+ logger = Lumberjack::DataDog.setup do |config|
43
+ # Truncate messages longer than 1000 characters
44
+ config.max_message_length = 1000
45
+
46
+ # Include thread information
47
+ config.thread_name = true # or :global for global thread ID
48
+
49
+ # Disable PID logging
50
+ config.pid = false
51
+
52
+ # Remap custom tags to DataDog attributes
53
+ config.remap_tags(
54
+ user_id: "usr.id",
55
+ request_id: "http.request_id"
56
+ )
57
+
58
+ # Enable pretty JSON for development
59
+ config.pretty = true
60
+ end
61
+ ```
62
+
63
+ ### Logging to a File
64
+
65
+ ```ruby
66
+ # Log to a file instead of STDOUT
67
+ File.open("/var/log/app.log", "a") do |file|
68
+ logger = Lumberjack::DataDog.setup(file)
69
+ logger.info("Logged to file")
70
+ end
71
+ ```
72
+
73
+ ### Exception Logging
74
+
75
+ Exceptions are automatically structured with DataDog's error attributes:
76
+
77
+ ```ruby
78
+ begin
79
+ raise StandardError, "Something went wrong"
80
+ rescue => e
81
+ # Results in structured error with error.kind, error.message, and error.stack fields
82
+ logger.error(e)
83
+ end
84
+ ```
85
+
86
+ ### Duration Logging
87
+
88
+ Duration values are automatically converted to nanoseconds:
89
+
90
+ ```ruby
91
+ # Log execution time
92
+ start_time = Time.now
93
+ # ... do some work ...
94
+ duration = Time.now - start_time
95
+
96
+ logger.info("Operation completed", duration: duration)
97
+ # duration is automatically converted to nanoseconds
98
+
99
+ # You can also use specific duration units
100
+ logger.info("DB query", duration_ms: 150.5) # milliseconds
101
+ logger.info("API call", duration_micros: 1500) # microseconds
102
+ logger.info("Function", duration_ns: 1500000) # nanoseconds
103
+ ```
104
+
105
+ ### Custom Tag Transformation
106
+
107
+ ```ruby
108
+ logger = Lumberjack::DataDog.setup do |config|
109
+ config.remap_tags(
110
+ # Simple remapping
111
+ correlation_id: "trace.correlation_id",
112
+
113
+ # Transform with a lambda
114
+ user_email: ->(email) { {"usr.email" => email.downcase} }
115
+ )
116
+ end
117
+
118
+ logger.info("User logged in", user_email: "USER@EXAMPLE.COM")
119
+ # Results in usr.email: "user@example.com"
120
+ ```
121
+
122
+ ## Installation
123
+
124
+ Add this line to your application's Gemfile:
125
+
126
+ ```ruby
127
+ gem "lumberjack_data_dog"
128
+ ```
129
+
130
+ And then execute:
131
+ ```bash
132
+ $ bundle install
133
+ ```
134
+
135
+ Or install it yourself as:
136
+ ```bash
137
+ $ gem install lumberjack_data_dog
138
+ ```
139
+
140
+ ## Contributing
141
+
142
+ Open a pull request on GitHub.
143
+
144
+ Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
145
+
146
+ ## License
147
+
148
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lumberjack_json_device"
4
+
5
+ module Lumberjack::DataDog
6
+ STANDARD_ATTRIBUTE_MAPPING = {
7
+ time: "timestamp",
8
+ severity: "status",
9
+ progname: ["logger", "name"],
10
+ pid: "pid"
11
+ }.freeze
12
+
13
+ class Config
14
+ attr_accessor :max_message_length
15
+ attr_accessor :backtrace_cleaner
16
+ attr_accessor :thread_name
17
+ attr_accessor :pid
18
+ attr_accessor :allow_all_tags
19
+ attr_reader :tag_mapping
20
+ attr_accessor :pretty
21
+
22
+ def initialize
23
+ @max_message_length = nil
24
+ @backtrace_cleaner = nil
25
+ @thread_name = false
26
+ @pid = true
27
+ @allow_all_tags = true
28
+ @tag_mapping = {}
29
+ @pretty = false
30
+ end
31
+
32
+ def remap_tags(tag_mapping)
33
+ @tag_mapping = @tag_mapping.merge(tag_mapping)
34
+ end
35
+
36
+ def validate!
37
+ if !max_message_length.nil? && (!max_message_length.is_a?(Integer) || max_message_length <= 0)
38
+ raise ArgumentError, "max_message_length must be a positive integer"
39
+ end
40
+
41
+ unless backtrace_cleaner.nil? || backtrace_cleaner.respond_to?(:clean)
42
+ raise ArgumentError, "backtrace_cleaner must respond to #clean"
43
+ end
44
+ end
45
+ end
46
+
47
+ class << self
48
+ def setup(stream = $stdout, &block)
49
+ config = Config.new
50
+ yield(config) if block_given?
51
+ config.validate!
52
+
53
+ new_logger(stream, config)
54
+ end
55
+
56
+ private
57
+
58
+ def new_logger(stream, config)
59
+ logger = Lumberjack::Logger.new(json_device(stream, config))
60
+
61
+ # Add the error to the error tag if an exception is logged as the message.
62
+ logger.message_formatter.add(Exception, message_exception_formatter)
63
+
64
+ # Split the error tag up into standard attributes if it is an exception.
65
+ logger.tag_formatter.add(Exception, exception_tag_formatter(config))
66
+
67
+ if config.thread_name
68
+ if config.thread_name == :global
69
+ logger.tag_globally("logger.thread_name" => -> { Lumberjack::Utils.global_thread_id })
70
+ else
71
+ logger.tag_globally("logger.thread_name" => -> { Lumberjack::Utils.thread_name })
72
+ end
73
+ end
74
+
75
+ if config.pid == :global
76
+ logger.tag_globally("pid" => -> { Lumberjack::Utils.global_pid })
77
+ end
78
+
79
+ logger
80
+ end
81
+
82
+ def json_device(stream, config)
83
+ Lumberjack::JsonDevice.new(stream, mapping: json_mapping(config), pretty: config.pretty)
84
+ end
85
+
86
+ def json_mapping(config)
87
+ mapping = config.tag_mapping.transform_keys(&:to_sym)
88
+ mapping = mapping.merge(STANDARD_ATTRIBUTE_MAPPING)
89
+
90
+ mapping.delete(:pid) if !config.pid || config.pid == :global
91
+
92
+ mapping[:tags] = "*" if config.allow_all_tags
93
+
94
+ mapping[:message] = if config.max_message_length
95
+ truncate_message_transformer(config.max_message_length)
96
+ else
97
+ "message"
98
+ end
99
+
100
+ mapping[:duration] = duration_nanosecond_transformer(1_000_000_000)
101
+ mapping[:duration_ms] = duration_nanosecond_transformer(1_000_000)
102
+ mapping[:duration_micros] = duration_nanosecond_transformer(1_000)
103
+ mapping[:duration_ns] = duration_nanosecond_transformer(1)
104
+
105
+ mapping.transform_keys!(&:to_s)
106
+ end
107
+
108
+ def truncate_message_transformer(max_length)
109
+ lambda do |msg|
110
+ msg = msg.inspect unless msg.is_a?(String)
111
+ msg = msg[0, max_length] if msg.is_a?(String) && msg.length > max_length
112
+ {"message" => msg}
113
+ end
114
+ end
115
+
116
+ def duration_nanosecond_transformer(multiplier)
117
+ lambda do |duration|
118
+ if duration.is_a?(Numeric)
119
+ {"duration" => (duration * multiplier).round}
120
+ else
121
+ {"duration" => nil}
122
+ end
123
+ end
124
+ end
125
+
126
+ def message_exception_formatter
127
+ lambda do |error|
128
+ Lumberjack::Formatter::TaggedMessage.new(error.inspect, error: error)
129
+ end
130
+ end
131
+
132
+ def exception_tag_formatter(config)
133
+ lambda do |error|
134
+ error_tags = {"kind" => error.class.name, "message" => error.message}
135
+ trace = error.backtrace
136
+ if trace
137
+ trace = config.backtrace_cleaner.clean(trace) if config.backtrace_cleaner
138
+ error_tags["stack"] = trace
139
+ end
140
+ error_tags
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lumberjack/data_dog"
@@ -0,0 +1,32 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "lumberjack_data_dog"
3
+ spec.version = File.read(File.join(__dir__, "VERSION")).strip
4
+ spec.authors = ["Brian Durand"]
5
+ spec.email = ["bbdurand@gmail.com"]
6
+
7
+ spec.summary = "Lumberjack logging device that outputs JSON formatted for DataDog with standard attribute mapping."
8
+ spec.homepage = "https://github.com/bdurand/lumberjack_data_dog"
9
+ spec.license = "MIT"
10
+
11
+ # Specify which files should be added to the gem when it is released.
12
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
13
+ ignore_files = %w[
14
+ .gitignore
15
+ .travis.yml
16
+ Appraisals
17
+ Gemfile
18
+ Gemfile.lock
19
+ Rakefile
20
+ gemfiles/
21
+ spec/
22
+ ]
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject { |f| ignore_files.any? { |path| f.start_with?(path) } }
25
+ end
26
+
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.required_ruby_version = ">= 2.5"
30
+
31
+ spec.add_dependency "lumberjack_json_device", ">= 2.0.0"
32
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lumberjack_data_dog
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Durand
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-07-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lumberjack_json_device
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.0
27
+ description:
28
+ email:
29
+ - bbdurand@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".github/dependabot.yml"
35
+ - ".github/workflows/continuous_integration.yml"
36
+ - ".standard.yml"
37
+ - CHANGE_LOG.md
38
+ - MIT_LICENSE.txt
39
+ - README.md
40
+ - VERSION
41
+ - lib/lumberjack/data_dog.rb
42
+ - lib/lumberjack_data_dog.rb
43
+ - lumberjack_data_dog.gemspec
44
+ homepage: https://github.com/bdurand/lumberjack_data_dog
45
+ licenses:
46
+ - MIT
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '2.5'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubygems_version: 3.4.10
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: Lumberjack logging device that outputs JSON formatted for DataDog with standard
67
+ attribute mapping.
68
+ test_files: []