Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.

## [2.4.0] - 2026-07-29
- Added semantic logging methods: `.config`, `.step`, `.success`, `.warning`, `.data`
- Semantic methods apply standard colours based on message type for consistent logging
- Semantic colours can be configured in the main `DVLA::Herodotus.configure` block alongside logger options
- Added `DVLA::Herodotus.reset_configuration!` to reset both logger options and semantic colours
- `DVLA::Herodotus::SemanticColours.configure` and `.reset!` still available for standalone colour configuration

## [2.3.2] - 2025-12-23
- Add 'spawn_child_logger' method to allow other processes to inherit the current logger's configuration

Expand Down
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,59 @@ You can stack multiple method calls to add additional styling and use string int

---

### Semantic Logging Methods

Herodotus provides semantic methods that associate message types with standard colours. This promotes consistent colour usage across teams and makes logs easier to read.

```ruby
LOG.info { "Environment: #{env}".config } # brown - configuration/environment
LOG.info { "Checking image status".step } # magenta - step execution/progress
LOG.info { "Application completed".success } # green - successful operations
LOG.info { "Retrying request".warning } # yellow - warnings/non-fatal issues
LOG.info { "Response: #{payload}".data } # blue - data values/payloads
```

#### Default Colours

| Method | Default Colour | Usage |
|-----------|----------------|-------------------------------------|
| `.config` | brown | Configuration, environment settings |
| `.step` | magenta | Method calls, checking, polling |
| `.success`| green | Successful operations, completions |
| `.warning`| yellow | Warnings, non-fatal issues |
| `.data` | blue | Data values, responses, payloads |

#### Custom Colour Configuration

You can configure semantic colours alongside other logger options in a single configure block:

```ruby
DVLA::Herodotus.configure do |c|
c.display_pid = true
c.prefix_colour = { overall: :blue }
c.config = :cyan
c.step = :blue
c.success = :bright_green
c.warning = :red
c.data = :magenta
end
```

To reset all configuration to defaults:

```ruby
DVLA::Herodotus.reset_configuration!
```

Semantic methods can be chained with other styles:

```ruby
LOG.info { "COMPLETED".success.bold }
LOG.info { "ERROR: #{msg}".warning.bg_red }
```

---

## Development

Herodotus is very lightweight. Currently, all code to generate a new logger can be found in `herodotus.rb` and the code for the logger is in `herodotus_logger.rb` so that is the best place to start with any modifications
28 changes: 21 additions & 7 deletions lib/dvla/herodotus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,37 @@
require_relative 'herodotus/herodotus_logger'
require_relative 'herodotus/multi_writer'
require_relative 'herodotus/proc_writer'
require_relative 'herodotus/semantic_colours'
require_relative 'herodotus/config'
require_relative 'herodotus/string'

module DVLA
module Herodotus
class << self
attr_accessor :main_logger
end

CONFIG_ATTRIBUTES = %i[display_pid main prefix_colour].freeze
def configuration
@configuration ||= Config.new
end

def configure
yield(configuration) if block_given?
configuration
end

def self.config
config ||= Struct.new(*CONFIG_ATTRIBUTES, keyword_init: true).new
yield(config) if block_given?
config
def config
c = Config.new
yield(c) if block_given?
c
end

def reset_configuration!
@configuration = Config.new
SemanticColours.reset!
end
end

def self.logger(system_name, config: self.config, output_path: nil)
def self.logger(system_name, config: self.configuration, output_path: nil)
create_logger(system_name, config, output_path)
end

Expand Down
23 changes: 23 additions & 0 deletions lib/dvla/herodotus/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module DVLA
module Herodotus
class Config
LOGGER_ATTRIBUTES = %i[display_pid main prefix_colour].freeze

attr_accessor(*LOGGER_ATTRIBUTES)

SemanticColours::DEFAULT_COLOURS.keys.each do |semantic_type|
define_method(semantic_type) do
SemanticColours.send(semantic_type)
end

define_method("#{semantic_type}=") do |colour|
SemanticColours.send("#{semantic_type}=", colour)
end
end

def reset_colours!
SemanticColours.reset!
end
end
end
end
6 changes: 3 additions & 3 deletions lib/dvla/herodotus/herodotus_logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ def initialize(system_name, *args, config: DVLA::Herodotus.config, **kwargs)
super(*args, **kwargs)

@system_name = system_name
@main = config[:main]
@display_pid = config[:display_pid]
@prefix_colour = config[:prefix_colour] || {}
@main = config.main
@display_pid = config.display_pid
@prefix_colour = config.prefix_colour || {}
validate_colour_config if @prefix_colour.any?
@correlation_id = SecureRandom.uuid[0, 8]
set_formatter
Expand Down
37 changes: 37 additions & 0 deletions lib/dvla/herodotus/semantic_colours.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module DVLA
module Herodotus
module SemanticColours
DEFAULT_COLOURS = {
config: :brown,
step: :magenta,
success: :green,
warning: :yellow,
data: :blue
}.freeze

class << self
def colours
@colours ||= DEFAULT_COLOURS.dup
end

