• Stars
    star
    109
  • Rank 319,077 (Top 7 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 11 years ago
  • Updated about 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Anyone can append to file. How about using AMQP? Pipes? ZeroMQ? Other cool shit??? Huh???

About Travis CI

In the Ruby community it's very popular to just append to a file in log/ directory in the current app. In many frameworks the developer can't even change the file. Damn it guys, we can do better!

Why Should I Care?

  • You might want to have the log files in /var/log for simpler log rotation.
  • You might not want to use files for logging at all. Not on the app server anyway. Especially considering that for security reasons it's better to send logs to a different server.
  • You might want to aggregate logs from multiple servers.
  • You might want to filter logs based on given pattern. Give me all error messages from all applications logs.#.error, all log items for database layer of testapp logs.testapp.db.*, all error messages for testapp logs.testapp.*.error etc.
  • Isn't ssh & tail -f really, really, I mean really lame? With AMQP, just subscribe to any pattern on any server you want from comfort of your own dev machine. Rock'n'roll!

Readable Logs (If You Want)

Besides, logs should be easy to read for the developers. That's why logging4hackers provides colourful formatter which uses colours instead of displaying log level as text and Logger#inspect for showing objects as syntax-highlighted JSON.

require 'logging'
require 'logging/code'

logger = Logging::Logger.new do |logger|
  logger.io = Logging::IO::Raw.new('testapp.logs.db')
  logger.formatter = Logging::Formatters::Colourful.new
end

logger.info("Starting the app.")
logger.inspect({method: 'GET', path: '/ideas.json', response: '200'})
logger.warn("Now I'm a tad bored ...")
logger.error("OK, gotta sleep now.")

Note: Actually the screenshot shows how would you inspect messages published into RabbitMQ, whereas in the code I'm using the IO::Raw which only prints to console. Example with AMQP is longer.

About @botanicus (blog)

botanicus I'm a launch-addict, creating stuff that matters is my biggest passion. I dropped out of high school and learnt programming before I'd end up on a street. In just a few months I moved from middle of nowhere to London where I worked as a freelancer for companies like VMware on the RabbitMQ team for which I, alongside great hacker michaelklishin, rewrote the AMQP gem.

I contributed to many famous OSS projects including RubyGems, rSpec and back in the g'd old days also to Merb. When EY decided to abandon Merb I wrote my own web framework, Rango (now discontinued), the only framework in Ruby with template inheritance.

My other hobbies include travelling, learning languages (ไฝ ๅฅฝ!) and personal development. My 3 + 2 rule was featured on LifeHacker.

My only goal for this year is to launch a successful start-up. Could MatcherApp be it?

Use-Cases

Logging Into RabbitMQ (Local or Remote)

TODO: Disconnect the AMQP, stop EM & terminate.

  • You can connect to RabbitMQ on localhost or remote server.
  • So far it requires some setup. In the future I might provide helpers for this.
  • It's the most powerful setup. You can filter patterns, you can discard messages just by not subscribing to those you're not interested in, you can consume given message multiple times, so you can for instance duplicate logs on two servers etc.
  • Instead writing directly to AMQP you can write to a named pipe and have a daemon which reroutes messages to RabbitMQ as described below.
require 'logging'
require 'logging/code'
require 'eventmachine'

EM.run do
  require 'amq/client'

  AMQ::Client.connect(adapter:  'eventmachine') do |connection|
    channel = AMQ::Client::Channel.new(connection, 1)

    channel.open

    exchange = AMQ::Client::Exchange.new(connection, channel, 'amq.topic', :topic)

    logger = Logging::Logger.new do |logger|
      logger.io = Logging::IO::AMQP.new('testapp.logs.db', exchange)
      logger.formatter = Logging::Formatters::Colourful.new
    end

    logger.info('Starting the app.')
    logger.inspect({method: 'GET', path: '/ideas.json', response: '200'})
    logger.error('Whops, no app defined, terminating.')
  end
end

Client/Server on Localhost using Named Pipe

  • You might not want to run EventMachine.
  • Setting up the Pipe logger on the client side requires much less setup, hence much less stuff can wrong.
  • It's easy to write a daemon to publish those messages from the pipe into RabbitMQ. In the future I might provide one.
# Create a named pipe.
mkfifo /tmp/loggingd.pipe

# Listen for messages coming to /tmp/loggingd.pipe.
#tail -f /tmp/loggingd.pipe

# Listen for new messages on the pipe and forward them to RabbitMQ.
./bin/loggingd.rb
logger = Logging::Logger.new do |logger|
  logger.io = Logging::IO::Pipe.new('testapp.logs.db', '/tmp/loggingd.pipe')
  logger.formatter = Logging::Formatters::Serialised.new(Logging::Formatters::Colourful.new)
end

Inspecting Remote Server

Often you want to figure out what's going on on server. This is how you do it:

./bin/logs_listen.rb 'logs.myapp.#' amqp://user:pass@remote_server/vhost

It creates temporary queue which it binds to the amq.topic exchange which exists by default in any RabbitMQ installation. Then it binds the temporary queue to this exchange with pattern we provide (in this case it's logs.myapp.#). This makes sure all the subscribers gets all the messages they're interested in.

Logging Best Practices

Don't Use Just One Logger Per App

In Ruby community people often don't use loggers at all. If they do, they work with only one instance. One logger instance for database, web server, application code and metrics. That doesn't scale.

If you use one logger instance per each module you can very easily filter based on specific pattern.

class DB
  def self.logger
    @logger ||= Logging::Logger.new do |logger|
      logger.io = Logging::IO::Pipe.new('testapp.logs.db', '/tmp/loggingd.pipe')
      logger.formatter = Logging::Formatters::Colourful.new
    end
  end
end

class App
  def self.logger
  @logger ||= Logging::Logger.new do |logger|
    logger.io = Logging::IO::Pipe.new('testapp.app.db', '/tmp/loggingd.pipe')
    logger.formatter = Logging::Formatters::Colourful.new
  end
end

Links

More Repositories

1

rango

Rango is ultralightweight, ultracustomizable, ultracool web framework inspired by Django.
Ruby
221
star
2

ace

Flexible static sites generator suitable even for big and complex sites with dynamically generated pages such as pages for each tag etc (not on the fly obviously as it just produces HTML).
Ruby
38
star
3

code-cleaner

Remove trailing whitespace, append missing \n and replace tabs by two spaces.
Ruby
38
star
4

minitest.js

Simple test framework for asynchronous testing in Node.js.
JavaScript
21
star
5

nake

Nake is light-weight and highly flexible Rake replacement with much better arguments parsing.
Ruby
19
star
6

git-deployer

Framework independent deployment solution based on Git hooks.
HTML
17
star
7

changelog

Simple CHANGELOG parser for Ruby.
Ruby
17
star
8

Node.js.tmbundle

TextMate bundle for Node.js
Ruby
15
star
9

cart

Cart is framework agnostic imlementation of shopping cart.
Ruby
11
star
10

formidable

Django-like forms
Ruby
11
star
11

pupu

Pupu is a framework independent plugin system for assets. It can handle dependencies and it provides a CLI interface, so itโ€™s really easy to bundle such packages into your app.
Ruby
11
star
12

colours.js

Terminal colours for JavaScript (works with Node.js).
JavaScript
9
star
13

template-inheritance

Ruby
8
star
14

task.node

Task manager similar to Rake for Node.js
JavaScript
8
star
15

multigiri

Post-processing for HTML & XML documents via Nokogiri. No more String#gsub in Rack middlewares!
Ruby
7
star
16

validations.js

Validation framework for JavaScript. Tested with Node.js, but it should be CommonJS-compatible.
JavaScript
6
star
17

merb-fixtures

Merb plugin that provides flexible fixtures system. DataMapper, ActiveRecord and Sequel is supported.
Ruby
6
star
18

git-tools

Others useful git related commands
Ruby
5
star
19

dm-spec

RSpec matchers for DataMapper.
Ruby
4
star
20

pupu-mooeditable

Pupu package for MooTools-based WYSIWYG editor.
JavaScript
3
star
21

rango-example-blog

Example blog application for Rango.
Ruby
3
star
22

helpers

Ruby
3
star
23

rango-examples

Example Rango projects.
3
star
24

padrino-template-inheritance-microblog

An example of using template-inheritance with Padrino.
Ruby
3
star
25

contributors

The contributors gem is useful for getting informations about project contributors. It assumes that Git is used. In particular it can be used for generating CONTRIBUTORS file.
Ruby
3
star
26

microgems

Microgems is drop-in replacement of Minigems which is drop-in replacement of RubyGems. As such, Microgems are the smallest RubyGems implementation ever. It do not provide any RubyGems API, it just handle file loading. No magic. No bloated code. Just Ruby.
Ruby
3
star
27

simple-templater

SimpleTemplater is dead-simple solution for creating generators. It strongly uses convention over configuration, so you don't have to write loads of code to generate one stupid plain text README.
Ruby
3
star
28

rango-in-rails

Example application which use template inheritance from Rango as replacement for Rails rendering via rails-template-inheritance gem.
Ruby
3
star
29

pupu-mootools

Pupu packaged MooTools framework. Both core and more are presented.
JavaScript
2
star
30

pupu-flash

Pupu package for MooTools-based flash messages.
JavaScript
2
star
31

get

Get is CLI utility for link collectiong and automatic downloading files from web. For example it can download all .mov from railscasts.com.
Ruby
2
star
32

botabot

Simple and extensible Jabber MUC bot in Ruby.
Ruby
2
star
33

media-path

Path abstraction that provides easier interaction with paths and corresponding URLs.
Ruby
2
star
34

pupu-github-badge

Badge with your Github projects.
JavaScript
1
star
35

pupu-blueprint

Pupu packaged Blueprint CSS framework.
Ruby
1
star
36

pupu-moorainbow

Pupu package with MooRainbow colour picker.
JavaScript
1
star
37

101ideas.cz

Ruby
1
star
38

dm-is-sluggable

Creates unique slug from property. For DataMapper ORM.
Ruby
1
star
39

pupu-autocompleter

Pupu package for MooTools-based autocompletion.
JavaScript
1
star
40

pupu-squeezebox

Pupu package for MooTools-based lightbox.
JavaScript
1
star
41

spm

Stupid package manager for Node.js
1
star
42

pupu-slider

Pupu package for MooTools-based slider.
JavaScript
1
star
43

deck.title.js

Changes document.title for each slide in deck.js
JavaScript
1
star
44

pupu-form-defaults

Pupu package for MooTools-based help text in forms.
JavaScript
1
star
45

pipeline.rb

Plugin pipeline infrastructure connected through RabbitMQ.
Ruby
1
star
46

pupu-ratings

Pupu package for MooTools-based ratings (RabidRatings).
JavaScript
1
star
47

pupu-tips

Pupu package for MooTools-based tips.
JavaScript
1
star
48

dm-is-serialized

Ruby
1
star
49

rails-template-inheritance

[Rails plugin] Template inheritance provided by Rango in Rails!
Ruby
1
star
50

pupu-remooz

Pupu package for MooTools-based lightbox.
JavaScript
1
star
51

pupu-growl

Pupu package for MooTools-based growl-like functionality.
JavaScript
1
star
52

veewee-ubuntu-box

Ubuntu 12.10 AMD 64 template for Vagrant (well, actually for Veewee which builds it).
Shell
1
star