storyteller 0.0.2
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 +7 -0
- data/LICENSE +202 -0
- data/README.md +72 -0
- data/lib/story_teller/book.rb +39 -0
- data/lib/story_teller/chapter.rb +44 -0
- data/lib/story_teller/configurable.rb +21 -0
- data/lib/story_teller/console.rb +7 -0
- data/lib/story_teller/environments/development/configuration.rb +3 -0
- data/lib/story_teller/environments/development/middleware.rb +12 -0
- data/lib/story_teller/environments/development/notifications.rb +286 -0
- data/lib/story_teller/environments/development/rack.rb +35 -0
- data/lib/story_teller/environments/development/sidekiq.rb +33 -0
- data/lib/story_teller/environments/development.rb +47 -0
- data/lib/story_teller/environments/production/configuration.rb +4 -0
- data/lib/story_teller/environments/production/middleware.rb +13 -0
- data/lib/story_teller/environments/production/rack.rb +36 -0
- data/lib/story_teller/environments/production.rb +6 -0
- data/lib/story_teller/environments/staging/configuration.rb +4 -0
- data/lib/story_teller/environments/staging.rb +5 -0
- data/lib/story_teller/environments/test/configuration.rb +4 -0
- data/lib/story_teller/environments/test.rb +4 -0
- data/lib/story_teller/environments.rb +4 -0
- data/lib/story_teller/exception.rb +23 -0
- data/lib/story_teller/formatters/development/error.rb +52 -0
- data/lib/story_teller/formatters/development/info.rb +85 -0
- data/lib/story_teller/formatters/development.rb +4 -0
- data/lib/story_teller/formatters/null.rb +3 -0
- data/lib/story_teller/formatters/structured.rb +8 -0
- data/lib/story_teller/formatters.rb +26 -0
- data/lib/story_teller/levels.rb +4 -0
- data/lib/story_teller/logger.rb +62 -0
- data/lib/story_teller/message.rb +35 -0
- data/lib/story_teller/railtie.rb +73 -0
- data/lib/story_teller/story.rb +26 -0
- data/lib/story_teller/version.rb +13 -0
- data/lib/story_teller.rb +76 -0
- data/storyteller.gemspec +19 -0
- metadata +81 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
class StoryTeller::Formatters
|
2
|
+
# These are autoloaded because it's wasteful to
|
3
|
+
# load all the code for each output if the application
|
4
|
+
# is not going to use them.
|
5
|
+
autoload :Development, "story_teller/formatters/development"
|
6
|
+
autoload :Structured, "story_teller/formatters/structured"
|
7
|
+
autoload :Null, "story_teller/formatters/null"
|
8
|
+
|
9
|
+
class Base
|
10
|
+
attr_reader :output, :name
|
11
|
+
|
12
|
+
def initialize(name:, output:)
|
13
|
+
@name = name
|
14
|
+
@output = output
|
15
|
+
config
|
16
|
+
end
|
17
|
+
|
18
|
+
def replace_output!(new_output)
|
19
|
+
@output = new_output
|
20
|
+
end
|
21
|
+
|
22
|
+
def write(story)
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module StoryTeller
|
2
|
+
# Logger is a helper class to allow
|
3
|
+
# libraries and application to use StoryTeller
|
4
|
+
# through.
|
5
|
+
|
6
|
+
# This also includes a bunch of hacks that makes
|
7
|
+
# sure the logger behaves with rails.
|
8
|
+
class Logger < ::Logger
|
9
|
+
def initialize(formatter:, log_level: ::Logger::DEBUG)
|
10
|
+
@formatter = formatter
|
11
|
+
@logdev = self
|
12
|
+
self.level = log_level
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(severity, message = nil, progname = nil)
|
16
|
+
return if severity < self.level
|
17
|
+
|
18
|
+
data = {severity: format_severity(severity)}
|
19
|
+
|
20
|
+
if message.nil?
|
21
|
+
if block_given?
|
22
|
+
message = yield
|
23
|
+
else
|
24
|
+
message = progname
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
@formatter.write(StoryTeller::Story.new(message: message.to_s, **data))
|
29
|
+
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def silence(*args)
|
34
|
+
if block_given?
|
35
|
+
yield
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# This is not used and is only needed
|
40
|
+
# so ActiveSupport doesn't extend this Logger
|
41
|
+
# with other method like it does here:
|
42
|
+
# https://github.com/rails/rails/blob/fad0c6b899ba786994c506f11f587e29d7bf9c2d/activesupport/lib/active_support/logger.rb#L16-L20
|
43
|
+
# https://github.com/rails/rails/blob/25d52ab782623e59c0bc920076393d1691999e4e/activerecord/lib/active_record/railtie.rb#L66
|
44
|
+
# https://github.com/rails/rails/blob/1bb9f0e616fb60a9cc1ea67c9bbdb49b2e18835a/railties/lib/rails/commands/server/server_command.rb#L84
|
45
|
+
def dev
|
46
|
+
STDOUT
|
47
|
+
end
|
48
|
+
|
49
|
+
def <<(msg)
|
50
|
+
self.add(Logger::DEBUG, msg)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Following methods have an effect on the Logger but don't within StoryTeller.
|
54
|
+
def reopen(logdev = nil);
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def close
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
class StoryTeller::Message
|
4
|
+
NIL_STRING = ""
|
5
|
+
|
6
|
+
def initialize(template)
|
7
|
+
template = template.to_s
|
8
|
+
|
9
|
+
unless template.encoding.name == "UTF-8"
|
10
|
+
template = template.encode("UTF-8", invalid: :replace)
|
11
|
+
end
|
12
|
+
|
13
|
+
@template = template
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(attrs)
|
17
|
+
attrs.each_pair do |k, v|
|
18
|
+
attributes[k] = v.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
template % attributes
|
22
|
+
rescue StandardError => e
|
23
|
+
e.message
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :template
|
29
|
+
|
30
|
+
def attributes
|
31
|
+
@attributes ||= Hash.new do
|
32
|
+
NIL_STRING
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "action_view/log_subscriber"
|
2
|
+
require "action_controller/log_subscriber"
|
3
|
+
require "action_mailer/log_subscriber"
|
4
|
+
require "active_job/log_subscriber"
|
5
|
+
require "active_storage/log_subscriber"
|
6
|
+
require "active_record/log_subscriber"
|
7
|
+
require "tempfile"
|
8
|
+
|
9
|
+
module StoryTeller
|
10
|
+
class Railtie < ::Rails::Railtie
|
11
|
+
class ProtectedNameError < StandardError; end
|
12
|
+
|
13
|
+
env_module = case
|
14
|
+
when Rails.env.development?
|
15
|
+
StoryTeller::Environments::Development
|
16
|
+
when Rails.env.staging?
|
17
|
+
StoryTeller::Environments::Staging
|
18
|
+
when Rails.env.test?
|
19
|
+
StoryTeller::Environments::Test
|
20
|
+
when Rails.env.production?
|
21
|
+
StoryTeller::Environments::Production
|
22
|
+
end
|
23
|
+
|
24
|
+
StoryTeller.include(env_module)
|
25
|
+
|
26
|
+
config.story_teller = StoryTeller.config
|
27
|
+
|
28
|
+
# Detaching the default ones from Rails
|
29
|
+
initializer "story_teller.log_subscribers" do |app|
|
30
|
+
::ActionController::LogSubscriber.detach_from :action_controller
|
31
|
+
::ActionView::LogSubscriber.detach_from :action_view
|
32
|
+
::ActionMailer::LogSubscriber.detach_from :action_mailer
|
33
|
+
|
34
|
+
::ActiveJob::LogSubscriber.detach_from :active_job
|
35
|
+
::ActiveStorage::LogSubscriber.detach_from :active_storage
|
36
|
+
::ActiveRecord::LogSubscriber.detach_from :active_record
|
37
|
+
end
|
38
|
+
|
39
|
+
initializer "story_teller.logger" do |app|
|
40
|
+
Rails.logger = StoryTeller::Logger.new(formatter: app.config.story_teller.log_formatter, log_level: app.config.log_level)
|
41
|
+
end
|
42
|
+
|
43
|
+
initializer "story_teller.middlewares" do |app|
|
44
|
+
::ActionController::Base.use(StoryTeller::Middleware)
|
45
|
+
|
46
|
+
app.config.middleware.insert_before(
|
47
|
+
ActionDispatch::ActionableExceptions,
|
48
|
+
StoryTeller::Rack,
|
49
|
+
app,
|
50
|
+
app.config.debug_exception_response_format
|
51
|
+
)
|
52
|
+
|
53
|
+
app.config.middleware.delete Rails::Rack::Logger
|
54
|
+
app.config.middleware.delete ::ActionDispatch::DebugExceptions
|
55
|
+
end
|
56
|
+
|
57
|
+
initializer "story_teller.finalize" do |app|
|
58
|
+
StoryTeller.initialize!(StoryTeller.config)
|
59
|
+
end
|
60
|
+
|
61
|
+
console do
|
62
|
+
file = Tempfile.new
|
63
|
+
|
64
|
+
puts "\u{1F58B} StoryTeller redirected logs while using the console: #{file.path}"
|
65
|
+
|
66
|
+
config.story_teller.formatters.each do |formatter|
|
67
|
+
formatter.replace_output!(file)
|
68
|
+
end
|
69
|
+
|
70
|
+
Rails::Console.prepend(StoryTeller::Console)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "story_teller/message"
|
2
|
+
|
3
|
+
class StoryTeller::Story
|
4
|
+
attr_reader :attributes, :message, :chapter, :timestamp
|
5
|
+
|
6
|
+
def initialize(message: "", chapter: nil, **data)
|
7
|
+
self.timestamp = Time.now.utc
|
8
|
+
self.message = StoryTeller::Message.new(message)
|
9
|
+
self.attributes = data.symbolize_keys
|
10
|
+
self.chapter = chapter
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_hash
|
14
|
+
{
|
15
|
+
timestamp: timestamp.strftime("%s%N"),
|
16
|
+
message: message.render(attributes),
|
17
|
+
data: {
|
18
|
+
story: attributes,
|
19
|
+
chapter: chapter&.attributes
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
attr_writer :timestamp, :message, :attributes, :chapter
|
26
|
+
end
|
data/lib/story_teller.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module StoryTeller
|
2
|
+
require "story_teller/configurable"
|
3
|
+
require "story_teller/levels"
|
4
|
+
require "story_teller/logger"
|
5
|
+
require "story_teller/book"
|
6
|
+
require "story_teller/exception"
|
7
|
+
require "story_teller/chapter"
|
8
|
+
require "story_teller/story"
|
9
|
+
require "story_teller/formatters"
|
10
|
+
require "story_teller/environments"
|
11
|
+
require "story_teller/console"
|
12
|
+
|
13
|
+
class AlreadyInitializedError < StandardError; end
|
14
|
+
class FormatterNotAllowedWithName < StandardError; end
|
15
|
+
|
16
|
+
module_function
|
17
|
+
|
18
|
+
def chapter(title: "", subject: "", **options, &block)
|
19
|
+
chapter = StoryTeller::Chapter.new(
|
20
|
+
title: title,
|
21
|
+
subject: subject,
|
22
|
+
parent: book.current_chapter
|
23
|
+
)
|
24
|
+
|
25
|
+
book.open(chapter, **options, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def book
|
29
|
+
StoryTeller::Book.current_book
|
30
|
+
end
|
31
|
+
|
32
|
+
def config(&block)
|
33
|
+
@config ||= StoryTeller::Configuration.new
|
34
|
+
|
35
|
+
block.call(@config) if block_given?
|
36
|
+
|
37
|
+
@config
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize!(config)
|
41
|
+
raise AlreadyInitializedError if frozen?
|
42
|
+
|
43
|
+
mod = Module.new do
|
44
|
+
config.formatters.each do |formatter|
|
45
|
+
func_name = formatter.name.to_sym
|
46
|
+
|
47
|
+
if method_defined?(func_name)
|
48
|
+
raise FormatterNotAllowedWithName, "#{func_name} is already defined as a method on StoryTeller. Please choose another name for your formatter"
|
49
|
+
end
|
50
|
+
|
51
|
+
define_method(func_name, ->(message, **data) {
|
52
|
+
chapter = StoryTeller.book.current_chapter
|
53
|
+
story = case message
|
54
|
+
when StoryTeller::Story
|
55
|
+
message
|
56
|
+
when StoryTeller::Exception
|
57
|
+
StoryTeller::Book.current_book.current_exception = message.exception
|
58
|
+
message
|
59
|
+
else
|
60
|
+
StoryTeller::Story.new(message: message, chapter: chapter, **data)
|
61
|
+
end
|
62
|
+
|
63
|
+
formatter.write(story)
|
64
|
+
nil
|
65
|
+
})
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
self.extend mod
|
70
|
+
freeze
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if defined?(::Rails::Engine)
|
75
|
+
require "story_teller/railtie"
|
76
|
+
end
|
data/storyteller.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "lib/story_teller/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "storyteller"
|
5
|
+
s.version = StoryTeller::Version.to_s
|
6
|
+
s.licenses = ["Apache-2.0"]
|
7
|
+
s.summary = "Observe and understand how your application is used."
|
8
|
+
s.description = %s{
|
9
|
+
StoryTeller is an observation framework that allows teams to
|
10
|
+
create meaningful stories to help them understand what's going on in
|
11
|
+
your production environment.
|
12
|
+
}
|
13
|
+
s.authors = ["Pier-Olivier Thibault"]
|
14
|
+
s.email = "storyteller@pier-olivier.dev"
|
15
|
+
s.homepage = "https://github.com/pier-oliviert/storyteller.rb"
|
16
|
+
s.metadata = { "source_code_uri" => "https://github.com/pier-oliviert/storyteller.rb" }
|
17
|
+
|
18
|
+
s.files = %w[storyteller.gemspec README.md LICENSE] + `git ls-files | grep -E '^(bin|lib|web)'`.split("\n")
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: storyteller
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pier-Olivier Thibault
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-06-01 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: "\n StoryTeller is an observation framework that allows teams to\n
|
14
|
+
\ create meaningful stories to help them understand what's going on in\n your
|
15
|
+
production environment.\n "
|
16
|
+
email: storyteller@pier-olivier.dev
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- LICENSE
|
22
|
+
- README.md
|
23
|
+
- lib/story_teller.rb
|
24
|
+
- lib/story_teller/book.rb
|
25
|
+
- lib/story_teller/chapter.rb
|
26
|
+
- lib/story_teller/configurable.rb
|
27
|
+
- lib/story_teller/console.rb
|
28
|
+
- lib/story_teller/environments.rb
|
29
|
+
- lib/story_teller/environments/development.rb
|
30
|
+
- lib/story_teller/environments/development/configuration.rb
|
31
|
+
- lib/story_teller/environments/development/middleware.rb
|
32
|
+
- lib/story_teller/environments/development/notifications.rb
|
33
|
+
- lib/story_teller/environments/development/rack.rb
|
34
|
+
- lib/story_teller/environments/development/sidekiq.rb
|
35
|
+
- lib/story_teller/environments/production.rb
|
36
|
+
- lib/story_teller/environments/production/configuration.rb
|
37
|
+
- lib/story_teller/environments/production/middleware.rb
|
38
|
+
- lib/story_teller/environments/production/rack.rb
|
39
|
+
- lib/story_teller/environments/staging.rb
|
40
|
+
- lib/story_teller/environments/staging/configuration.rb
|
41
|
+
- lib/story_teller/environments/test.rb
|
42
|
+
- lib/story_teller/environments/test/configuration.rb
|
43
|
+
- lib/story_teller/exception.rb
|
44
|
+
- lib/story_teller/formatters.rb
|
45
|
+
- lib/story_teller/formatters/development.rb
|
46
|
+
- lib/story_teller/formatters/development/error.rb
|
47
|
+
- lib/story_teller/formatters/development/info.rb
|
48
|
+
- lib/story_teller/formatters/null.rb
|
49
|
+
- lib/story_teller/formatters/structured.rb
|
50
|
+
- lib/story_teller/levels.rb
|
51
|
+
- lib/story_teller/logger.rb
|
52
|
+
- lib/story_teller/message.rb
|
53
|
+
- lib/story_teller/railtie.rb
|
54
|
+
- lib/story_teller/story.rb
|
55
|
+
- lib/story_teller/version.rb
|
56
|
+
- storyteller.gemspec
|
57
|
+
homepage: https://github.com/pier-oliviert/storyteller.rb
|
58
|
+
licenses:
|
59
|
+
- Apache-2.0
|
60
|
+
metadata:
|
61
|
+
source_code_uri: https://github.com/pier-oliviert/storyteller.rb
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubygems_version: 3.4.1
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Observe and understand how your application is used.
|
81
|
+
test_files: []
|