streamlog 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +47 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/streamlog/application.js +14 -0
- data/app/assets/javascripts/streamlog/logs.js +34 -0
- data/app/assets/stylesheets/streamlog/application.css +14 -0
- data/app/assets/stylesheets/streamlog/logs.css +130 -0
- data/app/controllers/streamlog/application_controller.rb +4 -0
- data/app/controllers/streamlog/logs_controller.rb +28 -0
- data/app/helpers/streamlog/application_helper.rb +4 -0
- data/app/helpers/streamlog/logs_helper.rb +4 -0
- data/app/views/layouts/streamlog/application.html.erb +14 -0
- data/app/views/streamlog/logs/index.html.erb +11 -0
- data/config/routes.rb +4 -0
- data/lib/streamlog.rb +9 -0
- data/lib/streamlog/content_length_with_exclusions.rb +17 -0
- data/lib/streamlog/engine.rb +13 -0
- data/lib/streamlog/log_colorize.rb +97 -0
- data/lib/streamlog/sse.rb +20 -0
- data/lib/streamlog/version.rb +3 -0
- data/lib/tasks/streamlog_tasks.rake +4 -0
- data/test/controllers/streamlog/logs_controller_test.rb +16 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +14 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +15 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +83 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/log/development.log +12713 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/08566920d369f24fb0e0fab666d6533f +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/0cc19a2b1a79905d88a44c841f3905a7 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/15077dcfc2991f4d53efb227afb355d8 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/1e0370339d0fe37d611e41d1753b64a7 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2672f8bcb16682edfcec89e54222e43e +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/3a5e2b558fc8fd85e97118737d640cc8 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/55bd36192b21d16d1da18eb7dfc63115 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/62ae17f9de91b7ce7f9d5389a82a4af5 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/737cb2e025a1ed703455f8b0b6c50705 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/a607af0389defa27633daba46cbc840a +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/a6e7eb2dba0d23cc46af96364c4f3594 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/b5eaa04d91d85a3b0ee804a3b8496517 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/c1767bfa4fed465c13b09ac150b937b1 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cc93a764cc1ad7c2772f18342be2a007 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cd4fa3bc740d41192a3777a88b7205b5 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/e3be3a6ce6847d32e038498a4fc2a773 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/e75d188293b55f22a5579aba994b4dba +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/fc993b8ba5edeca52d799d32cb4cf088 +0 -0
- data/test/dummy/tmp/restart.txt +0 -0
- data/test/helpers/streamlog/logs_helper_test.rb +6 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/streamlog_test.rb +7 -0
- data/test/test_helper.rb +15 -0
- metadata +249 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YTk1NzM4M2VkZDg4YTFkM2NjNzk2NGVjNGZhOTRhNTI4NTQ3OGNjMg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
Mzk4NmVhODNmMDNiNDQ5MzcxOGY0Zjc4MWFjYTk4ZTA0ZTI1MzFiNw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MGY0NmFiNmQ5NWFhNzliNmM4ZDgyMTRhZjE3NzJiYTAwZWFkOWM4Njc3NDgw
|
10
|
+
NTM2N2FjZDYxODQ5NjIxZDE1NGVhZTVmZjA2Y2M3OWUyNzZkYzM2M2I3NDY1
|
11
|
+
ZDFjMTExZjhjYWQxMzY5MzYxMzQ3YzAxOTdjNTllYzJhZDczN2E=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OWRmOTY4NmQ3YjUyZDg2MjVmNWY2ZTc4M2MzOWZlMmM4ODRiNzQxMTkyZWUz
|
14
|
+
YjM0NmE5MDUzYTkxMDE5OWQ2MDEzMjBkMDVhMTcwOWE3ZWI3YjBmZWM0ZWQ5
|
15
|
+
ZmZkNmI4Mzg3YjQwNDhlNGY1YWQ2YjM2MTNjOTdmNjJiNzZmOTE=
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 Salman Siddiqui
|
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,47 @@
|
|
1
|
+
# Streamlog
|
2
|
+
|
3
|
+
Streamlog is a Rails Mountable Engine. It streams rails application log to the browser. It is an alternative to [Browserlog](https://coveralls.io/r/dieb/browserlog).
|
4
|
+
Browserlog is based on polling while streamlog streams the log.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Simply add it to your `Gemfile`
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'streamlog'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then mount this rails engine on `config/routes.rb`
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
Rails.application.routes.draw do
|
18
|
+
mount Streamlog::Engine => '/streamlog'
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
This will make the log available at ``domain/streamlog``. Make sure to authenticate this route.
|
23
|
+
|
24
|
+
### Using your app layout
|
25
|
+
|
26
|
+
Create ``streamlog.rb`` in ``config/initializers``, and add the following line in it.
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
Streamlog.engine_layout = false
|
30
|
+
```
|
31
|
+
|
32
|
+
Then add the following line in your layout ``head`` tag
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
<% yield :head %>
|
36
|
+
```
|
37
|
+
|
38
|
+
This will use your application layout to load streamlog view.
|
39
|
+
|
40
|
+
## Note
|
41
|
+
|
42
|
+
You will need to install passenger with nginx/apache on your local machine to achieve concurrency and be able to test streamlog.
|
43
|
+
[GoRails](https://gorails.com/deploy/ubuntu/14.04) is a guide to setup Passenger with nginx.
|
44
|
+
|
45
|
+
## Supported Rails Version
|
46
|
+
|
47
|
+
* Rails >= 4.1
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Streamlog'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,14 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require_tree .
|
@@ -0,0 +1,34 @@
|
|
1
|
+
$(document).ready(function () {
|
2
|
+
var autoScroll = true;
|
3
|
+
|
4
|
+
function startAutoScroll() {
|
5
|
+
$("#streamlog-resume").addClass('streamlog-hidden');
|
6
|
+
autoScroll = true;
|
7
|
+
}
|
8
|
+
|
9
|
+
function stopAutoScroll() {
|
10
|
+
$("#streamlog-resume").removeClass('streamlog-hidden');
|
11
|
+
autoScroll = false;
|
12
|
+
}
|
13
|
+
|
14
|
+
function scrollToBottom() {
|
15
|
+
window.scrollTo(0, document.body.scrollHeight);
|
16
|
+
}
|
17
|
+
|
18
|
+
var source = new EventSource('/streamlog/stream');
|
19
|
+
|
20
|
+
source.addEventListener('message', function (e) {
|
21
|
+
$("<p>" + JSON.parse(e.data) + "</p>").insertBefore(".streamlog-cursor");
|
22
|
+
if (autoScroll)
|
23
|
+
scrollToBottom();
|
24
|
+
});
|
25
|
+
|
26
|
+
$("#streamlog-resume").click(scrollToBottom);
|
27
|
+
|
28
|
+
window.onscroll = function () {
|
29
|
+
if ($(window).scrollTop() + $(window).height() == $(document).height())
|
30
|
+
startAutoScroll();
|
31
|
+
else
|
32
|
+
stopAutoScroll();
|
33
|
+
};
|
34
|
+
});
|
@@ -0,0 +1,14 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*/
|
@@ -0,0 +1,130 @@
|
|
1
|
+
.streamlog {
|
2
|
+
min-width: 500px;
|
3
|
+
min-height: 430px;
|
4
|
+
background: #272822;
|
5
|
+
border-radius: 5px;
|
6
|
+
overflow: hidden;
|
7
|
+
font-size: 13px;
|
8
|
+
}
|
9
|
+
|
10
|
+
.streamlog-body {
|
11
|
+
font-family: Andale Mono, monospace;
|
12
|
+
padding: 10px;
|
13
|
+
}
|
14
|
+
|
15
|
+
.streamlog-body p {
|
16
|
+
color: #fff;
|
17
|
+
padding: 1px 0;
|
18
|
+
}
|
19
|
+
|
20
|
+
@keyframes blink {
|
21
|
+
0% {
|
22
|
+
background: rgba(225, 225, 225, 100);
|
23
|
+
}
|
24
|
+
100% {
|
25
|
+
background: rgba(225, 225, 225, 0);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
@-webkit-keyframes blink {
|
30
|
+
0% {
|
31
|
+
background: rgba(225, 225, 225, 100);
|
32
|
+
}
|
33
|
+
100% {
|
34
|
+
background: rgba(225, 225, 225, 0);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
@-moz-keyframes blink {
|
39
|
+
0% {
|
40
|
+
background: rgba(225, 225, 225, 100);
|
41
|
+
}
|
42
|
+
100% {
|
43
|
+
background: rgba(225, 225, 225, 0);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
.streamlog-cursor {
|
48
|
+
background: #63de00;
|
49
|
+
display: inline-block;
|
50
|
+
width: 11px;
|
51
|
+
height: 19px;
|
52
|
+
margin-bottom: -3px;
|
53
|
+
-webkit-animation-name: blink;
|
54
|
+
-webkit-animation-duration: 1.2s;
|
55
|
+
-webkit-animation-iteration-count: infinite;
|
56
|
+
-moz-animation-name: blink;
|
57
|
+
-moz-animation-duration: 1.2s;
|
58
|
+
-moz-animation-iteration-count: infinite;
|
59
|
+
}
|
60
|
+
|
61
|
+
.streamlog-hidden {
|
62
|
+
display: none !important;
|
63
|
+
visibility: hidden !important;
|
64
|
+
}
|
65
|
+
|
66
|
+
#streamlog-resume {
|
67
|
+
position: fixed;
|
68
|
+
right: 40px;
|
69
|
+
bottom: 40px;
|
70
|
+
}
|
71
|
+
|
72
|
+
.log-method {
|
73
|
+
color: #C285FF;
|
74
|
+
}
|
75
|
+
|
76
|
+
.log-path {
|
77
|
+
color: #85D6FF;
|
78
|
+
}
|
79
|
+
|
80
|
+
.log-ip {
|
81
|
+
color: #585856;
|
82
|
+
}
|
83
|
+
|
84
|
+
.log-date {
|
85
|
+
color: #585856;
|
86
|
+
}
|
87
|
+
|
88
|
+
.log-controller {
|
89
|
+
color: #FF0066;
|
90
|
+
}
|
91
|
+
|
92
|
+
.log-action {
|
93
|
+
color: #FF0066;
|
94
|
+
}
|
95
|
+
|
96
|
+
.log-format {
|
97
|
+
color: #C285FF;
|
98
|
+
}
|
99
|
+
|
100
|
+
.log-template-name {
|
101
|
+
color: #FFFF66;
|
102
|
+
}
|
103
|
+
|
104
|
+
.log-layout-name {
|
105
|
+
color: #C285FF;
|
106
|
+
}
|
107
|
+
|
108
|
+
.log-rendering-time {
|
109
|
+
color: #85D6FF;
|
110
|
+
}
|
111
|
+
|
112
|
+
.log-model-load {
|
113
|
+
color: #da564a;
|
114
|
+
}
|
115
|
+
|
116
|
+
.log-sql-query {
|
117
|
+
color: #585856;
|
118
|
+
}
|
119
|
+
|
120
|
+
.log-total-time {
|
121
|
+
color: #85D6FF;
|
122
|
+
}
|
123
|
+
|
124
|
+
.log-status.success {
|
125
|
+
color: #33D633;
|
126
|
+
}
|
127
|
+
|
128
|
+
.log-status.error {
|
129
|
+
color: red;
|
130
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_dependency 'streamlog/application_controller'
|
2
|
+
|
3
|
+
module Streamlog
|
4
|
+
class LogsController < ApplicationController
|
5
|
+
include ActionController::Live
|
6
|
+
layout 'application' unless Streamlog.engine_layout
|
7
|
+
|
8
|
+
def index
|
9
|
+
end
|
10
|
+
|
11
|
+
def stream
|
12
|
+
response.headers['Content-Type'] = 'text/event-stream'
|
13
|
+
|
14
|
+
sse = Streamlog::SSE.new(response.stream)
|
15
|
+
colorizer = Streamlog::LogColorize.new
|
16
|
+
|
17
|
+
begin
|
18
|
+
File::Tail::Logfile.tail(Rails.root.join('log', Rails.env+'.log').to_s, backward: 1) do |line|
|
19
|
+
sse.write colorizer.colorize_line line
|
20
|
+
end
|
21
|
+
rescue IOError
|
22
|
+
# When the client disconnects, we'll get an IOError on write
|
23
|
+
ensure
|
24
|
+
sse.close
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Streamlog</title>
|
5
|
+
<%= stylesheet_link_tag 'streamlog/application', media: 'all' %>
|
6
|
+
<%= javascript_include_tag 'streamlog/application' %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= h content_for(:title, 'Server Logs') %>
|
2
|
+
<% content_for :head do %>
|
3
|
+
<%= javascript_include_tag 'streamlog/application' %>
|
4
|
+
<%= stylesheet_link_tag 'streamlog/application' %>
|
5
|
+
<% end %>
|
6
|
+
<div class="streamlog">
|
7
|
+
<div class="streamlog-body">
|
8
|
+
<div class="streamlog-cursor"></div>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
<button class="streamlog-hidden" id="streamlog-resume">Resume</button>
|
data/config/routes.rb
ADDED
data/lib/streamlog.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rack
|
2
|
+
class ContentLengthWithExclusions < ContentLength
|
3
|
+
def initialize(app, options = {})
|
4
|
+
@app = app
|
5
|
+
|
6
|
+
@exclude = options[:exclude]
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
if @exclude && @exclude.call(env)
|
11
|
+
@app.call(env)
|
12
|
+
else
|
13
|
+
super(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'streamlog/content_length_with_exclusions'
|
2
|
+
|
3
|
+
module Streamlog
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
isolate_namespace Streamlog
|
6
|
+
|
7
|
+
config.middleware.use Rack::ContentLengthWithExclusions, exclude: proc { |env|
|
8
|
+
env['PATH_INFO'] == 'streamlog/stream'
|
9
|
+
}
|
10
|
+
config.preload_frameworks = true
|
11
|
+
config.allow_concurrency = true
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Streamlog
|
2
|
+
class LogColorize
|
3
|
+
REXP_REQUEST = /^Started (?<method>\w+) "(?<path>[\w\/\?\&\=\.]+)" for (?<ip>[\d\.]+) at (?<date>[\w:\-\+\s]+)$/
|
4
|
+
REXP_CONTROLLER = /^Processing by (?<controller>[\w\:]+)#(?<action>\w+) as (?<format>[\w\/\-\.]+)$/
|
5
|
+
REXP_RENDER = /^\s*Rendered (?<path>.*\/)(?<template_name>\w+\.html\.\w+) \((?<rendering_time>[\d+\.]+ms)\)$/
|
6
|
+
REXP_RENDER_WITHIN = /^\s*Rendered (?<path>.*\/)(?<template_name>\w+\.html\.\w+) within (?<layout_name>[\w\d\/]+) ?\((?<rendering_time>[\d+\.]+ms)\)$/
|
7
|
+
REXP_COMPLETE = /^Completed (?<status>\d+\s\w+) in (?<total_time>[\d+\.]+ms) (?<last_bit>.*)$/
|
8
|
+
REXP_SQL_QUERY = /^\s*(?<model_load>[\w:]+ Load) \((?<rendering_time>[\d+\.]+ms)\) \s*(?<sql_query>.*)$/
|
9
|
+
|
10
|
+
def colorize_line(line)
|
11
|
+
line = strip_ansi_colors(line)
|
12
|
+
line = colorize(line)
|
13
|
+
rescue => e
|
14
|
+
puts "Could not colorize: #{e.message}"
|
15
|
+
line
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def strip_ansi_colors(line)
|
21
|
+
line.gsub(/\e\[(\d+)m/, '')
|
22
|
+
end
|
23
|
+
|
24
|
+
def colorize(line)
|
25
|
+
case line
|
26
|
+
when REXP_REQUEST then colorize_request(line)
|
27
|
+
when REXP_CONTROLLER then colorize_controller(line)
|
28
|
+
when REXP_RENDER then colorize_render(line)
|
29
|
+
when REXP_RENDER_WITHIN then colorize_render_within(line)
|
30
|
+
when REXP_COMPLETE then colorize_complete(line)
|
31
|
+
when REXP_SQL_QUERY then colorize_sql_query(line)
|
32
|
+
else line
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def span(item, classname)
|
37
|
+
"<span class='#{classname}'>#{item}</span>"
|
38
|
+
end
|
39
|
+
|
40
|
+
def space
|
41
|
+
' '
|
42
|
+
end
|
43
|
+
|
44
|
+
def colorize_request(line)
|
45
|
+
data = regex_parse(line.match(REXP_REQUEST))
|
46
|
+
method = span(data[:method], 'log-method')
|
47
|
+
path = span(data[:path], 'log-path')
|
48
|
+
ip = span(data[:ip], 'log-ip')
|
49
|
+
date = span(data[:date], 'log-date')
|
50
|
+
"Started #{method} \"#{path}\" for #{ip} at #{date}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def colorize_controller(line)
|
54
|
+
data = regex_parse(line.match(REXP_CONTROLLER))
|
55
|
+
controller = span(data[:controller], 'log-controller')
|
56
|
+
action = span(data[:action], 'log-action')
|
57
|
+
format = span(data[:format], 'log-format')
|
58
|
+
"Processing by #{controller}##{action} as #{format}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def colorize_render(line)
|
62
|
+
data = regex_parse(line.match(REXP_RENDER))
|
63
|
+
path = data[:path]
|
64
|
+
template_name = span(data[:template_name], 'log-template-name')
|
65
|
+
rendering_time = span(data[:rendering_time], 'log-rendering-time')
|
66
|
+
"#{space}Rendered #{path}#{template_name} (#{rendering_time})"
|
67
|
+
end
|
68
|
+
|
69
|
+
def colorize_render_within(line)
|
70
|
+
data = regex_parse(line.match(REXP_RENDER_WITHIN))
|
71
|
+
path = data[:path]
|
72
|
+
layout_name = span(data[:layout_name], 'log-layout-name')
|
73
|
+
template_name = span(data[:template_name], 'log-template-name')
|
74
|
+
rendering_time = span(data[:rendering_time], 'log-rendering-time')
|
75
|
+
"#{space}Rendered #{path}#{template_name} within #{layout_name} (#{rendering_time})"
|
76
|
+
end
|
77
|
+
|
78
|
+
def colorize_sql_query(line)
|
79
|
+
data = regex_parse(line.match(REXP_SQL_QUERY))
|
80
|
+
model_load = span(data[:model_load], 'log-model-load')
|
81
|
+
rendering_time = span(data[:rendering_time], 'log-rendering-time')
|
82
|
+
sql_query = data[:sql_query]
|
83
|
+
"#{space}#{model_load} (#{rendering_time}) #{sql_query}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def colorize_complete(line)
|
87
|
+
data = regex_parse(line.match(REXP_COMPLETE))
|
88
|
+
status = span(data[:status], data[:status] =~ /^2/ ? 'log-status success' : 'log-status failure')
|
89
|
+
total_time = span(data[:total_time], 'log-total-time')
|
90
|
+
"Completed #{status} in #{total_time} #{data[:last_bit]}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def regex_parse(match)
|
94
|
+
Hash[match.names.collect { |k| k.to_sym }.zip(match.captures)]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|