def configure
yield(self) if block_given?
end

def reset!
@colours = DEFAULT_COLOURS.dup
end

DEFAULT_COLOURS.keys.each do |semantic_type|
define_method(semantic_type) do
colours[semantic_type]
end

define_method("#{semantic_type}=") do |colour|
colours[semantic_type] = colour
end
end
end
end
end
end
23 changes: 23 additions & 0 deletions lib/dvla/herodotus/string.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require_relative 'semantic_colours'

class String
def colourise(code, reset_code = 39)
# Making sure we align the correct reset codes
Expand Down Expand Up @@ -88,4 +90,25 @@ def strip_colour
alias grey gray
alias reverse_color reverse_colour
alias strip_color strip_colour

# Semantic logging methods - apply colours based on message type
def config
send(DVLA::Herodotus::SemanticColours.config)
end

def step
send(DVLA::Herodotus::SemanticColours.step)
end

def success
send(DVLA::Herodotus::SemanticColours.success)
end

def warning
send(DVLA::Herodotus::SemanticColours.warning)
end

def data
send(DVLA::Herodotus::SemanticColours.data)
end
end
2 changes: 1 addition & 1 deletion lib/dvla/herodotus/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module DVLA
module Herodotus
VERSION = '2.3.2'.freeze
VERSION = '2.4.0'.freeze
end
end
79 changes: 79 additions & 0 deletions spec/dvla/herodotus/semantic_colours_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require 'dvla/herodotus/semantic_colours'

RSpec.describe DVLA::Herodotus::SemanticColours do
after(:each) do
described_class.reset!
end

describe 'default colours' do
it 'has brown as the default config colour' do
expect(described_class.config).to eq(:brown)
end

it 'has magenta as the default step colour' do
expect(described_class.step).to eq(:magenta)
end

it 'has green as the default success colour' do
expect(described_class.success).to eq(:green)
end

it 'has yellow as the default warning colour' do
expect(described_class.warning).to eq(:yellow)
end

it 'has blue as the default data colour' do
expect(described_class.data).to eq(:blue)
end
end

describe '.configure' do
it 'allows overriding the config colour' do
described_class.configure do |c|
c.config = :cyan
end

expect(described_class.config).to eq(:cyan)
end

it 'allows overriding multiple colours' do
described_class.configure do |c|
c.step = :blue
c.warning = :red
end

expect(described_class.step).to eq(:blue)
expect(described_class.warning).to eq(:red)
end

it 'preserves unmodified colours' do
described_class.configure do |c|
c.success = :bright_green
end

expect(described_class.config).to eq(:brown)
expect(described_class.step).to eq(:magenta)
expect(described_class.success).to eq(:bright_green)
end
end

describe '.reset!' do
it 'restores all colours to defaults' do
described_class.configure do |c|
c.config = :red
c.step = :blue
c.success = :cyan
c.warning = :magenta
c.data = :green
end

described_class.reset!

expect(described_class.config).to eq(:brown)
expect(described_class.step).to eq(:magenta)
expect(described_class.success).to eq(:green)
expect(described_class.warning).to eq(:yellow)
expect(described_class.data).to eq(:blue)
end
end
end
55 changes: 55 additions & 0 deletions spec/dvla/herodotus/string_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,59 @@
expect('Test'.red.bg_yellow.reverse_colour.bold).to eq "\e[1m\e[7m\e[103m\e[31mTest\e[39m\e[49m\e[27m\e[22m"
end
end

describe 'semantic logging methods' do
after(:each) do
DVLA::Herodotus::SemanticColours.reset!
end

describe 'with default colours' do
it 'applies brown for config' do
expect('Test'.config).to eq("\e[33mTest\e[39m")
end

it 'applies magenta for step' do
expect('Test'.step).to eq("\e[35mTest\e[39m")
end

it 'applies green for success' do
expect('Test'.success).to eq("\e[32mTest\e[39m")
end

it 'applies yellow for warning' do
expect('Test'.warning).to eq("\e[93mTest\e[39m")
end

it 'applies blue for data' do
expect('Test'.data).to eq("\e[34mTest\e[39m")
end
end

describe 'with custom colours' do
it 'uses configured colour for config' do
DVLA::Herodotus::SemanticColours.configure { |c| c.config = :red }
expect('Test'.config).to eq("\e[31mTest\e[39m")
end

it 'uses configured colour for step' do
DVLA::Herodotus::SemanticColours.configure { |c| c.step = :cyan }
expect('Test'.step).to eq("\e[36mTest\e[39m")
end

it 'uses configured colour for success' do
DVLA::Herodotus::SemanticColours.configure { |c| c.success = :bright_green }
expect('Test'.success).to eq("\e[92mTest\e[39m")
end
end

describe 'chaining with other styles' do
it 'can chain semantic methods with styles' do
expect('Test'.config.bold).to eq("\e[1m\e[33mTest\e[39m\e[22m")
end

it 'can chain semantic methods with background colours' do
expect('Test'.success.bg_black).to eq("\e[40m\e[32mTest\e[39m\e[49m")
end
end
end
end
Loading
Loading