Skip to content

Instantly share code, notes, and snippets.

@madx
Created February 23, 2012 15:10
Show Gist options
  • Save madx/1893224 to your computer and use it in GitHub Desktop.
Save madx/1893224 to your computer and use it in GitHub Desktop.
Syslog Viewer

Instructions

MySQL

Create a MySQL database named syslog and a table called events (you may run the app once to automatically create the table). For rsyslog, you may use something like this :

$template SQLTemplate,"INSERT INTO syslog.events VALUES('', '%source%', '%msg%', '%syslogtag%', '%syslogfacility-text%', '%syslogpriority-text%', %timestamp:::date-mysql)",SQL
*.*;auth,authpriv.none :ommysql:localhost,<database>,<db_user>,<db_pass>;SQLTemplate

Replace <database>, <db_user>, <db_pass> with the correct values.

File setup

  1. Put the syslog-viewer.rb in a folder of your choice
  2. Download Twitter Bootstrap and unzip it in that folder
  3. Configure the constants at the top of the syslog-viewer.rb according to the database configuration

Enjoy.

DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2012 François Vaux <[email protected]>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
require 'sinatra'
require 'haml'
require 'sequel'
DBUSER = 'user'
DBPASS = 'password'
DBHOST = 'localhost'
DBBASE = 'syslog'
DB = Sequel.connect("mysql://#{DBUSER}:#{DBPASS}@#{DBHOST}/#{DBBASE}")
Sequel.extension :pagination
DB.create_table? :events do
primary_key :id
String :host
String :message
String :tag
String :facility
String :priority
Time :received_at
end
Event = DB[:events]
set :public_folder, File.dirname(__FILE__) + '/assets'
set :haml, ugly: true
helpers do
def plabel(pri)
return "label-#{
case pri
when *%w(notice info debug)
'info'
when *%w(err error crit alert emerg panic)
'important'
when *%w(warn warning)
'warning'
else
pri
end
}"
end
def selected(event, field)
key = field.to_sym
return params[key] == event[key] ? {selected: 'selected'} : Hash.new
end
def query?
params[:q] && !params[:q].empty?
end
def paginate?
params[:disable_pagination].nil? || params[:disable_pagination].to_i != 1
end
def query_url(options)
options.dup.each { |k,v| options[k.to_s] = v; options.delete(k.to_sym) }
url('/?' + params.merge(options).delete_if { |_, v| v.nil? }.map { |k,v|
"#{k}=#{URI.escape(v.to_s, /[^#{URI::PATTERN::UNRESERVED}]/)}"
}.join('&'))
end
end
get '/' do
query = params.inject({}) do |h, (key, value)|
next h unless %w(host priority facility).member?(key)
next h if value.empty?
h.update(key.to_sym => value)
end
@events = Event.filter(query)
@events = @events.grep([:tag, :message], "%#{params[:q]}%", case_insensitive: true) if query?
page ||= (i = (params[:page] || 1).to_i) <= 0 ? 1 : i
@events = @events.paginate(page, 50) if paginate?
haml :app
end
__END__
@@ app
!!! 5
%html{lang: 'fr'}
%head
%meta{charset: 'utf-8'}
%title Syslog Viewer
%link{href: '/css/bootstrap.css', rel: 'stylesheet'}
:css
body { padding-bottom: 40px; }
.control-group {
display: inline-block;
margin-right: 1em;
}
.evt-date a { text-decoration: none !important; }
tr:target { background-color: #d9edf7; }
.tag { color: #888; }
.page-count { text-align: center; }
.page-left { text-align: left; }
.page-right { text-align: right; }
%body
.navbar
.navbar-inner
.container
%a.brand#go-root{href: '/'} Syslog Viewer
.container
%h2 Filter logs
%form.form-inline.well{method: 'GET'}
.control-group
%label.control-label{for: "filter-host"} Host
.controls
%select#filter-host.span2{name: 'host'}
%option{value: ''} Any
- Event.group(:host).each do |ev|
%option{selected(ev, :host)}= ev[:host]
.control-group
%label.control-label{for: "filter-priority"} Priority
.controls
%select#filter-priority.span2{name: 'priority'}
%option{value: ''} Any
- Event.group(:priority).each do |ev|
%option{selected(ev, :priority)}= ev[:priority]
.control-group
%label.control-label{for: "filter-facility"} Facility
.controls
%select#filter-facility.span2{name: 'facility'}
%option{value: ''} Any
- Event.group(:facility).each do |ev|
%option{selected(ev, :facility)}= ev[:facility]
.control-group
.controls
%input#filter-text.input-large{type: 'text', placeholder: 'Text', name: 'q', value: params[:q]}
.control-group
.controls
%button.btn.btn-primary{type: 'submit'} Apply
.control-group
.controls
- if paginate?
%a.btn.btn-danger{href: query_url(disable_pagination: 1)} Show all
- else
%a.btn.btn-success{href: query_url(disable_pagination: nil)} Paginate
%input{type: 'hidden', name: 'page', value: '1'}
= haml :paginate, layout: false
%table.table.table-condensed
%thead
%tr
%th ID
%th Host
%th Facility
%th Priority
%th Date
%th Message
%tbody
- @events.order(:id.desc).each do |ev|
%tr{id: "evt-#{ev[:id]}"}
%td.evt-id
%a{href: "#evt-#{ev[:id]}"}= ev[:id]
%td.evt-host
%a{href: query_url(host: ev[:host])}= ev[:host]
%td.evt-facility
%a{href: query_url(facility: ev[:facility])}= ev[:facility]
%td.evt-priority
%a.label{href: query_url(priority: ev[:priority]), class: plabel(ev[:priority])}= ev[:priority]
%td.evt-date
%a{title: ev[:received_at]}
= ev[:received_at].strftime('%R')
%td.evt-message
%span.tag= ev[:tag]
= ev[:message]
= haml :paginate, layout: false
%script{src: '/js/jquery.js'}
%script{src: '/js/bootstrap.js'}
:javascript
$(function() {
$('.evt-date a').tooltip({
title: function (e) {
return $(e).attr('title');
}
});
$('.evt-date').tooltip({
title: function (e) {
return $(e).attr('title');
}
});
});
@@ paginate
- if paginate?
- cp = @events.current_page
- pc = @events.page_count
.pagination.pagination-centered
%ul
%li{class: cp <= 1 ? 'disabled' : ''}
%a{href: cp > 1 ? query_url(page: @events.current_page - 1) : ''} Newer
%li.disabled
%a== #{@events.current_page}/#{@events.page_count}
%li{class: cp >= pc ? 'disabled' : ''}
%a{href: cp < pc ? query_url(page: @events.current_page + 1) : ''} Older
// - if @events.current_page < @events.page_count
// - if @events.current_page > 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment